SDL2窗口调整大小时创建的纹理损坏问题排查与解决
窗口调整大小时SDL渲染目标纹理损坏问题(Windows平台特有)
我在带SDL_WINDOW_RESIZABLE标志的窗口中创建并渲染大量纹理,调整窗口大小后部分纹理会随机损坏,但用其他方式创建的纹理(比如渲染字形转纹理)没有这个问题。Linux平台测试无此问题,推测是Windows平台特有。
问题复现代码
#include <iostream> #include <string> #include <SDL.h> #include <vector> SDL_Texture* generateColoredTexture(SDL_Renderer *renderer_, int w_, int h_, const SDL_Color &col_) { auto *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w_, h_); SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); auto oldTar = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, tex); SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); SDL_RenderClear(renderer_); SDL_SetRenderDrawColor(renderer_, col_.r, col_.g, col_.b, col_.a); SDL_Rect dst = {4, 4, w_ - 8, h_ - 8}; SDL_RenderFillRect(renderer_, &dst); SDL_SetRenderTarget(renderer_, oldTar); return tex; } int main(int argc, char* args[]) { if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { std::cout << "SDL initialization error: " << SDL_GetError() << std::endl; return -1; } auto window = SDL_CreateWindow("TestWin", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); if (window == nullptr) { std::cout << "Window creation error: " << SDL_GetError() << std::endl; return -3; } auto renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); if (renderer == nullptr) { std::cout << "Renderer creation error: " << SDL_GetError() << std::endl; return -4; } std::vector<SDL_Texture*> texs; for (int i = 0; i < 53; ++i) { texs.push_back(generateColoredTexture(renderer, 50, 50, {uint8_t(rand() % 255), uint8_t(rand() % 255), uint8_t(rand() % 255), 255})); } std::vector<std::vector<int>> cells; for (int y = 0; y < 10; ++y) { cells.push_back({}); for (int x = 0; x < 20; ++x) { cells.back().push_back(rand() % texs.size()); } } bool running = true; SDL_Event e; while (running) { while( SDL_PollEvent( &e ) != 0 ) { if( e.type == SDL_QUIT ) { running = false; } } SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderClear(renderer); for (int y = 0; y < cells.size(); ++y) { for (int x = 0; x < cells[y].size(); ++x) { SDL_Rect dstrect{x * 50, y * 50, 50, 50}; SDL_RenderCopy(renderer, texs[cells[y][x]], NULL, &dstrect); } } SDL_RenderPresent(renderer); } for (auto *tex : texs) { SDL_DestroyTexture(tex); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
问题原因
- Windows平台下,窗口调整大小时SDL加速渲染器会重置GPU上下文,**渲染目标类型的纹理(
SDL_TEXTUREACCESS_TARGET)**的像素数据会丢失。普通静态纹理(如从Surface创建的)会被SDL自动重新上传至GPU,但渲染目标纹理没有这个自动恢复机制。 SDL_WINDOWEVENT_EXPOSED事件不触发是因为窗口调整大小对应的是SDL_WINDOWEVENT_RESIZED或SDL_WINDOWEVENT_SIZE_CHANGED事件,而非暴露事件。
解决方案
方案1:监听窗口大小变化事件,重新生成纹理
在事件循环中捕获窗口尺寸变化事件,销毁旧纹理并重新生成:
while( SDL_PollEvent( &e ) != 0 ) { if( e.type == SDL_QUIT ) { running = false; } else if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { // 销毁旧纹理 for (auto *tex : texs) { SDL_DestroyTexture(tex); } texs.clear(); // 重新生成纹理 for (int i = 0; i < 53; ++i) { texs.push_back(generateColoredTexture(renderer, 50, 50, {uint8_t(rand() % 255), uint8_t(rand() % 255), uint8_t(rand() % 255), 255})); } } } }
方案2:改用静态纹理替代渲染目标纹理
修改纹理生成函数,先在Surface上绘制内容再转换为纹理,这种静态纹理会被SDL自动处理GPU上下文重置:
SDL_Texture* generateColoredTexture(SDL_Renderer *renderer_, int w_, int h_, const SDL_Color &col_) { SDL_Surface* surf = SDL_CreateRGBSurface(0, w_, h_, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); SDL_FillRect(surf, nullptr, SDL_MapRGBA(surf->format, 0, 0, 0, 0)); SDL_Rect dst = {4, 4, w_ - 8, h_ - 8}; SDL_FillRect(surf, &dst, SDL_MapRGBA(surf->format, col_.r, col_.g, col_.b, col_.a)); SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer_, surf); SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); SDL_FreeSurface(surf); return tex; }
内容的提问来源于stack exchange,提问作者SavedowW
相关产品推荐
相关产品推荐

