mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 17:13:13 -05:00
Merge pull request #8697 from oneandonlydean/feat/f32-denorm-preserve
feat(shader): request DenormPreserve 32 where the device preserves f32 denormals
This commit is contained in:
15 files changed
+169
-19
No files matched your search
@@ -805,6 +805,19 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_test_executable(agc_f32_denorm_preserve_tests EXCLUDE_FROM_ALL tests/F32DenormPreserve.cpp)
|
||||
target_link_libraries(agc_f32_denorm_preserve_tests PRIVATE shader_recompiler_agc_driver libc)
|
||||
set_target_properties(agc_f32_denorm_preserve_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(agc_f32_denorm_preserve_tests)
|
||||
if(BUILD_TESTING)
|
||||
set_target_properties(agc_f32_denorm_preserve_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
|
||||
add_test(NAME agc_f32_denorm_preserve COMMAND agc_f32_denorm_preserve_tests)
|
||||
set_tests_properties(agc_f32_denorm_preserve PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1")
|
||||
if(WIN32)
|
||||
set_tests_properties(agc_f32_denorm_preserve PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_test_executable(agc_spirv_branch_targets_tests EXCLUDE_FROM_ALL tests/SpirvBranchTargets.cpp)
|
||||
target_link_libraries(agc_spirv_branch_targets_tests PRIVATE shader_recompiler_agc_driver libc)
|
||||
set_target_properties(agc_spirv_branch_targets_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
|
||||
@@ -321,6 +321,7 @@ struct VulkanDevice::State {
|
||||
bool float16DenormPreserve = false;
|
||||
bool float32FlushToZeroCandidate = false;
|
||||
bool float32FlushToZero = false;
|
||||
bool float32DenormPreserve = false;
|
||||
bool probingFloatControls = false;
|
||||
bool demoteToHelperInvocation = false;
|
||||
bool earlyFragmentSampleMaskTestBeforeSampleCounting = false;
|
||||
@@ -1329,7 +1330,8 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) :
|
||||
const auto float32Flush = Float32FlushControlFor(driverId, floatControls.shaderDenormFlushToZeroFloat32, floatControls.denormBehaviorIndependence);
|
||||
state->float32FlushToZeroCandidate = float32Flush.probe && std::getenv("APS5_NO_HW_F32_FLUSH") == nullptr;
|
||||
if (state->float32FlushToZeroCandidate && float32Flush.declared) state->capabilities.push_back(spv::CapabilityDenormFlushToZero);
|
||||
if (state->float16DenormPreserve && std::find(state->capabilities.begin(), state->capabilities.end(), static_cast<std::uint32_t>(spv::CapabilityDenormPreserve)) == state->capabilities.end()) state->capabilities.push_back(spv::CapabilityDenormPreserve);
|
||||
state->float32DenormPreserve = floatControls.shaderDenormPreserveFloat32 == VK_TRUE && floatControls.denormBehaviorIndependence != VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE && std::getenv("APS5_NO_HW_F32_PRESERVE") == nullptr;
|
||||
if ((state->float16DenormPreserve || state->float32DenormPreserve) && std::find(state->capabilities.begin(), state->capabilities.end(), static_cast<std::uint32_t>(spv::CapabilityDenormPreserve)) == state->capabilities.end()) state->capabilities.push_back(spv::CapabilityDenormPreserve);
|
||||
deviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
|
||||
deviceExtensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
|
||||
state->capabilities.push_back(4448);
|
||||
@@ -3642,6 +3644,7 @@ ShaderRecompiler::SpirvTarget VulkanDevice::Target() const {
|
||||
require(state->shaderProfile != nullptr, "shader device profile is unavailable");
|
||||
auto target = state->shaderProfile->Target();
|
||||
target.float32FlushToZero = state->float32FlushToZero;
|
||||
target.float32DenormPreserve = state->float32DenormPreserve;
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "Recompiler.hpp"
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace ShaderRecompiler;
|
||||
|
||||
void require(bool condition, const std::string& message) {
|
||||
if (!condition) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
constexpr std::array<std::uint32_t, 4> CvtF16F32Store{0x7e021500u, 0xe0700000u, 0x80000100u, 0xbf810000u};
|
||||
|
||||
std::vector<std::uint32_t> recompile(bool preserve, bool flush, bool flushCapability = true) {
|
||||
const std::array<std::uint32_t, 4> capabilities{spv::CapabilityShader, spv::CapabilityGroupNonUniform, spv::CapabilityDenormPreserve, spv::CapabilityDenormFlushToZero};
|
||||
const std::array<std::uint32_t, 4> userData{0x10000000u, 0x00100000u, 0x40u, 0x00027facu};
|
||||
const std::array<std::string_view, 2> extensions{"SPV_KHR_storage_buffer_storage_class", "SPV_KHR_float_controls"};
|
||||
RecompileRequest request{};
|
||||
request.shader = {ShaderStage::Compute, 0x20000u, CvtF16F32Store, 0, {}};
|
||||
request.context.waveSize = 64;
|
||||
request.context.userDataBaseRegister = 0;
|
||||
request.context.userData = userData;
|
||||
request.context.compute = ShaderComputeStageInfo{{64u, 1u, 1u}, 0u, {false, false, false}, false, 1u};
|
||||
request.target.vulkanVersion = 0x00403000u;
|
||||
request.target.spirvVersion = 0x00010600u;
|
||||
request.target.subgroupSize = 64;
|
||||
request.target.bdaAbiVersion = 1;
|
||||
request.target.supportedCapabilities = std::span<const std::uint32_t>(capabilities).first(flushCapability ? capabilities.size() : capabilities.size() - 1);
|
||||
request.target.supportedExtensions = extensions;
|
||||
request.target.maxWorkgroupSize = {1024u, 1024u, 64u};
|
||||
request.target.maxWorkgroupInvocations = 1024;
|
||||
request.target.maxWorkgroupSharedMemoryBytes = 49152;
|
||||
request.target.float32DenormPreserve = preserve;
|
||||
request.target.float32FlushToZero = flush;
|
||||
request.layout = {0, 0, 0, 128};
|
||||
request.useCache = false;
|
||||
auto result = Recompile(request);
|
||||
require(!result.spirv.empty(), "the shader did not recompile");
|
||||
return result.spirv.Copy();
|
||||
}
|
||||
|
||||
struct Modes {
|
||||
bool preserve32 = false;
|
||||
bool flush32 = false;
|
||||
bool preserveCapability = false;
|
||||
};
|
||||
|
||||
Modes scan(std::span<const std::uint32_t> words) {
|
||||
Modes modes;
|
||||
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);
|
||||
require(count != 0 && cursor + count <= words.size(), "truncated SPIR-V instruction");
|
||||
if (op == spv::OpCapability && words[cursor + 1] == spv::CapabilityDenormPreserve) modes.preserveCapability = true;
|
||||
if (op == spv::OpExecutionMode && count == 4 && words[cursor + 3] == 32u) {
|
||||
if (words[cursor + 2] == spv::ExecutionModeDenormPreserve) modes.preserve32 = true;
|
||||
if (words[cursor + 2] == spv::ExecutionModeDenormFlushToZero) modes.flush32 = true;
|
||||
}
|
||||
cursor += count;
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
void verify() {
|
||||
const auto preserved = scan(recompile(true, false));
|
||||
require(preserved.preserve32 && preserved.preserveCapability && !preserved.flush32, "float32DenormPreserve did not emit the 32-bit DenormPreserve execution mode and capability");
|
||||
const auto plain = scan(recompile(false, false));
|
||||
require(!plain.preserve32 && !plain.flush32, "a target without float32DenormPreserve or float32FlushToZero set a 32-bit denormal execution mode");
|
||||
const auto flushed = scan(recompile(true, true));
|
||||
require(flushed.flush32 && !flushed.preserve32, "the hardware 32-bit flush did not take precedence over the preserve flag");
|
||||
const auto nativeFlush = scan(recompile(true, true, false));
|
||||
require(!nativeFlush.preserve32 && !nativeFlush.flush32, "a hardware flush without the DenormFlushToZero capability declared a 32-bit denormal execution mode");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
verify();
|
||||
} catch (const std::exception& error) {
|
||||
std::fprintf(stderr, "F32DenormPreserve: %s\n", error.what());
|
||||
return 1;
|
||||
}
|
||||
std::puts("F32DenormPreserve: the 32-bit DenormPreserve execution mode follows the target flag and yields to the hardware flush");
|
||||
return 0;
|
||||
}
|
||||
@@ -743,6 +743,7 @@ void verifyKeySensitivity() {
|
||||
changes("the narrow subgroup clock", [](SampleRequest& sample) { sample.request.target.narrowSubgroupClock = true; });
|
||||
changes("f64 denormal preservation", [](SampleRequest& sample) { sample.request.target.float64DenormPreserve = true; });
|
||||
changes("f16 denormal preservation", [](SampleRequest& sample) { sample.request.target.float16DenormPreserve = true; });
|
||||
changes("f32 denormal preservation", [](SampleRequest& sample) { sample.request.target.float32DenormPreserve = true; });
|
||||
changes("spanned waves", [](SampleRequest& sample) { sample.request.target.spannedWaves = true; });
|
||||
changes("robust buffer access 2", [](SampleRequest& sample) { sample.request.target.robustBufferAccess2 = true; });
|
||||
changes("the workgroup size limit", [](SampleRequest& sample) { sample.request.target.maxWorkgroupSize[2] = 128; });
|
||||
|
||||
@@ -4848,7 +4848,7 @@ void verifyPixelRequestSerialization() {
|
||||
minimal.context.pixel->layerExported = true;
|
||||
minimal.context.pixel->ditheredAlphaToCoverage = true;
|
||||
const auto encoded = serializer.Serialize(minimal);
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQScAAAA=", "new requests did not use serialization version 39");
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQSkAAAA=", "new requests did not use serialization version 41");
|
||||
const std::array<std::uint32_t, 2> fallbacks{0x40u, 0x1ac4u};
|
||||
auto withFallbacks = minimal;
|
||||
withFallbacks.context.linkedCallFallbacks = fallbacks;
|
||||
@@ -4966,7 +4966,7 @@ void verifyPixelRequestSerialization() {
|
||||
for (std::size_t bytes = 0; bytes < 45u; ++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, layer export, dither or front-face flag, quad pixel mask or quad sample mask was accepted");
|
||||
}
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQSgAAAA="}) {
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQSoAAAA="}) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
|
||||
}
|
||||
static constexpr std::string_view version18Request{
|
||||
@@ -5059,6 +5059,7 @@ void verifyVertexInfoVersion19() {
|
||||
auto version20Vertex = flagged;
|
||||
version20Vertex.pop_back();
|
||||
version20Vertex.pop_back();
|
||||
version20Vertex.pop_back();
|
||||
version20Vertex[4] = 20u;
|
||||
const auto replay20Vertex = serializer.Deserialize(encodeBase64(version20Vertex));
|
||||
require(replay20Vertex.request.context.vertex.has_value() && replay20Vertex.request.context.vertex->paClVsOutCntl == 0x89abcdefu, "a main version20 request lost PA_CL_VS_OUT_CNTL");
|
||||
@@ -5069,6 +5070,7 @@ void verifyVertexInfoVersion19() {
|
||||
auto legacy = plain;
|
||||
legacy.pop_back();
|
||||
legacy.pop_back();
|
||||
legacy.pop_back();
|
||||
legacy.erase(legacy.begin() + offset, legacy.begin() + offset + 4u);
|
||||
for (const auto version : {16u, 17u, 18u, 19u}) {
|
||||
legacy[4] = static_cast<std::uint8_t>(version);
|
||||
@@ -5120,6 +5122,7 @@ void verifyVertexInfoVersion19() {
|
||||
version30.pop_back();
|
||||
version30.pop_back();
|
||||
version30.pop_back();
|
||||
version30.pop_back();
|
||||
version30.erase(version30.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u), version30.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u + iterationBytes));
|
||||
version30[4] = 30u;
|
||||
const auto replay30 = serializer.Deserialize(encodeBase64(version30));
|
||||
@@ -5127,6 +5130,7 @@ void verifyVertexInfoVersion19() {
|
||||
auto spanned = sampledPixel;
|
||||
spanned.target.spannedWaves = true;
|
||||
auto version32 = decodeBase64(serializer.Serialize(spanned));
|
||||
version32.pop_back();
|
||||
require(version32.size() > 2u && version32.back() == 0u && version32[version32.size() - 2u] == 0u && version32[version32.size() - 3u] == 1u, "the spanned-waves flag is not the byte before the depth remap flag");
|
||||
version32.pop_back();
|
||||
version32.pop_back();
|
||||
@@ -5138,8 +5142,10 @@ void verifyVertexInfoVersion19() {
|
||||
version29.pop_back();
|
||||
version29.pop_back();
|
||||
version29.pop_back();
|
||||
version29.pop_back();
|
||||
version29.erase(version29.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes), version29.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u + iterationBytes));
|
||||
auto version36 = sampledBytes;
|
||||
version36.pop_back();
|
||||
version36.erase(version36.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u + iterationBytes - 1u));
|
||||
version36[4] = 36u;
|
||||
const auto replay36 = serializer.Deserialize(encodeBase64(version36));
|
||||
@@ -5151,6 +5157,7 @@ void verifyVertexInfoVersion19() {
|
||||
version28.pop_back();
|
||||
version28.pop_back();
|
||||
version28.pop_back();
|
||||
version28.pop_back();
|
||||
version28.erase(version28.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u), version28.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u + iterationBytes));
|
||||
version28[4] = 28u;
|
||||
const auto replay28 = serializer.Deserialize(encodeBase64(version28));
|
||||
@@ -5159,6 +5166,7 @@ void verifyVertexInfoVersion19() {
|
||||
version27.pop_back();
|
||||
version27.pop_back();
|
||||
version27.pop_back();
|
||||
version27.pop_back();
|
||||
version27.erase(version27.begin() + static_cast<std::ptrdiff_t>(maskOffset), version27.begin() + static_cast<std::ptrdiff_t>(maskOffset + 1u + sampleMaskBytes + 4u + iterationBytes));
|
||||
version27[4] = 27u;
|
||||
const auto replay27 = serializer.Deserialize(encodeBase64(version27));
|
||||
@@ -5167,6 +5175,7 @@ void verifyVertexInfoVersion19() {
|
||||
legacyPixel.pop_back();
|
||||
legacyPixel.pop_back();
|
||||
legacyPixel.pop_back();
|
||||
legacyPixel.pop_back();
|
||||
legacyPixel.erase(legacyPixel.begin() + static_cast<std::ptrdiff_t>(allBitsOffset), legacyPixel.begin() + static_cast<std::ptrdiff_t>(allBitsOffset + 2u + sampleMaskBytes + 4u + iterationBytes));
|
||||
legacyPixel[4] = 26u;
|
||||
const auto replay26 = serializer.Deserialize(encodeBase64(legacyPixel));
|
||||
@@ -5216,9 +5225,10 @@ void verifyDepthRemapRequests() {
|
||||
require(serializer.Serialize(replay.request) == remapped, "a version 35 depth remap request changed on re-serialization");
|
||||
const auto plainBytes = decodeBase64(plain);
|
||||
const auto remappedBytes = decodeBase64(remapped);
|
||||
require(plainBytes.size() == remappedBytes.size() && plainBytes.size() > 5u && std::equal(plainBytes.begin(), plainBytes.end() - 6, remappedBytes.begin()), "the depth remap flag and mode are not the last 6 bytes before the f32 flush flag of a vertex request");
|
||||
require(plainBytes.size() == remappedBytes.size() && plainBytes.size() > 5u && std::equal(plainBytes.begin(), plainBytes.end() - 7, remappedBytes.begin()), "the depth remap flag and mode are not the 6 bytes before the f32 flush and denormal preserve flags of a vertex request");
|
||||
for (const std::uint8_t legacyVersion : {32u, 34u}) {
|
||||
auto legacyBytes = remappedBytes;
|
||||
legacyBytes.pop_back();
|
||||
legacyBytes[4] = legacyVersion;
|
||||
legacyBytes.resize(legacyBytes.size() - 6u);
|
||||
const auto legacy = serializer.Deserialize(encodeBase64(legacyBytes));
|
||||
@@ -5226,6 +5236,7 @@ void verifyDepthRemapRequests() {
|
||||
require(serializer.Serialize(legacy.request) == plain, ("a version " + std::to_string(legacyVersion) + " vertex request misread the fields before the depth remap").c_str());
|
||||
}
|
||||
auto version35 = remappedBytes;
|
||||
version35.pop_back();
|
||||
version35[4] = 35u;
|
||||
version35.pop_back();
|
||||
const auto replay35 = serializer.Deserialize(encodeBase64(version35));
|
||||
@@ -5272,6 +5283,7 @@ void verifyMergedHullConfiguration() {
|
||||
require(replay.request.graphics.has_value() && replay.request.graphics->tessellation.has_value() && replay.request.graphics->tessellation->mergedHull, "a merged hull configuration was lost in serialization");
|
||||
require(!serializer.Deserialize(separate).request.graphics->tessellation->mergedHull, "a separate hull configuration was read as merged");
|
||||
auto legacy = decodeBase64(merged);
|
||||
legacy.pop_back();
|
||||
require(legacy.size() > 52u && legacy[legacy.size() - 52u] == 1u, "the merged hull flag is not the last byte of the tessellation configuration");
|
||||
legacy.erase(legacy.end() - 52);
|
||||
for (const auto version : {30u, 31u, 32u, 34u, 35u, 36u, 37u, 38u}) {
|
||||
@@ -7650,6 +7662,13 @@ int main(int argc, char** argv) {
|
||||
std::vector<std::uint64_t> float32FlushKey;
|
||||
RecompileCacheKey::Build(float32Flush, float32FlushKey);
|
||||
require(plainKey != float32FlushKey, "f32 hardware flush did not change the cache key");
|
||||
auto float32Preserve = uncached;
|
||||
float32Preserve.target.float32DenormPreserve = true;
|
||||
require(RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(float32Preserve)).request.target.float32DenormPreserve, "f32 denormal preservation was lost in serialization");
|
||||
require(!RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(uncached)).request.target.float32DenormPreserve, "f32 denormal preservation appeared from nowhere in serialization");
|
||||
std::vector<std::uint64_t> float32PreserveKey;
|
||||
RecompileCacheKey::Build(float32Preserve, float32PreserveKey);
|
||||
require(plainKey != float32PreserveKey && float32FlushKey != float32PreserveKey, "f32 denormal preservation did not change the cache key");
|
||||
auto changedLayout = request;
|
||||
changedLayout.layout.pushConstantSizeBytes = 64;
|
||||
require(!Recompile(changedLayout).cacheHit, "binding layout change reused an incompatible variant");
|
||||
|
||||
@@ -289,6 +289,7 @@ private:
|
||||
append(key, value.spannedWaves);
|
||||
append(key, value.robustBufferAccess2);
|
||||
append(key, value.float32FlushToZero);
|
||||
append(key, value.float32DenormPreserve);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -827,7 +827,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
||||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(39u);
|
||||
writer.WriteU32(41u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
@@ -858,6 +858,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
||||
writer.WriteU32(request.context.vertex->depthRemapMode);
|
||||
}
|
||||
writer.WriteBool(request.target.float32FlushToZero);
|
||||
writer.WriteBool(request.target.float32DenormPreserve);
|
||||
return base64Encode(buffer);
|
||||
}
|
||||
|
||||
@@ -866,7 +867,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 > 39u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (version < 1u || version > 41u) 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);
|
||||
@@ -911,6 +912,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const
|
||||
}
|
||||
}
|
||||
if (version >= 36u) result.request.target.float32FlushToZero = reader.ReadBool();
|
||||
if (version >= 41u) result.request.target.float32DenormPreserve = reader.ReadBool();
|
||||
if (version >= 21u && reader.Remaining() != 0u) throw std::runtime_error("RequestSerializer: unexpected trailing data");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -603,6 +603,7 @@ CompiledVariant emitVariant(const RecompileRequest& request, IrProgram& program)
|
||||
targetOptions.spannedWaves = request.target.spannedWaves;
|
||||
targetOptions.robustBufferAccess2 = request.target.robustBufferAccess2;
|
||||
targetOptions.float32FlushToZero = request.target.float32FlushToZero;
|
||||
targetOptions.float32DenormPreserve = request.target.float32DenormPreserve;
|
||||
targetOptions.meshBarycentricPixel = request.shader.stage == ShaderStage::Fragment && request.graphics && request.graphics->mesh;
|
||||
if (request.shader.stage == ShaderStage::Mesh && request.graphics && request.graphics->meshBarycentrics) {
|
||||
const auto& paired = *request.graphics->meshBarycentrics;
|
||||
|
||||
@@ -290,6 +290,7 @@ struct SpirvTarget {
|
||||
bool robustBufferAccess2 = false;
|
||||
bool depthRangeRemap = false;
|
||||
bool float32FlushToZero = false;
|
||||
bool float32DenormPreserve = false;
|
||||
};
|
||||
|
||||
struct BindingLayout {
|
||||
|
||||
@@ -29,6 +29,7 @@ struct SpirvTargetOptions {
|
||||
bool spannedWaves = false;
|
||||
bool robustBufferAccess2 = false;
|
||||
bool float32FlushToZero = false;
|
||||
bool float32DenormPreserve = false;
|
||||
bool meshBarycentricPixel = false;
|
||||
bool meshBarycentricVertex = false;
|
||||
std::uint32_t barycentricSmoothLocation = 0xffffffffu;
|
||||
|
||||
@@ -112,6 +112,7 @@ struct SpirvEmitterState {
|
||||
bool float16DenormPreserve = false;
|
||||
bool robustBufferAccess2 = false;
|
||||
bool float32FlushToZero = false;
|
||||
bool float32DenormPreserve = false;
|
||||
bool meshBarycentricPixel = false;
|
||||
bool meshBarycentricVertex = false;
|
||||
std::uint32_t barycentricSmoothLocation = 0xffffffffu;
|
||||
|
||||
@@ -277,6 +277,7 @@ std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const Sh
|
||||
state.float16DenormPreserve = target.float16DenormPreserve;
|
||||
state.robustBufferAccess2 = target.robustBufferAccess2;
|
||||
state.float32FlushToZero = target.float32FlushToZero;
|
||||
state.float32DenormPreserve = target.float32DenormPreserve;
|
||||
state.hostSubgroupSize = target.subgroupSize;
|
||||
if (state.requirements.ldsLock && LdsInDeviceMemory(state) && std::find(target.supportedCapabilities.begin(), target.supportedCapabilities.end(), static_cast<std::uint32_t>(spv::CapabilityInt64Atomics)) != target.supportedCapabilities.end()) {
|
||||
state.requirements.ldsLock = false;
|
||||
|
||||
@@ -344,6 +344,9 @@ void DefineModule(SpirvEmitterState& state) {
|
||||
if (state.float32FlushToZero && std::find(state.supportedCapabilities.begin(), state.supportedCapabilities.end(), static_cast<std::uint32_t>(spv::CapabilityDenormFlushToZero)) != state.supportedCapabilities.end()) {
|
||||
state.module.EmitCapability(spv::CapabilityDenormFlushToZero);
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeDenormFlushToZero, 32u);
|
||||
} else if (state.float32DenormPreserve && !state.float32FlushToZero) {
|
||||
state.module.EmitCapability(spv::CapabilityDenormPreserve);
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeDenormPreserve, 32u);
|
||||
}
|
||||
const bool deviceFloat64 = std::find(state.supportedCapabilities.begin(), state.supportedCapabilities.end(), static_cast<std::uint32_t>(spv::CapabilityFloat64)) != state.supportedCapabilities.end();
|
||||
state.integerInterpolation = state.requirements.float64Interpolation && !deviceFloat64;
|
||||
|
||||
@@ -214,12 +214,12 @@ int main() {
|
||||
Require(request.graphics->draw.indexAddress == 0xabcdef3400u && request.graphics->draw.indexCount == 57u && request.graphics->draw.indexElementBytes == 4u && request.graphics->draw.instanceCount == 9u);
|
||||
Require(request.target.float16DenormPreserve == preserve);
|
||||
auto upgraded = bytes;
|
||||
upgraded[4] = 39u;
|
||||
upgraded[4] = 41u;
|
||||
InsertMeshBarycentricFlag(upgraded);
|
||||
if (fixture.pixel) upgraded.insert(upgraded.begin() + 258u, {0u, 0u, 0u});
|
||||
if (fixture.pixel) InsertPixelDefaultsAfterFrontFace(upgraded);
|
||||
if (fixture.tessellation) upgraded.insert(upgraded.end() - 35u, 6u, 0u);
|
||||
upgraded.insert(upgraded.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
upgraded.insert(upgraded.end(), sizeof(std::uint64_t) + 1u + 5u + 1u + 1u, 0u);
|
||||
Require(serializer.Serialize(request) == EncodeBase64(upgraded));
|
||||
auto truncated = bytes;
|
||||
truncated.pop_back();
|
||||
@@ -254,12 +254,12 @@ int main() {
|
||||
layerRejected = std::string_view(error.what()) == "RequestSerializer: truncated data";
|
||||
}
|
||||
Require(layerRejected);
|
||||
layered[4] = 39u;
|
||||
layered[4] = 41u;
|
||||
InsertMeshBarycentricFlag(layered);
|
||||
layered.insert(layered.begin() + 259u, {0u, 0u});
|
||||
InsertPixelDefaultsAfterFrontFace(layered);
|
||||
if (fixture.tessellation) layered.insert(layered.end() - 35u, 6u, 0u);
|
||||
layered.insert(layered.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
layered.insert(layered.end(), sizeof(std::uint64_t) + 1u + 5u + 1u + 1u, 0u);
|
||||
Require(serializer.Serialize(layerReplay.request) == EncodeBase64(layered));
|
||||
}
|
||||
}
|
||||
@@ -280,11 +280,11 @@ int main() {
|
||||
Require(replay.request.context.pixel->rasterPrimitive == ShaderRecompiler::RasterPrimitive::Rectangle);
|
||||
Require(replay.request.context.vertex->paClVsOutCntl == 0x00050a05u);
|
||||
Require(replay.request.target.float16DenormPreserve == preserve);
|
||||
bytes[4] = 39u;
|
||||
bytes[4] = 41u;
|
||||
InsertMeshBarycentricFlag(bytes);
|
||||
bytes.insert(bytes.begin() + 259u, {0u, 0u});
|
||||
InsertPixelDefaultsAfterFrontFace(bytes);
|
||||
bytes.insert(bytes.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
bytes.insert(bytes.end(), sizeof(std::uint64_t) + 1u + 5u + 1u + 1u, 0u);
|
||||
Require(serializer.Serialize(replay.request) == EncodeBase64(bytes));
|
||||
|
||||
auto truncated23 = DecodeBase64(encoded23);
|
||||
@@ -410,10 +410,10 @@ int main() {
|
||||
if (fixture.pixel) main38.insert(main38.begin() + 259u, {0u, 0u});
|
||||
if (fixture.pixel) InsertPixelDefaultsAfterFrontFace(main38);
|
||||
auto upgraded = main38;
|
||||
upgraded[4] = 39u;
|
||||
upgraded[4] = 41u;
|
||||
if (fixture.tessellation) upgraded.insert(upgraded.end() - 35u, 1u, 0u);
|
||||
main38.insert(main38.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
upgraded.insert(upgraded.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
upgraded.insert(upgraded.end(), sizeof(std::uint64_t) + 1u + 5u + 1u + 1u, 0u);
|
||||
Require(serializer.Serialize(request) == EncodeBase64(upgraded));
|
||||
const auto replay38 = serializer.Deserialize(EncodeBase64(main38));
|
||||
Require(replay38.request.graphics.has_value() && replay38.request.graphics->tessellation.has_value() == fixture.tessellation);
|
||||
@@ -429,14 +429,14 @@ int main() {
|
||||
const auto replay25 = serializer.Deserialize(EncodeBase64(main25));
|
||||
Require(!fixture.pixel || (replay25.request.context.pixel->layerExported == layer && replay25.request.context.pixel->ditheredAlphaToCoverage == dither && replay25.request.context.pixel->reversedBlend == 0u));
|
||||
Require(replay25.request.target.float16DenormPreserve == preserve && (!fixture.tessellation || replay25.request.graphics->tessellation->primitiveGroupSize == 37u));
|
||||
main25[4] = 39u;
|
||||
main25[4] = 41u;
|
||||
InsertMeshBarycentricFlag(main25);
|
||||
if (fixture.pixel) {
|
||||
main25.insert(main25.begin() + 260u, 0u);
|
||||
InsertPixelDefaultsAfterFrontFace(main25);
|
||||
}
|
||||
if (fixture.tessellation) main25.insert(main25.end() - 35u, 1u, 0u);
|
||||
main25.insert(main25.end(), sizeof(std::uint64_t) + 1u + 5u + 1u, 0u);
|
||||
main25.insert(main25.end(), sizeof(std::uint64_t) + 1u + 5u + 1u + 1u, 0u);
|
||||
Require(serializer.Serialize(replay25.request) == EncodeBase64(main25));
|
||||
}
|
||||
}
|
||||
@@ -447,13 +447,19 @@ int main() {
|
||||
for (const bool preserve : {false, true}) {
|
||||
request.target.float16DenormPreserve = preserve;
|
||||
request.target.float32FlushToZero = !preserve;
|
||||
request.target.float32DenormPreserve = preserve;
|
||||
const auto encoded = serializer.Serialize(request);
|
||||
const auto replay = serializer.Deserialize(encoded);
|
||||
Require(replay.request.target.float16DenormPreserve == preserve && replay.request.target.float32FlushToZero == !preserve);
|
||||
Require(replay.request.target.float16DenormPreserve == preserve && replay.request.target.float32FlushToZero == !preserve && replay.request.target.float32DenormPreserve == preserve);
|
||||
auto bytes = DecodeBase64(encoded);
|
||||
Require(bytes.size() >= 8u && bytes[4] == 39u && bytes[5] == 0u && bytes[6] == 0u && bytes[7] == 0u);
|
||||
Require(bytes.size() >= 8u && bytes[4] == 41u && bytes[5] == 0u && bytes[6] == 0u && bytes[7] == 0u);
|
||||
auto version40 = bytes;
|
||||
version40.pop_back();
|
||||
version40[4] = 40u;
|
||||
const auto replay40 = serializer.Deserialize(EncodeBase64(version40));
|
||||
Require(!replay40.request.target.float32DenormPreserve && replay40.request.target.float32FlushToZero == !preserve);
|
||||
auto future = bytes;
|
||||
future[4] = 40u;
|
||||
future[4] = 42u;
|
||||
bool futureRejected = false;
|
||||
try {
|
||||
static_cast<void>(serializer.Deserialize(EncodeBase64(future)));
|
||||
@@ -496,6 +502,7 @@ int main() {
|
||||
Require(serializer.Deserialize(serializer.Serialize(request)).request.target.spannedWaves == spanned);
|
||||
request.target.float32FlushToZero = true;
|
||||
auto version35 = DecodeBase64(serializer.Serialize(request));
|
||||
version35.pop_back();
|
||||
const auto stencilOperationValue = std::search(version35.begin(), version35.end(), pixel.sampleLocations.begin(), pixel.sampleLocations.begin() + 4) + pixel.sampleLocations.size();
|
||||
Require(stencilOperationValue < version35.end() && *stencilOperationValue == 0u);
|
||||
version35.erase(stencilOperationValue);
|
||||
|
||||
@@ -242,6 +242,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti
|
||||
| `APS5_NO_HOOK_SNAPSHOT` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp` | | takes the GpuMutex on every access instead of using the pending-write snapshot |
|
||||
| `APS5_NO_HOST_IMPORT` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | disables importing guest memory, so address-based shaders copy instead of using it in place |
|
||||
| `APS5_NO_HW_F32_FLUSH` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | skips the startup f32 denormal flush probe and keeps the emulated flush in every shader |
|
||||
| `APS5_NO_HW_F32_PRESERVE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | stops requesting DenormPreserve for 32-bit floats, so the device keeps its default f32 denormal handling |
|
||||
| `APS5_NO_IMAGE_PAGE_CACHE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp` | | leaves the main guest image's page-protection table uncached |
|
||||
| `APS5_NO_INDIRECT_DRAW` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp` | | rejects indirect draw packets at submission validation |
|
||||
| `APS5_NO_JOIN_WAW_CHECK` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp` | | lets a contiguous store join the pending one without the write-after-write check |
|
||||
|
||||
Reference in new issue
Block a user