mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-06 13:30:00 +00:00
fix(agc): render mesh layers within device limits
This commit is contained in:
1 parent
a9708c3a6d
commit
218ca18e42
16 files changed
+189
-18
No files matched your search
@@ -52,9 +52,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
explicit PipelineSpecialization(const ShaderRecompiler::RecompileResult& shader) {
|
||||
explicit PipelineSpecialization(const ShaderRecompiler::RecompileResult& shader, std::uint32_t layerBase = 0u, std::uint32_t layerCount = 0xffffffffu) {
|
||||
values = shader.specialization;
|
||||
for (auto& constant : values) {
|
||||
if (constant.id == ShaderRecompiler::PipelineSpecialization::MeshLayerBase) constant.value = layerBase;
|
||||
if (constant.id == ShaderRecompiler::PipelineSpecialization::MeshLayerCount) constant.value = layerCount;
|
||||
}
|
||||
Validate(shader);
|
||||
describe(shader.specialization);
|
||||
describe(values);
|
||||
}
|
||||
|
||||
PipelineSpecialization(const ShaderRecompiler::RecompileResult& shader, std::span<const ShaderRecompiler::PipelineSpecializationConstant> replaced) : values(shader.specialization) {
|
||||
|
||||
@@ -146,6 +146,8 @@ struct State {
|
||||
std::uint32_t reversedBlend = 0;
|
||||
VkExtent2D renderExtent;
|
||||
std::uint32_t renderLayers = 1;
|
||||
std::uint32_t meshLayerBase = 0;
|
||||
std::uint32_t meshLayerCount = 0xffffffffu;
|
||||
std::uint32_t layerExports = 0;
|
||||
VkPrimitiveTopology topology;
|
||||
bool primitiveRestart = false;
|
||||
|
||||
@@ -3748,6 +3748,28 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
|
||||
drawTessellationGeometry(context, state, draw, shaders, snapshots, trace);
|
||||
return;
|
||||
}
|
||||
if (state.stages.mesh && state.layerExports != 0u && state.renderLayers > context.meshLimits.maxMeshOutputLayers) {
|
||||
const auto limit = context.meshLimits.maxMeshOutputLayers;
|
||||
Require(limit != 0u && !state.depth, "mesh layer windows require a nonzero output limit and color-only layered rendering");
|
||||
Require(std::ranges::all_of(shaders, [](const CompiledShader& shader) { return shader.program != nullptr && shader.program->layerReplaySafe; }), "mesh layer windows refuse shaders with guest memory writes, clocks or unproven side effects");
|
||||
if (recipeOut != nullptr) recipeOut->reset();
|
||||
for (std::uint32_t base = 0; base < state.renderLayers;) {
|
||||
auto window = state;
|
||||
window.renderLayers = std::min(limit, state.renderLayers - base);
|
||||
window.meshLayerBase = base;
|
||||
window.meshLayerCount = window.renderLayers;
|
||||
for (auto& color : window.colors) {
|
||||
Require(color.layers == state.renderLayers, "mesh layer window targets have different layer counts");
|
||||
if (color.depth > 1u) color.depthSlice += base;
|
||||
else color.arraySlice += base;
|
||||
color.layers = window.renderLayers;
|
||||
}
|
||||
if (!window.colors.empty()) window.color = window.colors.front();
|
||||
Draw(context, window, draw, shaders, snapshots, nullptr, trace);
|
||||
base += window.renderLayers;
|
||||
}
|
||||
return;
|
||||
}
|
||||
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
|
||||
PreparedDraw prepared(profile);
|
||||
prepared.recipeOut = recipeOut;
|
||||
|
||||
@@ -340,6 +340,8 @@ bool libraryKeys(PipelineLibraryKeys& keys, const Context& context, const State&
|
||||
appendKey(key, identity.low);
|
||||
appendKey(key, identity.high);
|
||||
appendKey(key, shader.pushConstantOffset);
|
||||
appendKey(key, state.meshLayerBase);
|
||||
appendKey(key, state.meshLayerCount);
|
||||
if (resources.split) {
|
||||
auto& sets = shader.stage == Stage::Fragment ? keys.fragmentSets : keys.preRasterizationSets;
|
||||
for (const auto& binding : shader.program->bindings) {
|
||||
@@ -509,7 +511,7 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
|
||||
stages[i].module = _modules[i];
|
||||
stages[i].pName = "main";
|
||||
if (shaders[i].stage == ShaderRecompiler::ShaderStage::Mesh && context.meshWave32 && shader.hostSubgroupSize == 32u) stages[i].pNext = &meshSubgroup;
|
||||
specializations.emplace_back(shader);
|
||||
specializations.emplace_back(shader, state.meshLayerBase, state.meshLayerCount);
|
||||
stages[i].pSpecializationInfo = specializations.back().Info();
|
||||
}
|
||||
if (std::ranges::any_of(shaders, [](const CompiledShader& shader) { return shader.program != nullptr && !shader.program->stencilReferences.empty(); })) {
|
||||
@@ -1016,6 +1018,8 @@ void pipelineKey(std::vector<std::byte>& key, const Context& context, const Stat
|
||||
// Where the stage's push constants sit in the block (AssemblePushConstants).
|
||||
append(key, shader.pushConstantOffset);
|
||||
}
|
||||
append(key, state.meshLayerBase);
|
||||
append(key, state.meshLayerCount);
|
||||
append(key, PushConstantStages(shaders));
|
||||
append(key, input.bindings.size());
|
||||
for (const auto& binding : input.bindings) {
|
||||
|
||||
@@ -180,6 +180,8 @@ void encodeState(Writer& writer, const State& state) {
|
||||
writer.U32(static_cast<std::uint32_t>(state.stages.path));
|
||||
writer.U32(state.stages.vertexWaveSize);
|
||||
writer.U32(state.stages.fragmentWaveSize);
|
||||
writer.U32(state.meshLayerBase);
|
||||
writer.U32(state.meshLayerCount);
|
||||
encodeOptional(writer, state.stages.mesh, encodeMesh);
|
||||
encodeOptional(writer, state.stages.tessellation, encodeTessellation);
|
||||
writer.U32(state.stages.hullWaveSize);
|
||||
@@ -244,6 +246,8 @@ void decodeState(Reader& reader, State& state) {
|
||||
state.stages.path = static_cast<ShaderPath>(reader.U32());
|
||||
state.stages.vertexWaveSize = reader.U32();
|
||||
state.stages.fragmentWaveSize = reader.U32();
|
||||
state.meshLayerBase = reader.U32();
|
||||
state.meshLayerCount = reader.U32();
|
||||
if (reader.Bool()) decodeMesh(reader, state.stages.mesh.emplace());
|
||||
if (reader.Bool()) decodeTessellation(reader, state.stages.tessellation.emplace());
|
||||
state.stages.hullWaveSize = reader.U32();
|
||||
@@ -1518,6 +1522,8 @@ State CanonicalPipelineState(const State& state) {
|
||||
canonical.stages.path = state.stages.path;
|
||||
canonical.stages.vertexWaveSize = state.stages.vertexWaveSize;
|
||||
canonical.stages.fragmentWaveSize = state.stages.fragmentWaveSize;
|
||||
canonical.meshLayerBase = state.meshLayerBase;
|
||||
canonical.meshLayerCount = state.meshLayerCount;
|
||||
canonical.stages.mesh = state.stages.mesh;
|
||||
canonical.stages.tessellation = state.stages.tessellation;
|
||||
canonical.stages.hullWaveSize = state.stages.hullWaveSize;
|
||||
|
||||
@@ -1737,5 +1737,30 @@ int main() {
|
||||
return small.removedSelects == 500u && smallKept == 500u && large.removedSelects == 4000u && largeKept == 4000u && small.steps != 0u && large.steps <= 9u * small.steps && large.steps < 90'000u;
|
||||
});
|
||||
|
||||
passed &= run("layer replay permits local LDS but rejects GDS access", [] {
|
||||
Builder b;
|
||||
b.program.Resources().stage = IrShaderStage::Mesh;
|
||||
MemoryInfo memory{};
|
||||
memory.kind = ResourceKind::Lds;
|
||||
b.program.Resources().memoryInfo.push_back(memory);
|
||||
b.emit(IrOpcode::StoreShared, IrType::Void, {&b.constant(0u), &b.constant(1u)});
|
||||
if (!LayerReplaySafe(b.program)) return false;
|
||||
b.program.Resources().memoryInfo.front().kind = ResourceKind::Gds;
|
||||
return !LayerReplaySafe(b.program);
|
||||
});
|
||||
|
||||
passed &= run("layer replay rejects guest buffer stores and shader clocks", [] {
|
||||
Builder written;
|
||||
auto& buffer = written.emit(IrOpcode::GetBufferResource, IrType::BufferResource, {});
|
||||
written.emit(IrOpcode::StoreBuffer, IrType::Void, {&buffer, &written.constant(0u), &written.constant(1u)});
|
||||
if (LayerReplaySafe(written.program)) return false;
|
||||
for (const auto opcode : {IrOpcode::ShaderClock, IrOpcode::RealtimeClock}) {
|
||||
Builder clock;
|
||||
clock.emit(opcode, IrType::U64, {});
|
||||
if (LayerReplaySafe(clock.program)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return passed ? 0 : 1;
|
||||
}
|
||||
@@ -174,6 +174,7 @@ void requireSameArtifact(const CompiledShaderArtifact& left, const CompiledShade
|
||||
require(left.stencilReferences == right.stencilReferences, prefix + "stencil reference set differs");
|
||||
require(left.stencilReplaySafe == right.stencilReplaySafe, prefix + "stencil replay proof differs");
|
||||
require(left.bdaWrites == right.bdaWrites, prefix + "BDA write flag differs");
|
||||
require(left.layerReplaySafe == right.layerReplaySafe, prefix + "layer replay proof differs");
|
||||
require(left.stencilReferenceDword == right.stencilReferenceDword, prefix + "stencil replay push offset differs");
|
||||
require(left.fragmentParameters.size() == right.fragmentParameters.size(), prefix + "fragment parameter count differs");
|
||||
for (std::size_t i = 0; i < left.fragmentParameters.size(); ++i) {
|
||||
@@ -287,6 +288,7 @@ RecompileResult sampleResult() {
|
||||
result.stencilReferences = {0u, 17u, 255u};
|
||||
result.stencilReplaySafe = true;
|
||||
result.bdaWrites = true;
|
||||
result.layerReplaySafe = true;
|
||||
result.stencilReferenceDword = 23u;
|
||||
result.poisonedSrtReads = 3;
|
||||
result.shaderDataUserWords = {2u, 0xffffffffu, 9u};
|
||||
|
||||
@@ -33,10 +33,10 @@ using AgcDriver::Graphics::ColorTileMode;
|
||||
using AgcDriver::Graphics::Require;
|
||||
using ShaderRecompiler::ShaderStage;
|
||||
|
||||
constexpr std::uint32_t Width = 96;
|
||||
constexpr std::uint32_t Width = 99;
|
||||
constexpr std::uint32_t Height = 16;
|
||||
constexpr std::uint32_t Slices = 32;
|
||||
constexpr std::uint32_t FirstSlice = 0;
|
||||
constexpr std::uint32_t Slices = 34;
|
||||
constexpr std::uint32_t FirstSlice = 1;
|
||||
constexpr std::uint32_t Layers = Slices - FirstSlice;
|
||||
constexpr std::uint32_t BandWidth = Width / Layers;
|
||||
constexpr std::uint8_t Kept = 0x40;
|
||||
@@ -184,6 +184,17 @@ void Run(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarget
|
||||
if (mesh) geometry.graphics = ShaderRecompiler::GraphicsCompileContext{0, {}, Subgroup, std::nullopt, {draw.indexAddress, draw.indexCount, draw.indexSize, draw.instanceCount}};
|
||||
geometry.useCache = false;
|
||||
const auto geometryResult = ShaderRecompiler::Recompile(geometry);
|
||||
if (mesh) {
|
||||
for (const auto required : {ShaderRecompiler::PipelineSpecialization::MeshLayerBase, ShaderRecompiler::PipelineSpecialization::MeshLayerCount}) {
|
||||
bool found = false;
|
||||
for (std::size_t at = 5; at < geometryResult.spirv.size();) {
|
||||
const auto length = geometryResult.spirv[at] >> 16u;
|
||||
if ((geometryResult.spirv[at] & 0xffffu) == 71u && length == 4u && geometryResult.spirv[at + 2u] == 1u && geometryResult.spirv[at + 3u] == required) found = true;
|
||||
at += length;
|
||||
}
|
||||
Require(found, "mesh layer specialization was frozen: " + std::to_string(required));
|
||||
}
|
||||
}
|
||||
const auto geometryPush = static_cast<std::uint32_t>(geometryResult.pushConstants.size());
|
||||
|
||||
ShaderRecompiler::ShaderPixelStageInfo pixel{};
|
||||
@@ -229,12 +240,7 @@ void Run(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarget
|
||||
state.blendConstants = {};
|
||||
if (mesh) {
|
||||
Require(DeclaresLayer(geometryResult.spirv), "the mesh program declares no Layer output");
|
||||
VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES};
|
||||
subgroup.subgroupSize = target.subgroupSize;
|
||||
subgroup.supportedStages = VK_SHADER_STAGE_ALL;
|
||||
subgroup.supportedOperations = 0xffu;
|
||||
subgroup.quadOperationsInAllStages = VK_TRUE;
|
||||
AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false, false, false, false, false, false, false, false, true);
|
||||
|
||||
}
|
||||
device.Draw(state, draw, shaders);
|
||||
device.WaitIdle();
|
||||
@@ -394,6 +400,7 @@ int main(int argc, char** argv) {
|
||||
MipTargets(*device);
|
||||
return 0;
|
||||
}
|
||||
std::printf("graphics pipeline libraries: %s\n", device->GraphicsPipelineLibraries() ? "enabled" : "disabled");
|
||||
BuildBands();
|
||||
const GuestVolume volume((VolumeTarget(nullptr).bytes + 65535u) & ~std::size_t{65535u});
|
||||
const auto color = VolumeTarget(volume.Data());
|
||||
@@ -445,6 +452,10 @@ int main(int argc, char** argv) {
|
||||
const auto meshColor = VolumeTarget(meshVolume.Data());
|
||||
Run(*device, meshColor, true, PixelCode, false);
|
||||
Check(meshVolume.Data(), meshColor, "mesh draw", WhiteBands);
|
||||
const GuestVolume meshFieldVolume((VolumeTarget(nullptr).bytes + 65535u) & ~std::size_t{65535u});
|
||||
const auto meshFieldColor = VolumeTarget(meshFieldVolume.Data());
|
||||
Run(*device, meshFieldColor, true, LayerFieldPixelCode, true);
|
||||
Check(meshFieldVolume.Data(), meshFieldColor, "mesh fragment layer input", LayerFieldBands);
|
||||
}
|
||||
else std::puts("mesh case skipped, the device has no VK_EXT_mesh_shader");
|
||||
std::puts("layered volume draw tests passed");
|
||||
|
||||
@@ -33,6 +33,8 @@ inline constexpr std::uint32_t RasterPrimitive = 8233u;
|
||||
inline constexpr std::uint32_t LayerExported = 8234u;
|
||||
inline constexpr std::uint32_t StencilReferenceOffset = 8235u;
|
||||
inline constexpr std::uint32_t ReversedBlend = 8236u;
|
||||
inline constexpr std::uint32_t MeshLayerBase = 8237u;
|
||||
inline constexpr std::uint32_t MeshLayerCount = 8238u;
|
||||
inline constexpr std::uint32_t DepthRemapBase = 8240u;
|
||||
inline constexpr std::uint32_t DepthRemapWords = 1u;
|
||||
inline constexpr std::uint32_t WorkgroupOffsetBase = 8241u;
|
||||
|
||||
@@ -703,6 +703,7 @@ CompiledVariant emitVariant(const RecompileRequest& request, IrProgram& program)
|
||||
}
|
||||
result.registrationModule = registration;
|
||||
if (request.shader.stage == ShaderStage::Mesh && request.graphics) result.meshBarycentrics = request.graphics->meshBarycentrics;
|
||||
result.layerReplaySafe = LayerReplaySafe(program);
|
||||
result.stencilReplaySafe = (request.shader.stage == ShaderStage::Vertex || stencilReplay) && StencilReplaySafe(program);
|
||||
result.bdaWrites = program.Info().bdaWrites;
|
||||
if (stencilReplay) {
|
||||
@@ -781,6 +782,7 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
||||
|
||||
struct SpecializedModule {
|
||||
std::uint64_t specializationId = 0;
|
||||
std::vector<PipelineSpecializationConstant> specialization;
|
||||
std::vector<std::uint32_t> bindings;
|
||||
bool pushData = false;
|
||||
SpirvContentHash hash;
|
||||
@@ -820,6 +822,11 @@ std::vector<std::uint32_t> specializeWords(const CompiledShaderArtifact& artifac
|
||||
}
|
||||
const auto found = supplied.find(words[cursor + 3u]);
|
||||
if (found == supplied.end()) throw std::runtime_error("missing prepared specialization value");
|
||||
if (found->first == PipelineSpecialization::MeshLayerBase || found->first == PipelineSpecialization::MeshLayerCount) {
|
||||
retained.insert(words[cursor + 1u]);
|
||||
cursor += count;
|
||||
continue;
|
||||
}
|
||||
auto value = found->second;
|
||||
if (!artifact.stencilReferences.empty()) {
|
||||
if (found->first == PipelineSpecialization::StencilReferenceOffset) replaySlotBytes = value / PushData::DwordCount * NativePushSlotSize;
|
||||
@@ -834,9 +841,7 @@ std::vector<std::uint32_t> specializeWords(const CompiledShaderArtifact& artifac
|
||||
for (std::size_t cursor = 5; cursor < words.size();) {
|
||||
const auto count = words[cursor] >> 16u;
|
||||
const auto op = static_cast<spv::Op>(words[cursor] & 0xffffu);
|
||||
if (op == spv::OpSpecConstant && count == 4u && retained.contains(words[cursor + 2u])) {
|
||||
materialized.insert(materialized.end(), words.begin() + cursor, words.begin() + cursor + count);
|
||||
} else if (op == spv::OpDecorate && count == 4u && words[cursor + 2u] == spv::DecorationSpecId && retained.contains(words[cursor + 1u])) {
|
||||
if ((op == spv::OpSpecConstant && count == 4u && retained.contains(words[cursor + 2u])) || (op == spv::OpDecorate && count == 4u && words[cursor + 2u] == spv::DecorationSpecId && retained.contains(words[cursor + 1u]))) {
|
||||
materialized.insert(materialized.end(), words.begin() + cursor, words.begin() + cursor + count);
|
||||
} else if (op == spv::OpSpecConstant) {
|
||||
const auto found = count == 4u ? values.find(words[cursor + 2u]) : values.end();
|
||||
@@ -853,7 +858,7 @@ std::vector<std::uint32_t> specializeWords(const CompiledShaderArtifact& artifac
|
||||
}
|
||||
materialized = SpecializeFragmentOutputs(SpecializeSpirv(materialized), constants, specializedIds);
|
||||
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
||||
materialized = ValidateAndOptimizeSpirv(materialized, target.vulkanVersion, target.spirvVersion, target.nonConstantImageOffsets, true, true);
|
||||
materialized = ValidateAndOptimizeSpirv(materialized, target.vulkanVersion, target.spirvVersion, target.nonConstantImageOffsets, true, retained.empty());
|
||||
#endif
|
||||
std::map<std::uint32_t, std::uint32_t> descriptorVariables;
|
||||
std::set<std::uint32_t> usedDescriptors;
|
||||
@@ -894,6 +899,8 @@ std::vector<std::uint32_t> specializeWords(const CompiledShaderArtifact& artifac
|
||||
SpecializedSpirv buildSpecializedModule(const CompiledShaderArtifact& artifact, std::span<const std::uint32_t> classes, std::span<const PipelineSpecializationConstant> constants, const SpirvTarget& target) {
|
||||
auto materialized = specializeWords(artifact, classes, constants, target);
|
||||
auto module = std::make_shared<SpecializedModule>();
|
||||
std::map<std::uint32_t, std::uint32_t> supplied;
|
||||
for (const auto& constant : constants) supplied.emplace(constant.id, constant.value);
|
||||
std::map<std::uint32_t, std::uint32_t> bindingNumbers;
|
||||
std::unordered_set<std::uint32_t> functionLdsBuffer;
|
||||
for (std::size_t cursor = 5; cursor < materialized.size();) {
|
||||
@@ -901,6 +908,10 @@ SpecializedSpirv buildSpecializedModule(const CompiledShaderArtifact& artifact,
|
||||
const auto op = static_cast<spv::Op>(materialized[cursor] & 0xffffu);
|
||||
if (op == spv::OpDecorate && count == 4u && materialized[cursor + 2u] == spv::DecorationBinding) bindingNumbers.emplace(materialized[cursor + 1u], materialized[cursor + 3u]);
|
||||
if (op == spv::OpDecorate && count == 4u && materialized[cursor + 2u] == spv::DecorationDescriptorSet && (materialized[cursor + 3u] == PixelFunctionLdsDescriptorSet || materialized[cursor + 3u] == MeshWindowDescriptorSet)) functionLdsBuffer.insert(materialized[cursor + 1u]);
|
||||
if (op == spv::OpDecorate && count == 4u && materialized[cursor + 2u] == spv::DecorationSpecId) {
|
||||
const auto id = materialized[cursor + 3u];
|
||||
module->specialization.push_back({id, supplied.at(id)});
|
||||
}
|
||||
if (op == spv::OpVariable && count >= 4u && !functionLdsBuffer.contains(materialized[cursor + 2u])) {
|
||||
if (const auto found = bindingNumbers.find(materialized[cursor + 2u]); found != bindingNumbers.end()) module->bindings.push_back(found->second);
|
||||
module->pushData |= materialized[cursor + 3u] == spv::StorageClassPushConstant;
|
||||
@@ -1154,6 +1165,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
||||
.stencilReferences = artifact.stencilReferences,
|
||||
.stencilReplaySafe = artifact.stencilReplaySafe,
|
||||
.bdaWrites = artifact.bdaWrites,
|
||||
.layerReplaySafe = artifact.layerReplaySafe,
|
||||
.stencilReferenceDword = artifact.stencilReferenceDword,
|
||||
.variantId = artifact.variantId
|
||||
};
|
||||
@@ -1217,7 +1229,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
||||
if (artifact.registrationModule) runtimeSelectors.at(input.location / RuntimeAbi::VertexSelectorsPerDword) |= (selectors & 0xfffu) << (input.location % RuntimeAbi::VertexSelectorsPerDword * 16u);
|
||||
}
|
||||
}
|
||||
if (!plan->bindings.specialization.empty() || variant.bindings.layout.UsesPushData() || !artifact.vertexInputPatches.empty() || request.context.pixel || depthRemap) {
|
||||
if (!plan->bindings.specialization.empty() || variant.bindings.layout.UsesPushData() || !artifact.vertexInputPatches.empty() || request.context.pixel || depthRemap || request.shader.stage == ShaderStage::Mesh) {
|
||||
{
|
||||
std::shared_lock lock(plan->mutex);
|
||||
if (const auto found = plan->modules.find(moduleKey); found != plan->modules.end()) entry = found->second;
|
||||
@@ -1229,6 +1241,8 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
||||
}
|
||||
const auto specialize = [&] {
|
||||
auto constants = plan->bindings.specialization;
|
||||
constants.push_back({PipelineSpecialization::MeshLayerBase, 0u});
|
||||
constants.push_back({PipelineSpecialization::MeshLayerCount, 0xffffffffu});
|
||||
std::size_t index = 0;
|
||||
if (variant.bindings.layout.UsesPushData()) constants.push_back({PipelineSpecialization::PushDataOffset, moduleKey[index++]});
|
||||
if (!artifact.stencilReferences.empty()) constants.push_back({PipelineSpecialization::StencilReferenceOffset, moduleKey[index++]});
|
||||
@@ -1277,6 +1291,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
||||
if (built.empty()) built = specialize().spirv;
|
||||
result.specializationId = module->specializationId;
|
||||
result.spirv = std::move(built);
|
||||
result.specialization = module->specialization;
|
||||
bindingPlan = &entry->bindings;
|
||||
} else {
|
||||
result.spirv = artifact.spirv;
|
||||
|
||||
@@ -767,6 +767,7 @@ struct CompiledShaderArtifact {
|
||||
std::vector<std::uint32_t> stencilReferences;
|
||||
bool stencilReplaySafe = false;
|
||||
bool bdaWrites = false;
|
||||
bool layerReplaySafe = false;
|
||||
std::uint32_t stencilReferenceDword = 0xffffffffu;
|
||||
std::uint64_t variantId = 0;
|
||||
};
|
||||
|
||||
@@ -571,6 +571,7 @@ void encodeArtifact(Writer& writer, const CompiledShaderArtifact& result) {
|
||||
writer.Values(std::span<const std::uint32_t>(result.stencilReferences));
|
||||
writer.Value(result.stencilReplaySafe);
|
||||
writer.Value(result.bdaWrites);
|
||||
writer.Value(result.layerReplaySafe);
|
||||
writer.Value(result.stencilReferenceDword);
|
||||
}
|
||||
|
||||
@@ -640,6 +641,7 @@ void decodeArtifact(Reader& reader, CompiledShaderArtifact& result) {
|
||||
reader.Values(result.stencilReferences);
|
||||
reader.Value(result.stencilReplaySafe);
|
||||
reader.Value(result.bdaWrites);
|
||||
reader.Value(result.layerReplaySafe);
|
||||
reader.Value(result.stencilReferenceDword);
|
||||
result.variantId = 0;
|
||||
}
|
||||
|
||||
@@ -248,6 +248,10 @@ std::uint32_t EmitBuiltinU32(SpirvEmitterState& state, StageInputKind kind, std:
|
||||
const auto bits = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpLoad, TypeI32(state), value, variable);
|
||||
state.module.AddFunction(spv::OpBitcast, TypeU32(state), bits, value);
|
||||
if (kind == StageInputKind::Layer) {
|
||||
const auto base = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::MeshLayerBase, 0u);
|
||||
return EmitBinaryU32(state, spv::OpIAdd, bits, base);
|
||||
}
|
||||
return kind == StageInputKind::SampleId ? GuestSampleIndex(state, bits) : bits;
|
||||
}
|
||||
if (kind == StageInputKind::SampleMask) {
|
||||
@@ -1451,7 +1455,19 @@ void EmitMeshEntryPoint(SpirvEmitterState& state) {
|
||||
const auto layer = MeshLoad(state, output.meshDataVariable, spv::StorageClassWorkgroup, TypeU32(state), vertex.at(state.inputInfo.vertex->mesh.provokingVertex));
|
||||
const auto pointer = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpAccessChain, TypePointer(state, spv::StorageClassOutput, TypeU32(state)), pointer, output.variableId, index, ConstantU32(state, 0u));
|
||||
state.module.AddFunction(spv::OpStore, pointer, layer);
|
||||
const auto base = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::MeshLayerBase, 0u);
|
||||
const auto count = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::MeshLayerCount, 0xffffffffu);
|
||||
const auto relative = EmitBinaryU32(state, spv::OpISub, layer, base);
|
||||
const auto inside = state.module.AllocateId();
|
||||
const auto outside = state.module.AllocateId();
|
||||
const auto selected = state.module.AllocateId();
|
||||
const auto discarded = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpULessThan, TypeBool(state), inside, relative, count);
|
||||
state.module.AddFunction(spv::OpLogicalNot, TypeBool(state), outside, inside);
|
||||
state.module.AddFunction(spv::OpSelect, TypeU32(state), selected, inside, relative, ConstantU32(state, 0u));
|
||||
state.module.AddFunction(spv::OpLogicalOr, TypeBool(state), discarded, culled, outside);
|
||||
state.module.AddFunction(spv::OpStore, MeshElement(state, state.meshCull, spv::StorageClassOutput, TypeBool(state), index), discarded);
|
||||
state.module.AddFunction(spv::OpStore, pointer, selected);
|
||||
}
|
||||
if (emulating) {
|
||||
const auto corners = state.module.Type(spv::OpTypeArray, TypeF32Vector(state, 4u), ConstantU32(state, 3u));
|
||||
|
||||
@@ -848,6 +848,61 @@ bool StencilReferenceDefinedForKeptInvocations(const IrProgram& program) {
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool LayerReplaySafe(const IrProgram& program) {
|
||||
if (program.Info().bdaWrites || program.Info().usesDma) return false;
|
||||
for (const auto& block : program.Blocks()) {
|
||||
for (const auto* instruction : block->Instructions()) {
|
||||
const auto opcode = instruction->Opcode();
|
||||
if (opcode == IrOpcode::ImageBvhIntersectRay || (BufferAccessOf(opcode) == BufferAccess::Write || BufferAccessOf(opcode) == BufferAccess::Atomic) || AddressOpcodeInfoOf(opcode).access == AddressAccess::Write || AddressOpcodeInfoOf(opcode).access == AddressAccess::Atomic || ImageOpcodeInfoOf(opcode).access == ImageAccess::Write || ImageOpcodeInfoOf(opcode).access == ImageAccess::Atomic) return false;
|
||||
if (opcode == IrOpcode::UndefU1 || opcode == IrOpcode::UndefU8 || opcode == IrOpcode::UndefU16 || opcode == IrOpcode::UndefU32 || opcode == IrOpcode::UndefU64) return false;
|
||||
if (opcode == IrOpcode::ShaderClock || opcode == IrOpcode::RealtimeClock) return false;
|
||||
if (SharedAccessOf(opcode) != SharedAccess::None) {
|
||||
const auto memory = instruction->Flags<MemoryFlags>().index;
|
||||
if (memory >= program.Resources().memoryInfo.size() || program.Resources().memoryInfo[memory].kind != ResourceKind::Lds) return false;
|
||||
continue;
|
||||
}
|
||||
if (!instruction->MayHaveSideEffects()) continue;
|
||||
switch (opcode) {
|
||||
case IrOpcode::Barrier:
|
||||
case IrOpcode::MeshAllocate:
|
||||
case IrOpcode::Branch:
|
||||
case IrOpcode::BranchConditional:
|
||||
case IrOpcode::Return:
|
||||
case IrOpcode::Unreachable:
|
||||
case IrOpcode::Discard:
|
||||
case IrOpcode::Reference:
|
||||
case IrOpcode::ReferenceU32:
|
||||
case IrOpcode::SetAttribute:
|
||||
case IrOpcode::InterpolateHostP2:
|
||||
case IrOpcode::SetRegister:
|
||||
case IrOpcode::SetThreadBitScalarRegister:
|
||||
case IrOpcode::SetScalarMaskTag:
|
||||
case IrOpcode::SetScalarRegister:
|
||||
case IrOpcode::SetVectorRegister:
|
||||
case IrOpcode::StoreVectorWindow:
|
||||
case IrOpcode::SetGotoVariable:
|
||||
case IrOpcode::SetScc:
|
||||
case IrOpcode::SetExec:
|
||||
case IrOpcode::SetExecLo:
|
||||
case IrOpcode::SetExecHi:
|
||||
case IrOpcode::SetVcc:
|
||||
case IrOpcode::SetVccLo:
|
||||
case IrOpcode::SetVccHi:
|
||||
case IrOpcode::SetM0:
|
||||
case IrOpcode::Waitcnt:
|
||||
case IrOpcode::InstPrefetch:
|
||||
break;
|
||||
case IrOpcode::Sendmsg:
|
||||
if (instruction->ArgumentCount() != 0u) return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StencilReplaySafe(const IrProgram& program) {
|
||||
if (program.Info().sharedMemoryBytes != 0u || program.Info().bdaWrites || (program.Info().usesFaultBuffer && program.Resources().guardedSrtSlots.empty())) return false;
|
||||
if (program.Info().usesFaultBuffer && !FaultControlFlowAcyclic(program)) return false;
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ShaderRecompiler {
|
||||
|
||||
[[nodiscard]] std::vector<std::uint32_t> StencilReferenceValues(const IrProgram& program);
|
||||
[[nodiscard]] std::vector<std::uint32_t> StencilReferenceValues(const IrProgram& program, std::uint32_t shift);
|
||||
[[nodiscard]] bool LayerReplaySafe(const IrProgram& program);
|
||||
[[nodiscard]] bool StencilReplaySafe(const IrProgram& program);
|
||||
[[nodiscard]] bool StencilReferenceDefinedForKeptInvocations(const IrProgram& program);
|
||||
|
||||
|
||||
@@ -334,6 +334,8 @@
|
||||
- [Adaptive staging](../../../core/libs/prx/libSceAgcDriver/Graphics/include/AdaptiveStaging.hpp) (libSceAgcDriver): whether a staged element pays for its two copies is decided by probing the frame GPU time, not from the element's accesses. The probe is measured on one title (PPSA03912, NVIDIA on Linux). It judges by the whole frame's GPU time, so a scene whose GPU load moves by itself can keep the slower mode until the next probe, 20 to 160 s later, and each probe runs a few frames in the slower mode.
|
||||
- [WriteChanged](../../../core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp) (AGC driver) - a partly changed 16-byte write-back chunk is stored with one byte store per changed byte when at most `ChangedByteStoreLimit()` bytes changed, and with `MASKMOVDQU` otherwise. The limit is 16 (byte stores at every density) on a CPU whose vendor string is `AuthenticAMD` and 4 on any other. The cost of `MASKMOVDQU` is measured only on a Ryzen 9 7950X, where byte stores win up to about 10 changed bytes in a microbenchmark but PPSA16064 lost its gain when `MASKMOVDQU` was kept from 10 changed bytes on, and on a Core i9-14900KF, where byte stores win only at 3 or 4 changed bytes and are up to 2.6 times slower for dense chunks (#8930). Other AMD and Intel generations and other vendors are not measured.
|
||||
- [Host report](../../../core/libs/prx/libSceAgcDriver/Execution/src/HostReportVulkan.cpp) (libSceAgcDriver, `ANYPS5_HOST_REPORT=1`): run on Linux with a software Vulkan device only. Its Windows branches (`RtlGetVersion`, `GlobalMemoryStatusEx`, `USERNAME`, `COMPUTERNAME`, `USERPROFILE`) and its macOS branches are not run, and no hardware GPU's output is checked. The list of required Vulkan items in `HostReportVulkan.cpp` mirrors the checks of `VulkanDevice.cpp` by hand: a new hard requirement added there must be added to the list, or the start fails with the older single-item message. Closes when: the report is read on Windows, macOS and a hardware GPU.
|
||||
- [Draw](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) partitions layered mesh color draws into attachment windows within the host `maxMeshOutputLayers` limit, remaps output layers and reconstructs the guest layer for fragment ancillary input. Replay requires shaders without guest memory writes, clocks, GDS access or unproven side effects; those draws still throw until mesh output can be captured once and rasterized separately. Layered depth rendering remains unsupported. Vulkan layer-limit semantics follow [Layer](https://docs.vulkan.org/refpages/latest/refpages/source/Layer.html); PS5 console behavior and native title results have not been validated for this path.
|
||||
|
||||
|
||||
### Opt-in draw paths
|
||||
|
||||
|
||||
Reference in new issue
Block a user