From d8db55f8123a4a0871b1290fec2e5d0caae01bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Thu, 20 Aug 2026 05:39:04 +0000 Subject: [PATCH] D3D11, D3D12, GL, VK: Resolve when blitting from sampleable MSAA surface. (#3907) --- src/renderer_d3d11.cpp | 125 +++++++++++++++++++++++++++++++++------ src/renderer_d3d11.h | 1 + src/renderer_d3d12.cpp | 111 ++++++++++++++++++++++++---------- src/renderer_d3d12.h | 1 + src/renderer_gl.cpp | 64 +++++++++++++++++++- src/renderer_vk.cpp | 131 ++++++++++++++++++++++++++--------------- 6 files changed, 335 insertions(+), 98 deletions(-) diff --git a/src/renderer_d3d11.cpp b/src/renderer_d3d11.cpp index c0d7c74ce..9ac9a75a9 100644 --- a/src/renderer_d3d11.cpp +++ b/src/renderer_d3d11.cpp @@ -5421,6 +5421,15 @@ namespace bgfx { namespace d3d11 return handle; } + bool TextureD3D11::isMsaaSurface() const + { + const uint32_t msaaQuality = bx::satSub(uint32_t( (m_flags&BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT), 1u); + return true + && 1 < s_msaa[msaaQuality].Count + && 0 != (m_flags & BGFX_TEXTURE_MSAA_SAMPLE) + ; + } + DXGI_FORMAT TextureD3D11::getSrvFormat() const { if (bimg::isDepth(bimg::TextureFormat::Enum(m_textureFormat) ) ) @@ -6070,17 +6079,96 @@ namespace bgfx { namespace d3d11 const TextureD3D11& src = m_textures[blit.m_src.idx]; const TextureD3D11& dst = m_textures[blit.m_dst.idx]; + if ( src.isMsaaSurface() + && !dst.isMsaaSurface() + && TextureD3D11::Texture3D != src.m_type) + { + const DXGI_FORMAT resolveFormat = bimg::isDepth(bimg::TextureFormat::Enum(src.m_textureFormat) ) + ? s_textureFormat[src.m_textureFormat].m_fmt + : src.getSrvFormat() + ; + + const uint32_t srcSubresource = blit.m_srcZ*src.m_numMips + blit.m_srcMip; + const uint32_t dstSubresource = blit.m_dstZ*dst.m_numMips + blit.m_dstMip; + + if (0 != (dst.m_flags & BGFX_TEXTURE_READ_BACK) ) + { + const D3D11_TEXTURE2D_DESC desc = + { + .Width = bx::max(1, src.m_width >> blit.m_srcMip), + .Height = bx::max(1, src.m_height >> blit.m_srcMip), + .MipLevels = 1, + .ArraySize = 1, + .Format = resolveFormat, + .SampleDesc = { .Count = 1, .Quality = 0 }, + .Usage = D3D11_USAGE_DEFAULT, + .BindFlags = D3D11_BIND_SHADER_RESOURCE, + .CPUAccessFlags = 0, + .MiscFlags = 0, + }; + + ID3D11Texture2D* scratch; + DX_CHECK(m_device->CreateTexture2D(&desc, NULL, &scratch) ); + + deviceCtx->ResolveSubresource( + scratch + , 0 + , src.m_ptr + , srcSubresource + , resolveFormat + ); + + const D3D11_BOX box = + { + .left = blit.m_srcX, + .top = blit.m_srcY, + .front = 0, + .right = uint32_t(blit.m_srcX) + blit.m_width, + .bottom = uint32_t(blit.m_srcY) + blit.m_height, + .back = 1, + }; + + deviceCtx->CopySubresourceRegion( + dst.m_ptr + , dstSubresource + , blit.m_dstX + , blit.m_dstY + , 0 + , scratch + , 0 + , &box + ); + + DX_RELEASE(scratch, 0); + } + else + { + deviceCtx->ResolveSubresource( + dst.m_ptr + , dstSubresource + , src.m_ptr + , srcSubresource + , resolveFormat + ); + } + + continue; + } + if (TextureD3D11::Texture3D == src.m_type) { - D3D11_BOX box; - box.left = blit.m_srcX; - box.top = blit.m_srcY; - box.front = blit.m_srcZ; - box.right = blit.m_srcX + blit.m_width; - box.bottom = blit.m_srcY + blit.m_height; - box.back = blit.m_srcZ + bx::max(1, blit.m_depth); + const D3D11_BOX box = + { + .left = blit.m_srcX, + .top = blit.m_srcY, + .front = blit.m_srcZ, + .right = uint32_t(blit.m_srcX) + blit.m_width, + .bottom = uint32_t(blit.m_srcY) + blit.m_height, + .back = uint32_t(blit.m_srcZ) + bx::max(1, blit.m_depth), + }; - deviceCtx->CopySubresourceRegion(dst.m_ptr + deviceCtx->CopySubresourceRegion( + dst.m_ptr , blit.m_dstMip , blit.m_dstX , blit.m_dstY @@ -6092,24 +6180,27 @@ namespace bgfx { namespace d3d11 } else { - bool depthStencil = bimg::isDepth(bimg::TextureFormat::Enum(src.m_textureFormat) ); + const bool depthStencil = bimg::isDepth(bimg::TextureFormat::Enum(src.m_textureFormat) ); BX_ASSERT(!depthStencil || (blit.m_width == bx::max(1, src.m_width >> blit.m_srcMip) && blit.m_height == bx::max(1, src.m_height >> blit.m_srcMip)) , "When blitting depthstencil surface, source resolution must match destination." ); - D3D11_BOX box; - box.left = blit.m_srcX; - box.top = blit.m_srcY; - box.front = 0; - box.right = blit.m_srcX + blit.m_width; - box.bottom = blit.m_srcY + blit.m_height; - box.back = 1; + const D3D11_BOX box = + { + .left = blit.m_srcX, + .top = blit.m_srcY, + .front = 0, + .right = uint32_t(blit.m_srcX) + blit.m_width, + .bottom = uint32_t(blit.m_srcY) + blit.m_height, + .back = 1, + }; const uint32_t srcZ = blit.m_srcZ; const uint32_t dstZ = blit.m_dstZ; - deviceCtx->CopySubresourceRegion(dst.m_ptr + deviceCtx->CopySubresourceRegion( + dst.m_ptr , dstZ*dst.m_numMips+blit.m_dstMip , blit.m_dstX , blit.m_dstY diff --git a/src/renderer_d3d11.h b/src/renderer_d3d11.h index d035aebf7..f2e87078a 100644 --- a/src/renderer_d3d11.h +++ b/src/renderer_d3d11.h @@ -293,6 +293,7 @@ namespace bgfx { namespace d3d11 void resolve(uint8_t _resolve, uint32_t _layer, uint32_t _numLayers, uint32_t _mip) const; TextureHandle getHandle() const; DXGI_FORMAT getSrvFormat() const; + bool isMsaaSurface() const; union { diff --git a/src/renderer_d3d12.cpp b/src/renderer_d3d12.cpp index 32e3be125..0babab5ac 100644 --- a/src/renderer_d3d12.cpp +++ b/src/renderer_d3d12.cpp @@ -6988,6 +6988,15 @@ namespace bgfx { namespace d3d12 } } + bool TextureD3D12::isMsaaSurface() const + { + const uint32_t msaaQuality = bx::satSub(uint32_t( (m_flags&BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT), 1u); + return true + && 1 < s_msaa[msaaQuality].Count + && 0 != (m_flags & BGFX_TEXTURE_MSAA_SAMPLE) + ; + } + D3D12_RESOURCE_STATES TextureD3D12::setState(ID3D12GraphicsCommandList* _commandList, D3D12_RESOURCE_STATES _state) { if (m_state != _state) @@ -7864,27 +7873,61 @@ namespace bgfx { namespace d3d12 } } + if ( src.isMsaaSurface() + && !dst.isMsaaSurface() + && TextureD3D12::Texture3D != src.m_type + && kInvalidHandle != dstIdx) + { + const TextureFormatInfo& tfi = s_textureFormat[src.m_textureFormat]; + const DXGI_FORMAT resolveFormat = bimg::isDepth(bimg::TextureFormat::Enum(src.m_textureFormat) ) + ? tfi.m_fmt + : (0 != (src.m_flags & BGFX_TEXTURE_SRGB) ? tfi.m_fmtSrgb : tfi.m_fmtSrv) + ; + + src.setState(m_commandList, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); + dst.setState(m_commandList, D3D12_RESOURCE_STATE_RESOLVE_DEST); + + m_commandList->ResolveSubresource( + dst.m_ptr + , blit.m_dstZ*dst.m_numMips + blit.m_dstMip + , src.m_ptr + , blit.m_srcZ*src.m_numMips + blit.m_srcMip + , resolveFormat + ); + + src.setState(m_commandList, D3D12_RESOURCE_STATE_COPY_SOURCE); + dst.setState(m_commandList, D3D12_RESOURCE_STATE_COPY_DEST); + continue; + } + if (TextureD3D12::Texture3D == src.m_type) { - D3D12_BOX box; - box.left = blit.m_srcX; - box.top = blit.m_srcY; - box.front = blit.m_srcZ; - box.right = blit.m_srcX + blit.m_width; - box.bottom = blit.m_srcY + blit.m_height; - box.back = blit.m_srcZ + bx::max(1, blit.m_depth); + const D3D12_BOX box = + { + .left = blit.m_srcX, + .top = blit.m_srcY, + .front = blit.m_srcZ, + .right = uint32_t(blit.m_srcX) + blit.m_width, + .bottom = uint32_t(blit.m_srcY) + blit.m_height, + .back = uint32_t(blit.m_srcZ) + bx::max(1, blit.m_depth), + }; - D3D12_TEXTURE_COPY_LOCATION dstLocation; - dstLocation.pResource = dst.m_ptr; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = blit.m_dstMip; + const D3D12_TEXTURE_COPY_LOCATION dstLocation = + { + .pResource = dst.m_ptr, + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = blit.m_dstMip, + }; - D3D12_TEXTURE_COPY_LOCATION srcLocation; - srcLocation.pResource = src.m_ptr; - srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - srcLocation.SubresourceIndex = blit.m_srcMip; + const D3D12_TEXTURE_COPY_LOCATION srcLocation = + { + .pResource = src.m_ptr, + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = blit.m_srcMip, + }; - m_commandList->CopyTextureRegion(&dstLocation + m_commandList->CopyTextureRegion( + &dstLocation , blit.m_dstX , blit.m_dstY , blit.m_dstZ @@ -7894,13 +7937,15 @@ namespace bgfx { namespace d3d12 } else { - D3D12_BOX box; - box.left = blit.m_srcX; - box.top = blit.m_srcY; - box.front = 0; - box.right = blit.m_srcX + blit.m_width; - box.bottom = blit.m_srcY + blit.m_height; - box.back = 1; + const D3D12_BOX box = + { + .left = blit.m_srcX, + .top = blit.m_srcY, + .front = 0, + .right = uint32_t(blit.m_srcX) + blit.m_width, + .bottom = uint32_t(blit.m_srcY) + blit.m_height, + .back = 1, + }; const uint32_t srcZ = TextureD3D12::TextureCube == src.m_type ? blit.m_srcZ @@ -7911,15 +7956,19 @@ namespace bgfx { namespace d3d12 : bx::min(blit.m_dstZ, (uint16_t)dst.m_numLayers - 1) ; - D3D12_TEXTURE_COPY_LOCATION dstLocation; - dstLocation.pResource = dst.m_ptr; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = dstZ*dst.m_numMips+blit.m_dstMip; + const D3D12_TEXTURE_COPY_LOCATION dstLocation = + { + .pResource = dst.m_ptr, + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = dstZ*dst.m_numMips+blit.m_dstMip, + }; - D3D12_TEXTURE_COPY_LOCATION srcLocation; - srcLocation.pResource = NULL != src.m_singleMsaa ? src.m_singleMsaa : src.m_ptr; - srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - srcLocation.SubresourceIndex = srcZ*src.m_numMips+blit.m_srcMip; + const D3D12_TEXTURE_COPY_LOCATION srcLocation = + { + .pResource = NULL != src.m_singleMsaa ? src.m_singleMsaa : src.m_ptr, + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = srcZ*src.m_numMips+blit.m_srcMip, + }; const bool depthStencil = bimg::isDepth(bimg::TextureFormat::Enum(src.m_textureFormat) ); diff --git a/src/renderer_d3d12.h b/src/renderer_d3d12.h index 4f27e0560..213cbefe3 100644 --- a/src/renderer_d3d12.h +++ b/src/renderer_d3d12.h @@ -378,6 +378,7 @@ namespace bgfx { namespace d3d12 void update(ID3D12GraphicsCommandList* _commandList, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem); void resolve(ID3D12GraphicsCommandList* _commandList, uint8_t _resolve, uint32_t _layer, uint32_t _numLayers, uint32_t _mip); D3D12_RESOURCE_STATES setState(ID3D12GraphicsCommandList* _commandList, D3D12_RESOURCE_STATES _state); + bool isMsaaSurface() const; D3D12_SHADER_RESOURCE_VIEW_DESC m_srvd; D3D12_UNORDERED_ACCESS_VIEW_DESC m_uavd; diff --git a/src/renderer_gl.cpp b/src/renderer_gl.cpp index 1cdf180df..c053fb248 100644 --- a/src/renderer_gl.cpp +++ b/src/renderer_gl.cpp @@ -7277,6 +7277,62 @@ namespace bgfx { namespace gl ; } + static bool blitMsaaResolve2D( + const BlitItem& _bi + , const TextureGL& _src + , const TextureGL& _dst + , uint32_t _width + , uint32_t _height + , GLuint _currentFbo + ) + { + if (GL_TEXTURE_2D_MULTISAMPLE != _src.m_target + || GL_TEXTURE_2D != _dst.m_target + || 1 < _bi.m_depth + || NULL == glBlitFramebuffer) + { + return false; + } + + GLuint fbo[2]; + GL_CHECK(glGenFramebuffers(BX_COUNTOF(fbo), fbo) ); + + GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]) ); + GL_CHECK(glFramebufferTexture2D(GL_READ_FRAMEBUFFER + , GL_COLOR_ATTACHMENT0 + , _src.m_target + , _src.m_id + , 0 + ) ); + + GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]) ); + GL_CHECK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER + , GL_COLOR_ATTACHMENT0 + , _dst.m_target + , _dst.m_id + , _bi.m_dstMip + ) ); + + GL_CHECK(glDisable(GL_SCISSOR_TEST) ); + GL_CHECK(glBlitFramebuffer( + _bi.m_srcX + , _bi.m_srcY + , _bi.m_srcX + _width + , _bi.m_srcY + _height + , _bi.m_dstX + , _bi.m_dstY + , _bi.m_dstX + _width + , _bi.m_dstY + _height + , GL_COLOR_BUFFER_BIT + , GL_NEAREST + ) ); + + GL_CHECK(glDeleteFramebuffers(BX_COUNTOF(fbo), fbo) ); + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, _currentFbo) ); + + return true; + } + static bool blitCompressed2D( const BlitItem& _bi , const TextureGL& _src @@ -7409,7 +7465,13 @@ namespace bgfx { namespace gl } } - GL_CHECK(glCopyImageSubData(src.m_id + if (blitMsaaResolve2D(bi, src, dst, width, height, m_currentFbo) ) + { + continue; + } + + GL_CHECK(glCopyImageSubData( + src.m_id , src.m_target , bi.m_srcMip , bi.m_srcX diff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp index 545969f85..d9c6bf4bf 100644 --- a/src/renderer_vk.cpp +++ b/src/renderer_vk.cpp @@ -9479,76 +9479,109 @@ VK_DESTROY const uint16_t srcSamples = VK_NULL_HANDLE != src.m_singleMsaaImage ? 1 : src.m_sampler.Count; const uint16_t dstSamples = dst.m_sampler.Count; - BX_UNUSED(srcSamples, dstSamples); + + const bool resolve = true + && 1 < srcSamples + && 1 == dstSamples + && VK_IMAGE_ASPECT_COLOR_BIT == src.m_aspectFlags + && VK_IMAGE_ASPECT_COLOR_BIT == dst.m_aspectFlags + ; BX_ASSERT( - srcSamples == dstSamples + srcSamples == dstSamples || resolve , "Mismatching texture sample count (%d != %d)." , srcSamples , dstSamples ); - VkImageCopy copyInfo; - copyInfo.srcSubresource.aspectMask = src.m_aspectFlags; - copyInfo.srcSubresource.mipLevel = blit.m_srcMip; - copyInfo.srcSubresource.baseArrayLayer = 0; - copyInfo.srcSubresource.layerCount = 1; - copyInfo.srcOffset.x = blit.m_srcX; - copyInfo.srcOffset.y = blit.m_srcY; - copyInfo.srcOffset.z = 0; - copyInfo.dstSubresource.aspectMask = dst.m_aspectFlags; - copyInfo.dstSubresource.mipLevel = blit.m_dstMip; - copyInfo.dstSubresource.baseArrayLayer = 0; - copyInfo.dstSubresource.layerCount = 1; - copyInfo.dstOffset.x = blit.m_dstX; - copyInfo.dstOffset.y = blit.m_dstY; - copyInfo.dstOffset.z = 0; - const uint32_t srcMipWidth = bx::max(1, src.m_width >> blit.m_srcMip); const uint32_t srcMipHeight = bx::max(1, src.m_height >> blit.m_srcMip); const uint32_t dstMipWidth = bx::max(1, dst.m_width >> blit.m_dstMip); const uint32_t dstMipHeight = bx::max(1, dst.m_height >> blit.m_dstMip); + const uint32_t depth = bx::max(1, blit.m_depth); - copyInfo.extent.width = bx::min( - blit.m_width - , srcMipWidth - blit.m_srcX - , dstMipWidth - blit.m_dstX - ); - copyInfo.extent.height = bx::min( - blit.m_height - , srcMipHeight - blit.m_srcY - , dstMipHeight - blit.m_dstY - ); - copyInfo.extent.depth = 1; + const bool is3D = VK_IMAGE_VIEW_TYPE_3D == src.m_type; - const uint32_t depth = bx::max(1, blit.m_depth); + BX_ASSERT(!is3D || VK_IMAGE_VIEW_TYPE_3D == dst.m_type, "Can't blit between 2D and 3D image."); - if (VK_IMAGE_VIEW_TYPE_3D == src.m_type) + const VkImageCopy copyInfo = { - BX_ASSERT(VK_IMAGE_VIEW_TYPE_3D == dst.m_type, "Can't blit between 2D and 3D image."); + .srcSubresource = + { + .aspectMask = src.m_aspectFlags, + .mipLevel = blit.m_srcMip, + .baseArrayLayer = is3D ? 0u : uint32_t(blit.m_srcZ), + .layerCount = is3D ? 1u : depth, + }, + .srcOffset = + { + .x = blit.m_srcX, + .y = blit.m_srcY, + .z = is3D ? blit.m_srcZ : 0, + }, + .dstSubresource = + { + .aspectMask = dst.m_aspectFlags, + .mipLevel = blit.m_dstMip, + .baseArrayLayer = is3D ? 0u : uint32_t(blit.m_dstZ), + .layerCount = is3D ? 1u : depth, + }, + .dstOffset = + { + .x = blit.m_dstX, + .y = blit.m_dstY, + .z = is3D ? blit.m_dstZ : 0, + }, + .extent = + { + .width = bx::min( + blit.m_width + , srcMipWidth - blit.m_srcX + , dstMipWidth - blit.m_dstX + ), + .height = bx::min( + blit.m_height + , srcMipHeight - blit.m_srcY + , dstMipHeight - blit.m_dstY + ), + .depth = is3D ? depth : 1, + }, + }; - copyInfo.srcOffset.z = blit.m_srcZ; - copyInfo.dstOffset.z = blit.m_dstZ; - copyInfo.extent.depth = depth; + if (resolve) + { + const VkImageResolve resolveInfo = + { + .srcSubresource = copyInfo.srcSubresource, + .srcOffset = copyInfo.srcOffset, + .dstSubresource = copyInfo.dstSubresource, + .dstOffset = copyInfo.dstOffset, + .extent = copyInfo.extent, + }; + + vkCmdResolveImage( + m_commandBuffer + , src.m_textureImage + , src.m_currentImageLayout + , dst.m_textureImage + , dst.m_currentImageLayout + , 1 + , &resolveInfo + ); } else { - copyInfo.srcSubresource.baseArrayLayer = blit.m_srcZ; - copyInfo.dstSubresource.baseArrayLayer = blit.m_dstZ; - copyInfo.srcSubresource.layerCount = depth; - copyInfo.dstSubresource.layerCount = depth; + vkCmdCopyImage( + m_commandBuffer + , VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_singleMsaaImage : src.m_textureImage + , VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_currentSingleMsaaImageLayout : src.m_currentImageLayout + , dst.m_textureImage + , dst.m_currentImageLayout + , 1 + , ©Info + ); } - vkCmdCopyImage( - m_commandBuffer - , VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_singleMsaaImage : src.m_textureImage - , VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_currentSingleMsaaImageLayout : src.m_currentImageLayout - , dst.m_textureImage - , dst.m_currentImageLayout - , 1 - , ©Info - ); - setMemoryBarrier( m_commandBuffer , VK_PIPELINE_STAGE_TRANSFER_BIT