diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 95090493de..25437cd4f6 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -50,7 +50,7 @@ namespace dawn_native { namespace d3d12 { D3D12_TEXTURE_COPY_LOCATION copyLocation; copyLocation.pResource = texture.GetD3D12Resource(); copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - copyLocation.SubresourceIndex = texture.GetNumMipLevels() * slice + level; + copyLocation.SubresourceIndex = texture.GetSubresourceIndex(level, slice); return copyLocation; } @@ -346,6 +346,37 @@ namespace dawn_native { namespace d3d12 { } } + void ResolveMultisampledRenderPass(ComPtr commandList, + BeginRenderPassCmd* renderPass) { + ASSERT(renderPass != nullptr); + + for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + TextureViewBase* resolveTarget = + renderPass->colorAttachments[i].resolveTarget.Get(); + if (resolveTarget == nullptr) { + continue; + } + + Texture* colorTexture = + ToBackend(renderPass->colorAttachments[i].view->GetTexture()); + Texture* resolveTexture = ToBackend(resolveTarget->GetTexture()); + + // Transition the usages of the color attachment and resolve target. + colorTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); + resolveTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_DEST); + + // Do MSAA resolve with ResolveSubResource(). + ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource(); + ID3D12Resource* resolveTextureHandle = resolveTexture->GetD3D12Resource(); + const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex( + resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer()); + constexpr uint32_t kColorTextureSubresourceIndex = 0; + commandList->ResolveSubresource( + resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle, + kColorTextureSubresourceIndex, colorTexture->GetD3D12Format()); + } + } + } // anonymous namespace CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) @@ -734,6 +765,12 @@ namespace dawn_native { namespace d3d12 { switch (type) { case Command::EndRenderPass: { mCommands.NextCommand(); + + // TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA + // resolve in D3D12 render pass on the platforms that support this feature. + if (renderPass->sampleCount > 1) { + ResolveMultisampledRenderPass(commandList, renderPass); + } return; } break; diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp index 0b0aa06f9a..2cc719bfa1 100644 --- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp +++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp @@ -351,7 +351,7 @@ namespace dawn_native { namespace d3d12 { descriptorD3D12.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; descriptorD3D12.RasterizerState.DepthClipEnable = TRUE; - descriptorD3D12.RasterizerState.MultisampleEnable = FALSE; + descriptorD3D12.RasterizerState.MultisampleEnable = (GetSampleCount() > 1) ? TRUE : FALSE; descriptorD3D12.RasterizerState.AntialiasedLineEnable = FALSE; descriptorD3D12.RasterizerState.ForcedSampleCount = 0; descriptorD3D12.RasterizerState.ConservativeRaster = @@ -376,7 +376,8 @@ namespace dawn_native { namespace d3d12 { descriptorD3D12.SampleMask = UINT_MAX; descriptorD3D12.PrimitiveTopologyType = D3D12PrimitiveTopologyType(GetPrimitiveTopology()); - descriptorD3D12.SampleDesc.Count = 1; + descriptorD3D12.SampleDesc.Count = GetSampleCount(); + descriptorD3D12.SampleDesc.Quality = 0; ASSERT_SUCCESS(device->GetD3D12Device()->CreateGraphicsPipelineState( &descriptorD3D12, IID_PPV_ARGS(&mPipelineState))); diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index 63a0533d97..5e9a63b765 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -54,13 +54,19 @@ namespace dawn_native { namespace d3d12 { } D3D12_RESOURCE_FLAGS D3D12ResourceFlags(dawn::TextureUsageBit usage, - dawn::TextureFormat format) { + dawn::TextureFormat format, + bool isMultisampledTexture) { D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE; if (usage & dawn::TextureUsageBit::Storage) { flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } - if (usage & dawn::TextureUsageBit::OutputAttachment) { + + // A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or + // D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags. + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource + // _desc + if ((usage & dawn::TextureUsageBit::OutputAttachment) || isMultisampledTexture) { if (TextureFormatHasDepth(format) || TextureFormatHasStencil(format)) { flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; } else { @@ -120,10 +126,12 @@ namespace dawn_native { namespace d3d12 { resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize(); resourceDescriptor.MipLevels = static_cast(GetNumMipLevels()); resourceDescriptor.Format = D3D12TextureFormat(GetFormat()); - resourceDescriptor.SampleDesc.Count = 1; + resourceDescriptor.SampleDesc.Count = descriptor->sampleCount; + // TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern. resourceDescriptor.SampleDesc.Quality = 0; resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resourceDescriptor.Flags = D3D12ResourceFlags(GetUsage(), GetFormat()); + resourceDescriptor.Flags = + D3D12ResourceFlags(GetUsage(), GetFormat(), IsMultisampledTexture()); mResource = ToBackend(GetDevice()) ->GetResourceAllocator() @@ -169,26 +177,32 @@ namespace dawn_native { namespace d3d12 { void Texture::TransitionUsageNow(ComPtr commandList, dawn::TextureUsageBit usage) { + TransitionUsageNow(commandList, D3D12TextureUsage(usage, GetFormat())); + } + + void Texture::TransitionUsageNow(ComPtr commandList, + D3D12_RESOURCE_STATES newState) { // Avoid transitioning the texture when it isn't needed. // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. - if (usage == mLastUsage) { + if (mLastState == newState) { return; } - D3D12_RESOURCE_STATES lastState = D3D12TextureUsage(mLastUsage, GetFormat()); - D3D12_RESOURCE_STATES newState = D3D12TextureUsage(usage, GetFormat()); - D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = mResourcePtr; - barrier.Transition.StateBefore = lastState; + barrier.Transition.StateBefore = mLastState; barrier.Transition.StateAfter = newState; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; commandList->ResourceBarrier(1, &barrier); - mLastUsage = usage; + mLastState = newState; + } + + uint32_t Texture::GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const { + return GetNumMipLevels() * arraySlice + mipmapLevel; } TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) @@ -202,6 +216,7 @@ namespace dawn_native { namespace d3d12 { // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv // TODO(jiawei.shao@intel.com): support more texture view dimensions. + // TODO(jiawei.shao@intel.com): support creating SRV on multisampled textures. switch (descriptor->dimension) { case dawn::TextureViewDimension::e2D: case dawn::TextureViewDimension::e2DArray: @@ -242,26 +257,43 @@ namespace dawn_native { namespace d3d12 { ASSERT(GetTexture()->GetDimension() == dawn::TextureDimension::e2D); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.Format = GetD3D12Format(); - // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array layer - // and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as 1-layer 2D - // array textures. (Just like how we treat SRVs) - // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv - // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_rtv - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; - rtvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer(); - rtvDesc.Texture2DArray.ArraySize = GetLayerCount(); - rtvDesc.Texture2DArray.MipSlice = GetBaseMipLevel(); - rtvDesc.Texture2DArray.PlaneSlice = 0; + if (GetTexture()->IsMultisampledTexture()) { + ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 && + GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0); + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + } else { + // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array + // layer and layer count in D3D12_TEX2D_RTV. For 2D texture views, we treat them as + // 1-layer 2D array textures. (Just like how we treat SRVs) + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_rtv + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array + // _rtv + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer(); + rtvDesc.Texture2DArray.ArraySize = GetLayerCount(); + rtvDesc.Texture2DArray.MipSlice = GetBaseMipLevel(); + rtvDesc.Texture2DArray.PlaneSlice = 0; + } + return rtvDesc; } - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const { D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format(); - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - dsvDesc.Texture2D.MipSlice = 0; dsvDesc.Flags = D3D12_DSV_FLAG_NONE; + + // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. + ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 && + GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0); + + if (GetTexture()->IsMultisampledTexture()) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + } else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = 0; + } + return dsvDesc; } diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index 51676126a5..33b6be35bd 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -36,6 +36,10 @@ namespace dawn_native { namespace d3d12 { void TransitionUsageNow(ComPtr commandList, dawn::TextureUsageBit usage); + void TransitionUsageNow(ComPtr commandList, + D3D12_RESOURCE_STATES newState); + + uint32_t GetSubresourceIndex(uint32_t mipmapLevel, uint32_t arraySlice) const; private: // Dawn API @@ -45,7 +49,7 @@ namespace dawn_native { namespace d3d12 { ComPtr mResource = {}; ID3D12Resource* mResourcePtr = nullptr; - dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None; + D3D12_RESOURCE_STATES mLastState = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON; }; class TextureView : public TextureViewBase { diff --git a/src/tests/end2end/MultisampledRenderingTests.cpp b/src/tests/end2end/MultisampledRenderingTests.cpp index e22b561176..e1ceb83c79 100644 --- a/src/tests/end2end/MultisampledRenderingTests.cpp +++ b/src/tests/end2end/MultisampledRenderingTests.cpp @@ -466,4 +466,4 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { } // TODO(jiawei.shao@intel.com): enable multisampled rendering on all Dawn backends. -DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, OpenGLBackend); +DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, D3D12Backend, OpenGLBackend);