gpu errors

This commit is contained in:
2026-05-02 21:56:33 +02:00
parent fcc598adb1
commit c46443e2f4
6 changed files with 103 additions and 4 deletions

View File

@@ -17,11 +17,16 @@ namespace Game::Renderer {
// Build the texture for the font; Call getSDLTexture() afterwards // Build the texture for the font; Call getSDLTexture() afterwards
void build(SDL_Color color, std::string text); void build(SDL_Color color, std::string text);
// Rebuild GPU-backed texture after a renderer/device reset
bool reload(SDL_Renderer* renderer);
SDL_Texture* getSDLTexture(); SDL_Texture* getSDLTexture();
std::string getId(); std::string getId();
private: private:
TTF_Font* mFont; TTF_Font* mFont;
SDL_Renderer* mRenderer; SDL_Renderer* mRenderer;
// Remember last build params so we can rebuild fonts on device reset
std::string mLastText;
SDL_Color mLastColor;
}; };
} }

View File

@@ -21,10 +21,15 @@ namespace Game::Renderer {
float getHeight(); float getHeight();
bool isTiled() { return mIsTiled; } bool isTiled() { return mIsTiled; }
void setTiled(bool tiled) { mIsTiled = tiled; } void setTiled(bool tiled) { mIsTiled = tiled; }
// Reload GPU-backed texture using a new renderer after device reset
virtual bool reload(SDL_Renderer* renderer);
protected: protected:
SDL_Texture* mTex; SDL_Texture* mTex;
std::string mId; std::string mId;
// For textures created from disk, store the path so we can reload on device reset
std::string mPath;
bool mIsFromFile = false;
private: private:
bool mIsTiled = false; // Whether the texture is a tileset that should be rendered as a single tile or not bool mIsTiled = false; // Whether the texture is a tileset that should be rendered as a single tile or not
}; };

View File

