gpu errors
This commit is contained in:
@@ -17,11 +17,16 @@ namespace Game::Renderer {
|
||||
|
||||
// Build the texture for the font; Call getSDLTexture() afterwards
|
||||
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();
|
||||
std::string getId();
|
||||
private:
|
||||
TTF_Font* mFont;
|
||||
SDL_Renderer* mRenderer;
|
||||
// Remember last build params so we can rebuild fonts on device reset
|
||||
std::string mLastText;
|
||||
SDL_Color mLastColor;
|
||||
};
|
||||
}
|
||||
@@ -21,10 +21,15 @@ namespace Game::Renderer {
|
||||
float getHeight();
|
||||
bool isTiled() { return mIsTiled; }
|
||||
void setTiled(bool tiled) { mIsTiled = tiled; }
|
||||
// Reload GPU-backed texture using a new renderer after device reset
|
||||
virtual bool reload(SDL_Renderer* renderer);
|
||||
|
||||
protected:
|
||||
SDL_Texture* mTex;
|
||||
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:
|
||||
bool mIsTiled = false; // Whether the texture is a tileset that should be rendered as a single tile or not
|
||||
};
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace Game::Renderer {
|
||||
void Font::build(SDL_Color color, std::string text) {
|
||||
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);
|
||||
if (!surf) {
|
||||
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; }
|
||||
std::string Font::getId() { return mId; }
|
||||
}
|
||||
@@ -24,12 +24,18 @@ namespace Game::Renderer {
|
||||
bool Renderer::init(SDL_Window* window) {
|
||||
// Request VSync before/at renderer setup; some backends honor this hint.
|
||||
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) {
|
||||
std::string errorMsg = std::string("Neuspešno ustvarjanje rendererja: ") + std::string(SDL_GetError());
|
||||
ERROR(errorMsg.c_str());
|
||||
return false;
|
||||
WARN("Renderer creation failed, attempting software renderer: " << SDL_GetError());
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
|
||||
mRenderer = SDL_CreateRenderer(window, nullptr);
|
||||
if (!mRenderer) {
|
||||
std::string errorMsg = std::string("Neuspešno ustvarjanje rendererja: ") + std::string(SDL_GetError());
|
||||
ERROR(errorMsg.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mVSyncEnabled = SDL_SetRenderVSync(mRenderer, 1);
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace Game::Renderer {
|
||||
|
||||
Texture::Texture(const std::string& path, SDL_Renderer* renderer, std::string id)
|
||||
: mTex(nullptr), mId(id) {
|
||||
mPath = path;
|
||||
mIsFromFile = true;
|
||||
|
||||
SDL_Surface* surf = IMG_Load(path.c_str());
|
||||
if (!surf) {
|
||||
ERROR("Failed to load image at " << path);
|
||||
@@ -44,6 +47,34 @@ namespace Game::Renderer {
|
||||
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() {
|
||||
return mTex;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <window/window.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <renderer/texture.hpp>
|
||||
|
||||
namespace Game::Window {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user