From 1d7b6556079f143d11b59cbb78f4d219e72661e7 Mon Sep 17 00:00:00 2001 From: Manon Oomen Date: Tue, 5 May 2026 16:31:44 +0200 Subject: [PATCH 1/5] Make vertex buffers optional in the TraditionalRaster pipeline. --- include/Support/Pipeline.h | 4 +-- lib/API/DX/Device.cpp | 52 +++++++++++++++----------------------- lib/API/MTL/MTLDevice.cpp | 8 ++---- lib/API/VK/Device.cpp | 3 +++ 4 files changed, 28 insertions(+), 39 deletions(-) diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index c38ca24b1..6f0f2019e 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -383,11 +383,11 @@ struct VertexAttribute { struct IOBindings { std::string VertexBuffer; - CPUBuffer *VertexBufferPtr; + CPUBuffer *VertexBufferPtr = nullptr; llvm::SmallVector VertexAttributes; std::string RenderTarget; - CPUBuffer *RTargetBufferPtr; + CPUBuffer *RTargetBufferPtr = nullptr; uint32_t getVertexStride() const { uint32_t Stride = 0; diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index aabba47c6..003d541c1 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -1638,6 +1638,16 @@ class DXDevice : public offloadtest::Device { if (auto Err = CreateBuffer(Resource, IS.RootResources)) return Err; } + + if (P.isTraditionalRaster() && P.Bindings.VertexBufferPtr) { + auto VBOrErr = offloadtest::createVertexBufferFromCPUBuffer( + *this, *P.Bindings.VertexBufferPtr); + if (!VBOrErr) + return VBOrErr.takeError(); + IS.VB = std::move(*VBOrErr); + llvm::outs() << "Vertex buffer created.\n"; + } + return llvm::Error::success(); } @@ -1911,39 +1921,11 @@ class DXDevice : public offloadtest::Device { return llvm::Error::success(); } - llvm::Error createVertexBuffer(Pipeline &P, InvocationState &IS) { - if (!P.Bindings.VertexBufferPtr) - return llvm::createStringError( - std::errc::invalid_argument, - "No vertex buffer bound for graphics pipeline."); - - auto VBOrErr = offloadtest::createVertexBufferFromCPUBuffer( - *this, *P.Bindings.VertexBufferPtr); - if (!VBOrErr) - return VBOrErr.takeError(); - IS.VB = std::move(*VBOrErr); - - auto &VBBuf = llvm::cast(*IS.VB); - D3D12_VERTEX_BUFFER_VIEW VBView = {}; - VBView.BufferLocation = VBBuf.Buffer->GetGPUVirtualAddress(); - VBView.SizeInBytes = static_cast(IS.VB->getSizeInBytes()); - VBView.StrideInBytes = P.Bindings.getVertexStride(); - - IS.CB->CmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - IS.CB->CmdList->IASetVertexBuffers(0, 1, &VBView); - - return llvm::Error::success(); - } - llvm::Error createGraphicsCommands(Pipeline &P, InvocationState &IS) { auto &RT = llvm::cast(*IS.RT); auto &DS = llvm::cast(*IS.DS); auto &RTReadback = llvm::cast(*IS.RTReadback); - if (!IS.VB) - return llvm::createStringError(std::errc::invalid_argument, - "Vertex buffer not initialized."); - const DXPipelineState &DXPipeline = llvm::cast(*IS.Pipeline.get()); IS.CB->CmdList->SetGraphicsRootSignature(DXPipeline.RootSig.Get()); @@ -1981,6 +1963,17 @@ class DXDevice : public offloadtest::Device { static_cast(VP.Height)}; IS.CB->CmdList->RSSetScissorRects(1, &Scissor); + if (IS.VB) { + auto &VBBuf = llvm::cast(*IS.VB); + D3D12_VERTEX_BUFFER_VIEW VBView = {}; + VBView.BufferLocation = VBBuf.Buffer->GetGPUVirtualAddress(); + VBView.SizeInBytes = static_cast(IS.VB->getSizeInBytes()); + VBView.StrideInBytes = P.Bindings.getVertexStride(); + IS.CB->CmdList->IASetVertexBuffers(0, 1, &VBView); + } + + IS.CB->CmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + IS.CB->CmdList->DrawInstanced(P.getVertexCount(), 1, 0, 0); // Transition the render target to copy source and copy to the readback @@ -2108,9 +2101,6 @@ class DXDevice : public offloadtest::Device { if (auto Err = createDepthStencil(P, State)) return Err; llvm::outs() << "Depth stencil created.\n"; - if (auto Err = createVertexBuffer(P, State)) - return Err; - llvm::outs() << "Vertex buffer created.\n"; ShaderContainer VS = {}; ShaderContainer PS = {}; diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index a5ce8d3ed..a22eca815 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -955,17 +955,13 @@ class MTLDevice : public offloadtest::Device { } } - if (P.isTraditionalRaster()) { - if (!P.Bindings.VertexBufferPtr) - return llvm::createStringError( - std::errc::invalid_argument, - "No vertex buffer specified for graphics pipeline."); - + if (P.isTraditionalRaster() && P.Bindings.VertexBufferPtr) { auto VBOrErr = offloadtest::createVertexBufferFromCPUBuffer( *this, *P.Bindings.VertexBufferPtr); if (!VBOrErr) return VBOrErr.takeError(); IS.VB = std::move(*VBOrErr); + llvm::outs() << "Vertex buffer created.\n"; } return llvm::Error::success(); } diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index 6ee8adaa8..aef419f2c 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -1936,12 +1936,15 @@ class VulkanDevice : public offloadtest::Device { return llvm::createStringError( std::errc::invalid_argument, "No Vertex buffer specified for graphics pipeline."); + } + if (P.isTraditionalRaster() && P.Bindings.VertexBufferPtr) { auto VBOrErr = offloadtest::createVertexBufferFromCPUBuffer( *this, *P.Bindings.VertexBufferPtr); if (!VBOrErr) return VBOrErr.takeError(); IS.VB = std::move(*VBOrErr); + llvm::outs() << "Vertex buffer created.\n"; } return llvm::Error::success(); From 914b3f1372d5168e1bc5fe8f4ffe30754261460f Mon Sep 17 00:00:00 2001 From: Manon Oomen Date: Tue, 5 May 2026 17:27:17 +0200 Subject: [PATCH 2/5] Add a VerticesFromVertexID.test that doesn't use a vertex buffer. --- test/Graphics/VerticesFromVertexID.test | 74 +++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/Graphics/VerticesFromVertexID.test diff --git a/test/Graphics/VerticesFromVertexID.test b/test/Graphics/VerticesFromVertexID.test new file mode 100644 index 000000000..4eb933f77 --- /dev/null +++ b/test/Graphics/VerticesFromVertexID.test @@ -0,0 +1,74 @@ +#--- vertex.hlsl +struct PSInput +{ + float4 position : SV_POSITION; + float4 color : COLOR; +}; + +PSInput main(uint vertexID : SV_VertexID) +{ + PSInput result; + + if (vertexID == 0) { + result.position = float4(0.0, 0.25, 0.0, 1.0); + result.color = float4(1.0, 0.0, 0.0, 1.0); + } else if (vertexID == 1) { + result.position = float4(0.25, -0.25, 0.0, 1.0); + result.color = float4(0.0, 1.0, 0.0, 1.0); + } else { + result.position = float4(-0.25, -0.25, 0.0, 1.0); + result.color = float4(0.0, 0.0, 1.0, 1.0); + } + + return result; +} + + +#--- pixel.hlsl +struct PSInput +{ + float4 position : SV_POSITION; + float4 color : COLOR; +}; + +float4 main(PSInput input) : SV_TARGET +{ + return input.color; +} +#--- pipeline.yaml +--- +Shaders: + - Stage: Vertex + Entry: main + - Stage: Pixel + Entry: main +Buffers: + - Name: Output + Format: Float32 + Channels: 4 + FillSize: 1048576 # 256x256 @ 16 bytes per pixel + OutputProps: + Height: 256 + Width: 256 + Depth: 1 +Bindings: + RenderTarget: Output +DescriptorSets: [] +DispatchParameters: + VertexCount: 3 +... +#--- rules.yaml +--- +- Type: PixelPercent + Val: 0.2 # No more than 0.2% of pixels may be visibly different. +... +#--- end + +# XFAIL: Clang && !Vulkan +# REQUIRES: goldenimage + +# RUN: split-file %s %t +# RUN: %dxc_target -T vs_6_0 -Fo %t-vertex.o %t/vertex.hlsl +# RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl +# RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-pixel.o -r Output -o %t/Output.png +# RUN: imgdiff %t/Output.png %goldenimage_dir/hlsl/Graphics/VerticesFromVertexID.png -rules %t/rules.yaml From d9882333d26cab6951da7d4ace6b164de7236ed2 Mon Sep 17 00:00:00 2001 From: Manon Oomen Date: Fri, 8 May 2026 14:17:37 +0200 Subject: [PATCH 3/5] Remove old vertex buffer check that is no longer needed. --- lib/API/VK/Device.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index aef419f2c..908145e3c 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -1931,11 +1931,6 @@ class VulkanDevice : public offloadtest::Device { // conditional on the pipeline definition. if (auto Err = createDepthStencil(P, IS)) return Err; - - if (P.Bindings.VertexBufferPtr == nullptr) - return llvm::createStringError( - std::errc::invalid_argument, - "No Vertex buffer specified for graphics pipeline."); } if (P.isTraditionalRaster() && P.Bindings.VertexBufferPtr) { @@ -2740,9 +2735,10 @@ class VulkanDevice : public offloadtest::Device { << P.DispatchParameters.DispatchGroupCount[2] << " }\n"; } else { VkDeviceSize Offsets[1]{0}; - assert(IS.VB); - VkBuffer VBHandle = llvm::cast(*IS.VB).Buffer; - vkCmdBindVertexBuffers(IS.CB->CmdBuffer, 0, 1, &VBHandle, Offsets); + if (IS.VB) { + VkBuffer VBHandle = llvm::cast(*IS.VB).Buffer; + vkCmdBindVertexBuffers(IS.CB->CmdBuffer, 0, 1, &VBHandle, Offsets); + } // instanceCount must be >=1 to draw; previously was 0 which draws nothing vkCmdDraw(IS.CB->CmdBuffer, P.getVertexCount(), 1, 0, 0); llvm::outs() << "Drew " << P.getVertexCount() << " vertices.\n"; From 149a668a51939307a3589c68d1ac0feb899b29bb Mon Sep 17 00:00:00 2001 From: Manon Oomen Date: Fri, 8 May 2026 16:15:22 +0200 Subject: [PATCH 4/5] Only bind vertex buffer is available. --- lib/API/MTL/MTLDevice.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index a22eca815..8e5e78d73 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -1142,9 +1142,10 @@ class MTLDevice : public offloadtest::Device { CmdEncoder->setCullMode(MTL::CullModeNone); CmdEncoder->setFrontFacingWinding(MTL::WindingCounterClockwise); - // Bind vertex buffer at slot 0 to match the vertex descriptor which - // references buffer index 0. - CmdEncoder->setVertexBuffer(llvm::cast(*IS.VB).Buf, 0, 0); + if (IS.VB) + // Bind vertex buffer at slot 0 to match the vertex descriptor which + // references buffer index 0. + CmdEncoder->setVertexBuffer(llvm::cast(*IS.VB).Buf, 0, 0); CmdEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), P.getVertexCount()); @@ -1384,8 +1385,8 @@ class MTLDevice : public offloadtest::Device { "Mismatch between vertex shader attribute count and pipeline " "vertex input count."); - // Collect the attribute indices the shader expects so that we can map the - // specified attributes onto the correct indices. + // Collect the attribute indices the shader expects so that we can map + // the specified attributes onto the correct indices. llvm::StringMap ShaderAttrIndices; for (uint32_t I = 0; I < FnAttrs->count(); ++I) { auto *A = static_cast(FnAttrs->object(I)); From dd0dcf34e4c51a1187f08b2c597baa6bbb0fed1d Mon Sep 17 00:00:00 2001 From: Manon Oomen Date: Tue, 12 May 2026 18:23:47 +0200 Subject: [PATCH 5/5] Use SimpleTriangle.png as the reference image. --- test/Graphics/VerticesFromVertexID.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Graphics/VerticesFromVertexID.test b/test/Graphics/VerticesFromVertexID.test index 4eb933f77..c3bd1288a 100644 --- a/test/Graphics/VerticesFromVertexID.test +++ b/test/Graphics/VerticesFromVertexID.test @@ -71,4 +71,4 @@ DispatchParameters: # RUN: %dxc_target -T vs_6_0 -Fo %t-vertex.o %t/vertex.hlsl # RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl # RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-pixel.o -r Output -o %t/Output.png -# RUN: imgdiff %t/Output.png %goldenimage_dir/hlsl/Graphics/VerticesFromVertexID.png -rules %t/rules.yaml +# RUN: imgdiff %t/Output.png %goldenimage_dir/hlsl/Graphics/SimpleTriangle.png -rules %t/rules.yaml