@@ -36,6 +36,10 @@ namespace Game::Renderer {
void Font::build(SDL_Color color, std::string text) { void Font::build(SDL_Color color, std::string text) {
if (!mFont) { return; } if (!mFont) { return; }
// Store last build parameters so we can rebuild after device resets
mLastText = text;
mLastColor = color;
SDL_Surface* surf = TTF_RenderText_Blended(mFont, text.c_str(), text.size(), color); SDL_Surface* surf = TTF_RenderText_Blended(mFont, text.c_str(), text.size(), color);
if (!surf) { if (!surf) {
ERROR("TTF_RenderText_Blended Error: " << SDL_GetError() << " (This object may be unusuable)"); ERROR("TTF_RenderText_Blended Error: " << SDL_GetError() << " (This object may be unusuable)");
@@ -57,6 +61,14 @@ namespace Game::Renderer {
} }
bool Font::reload(SDL_Renderer* renderer) {
mRenderer = renderer;
if (mLastText.empty()) return false;
// Rebuild the texture using the stored last text and color
build(mLastColor, mLastText);
return true;
}
SDL_Texture* Font::getSDLTexture() { return mTex; } SDL_Texture* Font::getSDLTexture() { return mTex; }
std::string Font::getId() { return mId; } std::string Font::getId() { return mId; }
} }

View File

@@ -24,13 +24,19 @@ namespace Game::Renderer {
bool Renderer::init(SDL_Window* window) { bool Renderer::init(SDL_Window* window) {
// Request VSync before/at renderer setup; some backends honor this hint. // Request VSync before/at renderer setup; some backends honor this hint.
SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
// Create renderer using the portable API. Some SDL3 backends may not support the old flags,
// so fall back to a software renderer via hint if the first attempt fails.
mRenderer = SDL_CreateRenderer(window, nullptr);
if (!mRenderer) {
WARN("Renderer creation failed, attempting software renderer: " << SDL_GetError());
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
mRenderer = SDL_CreateRenderer(window, nullptr); mRenderer = SDL_CreateRenderer(window, nullptr);
if (!mRenderer) { if (!mRenderer) {
std::string errorMsg = std::string("Neuspešno ustvarjanje rendererja: ") + std::string(SDL_GetError()); std::string errorMsg = std::string("Neuspešno ustvarjanje rendererja: ") + std::string(SDL_GetError());
ERROR(errorMsg.c_str()); ERROR(errorMsg.c_str());
return false; return false;
} }
}
mVSyncEnabled = SDL_SetRenderVSync(mRenderer, 1); mVSyncEnabled = SDL_SetRenderVSync(mRenderer, 1);
if (!mVSyncEnabled) { if (!mVSyncEnabled) {

View File

@@ -7,6 +7,9 @@ namespace Game::Renderer {
Texture::Texture(const std::string& path, SDL_Renderer* renderer, std::string id) Texture::Texture(const std::string& path, SDL_Renderer* renderer, std::string id)
: mTex(nullptr), mId(id) { : mTex(nullptr), mId(id) {
mPath = path;
mIsFromFile = true;
SDL_Surface* surf = IMG_Load(path.c_str()); SDL_Surface* surf = IMG_Load(path.c_str());
if (!surf) { if (!surf) {
ERROR("Failed to load image at " << path); ERROR("Failed to load image at " << path);
@@ -44,6 +47,34 @@ namespace Game::Renderer {
LOG("Tekstura '" << mId << "' uničena") LOG("Tekstura '" << mId << "' uničena")
} }
bool Texture::reload(SDL_Renderer* renderer) {
if (!mIsFromFile || mPath.empty()) return false;
if (mTex) {
SDL_DestroyTexture(mTex);
mTex = nullptr;
}
SDL_Surface* surf = IMG_Load(mPath.c_str());
if (!surf) {
ERROR("Failed to reload image at " << mPath);
return false;
}
mTex = SDL_CreateTextureFromSurface(renderer, surf);
SDL_DestroySurface(surf);
if (!mTex) {
ERROR("Failed to create texture from surface when reloading " << mPath << ": " << SDL_GetError());
return false;
}
// Restore scale mode
if (!SDL_SetTextureScaleMode(mTex, SDL_SCALEMODE_NEAREST)) {
WARN("Failed to set texture scale mode to NEAREST for '" << mId << "' during reload: " << SDL_GetError());
}
return true;
}
SDL_Texture* Texture::getSDLTexture() { SDL_Texture* Texture::getSDLTexture() {
return mTex; return mTex;
} }

View File

@@ -1,6 +1,7 @@
#include <window/window.hpp> #include <window/window.hpp>
#include <algorithm> #include <algorithm>
#include <renderer/texture.hpp>
namespace Game::Window { namespace Game::Window {
std::mutex Window::sMutex; std::mutex Window::sMutex;
@@ -113,6 +114,45 @@ namespace Game::Window {
} }
}); });
} }
// Renderer device/targets reset (GPU device removed) - attempt to re-create renderer and reload textures
if (event.type == SDL_EVENT_RENDER_DEVICE_RESET || event.type == SDL_EVENT_RENDER_TARGETS_RESET) {
LOG("Renderer device reset event received: attempting to reinitialize renderer and reload textures");
// Destroy current renderer and try to re-init
mRenderer.destroy();
bool reinitOk = mRenderer.init(mWindow);
// If reinit failed, try forcing the software renderer
if (!reinitOk) {
WARN("Renderer re-init failed, forcing software renderer fallback");
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
reinitOk = mRenderer.init(mWindow);
if (!reinitOk) {
ERROR("Software renderer fallback also failed: " << SDL_GetError());
// Unable to recover; stop running to avoid crashes
mRunning = false;
break;
}
}
// Rebuild GPU textures for all entities (fonts and file-based textures)
State::GameState::getInstance().withEntitiesLocked([&](auto& entities) {
for (auto& [name, entity] : entities) {
(void)name;
if (entity) {
auto tex = entity->getTexture();
if (tex) {
try {
tex->reload(mRenderer.getSDLRenderer());
} catch (...) {
WARN("Exception while reloading texture for entity: " << entity->getName());
}
}
entity->onWindowResized(mLastWindowWidth, mLastWindowHeight); // ensure layout is correct
}
}
});
}
} }
mRenderer.renderFrame(); mRenderer.renderFrame();