为何if块内创建的SFML Texture会被销毁?如何延长其生命周期?
解决ImGui+SFML纹理显示白块问题
问题描述
我编写了结合ImGui与SFML的代码:
// Sprite. if(ImGui::CollapsingHeader("Sprite.")) { // Preview texture allocations. sf::Texture texture; // ... static char spritePath[MAX_OBJECTS_TEXT_SIZE / 2]; // (512 / 2) // ... if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) { texture.loadFromFile(spritePath); } // ... // Preview. (150, 150). ImGui::Image(texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White); }
运行后仅显示白块,example.png文件确实存在且无加载错误。
查阅SFML文档得知:
你成功加载了纹理,正确创建了精灵,但屏幕上却只有白块?这是常见错误。精灵仅存储纹理的指针,若纹理被销毁或内存地址变动,精灵的纹理指针会失效。
示例错误代码:
sf::Sprite loadSprite(std::string filename) { sf::Texture texture; texture.loadFromFile(filename); return sf::Sprite(texture); } // error: the texture is destroyed here
疑问点:
- 为什么单个if块内创建的Texture会被销毁?
- ImGui::Image会在下一帧绘制,此时Texture已被删除,如何让Texture拥有无限生命周期?
原因分析
- 局部变量作用域限制:
sf::Texture texture是if(ImGui::CollapsingHeader("Sprite."))代码块内的局部变量,当当前帧的代码执行完这个if块后,局部变量会立即被销毁,内存被释放。 - ImGui延迟渲染机制:
ImGui::Image只是把绘制指令添加到ImGui的渲染队列中,实际的GPU渲染操作是在主循环末尾调用ImGui::Render()时才执行。此时Texture已经被销毁,GPU上的纹理数据失效,自然显示白块。 - 和SFML示例错误原理一致:和文档里函数返回Sprite后Texture被销毁的问题本质相同,都是局部变量超出作用域被销毁,导致关联的纹理引用失效。
解决方法
方法1:将Texture声明为静态变量
把局部的sf::Texture texture;改成static sf::Texture texture;,静态变量的生命周期会延长至整个程序运行期间,不会随if块结束而销毁:
// Sprite. if(ImGui::CollapsingHeader("Sprite.")) { // 改为static,延长生命周期至程序结束 static sf::Texture texture; static char spritePath[MAX_OBJECTS_TEXT_SIZE / 2]; // (512 / 2) if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) { texture.loadFromFile(spritePath); } // Preview. (150, 150). ImGui::Image(texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White); }
方法2:将Texture提升为类成员或全局变量
如果UI逻辑是封装在类中的,把sf::Texture作为类的成员变量;如果是小型程序,也可以声明为全局变量,这样它的生命周期和程序完全一致:
// 全局声明,或作为类的成员变量 sf::Texture g_spriteTexture; // ... // Sprite. if(ImGui::CollapsingHeader("Sprite.")) { static char spritePath[MAX_OBJECTS_TEXT_SIZE / 2]; // (512 / 2) if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) { g_spriteTexture.loadFromFile(spritePath); } ImGui::Image(g_spriteTexture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White); }
方法3:使用智能指针管理生命周期
如果需要更灵活的资源管理,可以用std::shared_ptr<sf::Texture>来持有Texture,确保只要还有引用存在,Texture就不会被销毁:
// Sprite. if(ImGui::CollapsingHeader("Sprite.")) { static std::shared_ptr<sf::Texture> texture = std::make_shared<sf::Texture>(); static char spritePath[MAX_OBJECTS_TEXT_SIZE / 2]; // (512 / 2) if(ImGui::InputText("Sprite path.##sprite", spritePath, IM_ARRAYSIZE(spritePath))) { texture->loadFromFile(spritePath); } ImGui::Image(*texture, sf::Vector2f(DEFAULT_TEXTURE_PREVIEW_WIDTH, DEFAULT_TEXTURE_PREVIEW_HEIGHT), sf::Color::Transparent, sf::Color::White); }
内容的提问来源于stack exchange,提问作者null
相关产品推荐
相关产品推荐

