You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:14:55