Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion include/API/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,15 @@ struct TraditionalRasterPipelineCreateDesc {
llvm::SmallVector<Format> RTFormats;
std::optional<Format> DSFormat;
PrimitiveTopology Topology;
// Set if Topology == PatchList. Validated in
// Pipeline.cpp::validatePipelineKind.
std::optional<uint32_t> PatchControlPoints;

ShaderContainer VS;
// TODO: Optional Hull & Domain Shaders
// Hull and Domain are independent optionals here; Pipeline.cpp enforces that
// they must be set as a pair (and only with PatchList topology).
Comment on lines +95 to +96

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both must be set, there're not independent, but dependent or interdependent?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In-person: the here was referring to the two std::optional<>s below allowing them to be independent while they're not supposed to be independent. I would reword that to be more like the latter sentence to make the intent clear :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment is fine.

std::optional<ShaderContainer> HS;
std::optional<ShaderContainer> DS;
std::optional<ShaderContainer> GS;
ShaderContainer PS;

Expand All @@ -97,6 +104,12 @@ struct TraditionalRasterPipelineCreateDesc {
case Stages::Vertex:
VS = std::move(SC);
break;
case Stages::Hull:
HS = std::move(SC);
break;
case Stages::Domain:
DS = std::move(SC);
break;
case Stages::Geometry:
GS = std::move(SC);
break;
Expand Down
2 changes: 1 addition & 1 deletion include/API/Enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ enum class StoreAction {
DontCare, ///< Contents may be discarded after the pass.
};

enum class PrimitiveTopology { TriangleList, PointList };
enum class PrimitiveTopology { TriangleList, PointList, PatchList };

} // namespace offloadtest

Expand Down
16 changes: 14 additions & 2 deletions include/Support/Pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/Support/YAMLTraits.h"
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <variant>

Expand All @@ -32,6 +33,8 @@ enum class Stages {

// Traditional Raster
Vertex,
Hull,
Domain,
Geometry,
Pixel,

Expand All @@ -40,8 +43,8 @@ enum class Stages {
Mesh
};
inline constexpr std::array AllStages = {
Stages::Compute, Stages::Vertex, Stages::Geometry,
Stages::Pixel, Stages::Amplification, Stages::Mesh,
Stages::Compute, Stages::Vertex, Stages::Hull, Stages::Domain,
Stages::Geometry, Stages::Pixel, Stages::Amplification, Stages::Mesh,
};
inline constexpr size_t NumStages = AllStages.size();

Expand Down Expand Up @@ -402,6 +405,12 @@ struct IOBindings {
CPUBuffer *RTargetBufferPtr = nullptr;
PrimitiveTopology Topology = PrimitiveTopology::TriangleList;

// Set if Topology == PatchList. Validated in
// Pipeline.cpp::validatePipelineKind. Valid range is 1..32 (matches both

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only if you're making changes (resolving comments via Claude...) anyway:

Suggested change
// Pipeline.cpp::validatePipelineKind. Valid range is 1..32 (matches both
// Pipeline.cpp::validatePipelineKind(). Valid range is 1..32 (matches both

// D3D12's per-CP-patchlist topologies and Vulkan's
// VkPipelineTessellationStateCreateInfo::patchControlPoints).
std::optional<uint32_t> PatchControlPoints;

uint32_t getVertexStride() const {
uint32_t Stride = 0;
for (auto VA : VertexAttributes)
Expand Down Expand Up @@ -730,6 +739,8 @@ template <> struct ScalarEnumerationTraits<offloadtest::Stages> {
#define ENUM_CASE(Val) I.enumCase(V, #Val, offloadtest::Stages::Val)
ENUM_CASE(Compute);
ENUM_CASE(Vertex);
ENUM_CASE(Hull);
ENUM_CASE(Domain);
ENUM_CASE(Geometry);
ENUM_CASE(Pixel);
ENUM_CASE(Amplification);
Expand All @@ -743,6 +754,7 @@ template <> struct ScalarEnumerationTraits<offloadtest::PrimitiveTopology> {
#define ENUM_CASE(Val) I.enumCase(V, #Val, offloadtest::PrimitiveTopology::Val)
ENUM_CASE(TriangleList);
ENUM_CASE(PointList);
ENUM_CASE(PatchList);
#undef ENUM_CASE
}
};
Expand Down
22 changes: 20 additions & 2 deletions lib/API/DX/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,27 @@ getDXPrimitiveTopologyType(PrimitiveTopology Topology) {
return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
case PrimitiveTopology::PointList:
return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
case PrimitiveTopology::PatchList:
return D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
}
llvm_unreachable("All PrimitiveTopology cases handled");
}

static D3D_PRIMITIVE_TOPOLOGY
getDXPrimitiveTopology(PrimitiveTopology Topology) {
getDXPrimitiveTopology(PrimitiveTopology Topology,
std::optional<uint32_t> PatchControlPoints) {
switch (Topology) {
case PrimitiveTopology::TriangleList:
return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
case PrimitiveTopology::PointList:
return D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
case PrimitiveTopology::PatchList:
// _N_CONTROL_POINT_PATCHLIST enums are contiguous from 1..32.
assert(PatchControlPoints && *PatchControlPoints >= 1 &&
*PatchControlPoints <= 32);
return static_cast<D3D_PRIMITIVE_TOPOLOGY>(
D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST +
(*PatchControlPoints - 1));
}
llvm_unreachable("All PrimitiveTopology cases handled");
}
Expand Down Expand Up @@ -1159,6 +1169,12 @@ class DXDevice : public offloadtest::Device {
return llvm::createStringError(std::errc::invalid_argument,
"Graphics pipeline requires both a vertex "
"shader and a pixel shader.");
if (Desc.HS)
PSODesc.HS = {Desc.HS->Shader->getBuffer().data(),
Desc.HS->Shader->getBuffer().size()};
if (Desc.DS)
PSODesc.DS = {Desc.DS->Shader->getBuffer().data(),
Desc.DS->Shader->getBuffer().size()};
if (Desc.GS)
PSODesc.GS = {Desc.GS->Shader->getBuffer().data(),
Desc.GS->Shader->getBuffer().size()};
Expand Down Expand Up @@ -1187,7 +1203,8 @@ class DXDevice : public offloadtest::Device {
return Err;

return std::make_unique<DXPipelineState>(
Name, RootSig, PSO, getDXPrimitiveTopology(Desc.Topology));
Name, RootSig, PSO,
getDXPrimitiveTopology(Desc.Topology, Desc.PatchControlPoints));
}

llvm::Expected<std::unique_ptr<PipelineState>>
Expand Down Expand Up @@ -2489,6 +2506,7 @@ class DXDevice : public offloadtest::Device {

TraditionalRasterPipelineCreateDesc PipelineDesc = {};
PipelineDesc.Topology = P.Bindings.Topology;
PipelineDesc.PatchControlPoints = P.Bindings.PatchControlPoints;
PipelineDesc.DSFormat = Format::D32FloatS8Uint;
for (auto &Shader : P.Shaders) {
ShaderContainer SC = {};
Expand Down
9 changes: 9 additions & 0 deletions lib/API/MTL/MTLDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ static IRShaderStage getShaderStage(Stages Stage) {
return IRShaderStageCompute;
case Stages::Vertex:
return IRShaderStageVertex;
case Stages::Hull:
llvm_unreachable("Hull shaders are not supported on Metal.");
case Stages::Domain:
llvm_unreachable("Domain shaders are not supported on Metal.");
case Stages::Geometry:
llvm_unreachable("Geometry shaders are not supported on Metal.");
case Stages::Pixel:
Expand Down Expand Up @@ -1587,6 +1591,11 @@ class MTLDevice : public offloadtest::Device {
return llvm::createStringError(
std::errc::not_supported,
"Geometry shaders are not supported on this backend.");
if (Desc.HS || Desc.DS)
return llvm::createStringError(
std::errc::not_supported,
"Hull/Domain (tessellation) shaders are not supported on this "
"backend.");
if (Desc.Topology != PrimitiveTopology::TriangleList)
return llvm::createStringError(
std::errc::not_supported,
Expand Down
68 changes: 68 additions & 0 deletions lib/API/VK/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ static VkShaderStageFlagBits getShaderStageFlag(Stages Stage) {
return VK_SHADER_STAGE_COMPUTE_BIT;
case Stages::Vertex:
return VK_SHADER_STAGE_VERTEX_BIT;
case Stages::Hull:
return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
case Stages::Domain:
return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
case Stages::Geometry:
return VK_SHADER_STAGE_GEOMETRY_BIT;
case Stages::Pixel:
Expand All @@ -224,6 +228,8 @@ static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology Topology) {
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PrimitiveTopology::PointList:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PrimitiveTopology::PatchList:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
}
llvm_unreachable("All PrimitiveTopology cases handled");
}
Expand Down Expand Up @@ -1461,6 +1467,8 @@ class VulkanDevice : public offloadtest::Device {
const TraditionalRasterPipelineCreateDesc &Desc) override {
const ShaderContainer &VS = Desc.VS;
const ShaderContainer &PS = Desc.PS;
const std::optional<ShaderContainer> &HS = Desc.HS;
const std::optional<ShaderContainer> &DS = Desc.DS;
const std::optional<ShaderContainer> &GS = Desc.GS;
const llvm::ArrayRef<InputLayoutDesc> InputLayout = Desc.InputLayout;
const llvm::ArrayRef<Format> RTFormats = Desc.RTFormats;
Expand Down Expand Up @@ -1497,6 +1505,56 @@ class VulkanDevice : public offloadtest::Device {
ShaderStages.push_back(ShaderStage);
}

llvm::SmallVector<VkSpecializationMapEntry> HSSpecEntries;
llvm::SmallVector<char> HSSpecData;
VkSpecializationInfo HSSpecInfo = {};
if (HS) {
if (auto Err = parseSpecializationConstants(HS->SpecializationConstants,
HSSpecEntries, HSSpecData,
HSSpecInfo))
return Err;

auto HSModOrErr = createShaderModule(HS->Shader, "hull");
if (!HSModOrErr)
return HSModOrErr.takeError();

GraphicsFlags |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;

VkPipelineShaderStageCreateInfo ShaderStage = {};
ShaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ShaderStage.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
ShaderStage.module = *HSModOrErr;
ShaderStage.pName = HS->EntryPoint.c_str();
ShaderStage.pSpecializationInfo =
HS->SpecializationConstants.empty() ? nullptr : &HSSpecInfo;
ShaderStages.push_back(ShaderStage);
}

llvm::SmallVector<VkSpecializationMapEntry> DSSpecEntries;
llvm::SmallVector<char> DSSpecData;
VkSpecializationInfo DSSpecInfo = {};
if (DS) {
if (auto Err = parseSpecializationConstants(DS->SpecializationConstants,
DSSpecEntries, DSSpecData,
DSSpecInfo))
return Err;

auto DSModOrErr = createShaderModule(DS->Shader, "domain");
if (!DSModOrErr)
return DSModOrErr.takeError();

GraphicsFlags |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;

VkPipelineShaderStageCreateInfo ShaderStage = {};
ShaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ShaderStage.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
ShaderStage.module = *DSModOrErr;
ShaderStage.pName = DS->EntryPoint.c_str();
ShaderStage.pSpecializationInfo =
DS->SpecializationConstants.empty() ? nullptr : &DSSpecInfo;
ShaderStages.push_back(ShaderStage);
}

llvm::SmallVector<VkSpecializationMapEntry> GSSpecEntries;
llvm::SmallVector<char> GSSpecData;
VkSpecializationInfo GSSpecInfo = {};
Expand Down Expand Up @@ -1625,6 +1683,13 @@ class VulkanDevice : public offloadtest::Device {
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
InputAssemblyCI.topology = getVkPrimitiveTopology(Desc.Topology);

VkPipelineTessellationStateCreateInfo TessellationCI = {};
if (Desc.PatchControlPoints) {
TessellationCI.sType =
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
TessellationCI.patchControlPoints = *Desc.PatchControlPoints;
}

VkPipelineViewportStateCreateInfo ViewportCI = {};
ViewportCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
ViewportCI.viewportCount = 1;
Expand Down Expand Up @@ -1675,6 +1740,8 @@ class VulkanDevice : public offloadtest::Device {
PipelineCI.pStages = ShaderStages.data();
PipelineCI.pVertexInputState = &VertexInputCI;
PipelineCI.pInputAssemblyState = &InputAssemblyCI;
PipelineCI.pTessellationState =
Desc.PatchControlPoints ? &TessellationCI : nullptr;
PipelineCI.pViewportState = &ViewportCI;
PipelineCI.pRasterizationState = &RastCI;
PipelineCI.pMultisampleState = &MultisampleCI;
Expand Down Expand Up @@ -3222,6 +3289,7 @@ class VulkanDevice : public offloadtest::Device {
} else if (P.isTraditionalRaster()) {
TraditionalRasterPipelineCreateDesc PipelineDesc = {};
PipelineDesc.Topology = P.Bindings.Topology;
PipelineDesc.PatchControlPoints = P.Bindings.PatchControlPoints;
PipelineDesc.DSFormat = Format::D32FloatS8Uint;
for (auto &Shader : P.Shaders) {
ShaderContainer SC = {};
Expand Down
29 changes: 27 additions & 2 deletions lib/Support/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ namespace yaml {
void MappingTraits<offloadtest::Pipeline>::mapping(IO &I,
offloadtest::Pipeline &P) {
I.mapRequired("Shaders", P.Shaders);
if (auto Err = P.validatePipelineKind())
I.setError(llvm::toString(std::move(Err)));

// Runtime-specific settings.
I.mapOptional("RuntimeSettings", P.Settings);
Expand All @@ -68,6 +66,12 @@ void MappingTraits<offloadtest::Pipeline>::mapping(IO &I,
I.mapOptional("Bindings", P.Bindings);
I.mapOptional("PushConstants", P.PushConstants);

// Runs here (not right after Shaders) because the tessellation topology
// check reads Bindings.Topology and Bindings.PatchControlPoints. Must
// still run before validateDispatchParameters, which reads P.Kind.
if (auto Err = P.validatePipelineKind())
I.setError(llvm::toString(std::move(Err)));

I.mapOptional("DispatchParameters", P.DispatchParameters);
if (auto Err = P.validateDispatchParameters())
I.setError(llvm::toString(std::move(Err)));
Expand Down Expand Up @@ -427,6 +431,7 @@ void MappingTraits<offloadtest::IOBindings>::mapping(
I.mapOptional("RenderTarget", B.RenderTarget);
I.mapOptional("Topology", B.Topology,
offloadtest::PrimitiveTopology::TriangleList);
I.mapOptional("PatchControlPoints", B.PatchControlPoints);
}

void MappingTraits<offloadtest::PushConstantBlock>::mapping(
Expand Down Expand Up @@ -605,6 +610,26 @@ llvm::Error offloadtest::Pipeline::validatePipelineKind() {
return llvm::createStringError("Vertex and Mesh/Amplification Shaders "
"cannot be used in the same pipeline.");

const bool HasHS = HasShaderType[llvm::to_underlying(Stages::Hull)];
const bool HasDS = HasShaderType[llvm::to_underlying(Stages::Domain)];
if (HasHS != HasDS)
return llvm::createStringError(
"Hull and Domain shaders must be used together");

const bool IsTessellated = HasHS && HasDS;
const bool IsPatchList = Bindings.Topology == PrimitiveTopology::PatchList;
if (IsTessellated != IsPatchList)
return llvm::createStringError(
"Tessellation pipelines must use PatchList topology");
if (IsPatchList &&
(!Bindings.PatchControlPoints || *Bindings.PatchControlPoints < 1 ||
*Bindings.PatchControlPoints > 32))
return llvm::createStringError(
"PatchList topology requires PatchControlPoints in the range 1..32.");
if (!IsPatchList && Bindings.PatchControlPoints)
return llvm::createStringError(
"PatchControlPoints is only valid with PatchList topology.");

Kind = ShaderPipelineKind::TraditionalRaster;
return llvm::Error::success();
}
Expand Down
Loading
Loading