mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 17:13:13 -05:00
Two different vertex programs that compute the same position give bit-identical depth on the GPU, which a depth pre-pass followed by an equal-or-greater redraw relies on. Without Invariant, a Vulkan driver may compile the same position arithmetic differently in each shader (for example contracting into an FMA in only one), so the redraw fails the depth test. Decorate the gl_PerVertex Position member and the mesh shader's Position output Invariant, and check both in a new recompiler test. AI-assisted: yes
90 lines
3.8 KiB
C++
90 lines
3.8 KiB
C++
#include "Recompiler.hpp"
|
|
#include <spirv/unified1/spirv.hpp>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
void requireInvariantPositions(const ShaderRecompiler::RecompileResult& result, const char* what) {
|
|
constexpr std::uint32_t Variable = ~0u;
|
|
const auto& words = result.spirv.Words();
|
|
std::set<std::pair<std::uint32_t, std::uint32_t>> positions;
|
|
std::set<std::pair<std::uint32_t, std::uint32_t>> invariant;
|
|
for (std::size_t at = 5; at < words.size() && (words[at] >> 16u) != 0; at += words[at] >> 16u) {
|
|
const auto op = static_cast<spv::Op>(words[at] & 0xffffu);
|
|
const auto count = words[at] >> 16u;
|
|
if (op == spv::OpMemberDecorate && count >= 4) {
|
|
if (words[at + 3] == spv::DecorationBuiltIn && count == 5 && words[at + 4] == spv::BuiltInPosition) positions.emplace(words[at + 1], words[at + 2]);
|
|
if (words[at + 3] == spv::DecorationInvariant) invariant.emplace(words[at + 1], words[at + 2]);
|
|
}
|
|
if (op == spv::OpDecorate && count >= 3) {
|
|
if (words[at + 2] == spv::DecorationBuiltIn && count == 4 && words[at + 3] == spv::BuiltInPosition) positions.emplace(words[at + 1], Variable);
|
|
if (words[at + 2] == spv::DecorationInvariant) invariant.emplace(words[at + 1], Variable);
|
|
}
|
|
}
|
|
if (positions.empty()) throw std::runtime_error(std::string(what) + ": the module declares no position output");
|
|
for (const auto& position : positions) {
|
|
if (!invariant.contains(position)) throw std::runtime_error(std::string(what) + ": the position output is not Invariant");
|
|
}
|
|
}
|
|
|
|
constexpr std::array<std::uint32_t, 3> PositionExport{0xf80008cfu, 0u, 0xbf810000u};
|
|
|
|
ShaderRecompiler::RecompileRequest baseRequest(ShaderRecompiler::ShaderStage stage) {
|
|
using namespace ShaderRecompiler;
|
|
RecompileRequest request{};
|
|
request.shader = {stage, 0x10000u, PositionExport, 0, {}};
|
|
request.context.waveSize = 64;
|
|
request.context.vertex = ShaderVertexStageInfo{};
|
|
request.target.vulkanVersion = 0x00401000u;
|
|
request.target.spirvVersion = 0x00010300u;
|
|
request.target.subgroupSize = 64;
|
|
request.layout.pushConstantSizeBytes = 128;
|
|
request.useCache = false;
|
|
return request;
|
|
}
|
|
|
|
void verifyVertex() {
|
|
using namespace ShaderRecompiler;
|
|
requireInvariantPositions(Recompile(baseRequest(ShaderStage::Vertex)), "vertex shader");
|
|
}
|
|
|
|
void verifyMesh() {
|
|
using namespace ShaderRecompiler;
|
|
static constexpr std::array<std::uint32_t, 1> capabilities{spv::CapabilityMeshShadingEXT};
|
|
static constexpr std::array<std::string_view, 1> extensions{"SPV_EXT_mesh_shader"};
|
|
static constexpr std::array<std::uint32_t, 8> hiddenUserWords{};
|
|
auto request = baseRequest(ShaderStage::Mesh);
|
|
request.context.userData = hiddenUserWords;
|
|
request.target.spirvVersion = 0x00010400u;
|
|
request.target.supportedCapabilities = capabilities;
|
|
request.target.supportedExtensions = extensions;
|
|
request.target.maxWorkgroupSize = {1024u, 1024u, 64u};
|
|
request.target.maxWorkgroupInvocations = 1024u;
|
|
request.target.maxWorkgroupSharedMemoryBytes = 32768u;
|
|
request.target.mesh = MeshTargetLimits{{128u, 1u, 1u}, 128u, 32768u, 256u, 256u, 128u, 32768u, 1u, 1u};
|
|
request.graphics = GraphicsCompileContext{0u, {}, MeshConfiguration{4u, 1u, 3u, 3u, 1u, 64u, 128u, 0u, 4u}, std::nullopt, {}};
|
|
requireInvariantPositions(Recompile(request), "mesh shader");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
try {
|
|
verifyVertex();
|
|
verifyMesh();
|
|
std::cout << "Vertex and mesh position outputs are Invariant\n";
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
const std::string message(error.what());
|
|
std::cerr << message.substr(0, message.find("RecompileRequest:")) << '\n';
|
|
return 1;
|
|
}
|
|
}
|