From e5a4cfb935e201515d41818fc64404df800e44c7 Mon Sep 17 00:00:00 2001 From: Peng Huang Date: Fri, 28 Apr 2023 17:22:03 +0000 Subject: [PATCH] d3d11: enable some CopyTests To make B2B, B2T and T2B testes work, this CL implements several missed functions: - Texture::Clear() is need for initializing texture data - Texture::Read() is need for readback data from 2d and 3d textures Bug: dawn:1740,dawn:1768 Change-Id: Ib9e354c82fcdc8cc4e86b1699fa652cfc4a50721 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128621 Reviewed-by: Corentin Wallez Kokoro: Kokoro Commit-Queue: Peng Huang --- src/dawn/native/Texture.cpp | 11 + src/dawn/native/Texture.h | 3 + src/dawn/native/d3d11/BufferD3D11.cpp | 4 +- src/dawn/native/d3d11/CommandBufferD3D11.cpp | 89 ++---- src/dawn/native/d3d11/QueueD3D11.cpp | 5 + src/dawn/native/d3d11/TextureD3D11.cpp | 308 ++++++++++++++----- src/dawn/native/d3d11/TextureD3D11.h | 22 +- src/dawn/tests/end2end/CopyTests.cpp | 14 +- 8 files changed, 305 insertions(+), 151 deletions(-) diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp index 6169e4cf77..6a97f4dc74 100644 --- a/src/dawn/native/Texture.cpp +++ b/src/dawn/native/Texture.cpp @@ -750,6 +750,17 @@ bool TextureBase::IsMultisampledTexture() const { return mSampleCount > 1; } +bool TextureBase::CoverFullSubresource(const Extent3D& size) const { + switch (GetDimension()) { + case wgpu::TextureDimension::e1D: + return size.width == GetSize().width; + case wgpu::TextureDimension::e2D: + return size.width == GetSize().width && size.height == GetSize().height; + case wgpu::TextureDimension::e3D: + return size == GetSize(); + } +} + Extent3D TextureBase::GetMipLevelSingleSubresourceVirtualSize(uint32_t level) const { Extent3D extent = {std::max(mSize.width >> level, 1u), 1u, 1u}; if (mDimension == wgpu::TextureDimension::e1D) { diff --git a/src/dawn/native/Texture.h b/src/dawn/native/Texture.h index c3e81637a5..42edacffaf 100644 --- a/src/dawn/native/Texture.h +++ b/src/dawn/native/Texture.h @@ -79,6 +79,9 @@ class TextureBase : public ApiObjectBase { bool IsMultisampledTexture() const; + // Returns true if the size covers the whole subresource. + bool CoverFullSubresource(const Extent3D& size) const; + // For a texture with non-block-compressed texture format, its physical size is always equal // to its virtual size. For a texture with block compressed texture format, the physical // size is the one with paddings if necessary, which is always a multiple of the block size diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp index 5afb0b7028..b7e894c080 100644 --- a/src/dawn/native/d3d11/BufferD3D11.cpp +++ b/src/dawn/native/d3d11/BufferD3D11.cpp @@ -401,9 +401,7 @@ MaybeError Buffer::Write(CommandRecordingContext* commandContext, uint64_t offset, const void* data, size_t size) { - if (size == 0) { - return {}; - } + DAWN_ASSERT(size != 0); MarkUsedInPendingCommands(); // Map the buffer if it is possible, so EnsureDataInitializedAsDestination() and WriteInternal() diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp index b54ef84b05..5ba4ee36bc 100644 --- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp +++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp @@ -232,8 +232,6 @@ Ref CommandBuffer::Create(CommandEncoder* encoder, MaybeError CommandBuffer::Execute() { CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext(); - ID3D11DeviceContext1* d3d11DeviceContext1 = commandContext->GetD3D11DeviceContext1(); - auto LazyClearSyncScope = [commandContext](const SyncScopeResourceUsage& scope) -> MaybeError { for (size_t i = 0; i < scope.textures.size(); i++) { Texture* texture = ToBackend(scope.textures[i]); @@ -366,82 +364,31 @@ MaybeError CommandBuffer::Execute() { auto& dst = copy->destination; SubresourceRange subresources = GetSubresourcesAffectedByCopy(src, copy->copySize); - DAWN_TRY(ToBackend(src.texture) - ->EnsureSubresourceContentInitialized(commandContext, subresources)); - - TextureDescriptor desc = {}; - desc.label = "CopyTextureToBufferStaging"; - desc.dimension = src.texture->GetDimension(); - desc.size.width = copy->copySize.width; - desc.size.height = copy->copySize.height; - desc.size.depthOrArrayLayers = copy->copySize.depthOrArrayLayers; - desc.format = src.texture->GetFormat().format; - desc.mipLevelCount = 1; - desc.sampleCount = 1; - - Ref stagingTexture; - DAWN_TRY_ASSIGN(stagingTexture, - Texture::CreateStaging(ToBackend(GetDevice()), &desc)); - - CopyTextureToTextureCmd copyTextureToBufferCmd; - copyTextureToBufferCmd.source = src; - copyTextureToBufferCmd.destination.texture = stagingTexture.Get(); - copyTextureToBufferCmd.destination.origin = {0, 0, 0}; - copyTextureToBufferCmd.destination.mipLevel = 0; - copyTextureToBufferCmd.destination.aspect = src.aspect; - copyTextureToBufferCmd.copySize = copy->copySize; - - DAWN_TRY(Texture::Copy(commandContext, ©TextureToBufferCmd)); Buffer* buffer = ToBackend(dst.buffer.Get()); Buffer::ScopedMap scopedDstMap; DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(buffer)); - DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy)); - - for (uint32_t z = 0; z < copy->copySize.depthOrArrayLayers; ++z) { - // Copy the staging texture to the buffer. - // The Map() will block until the GPU is done with the texture. - // TODO(dawn:1705): avoid blocking the CPU. - D3D11_MAPPED_SUBRESOURCE mappedResource; - DAWN_TRY( - CheckHRESULT(d3d11DeviceContext1->Map(stagingTexture->GetD3D11Resource(), z, - D3D11_MAP_READ, 0, &mappedResource), - "D3D11 map staging texture")); - - uint8_t* pSrcData = reinterpret_cast(mappedResource.pData); - const TexelBlockInfo& blockInfo = - ToBackend(src.texture)->GetFormat().GetAspectInfo(src.aspect).block; - uint32_t bytesPerRow = blockInfo.byteSize * copy->copySize.width; - if (scopedDstMap.GetMappedData()) { - uint8_t* pDstData = scopedDstMap.GetMappedData() + dst.offset + - dst.bytesPerRow * dst.rowsPerImage * z; - for (uint32_t y = 0; y < copy->copySize.height; ++y) { - memcpy(pDstData, pSrcData, bytesPerRow); - pDstData += dst.bytesPerRow; - pSrcData += mappedResource.RowPitch; - } - } else { - uint64_t dstOffset = dst.offset + dst.bytesPerRow * dst.rowsPerImage * z; - if (dst.bytesPerRow == bytesPerRow && - mappedResource.RowPitch == bytesPerRow) { - // If there is no padding in the rows, we can upload the whole image in - // one buffer->Write() call. - DAWN_TRY(buffer->Write(commandContext, dstOffset, pSrcData, - dst.bytesPerRow * copy->copySize.height)); - } else { - // Otherwise, we need to upload each row separately. - for (uint32_t y = 0; y < copy->copySize.height; ++y) { - DAWN_TRY(buffer->Write(commandContext, dstOffset, pSrcData, - bytesPerRow)); - dstOffset += dst.bytesPerRow; - pSrcData += mappedResource.RowPitch; - } - } - } - d3d11DeviceContext1->Unmap(stagingTexture->GetD3D11Resource(), z); + Texture::ReadCallback callback; + if (scopedDstMap.GetMappedData()) { + callback = [&](const uint8_t* data, uint64_t offset, + uint64_t size) -> MaybeError { + memcpy(scopedDstMap.GetMappedData() + dst.offset + offset, data, size); + return {}; + }; + } else { + callback = [&](const uint8_t* data, uint64_t offset, + uint64_t size) -> MaybeError { + DAWN_TRY(ToBackend(dst.buffer) + ->Write(commandContext, dst.offset + offset, data, size)); + return {}; + }; } + DAWN_TRY(ToBackend(src.texture) + ->Read(commandContext, subresources, src.origin, copy->copySize, + dst.bytesPerRow, dst.rowsPerImage, callback)); + dst.buffer->MarkUsedInPendingCommands(); break; } diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp index 5bfaef0dc3..1fd5bc0e3a 100644 --- a/src/dawn/native/d3d11/QueueD3D11.cpp +++ b/src/dawn/native/d3d11/QueueD3D11.cpp @@ -50,6 +50,11 @@ MaybeError Queue::WriteBufferImpl(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size) { + if (size == 0) { + // skip the empty write + return {}; + } + CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext(); return ToBackend(buffer)->Write(commandContext, bufferOffset, data, size); } diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp index eb3fda37b0..80c0e822c2 100644 --- a/src/dawn/native/d3d11/TextureD3D11.cpp +++ b/src/dawn/native/d3d11/TextureD3D11.cpp @@ -49,6 +49,18 @@ UINT D3D11TextureBindFlags(wgpu::TextureUsage usage, const Format& format) { return bindFlags; } +Aspect D3D11Aspect(Aspect aspect) { + // https://learn.microsoft.com/en-us/windows/win32/direct3d12/subresources + // Planar formats existed in Direct3D 11, but individual planes could not be addressed + // individually. + if (IsSubset(aspect, Aspect::Depth | Aspect::Stencil)) { + return Aspect::CombinedDepthStencil; + } + + ASSERT(HasOneBit(aspect)); + return aspect; +} + } // namespace // static @@ -104,9 +116,9 @@ T Texture::GetD3D11TextureDesc() const { } desc.MipLevels = static_cast(GetNumMipLevels()); - desc.Format = d3d::DXGITextureFormat(GetFormat().format); + desc.Format = GetD3D11Format(); desc.Usage = mIsStaging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11TextureBindFlags(GetUsage(), GetFormat()); + desc.BindFlags = D3D11TextureBindFlags(GetInternalUsage(), GetFormat()); constexpr UINT kCPUReadWriteFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.CPUAccessFlags = mIsStaging ? kCPUReadWriteFlags : 0; @@ -116,6 +128,12 @@ T Texture::GetD3D11TextureDesc() const { MaybeError Texture::InitializeAsInternalTexture() { Device* device = ToBackend(GetDevice()); + if (GetFormat().isRenderable && !mIsStaging) { + // If the texture format is renderable, we need to add the render attachment usage + // internally, so the texture can be cleared with GPU. + AddInternalUsage(wgpu::TextureUsage::RenderAttachment); + } + switch (GetDimension()) { case wgpu::TextureDimension::e1D: { D3D11_TEXTURE1D_DESC desc = GetD3D11TextureDesc(); @@ -146,7 +164,8 @@ MaybeError Texture::InitializeAsInternalTexture() { } } - if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + // Staging texture is used internally, so we don't need to clear it. + if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting) && !mIsStaging) { CommandRecordingContext* commandContext = device->GetPendingCommandContext(); DAWN_TRY(Clear(commandContext, GetAllSubresources(), TextureBase::ClearValue::NonZero)); } @@ -184,28 +203,6 @@ ID3D11Resource* Texture::GetD3D11Resource() const { return mD3d11Resource.Get(); } -DXGI_FORMAT Texture::GetD3D11CopyableSubresourceFormat(Aspect aspect) const { - // TODO(dawn:1705): share the code with D3D12 - ASSERT(GetFormat().aspects & aspect); - - switch (GetFormat().format) { - case wgpu::TextureFormat::Depth24PlusStencil8: - case wgpu::TextureFormat::Depth32FloatStencil8: - case wgpu::TextureFormat::Stencil8: - switch (aspect) { - case Aspect::Depth: - return DXGI_FORMAT_R32_FLOAT; - case Aspect::Stencil: - return DXGI_FORMAT_R8_UINT; - default: - UNREACHABLE(); - } - default: - ASSERT(HasOneBit(GetFormat().aspects)); - return GetD3D11Format(); - } -} - D3D11_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(const Format& format, const SubresourceRange& range) const { D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; @@ -278,12 +275,16 @@ D3D11_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(const SubresourceRange& MaybeError Texture::Clear(CommandRecordingContext* commandContext, const SubresourceRange& range, TextureBase::ClearValue clearValue) { - // TODO(dawn:1740): Clear non-renderable texture. - if ((GetUsage() & wgpu::TextureUsage::RenderAttachment) == 0) { - return DAWN_UNIMPLEMENTED_ERROR("Clearing non-renderable textures is not implemented"); + bool isRenderable = GetInternalUsage() & wgpu::TextureUsage::RenderAttachment; + + if (!isRenderable) { + return DAWN_UNIMPLEMENTED_ERROR("Clearing non-renderable textures"); } - TextureViewDescriptor desc; + ID3D11DeviceContext* d3d11DeviceContext = commandContext->GetD3D11DeviceContext(); + + TextureViewDescriptor desc = {}; + desc.label = "ClearTextureView"; desc.format = GetFormat().format; switch (GetDimension()) { case wgpu::TextureDimension::e1D: @@ -296,11 +297,6 @@ MaybeError Texture::Clear(CommandRecordingContext* commandContext, desc.dimension = wgpu::TextureViewDimension::e3D; break; } - // TODO(dawn:1740): support clearing multiple layers. - if (range.levelCount != 1 || range.layerCount != 1) { - return DAWN_UNIMPLEMENTED_ERROR("Clearing multiple layers is not implemented"); - } - desc.baseMipLevel = range.baseMipLevel; desc.mipLevelCount = range.levelCount; desc.baseArrayLayer = range.baseArrayLayer; @@ -308,14 +304,36 @@ MaybeError Texture::Clear(CommandRecordingContext* commandContext, desc.aspect = wgpu::TextureAspect::All; Ref view = TextureView::Create(this, &desc); - ComPtr d3d11RTV; - DAWN_TRY_ASSIGN(d3d11RTV, view->CreateD3D11RenderTargetView()); - static constexpr std::array zero = {0.0f, 0.0f, 0.0f, 0.0f}; - static constexpr std::array nonZero = {1.0f, 1.0f, 1.0f, 1.0f}; + if (GetFormat().HasDepthOrStencil()) { + ComPtr d3d11DSV; + DAWN_TRY_ASSIGN(d3d11DSV, view->CreateD3D11DepthStencilView(/*depthReadOnly=*/false, + /*stencilReadOnly=*/false)); + UINT clearFlags = 0; + if (GetFormat().HasDepth() && range.aspects & Aspect::Depth) { + clearFlags |= D3D11_CLEAR_DEPTH; + } + if (GetFormat().HasStencil() && range.aspects & Aspect::Stencil) { + clearFlags |= D3D11_CLEAR_STENCIL; + } + d3d11DeviceContext->ClearDepthStencilView( + d3d11DSV.Get(), clearFlags, clearValue == TextureBase::ClearValue::Zero ? 0.0f : 1.0f, + 0); + } else { + static constexpr std::array kZero = {0.0f, 0.0f, 0.0f, 0.0f}; + static constexpr std::array kNonZero = {1.0f, 1.0f, 1.0f, 1.0f}; - commandContext->GetD3D11DeviceContext()->ClearRenderTargetView( - d3d11RTV.Get(), clearValue == TextureBase::ClearValue::Zero ? zero.data() : nonZero.data()); + ComPtr d3d11RTV; + DAWN_TRY_ASSIGN(d3d11RTV, view->CreateD3D11RenderTargetView()); + d3d11DeviceContext->ClearRenderTargetView( + d3d11RTV.Get(), + clearValue == TextureBase::ClearValue::Zero ? kZero.data() : kNonZero.data()); + } + + if (clearValue == TextureBase::ClearValue::Zero) { + SetIsSubresourceContentInitialized(true, range); + GetDevice()->IncrementLazyClearCountForTesting(); + } return {}; } @@ -348,11 +366,18 @@ MaybeError Texture::Write(CommandRecordingContext* commandContext, const uint8_t* data, uint32_t bytesPerRow, uint32_t rowsPerImage) { - DAWN_ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0); + ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0); + + if (GetFormat().HasDepth() && GetFormat().HasStencil()) { + return DAWN_UNIMPLEMENTED_ERROR("Write combined depth/stencil textures"); + } if (IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel)) { SetIsSubresourceContentInitialized(true, subresources); } else { + // Dawn validation should have ensured that full subresources write for depth/stencil + // textures. + ASSERT(!GetFormat().HasDepthOrStencil()); DAWN_TRY(EnsureSubresourceContentInitialized(commandContext, subresources)); } @@ -366,17 +391,20 @@ MaybeError Texture::Write(CommandRecordingContext* commandContext, dstBox.front = origin.z; dstBox.back = origin.z + size.depthOrArrayLayers; uint32_t subresource = - GetSubresourceIndex(subresources.baseMipLevel, origin.z, subresources.aspects); + GetSubresourceIndex(subresources.baseMipLevel, 0, D3D11Aspect(subresources.aspects)); commandContext->GetD3D11DeviceContext1()->UpdateSubresource(GetD3D11Resource(), subresource, - &dstBox, data, bytesPerRow, 0); + &dstBox, data, bytesPerRow, + bytesPerRow * rowsPerImage); } else { dstBox.front = 0; dstBox.back = 1; - for (uint32_t z = origin.z; z < size.depthOrArrayLayers; ++z) { + for (uint32_t layer = 0; layer < subresources.layerCount; ++layer) { uint32_t subresource = - GetSubresourceIndex(subresources.baseMipLevel, z, subresources.aspects); + GetSubresourceIndex(subresources.baseMipLevel, subresources.baseArrayLayer + layer, + D3D11Aspect(subresources.aspects)); + D3D11_BOX* pDstBox = GetFormat().HasDepthOrStencil() ? nullptr : &dstBox; commandContext->GetD3D11DeviceContext1()->UpdateSubresource( - GetD3D11Resource(), subresource, &dstBox, data, bytesPerRow, 0); + GetD3D11Resource(), subresource, pDstBox, data, bytesPerRow, 0); data += rowsPerImage * bytesPerRow; } } @@ -384,6 +412,124 @@ MaybeError Texture::Write(CommandRecordingContext* commandContext, return {}; } +MaybeError Texture::ReadStaging(CommandRecordingContext* commandContext, + const SubresourceRange& subresources, + const Origin3D& origin, + Extent3D size, + uint32_t dstBytesPerRow, + uint32_t dstRowsPerImage, + Texture::ReadCallback callback) { + ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0); + ASSERT(mIsStaging); + ASSERT(subresources.baseArrayLayer == 0); + ASSERT(origin.z == 0); + + ID3D11DeviceContext1* d3d11DeviceContext1 = commandContext->GetD3D11DeviceContext1(); + const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(subresources.aspects).block; + + if (GetDimension() == wgpu::TextureDimension::e2D) { + for (uint32_t layer = 0; layer < subresources.layerCount; ++layer) { + // Copy the staging texture to the buffer. + // The Map() will block until the GPU is done with the texture. + // TODO(dawn:1705): avoid blocking the CPU. + D3D11_MAPPED_SUBRESOURCE mappedResource; + DAWN_TRY(CheckHRESULT(d3d11DeviceContext1->Map(GetD3D11Resource(), layer, + D3D11_MAP_READ, 0, &mappedResource), + "D3D11 map staging texture")); + + uint8_t* pSrcData = static_cast(mappedResource.pData); + uint32_t bytesPerRow = blockInfo.byteSize * size.width; + uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * layer; + if (dstBytesPerRow == bytesPerRow && mappedResource.RowPitch == bytesPerRow) { + // If there is no padding in the rows, we can upload the whole image + // in one read. + DAWN_TRY(callback(pSrcData, dstOffset, dstBytesPerRow * size.height)); + } else { + // Otherwise, we need to read each row separately. + for (uint32_t y = 0; y < size.height; ++y) { + DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow)); + dstOffset += dstBytesPerRow; + pSrcData += mappedResource.RowPitch; + } + } + d3d11DeviceContext1->Unmap(GetD3D11Resource(), layer); + } + return {}; + } + + // 3D textures are copied one slice at a time. + // Copy the staging texture to the buffer. + // The Map() will block until the GPU is done with the texture. + // TODO(dawn:1705): avoid blocking the CPU. + D3D11_MAPPED_SUBRESOURCE mappedResource; + DAWN_TRY(CheckHRESULT( + d3d11DeviceContext1->Map(GetD3D11Resource(), 0, D3D11_MAP_READ, 0, &mappedResource), + "D3D11 map staging texture")); + + uint32_t bytesPerRow = blockInfo.byteSize * size.width; + for (uint32_t z = 0; z < size.depthOrArrayLayers; ++z) { + uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * z; + uint8_t* pSrcData = + static_cast(mappedResource.pData) + z * mappedResource.DepthPitch; + if (dstBytesPerRow == bytesPerRow && mappedResource.RowPitch == bytesPerRow) { + // If there is no padding in the rows, we can upload the whole image + // in one read. + DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow * size.height)); + } else { + // Otherwise, we need to read each row separately. + for (uint32_t y = 0; y < size.height; ++y) { + DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow)); + dstOffset += dstBytesPerRow; + pSrcData += mappedResource.RowPitch; + } + } + } + d3d11DeviceContext1->Unmap(GetD3D11Resource(), 0); + + return {}; +} + +MaybeError Texture::Read(CommandRecordingContext* commandContext, + const SubresourceRange& subresources, + const Origin3D& origin, + Extent3D size, + uint32_t dstBytesPerRow, + uint32_t dstRowsPerImage, + Texture::ReadCallback callback) { + ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0); + ASSERT(!mIsStaging); + + DAWN_TRY(EnsureSubresourceContentInitialized(commandContext, subresources)); + TextureDescriptor desc = {}; + desc.label = "CopyTextureToBufferStaging"; + desc.dimension = GetDimension(); + desc.size = size; + desc.format = GetFormat().format; + desc.mipLevelCount = subresources.levelCount; + desc.sampleCount = GetSampleCount(); + + Ref stagingTexture; + DAWN_TRY_ASSIGN(stagingTexture, CreateStaging(ToBackend(GetDevice()), &desc)); + + CopyTextureToTextureCmd copyCmd; + copyCmd.source.texture = this; + copyCmd.source.origin = origin; + copyCmd.source.mipLevel = subresources.baseMipLevel; + copyCmd.source.aspect = subresources.aspects; + copyCmd.destination.texture = stagingTexture.Get(); + copyCmd.destination.origin = {0, 0, 0}; + copyCmd.destination.mipLevel = 0; + copyCmd.destination.aspect = subresources.aspects; + copyCmd.copySize = size; + + DAWN_TRY(Texture::Copy(commandContext, ©Cmd)); + SubresourceRange stagingSubresources = SubresourceRange::MakeFull( + subresources.aspects, subresources.layerCount, subresources.levelCount); + + return stagingTexture->ReadStaging(commandContext, stagingSubresources, {0, 0, 0}, size, + dstBytesPerRow, dstRowsPerImage, callback); +} + // static MaybeError Texture::Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy) { ASSERT(copy->copySize.width != 0 && copy->copySize.height != 0 && @@ -392,39 +538,63 @@ MaybeError Texture::Copy(CommandRecordingContext* commandContext, CopyTextureToT auto& src = copy->source; auto& dst = copy->destination; - SubresourceRange subresources = GetSubresourcesAffectedByCopy(src, copy->copySize); - DAWN_TRY( - ToBackend(src.texture)->EnsureSubresourceContentInitialized(commandContext, subresources)); + ASSERT(src.aspect == dst.aspect); - subresources = GetSubresourcesAffectedByCopy(dst, copy->copySize); - if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel)) { - dst.texture->SetIsSubresourceContentInitialized(true, subresources); - } else { - // Partial update subresource of a depth/stencil texture is not allowed. - DAWN_ASSERT(!dst.texture->GetFormat().HasDepthOrStencil()); - DAWN_TRY(ToBackend(dst.texture) - ->EnsureSubresourceContentInitialized(commandContext, subresources)); + // TODO(dawn:1705): support copy between textures with different dimensions. + if (src.texture->GetDimension() != dst.texture->GetDimension()) { + return DAWN_UNIMPLEMENTED_ERROR("Copy between textures with different dimensions"); } - bool isWholeTextureCopy = - src.texture->GetSize() == copy->copySize && dst.texture->GetSize() == copy->copySize; - // Partial update subresource of a depth/stencil texture is not allowed. - DAWN_ASSERT(isWholeTextureCopy || !dst.texture->GetFormat().HasDepthOrStencil()); + SubresourceRange srcSubresources = GetSubresourcesAffectedByCopy(src, copy->copySize); + DAWN_TRY(ToBackend(src.texture) + ->EnsureSubresourceContentInitialized(commandContext, srcSubresources)); + + SubresourceRange dstSubresources = GetSubresourcesAffectedByCopy(dst, copy->copySize); + if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel)) { + dst.texture->SetIsSubresourceContentInitialized(true, dstSubresources); + } else { + // Partial update subresource of a depth/stencil texture is not allowed. + ASSERT(!dst.texture->GetFormat().HasDepthOrStencil()); + DAWN_TRY(ToBackend(dst.texture) + ->EnsureSubresourceContentInitialized(commandContext, dstSubresources)); + } D3D11_BOX srcBox; srcBox.left = src.origin.x; srcBox.right = src.origin.x + copy->copySize.width; srcBox.top = src.origin.y; srcBox.bottom = src.origin.y + copy->copySize.height; - srcBox.front = 0; - srcBox.back = 1; + switch (src.texture->GetDimension()) { + case wgpu::TextureDimension::e2D: + srcBox.front = 0; + srcBox.back = 1; + break; + case wgpu::TextureDimension::e3D: + srcBox.front = src.origin.z; + srcBox.back = src.origin.z + copy->copySize.depthOrArrayLayers; + break; + default: + // TODO(dawn:1705): support 1d texture. + UNREACHABLE(); + } - uint32_t subresource = src.texture->GetSubresourceIndex(src.mipLevel, src.origin.z, src.aspect); + bool isWholeSubresource = src.texture->CoverFullSubresource(copy->copySize); - commandContext->GetD3D11DeviceContext1()->CopySubresourceRegion( - ToBackend(dst.texture)->GetD3D11Resource(), dst.mipLevel, dst.origin.x, dst.origin.y, - dst.origin.z, ToBackend(src.texture)->GetD3D11Resource(), subresource, - isWholeTextureCopy ? nullptr : &srcBox); + // Partial update subresource of a depth/stencil texture is not allowed. + ASSERT(isWholeSubresource || !src.texture->GetFormat().HasDepthOrStencil()); + + for (uint32_t layer = 0; layer < srcSubresources.layerCount; ++layer) { + uint32_t srcSubresource = + src.texture->GetSubresourceIndex(src.mipLevel, srcSubresources.baseArrayLayer + layer, + D3D11Aspect(srcSubresources.aspects)); + uint32_t dstSubresource = + dst.texture->GetSubresourceIndex(dst.mipLevel, dstSubresources.baseArrayLayer + layer, + D3D11Aspect(dstSubresources.aspects)); + commandContext->GetD3D11DeviceContext1()->CopySubresourceRegion( + ToBackend(dst.texture)->GetD3D11Resource(), dstSubresource, dst.origin.x, dst.origin.y, + 0, ToBackend(src.texture)->GetD3D11Resource(), srcSubresource, + isWholeSubresource ? nullptr : &srcBox); + } return {}; } @@ -444,7 +614,7 @@ DXGI_FORMAT TextureView::GetD3D11Format() const { ResultOrError> TextureView::CreateD3D11ShaderResourceView() const { Device* device = ToBackend(GetDevice()); D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - srvDesc.Format = d3d::DXGITextureFormat(GetFormat().format); + srvDesc.Format = GetD3D11Format(); const Format& textureFormat = GetTexture()->GetFormat(); // TODO(dawn:1705): share below code with D3D12? diff --git a/src/dawn/native/d3d11/TextureD3D11.h b/src/dawn/native/d3d11/TextureD3D11.h index 9190a8cab6..00024fdcf6 100644 --- a/src/dawn/native/d3d11/TextureD3D11.h +++ b/src/dawn/native/d3d11/TextureD3D11.h @@ -37,14 +37,10 @@ class Texture final : public TextureBase { static ResultOrError> Create(Device* device, const TextureDescriptor* descriptor, ComPtr d3d11Texture); - static ResultOrError> CreateStaging(Device* device, - const TextureDescriptor* descriptor); DXGI_FORMAT GetD3D11Format() const; ID3D11Resource* GetD3D11Resource() const; - DXGI_FORMAT GetD3D11CopyableSubresourceFormat(Aspect aspect) const; - D3D11_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(const Format& format, const SubresourceRange& range) const; D3D11_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(const SubresourceRange& range, @@ -61,9 +57,20 @@ class Texture final : public TextureBase { const uint8_t* data, uint32_t bytesPerRow, uint32_t rowsPerImage); + using ReadCallback = std::function; + MaybeError Read(CommandRecordingContext* commandContext, + const SubresourceRange& subresources, + const Origin3D& origin, + Extent3D size, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + ReadCallback callback); static MaybeError Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy); private: + static ResultOrError> CreateStaging(Device* device, + const TextureDescriptor* descriptor); + Texture(Device* device, const TextureDescriptor* descriptor, TextureState state, @@ -85,6 +92,13 @@ class Texture final : public TextureBase { MaybeError Clear(CommandRecordingContext* commandContext, const SubresourceRange& range, TextureBase::ClearValue clearValue); + MaybeError ReadStaging(CommandRecordingContext* commandContext, + const SubresourceRange& subresources, + const Origin3D& origin, + Extent3D size, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + ReadCallback callback); const bool mIsStaging = false; ComPtr mD3d11Resource; diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp index 8aaabc3dc9..012f4b0241 100644 --- a/src/dawn/tests/end2end/CopyTests.cpp +++ b/src/dawn/tests/end2end/CopyTests.cpp @@ -1353,6 +1353,7 @@ TEST_P(CopyTests_T2B, Texture3DMipUnaligned) { } DAWN_INSTANTIATE_TEST(CopyTests_T2B, + D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), @@ -1946,6 +1947,7 @@ TEST_P(CopyTests_B2T, Texture3DMipUnaligned) { } DAWN_INSTANTIATE_TEST(CopyTests_B2T, + D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), @@ -2444,6 +2446,7 @@ TEST_P(CopyTests_T2T, Texture3DMipUnaligned) { } } +// TODO(dawn:1705): enable this test for D3D11 DAWN_INSTANTIATE_TEST_P( CopyTests_T2T, {D3D12Backend(), @@ -2470,8 +2473,8 @@ TEST_P(CopyTests_Formats, SrgbCompatibility) { } DAWN_INSTANTIATE_TEST_P(CopyTests_Formats, - {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), - VulkanBackend()}, + {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), + OpenGLESBackend(), VulkanBackend()}, {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb}); @@ -2502,6 +2505,7 @@ TEST_P(CopyTests_B2B, ZeroSizedCopy) { } DAWN_INSTANTIATE_TEST(CopyTests_B2B, + D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), @@ -2530,6 +2534,7 @@ TEST_P(ClearBufferTests, ZeroSizedClear) { } DAWN_INSTANTIATE_TEST(ClearBufferTests, + D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), @@ -2712,8 +2717,9 @@ TEST_P(CopyToDepthStencilTextureAfterDestroyingBigBufferTests, DoTest) { DAWN_INSTANTIATE_TEST_P( CopyToDepthStencilTextureAfterDestroyingBigBufferTests, - {D3D12Backend(), D3D12Backend({"d3d12_force_clear_copyable_depth_stencil_texture_on_creation"}), - MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()}, + {D3D11Backend(), D3D12Backend(), + D3D12Backend({"d3d12_force_clear_copyable_depth_stencil_texture_on_creation"}), MetalBackend(), + OpenGLBackend(), OpenGLESBackend(), VulkanBackend()}, {wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Stencil8}, {InitializationMethod::CopyBufferToTexture, InitializationMethod::WriteTexture, InitializationMethod::CopyTextureToTexture},