diff --git a/include/API/Enums.h b/include/API/Enums.h index d050b40fa..f7f051c2e 100644 --- a/include/API/Enums.h +++ b/include/API/Enums.h @@ -16,10 +16,12 @@ enum class ResourceKind { StructuredBuffer, ByteAddressBuffer, Texture2D, + Texture2DArray, RWBuffer, RWStructuredBuffer, RWByteAddressBuffer, RWTexture2D, + RWTexture2DArray, ConstantBuffer, Sampler, SampledTexture2D, diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index 9cf0e5f77..308691848 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -81,6 +81,7 @@ static inline DescriptorKind getDescriptorKind(ResourceKind RK) { case ResourceKind::StructuredBuffer: case ResourceKind::ByteAddressBuffer: case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::AccelerationStructure: return DescriptorKind::SRV; @@ -88,6 +89,7 @@ static inline DescriptorKind getDescriptorKind(ResourceKind RK) { case ResourceKind::RWBuffer: case ResourceKind::RWByteAddressBuffer: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: return DescriptorKind::UAV; case ResourceKind::ConstantBuffer: @@ -145,6 +147,7 @@ struct OutputProperties { int Width; int Depth; int MipLevels = 1; + int ArraySize = 1; }; static inline uint32_t getFormatSize(DataFormat Format) { @@ -246,7 +249,9 @@ struct Resource { case ResourceKind::Buffer: case ResourceKind::RWBuffer: case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::Sampler: case ResourceKind::SampledTexture2D: case ResourceKind::AccelerationStructure: @@ -273,7 +278,9 @@ struct Resource { case ResourceKind::RWByteAddressBuffer: case ResourceKind::ConstantBuffer: case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::SampledTexture2D: case ResourceKind::AccelerationStructure: return false; @@ -294,7 +301,9 @@ struct Resource { case ResourceKind::AccelerationStructure: return false; case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::SampledTexture2D: return true; } @@ -356,6 +365,7 @@ struct Resource { case ResourceKind::StructuredBuffer: case ResourceKind::ByteAddressBuffer: case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::ConstantBuffer: case ResourceKind::Sampler: case ResourceKind::SampledTexture2D: @@ -365,6 +375,7 @@ struct Resource { case ResourceKind::RWStructuredBuffer: case ResourceKind::RWByteAddressBuffer: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: return true; } llvm_unreachable("All cases handled"); @@ -829,10 +840,12 @@ template <> struct ScalarEnumerationTraits { ENUM_CASE(StructuredBuffer); ENUM_CASE(ByteAddressBuffer); ENUM_CASE(Texture2D); + ENUM_CASE(Texture2DArray); ENUM_CASE(RWBuffer); ENUM_CASE(RWStructuredBuffer); ENUM_CASE(RWByteAddressBuffer); ENUM_CASE(RWTexture2D); + ENUM_CASE(RWTexture2DArray); ENUM_CASE(ConstantBuffer); ENUM_CASE(Sampler); ENUM_CASE(SampledTexture2D); diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index edfaccb7b..0bfd8e0c8 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -226,7 +226,9 @@ static D3D12_RESOURCE_DIMENSION getDXDimension(ResourceKind RK) { case ResourceKind::AccelerationStructure: return D3D12_RESOURCE_DIMENSION_BUFFER; case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: return D3D12_RESOURCE_DIMENSION_TEXTURE2D; case ResourceKind::Sampler: return D3D12_RESOURCE_DIMENSION_UNKNOWN; @@ -241,16 +243,38 @@ getResourceDescription(const Resource &R) { const D3D12_RESOURCE_DIMENSION Dimension = getDXDimension(R.Kind); const offloadtest::CPUBuffer &B = *R.BufferPtr; - if (B.OutputProps.MipLevels != 1) + if (B.OutputProps.MipLevels < 1) + return llvm::createStringError(std::errc::invalid_argument, + "MipLevels must be >= 1."); + if (B.OutputProps.MipLevels > 1 && + getDescriptorKind(R.Kind) != DescriptorKind::SRV) return llvm::createStringError(std::errc::not_supported, - "Multiple mip levels are not yet supported " - "for DirectX textures."); + "Multiple mip levels are only supported " + "for read-only SRV textures."); + + if (B.OutputProps.ArraySize < 1) + return llvm::createStringError(std::errc::invalid_argument, + "OutputProps.ArraySize must be >= 1."); + + if (B.OutputProps.ArraySize > 1 && R.Kind != ResourceKind::Texture2DArray && + R.Kind != ResourceKind::RWTexture2DArray) + return llvm::createStringError( + std::errc::not_supported, + "OutputProps.ArraySize > 1 is only supported for Texture2DArray and " + "RWTexture2DArray."); const DXGI_FORMAT Format = R.isTexture() ? getDXFormat(B.Format, B.Channels) : DXGI_FORMAT_UNKNOWN; const uint32_t Width = R.isTexture() ? B.OutputProps.Width : getUAVBufferSize(R); const uint32_t Height = R.isTexture() ? B.OutputProps.Height : 1; + const uint16_t MipLevels = + R.isTexture() ? static_cast(B.OutputProps.MipLevels) : 1; + const uint16_t DepthOrArraySize = + (R.Kind == ResourceKind::Texture2DArray || + R.Kind == ResourceKind::RWTexture2DArray) + ? static_cast(B.OutputProps.ArraySize) + : 1; D3D12_TEXTURE_LAYOUT Layout; if (R.isTexture()) @@ -265,8 +289,9 @@ getResourceDescription(const Resource &R) { const D3D12_RESOURCE_FLAGS Flags = R.isReadWrite() ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS : D3D12_RESOURCE_FLAG_NONE; - const D3D12_RESOURCE_DESC ResDesc = {Dimension, 0, Width, Height, 1, 1, - Format, {1, 0}, Layout, Flags}; + const D3D12_RESOURCE_DESC ResDesc = { + Dimension, 0, Width, Height, DepthOrArraySize, + MipLevels, Format, {1, 0}, Layout, Flags}; return ResDesc; } @@ -294,12 +319,21 @@ static D3D12_SHADER_RESOURCE_VIEW_DESC getSRVDescription(const Resource &R) { break; case ResourceKind::Texture2D: Desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - Desc.Texture2D = D3D12_TEX2D_SRV{0, 1, 0, 0}; + Desc.Texture2D = D3D12_TEX2D_SRV{ + 0, static_cast(R.BufferPtr->OutputProps.MipLevels), 0, 0.0f}; + break; + case ResourceKind::Texture2DArray: + Desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + Desc.Texture2DArray = D3D12_TEX2D_ARRAY_SRV{ + 0, static_cast(R.BufferPtr->OutputProps.MipLevels), + 0, static_cast(R.BufferPtr->OutputProps.ArraySize), + 0, 0}; break; case ResourceKind::RWStructuredBuffer: case ResourceKind::RWBuffer: case ResourceKind::RWByteAddressBuffer: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::ConstantBuffer: case ResourceKind::Sampler: llvm_unreachable("Not an SRV type!"); @@ -337,10 +371,16 @@ static D3D12_UNORDERED_ACCESS_VIEW_DESC getUAVDescription(const Resource &R) { Desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; Desc.Texture2D = D3D12_TEX2D_UAV{0, 0}; break; + case ResourceKind::RWTexture2DArray: + Desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + Desc.Texture2DArray = D3D12_TEX2D_ARRAY_UAV{ + 0, 0, static_cast(R.BufferPtr->OutputProps.ArraySize), 0}; + break; case ResourceKind::StructuredBuffer: case ResourceKind::Buffer: case ResourceKind::ByteAddressBuffer: case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::ConstantBuffer: case ResourceKind::Sampler: llvm_unreachable("Not a UAV type!"); @@ -1559,21 +1599,34 @@ class DXDevice : public offloadtest::Device { return std::make_unique(Desc); } - void addResourceUploadCommands(Resource &R, InvocationState &IS, - ComPtr Destination, - ComPtr Source) { + void addResourceUploadCommands( + Resource &R, InvocationState &IS, ComPtr Destination, + ComPtr Source, + llvm::ArrayRef SubresFootprints = + {}) { addUploadBeginBarrier(IS, Destination); if (R.isTexture()) { const offloadtest::CPUBuffer &B = *R.BufferPtr; - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{ - 0, CD3DX12_SUBRESOURCE_FOOTPRINT( - getDXFormat(B.Format, B.Channels), B.OutputProps.Width, - B.OutputProps.Height, 1, - B.OutputProps.Width * B.getElementSize())}; - const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), 0); - const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(), Footprint); - - IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr); + if (!SubresFootprints.empty()) { + // Multi-subresource path (mips and/or array slices). Subresource index + // ordering matches D3D12: Sub = Slice * MipLevels + Mip. + for (uint32_t Sub = 0; Sub < SubresFootprints.size(); ++Sub) { + const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), Sub); + const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(), + SubresFootprints[Sub]); + IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr); + } + } else { + // Single-subresource fast path (no mips, no array slices). + const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{ + 0, CD3DX12_SUBRESOURCE_FOOTPRINT( + getDXFormat(B.Format, B.Channels), B.OutputProps.Width, + B.OutputProps.Height, 1, + B.OutputProps.Width * B.getElementSize())}; + const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(Destination.Get(), 0); + const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(Source.Get(), Footprint); + IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr); + } } else IS.CB->CmdList->CopyBufferRegion(Destination.Get(), 0, Source.Get(), 0, R.size()); @@ -1655,8 +1708,36 @@ class DXDevice : public offloadtest::Device { const D3D12_RESOURCE_DESC ResDesc = *ResDescOrErr; const D3D12_HEAP_PROPERTIES UploadHeapProp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); - const D3D12_RESOURCE_DESC UploadResDesc = - CD3DX12_RESOURCE_DESC::Buffer(R.size()); + + const bool IsTexture = R.isTexture(); + const uint32_t MipLevels = + IsTexture ? R.BufferPtr->OutputProps.MipLevels : 1; + const uint32_t ArraySize = + IsTexture && (R.Kind == ResourceKind::Texture2DArray || + R.Kind == ResourceKind::RWTexture2DArray) + ? R.BufferPtr->OutputProps.ArraySize + : 1; + // Use the GetCopyableFootprints layout whenever a texture has multiple + // subresources (mip > 1, array slices > 1, or both). This keeps row-pitch + // alignment (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) handled by the runtime + // for every (mip, slice) instead of the tight Width*EltSize assumption. + const bool NeedsSubresourceLayout = + IsTexture && (MipLevels > 1 || ArraySize > 1); + const uint32_t NumSubresources = MipLevels * ArraySize; + llvm::SmallVector SubresFootprints; + llvm::SmallVector SubresNumRows; + llvm::SmallVector SubresRowSizes; + UINT64 SubresTotalBytes = 0; + if (NeedsSubresourceLayout) { + SubresFootprints.resize(NumSubresources); + SubresNumRows.resize(NumSubresources); + SubresRowSizes.resize(NumSubresources); + Device->GetCopyableFootprints( + &ResDesc, 0, NumSubresources, 0, SubresFootprints.data(), + SubresNumRows.data(), SubresRowSizes.data(), &SubresTotalBytes); + } + const D3D12_RESOURCE_DESC UploadResDesc = CD3DX12_RESOURCE_DESC::Buffer( + NeedsSubresourceLayout ? SubresTotalBytes : R.size()); uint32_t RegOffset = 0; @@ -1708,14 +1789,35 @@ class DXDevice : public offloadtest::Device { // Upload data initialization void *ResDataPtr = nullptr; if (SUCCEEDED(UploadBuffer->Map(0, NULL, &ResDataPtr))) { - memcpy(ResDataPtr, ResData.get(), R.size()); + if (NeedsSubresourceLayout) { + // Source CPU data is tightly packed in subresource order + // (slice-major, mip-major within slice: Sub = Slice * MipLevels + Mip + // matching D3D12). Destination upload buffer has D3D12-aligned + // per-subresource layout from GetCopyableFootprints; copy each + // subresource row-by-row applying the possibly-padded row pitch. + const uint8_t *Src = reinterpret_cast(ResData.get()); + uint8_t *Dst = static_cast(ResDataPtr); + for (uint32_t Sub = 0; Sub < NumSubresources; ++Sub) { + const auto &FP = SubresFootprints[Sub]; + const size_t TightRowBytes = + static_cast(SubresRowSizes[Sub]); + const size_t PaddedRowPitch = + static_cast(FP.Footprint.RowPitch); + for (UINT Row = 0; Row < SubresNumRows[Sub]; ++Row) + memcpy(Dst + FP.Offset + Row * PaddedRowPitch, + Src + Row * TightRowBytes, TightRowBytes); + Src += TightRowBytes * SubresNumRows[Sub]; + } + } else { + memcpy(ResDataPtr, ResData.get(), R.size()); + } UploadBuffer->Unmap(0, nullptr); } else { return llvm::createStringError(std::errc::io_error, "Failed to map SRV upload buffer."); } - addResourceUploadCommands(R, IS, Buffer, UploadBuffer); + addResourceUploadCommands(R, IS, Buffer, UploadBuffer, SubresFootprints); Bundle.emplace_back(UploadBuffer, Buffer, nullptr, Heap); RegOffset++; @@ -1756,8 +1858,33 @@ class DXDevice : public offloadtest::Device { const D3D12_HEAP_PROPERTIES UploadHeapProp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); - const D3D12_RESOURCE_DESC UploadResDesc = - CD3DX12_RESOURCE_DESC::Buffer(BufferSize); + const bool IsTexture = R.isTexture(); + const uint32_t MipLevels = + IsTexture ? R.BufferPtr->OutputProps.MipLevels : 1; + const uint32_t ArraySize = + IsTexture && (R.Kind == ResourceKind::Texture2DArray || + R.Kind == ResourceKind::RWTexture2DArray) + ? R.BufferPtr->OutputProps.ArraySize + : 1; + // UAVs only ever address a single mip per descriptor; + // getResourceDescription already rejects mip>1 for non-SRV kinds. + // Multi-subresource layout is only needed here for array slices. + const bool NeedsSubresourceLayout = IsTexture && ArraySize > 1; + const uint32_t NumSubresources = MipLevels * ArraySize; + llvm::SmallVector SubresFootprints; + llvm::SmallVector SubresNumRows; + llvm::SmallVector SubresRowSizes; + UINT64 SubresTotalBytes = 0; + if (NeedsSubresourceLayout) { + SubresFootprints.resize(NumSubresources); + SubresNumRows.resize(NumSubresources); + SubresRowSizes.resize(NumSubresources); + Device->GetCopyableFootprints( + &ResDesc, 0, NumSubresources, 0, SubresFootprints.data(), + SubresNumRows.data(), SubresRowSizes.data(), &SubresTotalBytes); + } + const D3D12_RESOURCE_DESC UploadResDesc = CD3DX12_RESOURCE_DESC::Buffer( + NeedsSubresourceLayout ? SubresTotalBytes : BufferSize); uint32_t RegOffset = 0; @@ -1815,14 +1942,30 @@ class DXDevice : public offloadtest::Device { // Upload data initialization void *ResDataPtr = nullptr; if (SUCCEEDED(UploadBuffer->Map(0, NULL, &ResDataPtr))) { - memcpy(ResDataPtr, ResData.get(), R.size()); + if (NeedsSubresourceLayout) { + const uint8_t *Src = reinterpret_cast(ResData.get()); + uint8_t *Dst = static_cast(ResDataPtr); + for (uint32_t Sub = 0; Sub < NumSubresources; ++Sub) { + const auto &FP = SubresFootprints[Sub]; + const size_t TightRowBytes = + static_cast(SubresRowSizes[Sub]); + const size_t PaddedRowPitch = + static_cast(FP.Footprint.RowPitch); + for (UINT Row = 0; Row < SubresNumRows[Sub]; ++Row) + memcpy(Dst + FP.Offset + Row * PaddedRowPitch, + Src + Row * TightRowBytes, TightRowBytes); + Src += TightRowBytes * SubresNumRows[Sub]; + } + } else { + memcpy(ResDataPtr, ResData.get(), R.size()); + } UploadBuffer->Unmap(0, nullptr); } else { return llvm::createStringError(std::errc::io_error, "Failed to map UAV upload buffer."); } - addResourceUploadCommands(R, IS, Buffer, UploadBuffer); + addResourceUploadCommands(R, IS, Buffer, UploadBuffer, SubresFootprints); Bundle.emplace_back(UploadBuffer, Buffer, std::move(*ReadbackOrErr), Heap); @@ -2167,20 +2310,30 @@ class DXDevice : public offloadtest::Device { auto CopyBackResource = [&IS, this](ResourcePair &R) { if (R.first->isTexture()) { const offloadtest::CPUBuffer &B = *R.first->BufferPtr; - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint{ + const uint32_t RowPitch = B.OutputProps.Width * B.getElementSize(); + const uint32_t SliceBytes = RowPitch * B.OutputProps.Height; + const uint32_t NumSlices = + R.first->Kind == ResourceKind::RWTexture2DArray + ? B.OutputProps.ArraySize + : 1; + const D3D12_PLACED_SUBRESOURCE_FOOTPRINT BaseFootprint{ 0, CD3DX12_SUBRESOURCE_FOOTPRINT( getDXFormat(B.Format, B.Channels), B.OutputProps.Width, - B.OutputProps.Height, 1, - B.OutputProps.Width * B.getElementSize())}; + B.OutputProps.Height, 1, RowPitch)}; for (const ResourceSet &RS : R.second) { if (RS.Readback == nullptr) continue; const DXBuffer &ReadbackDX = llvm::cast(*RS.Readback); addReadbackBeginBarrier(IS, RS.Buffer); - const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(ReadbackDX.Buffer.Get(), - Footprint); - const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(RS.Buffer.Get(), 0); - IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, nullptr); + for (uint32_t Slice = 0; Slice < NumSlices; ++Slice) { + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint = BaseFootprint; + Footprint.Offset = Slice * SliceBytes; + const CD3DX12_TEXTURE_COPY_LOCATION DstLoc(ReadbackDX.Buffer.Get(), + Footprint); + const CD3DX12_TEXTURE_COPY_LOCATION SrcLoc(RS.Buffer.Get(), Slice); + IS.CB->CmdList->CopyTextureRegion(&DstLoc, 0, 0, 0, &SrcLoc, + nullptr); + } addReadbackEndBarrier(IS, RS.Buffer); } return; diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index 7959daa9b..bbb4e81f7 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -1092,6 +1092,10 @@ class MTLDevice : public offloadtest::Device { Desc = MTL::TextureDescriptor::texture2DDescriptor(Format, Width, Height, false); break; + case ResourceKind::Texture2DArray: + llvm_unreachable("Texture2DArray is not yet supported in Metal."); + case ResourceKind::RWTexture2DArray: + llvm_unreachable("RWTexture2DArray is not yet supported in Metal."); case ResourceKind::Sampler: llvm_unreachable("Not implemented yet."); case ResourceKind::SampledTexture2D: diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index 282b2b9c2..0a4358827 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -77,9 +77,11 @@ static VkDescriptorType getDescriptorType(const ResourceKind RK) { return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; case ResourceKind::ByteAddressBuffer: @@ -163,7 +165,9 @@ static VkBufferUsageFlagBits getFlagBits(const ResourceKind RK) { case ResourceKind::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::Sampler: case ResourceKind::SampledTexture2D: case ResourceKind::AccelerationStructure: @@ -179,6 +183,9 @@ static VkImageViewType getImageViewType(const ResourceKind RK) { case ResourceKind::RWTexture2D: case ResourceKind::SampledTexture2D: return VK_IMAGE_VIEW_TYPE_2D; + case ResourceKind::Texture2DArray: + case ResourceKind::RWTexture2DArray: + return VK_IMAGE_VIEW_TYPE_2D_ARRAY; case ResourceKind::Buffer: case ResourceKind::RWBuffer: case ResourceKind::ByteAddressBuffer: @@ -196,7 +203,9 @@ static VkImageViewType getImageViewType(const ResourceKind RK) { static VkImageType getVKImageType(const ResourceKind RK) { switch (RK) { case ResourceKind::Texture2D: + case ResourceKind::Texture2DArray: case ResourceKind::RWTexture2D: + case ResourceKind::RWTexture2DArray: case ResourceKind::SampledTexture2D: return VK_IMAGE_TYPE_2D; default: diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index 3940a550d..f53b517d2 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -429,6 +429,7 @@ void MappingTraits::mapping(IO &I, H = std::max(1u, H / 2); D = std::max(1u, D / 2); } + ExpectedSize *= static_cast(std::max(1, B.OutputProps.ArraySize)); if (B.Size != ExpectedSize) I.setError(Twine("Buffer '") + B.Name + "' size (" + Twine(B.Size) + @@ -546,6 +547,7 @@ void MappingTraits::mapping( I.mapRequired("Width", P.Width); I.mapRequired("Depth", P.Depth); I.mapOptional("MipLevels", P.MipLevels, 1); + I.mapOptional("ArraySize", P.ArraySize, 1); } void MappingTraits::mapping( diff --git a/test/Feature/Textures/RWTexture2DArray.Store.test.yaml b/test/Feature/Textures/RWTexture2DArray.Store.test.yaml new file mode 100644 index 000000000..5cb9b0ae1 --- /dev/null +++ b/test/Feature/Textures/RWTexture2DArray.Store.test.yaml @@ -0,0 +1,96 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] RWTexture2DArray Out : register(u0); + +[numthreads(16, 4, 1)] +void main(uint3 DTid : SV_DispatchThreadID) { + // Encode (x, slice) into the texel so readback can verify per-slice writes. + // Slice 0: x ramp in the R channel; slice 1: x ramp in the G channel. + if (DTid.z == 0) + Out[DTid] = float4(DTid.x / 15.0f, 0.0f, 0.0f, 1.0f); + else + Out[DTid] = float4(0.0f, DTid.x / 15.0f, 0.0f, 1.0f); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + +DispatchParameters: + DispatchGroupCount: [ 1, 1, 2 ] + +Buffers: + - Name: Out + Format: Float32 + Channels: 4 + # Width * sizeof(float4) = 16 * 16 = 256 (matches D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) + OutputProps: { Width: 16, Height: 4, Depth: 1, ArraySize: 2 } + FillSize: 2048 # 16 * 4 * 4 channels * sizeof(float) * 2 slices + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ + # Slice 0 (red ramp, 4 identical rows of 16 texels) + 0.0,0,0,1, 0.0666666667,0,0,1, 0.1333333333,0,0,1, 0.2,0,0,1, + 0.2666666667,0,0,1, 0.3333333333,0,0,1, 0.4,0,0,1, 0.4666666667,0,0,1, + 0.5333333333,0,0,1, 0.6,0,0,1, 0.6666666667,0,0,1, 0.7333333333,0,0,1, + 0.8,0,0,1, 0.8666666667,0,0,1, 0.9333333333,0,0,1, 1.0,0,0,1, + 0.0,0,0,1, 0.0666666667,0,0,1, 0.1333333333,0,0,1, 0.2,0,0,1, + 0.2666666667,0,0,1, 0.3333333333,0,0,1, 0.4,0,0,1, 0.4666666667,0,0,1, + 0.5333333333,0,0,1, 0.6,0,0,1, 0.6666666667,0,0,1, 0.7333333333,0,0,1, + 0.8,0,0,1, 0.8666666667,0,0,1, 0.9333333333,0,0,1, 1.0,0,0,1, + 0.0,0,0,1, 0.0666666667,0,0,1, 0.1333333333,0,0,1, 0.2,0,0,1, + 0.2666666667,0,0,1, 0.3333333333,0,0,1, 0.4,0,0,1, 0.4666666667,0,0,1, + 0.5333333333,0,0,1, 0.6,0,0,1, 0.6666666667,0,0,1, 0.7333333333,0,0,1, + 0.8,0,0,1, 0.8666666667,0,0,1, 0.9333333333,0,0,1, 1.0,0,0,1, + 0.0,0,0,1, 0.0666666667,0,0,1, 0.1333333333,0,0,1, 0.2,0,0,1, + 0.2666666667,0,0,1, 0.3333333333,0,0,1, 0.4,0,0,1, 0.4666666667,0,0,1, + 0.5333333333,0,0,1, 0.6,0,0,1, 0.6666666667,0,0,1, 0.7333333333,0,0,1, + 0.8,0,0,1, 0.8666666667,0,0,1, 0.9333333333,0,0,1, 1.0,0,0,1, + # Slice 1 (green ramp, 4 identical rows of 16 texels) + 0,0.0,0,1, 0,0.0666666667,0,1, 0,0.1333333333,0,1, 0,0.2,0,1, + 0,0.2666666667,0,1, 0,0.3333333333,0,1, 0,0.4,0,1, 0,0.4666666667,0,1, + 0,0.5333333333,0,1, 0,0.6,0,1, 0,0.6666666667,0,1, 0,0.7333333333,0,1, + 0,0.8,0,1, 0,0.8666666667,0,1, 0,0.9333333333,0,1, 0,1.0,0,1, + 0,0.0,0,1, 0,0.0666666667,0,1, 0,0.1333333333,0,1, 0,0.2,0,1, + 0,0.2666666667,0,1, 0,0.3333333333,0,1, 0,0.4,0,1, 0,0.4666666667,0,1, + 0,0.5333333333,0,1, 0,0.6,0,1, 0,0.6666666667,0,1, 0,0.7333333333,0,1, + 0,0.8,0,1, 0,0.8666666667,0,1, 0,0.9333333333,0,1, 0,1.0,0,1, + 0,0.0,0,1, 0,0.0666666667,0,1, 0,0.1333333333,0,1, 0,0.2,0,1, + 0,0.2666666667,0,1, 0,0.3333333333,0,1, 0,0.4,0,1, 0,0.4666666667,0,1, + 0,0.5333333333,0,1, 0,0.6,0,1, 0,0.6666666667,0,1, 0,0.7333333333,0,1, + 0,0.8,0,1, 0,0.8666666667,0,1, 0,0.9333333333,0,1, 0,1.0,0,1, + 0,0.0,0,1, 0,0.0666666667,0,1, 0,0.1333333333,0,1, 0,0.2,0,1, + 0,0.2666666667,0,1, 0,0.3333333333,0,1, 0,0.4,0,1, 0,0.4666666667,0,1, + 0,0.5333333333,0,1, 0,0.6,0,1, 0,0.6666666667,0,1, 0,0.7333333333,0,1, + 0,0.8,0,1, 0,0.8666666667,0,1, 0,0.9333333333,0,1, 0,1.0,0,1 + ] + +DescriptorSets: + - Resources: + - Name: Out + Kind: RWTexture2DArray + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + +Results: + - Result: StoreTest + Rule: BufferFloatULP + ULPT: 4 + Actual: Out + Expected: Expected +... +#--- end + + +# RWTexture2DArray support is currently DirectX-only. +# UNSUPPORTED: Vulkan || Metal + +# Clang HLSL has no RWTexture2DArray template yet. +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/Textures/Texture2D.GetDimensions.test.yaml b/test/Feature/Textures/Texture2D.GetDimensions.test.yaml index 484fcc93e..d74667a1e 100644 --- a/test/Feature/Textures/Texture2D.GetDimensions.test.yaml +++ b/test/Feature/Textures/Texture2D.GetDimensions.test.yaml @@ -115,7 +115,7 @@ Results: #--- end # Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 -# XFAIL: DirectX || Metal +# XFAIL: (Clang && DirectX) || Metal # Bug https://github.com/llvm/llvm-project/issues/197837 # XFAIL: Clang && Vulkan diff --git a/test/Feature/Textures/Texture2D.Load.MipMaps.test.yaml b/test/Feature/Textures/Texture2D.Load.MipMaps.test.yaml new file mode 100644 index 000000000..750168d8f --- /dev/null +++ b/test/Feature/Textures/Texture2D.Load.MipMaps.test.yaml @@ -0,0 +1,80 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + // Texture2D::Load(int3(x, y, mip)) + // Mip 0 (4x4) is Red + Out[0] = Tex.Load(int3(0, 0, 0)); + Out[1] = Tex.Load(int3(3, 3, 0)); + // Mip 1 (2x2) is Green + Out[2] = Tex.Load(int3(0, 0, 1)); + Out[3] = Tex.Load(int3(1, 1, 1)); + // Mip 2 (1x1) is Blue + Out[4] = Tex.Load(int3(0, 0, 2)); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 4, Height: 4, Depth: 1, MipLevels: 3 } + Data: [ + # --- Mip 0 (4x4 = 16 RGBA texels) - Red --- + 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + # --- Mip 1 (2x2 = 4 RGBA texels) - Green --- + 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, + # --- Mip 2 (1x1 = 1 RGBA texel) - Blue --- + 0.0, 0.0, 1.0, 1.0 + ] + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 80 # 5 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 1.0, 0.0, 0.0, 1.0, # [0] Red (mip 0 corner) + 1.0, 0.0, 0.0, 1.0, # [1] Red (mip 0 corner) + 0.0, 1.0, 0.0, 1.0, # [2] Green (mip 1 corner) + 0.0, 1.0, 0.0, 1.0, # [3] Green (mip 1 corner) + 0.0, 0.0, 1.0, 1.0 ] # [4] Blue (mip 2) + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + +Results: + - Result: LoadMipsTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + +# XFAIL: Clang && DirectX +# XFAIL: Metal + +# RUN: split-file %s %t +# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml b/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml index e57df3748..00185fd28 100644 --- a/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml +++ b/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml @@ -61,7 +61,7 @@ Results: #--- end # Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 -# XFAIL: DirectX +# XFAIL: Clang && DirectX # RUN: split-file %s %t # RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl diff --git a/test/Feature/Textures/Texture2D.mips.OperatorIndex.test.yaml b/test/Feature/Textures/Texture2D.mips.OperatorIndex.test.yaml index 49f8ee4ba..6fd165d14 100644 --- a/test/Feature/Textures/Texture2D.mips.OperatorIndex.test.yaml +++ b/test/Feature/Textures/Texture2D.mips.OperatorIndex.test.yaml @@ -61,8 +61,8 @@ Results: #--- end # Unimplemented: https://github.com/llvm/offload-test-suite/issues/1039 -# XFAIL: DirectX - +# DXC+DX (d3d12/warp) now supported; Clang+DX still has a separate issue. +# XFAIL: Clang && DirectX # XFAIL: Metal # RUN: split-file %s %t diff --git a/test/Feature/Textures/Texture2DArray.Load.MipMaps.test.yaml b/test/Feature/Textures/Texture2DArray.Load.MipMaps.test.yaml new file mode 100644 index 000000000..2855810a8 --- /dev/null +++ b/test/Feature/Textures/Texture2DArray.Load.MipMaps.test.yaml @@ -0,0 +1,102 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2DArray Tex : register(t0); +[[vk::binding(1, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + // Texture2DArray::Load(int4(x, y, slice, mip)) + // Per-slice mip chain (slice-major then mip-major within slice). + + // Slice 0, mip 0 (2x2): R, G, B, W + Out[0] = Tex.Load(int4(0, 0, 0, 0)); + Out[1] = Tex.Load(int4(1, 0, 0, 0)); + Out[2] = Tex.Load(int4(0, 1, 0, 0)); + Out[3] = Tex.Load(int4(1, 1, 0, 0)); + + // Slice 0, mip 1 (1x1): cyan + Out[4] = Tex.Load(int4(0, 0, 0, 1)); + + // Slice 1, mip 0 (2x2): solid red + Out[5] = Tex.Load(int4(0, 0, 1, 0)); + Out[6] = Tex.Load(int4(1, 1, 1, 0)); + + // Slice 1, mip 1 (1x1): solid green + Out[7] = Tex.Load(int4(0, 0, 1, 1)); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1, ArraySize: 2, MipLevels: 2 } + Data: [ + # --- Slice 0, mip 0 (2x2 = 4 RGBA texels): R, G, B, W --- + 1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + # --- Slice 0, mip 1 (1x1 = 1 RGBA texel): cyan --- + 0.0, 1.0, 1.0, 1.0, + # --- Slice 1, mip 0 (2x2 = 4 RGBA texels): solid red --- + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + # --- Slice 1, mip 1 (1x1 = 1 RGBA texel): solid green --- + 0.0, 1.0, 0.0, 1.0 + ] + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 128 # 8 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ + 1.0, 0.0, 0.0, 1.0, # [0] R (slice 0, mip 0, (0,0)) + 0.0, 1.0, 0.0, 1.0, # [1] G (slice 0, mip 0, (1,0)) + 0.0, 0.0, 1.0, 1.0, # [2] B (slice 0, mip 0, (0,1)) + 1.0, 1.0, 1.0, 1.0, # [3] W (slice 0, mip 0, (1,1)) + 0.0, 1.0, 1.0, 1.0, # [4] cyan (slice 0, mip 1) + 1.0, 0.0, 0.0, 1.0, # [5] red (slice 1, mip 0) + 1.0, 0.0, 0.0, 1.0, # [6] red (slice 1, mip 0) + 0.0, 1.0, 0.0, 1.0 # [7] green (slice 1, mip 1) + ] + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2DArray + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + +Results: + - Result: LoadMipsTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + + +# Texture2DArray support is currently DirectX-only. +# UNSUPPORTED: Vulkan || Metal + +# Clang HLSL has no Texture2DArray template yet. +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o diff --git a/test/Feature/Textures/Texture2DArray.Load.test.yaml b/test/Feature/Textures/Texture2DArray.Load.test.yaml new file mode 100644 index 000000000..33fc72ede --- /dev/null +++ b/test/Feature/Textures/Texture2DArray.Load.test.yaml @@ -0,0 +1,104 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2DArray Tex : register(t0); +[[vk::binding(1, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + // Explicit Load(int4) + // Location: (x, y, slice, mip) + // Slice 0: Red, Green, Blue, White + Out[0] = Tex.Load(int4(0, 0, 0, 0)); + Out[1] = Tex.Load(int4(1, 0, 0, 0)); + Out[2] = Tex.Load(int4(0, 1, 0, 0)); + Out[3] = Tex.Load(int4(1, 1, 0, 0)); + + // Slice 1: solid Red + Out[4] = Tex.Load(int4(0, 0, 1, 0)); + Out[5] = Tex.Load(int4(1, 1, 1, 0)); + + // Slice 2: solid Green + Out[6] = Tex.Load(int4(0, 0, 2, 0)); + Out[7] = Tex.Load(int4(1, 1, 2, 0)); + + // Load(int4, int2) - With Offset, slice 0 + // (0,0) + (1,0) = (1,0) -> Green + Out[8] = Tex.Load(int4(0, 0, 0, 0), int2(1, 0)); + // (1,1) + (-1,-1) = (0,0) -> Red + Out[9] = Tex.Load(int4(1, 1, 0, 0), int2(-1, -1)); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1, ArraySize: 3 } + Data: [ # Slice 0: Red, Green, Blue, White + 1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + # Slice 1: solid Red + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + # Slice 2: solid Green + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0 ] + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 160 # 10 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 1.0, 0.0, 0.0, 1.0, # Slice 0 (0,0) Red + 0.0, 1.0, 0.0, 1.0, # Slice 0 (1,0) Green + 0.0, 0.0, 1.0, 1.0, # Slice 0 (0,1) Blue + 1.0, 1.0, 1.0, 1.0, # Slice 0 (1,1) White + 1.0, 0.0, 0.0, 1.0, # Slice 1 (0,0) Red + 1.0, 0.0, 0.0, 1.0, # Slice 1 (1,1) Red + 0.0, 1.0, 0.0, 1.0, # Slice 2 (0,0) Green + 0.0, 1.0, 0.0, 1.0, # Slice 2 (1,1) Green + 0.0, 1.0, 0.0, 1.0, # Offset (1,0) -> Slice 0 Green + 1.0, 0.0, 0.0, 1.0 ] # Offset (-1,-1) -> Slice 0 Red + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2DArray + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + +Results: + - Result: LoadTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + + +# Texture2DArray support is currently DirectX-only. +# UNSUPPORTED: Vulkan || Metal + +# Clang HLSL has no Texture2DArray template yet. +# XFAIL: Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t.o