From 0b1abf1de90fba6b9746e1d724ee5dd22f2b98b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 May 2023 12:17:28 +0200 Subject: [PATCH 1/5] Add untested UpdateTextureLevels functionality to three of the four backends --- Common/GPU/D3D11/thin3d_d3d11.cpp | 6 ++ Common/GPU/D3D9/thin3d_d3d9.cpp | 36 +++++++---- Common/GPU/OpenGL/thin3d_gl.cpp | 33 +++++++--- Common/GPU/Vulkan/thin3d_vulkan.cpp | 94 ++++++++++++++++++++--------- Common/GPU/thin3d.h | 5 ++ 5 files changed, 123 insertions(+), 51 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 4a90c318c1f7..a3b2b363007c 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -92,6 +92,7 @@ class D3D11DrawContext : public DrawContext { 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 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; @@ -943,6 +944,11 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { return tex; } +void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { + D3D11Texture *tex = (D3D11Texture *)texture; + // TODO +} + ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { if (language != ShaderLanguage::HLSL_D3D11) { ERROR_LOG(G3D, "Unsupported shader language"); diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 32152c69889e..555f64a8e7ce 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -308,9 +308,10 @@ class D3D9Texture : public Texture { return nullptr; } } + void UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback); private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback); + void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback); bool Create(const TextureDesc &desc); LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; @@ -374,27 +375,31 @@ bool D3D9Texture::Create(const TextureDesc &desc) { break; } if (FAILED(hr)) { - ERROR_LOG(G3D, "Texture creation failed"); + ERROR_LOG(G3D, "D3D9 Texture creation failed"); return false; } if (desc.initData.size()) { // In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be // automatically generated. - int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size(); - int w = desc.width; - int h = desc.height; - int d = desc.depth; - for (int i = 0; i < maxLevel; i++) { - SetImageData(0, 0, 0, w, h, d, i, 0, desc.initData[i], desc.initDataCallback); - w = (w + 1) / 2; - h = (h + 1) / 2; - d = (d + 1) / 2; - } + int numLevels = desc.generateMips ? 1 : (int)desc.initData.size(); + UpdateTextureLevels(desc.initData.data(), numLevels, desc.initDataCallback); } return true; } +void D3D9Texture::UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) { + int w = width_; + int h = height_; + int d = depth_; + for (int i = 0; i < numLevels; i++) { + SetImageData(0, 0, 0, w, h, d, i, 0, data[i], initDataCallback); + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; + } +} + // Just switches R and G. inline uint32_t Shuffle8888(uint32_t x) { return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000); @@ -532,6 +537,7 @@ class D3D9Context : public DrawContext { 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 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 @@ -934,6 +940,12 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) { return tex; } +void D3D9Context::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { + D3D9Texture *tex = (D3D9Texture *)texture; + tex->UpdateTextureLevels(data, numLevels, TextureCallback()); +} + + void D3D9Context::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) { _assert_(start + count <= MAX_BOUND_TEXTURES); for (int i = start; i < start + count; i++) { diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index d4054987f006..4d6f27c34a5a 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -371,6 +371,7 @@ class OpenGLContext : public DrawContext { 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 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; @@ -853,8 +854,10 @@ class OpenGLTexture : public Texture { return tex_; } + void UpdateTextureLevels(GLRenderManager *render, const uint8_t *const *data, int numLevels, TextureCallback initDataCallback); + private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback); + void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback); GLRenderManager *render_; GLRTexture *tex_; @@ -862,16 +865,19 @@ class OpenGLTexture : public Texture { DataFormat format_; TextureType type_; int mipLevels_; - bool generatedMips_; + bool generateMips_; // Generate mips requested + bool generatedMips_; // Has generated mips }; OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) { generatedMips_ = false; + generateMips_ = desc.generateMips; width_ = desc.width; height_ = desc.height; depth_ = desc.depth; format_ = desc.format; type_ = desc.type; + GLenum target = TypeToTarget(desc.type); tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels); @@ -879,21 +885,25 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : if (desc.initData.empty()) return; + UpdateTextureLevels(render, desc.initData.data(), (int)desc.initData.size(), desc.initDataCallback); +} + +void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) { int level = 0; int width = width_; int height = height_; int depth = depth_; - for (auto data : desc.initData) { - SetImageData(0, 0, 0, width, height, depth, level, 0, data, desc.initDataCallback); + for (int i = 0; i < numLevels; i++) { + SetImageData(0, 0, 0, width, height, depth, level, 0, data[i], initDataCallback); width = (width + 1) / 2; height = (height + 1) / 2; depth = (depth + 1) / 2; level++; } - mipLevels_ = desc.generateMips ? desc.mipLevels : level; + mipLevels_ = generateMips_ ? mipLevels_ : level; bool genMips = false; - if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) { + if (numLevels < mipLevels_ && generateMips_) { // Assumes the texture is bound for editing genMips = true; generatedMips_ = true; @@ -923,7 +933,7 @@ class OpenGLFramebuffer : public Framebuffer { GLRFramebuffer *framebuffer_ = nullptr; }; -void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) { +void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback) { if ((width != width_ || height != height_ || depth != depth_) && level == 0) { // When switching to texStorage we need to handle this correctly. width_ = width; @@ -939,8 +949,8 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)]; bool texDataPopulated = false; - if (callback) { - texDataPopulated = callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment); + if (initDataCallback) { + texDataPopulated = initDataCallback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment); } if (texDataPopulated) { if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { @@ -1021,6 +1031,11 @@ Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { return new OpenGLTexture(&renderManager_, desc); } +void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { + OpenGLTexture *tex = (OpenGLTexture *)texture; + tex->UpdateTextureLevels(&renderManager_, data, numLevels, TextureCallback()); +} + DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) { OpenGLDepthStencilState *ds = new OpenGLDepthStencilState(); ds->depthTestEnabled = desc.depthTestEnabled; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index f199af699e06..b02911e56ba5 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -337,6 +337,7 @@ class VKTexture : public Texture { VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc) : 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); ~VKTexture() { Destroy(); @@ -356,7 +357,13 @@ class VKTexture : public Texture { return VK_NULL_HANDLE; // This would be bad. } + int NumLevels() const { + return mipLevels_; + } + private: + void UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels); + void Destroy() { if (vkTex_) { vkTex_->Destroy(); @@ -421,6 +428,7 @@ class VKContext : public DrawContext { 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 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; @@ -748,14 +756,14 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureDesc &desc) { +bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc) { // Zero-sized textures not allowed. _assert_(desc.width * desc.height * desc.depth > 0); // remember to set depth to 1! if (desc.width * desc.height * desc.depth <= 0) { ERROR_LOG(G3D, "Bad texture dimensions %dx%dx%d", desc.width, desc.height, desc.depth); return false; } - _assert_(push); + _dbg_assert_(pushBuffer); format_ = desc.format; mipLevels_ = desc.mipLevels; width_ = desc.width; @@ -763,8 +771,6 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD depth_ = desc.depth; vkTex_ = new VulkanTexture(vulkan_, desc.tag); VkFormat vulkanFormat = DataFormatToVulkan(format_); - int bpp = GetBpp(vulkanFormat); - int bytesPerPixel = bpp / 8; int usageBits = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; if (mipLevels_ > (int)desc.initData.size()) { // Gonna have to generate some, which requires TRANSFER_SRC @@ -779,33 +785,10 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD } VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; if (desc.initData.size()) { - int w = width_; - int h = height_; - int d = depth_; - int i; - for (i = 0; i < (int)desc.initData.size(); i++) { - uint32_t offset; - VkBuffer buf; - size_t size = w * h * d * bytesPerPixel; - uint8_t *dest = (uint8_t *)push->Allocate(size, 16, &buf, &offset); - if (desc.initDataCallback) { - _assert_(dest != nullptr); - if (!desc.initDataCallback(dest, desc.initData[i], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel)) { - memcpy(dest, desc.initData[i], size); - } - } else { - memcpy(dest, desc.initData[i], size); - } - TextureCopyBatch batch; - vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w); - vkTex_->FinishCopyBatch(cmd, &batch); - w = (w + 1) / 2; - h = (h + 1) / 2; - d = (d + 1) / 2; - } + UpdateInternal(cmd, pushBuffer, desc.initData.data(), desc.initDataCallback, (int)desc.initData.size()); // Generate the rest of the mips automatically. - if (i < mipLevels_) { - vkTex_->GenerateMips(cmd, i, false); + if (desc.initData.size() < mipLevels_) { + vkTex_->GenerateMips(cmd, desc.initData.size(), false); layout = VK_IMAGE_LAYOUT_GENERAL; } } @@ -813,6 +796,43 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD return true; } +void VKTexture::Update(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) { + // Before we can use UpdateInternal, we need to transition the image to the same state as after CreateDirect, + // making it ready for writing. + + UpdateInternal(cmd, pushBuffer, data, initDataCallback, numLevels); +} + +void VKTexture::UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) { + int w = width_; + int h = height_; + int d = depth_; + int i; + VkFormat vulkanFormat = DataFormatToVulkan(format_); + int bpp = GetBpp(vulkanFormat); + int bytesPerPixel = bpp / 8; + TextureCopyBatch batch; + for (i = 0; i < numLevels; i++) { + uint32_t offset; + VkBuffer buf; + size_t size = w * h * d * bytesPerPixel; + uint8_t *dest = (uint8_t *)pushBuffer->Allocate(size, 16, &buf, &offset); + if (initDataCallback) { + _assert_(dest != nullptr); + if (!initDataCallback(dest, data[i], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel)) { + memcpy(dest, data[i], size); + } + } else { + memcpy(dest, data[i], size); + } + vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w); + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; + } + vkTex_->FinishCopyBatch(cmd, &batch); +} + static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) { switch (fmt) { case VK_FORMAT_D24_UNORM_S8_UINT: @@ -1342,6 +1362,20 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) { } } +void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) { + VkCommandBuffer initCmd = renderManager_.GetInitCmd(); + if (!push_ || !initCmd) { + // Too early! Fail. + ERROR_LOG(G3D, "Can't create textures before the first frame has started."); + return; + } + + VKTexture *tex = (VKTexture *)texture; + + _dbg_assert_(numLevels <= tex->NumLevels()); + tex->Update(initCmd, push_, data, Draw::TextureCallback(), numLevels); +} + static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) { dest.compareOp = compToVK[(int)src.compareOp]; dest.failOp = stencilOpToVK[(int)src.failOp]; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index b32fbb3cbaba..8ebbb4432890 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -731,6 +731,11 @@ class DrawContext { // Copies data from the CPU over into the buffer, at a specific offset. This does not change the size of the buffer and cannot write outside it. virtual void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) = 0; + // 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 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; From 7c4b9bac902e949785bf698585d3579ffd16b5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 May 2023 13:08:51 +0200 Subject: [PATCH 2/5] Cache textures created by MakePixelsTexture and reuse where appropriate. --- Common/GPU/D3D11/thin3d_d3d11.cpp | 5 +-- Common/GPU/D3D9/thin3d_d3d9.cpp | 7 ++-- Common/GPU/OpenGL/thin3d_gl.cpp | 7 ++-- Common/GPU/Vulkan/thin3d_vulkan.cpp | 14 ++++---- Common/GPU/thin3d.cpp | 4 +-- Common/GPU/thin3d.h | 4 ++- GPU/Common/FramebufferManagerCommon.cpp | 46 ++++++++++++++++++++++--- GPU/Common/FramebufferManagerCommon.h | 7 ++++ GPU/Common/PresentationCommon.cpp | 7 ++-- GPU/Common/StencilCommon.cpp | 1 - 10 files changed, 75 insertions(+), 27 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index a3b2b363007c..7f4ed070d89f 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -92,7 +92,7 @@ class D3D11DrawContext : public DrawContext { 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 @@ class D3D11Texture : public Texture { 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 555f64a8e7ce..5389eac6febe 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -316,7 +316,6 @@ class D3D9Texture : public Texture { LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; TextureType type_; - DataFormat format_; D3DFORMAT d3dfmt_; LPDIRECT3DTEXTURE9 tex_ = nullptr; LPDIRECT3DVOLUMETEXTURE9 volTex_ = nullptr; @@ -537,7 +536,7 @@ class D3D9Context : public DrawContext { 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 4d6f27c34a5a..3f29d54207c5 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -371,7 +371,7 @@ class OpenGLContext : public DrawContext { 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 @@ class OpenGLTexture : public Texture { 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 b02911e56ba5..e306d44875f1 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 @@ class VKTexture : public Texture { VulkanTexture *vkTex_ = nullptr; int mipLevels_ = 0; - - DataFormat format_ = DataFormat::UNDEFINED; }; class VKFramebuffer; @@ -428,7 +428,7 @@ class VKContext : public DrawContext { 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 7f7f905067e7..04a937d8ebdf 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 8ebbb4432890..77b5582479cb 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -466,9 +466,11 @@ class Texture : public RefCountedObject { 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 @@ class DrawContext { // 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 b80d47d40b50..914d6780f816 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 0c23a7e41e9c..5d96e838bf21 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 @@ class FramebufferManagerCommon { 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 6595ddfeec63..33861b4c7285 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 00e81b19d1f2..e146a01110af 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); From 198f9756b8aaba915b89273013c7dfb60c00b295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 May 2023 14:35:22 +0200 Subject: [PATCH 3/5] D3D11 texture upload: Refactor for easier changes --- Common/GPU/D3D11/thin3d_d3d11.cpp | 106 ++++++++++++++++-------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 7f4ed070d89f..8361c103c882 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -799,33 +799,22 @@ class D3D11Texture : public Texture { format_ = desc.format; } ~D3D11Texture() { - if (tex) - tex->Release(); - if (stagingTex) - stagingTex->Release(); - if (view) - view->Release(); + if (tex_) + tex_->Release(); + if (stagingTex_) + stagingTex_->Release(); + if (view_) + view_->Release(); } - ID3D11Texture2D *tex = nullptr; - ID3D11Texture2D *stagingTex = nullptr; - ID3D11ShaderResourceView *view = nullptr; -}; - -Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { - if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) { - // D3D11 does not support this format as a texture format. - return nullptr; - } + bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips); - D3D11Texture *tex = new D3D11Texture(desc); - - bool generateMips = desc.generateMips; - if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) { - // D3D11 does not support autogenerating mipmaps for this format. - generateMips = false; - } + ID3D11Texture2D *tex_ = nullptr; + ID3D11Texture2D *stagingTex_ = nullptr; + ID3D11ShaderResourceView *view_ = nullptr; +}; +bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips) { D3D11_TEXTURE2D_DESC descColor{}; descColor.Width = desc.width; descColor.Height = desc.height; @@ -841,10 +830,11 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { descColor.MiscFlags = 0; descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - HRESULT hr = device_->CreateTexture2D(&descColor, nullptr, &tex->stagingTex); + HRESULT hr = device->CreateTexture2D(&descColor, nullptr, &stagingTex_); if (!SUCCEEDED(hr)) { - delete tex; - return nullptr; + stagingTex_->Release(); + stagingTex_ = nullptr; + return false; } } @@ -872,21 +862,22 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { initDataParam = initData; } - HRESULT hr = device_->CreateTexture2D(&descColor, initDataParam, &tex->tex); + HRESULT hr = device->CreateTexture2D(&descColor, initDataParam, &tex_); if (!SUCCEEDED(hr)) { - delete tex; - return nullptr; + tex_ = nullptr; + return false; } - hr = device_->CreateShaderResourceView(tex->tex, nullptr, &tex->view); + hr = device->CreateShaderResourceView(tex_, nullptr, &view_); if (!SUCCEEDED(hr)) { - delete tex; - return nullptr; + return false; } auto populateLevelCallback = [&](int level, int w, int h, int d) { D3D11_MAPPED_SUBRESOURCE mapped; - hr = context_->Map(tex->stagingTex, level, D3D11_MAP_WRITE, 0, &mapped); + hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped); if (!SUCCEEDED(hr)) { + tex_->Release(); + tex_ = nullptr; return false; } @@ -900,25 +891,25 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { } } } - context_->Unmap(tex->stagingTex, level); + context->Unmap(stagingTex_, level); return true; }; if (generateMips && desc.initData.size() >= 1) { if (desc.initDataCallback) { if (!populateLevelCallback(0, desc.width, desc.height, desc.depth)) { - delete tex; - return nullptr; + tex_->Release(); + return false; } - context_->CopyResource(tex->stagingTex, tex->stagingTex); - tex->stagingTex->Release(); - tex->stagingTex = nullptr; + context->CopyResource(tex_, stagingTex_); + stagingTex_->Release(); + stagingTex_ = nullptr; } else { uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format); - context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], byteStride, 0); + context->UpdateSubresource(tex_, 0, nullptr, desc.initData[0], byteStride, 0); } - context_->GenerateMips(tex->view); + context->GenerateMips(view_); } else if (desc.initDataCallback) { int w = desc.width; int h = desc.height; @@ -926,8 +917,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { for (int i = 0; i < (int)desc.initData.size(); i++) { if (!populateLevelCallback(i, desc.width, desc.height, desc.depth)) { if (i == 0) { - delete tex; - return nullptr; + return false; } else { break; } @@ -938,10 +928,30 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { d = (d + 1) / 2; } - context_->CopyResource(tex->tex, tex->stagingTex); - tex->stagingTex->Release(); - tex->stagingTex = nullptr; + context->CopyResource(tex_, stagingTex_); + stagingTex_->Release(); + stagingTex_ = nullptr; + } + return true; +} + +Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { + if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) { + // D3D11 does not support this format as a texture format. + return nullptr; + } + + D3D11Texture *tex = new D3D11Texture(desc); + bool generateMips = desc.generateMips; + if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) { + // D3D11 does not support autogenerating mipmaps for this format. + generateMips = false; } + if (!tex->Create(context_, device_, desc, generateMips)) { + tex->Release(); + return nullptr; + } + return tex; } @@ -1418,7 +1428,7 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, Te _assert_(start + count <= ARRAY_SIZE(views)); for (int i = 0; i < count; i++) { D3D11Texture *tex = (D3D11Texture *)textures[i]; - views[i] = tex ? tex->view : nullptr; + views[i] = tex ? tex->view_ : nullptr; } context_->PSSetShaderResources(start, count, views); } @@ -1778,7 +1788,7 @@ uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) { case NativeObject::FEATURE_LEVEL: return (uint64_t)(uintptr_t)featureLevel_; case NativeObject::TEXTURE_VIEW: - return (uint64_t)(((D3D11Texture *)srcObject)->view); + return (uint64_t)(((D3D11Texture *)srcObject)->view_); default: return 0; } From 3a9ce528e7e20acc41e67595482e1d01893b264d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 May 2023 14:43:54 +0200 Subject: [PATCH 4/5] D3D11: Break out CreateStagingTexture --- Common/GPU/D3D11/thin3d_d3d11.cpp | 54 ++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 8361c103c882..7ae3adaf0cdd 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -797,6 +797,7 @@ class D3D11Texture : public Texture { height_ = desc.height; depth_ = desc.depth; format_ = desc.format; + mipLevels_ = desc.mipLevels; } ~D3D11Texture() { if (tex_) @@ -809,11 +810,39 @@ class D3D11Texture : public Texture { bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips); + bool CreateStagingTexture(ID3D11Device *device); + ID3D11Texture2D *tex_ = nullptr; ID3D11Texture2D *stagingTex_ = nullptr; ID3D11ShaderResourceView *view_ = nullptr; + int mipLevels_ = 0; }; +bool D3D11Texture::CreateStagingTexture(ID3D11Device *device) { + if (stagingTex_) + return true; + D3D11_TEXTURE2D_DESC descColor{}; + descColor.Width = width_; + descColor.Height = height_; + descColor.MipLevels = mipLevels_; + descColor.ArraySize = 1; + descColor.Format = dataFormatToD3D11(format_); + descColor.SampleDesc.Count = 1; + descColor.SampleDesc.Quality = 0; + descColor.Usage = D3D11_USAGE_STAGING; + descColor.BindFlags = 0; + descColor.MiscFlags = 0; + descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + HRESULT hr = device->CreateTexture2D(&descColor, nullptr, &stagingTex_); + if (!SUCCEEDED(hr)) { + stagingTex_->Release(); + stagingTex_ = nullptr; + return false; + } + return true; +} + bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips) { D3D11_TEXTURE2D_DESC descColor{}; descColor.Width = desc.width; @@ -823,26 +852,16 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co descColor.Format = dataFormatToD3D11(desc.format); descColor.SampleDesc.Count = 1; descColor.SampleDesc.Quality = 0; - - if (desc.initDataCallback) { - descColor.Usage = D3D11_USAGE_STAGING; - descColor.BindFlags = 0; - descColor.MiscFlags = 0; - descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - - HRESULT hr = device->CreateTexture2D(&descColor, nullptr, &stagingTex_); - if (!SUCCEEDED(hr)) { - stagingTex_->Release(); - stagingTex_ = nullptr; - return false; - } - } - descColor.Usage = D3D11_USAGE_DEFAULT; descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE; descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0; descColor.CPUAccessFlags = 0; + // Make sure we have a staging texture if we'll need it. + if (desc.initDataCallback && !CreateStagingTexture(device)) { + return false; + } + D3D11_SUBRESOURCE_DATA *initDataParam = nullptr; D3D11_SUBRESOURCE_DATA initData[12]{}; std::vector initDataBuffer[12]; @@ -957,7 +976,10 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { D3D11Texture *tex = (D3D11Texture *)texture; - // TODO + + // If no staging texture, let's create one. + + } ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { From 25662a7f6c8f64ae03387ea5f03dfd8283f78ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 May 2023 14:54:19 +0200 Subject: [PATCH 5/5] Implement UpdateTextureLevels for D3D11 --- Common/GPU/D3D11/thin3d_d3d11.cpp | 89 ++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 7ae3adaf0cdd..443367b1aa70 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -811,6 +811,12 @@ class D3D11Texture : public Texture { bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips); bool CreateStagingTexture(ID3D11Device *device); + void UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t *const *data, TextureCallback initDataCallback, int numLevels); + + ID3D11ShaderResourceView *View() { return view_; } + +private: + bool FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback); ID3D11Texture2D *tex_ = nullptr; ID3D11Texture2D *stagingTex_ = nullptr; @@ -818,6 +824,29 @@ class D3D11Texture : public Texture { int mipLevels_ = 0; }; +bool D3D11Texture::FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback) { + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped); + if (!SUCCEEDED(hr)) { + tex_->Release(); + tex_ = nullptr; + return false; + } + + if (!initDataCallback((uint8_t *)mapped.pData, data[level], w, h, d, mapped.RowPitch, mapped.DepthPitch)) { + for (int s = 0; s < d; ++s) { + for (int y = 0; y < h; ++y) { + void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y; + uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(format_); + const void *src = data[level] + byteStride * (y + h * d); + memcpy(dest, src, byteStride); + } + } + } + context->Unmap(stagingTex_, level); + return true; +} + bool D3D11Texture::CreateStagingTexture(ID3D11Device *device) { if (stagingTex_) return true; @@ -891,32 +920,9 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co return false; } - auto populateLevelCallback = [&](int level, int w, int h, int d) { - D3D11_MAPPED_SUBRESOURCE mapped; - hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped); - if (!SUCCEEDED(hr)) { - tex_->Release(); - tex_ = nullptr; - return false; - } - - if (!desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[level], w, h, d, mapped.RowPitch, mapped.DepthPitch)) { - for (int s = 0; s < d; ++s) { - for (int y = 0; y < h; ++y) { - void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y; - uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(desc.format); - const void *src = desc.initData[level] + byteStride * (y + h * d); - memcpy(dest, src, byteStride); - } - } - } - context->Unmap(stagingTex_, level); - return true; - }; - if (generateMips && desc.initData.size() >= 1) { if (desc.initDataCallback) { - if (!populateLevelCallback(0, desc.width, desc.height, desc.depth)) { + if (!FillLevel(context, 0, desc.width, desc.height, desc.depth, desc.initData.data(), desc.initDataCallback)) { tex_->Release(); return false; } @@ -934,7 +940,7 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co int h = desc.height; int d = desc.depth; for (int i = 0; i < (int)desc.initData.size(); i++) { - if (!populateLevelCallback(i, desc.width, desc.height, desc.depth)) { + if (!FillLevel(context, i, w, h, d, desc.initData.data(), desc.initDataCallback)) { if (i == 0) { return false; } else { @@ -954,6 +960,29 @@ bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, co return true; } +void D3D11Texture::UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t * const*data, TextureCallback initDataCallback, int numLevels) { + if (!CreateStagingTexture(device)) { + return; + } + + int w = width_; + int h = height_; + int d = depth_; + for (int i = 0; i < (int)numLevels; i++) { + if (!FillLevel(context, i, w, h, d, data, initDataCallback)) { + break; + } + + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; + } + + context->CopyResource(tex_, stagingTex_); + stagingTex_->Release(); + stagingTex_ = nullptr; +} + Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) { // D3D11 does not support this format as a texture format. @@ -974,12 +1003,10 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { return tex; } + void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) { D3D11Texture *tex = (D3D11Texture *)texture; - - // If no staging texture, let's create one. - - + tex->UpdateTextureLevels(context_, device_, texture, data, initDataCallback, numLevels); } ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) { @@ -1450,7 +1477,7 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, Te _assert_(start + count <= ARRAY_SIZE(views)); for (int i = 0; i < count; i++) { D3D11Texture *tex = (D3D11Texture *)textures[i]; - views[i] = tex ? tex->view_ : nullptr; + views[i] = tex ? tex->View() : nullptr; } context_->PSSetShaderResources(start, count, views); } @@ -1810,7 +1837,7 @@ uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) { case NativeObject::FEATURE_LEVEL: return (uint64_t)(uintptr_t)featureLevel_; case NativeObject::TEXTURE_VIEW: - return (uint64_t)(((D3D11Texture *)srcObject)->view_); + return (uint64_t)(((D3D11Texture *)srcObject)->View()); default: return 0; }