diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index a66f47196..aa2410d61 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -235,6 +235,8 @@ Graphics::~Graphics() for (int i = 0; i < (int) SHADERSTAGE_MAX_ENUM; i++) cachedShaderStages[i].clear(); + clearTemporaryResources(); + Shader::deinitialize(); } @@ -887,6 +889,10 @@ void Graphics::setRenderTargets(const RenderTargets &rts) realRTs.depthStencil.texture = getTemporaryTexture(dsformat, pixelw, pixelh, reqmsaa); realRTs.depthStencil.slice = 0; + // TODO: fix this to call release at the right time. + // This only works here because nothing else calls getTemporaryTexture. + releaseTemporaryTexture(realRTs.depthStencil.texture); + setRenderTargetsInternal(realRTs, pixelw, pixelh, hasSRGBtexture); } else @@ -989,12 +995,15 @@ Texture *Graphics::getTemporaryTexture(PixelFormat format, int w, int h, int sam for (TemporaryTexture &temp : temporaryTextures) { + if (temp.framesSinceUse < 0) + continue; + Texture *c = temp.texture; if (c->getPixelFormat() == format && c->getPixelWidth() == w && c->getPixelHeight() == h && c->getRequestedMSAA() == samples) { texture = c; - temp.framesSinceUse = 0; + temp.framesSinceUse = -1; break; } } @@ -1016,6 +1025,101 @@ Texture *Graphics::getTemporaryTexture(PixelFormat format, int w, int h, int sam return texture; } +void Graphics::releaseTemporaryTexture(Texture *texture) +{ + for (TemporaryTexture &temp : temporaryTextures) + { + if (temp.texture == texture) + { + temp.framesSinceUse = 0; + break; + } + } +} + +Buffer *Graphics::getTemporaryBuffer(size_t size, DataFormat format, uint32 usageflags, BufferDataUsage datausage) +{ + Buffer *buffer = nullptr; + + for (TemporaryBuffer &temp : temporaryBuffers) + { + if (temp.framesSinceUse < 0) + continue; + + Buffer *b = temp.buffer; + + if (temp.size == size && b->getDataMember(0).decl.format == format + && b->getUsageFlags() == usageflags && b->getDataUsage() == datausage) + { + buffer = b; + temp.framesSinceUse = -1; + break; + } + } + + if (buffer == nullptr) + { + Buffer::Settings settings(usageflags, datausage); + buffer = newBuffer(settings, format, nullptr, size, 0); + + temporaryBuffers.emplace_back(buffer, size); + } + + return buffer; +} + +void Graphics::releaseTemporaryBuffer(Buffer *buffer) +{ + for (TemporaryBuffer &temp : temporaryBuffers) + { + if (temp.buffer == buffer) + { + temp.framesSinceUse = 0; + break; + } + } +} + +void Graphics::updateTemporaryResources() +{ + for (int i = (int) temporaryTextures.size() - 1; i >= 0; i--) + { + auto &t = temporaryTextures[i]; + if (t.framesSinceUse >= MAX_TEMPORARY_RESOURCE_UNUSED_FRAMES) + { + t.texture->release(); + t = temporaryTextures.back(); + temporaryTextures.pop_back(); + } + else if (t.framesSinceUse >= 0) + t.framesSinceUse++; + } + + for (int i = (int) temporaryBuffers.size() - 1; i >= 0; i--) + { + auto &t = temporaryBuffers[i]; + if (t.framesSinceUse >= MAX_TEMPORARY_RESOURCE_UNUSED_FRAMES) + { + t.buffer->release(); + t = temporaryBuffers.back(); + temporaryBuffers.pop_back(); + } + else if (t.framesSinceUse >= 0) + t.framesSinceUse++; + } +} + +void Graphics::clearTemporaryResources() +{ + for (auto temp :temporaryBuffers) + temp.buffer->release(); + + for (auto temp : temporaryTextures) + temp.texture->release(); + + temporaryBuffers.clear(); + temporaryTextures.clear(); +} void Graphics::intersectScissor(const Rect &rect) { Rect currect = states.back().scissorRect; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 61b6cbdbb..161b35d7e 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -856,6 +856,12 @@ public: static void flushBatchedDrawsGlobal(); + Texture *getTemporaryTexture(PixelFormat format, int w, int h, int samples); + void releaseTemporaryTexture(Texture *texture); + + Buffer *getTemporaryBuffer(size_t size, DataFormat format, uint32 usageflags, BufferDataUsage datausage); + void releaseTemporaryBuffer(Buffer *buffer); + void cleanupCachedShaderStage(ShaderStageType type, const std::string &cachekey); template @@ -954,6 +960,19 @@ protected: } }; + struct TemporaryBuffer + { + Buffer *buffer; + size_t size; + int framesSinceUse; + + TemporaryBuffer(Buffer *buf, size_t size) + : buffer(buf) + , size(size) + , framesSinceUse(-1) + {} + }; + struct TemporaryTexture { Texture *texture; @@ -961,7 +980,7 @@ protected: TemporaryTexture(Texture *tex) : texture(tex) - , framesSinceUse(0) + , framesSinceUse(-1) {} }; @@ -980,7 +999,8 @@ protected: void createQuadIndexBuffer(); void createFanIndexBuffer(); - Texture *getTemporaryTexture(PixelFormat format, int w, int h, int samples); + void updateTemporaryResources(); + void clearTemporaryResources(); void restoreState(const DisplayState &s); void restoreStateChecked(const DisplayState &s); @@ -1014,6 +1034,7 @@ protected: std::vector states; std::vector stackTypeStack; + std::vector temporaryBuffers; std::vector temporaryTextures; int renderTargetSwitchCount; @@ -1028,7 +1049,7 @@ protected: Deprecations deprecations; static const size_t MAX_USER_STACK_DEPTH = 128; - static const int MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES = 16; + static const int MAX_TEMPORARY_RESOURCE_UNUSED_FRAMES = 16; private: diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index 2704118d0..90bc1edfd 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -501,10 +501,7 @@ void Graphics::unSetMode() submitCommandBuffer(SUBMIT_DONE); - for (auto temp : temporaryTextures) - temp.texture->release(); - - temporaryTextures.clear(); + clearTemporaryResources(); created = false; metalLayer = nil; @@ -1620,18 +1617,7 @@ void Graphics::present(void *screenshotCallbackData) renderTargetSwitchCount = 0; drawCallsBatched = 0; - // This assumes temporary canvases will only be used within a render pass. - for (int i = (int) temporaryTextures.size() - 1; i >= 0; i--) - { - if (temporaryTextures[i].framesSinceUse >= MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES) - { - temporaryTextures[i].texture->release(); - temporaryTextures[i] = temporaryTextures.back(); - temporaryTextures.pop_back(); - } - else - temporaryTextures[i].framesSinceUse++; - } + updateTemporaryResources(); }} int Graphics::getRequestedBackbufferMSAA() const diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index df1b911fc..92e48634e 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -462,14 +462,12 @@ void Graphics::unSetMode() // mode change. Volatile::unloadAll(); + clearTemporaryResources(); + for (const auto &pair : framebufferObjects) gl.deleteFramebuffer(pair.second); - for (auto temp : temporaryTextures) - temp.texture->release(); - framebufferObjects.clear(); - temporaryTextures.clear(); if (mainVAO != 0) { @@ -1335,18 +1333,7 @@ void Graphics::present(void *screenshotCallbackData) renderTargetSwitchCount = 0; drawCallsBatched = 0; - // This assumes temporary textures will only be used within a render pass. - for (int i = (int) temporaryTextures.size() - 1; i >= 0; i--) - { - if (temporaryTextures[i].framesSinceUse >= MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES) - { - temporaryTextures[i].texture->release(); - temporaryTextures[i] = temporaryTextures.back(); - temporaryTextures.pop_back(); - } - else - temporaryTextures[i].framesSinceUse++; - } + updateTemporaryResources(); } int Graphics::getRequestedBackbufferMSAA() const