| // Copyright 2019 The Dawn Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "dawn_native/d3d12/UtilsD3D12.h" |
| |
| #include "common/Assert.h" |
| #include "dawn_native/Format.h" |
| #include "dawn_native/d3d12/BufferD3D12.h" |
| #include "dawn_native/d3d12/CommandRecordingContext.h" |
| #include "dawn_native/d3d12/D3D12Error.h" |
| #include "dawn_native/d3d12/DeviceD3D12.h" |
| |
| #include <stringapiset.h> |
| |
| namespace dawn::native::d3d12 { |
| |
| ResultOrError<std::wstring> ConvertStringToWstring(const char* str) { |
| size_t len = strlen(str); |
| if (len == 0) { |
| return std::wstring(); |
| } |
| int numChars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, len, nullptr, 0); |
| if (numChars == 0) { |
| return DAWN_INTERNAL_ERROR("Failed to convert string to wide string"); |
| } |
| std::wstring result; |
| result.resize(numChars); |
| int numConvertedChars = |
| MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, len, &result[0], numChars); |
| if (numConvertedChars != numChars) { |
| return DAWN_INTERNAL_ERROR("Failed to convert string to wide string"); |
| } |
| return std::move(result); |
| } |
| |
| D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(wgpu::CompareFunction func) { |
| switch (func) { |
| case wgpu::CompareFunction::Never: |
| return D3D12_COMPARISON_FUNC_NEVER; |
| case wgpu::CompareFunction::Less: |
| return D3D12_COMPARISON_FUNC_LESS; |
| case wgpu::CompareFunction::LessEqual: |
| return D3D12_COMPARISON_FUNC_LESS_EQUAL; |
| case wgpu::CompareFunction::Greater: |
| return D3D12_COMPARISON_FUNC_GREATER; |
| case wgpu::CompareFunction::GreaterEqual: |
| return D3D12_COMPARISON_FUNC_GREATER_EQUAL; |
| case wgpu::CompareFunction::Equal: |
| return D3D12_COMPARISON_FUNC_EQUAL; |
| case wgpu::CompareFunction::NotEqual: |
| return D3D12_COMPARISON_FUNC_NOT_EQUAL; |
| case wgpu::CompareFunction::Always: |
| return D3D12_COMPARISON_FUNC_ALWAYS; |
| |
| case wgpu::CompareFunction::Undefined: |
| UNREACHABLE(); |
| } |
| } |
| |
| D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture, |
| uint32_t level, |
| uint32_t layer, |
| Aspect aspect) { |
| D3D12_TEXTURE_COPY_LOCATION copyLocation; |
| copyLocation.pResource = texture->GetD3D12Resource(); |
| copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; |
| copyLocation.SubresourceIndex = texture->GetSubresourceIndex(level, layer, aspect); |
| |
| return copyLocation; |
| } |
| |
| D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion( |
| const Texture* texture, |
| ID3D12Resource* bufferResource, |
| const Extent3D& bufferSize, |
| const uint64_t offset, |
| const uint32_t rowPitch, |
| Aspect aspect) { |
| D3D12_TEXTURE_COPY_LOCATION bufferLocation; |
| bufferLocation.pResource = bufferResource; |
| bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; |
| bufferLocation.PlacedFootprint.Offset = offset; |
| bufferLocation.PlacedFootprint.Footprint.Format = |
| texture->GetD3D12CopyableSubresourceFormat(aspect); |
| bufferLocation.PlacedFootprint.Footprint.Width = bufferSize.width; |
| bufferLocation.PlacedFootprint.Footprint.Height = bufferSize.height; |
| bufferLocation.PlacedFootprint.Footprint.Depth = bufferSize.depthOrArrayLayers; |
| bufferLocation.PlacedFootprint.Footprint.RowPitch = rowPitch; |
| return bufferLocation; |
| } |
| |
| D3D12_BOX ComputeD3D12BoxFromOffsetAndSize(const Origin3D& offset, const Extent3D& copySize) { |
| D3D12_BOX sourceRegion; |
| sourceRegion.left = offset.x; |
| sourceRegion.top = offset.y; |
| sourceRegion.front = offset.z; |
| sourceRegion.right = offset.x + copySize.width; |
| sourceRegion.bottom = offset.y + copySize.height; |
| sourceRegion.back = offset.z + copySize.depthOrArrayLayers; |
| return sourceRegion; |
| } |
| |
| bool IsTypeless(DXGI_FORMAT format) { |
| // List generated from <dxgiformat.h> |
| switch (format) { |
| case DXGI_FORMAT_R32G32B32A32_TYPELESS: |
| case DXGI_FORMAT_R32G32B32_TYPELESS: |
| case DXGI_FORMAT_R16G16B16A16_TYPELESS: |
| case DXGI_FORMAT_R32G32_TYPELESS: |
| case DXGI_FORMAT_R32G8X24_TYPELESS: |
| case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: |
| case DXGI_FORMAT_R10G10B10A2_TYPELESS: |
| case DXGI_FORMAT_R8G8B8A8_TYPELESS: |
| case DXGI_FORMAT_R16G16_TYPELESS: |
| case DXGI_FORMAT_R32_TYPELESS: |
| case DXGI_FORMAT_R24G8_TYPELESS: |
| case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: |
| case DXGI_FORMAT_R8G8_TYPELESS: |
| case DXGI_FORMAT_R16_TYPELESS: |
| case DXGI_FORMAT_R8_TYPELESS: |
| case DXGI_FORMAT_BC1_TYPELESS: |
| case DXGI_FORMAT_BC2_TYPELESS: |
| case DXGI_FORMAT_BC3_TYPELESS: |
| case DXGI_FORMAT_BC4_TYPELESS: |
| case DXGI_FORMAT_BC5_TYPELESS: |
| case DXGI_FORMAT_B8G8R8A8_TYPELESS: |
| case DXGI_FORMAT_B8G8R8X8_TYPELESS: |
| case DXGI_FORMAT_BC6H_TYPELESS: |
| case DXGI_FORMAT_BC7_TYPELESS: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void RecordCopyBufferToTextureFromTextureCopySplit(ID3D12GraphicsCommandList* commandList, |
| const TextureCopySubresource& baseCopySplit, |
| ID3D12Resource* bufferResource, |
| uint64_t baseOffset, |
| uint64_t bufferBytesPerRow, |
| Texture* texture, |
| uint32_t textureMiplevel, |
| uint32_t textureLayer, |
| Aspect aspect) { |
| ASSERT(HasOneBit(aspect)); |
| const D3D12_TEXTURE_COPY_LOCATION textureLocation = |
| ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureLayer, aspect); |
| |
| for (uint32_t i = 0; i < baseCopySplit.count; ++i) { |
| const TextureCopySubresource::CopyInfo& info = baseCopySplit.copies[i]; |
| |
| // TODO(jiawei.shao@intel.com): pre-compute bufferLocation and sourceRegion as |
| // members in TextureCopySubresource::CopyInfo. |
| const uint64_t offsetBytes = info.alignedOffset + baseOffset; |
| const D3D12_TEXTURE_COPY_LOCATION bufferLocation = |
| ComputeBufferLocationForCopyTextureRegion(texture, bufferResource, info.bufferSize, |
| offsetBytes, bufferBytesPerRow, aspect); |
| const D3D12_BOX sourceRegion = |
| ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize); |
| |
| commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x, |
| info.textureOffset.y, info.textureOffset.z, |
| &bufferLocation, &sourceRegion); |
| } |
| } |
| |
| void CopyBufferTo2DTextureWithCopySplit(CommandRecordingContext* commandContext, |
| const TextureCopy& textureCopy, |
| ID3D12Resource* bufferResource, |
| const uint64_t offset, |
| const uint32_t bytesPerRow, |
| const uint32_t rowsPerImage, |
| const Extent3D& copySize, |
| Texture* texture, |
| Aspect aspect) { |
| ASSERT(HasOneBit(aspect)); |
| // See comments in Compute2DTextureCopySplits() for more details. |
| const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(aspect).block; |
| const TextureCopySplits copySplits = Compute2DTextureCopySplits( |
| textureCopy.origin, copySize, blockInfo, offset, bytesPerRow, rowsPerImage); |
| |
| const uint64_t bytesPerLayer = bytesPerRow * rowsPerImage; |
| |
| // copySplits.copySubresources[1] is always calculated for the second copy layer with |
| // extra "bytesPerLayer" copy offset compared with the first copy layer. So |
| // here we use an array bufferOffsetsForNextLayer to record the extra offsets |
| // for each copy layer: bufferOffsetsForNextLayer[0] is the extra offset for |
| // the next copy layer that uses copySplits.copySubresources[0], and |
| // bufferOffsetsForNextLayer[1] is the extra offset for the next copy layer |
| // that uses copySplits.copySubresources[1]. |
| std::array<uint64_t, TextureCopySplits::kMaxTextureCopySubresources> |
| bufferOffsetsForNextLayer = {{0u, 0u}}; |
| |
| for (uint32_t copyLayer = 0; copyLayer < copySize.depthOrArrayLayers; ++copyLayer) { |
| const uint32_t splitIndex = copyLayer % copySplits.copySubresources.size(); |
| |
| const TextureCopySubresource& copySplitPerLayerBase = |
| copySplits.copySubresources[splitIndex]; |
| const uint64_t bufferOffsetForNextLayer = bufferOffsetsForNextLayer[splitIndex]; |
| const uint32_t copyTextureLayer = copyLayer + textureCopy.origin.z; |
| |
| RecordCopyBufferToTextureFromTextureCopySplit( |
| commandContext->GetCommandList(), copySplitPerLayerBase, bufferResource, |
| bufferOffsetForNextLayer, bytesPerRow, texture, textureCopy.mipLevel, |
| copyTextureLayer, aspect); |
| |
| bufferOffsetsForNextLayer[splitIndex] += |
| bytesPerLayer * copySplits.copySubresources.size(); |
| } |
| } |
| |
| void CopyBufferTo3DTexture(CommandRecordingContext* commandContext, |
| const TextureCopy& textureCopy, |
| ID3D12Resource* bufferResource, |
| const uint64_t offset, |
| const uint32_t bytesPerRow, |
| const uint32_t rowsPerImage, |
| const Extent3D& copySize, |
| Texture* texture, |
| Aspect aspect) { |
| ASSERT(HasOneBit(aspect)); |
| // See comments in Compute3DTextureCopySplits() for more details. |
| const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(aspect).block; |
| const TextureCopySubresource copyRegions = Compute3DTextureCopySplits( |
| textureCopy.origin, copySize, blockInfo, offset, bytesPerRow, rowsPerImage); |
| |
| RecordCopyBufferToTextureFromTextureCopySplit(commandContext->GetCommandList(), copyRegions, |
| bufferResource, 0, bytesPerRow, texture, |
| textureCopy.mipLevel, 0, aspect); |
| } |
| |
| void RecordCopyBufferToTexture(CommandRecordingContext* commandContext, |
| const TextureCopy& textureCopy, |
| ID3D12Resource* bufferResource, |
| const uint64_t offset, |
| const uint32_t bytesPerRow, |
| const uint32_t rowsPerImage, |
| const Extent3D& copySize, |
| Texture* texture, |
| Aspect aspect) { |
| // Record the CopyTextureRegion commands for 3D textures. Multiple depths of 3D |
| // textures can be copied in one shot and copySplits are not needed. |
| if (texture->GetDimension() == wgpu::TextureDimension::e3D) { |
| CopyBufferTo3DTexture(commandContext, textureCopy, bufferResource, offset, bytesPerRow, |
| rowsPerImage, copySize, texture, aspect); |
| } else { |
| // Compute the copySplits and record the CopyTextureRegion commands for 2D |
| // textures. |
| CopyBufferTo2DTextureWithCopySplit(commandContext, textureCopy, bufferResource, offset, |
| bytesPerRow, rowsPerImage, copySize, texture, |
| aspect); |
| } |
| } |
| |
| void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList, |
| const TextureCopySubresource& baseCopySplit, |
| Buffer* buffer, |
| uint64_t baseOffset, |
| uint64_t bufferBytesPerRow, |
| Texture* texture, |
| uint32_t textureMiplevel, |
| uint32_t textureLayer, |
| Aspect aspect) { |
| const D3D12_TEXTURE_COPY_LOCATION textureLocation = |
| ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureLayer, aspect); |
| |
| for (uint32_t i = 0; i < baseCopySplit.count; ++i) { |
| const TextureCopySubresource::CopyInfo& info = baseCopySplit.copies[i]; |
| |
| // TODO(jiawei.shao@intel.com): pre-compute bufferLocation and sourceRegion as |
| // members in TextureCopySubresource::CopyInfo. |
| const uint64_t offsetBytes = info.alignedOffset + baseOffset; |
| const D3D12_TEXTURE_COPY_LOCATION bufferLocation = |
| ComputeBufferLocationForCopyTextureRegion(texture, buffer->GetD3D12Resource(), |
| info.bufferSize, offsetBytes, |
| bufferBytesPerRow, aspect); |
| const D3D12_BOX sourceRegion = |
| ComputeD3D12BoxFromOffsetAndSize(info.textureOffset, info.copySize); |
| |
| commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x, |
| info.bufferOffset.y, info.bufferOffset.z, |
| &textureLocation, &sourceRegion); |
| } |
| } |
| |
| void Copy2DTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList, |
| const TextureCopy& textureCopy, |
| const BufferCopy& bufferCopy, |
| Texture* texture, |
| Buffer* buffer, |
| const Extent3D& copySize) { |
| ASSERT(HasOneBit(textureCopy.aspect)); |
| const TexelBlockInfo& blockInfo = |
| texture->GetFormat().GetAspectInfo(textureCopy.aspect).block; |
| |
| // See comments around Compute2DTextureCopySplits() for more details. |
| const TextureCopySplits copySplits = |
| Compute2DTextureCopySplits(textureCopy.origin, copySize, blockInfo, bufferCopy.offset, |
| bufferCopy.bytesPerRow, bufferCopy.rowsPerImage); |
| |
| const uint64_t bytesPerLayer = bufferCopy.bytesPerRow * bufferCopy.rowsPerImage; |
| |
| // copySplits.copySubresources[1] is always calculated for the second copy layer with |
| // extra "bytesPerLayer" copy offset compared with the first copy layer. So |
| // here we use an array bufferOffsetsForNextLayer to record the extra offsets |
| // for each copy layer: bufferOffsetsForNextLayer[0] is the extra offset for |
| // the next copy layer that uses copySplits.copySubresources[0], and |
| // bufferOffsetsForNextLayer[1] is the extra offset for the next copy layer |
| // that uses copySplits.copySubresources[1]. |
| std::array<uint64_t, TextureCopySplits::kMaxTextureCopySubresources> |
| bufferOffsetsForNextLayer = {{0u, 0u}}; |
| for (uint32_t copyLayer = 0; copyLayer < copySize.depthOrArrayLayers; ++copyLayer) { |
| const uint32_t splitIndex = copyLayer % copySplits.copySubresources.size(); |
| |
| const TextureCopySubresource& copySplitPerLayerBase = |
| copySplits.copySubresources[splitIndex]; |
| const uint64_t bufferOffsetForNextLayer = bufferOffsetsForNextLayer[splitIndex]; |
| const uint32_t copyTextureLayer = copyLayer + textureCopy.origin.z; |
| |
| RecordCopyTextureToBufferFromTextureCopySplit( |
| commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextLayer, |
| bufferCopy.bytesPerRow, texture, textureCopy.mipLevel, copyTextureLayer, |
| textureCopy.aspect); |
| |
| bufferOffsetsForNextLayer[splitIndex] += |
| bytesPerLayer * copySplits.copySubresources.size(); |
| } |
| } |
| |
| void Copy3DTextureToBuffer(ID3D12GraphicsCommandList* commandList, |
| const TextureCopy& textureCopy, |
| const BufferCopy& bufferCopy, |
| Texture* texture, |
| Buffer* buffer, |
| const Extent3D& copySize) { |
| ASSERT(HasOneBit(textureCopy.aspect)); |
| const TexelBlockInfo& blockInfo = |
| texture->GetFormat().GetAspectInfo(textureCopy.aspect).block; |
| |
| // See comments around Compute3DTextureCopySplits() for more details. |
| const TextureCopySubresource copyRegions = |
| Compute3DTextureCopySplits(textureCopy.origin, copySize, blockInfo, bufferCopy.offset, |
| bufferCopy.bytesPerRow, bufferCopy.rowsPerImage); |
| |
| RecordCopyTextureToBufferFromTextureCopySplit(commandList, copyRegions, buffer, 0, |
| bufferCopy.bytesPerRow, texture, |
| textureCopy.mipLevel, 0, textureCopy.aspect); |
| } |
| |
| void RecordCopyTextureToBuffer(ID3D12GraphicsCommandList* commandList, |
| const TextureCopy& textureCopy, |
| const BufferCopy& bufferCopy, |
| Texture* texture, |
| Buffer* buffer, |
| const Extent3D& copySize) { |
| if (texture->GetDimension() == wgpu::TextureDimension::e3D) { |
| Copy3DTextureToBuffer(commandList, textureCopy, bufferCopy, texture, buffer, copySize); |
| } else { |
| Copy2DTextureToBufferWithCopySplit(commandList, textureCopy, bufferCopy, texture, |
| buffer, copySize); |
| } |
| } |
| |
| void SetDebugName(Device* device, ID3D12Object* object, const char* prefix, std::string label) { |
| if (!object) { |
| return; |
| } |
| |
| if (label.empty() || !device->IsToggleEnabled(Toggle::UseUserDefinedLabelsInBackend)) { |
| object->SetPrivateData(WKPDID_D3DDebugObjectName, strlen(prefix), prefix); |
| return; |
| } |
| |
| std::string objectName = prefix; |
| objectName += "_"; |
| objectName += label; |
| object->SetPrivateData(WKPDID_D3DDebugObjectName, objectName.length(), objectName.c_str()); |
| } |
| |
| } // namespace dawn::native::d3d12 |