From 684a16986a4bf51c8f0b1f0a691766fe242a56f5 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 31 Mar 2026 13:25:23 +0200 Subject: [PATCH 01/11] Add `getSizeInBytes` to `Buffer` --- include/API/Buffer.h | 1 + lib/API/DX/Device.cpp | 2 ++ lib/API/MTL/MTLDevice.cpp | 2 ++ lib/API/VK/Device.cpp | 2 ++ 4 files changed, 7 insertions(+) diff --git a/include/API/Buffer.h b/include/API/Buffer.h index c530873aa..659c33a7a 100644 --- a/include/API/Buffer.h +++ b/include/API/Buffer.h @@ -23,6 +23,7 @@ struct BufferCreateDesc { class Buffer { public: virtual ~Buffer(); + virtual size_t getSizeInBytes() const = 0; Buffer(const Buffer &) = delete; Buffer &operator=(const Buffer &) = delete; diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index c74913d27..993dc594d 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -293,6 +293,8 @@ class DXBuffer : public offloadtest::Buffer { DXBuffer(ComPtr Buffer, llvm::StringRef Name, BufferCreateDesc Desc, size_t SizeInBytes) : Buffer(Buffer), Name(Name), Desc(Desc), SizeInBytes(SizeInBytes) {} + + size_t getSizeInBytes() const override { return SizeInBytes; } }; class DXTexture : public offloadtest::Texture { diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index 4adb5ca0f..962ff6bd5 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -127,6 +127,8 @@ class MTLBuffer : public offloadtest::Buffer { size_t SizeInBytes) : Buf(Buf), Name(Name), Desc(Desc), SizeInBytes(SizeInBytes) {} + size_t getSizeInBytes() const override { return SizeInBytes; } + ~MTLBuffer() override { if (Buf) Buf->release(); diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index f77fb8760..a20e8f447 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -386,6 +386,8 @@ class VulkanBuffer : public offloadtest::Buffer { : Dev(Dev), Buffer(Buffer), Memory(Memory), Name(Name), Desc(Desc), SizeInBytes(SizeInBytes) {} + size_t getSizeInBytes() const override { return SizeInBytes; } + ~VulkanBuffer() override { vkDestroyBuffer(Dev, Buffer, nullptr); vkFreeMemory(Dev, Memory, nullptr); From 7fb2ecbb6a3be83ce414603caca4e70d573d1cf0 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 31 Mar 2026 13:57:02 +0200 Subject: [PATCH 02/11] Add `VertexBuffers` parsing to pipeline yaml --- include/API/Resources.h | 29 +++++++++ include/Support/Pipeline.h | 87 +++++++++++++++++++++++++++ lib/Support/Pipeline.cpp | 118 +++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) diff --git a/include/API/Resources.h b/include/API/Resources.h index c7610a00a..0b4b81dcf 100644 --- a/include/API/Resources.h +++ b/include/API/Resources.h @@ -121,6 +121,35 @@ inline uint32_t getFormatSizeInBytes(Format Format) { llvm_unreachable("All Format cases handled"); } +// Returns the number of components per element for the given format. +inline uint32_t getComponentCount(Format Format) { + switch (Format) { + case Format::R16Sint: + case Format::R16Uint: + case Format::R32Sint: + case Format::R32Uint: + case Format::R32Float: + case Format::D32Float: + return 1; + case Format::RG16Sint: + case Format::RG16Uint: + case Format::RG32Sint: + case Format::RG32Uint: + case Format::RG32Float: + case Format::D32FloatS8Uint: + return 2; + case Format::RGB32Float: + return 3; + case Format::RGBA16Sint: + case Format::RGBA16Uint: + case Format::RGBA32Sint: + case Format::RGBA32Uint: + case Format::RGBA32Float: + return 4; + } + llvm_unreachable("All Format cases handled"); +} + inline bool isDepthFormat(Format Format) { switch (Format) { case Format::R16Sint: diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index 01da45b76..5033d5c56 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -13,6 +13,8 @@ #ifndef OFFLOADTEST_SUPPORT_PIPELINE_H #define OFFLOADTEST_SUPPORT_PIPELINE_H +#include "API/Resources.h" + #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" @@ -345,6 +347,48 @@ struct VertexAttribute { uint32_t size() const { return getFormatSize(Format) * Channels; } }; +// Parsed vertex stream from the YAML VertexBuffers section. Holds per-stream +// data before interleaving into the final vertex buffer. +// +// Values are stored as doubles (the YAML parser's native numeric type) rather +// than in the target format's storage type. This avoids needing format-specific +// parsing and lets us derive the vertex count directly from the number of +// values. +// +// Conversion to the target byte representation happens during interleaving +// into ParsedVertexBuffer::InterleavedData. +struct VertexStreamData { + std::string Name; // Semantic name (e.g. POSITION, COLOR). + Format Fmt; + llvm::SmallVector Values; // One value per component. +}; + +// Parsed vertex buffer from the YAML VertexBuffers section. The parser +// interleaves per-stream data into InterleavedData. +// +// TODO: Add support for de-interleaved data? +struct ParsedVertexBuffer { + std::string Name; + llvm::SmallVector Streams; + // Interleaved vertex data, computed by the parser from per-stream data. + std::unique_ptr InterleavedData; + size_t InterleavedSize = 0; + + uint32_t getStride() const { + uint32_t Stride = 0; + for (const auto &S : Streams) + Stride += getFormatSize(S.Fmt); + return Stride; + } + + uint32_t getVertexCount() const { + uint32_t Stride = getStride(); + if (Stride == 0) + return 0; + return InterleavedSize / Stride; + } +}; + struct IOBindings { std::string VertexBuffer; CPUBuffer *VertexBufferPtr; @@ -415,6 +459,7 @@ struct Pipeline { IOBindings Bindings; llvm::SmallVector PushConstants; llvm::SmallVector Buffers; + llvm::SmallVector VertexBuffers; llvm::SmallVector Samplers; llvm::SmallVector Results; llvm::SmallVector Sets; @@ -441,6 +486,13 @@ struct Pipeline { return nullptr; } + ParsedVertexBuffer *getVertexBuffer(llvm::StringRef Name) { + for (auto &VB : VertexBuffers) + if (Name == VB.Name) + return &VB; + return nullptr; + } + Sampler *getSampler(llvm::StringRef Name) { for (auto &S : Samplers) if (Name == S.Name) @@ -464,6 +516,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::Shader) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::dx::RootParameter) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::Result) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::VertexAttribute) +LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::VertexStreamData) +LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::ParsedVertexBuffer) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::SpecializationConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::PushConstantBlock) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::PushConstantValue) @@ -519,6 +573,14 @@ template <> struct MappingTraits { static void mapping(IO &I, offloadtest::VertexAttribute &A); }; +template <> struct MappingTraits { + static void mapping(IO &I, offloadtest::VertexStreamData &S); +}; + +template <> struct MappingTraits { + static void mapping(IO &I, offloadtest::ParsedVertexBuffer &VB); +}; + template <> struct MappingTraits { static void mapping(IO &I, offloadtest::OutputProperties &P); }; @@ -547,6 +609,31 @@ template <> struct MappingTraits { static void mapping(IO &I, offloadtest::SpecializationConstant &C); }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &I, offloadtest::Format &V) { +#define ENUM_CASE(Val) I.enumCase(V, #Val, offloadtest::Format::Val) + ENUM_CASE(R16Sint); + ENUM_CASE(R16Uint); + ENUM_CASE(RG16Sint); + ENUM_CASE(RG16Uint); + ENUM_CASE(RGBA16Sint); + ENUM_CASE(RGBA16Uint); + ENUM_CASE(R32Sint); + ENUM_CASE(R32Uint); + ENUM_CASE(R32Float); + ENUM_CASE(RG32Sint); + ENUM_CASE(RG32Uint); + ENUM_CASE(RG32Float); + ENUM_CASE(RGB32Float); + ENUM_CASE(RGBA32Sint); + ENUM_CASE(RGBA32Uint); + ENUM_CASE(RGBA32Float); + ENUM_CASE(D32Float); + ENUM_CASE(D32FloatS8Uint); +#undef ENUM_CASE + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &I, offloadtest::Rule &V) { #define ENUM_CASE(Val) I.enumCase(V, #Val, offloadtest::Rule::Val) diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index d28f81182..faa546336 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -44,6 +44,7 @@ void MappingTraits::mapping(IO &I, I.mapOptional("RuntimeSettings", P.Settings); I.mapRequired("Buffers", P.Buffers); + I.mapOptional("VertexBuffers", P.VertexBuffers); I.mapOptional("Samplers", P.Samplers); I.mapOptional("Results", P.Results); I.mapRequired("DescriptorSets", P.Sets); @@ -398,6 +399,123 @@ void MappingTraits::mapping( I.mapRequired("Name", A.Name); } +void MappingTraits::mapping( + IO &I, offloadtest::VertexStreamData &S) { + I.mapRequired("Name", S.Name); + I.mapRequired("Format", S.Fmt); + // Values are always parsed as doubles regardless of the target format. + // Conversion to the storage type happens during interleaving. + I.mapRequired("Data", S.Values); + + if (!I.outputting()) { + if (!offloadtest::isVertexCompatible(S.Fmt)) { + I.setError("Format '" + llvm::Twine(offloadtest::getFormatName(S.Fmt)) + + "' is not a valid vertex stream format."); + return; + } + const uint32_t Components = offloadtest::getComponentCount(S.Fmt); + if (S.Values.size() % Components != 0) { + I.setError("Data size is not a multiple of the format's component " + "count (" + + llvm::Twine(Components) + ")."); + return; + } + } +} + +// Converts a double value to the storage type for the given format and writes +// it to Dst. Returns the number of bytes written. +static uint32_t writeComponent(char *Dst, double Value, + offloadtest::Format Fmt) { + using F = offloadtest::Format; + switch (Fmt) { + case F::R16Sint: + case F::RG16Sint: + case F::RGBA16Sint: { + int16_t V = static_cast(Value); + memcpy(Dst, &V, sizeof(V)); + return sizeof(V); + } + case F::R16Uint: + case F::RG16Uint: + case F::RGBA16Uint: { + uint16_t V = static_cast(Value); + memcpy(Dst, &V, sizeof(V)); + return sizeof(V); + } + case F::R32Sint: + case F::RG32Sint: + case F::RGBA32Sint: { + int32_t V = static_cast(Value); + memcpy(Dst, &V, sizeof(V)); + return sizeof(V); + } + case F::R32Uint: + case F::RG32Uint: + case F::RGBA32Uint: { + uint32_t V = static_cast(Value); + memcpy(Dst, &V, sizeof(V)); + return sizeof(V); + } + case F::R32Float: + case F::RG32Float: + case F::RGB32Float: + case F::RGBA32Float: { + float V = static_cast(Value); + memcpy(Dst, &V, sizeof(V)); + return sizeof(V); + } + case F::D32Float: + case F::D32FloatS8Uint: + llvm_unreachable("Depth formats are not valid vertex stream formats"); + } + llvm_unreachable("All Format cases handled"); +} + +void MappingTraits::mapping( + IO &I, offloadtest::ParsedVertexBuffer &VB) { + I.mapRequired("Name", VB.Name); + I.mapRequired("Streams", VB.Streams); + + if (!I.outputting() && !VB.Streams.empty()) { + // Derive vertex count from the first stream's values and component count. + const uint32_t Components0 = + offloadtest::getComponentCount(VB.Streams[0].Fmt); + const uint32_t VertexCount = VB.Streams[0].Values.size() / Components0; + + // Validate all streams have the same vertex count. + for (size_t S = 1; S < VB.Streams.size(); ++S) { + const uint32_t Components = + offloadtest::getComponentCount(VB.Streams[S].Fmt); + const uint32_t Count = VB.Streams[S].Values.size() / Components; + if (Count != VertexCount) { + I.setError("All vertex streams must have the same vertex count."); + return; + } + } + + // Interleave per-stream data into a single buffer, converting each + // double value to the stream's target storage type. + const uint32_t Stride = VB.getStride(); + VB.InterleavedSize = static_cast(Stride) * VertexCount; + VB.InterleavedData = std::make_unique(VB.InterleavedSize); + + for (uint32_t V = 0; V < VertexCount; ++V) { + uint32_t StreamOffset = 0; + for (const auto &Stream : VB.Streams) { + const uint32_t Components = + offloadtest::getComponentCount(Stream.Fmt); + char *Dst = VB.InterleavedData.get() + V * Stride + StreamOffset; + for (uint32_t C = 0; C < Components; ++C) { + double Value = Stream.Values[V * Components + C]; + Dst += writeComponent(Dst, Value, Stream.Fmt); + } + StreamOffset += offloadtest::getFormatSize(Stream.Fmt); + } + } + } +} + void MappingTraits::mapping( IO &I, offloadtest::IOBindings &B) { I.mapOptional("VertexBuffer", B.VertexBuffer); From 279f08e5793c37a2072883cce99fdc4b404ab27c Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 31 Mar 2026 18:05:37 +0200 Subject: [PATCH 03/11] use new parsed vtx buffers in backends --- include/API/Buffer.h | 6 + include/Support/Pipeline.h | 30 +--- lib/API/DX/Device.cpp | 73 +++++---- lib/API/MTL/MTLDevice.cpp | 142 +++++++++++------- lib/API/VK/Device.cpp | 111 ++++++++------ lib/Support/Pipeline.cpp | 15 +- test/Feature/Semantics/ArraySemantics.test | 19 +-- test/Feature/Semantics/MatrixSemantics.test | 19 +-- .../Semantics/NestedStructSemantics.test | 19 +-- test/Feature/Semantics/SemanticTypes.test | 19 +-- test/Feature/Semantics/ShadowedSemantics.test | 19 +-- test/Feature/Semantics/UserSemantics.test | 19 +-- ...Texture2D.CalculateLevelOfDetail.test.yaml | 21 ++- .../Textures/Texture2D.Sample.test.yaml | 21 ++- .../Textures/Texture2D.SampleBias.test.yaml | 21 ++- .../Textures/Texture2D.SampleCmp.test.yaml | 21 ++- .../Texture2D.Sampler.address.test.yaml | 21 ++- .../Texture2D.Sampler.filter.test.yaml | 21 ++- test/Graphics/SimpleTriangle.test | 28 ++-- test/Graphics/ddx_fine.test | 25 ++- test/Graphics/ddy_fine.test | 25 ++- test/Graphics/fwidth.test | 25 ++- 22 files changed, 356 insertions(+), 364 deletions(-) diff --git a/include/API/Buffer.h b/include/API/Buffer.h index 659c33a7a..dcfadd5dc 100644 --- a/include/API/Buffer.h +++ b/include/API/Buffer.h @@ -16,8 +16,14 @@ namespace offloadtest { +enum class BufferUsage { + Storage, + VertexBuffer, +}; + struct BufferCreateDesc { MemoryLocation Location; + BufferUsage Usage; }; class Buffer { diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index 5033d5c56..998f32055 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -338,15 +338,6 @@ struct RuntimeSettings { dx::Settings DX; }; -struct VertexAttribute { - DataFormat Format; - int Channels; - int Offset; - std::string Name; - - uint32_t size() const { return getFormatSize(Format) * Channels; } -}; - // Parsed vertex stream from the YAML VertexBuffers section. Holds per-stream // data before interleaving into the final vertex buffer. // @@ -391,22 +382,10 @@ struct ParsedVertexBuffer { struct IOBindings { std::string VertexBuffer; - CPUBuffer *VertexBufferPtr; - llvm::SmallVector VertexAttributes; + ParsedVertexBuffer *VertexBufferPtr = nullptr; std::string RenderTarget; - CPUBuffer *RTargetBufferPtr; - - uint32_t getVertexStride() const { - uint32_t Stride = 0; - for (auto VA : VertexAttributes) - Stride += VA.size(); - return Stride; - } - - uint32_t getVertexCount() const { - return VertexBufferPtr->size() / getVertexStride(); - } + CPUBuffer *RTargetBufferPtr = nullptr; }; // Describes a contiguous group of bytes in a push constant block. @@ -515,7 +494,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::Sampler) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::Shader) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::dx::RootParameter) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::Result) -LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::VertexAttribute) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::VertexStreamData) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::ParsedVertexBuffer) LLVM_YAML_IS_SEQUENCE_VECTOR(offloadtest::SpecializationConstant) @@ -569,10 +547,6 @@ template <> struct MappingTraits { static void mapping(IO &I, offloadtest::PushConstantBlock &B); }; -template <> struct MappingTraits { - static void mapping(IO &I, offloadtest::VertexAttribute &A); -}; - template <> struct MappingTraits { static void mapping(IO &I, offloadtest::VertexStreamData &S); }; diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index 993dc594d..32e83f62c 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -33,6 +33,8 @@ #include "API/Capabilities.h" #include "API/Device.h" +#include "API/FormatConversion.h" +#include "API/VertexBuffer.h" #include "DXFeatures.h" #include "Support/Pipeline.h" #include "Support/WinError.h" @@ -484,7 +486,7 @@ class DXDevice : public offloadtest::Device { std::shared_ptr RT; std::shared_ptr RTReadback; std::shared_ptr DS; - ComPtr VB; + std::optional VB; llvm::SmallVector DescTables; llvm::SmallVector RootResources; @@ -516,9 +518,9 @@ class DXDevice : public offloadtest::Device { const D3D12_HEAP_TYPE HeapType = getDXHeapType(Desc.Location); const D3D12_RESOURCE_FLAGS Flags = - HeapType == D3D12_HEAP_TYPE_READBACK - ? D3D12_RESOURCE_FLAG_NONE - : D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + HeapType == D3D12_HEAP_TYPE_DEFAULT + ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS + : D3D12_RESOURCE_FLAG_NONE; const D3D12_HEAP_PROPERTIES HeapProps = CD3DX12_HEAP_PROPERTIES(HeapType); const D3D12_RESOURCE_DESC BufferDesc = @@ -1587,6 +1589,7 @@ class DXDevice : public offloadtest::Device { // Create readback buffer sized for the pixel data (raw bytes). BufferCreateDesc BufDesc = {}; BufDesc.Location = MemoryLocation::GpuToCpu; + BufDesc.Usage = BufferUsage::Storage; auto BufOrErr = createBuffer("RTReadback", BufDesc, OutBuf.size()); if (!BufOrErr) return BufOrErr.takeError(); @@ -1610,29 +1613,37 @@ class DXDevice : public offloadtest::Device { return llvm::createStringError( std::errc::invalid_argument, "No vertex buffer bound for graphics pipeline."); - const CPUBuffer &VB = *P.Bindings.VertexBufferPtr; - const uint64_t VBSize = VB.size(); - D3D12_RESOURCE_DESC const Desc = CD3DX12_RESOURCE_DESC::Buffer(VBSize); - CD3DX12_HEAP_PROPERTIES HeapProps = - CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); - if (auto Err = HR::toError(Device->CreateCommittedResource( - &HeapProps, D3D12_HEAP_FLAG_NONE, &Desc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, - IID_PPV_ARGS(&IS.VB)), - "Failed to create vertex buffer")) - return Err; + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; + + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + + VertexBufferDesc VBDesc; + for (const auto &S : PVB.Streams) + VBDesc.Streams.push_back({S.Name, S.Fmt}); + + IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + + // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU + // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly + // vertex buffer instead. + auto *DXBuf = static_cast(IS.VB->Data.get()); void *Ptr = nullptr; - if (auto Err = HR::toError(IS.VB->Map(0, nullptr, &Ptr), + if (auto Err = HR::toError(DXBuf->Buffer->Map(0, nullptr, &Ptr), "Failed to map vertex buffer")) return Err; - memcpy(Ptr, VB.Data[0].get(), VBSize); - IS.VB->Unmap(0, nullptr); + memcpy(Ptr, PVB.InterleavedData.get(), IS.VB->Data->getSizeInBytes()); + DXBuf->Buffer->Unmap(0, nullptr); D3D12_VERTEX_BUFFER_VIEW VBView = {}; - VBView.BufferLocation = IS.VB->GetGPUVirtualAddress(); - VBView.SizeInBytes = static_cast(VBSize); - VBView.StrideInBytes = P.Bindings.getVertexStride(); + VBView.BufferLocation = DXBuf->Buffer->GetGPUVirtualAddress(); + VBView.SizeInBytes = static_cast(IS.VB->Data->getSizeInBytes()); + VBView.StrideInBytes = IS.VB->Desc.getStride(); IS.CB->CmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); IS.CB->CmdList->IASetVertexBuffers(0, 1, &VBView); @@ -1641,13 +1652,16 @@ class DXDevice : public offloadtest::Device { } llvm::Error createGraphicsPSO(Pipeline &P, InvocationState &IS) { - // Create the input layout based on the vertex attributes. + if (!IS.VB) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex buffer not initialized."); + // Create the input layout from the vertex buffer description. std::vector InputLayout; - for (size_t I = 0; I < P.Bindings.VertexAttributes.size(); ++I) { - const VertexAttribute &Attr = P.Bindings.VertexAttributes[I]; - InputLayout.push_back({Attr.Name.c_str(), 0, - getDXFormat(Attr.Format, Attr.Channels), 0, - static_cast(Attr.Offset), + const VertexBufferDesc &VBDesc = IS.VB->Desc; + for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { + const VertexStream &S = VBDesc.Streams[I]; + InputLayout.push_back({S.Name.c_str(), 0, getDXGIFormat(S.Fmt), 0, + VBDesc.getOffset(I), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}); } @@ -1701,6 +1715,9 @@ class DXDevice : public offloadtest::Device { } llvm::Error createGraphicsCommands(Pipeline &P, InvocationState &IS) { + if (!IS.VB) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex buffer not initialized."); IS.CB->CmdList->SetGraphicsRootSignature(IS.RootSig.Get()); if (IS.DescHeap) { ID3D12DescriptorHeap *const Heaps[] = {IS.DescHeap.Get()}; @@ -1737,7 +1754,7 @@ class DXDevice : public offloadtest::Device { static_cast(VP.Height)}; IS.CB->CmdList->RSSetScissorRects(1, &Scissor); - IS.CB->CmdList->DrawInstanced(P.Bindings.getVertexCount(), 1, 0, 0); + IS.CB->CmdList->DrawInstanced(IS.VB->getVertexCount(), 1, 0, 0); // Transition the render target to copy source and copy to the readback // buffer. diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index 962ff6bd5..8a563ef0b 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -10,6 +10,8 @@ #include "metal_irconverter_runtime.h" #include "API/Device.h" +#include "API/FormatConversion.h" +#include "API/VertexBuffer.h" #include "MTLResources.h" #include "Support/Pipeline.h" @@ -198,7 +200,7 @@ class MTLDevice : public offloadtest::Device { MTL::ComputePipelineState *ComputePipeline = nullptr; MTL::RenderPipelineState *RenderPipeline = nullptr; MTL::Buffer *ArgBuffer; - MTL::Buffer *VertexBuffer; + std::optional VB; MTL::VertexDescriptor *VertexDescriptor; llvm::SmallVector Textures; llvm::SmallVector Buffers; @@ -211,56 +213,58 @@ class MTLDevice : public offloadtest::Device { llvm::Error setupVertexShader(InvocationState &IS, const Pipeline &P, MTL::Function *Fn) { - if (P.Bindings.VertexBufferPtr) { - NS::Array *FnAttrs = Fn->vertexAttributes(); - // I'm not really sure if there's any valid case for a vertex shader with - // no vertex attributes, so we just error if that ever occurs. - if (!FnAttrs) - return llvm::createStringError( - std::errc::invalid_argument, - "Vertex shader has no vertex attributes."); - if (FnAttrs->count() != P.Bindings.VertexAttributes.size()) - return llvm::createStringError( - std::errc::invalid_argument, - "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. - llvm::StringMap ShaderAttrIndices; - for (uint32_t Ai = 0; Ai < FnAttrs->count(); ++Ai) { - auto *A = static_cast(FnAttrs->object(Ai)); - if (A && A->isActive()) { - ShaderAttrIndices.insert(std::make_pair( - llvm::StringRef(A->name()->utf8String()), A->attributeIndex())); - llvm::errs() << "Shader attr: " << A->name()->utf8String() - << " at index " << A->attributeIndex() << "\n"; - } - } + if (!IS.VB) + return llvm::Error::success(); + + const VertexBufferDesc &VBDesc = IS.VB->Desc; - IS.VertexDescriptor = MTL::VertexDescriptor::alloc()->init(); - const uint32_t Stride = P.Bindings.getVertexStride(); - for (const VertexAttribute &VA : P.Bindings.VertexAttributes) { - llvm::SmallString<32> AttrName(VA.Name); - llvm::transform(AttrName, AttrName.begin(), tolower); - // Append a zero since we're only supporting one attribute per name. - // We'll need to revisit this if we ever support indexed attributes. - AttrName += "0"; - MTL::VertexAttributeDescriptor *VADesc = - MTL::VertexAttributeDescriptor::alloc()->init(); - VADesc->setBufferIndex(0); - VADesc->setOffset(VA.Offset); - VADesc->setFormat(getMTLVertexFormat(VA.Format, VA.Channels)); - IS.VertexDescriptor->attributes()->setObject( - VADesc, ShaderAttrIndices[AttrName]); + NS::Array *FnAttrs = Fn->vertexAttributes(); + if (!FnAttrs) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex shader has no vertex attributes."); + if (FnAttrs->count() != VBDesc.Streams.size()) + return llvm::createStringError( + std::errc::invalid_argument, + "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. + llvm::StringMap ShaderAttrIndices; + for (uint32_t Ai = 0; Ai < FnAttrs->count(); ++Ai) { + auto *A = static_cast(FnAttrs->object(Ai)); + if (A && A->active()) { + ShaderAttrIndices.insert(std::make_pair( + llvm::StringRef(A->name()->utf8String()), A->attributeIndex())); + llvm::errs() << "Shader attr: " << A->name()->utf8String() + << " at index " << A->attributeIndex() << "\n"; } + } - MTL::VertexBufferLayoutDescriptor *LDesc = - MTL::VertexBufferLayoutDescriptor::alloc()->init(); - LDesc->setStride(Stride); - LDesc->setStepRate(1); - LDesc->setStepFunction(MTL::VertexStepFunctionPerVertex); - IS.VertexDescriptor->layouts()->setObject(LDesc, 0); + IS.VertexDescriptor = MTL::VertexDescriptor::alloc()->init(); + for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { + const VertexStream &S = VBDesc.Streams[I]; + llvm::SmallString<32> AttrName(S.Name); + llvm::transform(AttrName, AttrName.begin(), tolower); + // Append a zero since we're only supporting one attribute per name. + // We'll need to revisit this if we ever support indexed attributes. + AttrName += "0"; + MTL::VertexAttributeDescriptor *VADesc = + MTL::VertexAttributeDescriptor::alloc()->init(); + VADesc->setBufferIndex(0); + VADesc->setOffset(VBDesc.getOffset(I)); + VADesc->setFormat(getMetalVertexFormat(S.Fmt)); + IS.VertexDescriptor->attributes()->setObject(VADesc, + ShaderAttrIndices[AttrName]); } + + MTL::VertexBufferLayoutDescriptor *LDesc = + MTL::VertexBufferLayoutDescriptor::alloc()->init(); + LDesc->setStride(VBDesc.getStride()); + LDesc->setStepRate(1); + LDesc->setStepFunction(MTL::VertexStepFunctionPerVertex); + IS.VertexDescriptor->layouts()->setObject(LDesc, 0); + return llvm::Error::success(); } @@ -440,12 +444,35 @@ class MTLDevice : public offloadtest::Device { IS.ArgBuffer->didModifyRange(NS::Range::Make(0, IS.ArgBuffer->length())); } if (P.isGraphics()) { - // Create and mark the vertex buffer as modified. - IS.VertexBuffer = Device->newBuffer( - P.Bindings.VertexBufferPtr->Data.back().get(), - P.Bindings.VertexBufferPtr->size(), MTL::ResourceStorageModeManaged); - IS.VertexBuffer->didModifyRange( - NS::Range::Make(0, IS.VertexBuffer->length())); + if (!P.Bindings.VertexBufferPtr) + return llvm::createStringError( + std::errc::invalid_argument, + "No vertex buffer specified for graphics pipeline."); + + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; + + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = + createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + + VertexBufferDesc VBDesc; + for (const auto &S : PVB.Streams) + VBDesc.Streams.push_back({S.Name, S.Fmt}); + + IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + + // TODO: Currently uses a single CpuToGpu mapped buffer. On discrete GPUs + // (DX/VK), consider using a staging buffer + copy to a GpuOnly vertex + // buffer for optimal GPU read performance. On Apple Silicon this is + // unnecessary due to unified memory. + auto *MTLBuf = static_cast(IS.VB->Data.get()); + const size_t BufSize = IS.VB->Data->getSizeInBytes(); + memcpy(MTLBuf->Buf->contents(), PVB.InterleavedData.get(), BufSize); + MTLBuf->Buf->didModifyRange(NS::Range::Make(0, BufSize)); } return llvm::Error::success(); } @@ -534,6 +561,7 @@ class MTLDevice : public offloadtest::Device { // Create a readback buffer for copying render target data to the CPU. BufferCreateDesc BufDesc = {}; BufDesc.Location = MemoryLocation::GpuToCpu; + BufDesc.Usage = BufferUsage::Storage; auto BufOrErr = createBuffer("RTReadback", BufDesc, OutBuf.size()); if (!BufOrErr) return BufOrErr.takeError(); @@ -624,10 +652,14 @@ class MTLDevice : public offloadtest::Device { // Bind vertex buffer at slot 0 to match the vertex descriptor which // references buffer index 0. - CmdEncoder->setVertexBuffer(IS.VertexBuffer, 0, 0); + if (!IS.VB) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex buffer not initialized."); + CmdEncoder->setVertexBuffer( + static_cast(IS.VB->Data.get())->Buf, 0, 0); CmdEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), - P.Bindings.getVertexCount()); + IS.VB->getVertexCount()); CmdEncoder->endEncoding(); diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index a20e8f447..6fe9a955e 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -10,6 +10,8 @@ //===----------------------------------------------------------------------===// #include "API/Device.h" +#include "API/FormatConversion.h" +#include "API/VertexBuffer.h" #include "Support/Pipeline.h" #include "VKResources.h" #include "llvm/ADT/DenseSet.h" @@ -626,7 +628,7 @@ class VulkanDevice : public offloadtest::Device { std::shared_ptr RenderTarget; std::shared_ptr RTReadback; std::shared_ptr DepthStencil; - std::optional VertexBuffer = std::nullopt; + std::optional VB = std::nullopt; VkRenderPass RenderPass = VK_NULL_HANDLE; uint32_t ShaderStageMask = 0; @@ -794,9 +796,17 @@ class VulkanDevice : public offloadtest::Device { VkBufferCreateInfo BufInfo = {}; BufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; BufInfo.size = SizeInBytes; - BufInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT; + BufInfo.usage = + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + switch (Desc.Usage) { + case BufferUsage::Storage: + BufInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + break; + case BufferUsage::VertexBuffer: + BufInfo.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + break; + } BufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VkBuffer DeviceBuffer; @@ -1234,6 +1244,7 @@ class VulkanDevice : public offloadtest::Device { // Create a host-visible staging buffer for readback. BufferCreateDesc BufDesc = {}; BufDesc.Location = MemoryLocation::GpuToCpu; + BufDesc.Usage = BufferUsage::Storage; auto BufOrErr = createBuffer("RTReadback", BufDesc, RTBuf.size()); if (!BufOrErr) return BufOrErr.takeError(); @@ -1268,34 +1279,36 @@ class VulkanDevice : public offloadtest::Device { if (auto Err = createDepthStencil(P, IS)) return Err; - if (P.Bindings.VertexBufferPtr == nullptr) + if (!P.Bindings.VertexBufferPtr) return llvm::createStringError( std::errc::invalid_argument, - "No Vertex buffer specified for graphics pipeline."); - const Resource VertexBuffer = {ResourceKind::StructuredBuffer, - "VertexBuffer", - {}, - {}, - P.Bindings.VertexBufferPtr, - nullptr, - false, - std::nullopt, - false}; - auto ExVHostBuf = createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - VertexBuffer.size(), VertexBuffer.BufferPtr->Data[0].get()); - if (!ExVHostBuf) - return ExVHostBuf.takeError(); - auto ExDeviceBuf = createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VertexBuffer.size()); - if (!ExDeviceBuf) - return ExDeviceBuf.takeError(); - VkBufferCopy Copy = {}; - Copy.size = VertexBuffer.size(); - vkCmdCopyBuffer(IS.CB->CmdBuffer, ExVHostBuf->Buffer, ExDeviceBuf->Buffer, - 1, &Copy); - IS.VertexBuffer = ResourceRef(*ExVHostBuf, *ExDeviceBuf); + "No vertex buffer specified for graphics pipeline."); + + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; + + BufferCreateDesc VBBufDesc = {}; + VBBufDesc.Location = MemoryLocation::CpuToGpu; + VBBufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = + createBuffer("VertexBuffer", VBBufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + + VertexBufferDesc VBDesc; + for (const auto &S : PVB.Streams) + VBDesc.Streams.push_back({S.Name, S.Fmt}); + + IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + + // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU + // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly + // vertex buffer instead. + auto *VKBuf = static_cast(IS.VB->Data.get()); + const size_t BufSize = IS.VB->Data->getSizeInBytes(); + void *Mapped = nullptr; + vkMapMemory(Device, VKBuf->Memory, 0, BufSize, 0, &Mapped); + memcpy(Mapped, PVB.InterleavedData.get(), BufSize); + vkUnmapMemory(Device, VKBuf->Memory); } return llvm::Error::success(); @@ -1961,21 +1974,25 @@ class VulkanDevice : public offloadtest::Device { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; MultisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - const uint32_t Stride = P.Bindings.getVertexStride(); + // Build vertex input state from the vertex buffer description. + if (!IS.VB) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex buffer not initialized."); + const VertexBufferDesc &VBDesc = IS.VB->Desc; VkVertexInputBindingDescription VertexInputBinding{}; VertexInputBinding.binding = 0; - VertexInputBinding.stride = Stride; + VertexInputBinding.stride = VBDesc.getStride(); VertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; llvm::SmallVector Attributes; - for (size_t I = 0; I < P.Bindings.VertexAttributes.size(); ++I) { - const VertexAttribute &VA = P.Bindings.VertexAttributes[I]; + for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { + const VertexStream &S = VBDesc.Streams[I]; VkVertexInputAttributeDescription VkVA = {}; VkVA.location = I; VkVA.binding = 0; - VkVA.format = getVKFormat(VA.Format, VA.Channels); - VkVA.offset = VA.Offset; + VkVA.format = getVulkanFormat(S.Fmt); + VkVA.offset = VBDesc.getOffset(I); Attributes.push_back(VkVA); } @@ -2352,13 +2369,16 @@ class VulkanDevice : public offloadtest::Device { llvm::outs() << "Dispatched compute shader: { " << DispatchSize[0] << ", " << DispatchSize[1] << ", " << DispatchSize[2] << " }\n"; } else { + if (!IS.VB) + return llvm::createStringError(std::errc::invalid_argument, + "Vertex buffer not initialized."); VkDeviceSize Offsets[1]{0}; - assert(IS.VertexBuffer.has_value()); - vkCmdBindVertexBuffers(IS.CB->CmdBuffer, 0, 1, - &IS.VertexBuffer->Device.Buffer, Offsets); + VkBuffer VBHandle = + static_cast(IS.VB->Data.get())->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.Bindings.getVertexCount(), 1, 0, 0); - llvm::outs() << "Drew " << P.Bindings.getVertexCount() << " vertices.\n"; + vkCmdDraw(IS.CB->CmdBuffer, IS.VB->getVertexCount(), 1, 0, 0); + llvm::outs() << "Drew " << IS.VB->getVertexCount() << " vertices.\n"; vkCmdEndRenderPass(IS.CB->CmdBuffer); copyTextureToReadback(IS.CB->CmdBuffer, *IS.RenderTarget, *IS.RTReadback, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, @@ -2468,12 +2488,9 @@ class VulkanDevice : public offloadtest::Device { } if (IS.getFullShaderStageMask() != VK_SHADER_STAGE_COMPUTE_BIT) { - if (IS.VertexBuffer.has_value()) { - vkDestroyBuffer(Device, IS.VertexBuffer->Device.Buffer, nullptr); - vkFreeMemory(Device, IS.VertexBuffer->Device.Memory, nullptr); - vkDestroyBuffer(Device, IS.VertexBuffer->Host.Buffer, nullptr); - vkFreeMemory(Device, IS.VertexBuffer->Host.Memory, nullptr); - } + // Vertex buffer, render target, readback buffer, and depth stencil are + // owned by shared_ptrs (IS.VB, IS.RenderTarget, IS.RTReadback, + // IS.DepthStencil). vkDestroyFramebuffer(Device, IS.FrameBuffer, nullptr); vkDestroyRenderPass(Device, IS.RenderPass, nullptr); } diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index faa546336..895e525eb 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -122,10 +122,12 @@ void MappingTraits::mapping(IO &I, std::to_string(DescriptorTableCount)); if (!P.Bindings.VertexBuffer.empty()) { - P.Bindings.VertexBufferPtr = P.getBuffer(P.Bindings.VertexBuffer); + P.Bindings.VertexBufferPtr = + P.getVertexBuffer(P.Bindings.VertexBuffer); if (!P.Bindings.VertexBufferPtr) I.setError(Twine("Referenced vertex buffer ") + - P.Bindings.VertexBuffer + " not found!"); + P.Bindings.VertexBuffer + + " not found in VertexBuffers!"); } if (!P.Bindings.RenderTarget.empty()) { @@ -391,14 +393,6 @@ void MappingTraits::mapping( I.mapOptional("CounterBinding", B.CounterBinding); } -void MappingTraits::mapping( - IO &I, offloadtest::VertexAttribute &A) { - I.mapRequired("Format", A.Format); - I.mapRequired("Channels", A.Channels); - I.mapRequired("Offset", A.Offset); - I.mapRequired("Name", A.Name); -} - void MappingTraits::mapping( IO &I, offloadtest::VertexStreamData &S) { I.mapRequired("Name", S.Name); @@ -519,7 +513,6 @@ void MappingTraits::mapping( void MappingTraits::mapping( IO &I, offloadtest::IOBindings &B) { I.mapOptional("VertexBuffer", B.VertexBuffer); - I.mapOptional("VertexAttributes", B.VertexAttributes); I.mapOptional("RenderTarget", B.RenderTarget); } diff --git a/test/Feature/Semantics/ArraySemantics.test b/test/Feature/Semantics/ArraySemantics.test index afa4ba368..9b3a645c6 100644 --- a/test/Feature/Semantics/ArraySemantics.test +++ b/test/Feature/Semantics/ArraySemantics.test @@ -35,13 +35,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -52,11 +54,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: [] ... diff --git a/test/Feature/Semantics/MatrixSemantics.test b/test/Feature/Semantics/MatrixSemantics.test index 743e7fcea..69ffe2660 100644 --- a/test/Feature/Semantics/MatrixSemantics.test +++ b/test/Feature/Semantics/MatrixSemantics.test @@ -49,13 +49,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -73,11 +75,6 @@ Buffers: Data: [ 1, 2, 3, 4, 5, 6, 7 ] Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: - Resources: diff --git a/test/Feature/Semantics/NestedStructSemantics.test b/test/Feature/Semantics/NestedStructSemantics.test index e2f7aadda..882cc072e 100644 --- a/test/Feature/Semantics/NestedStructSemantics.test +++ b/test/Feature/Semantics/NestedStructSemantics.test @@ -52,13 +52,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -76,11 +78,6 @@ Buffers: Data: [ 1, 2, 3 ] Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: - Resources: diff --git a/test/Feature/Semantics/SemanticTypes.test b/test/Feature/Semantics/SemanticTypes.test index c4842702a..73e0cea1c 100644 --- a/test/Feature/Semantics/SemanticTypes.test +++ b/test/Feature/Semantics/SemanticTypes.test @@ -50,13 +50,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -78,11 +80,6 @@ Buffers: Data: [ 0xffffffff, 0x2, 0x40600000 ] Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: - Resources: diff --git a/test/Feature/Semantics/ShadowedSemantics.test b/test/Feature/Semantics/ShadowedSemantics.test index cf5a7153e..9b359b21f 100644 --- a/test/Feature/Semantics/ShadowedSemantics.test +++ b/test/Feature/Semantics/ShadowedSemantics.test @@ -57,13 +57,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -81,11 +83,6 @@ Buffers: Data: [ 1, 2, 3 ] Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: - Resources: diff --git a/test/Feature/Semantics/UserSemantics.test b/test/Feature/Semantics/UserSemantics.test index 5adea4b4b..8c51aa25d 100644 --- a/test/Feature/Semantics/UserSemantics.test +++ b/test/Feature/Semantics/UserSemantics.test @@ -32,13 +32,15 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 16 - Data: [ 0.0, 3.0, 0.0, 1.0, - 3.0, -3.0, 0.0, 1.0, - -3.0, -3.0, 0.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGBA32Float + Data: [ 0.0, 3.0, 0.0, 1.0, + 3.0, -3.0, 0.0, 1.0, + -3.0, -3.0, 0.0, 1.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -49,11 +51,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 4 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: [] ... diff --git a/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml b/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml index 74d3302fc..de748f99a 100644 --- a/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml +++ b/test/Feature/Textures/Texture2D.CalculateLevelOfDetail.test.yaml @@ -57,6 +57,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Float32 @@ -80,20 +89,8 @@ Buffers: 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: diff --git a/test/Feature/Textures/Texture2D.Sample.test.yaml b/test/Feature/Textures/Texture2D.Sample.test.yaml index 34ba1bff0..ba0daf177 100644 --- a/test/Feature/Textures/Texture2D.Sample.test.yaml +++ b/test/Feature/Textures/Texture2D.Sample.test.yaml @@ -73,6 +73,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Float32 @@ -105,20 +114,8 @@ Buffers: 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: diff --git a/test/Feature/Textures/Texture2D.SampleBias.test.yaml b/test/Feature/Textures/Texture2D.SampleBias.test.yaml index ff06f8348..49f541a74 100644 --- a/test/Feature/Textures/Texture2D.SampleBias.test.yaml +++ b/test/Feature/Textures/Texture2D.SampleBias.test.yaml @@ -64,6 +64,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Float32 @@ -96,20 +105,8 @@ Buffers: 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: diff --git a/test/Feature/Textures/Texture2D.SampleCmp.test.yaml b/test/Feature/Textures/Texture2D.SampleCmp.test.yaml index 52a06c512..78ba427f6 100644 --- a/test/Feature/Textures/Texture2D.SampleCmp.test.yaml +++ b/test/Feature/Textures/Texture2D.SampleCmp.test.yaml @@ -105,6 +105,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Depth32 @@ -134,20 +143,8 @@ Buffers: 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: diff --git a/test/Feature/Textures/Texture2D.Sampler.address.test.yaml b/test/Feature/Textures/Texture2D.Sampler.address.test.yaml index 844b8f5e7..36cf51398 100644 --- a/test/Feature/Textures/Texture2D.Sampler.address.test.yaml +++ b/test/Feature/Textures/Texture2D.Sampler.address.test.yaml @@ -62,6 +62,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Float32 @@ -89,20 +98,8 @@ Buffers: 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: diff --git a/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml b/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml index 5dfd8255b..324a9d90c 100644 --- a/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml +++ b/test/Feature/Textures/Texture2D.Sampler.filter.test.yaml @@ -49,6 +49,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: Tex Format: Float32 @@ -76,20 +85,8 @@ Buffers: 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: diff --git a/test/Graphics/SimpleTriangle.test b/test/Graphics/SimpleTriangle.test index 284ca3bd4..e4cb4e4c9 100644 --- a/test/Graphics/SimpleTriangle.test +++ b/test/Graphics/SimpleTriangle.test @@ -34,13 +34,20 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 28 # 32 bytes per vertex - Data: [ 0.0, 0.25, 0.0, 1.0, 0.0, 0.0, 1.0, - 0.25, -0.25, 0.0, 0.0, 1.0, 0.0, 1.0, - -0.25, -0.25, 0.0, 0.0, 0.0, 1.0, 1.0 ] + Streams: + - Name: POSITION + Format: RGB32Float + Data: [ 0.0, 0.25, 0.0, + 0.25, -0.25, 0.0, + -0.25, -0.25, 0.0 ] + - Name: COLOR + Format: RGBA32Float + 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 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -51,15 +58,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 3 - Offset: 0 - Name: POSITION - - Format: Float32 - Channels: 4 - Offset: 12 - Name: COLOR RenderTarget: Output DescriptorSets: [] ... diff --git a/test/Graphics/ddx_fine.test b/test/Graphics/ddx_fine.test index 18e5e2344..6e55cb34a 100644 --- a/test/Graphics/ddx_fine.test +++ b/test/Graphics/ddx_fine.test @@ -46,16 +46,18 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 12 # 16 bytes per vertex - Data: [ -1.0, -1.0, 0.0, - -1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, -1.0, 0.0, - -1.0, -1.0, 0.0 ] + Streams: + - Name: POSITION + Format: RGB32Float + Data: [ -1.0, -1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -66,11 +68,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 3 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: [] ... diff --git a/test/Graphics/ddy_fine.test b/test/Graphics/ddy_fine.test index 372fde968..56bac257e 100644 --- a/test/Graphics/ddy_fine.test +++ b/test/Graphics/ddy_fine.test @@ -46,16 +46,18 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 12 # 16 bytes per vertex - Data: [ -1.0, -1.0, 0.0, - -1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, -1.0, 0.0, - -1.0, -1.0, 0.0 ] + Streams: + - Name: POSITION + Format: RGB32Float + Data: [ -1.0, -1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -66,11 +68,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 3 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: [] ... diff --git a/test/Graphics/fwidth.test b/test/Graphics/fwidth.test index 74670633a..1f7606ad1 100644 --- a/test/Graphics/fwidth.test +++ b/test/Graphics/fwidth.test @@ -45,16 +45,18 @@ Shaders: Entry: main - Stage: Pixel Entry: main -Buffers: +VertexBuffers: - Name: VertexData - Format: Float32 - Stride: 12 # 16 bytes per vertex - Data: [ -1.0, -1.0, 0.0, - -1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, -1.0, 0.0, - -1.0, -1.0, 0.0 ] + Streams: + - Name: POSITION + Format: RGB32Float + Data: [ -1.0, -1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0 ] +Buffers: - Name: Output Format: Float32 Channels: 4 @@ -65,11 +67,6 @@ Buffers: Depth: 1 Bindings: VertexBuffer: VertexData - VertexAttributes: - - Format: Float32 - Channels: 3 - Offset: 0 - Name: POSITION RenderTarget: Output DescriptorSets: [] ... From 414ca2a6d84318bcce899fd7ad91109c5966a83f Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Thu, 2 Apr 2026 10:33:34 +0200 Subject: [PATCH 04/11] rebase fixups --- include/API/VertexBuffer.h | 67 ++++++++++++++++++++++++++++++++++++++ lib/API/VK/Device.cpp | 3 -- 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 include/API/VertexBuffer.h diff --git a/include/API/VertexBuffer.h b/include/API/VertexBuffer.h new file mode 100644 index 000000000..ee9968536 --- /dev/null +++ b/include/API/VertexBuffer.h @@ -0,0 +1,67 @@ +//===- VertexBuffer.h - Offload API Vertex Buffer -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#ifndef OFFLOADTEST_API_VERTEXBUFFER_H +#define OFFLOADTEST_API_VERTEXBUFFER_H + +#include "API/Buffer.h" +#include "API/Resources.h" + +#include "llvm/ADT/SmallVector.h" + +#include +#include +#include +#include + +namespace offloadtest { + +struct VertexStream { + std::string Name; // Semantic name (e.g. POSITION, COLOR). + Format Fmt; +}; + +struct VertexBufferDesc { + llvm::SmallVector Streams; + + // Returns the stride in bytes (sum of all stream format sizes). + uint32_t getStride() const { + uint32_t Stride = 0; + for (const auto &S : Streams) + Stride += getFormatSize(S.Fmt); + return Stride; + } + + // Returns the byte offset of the stream at the given index. + uint32_t getOffset(uint32_t Index) const { + assert(Index < Streams.size() && "Stream index out of bounds"); + uint32_t Offset = 0; + for (uint32_t I = 0; I < Index; ++I) + Offset += getFormatSize(Streams[I].Fmt); + return Offset; + } +}; + +struct VertexBuffer { + VertexBufferDesc Desc; + std::shared_ptr Data; + + uint32_t getVertexCount() const { + uint32_t Stride = Desc.getStride(); + if (Stride == 0) + return 0; + return static_cast(Data->getSizeInBytes()) / Stride; + } +}; + +} // namespace offloadtest + +#endif // OFFLOADTEST_API_VERTEXBUFFER_H diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index 6fe9a955e..6fb6acf96 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -2488,9 +2488,6 @@ class VulkanDevice : public offloadtest::Device { } if (IS.getFullShaderStageMask() != VK_SHADER_STAGE_COMPUTE_BIT) { - // Vertex buffer, render target, readback buffer, and depth stencil are - // owned by shared_ptrs (IS.VB, IS.RenderTarget, IS.RTReadback, - // IS.DepthStencil). vkDestroyFramebuffer(Device, IS.FrameBuffer, nullptr); vkDestroyRenderPass(Device, IS.RenderPass, nullptr); } From b0c9dbcadeba90c2645a934c90b54c21d35f5781 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 7 Apr 2026 10:43:23 +0200 Subject: [PATCH 05/11] fixup vertex buffer definition in new sampletexture2d test --- .../Vk.SampledTexture2D.Sample.test.yaml | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sample.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sample.test.yaml index a82cd9396..cdbecc732 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sample.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sample.test.yaml @@ -72,6 +72,15 @@ Shaders: - Stage: Pixel Entry: mainPS +VertexBuffers: + - Name: VB + Streams: + - Name: POSITION + Format: RGB32Float + Data: [-1.0, -1.0, 0.0, + 3.0, -1.0, 0.0, + -1.0, 3.0, 0.0] + Buffers: - Name: SampledTex0 Format: Float32 @@ -114,20 +123,8 @@ Buffers: 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: From 9f945b0601dcb5e05939c675c39822be9abc5bad Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Mon, 13 Apr 2026 15:28:45 +0200 Subject: [PATCH 06/11] migrate new tests to new VB yaml definition --- ...edTexture2D.CalculateLevelOfDetail.test.yaml | 17 +++++++---------- .../Vk.SampledTexture2D.SampleBias.test.yaml | 17 +++++++---------- .../Vk.SampledTexture2D.SampleCmp.test.yaml | 17 +++++++---------- ...k.SampledTexture2D.Sampler.address.test.yaml | 17 +++++++---------- ...Vk.SampledTexture2D.Sampler.filter.test.yaml | 17 +++++++---------- 5 files changed, 35 insertions(+), 50 deletions(-) diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.CalculateLevelOfDetail.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.CalculateLevelOfDetail.test.yaml index 9ef2900d7..93765d8ee 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.CalculateLevelOfDetail.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.CalculateLevelOfDetail.test.yaml @@ -77,20 +77,17 @@ Buffers: FillSize: 64 OutputProps: { Width: 2, Height: 2, Depth: 1 } +VertexBuffers: - 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] + Streams: + - Name: POSITION + Format: RGB32Float + 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: diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleBias.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleBias.test.yaml index 66cd2ead4..1b7464309 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleBias.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleBias.test.yaml @@ -105,20 +105,17 @@ Buffers: FillSize: 64 OutputProps: { Width: 2, Height: 2, Depth: 1 } +VertexBuffers: - 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] + Streams: + - Name: POSITION + Format: RGB32Float + 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: diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleCmp.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleCmp.test.yaml index 7d9a605bc..cddb23424 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleCmp.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.SampleCmp.test.yaml @@ -124,20 +124,17 @@ Buffers: FillSize: 16 OutputProps: { Width: 1, Height: 1, Depth: 1 } +VertexBuffers: - 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] + Streams: + - Name: POSITION + Format: RGB32Float + 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: diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.address.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.address.test.yaml index 7e20e24b9..a3e473887 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.address.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.address.test.yaml @@ -108,20 +108,17 @@ Buffers: FillSize: 16 OutputProps: { Width: 1, Height: 1, Depth: 1 } +VertexBuffers: - 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] + Streams: + - Name: POSITION + Format: RGB32Float + 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: diff --git a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.filter.test.yaml b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.filter.test.yaml index 21a8b9b47..d20069f8f 100644 --- a/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.filter.test.yaml +++ b/test/Feature/Vk.SampledTextures/Vk.SampledTexture2D/Vk.SampledTexture2D.Sampler.filter.test.yaml @@ -82,20 +82,17 @@ Buffers: FillSize: 16 OutputProps: { Width: 1, Height: 1, Depth: 1 } +VertexBuffers: - 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] + Streams: + - Name: POSITION + Format: RGB32Float + 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: From 216dabd2939ccfeffbb3746b1f6019bb3fb56895 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Mon, 13 Apr 2026 16:07:48 +0200 Subject: [PATCH 07/11] createVertexBuffer in api-agnostic device --- include/API/Device.h | 5 +++++ include/API/VertexBuffer.h | 1 - include/Support/Pipeline.h | 2 +- lib/API/DX/Device.cpp | 17 ++++------------- lib/API/Device.cpp | 21 +++++++++++++++++++++ lib/API/MTL/MTLDevice.cpp | 18 ++++-------------- lib/API/VK/Device.cpp | 18 ++++-------------- 7 files changed, 39 insertions(+), 43 deletions(-) diff --git a/include/API/Device.h b/include/API/Device.h index fc444f6a8..5a10c1c3c 100644 --- a/include/API/Device.h +++ b/include/API/Device.h @@ -19,6 +19,7 @@ #include "API/Capabilities.h" #include "API/CommandBuffer.h" #include "API/Texture.h" +#include "API/VertexBuffer.h" #include "Support/Pipeline.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" @@ -118,6 +119,10 @@ createRenderTargetFromCPUBuffer(Device &Dev, const CPUBuffer &Buf); llvm::Expected> createDefaultDepthStencilTarget(Device &Dev, uint32_t Width, uint32_t Height); +// Creates a VertexBuffer from a ParsedVertexBuffer. +llvm::Expected +createVertexBuffer(Device &Dev, const ParsedVertexBuffer &PVB); + } // namespace offloadtest #endif // OFFLOADTEST_API_DEVICE_H diff --git a/include/API/VertexBuffer.h b/include/API/VertexBuffer.h index ee9968536..98f851415 100644 --- a/include/API/VertexBuffer.h +++ b/include/API/VertexBuffer.h @@ -32,7 +32,6 @@ struct VertexStream { struct VertexBufferDesc { llvm::SmallVector Streams; - // Returns the stride in bytes (sum of all stream format sizes). uint32_t getStride() const { uint32_t Stride = 0; for (const auto &S : Streams) diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index 998f32055..e30fe69f0 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -351,7 +351,7 @@ struct RuntimeSettings { struct VertexStreamData { std::string Name; // Semantic name (e.g. POSITION, COLOR). Format Fmt; - llvm::SmallVector Values; // One value per component. + llvm::SmallVector Values; }; // Parsed vertex buffer from the YAML VertexBuffers section. The parser diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index 32e83f62c..aa0aa7863 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -1615,19 +1615,10 @@ class DXDevice : public offloadtest::Device { "No vertex buffer bound for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - - BufferCreateDesc BufDesc = {}; - BufDesc.Location = MemoryLocation::CpuToGpu; - BufDesc.Usage = BufferUsage::VertexBuffer; - auto BufOrErr = createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); - if (!BufOrErr) - return BufOrErr.takeError(); - - VertexBufferDesc VBDesc; - for (const auto &S : PVB.Streams) - VBDesc.Streams.push_back({S.Name, S.Fmt}); - - IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); + if (!VBOrErr) + return VBOrErr.takeError(); + IS.VB = std::move(*VBOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly diff --git a/lib/API/Device.cpp b/lib/API/Device.cpp index ec19c620b..1e7c5ea62 100644 --- a/lib/API/Device.cpp +++ b/lib/API/Device.cpp @@ -11,6 +11,7 @@ #include "API/Device.h" #include "API/FormatConversion.h" +#include "API/VertexBuffer.h" #include "Config.h" @@ -87,6 +88,26 @@ offloadtest::createRenderTargetFromCPUBuffer(Device &Dev, return Dev.createTexture("RenderTarget", Desc); } +llvm::Expected +offloadtest::createVertexBuffer(Device &Dev, const ParsedVertexBuffer &PVB) { + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = + Dev.createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + + VertexBufferDesc VBDesc; + for (const auto &S : PVB.Streams) + VBDesc.Streams.push_back({S.Name, S.Fmt}); + + // TODO: Generalize VB data copy so that we can deduplicate that from each + // backend. + + return VertexBuffer{VBDesc, *BufOrErr}; +} + llvm::Expected> offloadtest::createDefaultDepthStencilTarget(Device &Dev, uint32_t Width, uint32_t Height) { diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index 8a563ef0b..4969cf664 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -450,20 +450,10 @@ class MTLDevice : public offloadtest::Device { "No vertex buffer specified for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - - BufferCreateDesc BufDesc = {}; - BufDesc.Location = MemoryLocation::CpuToGpu; - BufDesc.Usage = BufferUsage::VertexBuffer; - auto BufOrErr = - createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); - if (!BufOrErr) - return BufOrErr.takeError(); - - VertexBufferDesc VBDesc; - for (const auto &S : PVB.Streams) - VBDesc.Streams.push_back({S.Name, S.Fmt}); - - IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); + if (!VBOrErr) + return VBOrErr.takeError(); + IS.VB = std::move(*VBOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. On discrete GPUs // (DX/VK), consider using a staging buffer + copy to a GpuOnly vertex diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index 6fb6acf96..9fae13e02 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -1285,20 +1285,10 @@ class VulkanDevice : public offloadtest::Device { "No vertex buffer specified for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - - BufferCreateDesc VBBufDesc = {}; - VBBufDesc.Location = MemoryLocation::CpuToGpu; - VBBufDesc.Usage = BufferUsage::VertexBuffer; - auto BufOrErr = - createBuffer("VertexBuffer", VBBufDesc, PVB.InterleavedSize); - if (!BufOrErr) - return BufOrErr.takeError(); - - VertexBufferDesc VBDesc; - for (const auto &S : PVB.Streams) - VBDesc.Streams.push_back({S.Name, S.Fmt}); - - IS.VB = offloadtest::VertexBuffer{VBDesc, *BufOrErr}; + auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); + if (!VBOrErr) + return VBOrErr.takeError(); + IS.VB = std::move(*VBOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly From 1b8dcc337419f40c553d70ea1b9660c327e1b115 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 14 Apr 2026 14:57:46 +0200 Subject: [PATCH 08/11] rebase fixup: `getFormatSize` -> `getFormatSizeInBytes` rename --- include/API/VertexBuffer.h | 4 ++-- include/Support/Pipeline.h | 2 +- lib/Support/Pipeline.cpp | 11 ++++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/API/VertexBuffer.h b/include/API/VertexBuffer.h index 98f851415..b54b604d1 100644 --- a/include/API/VertexBuffer.h +++ b/include/API/VertexBuffer.h @@ -35,7 +35,7 @@ struct VertexBufferDesc { uint32_t getStride() const { uint32_t Stride = 0; for (const auto &S : Streams) - Stride += getFormatSize(S.Fmt); + Stride += getFormatSizeInBytes(S.Fmt); return Stride; } @@ -44,7 +44,7 @@ struct VertexBufferDesc { assert(Index < Streams.size() && "Stream index out of bounds"); uint32_t Offset = 0; for (uint32_t I = 0; I < Index; ++I) - Offset += getFormatSize(Streams[I].Fmt); + Offset += getFormatSizeInBytes(Streams[I].Fmt); return Offset; } }; diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index e30fe69f0..f9e41c1ad 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -368,7 +368,7 @@ struct ParsedVertexBuffer { uint32_t getStride() const { uint32_t Stride = 0; for (const auto &S : Streams) - Stride += getFormatSize(S.Fmt); + Stride += getFormatSizeInBytes(S.Fmt); return Stride; } diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index 895e525eb..a5b99a505 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -122,12 +122,10 @@ void MappingTraits::mapping(IO &I, std::to_string(DescriptorTableCount)); if (!P.Bindings.VertexBuffer.empty()) { - P.Bindings.VertexBufferPtr = - P.getVertexBuffer(P.Bindings.VertexBuffer); + P.Bindings.VertexBufferPtr = P.getVertexBuffer(P.Bindings.VertexBuffer); if (!P.Bindings.VertexBufferPtr) I.setError(Twine("Referenced vertex buffer ") + - P.Bindings.VertexBuffer + - " not found in VertexBuffers!"); + P.Bindings.VertexBuffer + " not found in VertexBuffers!"); } if (!P.Bindings.RenderTarget.empty()) { @@ -497,14 +495,13 @@ void MappingTraits::mapping( for (uint32_t V = 0; V < VertexCount; ++V) { uint32_t StreamOffset = 0; for (const auto &Stream : VB.Streams) { - const uint32_t Components = - offloadtest::getComponentCount(Stream.Fmt); + const uint32_t Components = offloadtest::getComponentCount(Stream.Fmt); char *Dst = VB.InterleavedData.get() + V * Stride + StreamOffset; for (uint32_t C = 0; C < Components; ++C) { double Value = Stream.Values[V * Components + C]; Dst += writeComponent(Dst, Value, Stream.Fmt); } - StreamOffset += offloadtest::getFormatSize(Stream.Fmt); + StreamOffset += offloadtest::getFormatSizeInBytes(Stream.Fmt); } } } From 4218dbf444f2ac922d180ba5ace152b0281f7d78 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Tue, 14 Apr 2026 15:37:42 +0200 Subject: [PATCH 09/11] fmt --- include/API/Device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/API/Device.h b/include/API/Device.h index 5a10c1c3c..340c6c37d 100644 --- a/include/API/Device.h +++ b/include/API/Device.h @@ -120,8 +120,8 @@ llvm::Expected> createDefaultDepthStencilTarget(Device &Dev, uint32_t Width, uint32_t Height); // Creates a VertexBuffer from a ParsedVertexBuffer. -llvm::Expected -createVertexBuffer(Device &Dev, const ParsedVertexBuffer &PVB); +llvm::Expected createVertexBuffer(Device &Dev, + const ParsedVertexBuffer &PVB); } // namespace offloadtest From 796648a3562ee3a14943430640dda5175c2c8186 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Thu, 16 Apr 2026 09:56:52 +0200 Subject: [PATCH 10/11] const --- lib/Support/Pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index a5b99a505..e22ebd093 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -498,7 +498,7 @@ void MappingTraits::mapping( const uint32_t Components = offloadtest::getComponentCount(Stream.Fmt); char *Dst = VB.InterleavedData.get() + V * Stride + StreamOffset; for (uint32_t C = 0; C < Components; ++C) { - double Value = Stream.Values[V * Components + C]; + const double Value = Stream.Values[V * Components + C]; Dst += writeComponent(Dst, Value, Stream.Fmt); } StreamOffset += offloadtest::getFormatSizeInBytes(Stream.Fmt); From c575acad5dc5ef947a5c883dbbba5457ab981568 Mon Sep 17 00:00:00 2001 From: EmilioLaiso Date: Thu, 16 Apr 2026 10:29:57 +0200 Subject: [PATCH 11/11] remove VertexBuffer abstraction. Rely on ParsedVertexBuffer for input layout and buffer metadata --- include/API/Device.h | 5 --- include/API/VertexBuffer.h | 66 -------------------------------------- include/Support/Pipeline.h | 11 +++++++ lib/API/DX/Device.cpp | 41 ++++++++++++----------- lib/API/Device.cpp | 21 ------------ lib/API/MTL/MTLDevice.cpp | 40 ++++++++++++----------- lib/API/VK/Device.cpp | 43 +++++++++++++------------ 7 files changed, 77 insertions(+), 150 deletions(-) delete mode 100644 include/API/VertexBuffer.h diff --git a/include/API/Device.h b/include/API/Device.h index 340c6c37d..fc444f6a8 100644 --- a/include/API/Device.h +++ b/include/API/Device.h @@ -19,7 +19,6 @@ #include "API/Capabilities.h" #include "API/CommandBuffer.h" #include "API/Texture.h" -#include "API/VertexBuffer.h" #include "Support/Pipeline.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" @@ -119,10 +118,6 @@ createRenderTargetFromCPUBuffer(Device &Dev, const CPUBuffer &Buf); llvm::Expected> createDefaultDepthStencilTarget(Device &Dev, uint32_t Width, uint32_t Height); -// Creates a VertexBuffer from a ParsedVertexBuffer. -llvm::Expected createVertexBuffer(Device &Dev, - const ParsedVertexBuffer &PVB); - } // namespace offloadtest #endif // OFFLOADTEST_API_DEVICE_H diff --git a/include/API/VertexBuffer.h b/include/API/VertexBuffer.h deleted file mode 100644 index b54b604d1..000000000 --- a/include/API/VertexBuffer.h +++ /dev/null @@ -1,66 +0,0 @@ -//===- VertexBuffer.h - Offload API Vertex Buffer -------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// -//===----------------------------------------------------------------------===// - -#ifndef OFFLOADTEST_API_VERTEXBUFFER_H -#define OFFLOADTEST_API_VERTEXBUFFER_H - -#include "API/Buffer.h" -#include "API/Resources.h" - -#include "llvm/ADT/SmallVector.h" - -#include -#include -#include -#include - -namespace offloadtest { - -struct VertexStream { - std::string Name; // Semantic name (e.g. POSITION, COLOR). - Format Fmt; -}; - -struct VertexBufferDesc { - llvm::SmallVector Streams; - - uint32_t getStride() const { - uint32_t Stride = 0; - for (const auto &S : Streams) - Stride += getFormatSizeInBytes(S.Fmt); - return Stride; - } - - // Returns the byte offset of the stream at the given index. - uint32_t getOffset(uint32_t Index) const { - assert(Index < Streams.size() && "Stream index out of bounds"); - uint32_t Offset = 0; - for (uint32_t I = 0; I < Index; ++I) - Offset += getFormatSizeInBytes(Streams[I].Fmt); - return Offset; - } -}; - -struct VertexBuffer { - VertexBufferDesc Desc; - std::shared_ptr Data; - - uint32_t getVertexCount() const { - uint32_t Stride = Desc.getStride(); - if (Stride == 0) - return 0; - return static_cast(Data->getSizeInBytes()) / Stride; - } -}; - -} // namespace offloadtest - -#endif // OFFLOADTEST_API_VERTEXBUFFER_H diff --git a/include/Support/Pipeline.h b/include/Support/Pipeline.h index f9e41c1ad..81966a865 100644 --- a/include/Support/Pipeline.h +++ b/include/Support/Pipeline.h @@ -16,9 +16,11 @@ #include "API/Resources.h" #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/YAMLTraits.h" +#include #include #include #include @@ -372,6 +374,15 @@ struct ParsedVertexBuffer { return Stride; } + // Returns the byte offset of the stream at the given index. + uint32_t getOffset(uint32_t Index) const { + assert(Index < Streams.size() && "Stream index out of bounds"); + uint32_t Offset = 0; + for (uint32_t I = 0; I < Index; ++I) + Offset += getFormatSizeInBytes(Streams[I].Fmt); + return Offset; + } + uint32_t getVertexCount() const { uint32_t Stride = getStride(); if (Stride == 0) diff --git a/lib/API/DX/Device.cpp b/lib/API/DX/Device.cpp index aa0aa7863..ef8e0600e 100644 --- a/lib/API/DX/Device.cpp +++ b/lib/API/DX/Device.cpp @@ -34,7 +34,6 @@ #include "API/Capabilities.h" #include "API/Device.h" #include "API/FormatConversion.h" -#include "API/VertexBuffer.h" #include "DXFeatures.h" #include "Support/Pipeline.h" #include "Support/WinError.h" @@ -486,7 +485,7 @@ class DXDevice : public offloadtest::Device { std::shared_ptr RT; std::shared_ptr RTReadback; std::shared_ptr DS; - std::optional VB; + std::shared_ptr VB; llvm::SmallVector DescTables; llvm::SmallVector RootResources; @@ -1615,26 +1614,29 @@ class DXDevice : public offloadtest::Device { "No vertex buffer bound for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); - if (!VBOrErr) - return VBOrErr.takeError(); - IS.VB = std::move(*VBOrErr); + + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + IS.VB = std::static_pointer_cast(*BufOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly // vertex buffer instead. - auto *DXBuf = static_cast(IS.VB->Data.get()); void *Ptr = nullptr; - if (auto Err = HR::toError(DXBuf->Buffer->Map(0, nullptr, &Ptr), + if (auto Err = HR::toError(IS.VB->Buffer->Map(0, nullptr, &Ptr), "Failed to map vertex buffer")) return Err; - memcpy(Ptr, PVB.InterleavedData.get(), IS.VB->Data->getSizeInBytes()); - DXBuf->Buffer->Unmap(0, nullptr); + memcpy(Ptr, PVB.InterleavedData.get(), IS.VB->getSizeInBytes()); + IS.VB->Buffer->Unmap(0, nullptr); D3D12_VERTEX_BUFFER_VIEW VBView = {}; - VBView.BufferLocation = DXBuf->Buffer->GetGPUVirtualAddress(); - VBView.SizeInBytes = static_cast(IS.VB->Data->getSizeInBytes()); - VBView.StrideInBytes = IS.VB->Desc.getStride(); + VBView.BufferLocation = IS.VB->Buffer->GetGPUVirtualAddress(); + VBView.SizeInBytes = static_cast(IS.VB->getSizeInBytes()); + VBView.StrideInBytes = PVB.getStride(); IS.CB->CmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); IS.CB->CmdList->IASetVertexBuffers(0, 1, &VBView); @@ -1646,13 +1648,13 @@ class DXDevice : public offloadtest::Device { if (!IS.VB) return llvm::createStringError(std::errc::invalid_argument, "Vertex buffer not initialized."); - // Create the input layout from the vertex buffer description. + // Create the input layout from the parsed vertex buffer streams. + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; std::vector InputLayout; - const VertexBufferDesc &VBDesc = IS.VB->Desc; - for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { - const VertexStream &S = VBDesc.Streams[I]; + for (uint32_t I = 0; I < PVB.Streams.size(); ++I) { + const VertexStreamData &S = PVB.Streams[I]; InputLayout.push_back({S.Name.c_str(), 0, getDXGIFormat(S.Fmt), 0, - VBDesc.getOffset(I), + PVB.getOffset(I), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}); } @@ -1745,7 +1747,8 @@ class DXDevice : public offloadtest::Device { static_cast(VP.Height)}; IS.CB->CmdList->RSSetScissorRects(1, &Scissor); - IS.CB->CmdList->DrawInstanced(IS.VB->getVertexCount(), 1, 0, 0); + IS.CB->CmdList->DrawInstanced(P.Bindings.VertexBufferPtr->getVertexCount(), + 1, 0, 0); // Transition the render target to copy source and copy to the readback // buffer. diff --git a/lib/API/Device.cpp b/lib/API/Device.cpp index 1e7c5ea62..ec19c620b 100644 --- a/lib/API/Device.cpp +++ b/lib/API/Device.cpp @@ -11,7 +11,6 @@ #include "API/Device.h" #include "API/FormatConversion.h" -#include "API/VertexBuffer.h" #include "Config.h" @@ -88,26 +87,6 @@ offloadtest::createRenderTargetFromCPUBuffer(Device &Dev, return Dev.createTexture("RenderTarget", Desc); } -llvm::Expected -offloadtest::createVertexBuffer(Device &Dev, const ParsedVertexBuffer &PVB) { - BufferCreateDesc BufDesc = {}; - BufDesc.Location = MemoryLocation::CpuToGpu; - BufDesc.Usage = BufferUsage::VertexBuffer; - auto BufOrErr = - Dev.createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); - if (!BufOrErr) - return BufOrErr.takeError(); - - VertexBufferDesc VBDesc; - for (const auto &S : PVB.Streams) - VBDesc.Streams.push_back({S.Name, S.Fmt}); - - // TODO: Generalize VB data copy so that we can deduplicate that from each - // backend. - - return VertexBuffer{VBDesc, *BufOrErr}; -} - llvm::Expected> offloadtest::createDefaultDepthStencilTarget(Device &Dev, uint32_t Width, uint32_t Height) { diff --git a/lib/API/MTL/MTLDevice.cpp b/lib/API/MTL/MTLDevice.cpp index 4969cf664..c70c1d453 100644 --- a/lib/API/MTL/MTLDevice.cpp +++ b/lib/API/MTL/MTLDevice.cpp @@ -11,7 +11,6 @@ #include "API/Device.h" #include "API/FormatConversion.h" -#include "API/VertexBuffer.h" #include "MTLResources.h" #include "Support/Pipeline.h" @@ -200,7 +199,7 @@ class MTLDevice : public offloadtest::Device { MTL::ComputePipelineState *ComputePipeline = nullptr; MTL::RenderPipelineState *RenderPipeline = nullptr; MTL::Buffer *ArgBuffer; - std::optional VB; + std::shared_ptr VB; MTL::VertexDescriptor *VertexDescriptor; llvm::SmallVector Textures; llvm::SmallVector Buffers; @@ -216,13 +215,13 @@ class MTLDevice : public offloadtest::Device { if (!IS.VB) return llvm::Error::success(); - const VertexBufferDesc &VBDesc = IS.VB->Desc; + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; NS::Array *FnAttrs = Fn->vertexAttributes(); if (!FnAttrs) return llvm::createStringError(std::errc::invalid_argument, "Vertex shader has no vertex attributes."); - if (FnAttrs->count() != VBDesc.Streams.size()) + if (FnAttrs->count() != PVB.Streams.size()) return llvm::createStringError( std::errc::invalid_argument, "Mismatch between vertex shader attribute count and pipeline " @@ -242,8 +241,8 @@ class MTLDevice : public offloadtest::Device { } IS.VertexDescriptor = MTL::VertexDescriptor::alloc()->init(); - for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { - const VertexStream &S = VBDesc.Streams[I]; + for (uint32_t I = 0; I < PVB.Streams.size(); ++I) { + const VertexStreamData &S = PVB.Streams[I]; llvm::SmallString<32> AttrName(S.Name); llvm::transform(AttrName, AttrName.begin(), tolower); // Append a zero since we're only supporting one attribute per name. @@ -252,7 +251,7 @@ class MTLDevice : public offloadtest::Device { MTL::VertexAttributeDescriptor *VADesc = MTL::VertexAttributeDescriptor::alloc()->init(); VADesc->setBufferIndex(0); - VADesc->setOffset(VBDesc.getOffset(I)); + VADesc->setOffset(PVB.getOffset(I)); VADesc->setFormat(getMetalVertexFormat(S.Fmt)); IS.VertexDescriptor->attributes()->setObject(VADesc, ShaderAttrIndices[AttrName]); @@ -260,7 +259,7 @@ class MTLDevice : public offloadtest::Device { MTL::VertexBufferLayoutDescriptor *LDesc = MTL::VertexBufferLayoutDescriptor::alloc()->init(); - LDesc->setStride(VBDesc.getStride()); + LDesc->setStride(PVB.getStride()); LDesc->setStepRate(1); LDesc->setStepFunction(MTL::VertexStepFunctionPerVertex); IS.VertexDescriptor->layouts()->setObject(LDesc, 0); @@ -450,19 +449,23 @@ class MTLDevice : public offloadtest::Device { "No vertex buffer specified for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); - if (!VBOrErr) - return VBOrErr.takeError(); - IS.VB = std::move(*VBOrErr); + + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = + createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + IS.VB = std::static_pointer_cast(*BufOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. On discrete GPUs // (DX/VK), consider using a staging buffer + copy to a GpuOnly vertex // buffer for optimal GPU read performance. On Apple Silicon this is // unnecessary due to unified memory. - auto *MTLBuf = static_cast(IS.VB->Data.get()); - const size_t BufSize = IS.VB->Data->getSizeInBytes(); - memcpy(MTLBuf->Buf->contents(), PVB.InterleavedData.get(), BufSize); - MTLBuf->Buf->didModifyRange(NS::Range::Make(0, BufSize)); + const size_t BufSize = IS.VB->getSizeInBytes(); + memcpy(IS.VB->Buf->contents(), PVB.InterleavedData.get(), BufSize); + IS.VB->Buf->didModifyRange(NS::Range::Make(0, BufSize)); } return llvm::Error::success(); } @@ -645,11 +648,10 @@ class MTLDevice : public offloadtest::Device { if (!IS.VB) return llvm::createStringError(std::errc::invalid_argument, "Vertex buffer not initialized."); - CmdEncoder->setVertexBuffer( - static_cast(IS.VB->Data.get())->Buf, 0, 0); + CmdEncoder->setVertexBuffer(IS.VB->Buf, 0, 0); CmdEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), - IS.VB->getVertexCount()); + P.Bindings.VertexBufferPtr->getVertexCount()); CmdEncoder->endEncoding(); diff --git a/lib/API/VK/Device.cpp b/lib/API/VK/Device.cpp index 9fae13e02..77038def1 100644 --- a/lib/API/VK/Device.cpp +++ b/lib/API/VK/Device.cpp @@ -11,7 +11,6 @@ #include "API/Device.h" #include "API/FormatConversion.h" -#include "API/VertexBuffer.h" #include "Support/Pipeline.h" #include "VKResources.h" #include "llvm/ADT/DenseSet.h" @@ -628,7 +627,7 @@ class VulkanDevice : public offloadtest::Device { std::shared_ptr RenderTarget; std::shared_ptr RTReadback; std::shared_ptr DepthStencil; - std::optional VB = std::nullopt; + std::shared_ptr VB; VkRenderPass RenderPass = VK_NULL_HANDLE; uint32_t ShaderStageMask = 0; @@ -1285,20 +1284,24 @@ class VulkanDevice : public offloadtest::Device { "No vertex buffer specified for graphics pipeline."); const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; - auto VBOrErr = offloadtest::createVertexBuffer(*this, PVB); - if (!VBOrErr) - return VBOrErr.takeError(); - IS.VB = std::move(*VBOrErr); + + BufferCreateDesc BufDesc = {}; + BufDesc.Location = MemoryLocation::CpuToGpu; + BufDesc.Usage = BufferUsage::VertexBuffer; + auto BufOrErr = + createBuffer("VertexBuffer", BufDesc, PVB.InterleavedSize); + if (!BufOrErr) + return BufOrErr.takeError(); + IS.VB = std::static_pointer_cast(*BufOrErr); // TODO: Currently uses a single CpuToGpu mapped buffer. For optimal GPU // performance on discrete GPUs, use a staging buffer + copy to a GpuOnly // vertex buffer instead. - auto *VKBuf = static_cast(IS.VB->Data.get()); - const size_t BufSize = IS.VB->Data->getSizeInBytes(); + const size_t BufSize = IS.VB->getSizeInBytes(); void *Mapped = nullptr; - vkMapMemory(Device, VKBuf->Memory, 0, BufSize, 0, &Mapped); + vkMapMemory(Device, IS.VB->Memory, 0, BufSize, 0, &Mapped); memcpy(Mapped, PVB.InterleavedData.get(), BufSize); - vkUnmapMemory(Device, VKBuf->Memory); + vkUnmapMemory(Device, IS.VB->Memory); } return llvm::Error::success(); @@ -1964,25 +1967,25 @@ class VulkanDevice : public offloadtest::Device { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; MultisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - // Build vertex input state from the vertex buffer description. + // Build vertex input state from the parsed vertex buffer streams. if (!IS.VB) return llvm::createStringError(std::errc::invalid_argument, "Vertex buffer not initialized."); - const VertexBufferDesc &VBDesc = IS.VB->Desc; + const ParsedVertexBuffer &PVB = *P.Bindings.VertexBufferPtr; VkVertexInputBindingDescription VertexInputBinding{}; VertexInputBinding.binding = 0; - VertexInputBinding.stride = VBDesc.getStride(); + VertexInputBinding.stride = PVB.getStride(); VertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; llvm::SmallVector Attributes; - for (uint32_t I = 0; I < VBDesc.Streams.size(); ++I) { - const VertexStream &S = VBDesc.Streams[I]; + for (uint32_t I = 0; I < PVB.Streams.size(); ++I) { + const VertexStreamData &S = PVB.Streams[I]; VkVertexInputAttributeDescription VkVA = {}; VkVA.location = I; VkVA.binding = 0; VkVA.format = getVulkanFormat(S.Fmt); - VkVA.offset = VBDesc.getOffset(I); + VkVA.offset = PVB.getOffset(I); Attributes.push_back(VkVA); } @@ -2363,12 +2366,12 @@ class VulkanDevice : public offloadtest::Device { return llvm::createStringError(std::errc::invalid_argument, "Vertex buffer not initialized."); VkDeviceSize Offsets[1]{0}; - VkBuffer VBHandle = - static_cast(IS.VB->Data.get())->Buffer; + VkBuffer VBHandle = IS.VB->Buffer; vkCmdBindVertexBuffers(IS.CB->CmdBuffer, 0, 1, &VBHandle, Offsets); + const uint32_t VertexCount = P.Bindings.VertexBufferPtr->getVertexCount(); // instanceCount must be >=1 to draw; previously was 0 which draws nothing - vkCmdDraw(IS.CB->CmdBuffer, IS.VB->getVertexCount(), 1, 0, 0); - llvm::outs() << "Drew " << IS.VB->getVertexCount() << " vertices.\n"; + vkCmdDraw(IS.CB->CmdBuffer, VertexCount, 1, 0, 0); + llvm::outs() << "Drew " << VertexCount << " vertices.\n"; vkCmdEndRenderPass(IS.CB->CmdBuffer); copyTextureToReadback(IS.CB->CmdBuffer, *IS.RenderTarget, *IS.RTReadback, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,