Merge pull request #4114 from Celegans12/fix/agc-tessellation-num-patches
fix(agc): bound tessellation patch records by the draw's patch count
This commit is contained in:
commit
2cf23fcc56
|
|
@ -26,7 +26,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
|
|||
mix(graphics.stages.tessellation.has_value());
|
||||
if (graphics.stages.tessellation) {
|
||||
const auto& tess = *graphics.stages.tessellation;
|
||||
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology}) mix(value);
|
||||
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology, tess.patchesPerGroup}) mix(value);
|
||||
}
|
||||
mix(graphics.rectList);
|
||||
mix(programs.size());
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
|
|||
const auto sameTess = [](const std::optional<ShaderRecompiler::TessellationConfiguration>& x, const std::optional<ShaderRecompiler::TessellationConfiguration>& y) {
|
||||
if (x.has_value() != y.has_value()) return false;
|
||||
if (!x) return true;
|
||||
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology;
|
||||
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology && x->patchesPerGroup == y->patchesPerGroup;
|
||||
};
|
||||
if (s.stages.path != t.stages.path || s.stages.registerValue != t.stages.registerValue || s.stages.vertexWaveSize != t.stages.vertexWaveSize || s.stages.fragmentWaveSize != t.stages.fragmentWaveSize || !sameMesh(s.stages.mesh, t.stages.mesh) || !sameTess(s.stages.tessellation, t.stages.tessellation)) return false;
|
||||
if (!sameColor(s.color, t.color) || s.colors.size() != t.colors.size() || s.blends.size() != t.blends.size()) return false;
|
||||
|
|
|
|||
|
|
@ -665,6 +665,7 @@ bool ValidationKey(const Context& context, std::span<const CompiledShader> shade
|
|||
add(tessellation.domain);
|
||||
add(tessellation.partitioning);
|
||||
add(tessellation.outputTopology);
|
||||
add(tessellation.patchesPerGroup);
|
||||
}
|
||||
}
|
||||
return keyed;
|
||||
|
|
|
|||
|
|
@ -827,6 +827,7 @@ void pipelineKey(std::vector<std::byte>& key, const Context& context, const Stat
|
|||
append(key, tessellation.domain);
|
||||
append(key, tessellation.partitioning);
|
||||
append(key, tessellation.outputTopology);
|
||||
append(key, tessellation.patchesPerGroup);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -612,8 +612,9 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) {
|
|||
validate((value & ~0x0207ed0du) == 0 && (value & 0x02002000u) == 0x02002000u && (value & 3u) == 1u && ((value >> 3u) & 3u) == 1u, "unsupported tessellation routing");
|
||||
const auto config = read(queue.context, 0x2d6);
|
||||
const auto parameters = read(queue.context, 0x2db);
|
||||
ShaderRecompiler::TessellationConfiguration tess{(config >> 8u) & 0x3fu, (config >> 14u) & 0x3fu, parameters & 3u, (parameters >> 2u) & 7u, (parameters >> 5u) & 7u};
|
||||
ShaderRecompiler::TessellationConfiguration tess{(config >> 8u) & 0x3fu, (config >> 14u) & 0x3fu, parameters & 3u, (parameters >> 2u) & 7u, (parameters >> 5u) & 7u, config & 0xffu};
|
||||
validate(tess.inputControlPoints != 0 && tess.inputControlPoints <= 32 && tess.outputControlPoints != 0 && tess.outputControlPoints <= 32, "invalid tessellation control-point counts");
|
||||
validate(tess.patchesPerGroup != 0, "VGT_LS_HS_CONFIG.NUM_PATCHES is zero");
|
||||
if ((parameters & ~0x000600ffu) != 0) Require(false, zeroMessage(0x2db, parameters, "VGT_TF_PARAM donut, factor detection, memory type or reserved state"));
|
||||
validate((tess.domain == 1 || tess.domain == 2) && tess.partitioning == 2 && (tess.outputTopology == 2 || tess.outputTopology == 3), "only triangle and quad domains with fractional-odd partitioning and triangle output are supported by the reference path");
|
||||
result.minTessellationLevel = readFloat(queue.context, 0x287);
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ void ShaderStageTests() {
|
|||
}
|
||||
Require(stages.path == AgcDriver::Graphics::ShaderPath::Tessellation && stages.tessellation, "tessellation routing changed");
|
||||
const auto& tessellation = *stages.tessellation;
|
||||
Require(tessellation.inputControlPoints == 3 && tessellation.outputControlPoints == 3 && tessellation.domain == 1 && tessellation.partitioning == 2 && tessellation.outputTopology == 2 && stages.minTessellationLevel == 1.0f && stages.maxTessellationLevel == 64.0f, "the passthrough triangle-patch configuration decoded differently");
|
||||
Require(tessellation.inputControlPoints == 3 && tessellation.outputControlPoints == 3 && tessellation.domain == 1 && tessellation.partitioning == 2 && tessellation.outputTopology == 2 && stages.minTessellationLevel == 1.0f && stages.maxTessellationLevel == 64.0f && tessellation.patchesPerGroup == 85, "the passthrough triangle-patch configuration decoded differently");
|
||||
for (const auto [parameters, reason] : {std::pair{0x44049u, "VGT_TF_PARAM"}, std::pair{0x140049u, "VGT_TF_PARAM"}, std::pair{0x2040049u, "VGT_TF_PARAM"}, std::pair{0x40059u, "fractional-odd partitioning"}, std::pair{0x4004du, "fractional-odd partitioning"}, std::pair{0x40048u, "fractional-odd partitioning"}, std::pair{0x40029u, "fractional-odd partitioning"}, std::pair{0x4004bu, "fractional-odd partitioning"}}) {
|
||||
queue.context[0x2db] = parameters;
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, reason);
|
||||
|
|
@ -609,7 +609,7 @@ void ShaderStageTests() {
|
|||
queue.context[0x286] = 0x41100000;
|
||||
const auto quadStages = AgcDriver::Graphics::DecodeState(queue).stages;
|
||||
const auto& quads = quadStages.tessellation;
|
||||
Require(quads && quads->inputControlPoints == 4 && quads->outputControlPoints == 4 && quads->domain == 2 && quads->partitioning == 2 && quads->outputTopology == 3 && quadStages.minTessellationLevel == 1.0f && quadStages.maxTessellationLevel == 9.0f, "counter-clockwise quad patches with HOS levels [1, 9] decoded differently");
|
||||
Require(quads && quads->inputControlPoints == 4 && quads->outputControlPoints == 4 && quads->domain == 2 && quads->partitioning == 2 && quads->outputTopology == 3 && quadStages.minTessellationLevel == 1.0f && quadStages.maxTessellationLevel == 9.0f && quads->patchesPerGroup == 63, "counter-clockwise quad patches with HOS levels [1, 9] and 63 patches per group decoded differently");
|
||||
queue.context[0x2db] = 0x40069;
|
||||
Require(AgcDriver::Graphics::DecodeState(queue).stages.tessellation->outputTopology == 3, "counter-clockwise triangle patches decoded differently");
|
||||
for (const auto [maximum, minimum, reason] : {std::tuple{0x3f800000u, 0x40000000u, "tessellation level clamps"}, std::tuple{0x41100000u, 0xbf800000u, "tessellation level clamps"}, std::tuple{0x3f000000u, 0x00000000u, "tessellation level clamps"}, std::tuple{0x42800000u, 0x42800000u, "tessellation level clamps"}, std::tuple{0x7fc00000u, 0x3f800000u, "non-finite register"}, std::tuple{0x7f800000u, 0x3f800000u, "non-finite register"}}) {
|
||||
|
|
@ -626,6 +626,8 @@ void ShaderStageTests() {
|
|||
queue.context[0x2d5] = 0x0200210d;
|
||||
queue.context[0x2d6] = 0;
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "control-point counts");
|
||||
queue.context[0x2d6] = 0xc300;
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "NUM_PATCHES is zero");
|
||||
queue = makeState();
|
||||
for (const auto value : {0x2003u, 0x2018u, 0x20c0u, 0x80002000u}) {
|
||||
queue.context[0x2d5] = value;
|
||||
|
|
|
|||
|
|
@ -1686,6 +1686,23 @@ void verifyMeshConfiguration() {
|
|||
other.graphics = GraphicsCompileContext{0u, {}, otherMesh, std::nullopt, {}};
|
||||
RecompileCacheKey::Build(other, key);
|
||||
require(key != first && RecompileCacheKey::ContextHash(request) != RecompileCacheKey::ContextHash(other), "the cache keys ignore the mesh configuration");
|
||||
RecompileRequest hull{};
|
||||
hull.shader = {ShaderStage::TessellationControl, 0x10000u, code, 0, {}};
|
||||
hull.context.waveSize = 64;
|
||||
hull.graphics = GraphicsCompileContext{0u, {}, std::nullopt, TessellationConfiguration{4u, 4u, 2u, 2u, 3u, 63u}, {}};
|
||||
const auto hullReplay = RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(hull));
|
||||
const auto* replayed = hullReplay.request.graphics && hullReplay.request.graphics->tessellation ? &*hullReplay.request.graphics->tessellation : nullptr;
|
||||
require(replayed != nullptr && replayed->domain == 2u && replayed->outputTopology == 3u && replayed->patchesPerGroup == 63u, "the quad tessellation configuration or its patches per group were lost in serialization");
|
||||
auto grouped = hull;
|
||||
grouped.graphics->tessellation->patchesPerGroup = 62u;
|
||||
for (const auto stage : {ShaderStage::Local, ShaderStage::TessellationControl, ShaderStage::TessellationEvaluation}) {
|
||||
hull.shader.stage = stage;
|
||||
grouped.shader.stage = stage;
|
||||
RecompileCacheKey::Build(hull, key);
|
||||
const auto patchKey = key;
|
||||
RecompileCacheKey::Build(grouped, key);
|
||||
require((key != patchKey) == (stage != ShaderStage::Local), "only the hull and domain cache keys may depend on the patches per group, which bound their relative patch index");
|
||||
}
|
||||
const std::array<std::uint32_t, 8> users{};
|
||||
request.context.waveSize = 32u;
|
||||
request.context.userData = users;
|
||||
|
|
@ -1826,13 +1843,13 @@ void verifyPixelRequestSerialization() {
|
|||
minimal.context.pixel = ShaderPixelStageInfo{};
|
||||
minimal.context.pixel->rasterPrimitive = RasterPrimitive::Rectangle;
|
||||
const auto encoded = serializer.Serialize(minimal);
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQRUAAAA=", "new requests did not use serialization version 21");
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQRYAAAA=", "new requests did not use serialization version 22");
|
||||
require(serializer.Deserialize(encoded).request.context.pixel->rasterPrimitive == RasterPrimitive::Rectangle, "the pixel primitive class did not round-trip");
|
||||
constexpr std::size_t mappingOffset = 8u + 37u + 18u + 163u;
|
||||
for (std::size_t bytes = 0; bytes < 21u; ++bytes) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source, alpha-to-coverage, stencil reference export flag or primitive class was accepted");
|
||||
}
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRYAAAA="}) {
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRcAAAA="}) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
|
||||
}
|
||||
static constexpr std::string_view version18Request{
|
||||
|
|
|
|||
|
|
@ -64,6 +64,16 @@ alignas(256) constexpr std::array<std::uint32_t, 29> LocalIndexAsData{
|
|||
0x0000030a, 0xd8340010, 0x0000070a, 0xbf8cc07f, 0xbefd2106,
|
||||
};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 50> HullDescendingRecords{
|
||||
0xd7650000, 0x000100c1, 0xd7660000, 0x000200c1, 0xbf8a0000, 0x7da206f9, 0x81060000, 0xd5480014,
|
||||
0x02151101, 0x362402ff, 0x000000ff, 0x160028a0, 0xd5430015, 0x040224ff, 0x00000060, 0xdbfc0000,
|
||||
0x02000015, 0x162c2890, 0xd5430016, 0x045a24ff, 0x00000030, 0xbf8cc07f, 0xe0781000, 0x02020216,
|
||||
0x7da82881, 0xbe94047e, 0xb06a0060, 0x162602f9, 0x0086066a, 0xd8d80008, 0x06000013, 0xd8dc1008,
|
||||
0x07000013, 0xbf8cc07f, 0x7e300306, 0x7e320306, 0x7e340306, 0x7e360306, 0x34142484, 0xbefe0414,
|
||||
0xe0781000, 0x0403180a, 0x4a162481, 0x161616ff, 0x000000d0, 0x4c1616ff, 0x00008000, 0xe0741000,
|
||||
0x0202070b, 0xbf810000,
|
||||
};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 46> HullUnguardedFactors{
|
||||
0xd7650000, 0x000100c1, 0xd7660000, 0x000200c1, 0xbf8a0000, 0x7da206f9, 0x81060000, 0xd5480014,
|
||||
0x02151101, 0x362402ff, 0x000000ff, 0x160028a0, 0xd5430015, 0x040224ff, 0x00000060, 0xdbfc0000,
|
||||
|
|
@ -297,24 +307,24 @@ std::uint32_t CoveredPixels(std::uint32_t patch, bool interior) {
|
|||
return count;
|
||||
}
|
||||
|
||||
void RequirePatches(AgcDriver::VulkanDevice& device, const ShaderRecompiler::SpirvTarget& target, std::span<const std::uint32_t> hull, const std::string& program) {
|
||||
void RequirePatches(AgcDriver::VulkanDevice& device, const ShaderRecompiler::SpirvTarget& target, std::span<const std::uint32_t> hull, const std::string& program, const PatchDraw& patches = Triangles) {
|
||||
std::uint32_t cursor = 0;
|
||||
const Stages stages{
|
||||
Compile(target, ShaderStage::Local, LocalCode, cursor),
|
||||
Compile(target, ShaderStage::TessellationControl, hull, cursor),
|
||||
Compile(target, ShaderStage::TessellationEvaluation, DomainCode, cursor),
|
||||
Compile(target, ShaderStage::Fragment, PixelCode, cursor),
|
||||
Compile(target, ShaderStage::Local, LocalCode, cursor, patches),
|
||||
Compile(target, ShaderStage::TessellationControl, hull, cursor, patches),
|
||||
Compile(target, ShaderStage::TessellationEvaluation, DomainCode, cursor, patches),
|
||||
Compile(target, ShaderStage::Fragment, PixelCode, cursor, patches),
|
||||
};
|
||||
const auto center = Row(-0.5f + 1.0f / 3.0f);
|
||||
for (const auto cull : {VK_CULL_MODE_NONE, VK_CULL_MODE_BACK_BIT}) {
|
||||
Draw(device, stages, cull, VK_FRONT_FACE_COUNTER_CLOCKWISE);
|
||||
Draw(device, stages, cull, VK_FRONT_FACE_COUNTER_CLOCKWISE, patches);
|
||||
const std::string name = program + (cull == VK_CULL_MODE_NONE ? ", unculled" : ", back-face culled");
|
||||
Require(CoveredPixels(0, false) > 100 && CoveredPixels(0, true) == 0, name + ": the level-1 patch was not drawn as one flat triangle, centre " + PixelText(PixelAt(Column(0, 0.25f), center)));
|
||||
Require(CoveredPixels(1, false) == 0, name + ": the patch with level 0 was not culled");
|
||||
Require(CoveredPixels(2, false) > 100 && PixelAt(Column(2, 0.25f), center)[1] > 32, name + ": the level-5 patch has no interior domain points at its centre, " + PixelText(PixelAt(Column(2, 0.25f), center)));
|
||||
Require(PixelAt(0, 0) == Background && PixelAt(Width - 1, Height - 1) == Background, name + ": the corners changed");
|
||||
}
|
||||
Draw(device, stages, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE);
|
||||
Draw(device, stages, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, patches);
|
||||
Require(CoveredPixels(0, false) == 0 && CoveredPixels(2, false) == 0, program + ": tessellated triangles did not keep the control points' winding");
|
||||
}
|
||||
|
||||
|
|
@ -443,10 +453,13 @@ int main() {
|
|||
ExpectRefusal(target, ShaderStage::TessellationEvaluation, DomainCulledPrimitive, "passthrough primitive");
|
||||
ExpectRefusal(target, ShaderStage::Local, LocalIndexAsData, "outside a tessellation ring or LDS address");
|
||||
ExpectRefusal(target, ShaderStage::TessellationControl, HullCode, "outside its patch's quad factors", {{3u, 3u, 2u, 2u, 2u}, Indices});
|
||||
ExpectRefusal(target, ShaderStage::TessellationControl, HullDescendingRecords, "not affine");
|
||||
ExpectRefusal(target, ShaderStage::TessellationControl, HullDescendingRecords, "not affine", {{3u, 3u, 1u, 2u, 2u, 158u}, Indices});
|
||||
|
||||
RequirePatches(*device, target, HullCode, "hull program");
|
||||
RequirePatches(*device, target, HullRestoredExecSaved, "hull program saving its restored exec");
|
||||
RequirePatches(*device, target, HullDynamicIndex, "hull program reading a control point at a computed index");
|
||||
RequirePatches(*device, target, HullDescendingRecords, "hull program with descending patch records in 157-patch groups", {{3u, 3u, 1u, 2u, 2u, 157u}, Indices});
|
||||
RequireCounterClockwise(*device, target);
|
||||
RequireLevelClamps(*device, target);
|
||||
RequireQuads(*device, target);
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ private:
|
|||
append(key, tessellation != nullptr);
|
||||
if (tessellation == nullptr) return;
|
||||
for (const auto value : {tessellation->inputControlPoints, tessellation->outputControlPoints, tessellation->domain, tessellation->partitioning, tessellation->outputTopology}) append(key, value);
|
||||
if (request.shader.stage == ShaderStage::Local) return;
|
||||
append(key, tessellation->patchesPerGroup);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
|
|
|
|||
|
|
@ -520,15 +520,17 @@ void writeTessellationConfiguration(Writer& writer, const TessellationConfigurat
|
|||
writer.WriteU32(configuration.domain);
|
||||
writer.WriteU32(configuration.partitioning);
|
||||
writer.WriteU32(configuration.outputTopology);
|
||||
writer.WriteU32(configuration.patchesPerGroup);
|
||||
}
|
||||
|
||||
TessellationConfiguration readTessellationConfiguration(Reader& reader) {
|
||||
TessellationConfiguration readTessellationConfiguration(Reader& reader, std::uint32_t version) {
|
||||
TessellationConfiguration configuration{};
|
||||
configuration.inputControlPoints = reader.ReadU32();
|
||||
configuration.outputControlPoints = reader.ReadU32();
|
||||
configuration.domain = reader.ReadU32();
|
||||
configuration.partitioning = reader.ReadU32();
|
||||
configuration.outputTopology = reader.ReadU32();
|
||||
if (version >= 22u) configuration.patchesPerGroup = reader.ReadU32();
|
||||
return configuration;
|
||||
}
|
||||
|
||||
|
|
@ -731,7 +733,7 @@ GraphicsCompileContext readGraphicsCompileContext(Reader& reader, DeserializedGr
|
|||
graphics.mesh = storage.mesh;
|
||||
}
|
||||
if (reader.ReadBool()) {
|
||||
storage.tessellation = readTessellationConfiguration(reader);
|
||||
storage.tessellation = readTessellationConfiguration(reader, version);
|
||||
graphics.tessellation = storage.tessellation;
|
||||
}
|
||||
storage.draw.indexAddress = reader.ReadU64();
|
||||
|
|
@ -748,7 +750,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(21u);
|
||||
writer.WriteU32(22u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
|
|
@ -780,7 +782,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const
|
|||
Reader reader(decoded);
|
||||
if (reader.ReadU32() != 0x41505335u) throw std::runtime_error("invalid recompile request signature");
|
||||
const auto version = reader.ReadU32();
|
||||
if (version < 1u || version > 21u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (version < 1u || version > 22u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
DeserializedRequest result{};
|
||||
result.request.shader = readShaderBinary(reader, result.shaderCode, result.shaderHeader);
|
||||
result.request.context = readGuestContext(reader, result, version);
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ struct ShaderTessellationInputInfo {
|
|||
std::uint32_t domain = 0;
|
||||
std::uint32_t partitioning = 0;
|
||||
std::uint32_t outputTopology = 0;
|
||||
std::uint32_t patchesPerGroup = 256;
|
||||
};
|
||||
|
||||
struct ShaderVertexInputInfo {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
namespace ShaderRecompiler {
|
||||
namespace {
|
||||
|
||||
constexpr std::int64_t PatchIndexCount = 256;
|
||||
constexpr std::int64_t LocalVertexIndexCount = 8192;
|
||||
constexpr std::int64_t TriangleFactorBytes = 16;
|
||||
constexpr std::int64_t QuadFactorBytes = 24;
|
||||
|
|
@ -418,7 +417,7 @@ std::uint32_t ComponentCount(IrOpcode opcode) {
|
|||
class Lowering {
|
||||
public:
|
||||
Lowering(IrProgram& program, const ShaderTessellationInputInfo& tessellation)
|
||||
: program(program), tessellation(tessellation), stage(program.Resources().stage), evaluator(stage == IrShaderStage::TessellationControl ? tessellation.outputControlPoints : 0u, stage == IrShaderStage::Local ? LocalVertexIndexCount : PatchIndexCount) {}
|
||||
: program(program), tessellation(tessellation), stage(program.Resources().stage), evaluator(stage == IrShaderStage::TessellationControl ? tessellation.outputControlPoints : 0u, stage == IrShaderStage::Local ? LocalVertexIndexCount : static_cast<std::int64_t>(tessellation.patchesPerGroup)) {}
|
||||
|
||||
void Run() {
|
||||
for (IrBlock* block : program.BlockOrder()) {
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ struct TessellationConfiguration {
|
|||
std::uint32_t domain;
|
||||
std::uint32_t partitioning;
|
||||
std::uint32_t outputTopology;
|
||||
std::uint32_t patchesPerGroup = 256;
|
||||
};
|
||||
|
||||
inline constexpr std::uint32_t MeshDrawPushOffsetBytes = 104;
|
||||
|
|
|
|||
|
|
@ -181,12 +181,14 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
|
|||
if (tessellation == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: tessellation configuration is missing");
|
||||
if (tessellation->inputControlPoints == 0u || tessellation->inputControlPoints > 32u || tessellation->outputControlPoints == 0u || tessellation->outputControlPoints > 32u) throw std::runtime_error("ShaderInputInfoBuilder: invalid tessellation control-point counts");
|
||||
if ((tessellation->domain != 1u && tessellation->domain != 2u) || tessellation->partitioning != 2u || (tessellation->outputTopology != 2u && tessellation->outputTopology != 3u)) throw std::runtime_error("ShaderInputInfoBuilder: unsupported tessellation configuration");
|
||||
if (tessellation->patchesPerGroup == 0u || tessellation->patchesPerGroup > 256u) throw std::runtime_error("ShaderInputInfoBuilder: invalid tessellation patches per group");
|
||||
auto& target = vertexStorage.tess;
|
||||
target.inputControlPoints = tessellation->inputControlPoints;
|
||||
target.outputControlPoints = tessellation->outputControlPoints;
|
||||
target.domain = tessellation->domain;
|
||||
target.partitioning = tessellation->partitioning;
|
||||
target.outputTopology = tessellation->outputTopology;
|
||||
target.patchesPerGroup = tessellation->patchesPerGroup;
|
||||
}
|
||||
if (stage == ShaderStageKind::Mesh) {
|
||||
if (mesh == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: a mesh-stage program has no mesh configuration");
|
||||
|
|
|
|||
Loading…
Reference in New Issue