diff --git a/README.md b/README.md index 6f6840c5e..7dc6d59dd 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,19 @@ Buffers: - Name: In2 Format: Hex16 Data: [ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8] + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: + Width: 2 + Height: 2 + Depth: 1 + MipLevels: 2 + Data: [ 1.0, 0.0, 0.0, 1.0, # Mip 0 (2x2) + 0.0, 1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 0.0, 1.0 ] # Mip 1 (1x1) - Name: Out1 # Buffer where our output will go Format: Float32 Stride: 4 diff --git a/docs/MipMappedTextures.md b/docs/MipMappedTextures.md new file mode 100644 index 000000000..c38610033 --- /dev/null +++ b/docs/MipMappedTextures.md @@ -0,0 +1,93 @@ +# Mipmapped Textures + +The test suite supports textures with multiple mipmap levels. This allows +verifying that the compiler correctly generates code for +`CalculateLevelOfDetail`, `SampleGrad` (explicit gradients), and direct access +via `mips[level][coords]`. + +## Defining Mip Levels + +To define a texture with multiple mips, specify the `MipLevels` field in +`OutputProps`. + +**Example:** +```yaml +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: + Width: 4 + Height: 4 + Depth: 1 + MipLevels: 3 # Defines Mip 0 (4x4), Mip 1 (2x2), and Mip 2 (1x1) +``` + +## Dimension Calculation + +The dimensions of each mip level are calculated automatically based on the base +`Width`, `Height`, and `Depth` provided in `OutputProps`. Following standard +graphics API conventions (Vulkan, DirectX, Metal), each subsequent mip level is +half the size of the previous level, rounded down, with a minimum of 1. + +For Mip Level $N$: +* $\text{Width}_N = \max(1, \lfloor \text{Width}_0 / 2^N \rfloor)$ +* $\text{Height}_N = \max(1, \lfloor \text{Height}_0 / 2^N \rfloor)$ +* $\text{Depth}_N = \max(1, \lfloor \text{Depth}_0 / 2^N \rfloor)$ + +## Data Layout + +The `Data` array in the YAML must contain the texel data for **all mip levels +sequentially**, packed tightly without padding. + +For a 4x4 texture with 3 mip levels, the `Data` array structure is: + +1. **Mip 0 (4x4 = 16 texels):** Indices 0 - 15. +2. **Mip 1 (2x2 = 4 texels):** Indices 16 - 19. +3. **Mip 2 (1x1 = 1 texel):** Index 20. + +**Total Size:** 21 texels. + +### Example YAML + +```yaml + - Name: Tex + Format: Float32 + Channels: 4 # RGBA + OutputProps: { Width: 4, Height: 4, Depth: 1, MipLevels: 3 } + Data: [ + # --- Mip 0 (4x4) --- + 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, # Row 0 (Red) + 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, # Row 1 + 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, # Row 2 + 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, # Row 3 + + # --- Mip 1 (2x2) --- + 0,1,0,1, 0,1,0,1, # Row 0 (Green) + 0,1,0,1, 0,1,0,1, # Row 1 + + # --- Mip 2 (1x1) --- + 0,0,1,1 # Single Pixel (Blue) + ] +``` + +## Usage in Tests + +This structure allows you to verify explicit mip selection in shaders: + +```hlsl +// Should return Green (Mip 1) +float4 val = Tex.mips[1][int2(0,0)]; + +// Should return Blue (Mip 2) +float4 val2 = Tex.Load(int3(0,0,2)); +``` + +## Implementation Notes + +### Mipmap Filtering + +Currently, the test suite hardcodes the mipmap sampling mode to **Nearest** +(`VK_SAMPLER_MIPMAP_MODE_NEAREST` in Vulkan). This ensures deterministic results +for tests that verify specific mip level selection without requiring linear +interpolation between levels. Linear mip filtering is not configurable via YAML. diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index 13d04825e..93ed192b8 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -101,6 +101,7 @@ struct OutputProperties { int Height; int Width; int Depth; + int MipLevels = 1; }; static inline uint32_t getFormatSize(DataFormat Format) { diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index 84535a399..6576ce0c9 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -156,9 +156,16 @@ static DXResourceKind getDXKind(offloadtest::ResourceKind RK) { llvm_unreachable("All cases handled"); } -static D3D12_RESOURCE_DESC getResourceDescription(const Resource &R) { +static llvm::Expected +getResourceDescription(const Resource &R) { const D3D12_RESOURCE_DIMENSION Dimension = getDXDimension(R.Kind); const offloadtest::Buffer &B = *R.BufferPtr; + + if (B.OutputProps.MipLevels != 1) + return llvm::createStringError(std::errc::not_supported, + "Multiple mip levels are not yet supported " + "for DirectX textures."); + const DXGI_FORMAT Format = R.isTexture() ? getDXFormat(B.Format, B.Channels) : DXGI_FORMAT_UNKNOWN; const uint32_t Width = @@ -611,7 +618,10 @@ class DXDevice : public offloadtest::Device { llvm::Expected createSRV(Resource &R, InvocationState &IS) { ResourceBundle Bundle; - const D3D12_RESOURCE_DESC ResDesc = getResourceDescription(R); + auto ResDescOrErr = getResourceDescription(R); + if (!ResDescOrErr) + return ResDescOrErr.takeError(); + const D3D12_RESOURCE_DESC ResDesc = *ResDescOrErr; const D3D12_HEAP_PROPERTIES UploadHeapProp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); const D3D12_RESOURCE_DESC UploadResDesc = @@ -708,7 +718,10 @@ class DXDevice : public offloadtest::Device { ResourceBundle Bundle; const uint32_t BufferSize = getUAVBufferSize(R); - const D3D12_RESOURCE_DESC ResDesc = getResourceDescription(R); + auto ResDescOrErr = getResourceDescription(R); + if (!ResDescOrErr) + return ResDescOrErr.takeError(); + const D3D12_RESOURCE_DESC ResDesc = *ResDescOrErr; const D3D12_HEAP_PROPERTIES ReadBackHeapProp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK); @@ -1314,6 +1327,11 @@ class DXDevice : public offloadtest::Device { std::errc::invalid_argument, "No render target bound for graphics pipeline."); const Buffer &OutBuf = *P.Bindings.RTargetBufferPtr; + if (OutBuf.OutputProps.MipLevels != 1) + return llvm::createStringError( + std::errc::not_supported, + "Multiple mip levels are not yet supported for DirectX render " + "targets."); D3D12_RESOURCE_DESC Desc = {}; Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; Desc.Width = OutBuf.OutputProps.Width; diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index beb86736d..50bd2ae09 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -14,6 +14,8 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/Support/Error.h" +#include +#include #include #include #include @@ -714,7 +716,7 @@ class VKDevice : public offloadtest::Device { ImageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; ImageCreateInfo.imageType = VK_IMAGE_TYPE_2D; ImageCreateInfo.format = getVKFormat(B.Format, B.Channels); - ImageCreateInfo.mipLevels = 1; + ImageCreateInfo.mipLevels = B.OutputProps.MipLevels; ImageCreateInfo.arrayLayers = 1; ImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; ImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; @@ -764,7 +766,7 @@ class VKDevice : public offloadtest::Device { const Sampler &S = *R.SamplerPtr; SamplerInfo.magFilter = getVKFilter(S.MagFilter); SamplerInfo.minFilter = getVKFilter(S.MinFilter); - SamplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + SamplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; SamplerInfo.addressModeU = getVKAddressMode(S.Address); SamplerInfo.addressModeV = getVKAddressMode(S.Address); SamplerInfo.addressModeW = getVKAddressMode(S.Address); @@ -1202,7 +1204,8 @@ class VKDevice : public offloadtest::Device { ViewCreateInfo.subresourceRange.baseMipLevel = 0; ViewCreateInfo.subresourceRange.baseArrayLayer = 0; ViewCreateInfo.subresourceRange.layerCount = 1; - ViewCreateInfo.subresourceRange.levelCount = 1; + ViewCreateInfo.subresourceRange.levelCount = + R.BufferPtr->OutputProps.MipLevels; IndexOfFirstBufferDataInArray = ImageInfos.size(); for (auto &ResRef : IS.Resources[OverallResIdx].ResourceRefs) { ViewCreateInfo.image = ResRef.Image.Image; @@ -1722,20 +1725,31 @@ class VKDevice : public offloadtest::Device { return; if (R.isImage()) { const offloadtest::Buffer &B = *R.BufferPtr; - VkBufferImageCopy BufferCopyRegion = {}; - BufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - BufferCopyRegion.imageSubresource.mipLevel = 0; - BufferCopyRegion.imageSubresource.baseArrayLayer = 0; - BufferCopyRegion.imageSubresource.layerCount = 1; - BufferCopyRegion.imageExtent.width = B.OutputProps.Width; - BufferCopyRegion.imageExtent.height = B.OutputProps.Height; - BufferCopyRegion.imageExtent.depth = 1; - BufferCopyRegion.bufferOffset = 0; + llvm::SmallVector Regions; + uint64_t CurrentOffset = 0; + for (int I = 0; I < B.OutputProps.MipLevels; ++I) { + VkBufferImageCopy Region = {}; + Region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + Region.imageSubresource.mipLevel = I; + Region.imageSubresource.baseArrayLayer = 0; + Region.imageSubresource.layerCount = 1; + Region.imageExtent.width = + std::max(1u, static_cast(B.OutputProps.Width) >> I); + Region.imageExtent.height = + std::max(1u, static_cast(B.OutputProps.Height) >> I); + Region.imageExtent.depth = + std::max(1u, static_cast(B.OutputProps.Depth) >> I); + Region.bufferOffset = CurrentOffset; + Regions.push_back(Region); + CurrentOffset += static_cast(Region.imageExtent.width) * + Region.imageExtent.height * Region.imageExtent.depth * + B.getElementSize(); + } VkImageSubresourceRange SubRange = {}; SubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; SubRange.baseMipLevel = 0; - SubRange.levelCount = 1; + SubRange.levelCount = B.OutputProps.MipLevels; SubRange.layerCount = 1; VkImageMemoryBarrier ImageBarrier = {}; @@ -1755,8 +1769,8 @@ class VKDevice : public offloadtest::Device { nullptr, 1, &ImageBarrier); vkCmdCopyBufferToImage(IS.CmdBuffer, ResRef.Host.Buffer, - ResRef.Image.Image, VK_IMAGE_LAYOUT_GENERAL, 1, - &BufferCopyRegion); + ResRef.Image.Image, VK_IMAGE_LAYOUT_GENERAL, + Regions.size(), Regions.data()); } ImageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -1792,10 +1806,11 @@ class VKDevice : public offloadtest::Device { if (!R.isReadWrite()) return; if (R.isImage()) { + const offloadtest::Buffer &B = *R.BufferPtr; VkImageSubresourceRange SubRange = {}; SubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; SubRange.baseMipLevel = 0; - SubRange.levelCount = 1; + SubRange.levelCount = B.OutputProps.MipLevels; SubRange.layerCount = 1; VkImageMemoryBarrier ImageBarrier = {}; @@ -1815,20 +1830,31 @@ class VKDevice : public offloadtest::Device { nullptr, 1, &ImageBarrier); } - const offloadtest::Buffer &B = *R.BufferPtr; - VkBufferImageCopy BufferCopyRegion = {}; - BufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - BufferCopyRegion.imageSubresource.mipLevel = 0; - BufferCopyRegion.imageSubresource.baseArrayLayer = 0; - BufferCopyRegion.imageSubresource.layerCount = 1; - BufferCopyRegion.imageExtent.width = B.OutputProps.Width; - BufferCopyRegion.imageExtent.height = B.OutputProps.Height; - BufferCopyRegion.imageExtent.depth = 1; - BufferCopyRegion.bufferOffset = 0; + llvm::SmallVector Regions; + uint64_t CurrentOffset = 0; + for (int I = 0; I < B.OutputProps.MipLevels; ++I) { + VkBufferImageCopy Region = {}; + Region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + Region.imageSubresource.mipLevel = I; + Region.imageSubresource.baseArrayLayer = 0; + Region.imageSubresource.layerCount = 1; + Region.imageExtent.width = + std::max(1u, static_cast(B.OutputProps.Width) >> I); + Region.imageExtent.height = + std::max(1u, static_cast(B.OutputProps.Height) >> I); + Region.imageExtent.depth = + std::max(1u, static_cast(B.OutputProps.Depth) >> I); + Region.bufferOffset = CurrentOffset; + Regions.push_back(Region); + CurrentOffset += static_cast(Region.imageExtent.width) * + Region.imageExtent.height * Region.imageExtent.depth * + B.getElementSize(); + } + for (auto &ResRef : R.ResourceRefs) vkCmdCopyImageToBuffer(IS.CmdBuffer, ResRef.Image.Image, - VK_IMAGE_LAYOUT_GENERAL, ResRef.Host.Buffer, 1, - &BufferCopyRegion); + VK_IMAGE_LAYOUT_GENERAL, ResRef.Host.Buffer, + Regions.size(), Regions.data()); VkBufferMemoryBarrier Barrier = {}; Barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index e875b3cdc..2cdc86778 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -320,6 +320,25 @@ void MappingTraits::mapping(IO &I, } I.mapOptional("OutputProps", B.OutputProps); + + if (!I.outputting() && B.OutputProps.Width > 0) { + uint32_t ExpectedSize = 0; + uint32_t W = B.OutputProps.Width; + uint32_t H = B.OutputProps.Height; + uint32_t D = B.OutputProps.Depth; + const uint32_t ElementSize = B.getElementSize(); + for (int I = 0; I < B.OutputProps.MipLevels; ++I) { + ExpectedSize += W * H * D * ElementSize; + W = std::max(1u, W / 2); + H = std::max(1u, H / 2); + D = std::max(1u, D / 2); + } + + if (B.Size != ExpectedSize) + I.setError(Twine("Buffer '") + B.Name + "' size (" + Twine(B.Size) + + ") does not match OutputProps dimensions (" + + Twine(ExpectedSize) + ")"); + } } void MappingTraits::mapping(IO &I, @@ -419,6 +438,7 @@ void MappingTraits::mapping( I.mapRequired("Height", P.Height); I.mapRequired("Width", P.Width); I.mapRequired("Depth", P.Depth); + I.mapOptional("MipLevels", P.MipLevels, 1); } void MappingTraits::mapping( diff --git a/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml b/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml new file mode 100644 index 000000000..46490636b --- /dev/null +++ b/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml @@ -0,0 +1,135 @@ +#--- source.hlsl +struct VSInput { + float3 Pos : POSITION; +}; + +struct VSOutput { + float4 Pos : SV_POSITION; +}; + +VSOutput mainVS(VSInput input) { + VSOutput output; + output.Pos = float4(input.Pos, 1.0f); + return output; +} + +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] SamplerState Samp : register(s0); +[[vk::binding(2, 0)]] RWBuffer Out : register(u0); + +float4 mainPS(VSOutput input) : SV_Target { + // Use SV_Position (pixel coordinates) to simulate the grid logic. + // Input position is [-1, 1], mapped to viewport 2x2. + // Pixel centers will be at (0.5, 0.5), (1.5, 0.5), etc. + + uint2 GTid = (uint2)input.Pos.xy; + + // Case 1: Derivative = 0.5. Texture Size = 4. + // LOD = log2(0.5 * 4) = 1.0 + float2 uv0 = 0.25 + float2(GTid.xy) * 0.5; + + // Case 2: Derivative = 2.0. Texture Size = 4. + // LOD = log2(2.0 * 4) = 3.0. + // Note: Since the texture has 3 mip levels (0, 1, 2), + // the clamped LOD will be 2.0. + float2 uv1 = 0.25 + float2(GTid.xy) * 2.0; + + float lod0 = Tex.CalculateLevelOfDetail(Samp, uv0); + float lod1 = Tex.CalculateLevelOfDetail(Samp, uv1); + float lod2 = Tex.CalculateLevelOfDetailUnclamped(Samp, uv0); + float lod3 = Tex.CalculateLevelOfDetailUnclamped(Samp, uv1); + + if (all(GTid.xy == 0)) { + Out[0] = lod0; + Out[1] = lod1; + Out[2] = lod2; + Out[3] = lod3; + } + return float4(0, 0, 0, 0); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Vertex + Entry: mainVS + - Stage: Pixel + Entry: mainPS + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 4, Height: 4, Depth: 1, MipLevels: 3 } + FillSize: 336 # (16+4+1)*16 + + - Name: Out + Format: Float32 + Channels: 1 + FillSize: 16 # 4 * sizeof(float) + + - Name: Expected + Format: Float32 + Channels: 1 + Data: [ 1.0, 2.0, 1.0, 3.0 ] + + - Name: RT + Format: Float32 + Channels: 4 + FillSize: 64 + OutputProps: { Width: 2, Height: 2, Depth: 1 } + + - Name: VB + Format: Float32 + Channels: 3 + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + +Bindings: + VertexBuffer: VB + VertexAttributes: + - Format: Float32 + Channels: 3 + Offset: 0 + Name: POSITION + RenderTarget: RT + +Samplers: + - Name: Samp + MinFilter: Linear + MagFilter: Linear + Address: Clamp + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Samp + Kind: Sampler + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 2 } + +Results: + - Result: LODTest + Rule: BufferFloatULP + Actual: Out + Expected: Expected + ULPT: 1 +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# Unimplemented: Clang + VK: https://github.com/llvm/llvm-project/issues/175630 +# XFAIL: DirectX || Metal || Clang + +# RUN: split-file %s %t +# RUN: %dxc_target -T vs_6_6 -E mainVS -Fo %t-vs.o %t/source.hlsl +# RUN: %dxc_target -T ps_6_6 -E mainPS -Fo %t-ps.o %t/source.hlsl +# RUN: %offloader %t/pipeline.yaml %t-vs.o %t-ps.o diff --git a/test/Feature/Textures/Texture2D.Gather.test.yaml b/test/Feature/Textures/Texture2D.Gather.test.yaml new file mode 100644 index 000000000..80e5eac2e --- /dev/null +++ b/test/Feature/Textures/Texture2D.Gather.test.yaml @@ -0,0 +1,127 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] SamplerState Samp : register(s0); +[[vk::binding(3, 0)]] SamplerState SampRepeat : register(s1); +[[vk::binding(2, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + // Gather at (0.5, 0.5) on a 2x2 texture. + // Texels used: (0,0), (1,0), (0,1), (1,1). + // Gather returns a float4 containing the Red component of: + // [ (0,1), (1,1), (1,0), (0,0) ] + Out[0] = Tex.Gather(Samp, float2(0.5, 0.5)); + + // Also test GatherRed explicitly (equivalent to Gather) + Out[1] = Tex.GatherRed(Samp, float2(0.5, 0.5)); + + // Also test GatherGreen + Out[2] = Tex.GatherGreen(Samp, float2(0.5, 0.5)); + + // Also test GatherBlue + Out[3] = Tex.GatherBlue(Samp, float2(0.5, 0.5)); + + // Also test GatherAlpha + Out[4] = Tex.GatherAlpha(Samp, float2(0.5, 0.5)); + + // Test Sampler Usage for all channels: + float2 uv_outside = float2(1.5, 1.5); + + // Red + Out[5] = Tex.GatherRed(Samp, uv_outside); + Out[6] = Tex.GatherRed(SampRepeat, uv_outside); + + // Green + Out[7] = Tex.GatherGreen(Samp, uv_outside); + Out[8] = Tex.GatherGreen(SampRepeat, uv_outside); + + // Blue + Out[9] = Tex.GatherBlue(Samp, uv_outside); + Out[10] = Tex.GatherBlue(SampRepeat, uv_outside); + + // Alpha + Out[11] = Tex.GatherAlpha(Samp, uv_outside); + Out[12] = Tex.GatherAlpha(SampRepeat, uv_outside); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1 } + Data: [ 1.0, 0.0, 0.0, 1.0, # Red (0,0) -> R=1, G=0, B=0, A=1 + 0.0, 1.0, 0.0, 1.0, # Green (1,0) -> R=0, G=1, B=0, A=1 + 0.0, 0.0, 1.0, 1.0, # Blue (0,1) -> R=0, G=0, B=1, A=1 + 1.0, 1.0, 1.0, 0.5 ] # White (1,1) -> R=1, G=1, B=1, A=0.5 + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 208 # 13 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 0.0, 1.0, 0.0, 1.0, # 0: Gather (Red) (0.5, 0.5) + 0.0, 1.0, 0.0, 1.0, # 1: GatherRed (0.5, 0.5) + 0.0, 1.0, 1.0, 0.0, # 2: GatherGreen (0.5, 0.5) + 1.0, 1.0, 0.0, 0.0, # 3: GatherBlue (0.5, 0.5) + 1.0, 0.5, 1.0, 1.0, # 4: GatherAlpha (0.5, 0.5) + 1.0, 1.0, 1.0, 1.0, # 5: Red Clamp (1.5, 1.5) -> (1,1) everywhere + 0.0, 1.0, 0.0, 1.0, # 6: Red Repeat (1.5, 1.5) -> same as (0.5, 0.5) + 1.0, 1.0, 1.0, 1.0, # 7: Green Clamp (1.5, 1.5) + 0.0, 1.0, 1.0, 0.0, # 8: Green Repeat (1.5, 1.5) + 1.0, 1.0, 1.0, 1.0, # 9: Blue Clamp (1.5, 1.5) + 1.0, 1.0, 0.0, 0.0, # 10: Blue Repeat (1.5, 1.5) + 0.5, 0.5, 0.5, 0.5, # 11: Alpha Clamp (1.5, 1.5) + 1.0, 0.5, 1.0, 1.0 ] # 12: Alpha Repeat (1.5, 1.5) + +Samplers: + - Name: Samp + MinFilter: Linear + MagFilter: Linear + Address: Clamp + - Name: SampRepeat + Address: Repeat + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Samp + Kind: Sampler + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 2 } + - Name: SampRepeat + Kind: Sampler + DirectXBinding: { Register: 1, Space: 0 } + VulkanBinding: { Binding: 3 } + +Results: + - Result: GatherTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# Unimplemented: Clang + VK: https://github.com/llvm/llvm-project/issues/175630 +# XFAIL: DirectX || Metal || 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 new file mode 100644 index 000000000..c2ccd6a35 --- /dev/null +++ b/test/Feature/Textures/Texture2D.GetDimensions.test.yaml @@ -0,0 +1,110 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex1 : register(t0); +[[vk::binding(1, 0)]] Texture2D Tex2 : register(t1); +[[vk::binding(3, 0)]] Texture2D Tex3 : register(t2); // 4x4 with 3 mips +[[vk::binding(2, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + float width, height, levels; + + // Tex1 (4x8, 1 mip) + Tex1.GetDimensions(0, width, height, levels); + Out[0] = width; + Out[1] = height; + Out[2] = levels; + + float w, h; + Tex1.GetDimensions(w, h); + Out[3] = w; + Out[4] = h; + + // Tex2 (16x32, 1 mip) + Tex2.GetDimensions(0, width, height, levels); + Out[5] = width; + Out[6] = height; + Out[7] = levels; + + Tex2.GetDimensions(w, h); + Out[8] = w; + Out[9] = h; + + // Tex3 (4x4, 3 mips) + Tex3.GetDimensions(0, width, height, levels); + Out[10] = width; + Out[11] = height; + Out[12] = levels; +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex1 + Format: Float32 + Channels: 4 + OutputProps: { Width: 4, Height: 8, Depth: 1 } + FillSize: 512 # 4*8*4*4 + + - Name: Tex2 + Format: Float32 + Channels: 4 + OutputProps: { Width: 16, Height: 32, Depth: 1 } + FillSize: 8192 # 16*32*4*4 + + - Name: Tex3 + Format: Float32 + Channels: 4 + OutputProps: { Width: 4, Height: 4, Depth: 1, MipLevels: 3 } + FillSize: 336 # (16 + 4 + 1) * 4 * 4 = 21 * 16 = 336 + + - Name: Out + Format: Float32 + Channels: 1 + FillSize: 52 # 13 * sizeof(float) + + - Name: Expected + Format: Float32 + Channels: 1 + Data: [ 4.0, 8.0, 1.0, 4.0, 8.0, # Tex1 + 16.0, 32.0, 1.0, 16.0, 32.0, # Tex2 + 4.0, 4.0, 3.0 ] # Tex3 + +DescriptorSets: + - Resources: + - Name: Tex1 + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Tex2 + Kind: Texture2D + DirectXBinding: { Register: 1, Space: 0 } + VulkanBinding: { Binding: 1 } + - Name: Tex3 + Kind: Texture2D + DirectXBinding: { Register: 2, Space: 0 } + VulkanBinding: { Binding: 3 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 2 } + +Results: + - Result: GetDimensionsTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# Unimplemented: Clang + VK: https://github.com/llvm/llvm-project/issues/175630 +# XFAIL: DirectX || Metal || 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.Load.test.yaml b/test/Feature/Textures/Texture2D.Load.test.yaml new file mode 100644 index 000000000..15088bead --- /dev/null +++ b/test/Feature/Textures/Texture2D.Load.test.yaml @@ -0,0 +1,85 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + // Explicit Load(int3) + // Location: (x, y, mip) + Out[0] = Tex.Load(int3(0, 0, 0)); // Red + Out[1] = Tex.Load(int3(1, 0, 0)); // Green + Out[2] = Tex.Load(int3(0, 1, 0)); // Blue + Out[3] = Tex.Load(int3(1, 1, 0)); // White + + // Load(int3, int2) - With Offset + // (0,0) + (1,0) = (1,0) -> Green + Out[4] = Tex.Load(int3(0, 0, 0), int2(1, 0)); + + // (1,1) + (-1,-1) = (0,0) -> Red + Out[5] = Tex.Load(int3(1, 1, 0), int2(-1, -1)); + + // (0,0) + (1,1) = (1,1) -> White + Out[6] = Tex.Load(int3(0, 0, 0), int2(1, 1)); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1 } + Data: [ 1.0, 0.0, 0.0, 1.0, # Red (0,0) + 0.0, 1.0, 0.0, 1.0, # Green (1,0) + 0.0, 0.0, 1.0, 1.0, # Blue (0,1) + 1.0, 1.0, 1.0, 1.0 ] # White (1,1) + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 112 # 7 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 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, + 0.0, 1.0, 0.0, 1.0, # Offset (1,0) -> Green + 1.0, 0.0, 0.0, 1.0, # Offset (-1,-1) -> Red + 1.0, 1.0, 1.0, 1.0 ] # Offset (1,1) -> White + +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: LoadTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# Unimplemented: Clang + VK: https://github.com/llvm/llvm-project/issues/175630 +# 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 \ No newline at end of file diff --git a/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml b/test/Feature/Textures/Texture2D.OperatorIndex.test.yaml new file mode 100644 index 000000000..447f5f048 --- /dev/null +++ b/test/Feature/Textures/Texture2D.OperatorIndex.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::operator[] (int2) + Out[0] = Tex[int2(0, 0)]; // Red + Out[1] = Tex[int2(1, 0)]; // Green + Out[2] = Tex[int2(0, 1)]; // Blue + Out[3] = Tex[int2(1, 1)]; // White + + // Texture2D::mips.operator[][] + Out[4] = Tex.mips[0][int2(0, 0)]; // Red + Out[5] = Tex.mips[0][int2(1, 1)]; // White + + // Test mip 1 (1x1) + Out[6] = Tex.mips[1][int2(0, 0)]; // Yellow +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1, MipLevels: 2 } + Data: [ 1.0, 0.0, 0.0, 1.0, # Mip 0 (2x2) + 0.0, 1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 0.0, 1.0 ] # Mip 1 (1x1) - Yellow + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 112 # 7 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 1.0, 0.0, 0.0, 1.0, # [0] + 0.0, 1.0, 0.0, 1.0, # [1] + 0.0, 0.0, 1.0, 1.0, # [2] + 1.0, 1.0, 1.0, 1.0, # [3] + 1.0, 0.0, 0.0, 1.0, # [4] + 1.0, 1.0, 1.0, 1.0, # [5] + 1.0, 1.0, 0.0, 1.0 ] # [6] Yellow (mip 1) + +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: OperatorIndexTest + Rule: BufferExact + Actual: Out + Expected: Expected +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# Unimplemented: Clang + VK: https://github.com/llvm/llvm-project/issues/175630 +# XFAIL: DirectX || Metal || 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.SRVToUAV.array.test.yaml b/test/Feature/Textures/Texture2D.SRVToUAV.array.test.yaml index 296f7a594..262c0103c 100644 --- a/test/Feature/Textures/Texture2D.SRVToUAV.array.test.yaml +++ b/test/Feature/Textures/Texture2D.SRVToUAV.array.test.yaml @@ -36,7 +36,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 - Name: Out Format: Float32 Channels: 4 @@ -45,7 +45,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 - Name: ExpectedOut Format: Float32 Channels: 4 @@ -62,7 +62,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 Results: - Result: Test1 Rule: BufferExact diff --git a/test/Feature/Textures/Texture2D.SRVToUAV.test.yaml b/test/Feature/Textures/Texture2D.SRVToUAV.test.yaml index 07186bb5e..91c3907fd 100644 --- a/test/Feature/Textures/Texture2D.SRVToUAV.test.yaml +++ b/test/Feature/Textures/Texture2D.SRVToUAV.test.yaml @@ -26,7 +26,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 - Name: Out Format: Float32 Channels: 4 @@ -34,7 +34,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 - Name: ExpectedOut Format: Float32 Channels: 4 @@ -45,7 +45,7 @@ Buffers: OutputProps: Height: 2 Width: 2 - Depth: 16 + Depth: 1 Results: - Result: Test1 Rule: BufferExact diff --git a/test/Feature/Textures/Texture2D.Sample.test.yaml b/test/Feature/Textures/Texture2D.Sample.test.yaml index b786a73e6..34ba1bff0 100644 --- a/test/Feature/Textures/Texture2D.Sample.test.yaml +++ b/test/Feature/Textures/Texture2D.Sample.test.yaml @@ -33,27 +33,34 @@ float4 mainPS(float4 Pos : SV_POSITION) : SV_Target { float2 uv_min = BaseUV + offset * 2.0; // Only check the first thread in the quad where UV is exactly (0.5, 0.5). + // We expect: + // Samp0 (Mag=Nearest, Min=Linear): + // Sample(uv_mag) -> Mag(Nearest) -> White + // Sample(uv_min) -> Min(Linear) -> Grey + float4 r0 = Tex.Sample(Samp0, uv_mag); + float4 r1 = Tex.Sample(Samp0, uv_min); + + // Samp1 (Mag=Linear, Min=Nearest): + // Sample(uv_mag) -> Mag(Linear) -> Grey + // Sample(uv_min) -> Min(Nearest) -> White + float4 r2 = Tex.Sample(Samp1, uv_mag); + float4 r3 = Tex.Sample(Samp1, uv_min); + + // Overloads with Offset + // Offset (-1, -1) applied to (0.5001, 0.5001) effectively samples near (0,0) -> Red + float4 r4 = Tex.Sample(Samp0, uv_mag, int2(-1, -1)); + + // Overloads with Offset and Clamp + // Clamp 0.0f should behave normally (Red) + float4 r5 = Tex.Sample(Samp0, uv_mag, int2(-1, -1), 0.0f); + if (all(GTid.xy == 0)) { - // We expect: - // Samp0 (Mag=Nearest, Min=Linear): - // Sample(uv_mag) -> Mag(Nearest) -> White - // Sample(uv_min) -> Min(Linear) -> Grey - Out[0] = Tex.Sample(Samp0, uv_mag); - Out[1] = Tex.Sample(Samp0, uv_min); - - // Samp1 (Mag=Linear, Min=Nearest): - // Sample(uv_mag) -> Mag(Linear) -> Grey - // Sample(uv_min) -> Min(Nearest) -> White - Out[2] = Tex.Sample(Samp1, uv_mag); - Out[3] = Tex.Sample(Samp1, uv_min); - - // Overloads with Offset - // Offset (-1, -1) applied to (0.5001, 0.5001) effectively samples near (0,0) -> Red - Out[4] = Tex.Sample(Samp0, uv_mag, int2(-1, -1)); - - // Overloads with Offset and Clamp - // Clamp 0.0f should behave normally (Red) - Out[5] = Tex.Sample(Samp0, uv_mag, int2(-1, -1), 0.0f); + Out[0] = r0; + Out[1] = r1; + Out[2] = r2; + Out[3] = r3; + Out[4] = r4; + Out[5] = r5; } return float4(0,0,0,0); } @@ -70,11 +77,12 @@ Buffers: - Name: Tex Format: Float32 Channels: 4 - OutputProps: { Width: 2, Height: 2, Depth: 1 } + OutputProps: { Width: 2, Height: 2, Depth: 1, MipLevels: 2 } Data: [ 1.0, 0.0, 0.0, 1.0, # Red (0,0) 0.0, 1.0, 0.0, 1.0, # Green (1,0) 0.0, 0.0, 1.0, 1.0, # Blue (0,1) - 1.0, 1.0, 1.0, 1.0 ] # White (1,1) + 1.0, 1.0, 1.0, 1.0, # White (1,1) + 0.5, 0.5, 0.5, 1.0 ] # Mip 1 (Average) - Name: Out Format: Float32 @@ -87,7 +95,7 @@ Buffers: Data: [ 1.0, 1.0, 1.0, 1.0, # Samp0 Mag (Nearest) -> White 0.5, 0.5, 0.5, 1.0, # Samp0 Min (Linear) -> Grey 0.5, 0.5, 0.5, 1.0, # Samp1 Mag (Linear) -> Grey - 1.0, 1.0, 1.0, 1.0, # Samp1 Min (Nearest) -> White + 0.5, 0.5, 0.5, 1.0, # Samp1 Min (Nearest) -> Grey 1.0, 0.0, 0.0, 1.0, # Offset (-1,-1) -> Red 1.0, 0.0, 0.0, 1.0 ] # Offset (-1,-1) Clamp 0.0 -> Red @@ -153,12 +161,8 @@ Results: # Unimplemented: https://github.com/llvm/offload-test-suite/issues/664 # XFAIL: DirectX || Metal || Vulkan && Darwin +# XFAIL: Clang && !Vulkan -# Unimplemented: https://github.com/llvm/llvm-project/issues/175630 -# XFAIL: Clang && !Intel - -# Bug: https://github.com/llvm/offload-test-suite/issues/690 -# XFAIL: Vulkan && DXC && !Intel # RUN: split-file %s %t # RUN: %dxc_target -T vs_6_0 -E mainVS -Fo %t-vs.o %t/vertex.hlsl diff --git a/test/Feature/Textures/Texture2D.SampleBias.test.yaml b/test/Feature/Textures/Texture2D.SampleBias.test.yaml index 9dec85556..ff06f8348 100644 --- a/test/Feature/Textures/Texture2D.SampleBias.test.yaml +++ b/test/Feature/Textures/Texture2D.SampleBias.test.yaml @@ -27,24 +27,31 @@ float4 mainPS(float4 Pos : SV_POSITION) : SV_Target { uint2 GTid = (uint2)Pos.xy; float2 sample_uv = 0.5 + float2(GTid.xy) * 0.5; - if (all(GTid.xy == 0)) { - // 1. Bias = -0.1 -> LOD = -0.1 -> Mag(Nearest) -> White - Out[0] = Tex.SampleBias(Samp, sample_uv, -0.1); + // 1. Bias = -0.1 -> LOD = -0.1 -> Mag(Nearest) -> White + float4 r0 = Tex.SampleBias(Samp, sample_uv, -0.1); + + // 2. Bias = 0.1 -> LOD = 0.1 -> Min(Linear) -> Grey + float4 r1 = Tex.SampleBias(Samp, sample_uv, 0.1); - // 2. Bias = 0.1 -> LOD = 0.1 -> Min(Linear) -> Grey - Out[1] = Tex.SampleBias(Samp, sample_uv, 0.1); + // 3. Bias = -0.1, Offset = (-1, -1) -> UV = (0.5, 0.5) -> Texel (1, 1). Offset (-1, -1) -> Texel (0, 0) -> Red + float4 r2 = Tex.SampleBias(Samp, sample_uv, -0.1, int2(-1, -1)); - // 3. Bias = -0.1, Offset = (-1, -1) -> UV = (0.5, 0.5) -> Texel (1, 1). Offset (-1, -1) -> Texel (0, 0) -> Red - Out[2] = Tex.SampleBias(Samp, sample_uv, -0.1, int2(-1, -1)); + // 4. Bias = -0.1, Clamp = 0.1 -> LOD = 0.1 -> Min(Linear) -> Grey + float4 r3 = Tex.SampleBias(Samp, sample_uv, -0.1, int2(0, 0), 0.1); - // 4. Bias = -0.1, Clamp = 0.1 -> LOD = 0.1 -> Mag(Linear) -> Grey - Out[3] = Tex.SampleBias(Samp, sample_uv, -0.1, int2(0, 0), 0.1); + // 5. Bias = -0.1 -> LOD = -0.1 -> Mag(Linear) -> Grey + float4 r4 = Tex.SampleBias(Samp2, sample_uv, -0.1); - // 5. Bias = -0.1 -> LOD = -0.1 -> Mag(Linear) -> Grey - Out[4] = Tex.SampleBias(Samp2, sample_uv, -0.1); + // 6. Sample at (0.25, 0.25) -> Red. + float4 r5 = Tex.SampleBias(Samp, float2(0.25, 0.25), -0.1); - // 6. Sample at (0.25, 0.25) -> Red. - Out[5] = Tex.SampleBias(Samp, float2(0.25, 0.25), -0.1); + if (all(GTid.xy == 0)) { + Out[0] = r0; + Out[1] = r1; + Out[2] = r2; + Out[3] = r3; + Out[4] = r4; + Out[5] = r5; } return float4(0,0,0,0); } @@ -61,11 +68,12 @@ Buffers: - Name: Tex Format: Float32 Channels: 4 - OutputProps: { Width: 2, Height: 2, Depth: 1 } + OutputProps: { Width: 2, Height: 2, Depth: 1, MipLevels: 2 } Data: [ 1.0, 0.0, 0.0, 1.0, # Red (0,0) 0.0, 1.0, 0.0, 1.0, # Green (1,0) 0.0, 0.0, 1.0, 1.0, # Blue (0,1) - 1.0, 1.0, 1.0, 1.0 ] # White (1,1) + 1.0, 1.0, 1.0, 1.0, # White (1,1) + 0.5, 0.5, 0.5, 1.0 ] # Mip 1 (Average) - Name: Out Format: Float32 @@ -78,7 +86,7 @@ Buffers: Data: [ 1.0, 1.0, 1.0, 1.0, # Bias -0.1 -> Mag(Nearest) -> White 0.5, 0.5, 0.5, 1.0, # Bias 0.1 -> Min(Linear) -> Grey 1.0, 0.0, 0.0, 1.0, # Offset (-1, -1) -> Red - 0.5, 0.5, 0.5, 1.0, # Clamp 0.1 -> Min(Linear) -> Grey + 0.5, 0.5, 0.5, 1.0, # Clamp 0.1 -> Min(Linear) -> Grey (Spec: LOD 0.1 is Minification. Lavapipe might fail this and give White) 0.5, 0.5, 0.5, 1.0, # Bias -0.1 -> Mag(Linear) -> Grey 1.0, 0.0, 0.0, 1.0 ] # Sample at (0.25, 0.25) -> Red @@ -148,8 +156,6 @@ Results: # Unimplemented: https://github.com/llvm/llvm-project/issues/175630 # XFAIL: Clang && !Intel -# Bug: https://github.com/llvm/offload-test-suite/issues/690 -# XFAIL: Vulkan && DXC && !Intel # RUN: split-file %s %t # RUN: %dxc_target -T vs_6_0 -E mainVS -Fo %t-vs.o %t/vertex.hlsl diff --git a/test/Feature/Textures/Texture2D.SampleCmp.test.yaml b/test/Feature/Textures/Texture2D.SampleCmp.test.yaml deleted file mode 100644 index 96ddbb391..000000000 --- a/test/Feature/Textures/Texture2D.SampleCmp.test.yaml +++ /dev/null @@ -1,189 +0,0 @@ -#--- vertex.hlsl -struct VSInput { - float3 Pos : POSITION; -}; - -struct VSOutput { - float4 Pos : SV_POSITION; -}; - -VSOutput mainVS(VSInput input) { - VSOutput output; - output.Pos = float4(input.Pos, 1.0f); - return output; -} - -#--- pixel.hlsl -[[vk::binding(0, 0)]] Texture2D Tex : register(t0); -[[vk::binding(1, 0)]] SamplerComparisonState Samp : register(s0); // Less -[[vk::binding(3, 0)]] SamplerComparisonState SampGreater : register(s1); // Greater -[[vk::binding(2, 0)]] RWBuffer Out : register(u0); - -float4 mainPS(float4 Pos : SV_POSITION) : SV_Target { - // TODO: Add extra testing when we add multiple mip levels. - - float2 uv = float2(0.5, 0.5); - - // 1. Ref = 0.5. Samp (Less: Ref < Sample). - // 0.5 < (0,0)=0.2 -> 0.0 - // 0.5 < (1,0)=0.8 -> 1.0 - // 0.5 < (0,1)=0.2 -> 0.0 - // 0.5 < (1,1)=0.8 -> 1.0 - // Linear Filter blends (0+1+0+1)/4 = 0.5 - Out[0] = Tex.SampleCmp(Samp, uv, 0.5); - - // 2. Ref = 0.1. (Less: Ref < Sample). - // 0.1 < 0.2 -> 1.0 - // 0.1 < 0.8 -> 1.0 - // Result 1.0. - Out[1] = Tex.SampleCmp(Samp, uv, 0.1); - - // 3. Ref = 0.9. (Less: Ref < Sample). - // 0.9 < 0.2 -> 0.0 - // 0.9 < 0.8 -> 0.0 - // Result 0.0. - Out[2] = Tex.SampleCmp(Samp, uv, 0.9); - - // 4. Ref = 0.5. SampGreater (Greater: Ref > Sample). - // 0.5 > 0.2 -> 1.0 - // 0.5 > 0.8 -> 0.0 - // Linear Filter blends (1+0+1+0)/4 = 0.5 - Out[3] = Tex.SampleCmp(SampGreater, uv, 0.5); - - // 5. Ref = 0.5. Offset (-1, -1). Samp (Less: Ref < Sample). - Out[4] = Tex.SampleCmp(Samp, uv, 0.5, int2(-1, -1)); - - // 6. Ref = 0.5. Clamp 0.0. Samp (Less: Ref < Sample). - Out[5] = Tex.SampleCmp(Samp, uv, 0.5, int2(0, 0), 0.0); - - // 7. Ref = 0.5. Location (0.25, 0.25). Samp (Less: Ref < Sample). - // Center of Texel (0,0) is 0.2. - // 0.5 < 0.2 is FALSE -> 0.0. - Out[6] = Tex.SampleCmp(Samp, float2(0.25, 0.25), 0.5); - - // 8. SampleCmpLevelZero (LOD 0 explicit) Ref = 0.5 - Out[7] = Tex.SampleCmpLevelZero(Samp, uv, 0.5); - - // 9. SampleCmpLevelZero Ref = 0.1 - Out[8] = Tex.SampleCmpLevelZero(Samp, uv, 0.1); - - // 10. SampleCmpLevelZero Ref = 0.9 - Out[9] = Tex.SampleCmpLevelZero(Samp, uv, 0.9); - - // 11. SampleCmpLevelZero Offset (-1, -1) - Out[10] = Tex.SampleCmpLevelZero(Samp, uv, 0.5, int2(-1, -1)); - - // 12. SampleCmpLevelZero SampGreater - Out[11] = Tex.SampleCmpLevelZero(SampGreater, uv, 0.5); - - // 13. SampleCmpLevelZero Location - Out[12] = Tex.SampleCmpLevelZero(Samp, float2(0.25, 0.25), 0.5); - return float4(0,0,0,0); -} - -//--- pipeline.yaml ---- -Shaders: - - Stage: Vertex - Entry: mainVS - - Stage: Pixel - Entry: mainPS - -Buffers: - - Name: Tex - Format: Float32 - Channels: 4 - OutputProps: { Width: 2, Height: 2, Depth: 1 } - Data: [ 0.2, 0.0, 0.0, 1.0, # (0,0) -> 0.2 - 0.8, 0.0, 0.0, 1.0, # (1,0) -> 0.8 - 0.2, 0.0, 0.0, 1.0, # (0,1) -> 0.2 - 0.8, 0.0, 0.0, 1.0 ] # (1,1) -> 0.8 - - - Name: Out - Format: Float32 - Channels: 1 - FillSize: 52 # 13 * sizeof(float) - - - Name: Expected - Format: Float32 - Channels: 1 - Data: [ 0.5, 1.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 1.0, 0.0, 0.0, 0.5, - 0.0 ] - - - Name: RT - Format: Float32 - Channels: 4 - FillSize: 16 - OutputProps: { Width: 1, Height: 1, Depth: 1 } - - - Name: VB - Format: Float32 - Channels: 3 - Data: [-1.0, -1.0, 0.0, - 3.0, -1.0, 0.0, - -1.0, 3.0, 0.0] - -Bindings: - VertexBuffer: VB - VertexAttributes: - - Format: Float32 - Channels: 3 - Offset: 0 - Name: POSITION - RenderTarget: RT - -Samplers: - - Name: Samp - MinFilter: Linear - MagFilter: Linear - Address: Clamp - ComparisonOp: Less - - Name: SampGreater - MinFilter: Linear - MagFilter: Linear - Address: Clamp - ComparisonOp: Greater - -DescriptorSets: - - Resources: - - Name: Tex - Kind: Texture2D - DirectXBinding: { Register: 0, Space: 0 } - VulkanBinding: { Binding: 0 } - - Name: Samp - Kind: SamplerComparison - DirectXBinding: { Register: 0, Space: 0 } - VulkanBinding: { Binding: 1 } - - Name: Out - Kind: RWBuffer - DirectXBinding: { Register: 0, Space: 0 } - VulkanBinding: { Binding: 2 } - - Name: SampGreater - Kind: SamplerComparison - DirectXBinding: { Register: 1, Space: 0 } - VulkanBinding: { Binding: 3 } - -Results: - - Result: SampleCmpTest - Rule: BufferFloatULP - Actual: Out - Expected: Expected - ULPT: 1 -... -#--- end - -# Unimplemented: https://github.com/llvm/offload-test-suite/issues/664 -# XFAIL: DirectX || Metal || Vulkan && Darwin - -# Unimplemented: https://github.com/llvm/llvm-project/issues/175630 -# XFAIL: Clang && !Intel - -# Bug: https://github.com/llvm/offload-test-suite/issues/690 -# XFAIL: Vulkan && DXC && !Intel - -# RUN: split-file %s %t -# RUN: %dxc_target -T vs_6_0 -E mainVS -Fo %t-vs.o %t/vertex.hlsl -# RUN: %dxc_target -T ps_6_0 -E mainPS -Fo %t-ps.o %t/pixel.hlsl -# RUN: %offloader %t/pipeline.yaml %t-vs.o %t-ps.o diff --git a/test/Feature/Textures/Texture2D.SampleGrad.test.yaml b/test/Feature/Textures/Texture2D.SampleGrad.test.yaml new file mode 100644 index 000000000..f55154803 --- /dev/null +++ b/test/Feature/Textures/Texture2D.SampleGrad.test.yaml @@ -0,0 +1,112 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] SamplerState Samp0 : register(s0); // Mag=Nearest, Min=Linear +[[vk::binding(2, 0)]] SamplerState Samp1 : register(s1); // Mag=Linear, Min=Nearest +[[vk::binding(3, 0)]] RWBuffer Out : register(u0); + +[numthreads(1, 1, 1)] +void main() { + float2 uv0 = float2(0.5, 0.5); + float2 uv1 = float2(0.25, 0.25); + + float2 ddx_mag = float2(0.0001, 0.0); + float2 ddy_mag = float2(0.0, 0.0001); + float2 ddx_min = float2(2.0, 0.0); + float2 ddy_min = float2(0.0, 2.0); + + // 1. Samp0 at (0.5, 0.5) + // Mag -> Nearest(0.5) -> White + // Min -> Linear(0.5) -> Grey + Out[0] = Tex.SampleGrad(Samp0, uv0, ddx_mag, ddy_mag); + Out[1] = Tex.SampleGrad(Samp0, uv0, ddx_min, ddy_min); + + // 2. Samp1 at (0.25, 0.25) + // Mag -> Linear(0.25) -> Red + // Min -> Nearest(0.25) -> Red + Out[2] = Tex.SampleGrad(Samp1, uv1, ddx_mag, ddy_mag); + Out[3] = Tex.SampleGrad(Samp1, uv1, ddx_min, ddy_min); + + // 3. Mip Selection Test + // LOD = log2(1.0 * 2) = 1.0 -> Mip 1 + float2 ddx_mip1 = float2(1.0, 0.0); + float2 ddy_mip1 = float2(0.0, 1.0); + Out[4] = Tex.SampleGrad(Samp0, uv0, ddx_mip1, ddy_mip1); // Yellow +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 2, Depth: 1, MipLevels: 2 } + Data: [ 1.0, 0.0, 0.0, 1.0, # Red (0,0) + 0.0, 1.0, 0.0, 1.0, # Green (1,0) + 0.0, 0.0, 1.0, 1.0, # Blue (0,1) + 1.0, 1.0, 1.0, 1.0, # White (1,1) + 1.0, 1.0, 0.0, 1.0 ] # Mip 1 (1x1) - Yellow + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 80 # 5 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 1.0, 1.0, 1.0, 1.0, # Samp0, uv0, Mag -> White + 1.0, 1.0, 0.0, 1.0, # Samp0, uv0, Min -> Yellow (mip 1) + 1.0, 0.0, 0.0, 1.0, # Samp1, uv1, Mag -> Red + 1.0, 1.0, 0.0, 1.0, # Samp1, uv1, Min -> Yellow (mip 1) + 1.0, 1.0, 0.0, 1.0 ] # Mip Selection -> Yellow (mip 1) + +Samplers: + - Name: Samp0 + MinFilter: Linear + MagFilter: Nearest + Address: Clamp + - Name: Samp1 + MinFilter: Nearest + MagFilter: Linear + Address: Clamp + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: Samp0 + Kind: Sampler + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + - Name: Samp1 + Kind: Sampler + DirectXBinding: { Register: 1, Space: 0 } + VulkanBinding: { Binding: 2 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 3 } + +Results: + - Result: SampleGradTest + Rule: BufferFloatULP + Actual: Out + Expected: Expected + ULPT: 1 +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# XFAIL: DirectX || Metal +# XFAIL: Clang && !Vulkan + +# 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.Sampler.address.test.yaml b/test/Feature/Textures/Texture2D.Sampler.address.test.yaml index 7d706205a..844b8f5e7 100644 --- a/test/Feature/Textures/Texture2D.Sampler.address.test.yaml +++ b/test/Feature/Textures/Texture2D.Sampler.address.test.yaml @@ -30,20 +30,27 @@ float4 mainPS(float4 Pos : SV_POSITION) : SV_Target { float2 uv = float2(1.25, 0.5); // 1. Clamp: Snaps 1.25 -> 1.0 (Edge). Result: Green. - Out[0] = Tex.SampleLevel(ClampSamp, uv, 0); + float4 r0 = Tex.SampleLevel(ClampSamp, uv, 0); // 2. Repeat: Wraps 1.25 -> 0.25 (Start). Result: Red. - Out[1] = Tex.SampleLevel(RepeatSamp, uv, 0); + float4 r1 = Tex.SampleLevel(RepeatSamp, uv, 0); // 3. Border: 1.25 is outside. Result: Border Color (0,0,0,0). - Out[2] = Tex.SampleLevel(BorderSamp, uv, 0); + float4 r2 = Tex.SampleLevel(BorderSamp, uv, 0); // 4. Mirror: Test at 1.75 // Range [1.0, 2.0] is mirrored [1.0, 0.0]. // 1.75 maps to 1.0 - (0.75) = 0.25 (Center of Red). // Result: Red. // Note: Repeat/Clamp would map 1.75 to Green (0.75 or 1.0). - Out[3] = Tex.SampleLevel(MirrorSamp, float2(1.75, 0.5), 0); + float4 r3 = Tex.SampleLevel(MirrorSamp, float2(1.75, 0.5), 0); + + if (all((int2)Pos.xy == 0)) { + Out[0] = r0; + Out[1] = r1; + Out[2] = r2; + Out[3] = r3; + } return float4(0,0,0,0); } diff --git a/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml b/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml index c72a3abca..5dfd8255b 100644 --- a/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml +++ b/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml @@ -27,12 +27,17 @@ float4 mainPS(float4 Pos : SV_POSITION) : SV_Target { // Linear sample at center (0.5, 0.5) // Should average all 4 pixels: (1+0+0+1)/4 = 0.5 for R, etc. // Expected: (0.5, 0.5, 0.5, 1.0) - Out[0] = Tex.SampleLevel(LinearSamp, float2(0.5, 0.5), 0); + float4 r0 = Tex.SampleLevel(LinearSamp, float2(0.5, 0.5), 0); // Point sample at top-left center (0.25, 0.25) // Should pick (0,0) -> Red // Expected: (1.0, 0.0, 0.0, 1.0) - Out[1] = Tex.SampleLevel(PointSamp, float2(0.25, 0.25), 0); + float4 r1 = Tex.SampleLevel(PointSamp, float2(0.25, 0.25), 0); + + if (all((int2)Pos.xy == 0)) { + Out[0] = r0; + Out[1] = r1; + } return float4(0,0,0,0); } diff --git a/test/Feature/Textures/Texture2D.Sampler.mips.test.yaml b/test/Feature/Textures/Texture2D.Sampler.mips.test.yaml new file mode 100644 index 000000000..50d574b5d --- /dev/null +++ b/test/Feature/Textures/Texture2D.Sampler.mips.test.yaml @@ -0,0 +1,103 @@ +#--- source.hlsl +[[vk::binding(0, 0)]] Texture2D Tex : register(t0); +[[vk::binding(1, 0)]] SamplerState MagLinearMinPoint : register(s0); +[[vk::binding(2, 0)]] SamplerState MagPointMinLinear : register(s1); +[[vk::binding(3, 0)]] RWBuffer Out : register(u0); + +[numthreads(1,1,1)] +void main() { + // Texture 2x1: [Red, Green] + // Centers at x=0.25, x=0.75. + // Sample at x=0.4. + // Nearest -> Red (closer to 0.25). + // Linear -> Lerp(Red, Green, 0.3) -> (0.7, 0.3, 0.0, 1.0). + + float2 uv = float2(0.4, 0.5); + + // 0: Mag Test (LOD -1) with Mag=Linear -> Linear Result + Out[0] = Tex.SampleLevel(MagLinearMinPoint, uv, -1.0); + + // 1: Mag Test (LOD -1) with Mag=Point -> Nearest Result + Out[1] = Tex.SampleLevel(MagPointMinLinear, uv, -1.0); + + // 2: Min Test (LOD 1) with Min=Point -> Nearest Result + Out[2] = Tex.SampleLevel(MagLinearMinPoint, uv, 1.0); + + // 3: Min Test (LOD 1) with Min=Linear -> Linear Result + Out[3] = Tex.SampleLevel(MagPointMinLinear, uv, 1.0); +} + +//--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: Tex + Format: Float32 + Channels: 4 + OutputProps: { Width: 2, Height: 1, Depth: 1, MipLevels: 2 } + Data: [ 1.0, 0.0, 0.0, 1.0, # Red + 0.0, 1.0, 0.0, 1.0, # Green + 0.5, 0.5, 0.0, 1.0 ] # Mip 1 + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 64 # 4 * sizeof(float4) + + - Name: Expected + Format: Float32 + Channels: 4 + Data: [ 0.7, 0.3, 0.0, 1.0, # Linear + 1.0, 0.0, 0.0, 1.0, # Nearest + 0.5, 0.5, 0.0, 1.0, # Nearest (LOD 1) + 0.5, 0.5, 0.0, 1.0 ] # Linear (LOD 1) + +Samplers: + - Name: MagLinearMinPoint + MinFilter: Nearest + MagFilter: Linear + Address: Clamp + - Name: MagPointMinLinear + MinFilter: Linear + MagFilter: Nearest + Address: Clamp + +DescriptorSets: + - Resources: + - Name: Tex + Kind: Texture2D + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 0 } + - Name: MagLinearMinPoint + Kind: Sampler + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 1 } + - Name: MagPointMinLinear + Kind: Sampler + DirectXBinding: { Register: 1, Space: 0 } + VulkanBinding: { Binding: 2 } + - Name: Out + Kind: RWBuffer + DirectXBinding: { Register: 0, Space: 0 } + VulkanBinding: { Binding: 3 } + +Results: + - Result: FilterTest + Rule: BufferFloatEpsilon + Actual: Out + Expected: Expected + Epsilon: 0.01 +... +#--- end + +# Unimplemented: Clang + DX: https://github.com/llvm/llvm-project/issues/101558 +# XFAIL: DirectX || Metal +# XFAIL: Clang && !Vulkan + +# 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/Tools/Offloader/BufferSizeValidation.test.yaml b/test/Tools/Offloader/BufferSizeValidation.test.yaml new file mode 100644 index 000000000..f586d68dc --- /dev/null +++ b/test/Tools/Offloader/BufferSizeValidation.test.yaml @@ -0,0 +1,32 @@ +#--- pipeline.yaml +--- +Shaders: + - Stage: Compute + Entry: main + DispatchSize: [1, 1, 1] + +Buffers: + - Name: MismatchBuffer + Format: Float32 + Channels: 4 + # Expected size: 2x2x1 * 4 * 4 = 64 bytes + # Actual size: defined by FillSize = 16 bytes + OutputProps: { Width: 2, Height: 2, Depth: 1 } + FillSize: 16 + + - Name: Out + Format: Float32 + Channels: 4 + FillSize: 16 + +DescriptorSets: + - Resources: [] + +Results: [] +... +#--- end + +# RUN: split-file %s %t +# RUN: not %offloader %t/pipeline.yaml 2>&1 | FileCheck %s + +# CHECK: Buffer 'MismatchBuffer' size (16) does not match OutputProps dimensions (64)