diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index a3b2b36300..7f4ed070d8 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -92,7 +92,7 @@ public: Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override; + void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; @@ -796,6 +796,7 @@ public: width_ = desc.width; height_ = desc.height; depth_ = desc.depth; + format_ = desc.format; } ~D3D11Texture() { if (tex) @@ -944,7 +945,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { return tex; } -void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { +void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { D3D11Texture *tex = (D3D11Texture *)texture; // TODO } diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 555f64a8e7..5389eac6fe 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -316,7 +316,6 @@ private: LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; TextureType type_; - DataFormat format_; D3DFORMAT d3dfmt_; LPDIRECT3DTEXTURE9 tex_ = nullptr; LPDIRECT3DVOLUMETEXTURE9 volTex_ = nullptr; @@ -537,7 +536,7 @@ public: Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override; + void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override { // Not implemented @@ -940,9 +939,9 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) { return tex; } -void D3D9Context::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { +void D3D9Context::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { D3D9Texture *tex = (D3D9Texture *)texture; - tex->UpdateTextureLevels(data, numLevels, TextureCallback()); + tex->UpdateTextureLevels(data, numLevels, initDataCallback); } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 4d6f27c34a..3f29d54207 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -371,7 +371,7 @@ public: void EndFrame() override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override; + void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; @@ -862,7 +862,6 @@ private: GLRenderManager *render_; GLRTexture *tex_; - DataFormat format_; TextureType type_; int mipLevels_; bool generateMips_; // Generate mips requested @@ -1031,9 +1030,9 @@ Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { return new OpenGLTexture(&renderManager_, desc); } -void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { +void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { OpenGLTexture *tex = (OpenGLTexture *)texture; - tex->UpdateTextureLevels(&renderManager_, data, numLevels, TextureCallback()); + tex->UpdateTextureLevels(&renderManager_, data, numLevels, initDataCallback); } DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) { diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index b02911e56b..e306d44875 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -335,7 +335,9 @@ struct DescriptorSetKey { class VKTexture : public Texture { public: VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc) - : vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) {} + : vulkan_(vulkan), mipLevels_(desc.mipLevels) { + format_ = desc.format; + } bool Create(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc); void Update(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels); @@ -376,8 +378,6 @@ private: VulkanTexture *vkTex_ = nullptr; int mipLevels_ = 0; - - DataFormat format_ = DataFormat::UNDEFINED; }; class VKFramebuffer; @@ -428,7 +428,7 @@ public: Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; - void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override; + void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override; @@ -788,7 +788,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const Te UpdateInternal(cmd, pushBuffer, desc.initData.data(), desc.initDataCallback, (int)desc.initData.size()); // Generate the rest of the mips automatically. if (desc.initData.size() < mipLevels_) { - vkTex_->GenerateMips(cmd, desc.initData.size(), false); + vkTex_->GenerateMips(cmd, (int)desc.initData.size(), false); layout = VK_IMAGE_LAYOUT_GENERAL; } } @@ -1362,7 +1362,7 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) { } } -void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { +void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { VkCommandBuffer initCmd = renderManager_.GetInitCmd(); if (!push_ || !initCmd) { // Too early! Fail. @@ -1373,7 +1373,7 @@ void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int VKTexture *tex = (VKTexture *)texture; _dbg_assert_(numLevels <= tex->NumLevels()); - tex->Update(initCmd, push_, data, Draw::TextureCallback(), numLevels); + tex->Update(initCmd, push_, data, initDataCallback, numLevels); } static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) { diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index 7f7f905067..04a937d8eb 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -132,6 +132,7 @@ bool RefCountedObject::Release() { return true; } } else { + // No point in printing the name here if the object has already been free-d, it'll be corrupt and dangerous to print. _dbg_assert_msg_(false, "Refcount (%d) invalid for object %p - corrupt?", refcount_.load(), this); } return false; @@ -139,11 +140,10 @@ bool RefCountedObject::Release() { bool RefCountedObject::ReleaseAssertLast() { bool released = Release(); - _dbg_assert_msg_(released, "RefCountedObject: Expected to be the last reference, but isn't!"); + _dbg_assert_msg_(released, "RefCountedObject: Expected to be the last reference, but isn't! (%s)", name_); return released; } - // ================================== PIXEL/FRAGMENT SHADERS // The Vulkan ones can be re-used with modern GL later if desired, as they're just GLSL. diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 8ebbb44328..77b5582479 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -466,9 +466,11 @@ public: int Width() { return width_; } int Height() { return height_; } int Depth() { return depth_; } + DataFormat Format() { return format_; } protected: int width_ = -1, height_ = -1, depth_ = -1; + DataFormat format_ = DataFormat::UNDEFINED; }; struct BindingDesc { @@ -734,7 +736,7 @@ public: // Used to optimize DrawPixels by re-using previously allocated temp textures. // Do not try to update a texture that might be used by an in-flight command buffer! In OpenGL and D3D, this will cause stalls // while in Vulkan this might cause various strangeness like image corruption. - virtual void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) = 0; + virtual void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) = 0; virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) = 0; virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) = 0; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index b80d47d40b..914d6780f8 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1217,7 +1217,6 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); gpuStats.numUploads++; - pixelsTex->Release(); draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE); gstate_c.Dirty(DIRTY_ALL_RENDER_STATE); @@ -1401,11 +1400,26 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G return true; }; + Draw::DataFormat texFormat = srcPixelFormat == GE_FORMAT_DEPTH16 ? depthFormat : preferredPixelsFormat_; + + // Look for a matching texture we can re-use. + for (auto &iter : drawPixelsCache_) { + if (iter.frameNumber > gpuStats.numFlips - 3 || iter.tex->Width() != width || iter.tex->Height() != height || iter.tex->Format() != texFormat) { + continue; + } + + // OK, current one seems good, let's use it (and mark it used). + gpuStats.numDrawPixels++; + draw_->UpdateTextureLevels(iter.tex, &srcPixels, generateTexture, 1); + iter.frameNumber = gpuStats.numFlips; + return iter.tex; + } + // Note: For depth, we create an R16_UNORM texture, that'll be just fine for uploading depth through a shader, // and likely more efficient. Draw::TextureDesc desc{ Draw::TextureType::LINEAR2D, - srcPixelFormat == GE_FORMAT_DEPTH16 ? depthFormat : preferredPixelsFormat_, + texFormat, width, height, 1, @@ -1424,6 +1438,12 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G ERROR_LOG(G3D, "Failed to create DrawPixels texture"); } gpuStats.numDrawPixels++; + gpuStats.numTexturesDecoded++; // Separate stat for this later? + + INFO_LOG(G3D, "Creating drawPixelsCache texture: %dx%d", tex->Width(), tex->Height()); + + DrawPixelsEntry entry{ tex, gpuStats.numFlips }; + drawPixelsCache_.push_back(entry); return tex; } @@ -1450,7 +1470,6 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int presentation_->UpdateUniforms(textureCache_->VideoIsPlaying()); presentation_->SourceTexture(pixelsTex, 512, 272); presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1); - pixelsTex->Release(); // PresentationCommon sets all kinds of state, we can't rely on anything. gstate_c.Dirty(DIRTY_ALL); @@ -1672,6 +1691,20 @@ void FramebufferManagerCommon::DecimateFBOs() { bvfbs_.erase(bvfbs_.begin() + i--); } } + + // And DrawPixels cached textures. + + for (auto it = drawPixelsCache_.begin(); it != drawPixelsCache_.end(); ) { + int age = gpuStats.numFlips - it->frameNumber; + if (age > 10) { + INFO_LOG(G3D, "Releasing drawPixelsCache texture: %dx%d", it->tex->Width(), it->tex->Height()); + it->tex->Release(); + it->tex = nullptr; + it = drawPixelsCache_.erase(it); + } else { + ++it; + } + } } // Requires width/height to be set already. @@ -2604,10 +2637,15 @@ void FramebufferManagerCommon::DestroyAllFBOs() { } tempFBOs_.clear(); - for (auto iter : fbosToDelete_) { + for (auto &iter : fbosToDelete_) { iter->Release(); } fbosToDelete_.clear(); + + for (auto &iter : drawPixelsCache_) { + iter.tex->Release(); + } + drawPixelsCache_.clear(); } static const char *TempFBOReasonToString(TempFBO reason) { diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 0c23a7e41e..5d96e838bf 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -267,6 +267,11 @@ namespace Draw { class DrawContext; } +struct DrawPixelsEntry { + Draw::Texture *tex; + int frameNumber; +}; + struct GPUDebugBuffer; class DrawEngineCommon; class PresentationCommon; @@ -571,6 +576,8 @@ protected: std::vector vfbs_; std::vector bvfbs_; // blitting framebuffers (for download) + std::vector drawPixelsCache_; + bool gameUsesSequentialCopies_ = false; // Sampled in BeginFrame/UpdateSize for safety. diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 6595ddfeec..33861b4c72 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -581,20 +581,23 @@ Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, S } void PresentationCommon::SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight) { + // AddRef before release and assign in case it's the same. + texture->AddRef(); + DoRelease(srcTexture_); DoRelease(srcFramebuffer_); - texture->AddRef(); srcTexture_ = texture; srcWidth_ = bufferWidth; srcHeight_ = bufferHeight; } void PresentationCommon::SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight) { + fb->AddRef(); + DoRelease(srcTexture_); DoRelease(srcFramebuffer_); - fb->AddRef(); srcFramebuffer_ = fb; srcWidth_ = bufferWidth; srcHeight_ = bufferHeight; diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp index 00e81b19d1..e146a01110 100644 --- a/GPU/Common/StencilCommon.cpp +++ b/GPU/Common/StencilCommon.cpp @@ -354,7 +354,6 @@ bool FramebufferManagerCommon::PerformWriteStencilFromMemory(u32 addr, int size, draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST, "WriteStencilFromMemory_Blit"); RebindFramebuffer("RebindFramebuffer - Stencil"); } - tex->Release(); draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE); gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);