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
Original file line number Diff line number Diff line change
Expand Up @@ -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, tess.patchesPerGroup}) mix(value);
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology, tess.patchesPerGroup, tess.primitiveGroupSize, static_cast<std::uint32_t>(tess.breakAtInstance)}) mix(value);
}
mix(graphics.rectList);
mix(programs.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 && x->patchesPerGroup == y->patchesPerGroup;
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 && x->primitiveGroupSize == y->primitiveGroupSize && x->breakAtInstance == y->breakAtInstance;
};
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;
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,8 @@ bool ValidationKey(const Context& context, std::span<const CompiledShader> shade
add(tessellation.partitioning);
add(tessellation.outputTopology);
add(tessellation.patchesPerGroup);
add(tessellation.primitiveGroupSize);
add(tessellation.breakAtInstance ? 1u : 0u);
}
}
return keyed;
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,8 @@ void pipelineKey(std::vector<std::byte>& key, const Context& context, const Stat
append(key, tessellation.partitioning);
append(key, tessellation.outputTopology);
append(key, tessellation.patchesPerGroup);
append(key, tessellation.primitiveGroupSize);
append(key, tessellation.breakAtInstance);
}
}

Expand Down
3 changes: 2 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,8 @@ 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, config & 0xffu};
const auto primitiveGroup = find(queue.userConfig, 0x25b, RegisterBank::UserConfig);
ShaderRecompiler::TessellationConfiguration tess{(config >> 8u) & 0x3fu, (config >> 14u) & 0x3fu, parameters & 3u, (parameters >> 2u) & 7u, (parameters >> 5u) & 7u, config & 0xffu, primitiveGroup == queue.userConfig.end() ? 0u : primitiveGroup->second & 0x1ffu, primitiveGroup != queue.userConfig.end() && (primitiveGroup->second & 0x40000u) != 0u};
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"));
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/tests/BdaContracts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void RunBdaContractTests() {
ValidateBdaTarget(program, target);
AgcDriver::Graphics::Require(!BdaInvocationsMayStop(program), "a BDA program with barriers must keep its invocations running");
program.Resources().stage = IrShaderStage::TessellationControl;
reject([&] { ValidateBdaTarget(program, target); }, "barrier-safe");
ValidateBdaTarget(program, target);
program.Resources().stage = IrShaderStage::Compute;
RecompileRequest request{};
request.target.bdaAbiVersion = BdaAbi::Version;
Expand Down
12 changes: 10 additions & 2 deletions core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,7 @@ void ShaderStageTests() {
queue.context[0x2d5] = 0x200d;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "Patch topology and HS_EN disagree");
queue.userConfig[0x242] = 9;
queue.userConfig[0x25b] = 0x1fe55;
queue.context[0x2d6] = 0xc355;
queue.context[0x2db] = 0x40049;
queue.context[0x286] = 0x42800000;
Expand All @@ -765,7 +766,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 && tessellation.patchesPerGroup == 85, "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 && tessellation.primitiveGroupSize == 85 && !tessellation.breakAtInstance, "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{0x40045u, "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);
Expand All @@ -775,9 +776,16 @@ void ShaderStageTests() {
queue.context[0x2d6] = 0x1043f;
queue.context[0x2db] = 0x4006a;
queue.context[0x286] = 0x41100000;
queue.userConfig[0x25b] = 0x5fe3f;
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 && quads->patchesPerGroup == 63, "counter-clockwise quad patches with HOS levels [1, 9] and 63 patches per group 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 && quads->primitiveGroupSize == 63 && quads->breakAtInstance, "counter-clockwise quad patches with HOS levels [1, 9], 63 patches per group and BREAK_WAVE_AT_EOI decoded differently");
queue.userConfig[0x25b] = 0x8040;
const auto shared = AgcDriver::Graphics::DecodeState(queue).stages.tessellation;
Require(shared && shared->primitiveGroupSize == 64 && !shared->breakAtInstance, "GE_CNTL 0x8040 did not decode as 64 primitives per group with BREAK_WAVE_AT_EOI clear");
queue.userConfig.erase(0x25b);
const auto missing = AgcDriver::Graphics::DecodeState(queue).stages.tessellation;
Require(missing->primitiveGroupSize == 0 && !missing->breakAtInstance, "a draw without GE_CNTL decoded a primitive group size or BREAK_WAVE_AT_EOI");
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"}}) {
Expand Down
48 changes: 44 additions & 4 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,25 @@ void verifyHalfWaveReduction() {
};
require(reductions(vertex(scan(0xffffffffu, 0x26000000u, 0x83800000u), 32u), spv::OpGroupNonUniformUMin) == 1u, "half-wave reduction: the wave64 vertex UMin scan, whose entry EXEC is every invocation, did not read lane 31 as a subgroup UMin on 32 lanes");
require(halves(vertex(scan(0x80000000u, 0x24000000u, 0x84000000u), 64u), spv::OpGroupNonUniformSMax, 0x80000000u), "half-wave reduction: the wave64 vertex SMax scan did not read lanes 31 and 63 as reductions of host invocations 0-31 and 32-63 on 64 lanes");
const auto domain = [](std::vector<std::uint32_t> code, std::uint32_t subgroupSize) {
*std::find(code.begin(), code.end(), 0xf800180fu) = 0xf80008cfu;
*std::find(code.begin(), code.end(), 0x7e160f00u) = 0x7e160f01u;
RecompileRequest request{};
request.shader = {ShaderStage::TessellationEvaluation, 0x10000u, code, 0, {}};
request.context.waveSize = 64;
request.context.vertex = ShaderVertexStageInfo{};
request.target.vulkanVersion = 0x00401000u;
request.target.spirvVersion = 0x00010300u;
request.target.subgroupSize = subgroupSize;
request.target.supportedCapabilities = capabilities;
request.target.fragmentShaderBarycentricEnabled = false;
request.layout.pushConstantSizeBytes = 128;
request.graphics = GraphicsCompileContext{0u, {}, std::nullopt, TessellationConfiguration{3u, 3u, 1u, 2u, 2u}, {}};
request.useCache = false;
return Recompile(request).spirv;
};
require(reductions(domain(scan(0xffffffffu, 0x26000000u, 0x83800000u), 32u), spv::OpGroupNonUniformUMin) == 1u, "half-wave reduction: the wave64 domain UMin scan under s_orn2_saveexec_b64 vcc, exec did not read lane 31 as a subgroup UMin on 32 lanes");
require(halves(domain(scan(0u, 0x38000000u, 0x88000000u), 64u), spv::OpGroupNonUniformBitwiseOr, 0u), "half-wave reduction: the wave64 domain OR scan did not read lanes 31 and 63 as reductions of host invocations 0-31 and 32-63 on 64 lanes");
const auto floatScan = [&scan](std::uint32_t identity, std::uint32_t vector, std::uint32_t join) {
auto code = scan(identity, vector, 0u);
code[23] = join;
Expand Down Expand Up @@ -1432,6 +1451,7 @@ void verifyHalfWaveReduction() {
auto overwritten = umin;
overwritten.insert(overwritten.begin() + 17, {0x7ef80500u, 0x7e008514u});
expectFailure([&] { static_cast<void>(pixel(overwritten, 32u, true)); }, lane63, "half-wave reduction: a scan of window registers read lane 63 after a v_movreld_b32 at a runtime M0 that may overwrite them");
require(reductions(domain(divergent, 32u), spv::OpGroupNonUniformUMin) == 1u, "half-wave reduction: domain keys masked inside a divergent branch did not read lane 31 as a subgroup UMin on 32 lanes");
const char* arithmetic = "v_readlane_b32 of lane 31 of a wave64 half-wave reduction scan needs subgroup arithmetic";
expectFailure([&] { static_cast<void>(pixel(umin, 32u, false)); }, arithmetic, "half-wave reduction: a device without subgroup arithmetic read lane 31 on 32 lanes");
expectFailure([&] { static_cast<void>(pixel(umin, 64u, false)); }, arithmetic, "half-wave reduction: a device without subgroup arithmetic read lane 31 on 64 lanes");
Expand Down Expand Up @@ -1689,10 +1709,10 @@ void verifyMeshConfiguration() {
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}, {}};
hull.graphics = GraphicsCompileContext{0u, {}, std::nullopt, TessellationConfiguration{4u, 4u, 2u, 2u, 3u, 63u, 63u, true}, {}};
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");
require(replayed != nullptr && replayed->domain == 2u && replayed->outputTopology == 3u && replayed->patchesPerGroup == 63u && replayed->primitiveGroupSize == 63u && replayed->breakAtInstance, "the quad tessellation configuration, its patches per group, its primitive group size or BREAK_WAVE_AT_EOI were lost in serialization");
auto grouped = hull;
grouped.graphics->tessellation->patchesPerGroup = 62u;
for (const auto stage : {ShaderStage::Local, ShaderStage::TessellationControl, ShaderStage::TessellationEvaluation}) {
Expand All @@ -1703,6 +1723,26 @@ void verifyMeshConfiguration() {
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");
}
auto primitiveGrouped = hull;
primitiveGrouped.graphics->tessellation->primitiveGroupSize = 126u;
for (const auto stage : {ShaderStage::Local, ShaderStage::TessellationControl, ShaderStage::TessellationEvaluation}) {
hull.shader.stage = stage;
primitiveGrouped.shader.stage = stage;
RecompileCacheKey::Build(hull, key);
const auto patchKey = key;
RecompileCacheKey::Build(primitiveGrouped, key);
require((key != patchKey) == (stage != ShaderStage::Local), "only the hull and domain cache keys may depend on the primitive group size, which rebuilds their relative patch index");
}
auto shared = hull;
shared.graphics->tessellation->breakAtInstance = false;
for (const auto stage : {ShaderStage::Local, ShaderStage::TessellationControl, ShaderStage::TessellationEvaluation}) {
hull.shader.stage = stage;
shared.shader.stage = stage;
RecompileCacheKey::Build(hull, key);
const auto patchKey = key;
RecompileCacheKey::Build(shared, key);
require((key != patchKey) == (stage != ShaderStage::Local), "only the hull and domain cache keys may depend on BREAK_WAVE_AT_EOI, which decides whether their relative patch index can be rebuilt");
}
const std::array<std::uint32_t, 8> users{};
request.context.waveSize = 32u;
request.context.userData = users;
Expand Down Expand Up @@ -1844,14 +1884,14 @@ void verifyPixelRequestSerialization() {
minimal.context.pixel->rasterPrimitive = RasterPrimitive::Rectangle;
minimal.context.pixel->layerExported = true;
const auto encoded = serializer.Serialize(minimal);
require(requestPrefix(encoded, 8u) == "NVNQQRcAAAA=", "new requests did not use serialization version 23");
require(requestPrefix(encoded, 8u) == "NVNQQRgAAAA=", "new requests did not use serialization version 24");
require(serializer.Deserialize(encoded).request.context.pixel->rasterPrimitive == RasterPrimitive::Rectangle, "the pixel primitive class did not round-trip");
require(serializer.Deserialize(encoded).request.context.pixel->layerExported, "the pixel layer export did not round-trip");
constexpr std::size_t mappingOffset = 8u + 37u + 18u + 163u;
for (std::size_t bytes = 0; bytes < 22u; ++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, primitive class or layer export flag was accepted");
}
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRgAAAA="}) {
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRkAAAA="}) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
}
static constexpr std::string_view version18Request{
Expand Down
Loading
Loading