perf(recompiler): read buffer V# words in the shader when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
436e310ddb
commit
ddb6680667
|
|
@ -1010,6 +1010,8 @@ target_include_directories(agc_driver_provoking_vertex_tests PRIVATE ${agcTestSh
|
|||
|
||||
target_include_directories(agc_driver_prepared_exports_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_include_directories(agc_driver_buffer_descriptor_runtime_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_include_directories(agc_driver_prepared_publication_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_include_directories(agc_driver_prepared_graphics_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
|
|
|||
|
|
@ -2926,6 +2926,7 @@ ShaderRecompiler::SpirvTarget VulkanDevice::buildTarget() const {
|
|||
target.narrowSubgroupClock = state->narrowSubgroupClock;
|
||||
target.float64DenormPreserve = state->float64DenormPreserve;
|
||||
target.fixedPushSlots = state->graphicsPipelineLibrary;
|
||||
target.registrationModules = state->graphicsPipelineLibrary;
|
||||
target.srgbDecodeFormats = state->srgbDecodeFormats;
|
||||
if (state->meshShader) {
|
||||
const auto& mesh = state->meshLimits;
|
||||
|
|
|
|||
|
|
@ -1625,12 +1625,12 @@ void verifyPixelRequestSerialization() {
|
|||
minimal.context.waveSize = 64;
|
||||
minimal.context.pixel = ShaderPixelStageInfo{};
|
||||
const auto encoded = serializer.Serialize(minimal);
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQRAAAAA=", "new requests did not use serialization version 16");
|
||||
require(requestPrefix(encoded, 8u) == "NVNQQREAAAA=", "new requests did not use serialization version 17");
|
||||
constexpr std::size_t mappingOffset = 8u + 37u + 18u + 163u;
|
||||
for (std::size_t bytes = 0; bytes < 18u; ++bytes) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source or alpha-to-coverage flag was accepted");
|
||||
}
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQREAAAA="}) {
|
||||
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQRIAAAA="}) {
|
||||
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
|
||||
}
|
||||
}
|
||||
|
|
@ -1725,7 +1725,7 @@ void verifyLegacyPixelRequests() {
|
|||
require(!pixel.orderedPixelShader, "a legacy request became a primitive-ordered pixel shader");
|
||||
require(request.shader.stage == ShaderStage::Fragment && request.shader.code.size() == 1u && request.shader.code[0] == 0xbf810000u && !request.context.vertex.has_value() && request.context.memory.empty(), "legacy guest context was misaligned");
|
||||
require(request.target.vulkanVersion == 0x00401000u && request.target.spirvVersion == 0x00010300u && request.target.subgroupSize == 64u && request.layout.firstBinding == 11u && request.layout.pushConstantSizeBytes == 128u, "legacy target or binding layout was misaligned");
|
||||
require(request.useCache == (version == 1u) && request.target.nonConstantImageOffsets == (version >= 6u) && request.target.srgbDecodeFormats == 0u && !request.target.narrowSubgroupClock && !request.target.float64DenormPreserve, "legacy request trailer was misread");
|
||||
require(request.useCache == (version == 1u) && request.target.nonConstantImageOffsets == (version >= 6u) && request.target.srgbDecodeFormats == 0u && !request.target.narrowSubgroupClock && !request.target.float64DenormPreserve && !request.target.registrationModules, "legacy request trailer was misread");
|
||||
const auto upgraded = serializer.Deserialize(serializer.Serialize(request));
|
||||
require(upgraded.request.context.pixel->targetExportMapping == pixel.targetExportMapping && RecompileCacheKey::ContextHash(upgraded.request) == RecompileCacheKey::ContextHash(request), "upgrading a legacy capture changed its pixel mapping");
|
||||
}
|
||||
|
|
@ -3218,6 +3218,14 @@ int main(int argc, char** argv) {
|
|||
auto float64Denormals = uncached;
|
||||
float64Denormals.target.float64DenormPreserve = true;
|
||||
require(RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(float64Denormals)).request.target.float64DenormPreserve, "f64 denormal preservation was lost in serialization");
|
||||
auto registrationModules = uncached;
|
||||
registrationModules.target.registrationModules = true;
|
||||
require(RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(registrationModules)).request.target.registrationModules, "registration modules were lost in serialization");
|
||||
std::vector<std::uint64_t> plainKey;
|
||||
std::vector<std::uint64_t> registrationKey;
|
||||
RecompileCacheKey::Build(uncached, plainKey);
|
||||
RecompileCacheKey::Build(registrationModules, registrationKey);
|
||||
require(plainKey != registrationKey, "registration modules did not change the cache key");
|
||||
auto changedLayout = request;
|
||||
changedLayout.layout.pushConstantSizeBytes = 64;
|
||||
require(!Recompile(changedLayout).cacheHit, "binding layout change reused an incompatible variant");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "Recompiler.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using AgcDriver::Graphics::Require;
|
||||
using ShaderRecompiler::ShaderStage;
|
||||
|
||||
constexpr std::uint32_t Lanes = 32;
|
||||
constexpr std::uint32_t InputBytes = 256;
|
||||
constexpr std::uint32_t Fill = 0xdeadbeefu;
|
||||
alignas(256) std::array<std::uint8_t, InputBytes> Input{};
|
||||
alignas(256) std::array<std::uint32_t, Lanes> Output{};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 7> Code{
|
||||
0x34020082, 0xe0301000, 0x80000201, 0xbf8c3f70, 0xe0701000, 0x80010201, 0xbf810000,
|
||||
};
|
||||
|
||||
struct Case {
|
||||
const char* name;
|
||||
std::uint32_t baseOffset;
|
||||
std::uint32_t records;
|
||||
bool present;
|
||||
};
|
||||
|
||||
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
|
||||
}
|
||||
|
||||
std::string Hex(std::uint32_t value) {
|
||||
char text[16];
|
||||
std::snprintf(text, sizeof(text), "0x%08x", value);
|
||||
return text;
|
||||
}
|
||||
|
||||
ShaderRecompiler::RecompileRequest Request(std::span<const std::uint32_t> userData, const ShaderRecompiler::SpirvTarget& target, std::span<const ShaderRecompiler::MemoryRegion> memory) {
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{Lanes, 1, 1}, 0u, {false, false, false}, false, 1};
|
||||
return {
|
||||
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(Code.data()), Code, 0, {}},
|
||||
{Lanes, 0, userData, compute, std::nullopt, std::nullopt, memory},
|
||||
target,
|
||||
{0, 0, 0, 128}
|
||||
};
|
||||
}
|
||||
|
||||
void Run(AgcDriver::VulkanDevice& device, bool registrationModules) {
|
||||
const std::string mode = registrationModules ? "registration modules" : "specialized modules";
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(Code.data()), std::as_bytes(std::span(Code))}}};
|
||||
std::array<std::uint32_t, 8> userData{};
|
||||
auto target = device.ComputeTarget(Lanes);
|
||||
target.registrationModules = registrationModules;
|
||||
const auto request = Request(userData, target, memory);
|
||||
const auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
const std::array<Case, 7> cases{{
|
||||
{"aligned", 0, 128, true},
|
||||
{"base +1", 1, 128, true},
|
||||
{"base +2", 2, 128, true},
|
||||
{"base +3", 3, 128, true},
|
||||
{"records 37", 0, 37, true},
|
||||
{"records 64 base +2", 2, 64, true},
|
||||
{"absent", 0, 128, false},
|
||||
}};
|
||||
const void* first = nullptr;
|
||||
for (const auto& test : cases) {
|
||||
Output.fill(Fill);
|
||||
const auto input = test.present ? BufferDescriptor(Input.data() + test.baseOffset, test.records) : std::array<std::uint32_t, 4>{0u, 0u, 0u, 0x31016facu};
|
||||
const auto output = BufferDescriptor(Output.data(), sizeof(Output));
|
||||
std::copy(input.begin(), input.end(), userData.begin());
|
||||
std::copy(output.begin(), output.end(), userData.begin() + 4);
|
||||
ShaderRecompiler::SrtRuntime runtime{};
|
||||
runtime.userData = userData;
|
||||
const auto capture = ShaderRecompiler::CaptureResources(request, runtime, *handle);
|
||||
const auto result = ShaderRecompiler::MaterializeShader(request, *capture, *handle);
|
||||
if (first == nullptr) first = result->spirv.data();
|
||||
if (registrationModules) Require(result->spirv.data() == first, "buffer descriptor runtime, " + mode + ", " + test.name + ": the V# specialized the module");
|
||||
device.Dispatch(*result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(Code.data()));
|
||||
device.WaitIdle();
|
||||
for (std::uint32_t lane = 0; lane < Lanes; ++lane) {
|
||||
std::uint32_t expected = 0;
|
||||
if (test.present && lane * 4u + 4u <= test.records) std::memcpy(&expected, Input.data() + test.baseOffset + lane * 4u, sizeof(expected));
|
||||
Require(Output[lane] == expected, "buffer descriptor runtime, " + mode + ", " + test.name + ": lane " + std::to_string(lane) + " is " + Hex(Output[lane]) + ", expected " + Hex(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
const auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
for (std::uint32_t index = 0; index < InputBytes; ++index) Input[index] = static_cast<std::uint8_t>(index * 41u + 3u + (index >> 3u));
|
||||
Run(*device, false);
|
||||
Run(*device, true);
|
||||
std::puts("buffer descriptor runtime tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -240,6 +240,7 @@ private:
|
|||
append(key, value.srgbDecodeFormats);
|
||||
append(key, value.narrowSubgroupClock);
|
||||
append(key, value.float64DenormPreserve);
|
||||
append(key, value.registrationModules);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -734,7 +734,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
std::string buffer;
|
||||
Writer writer(buffer);
|
||||
writer.WriteU32(0x41505335u);
|
||||
writer.WriteU32(16u);
|
||||
writer.WriteU32(17u);
|
||||
writeShaderBinary(writer, request.shader);
|
||||
writeGuestContext(writer, request.context);
|
||||
writeSpirvTarget(writer, request.target);
|
||||
|
|
@ -754,6 +754,7 @@ std::string RequestSerializer::Serialize(const RecompileRequest& request) const
|
|||
if (request.context.compute.has_value()) writer.WriteU32(request.context.compute->scratchDwords);
|
||||
writer.WriteBool(request.target.narrowSubgroupClock);
|
||||
writer.WriteBool(request.target.float64DenormPreserve);
|
||||
writer.WriteBool(request.target.registrationModules);
|
||||
return base64Encode(buffer);
|
||||
}
|
||||
|
||||
|
|
@ -762,7 +763,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 > 16u) throw std::runtime_error("unsupported recompile request serialization version");
|
||||
if (version < 1u || version > 17u) 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);
|
||||
|
|
@ -783,6 +784,7 @@ DeserializedRequest RequestSerializer::Deserialize(std::string_view text) const
|
|||
if (version >= 10u && result.request.context.compute.has_value()) result.request.context.compute->scratchDwords = reader.ReadU32();
|
||||
if (version >= 12u) result.request.target.narrowSubgroupClock = reader.ReadBool();
|
||||
if (version >= 16u) result.request.target.float64DenormPreserve = reader.ReadBool();
|
||||
if (version >= 17u) result.request.target.registrationModules = reader.ReadBool();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ struct DescriptorBindingPlan {
|
|||
|
||||
class DescriptorBindingBuilder {
|
||||
public:
|
||||
DescriptorBindingPlan Prepare(const IrBindingLayout& layout, const ShaderInfo& info, IrShaderStage stage, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exportMappings = {}) const;
|
||||
DescriptorBindingPlan Prepare(const IrBindingLayout& layout, const ShaderInfo& info, IrShaderStage stage, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exportMappings = {}, bool registrationModules = false) const;
|
||||
DescriptorBindingPlan Select(const DescriptorBindingPlan& plan, std::span<const std::uint32_t> bindings) const;
|
||||
void Populate(BindingAllocationResult& allocation, const CompiledBindingLayout& compiled, const DescriptorBindingPlan& plan, std::uint32_t userDataBase, const ResourceSnapshot& snapshot, const std::array<std::uint32_t, 3>& partialThreads) const;
|
||||
void Populate(BindingAllocationResult& allocation, const IrProgram& program, const ResourceSnapshot& snapshot, const std::array<std::uint32_t, 3>& partialThreads, std::span<const std::uint8_t> exportMappings = {}) const;
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, con
|
|||
allocation.specialization = std::move(plan.specialization);
|
||||
}
|
||||
|
||||
DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& layout, const ShaderInfo& info, IrShaderStage stage, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exportMappings) const {
|
||||
DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& layout, const ShaderInfo& info, IrShaderStage stage, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exportMappings, bool registrationModules) const {
|
||||
if (stage == IrShaderStage::Pixel && exportMappings.size() != 8u) fail("fragment runtime export mappings are missing");
|
||||
if (info.images.size() > ShaderInfo::MaxImages) fail("runtime image count exceeds the static capacity");
|
||||
DescriptorBindingPlan plan;
|
||||
|
|
@ -280,11 +280,13 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
auto alignment = static_cast<std::uint32_t>(buffer.typedAlignment);
|
||||
if (buffer.descriptorFormatted && format != 0u) alignment = std::max(alignment, std::min(GetFormatInfo(static_cast<IrBufferFormat>(format)).byteSize, 4u));
|
||||
if (alignment == 0u || misalignment % alignment != 0u) fail("is a formatted buffer access through a V# whose base is not aligned to its element");
|
||||
const auto first = PipelineSpecialization::BufferBase + index * PipelineSpecialization::BufferWords;
|
||||
plan.specialization.push_back({first, descriptor.dwords[1] & 0xffff0000u});
|
||||
plan.specialization.push_back({first + 1u, descriptor.dwords[3]});
|
||||
plan.specialization.push_back({first + 2u, present ? 1u : 0u});
|
||||
plan.specialization.push_back({first + 3u, misalignment});
|
||||
if (!registrationModules) {
|
||||
const auto first = PipelineSpecialization::BufferBase + index * PipelineSpecialization::BufferWords;
|
||||
plan.specialization.push_back({first, descriptor.dwords[1] & 0xffff0000u});
|
||||
plan.specialization.push_back({first + 1u, descriptor.dwords[3]});
|
||||
plan.specialization.push_back({first + 2u, present ? 1u : 0u});
|
||||
plan.specialization.push_back({first + 3u, misalignment});
|
||||
}
|
||||
if (buffer.formattedReadMask != 0u) {
|
||||
for (std::uint32_t component = 0; component < 4u; ++component) {
|
||||
if ((buffer.formattedReadMask & (1u << component)) == 0u) continue;
|
||||
|
|
|
|||
|
|
@ -418,6 +418,7 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra
|
|||
targetOptions.nonConstantImageOffsets = request.target.nonConstantImageOffsets;
|
||||
targetOptions.narrowSubgroupClock = request.target.narrowSubgroupClock;
|
||||
targetOptions.float64DenormPreserve = request.target.float64DenormPreserve;
|
||||
targetOptions.registrationModules = request.target.registrationModules;
|
||||
|
||||
constexpr SpirvEmitter spirvEmitter;
|
||||
CompiledShaderArtifact result;
|
||||
|
|
@ -601,7 +602,7 @@ struct PreparedBindingPlan {
|
|||
std::map<std::vector<std::uint32_t>, std::shared_ptr<PreparedModuleEntry>> modules;
|
||||
};
|
||||
|
||||
std::shared_ptr<PreparedBindingPlan> preparedBindingPlan(const CompiledVariant& variant, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exports) {
|
||||
std::shared_ptr<PreparedBindingPlan> preparedBindingPlan(const CompiledVariant& variant, const ResourceSnapshot& snapshot, std::span<const std::uint8_t> exports, bool registrationModules) {
|
||||
struct BindingPlanKeyStorage {};
|
||||
auto& key = HostThreadLocal<std::vector<std::uint64_t>, BindingPlanKeyStorage>();
|
||||
key.clear();
|
||||
|
|
@ -642,7 +643,7 @@ std::shared_ptr<PreparedBindingPlan> preparedBindingPlan(const CompiledVariant&
|
|||
}
|
||||
std::call_once(plan->ready, [&] {
|
||||
try {
|
||||
plan->bindings = DescriptorBindingBuilder{}.Prepare(variant.bindings.layout, variant.info.info, variant.info.stage, snapshot, exports);
|
||||
plan->bindings = DescriptorBindingBuilder{}.Prepare(variant.bindings.layout, variant.info.info, variant.info.stage, snapshot, exports, registrationModules);
|
||||
} catch (...) {
|
||||
plan->failure = std::current_exception();
|
||||
}
|
||||
|
|
@ -704,7 +705,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil
|
|||
.barycentricEmulation = artifact.barycentricEmulation,
|
||||
.variantId = artifact.variantId
|
||||
};
|
||||
const auto plan = preparedBindingPlan(variant, snapshot, request.context.pixel ? std::span<const std::uint8_t>(request.context.pixel->targetExportMapping) : std::span<const std::uint8_t>{});
|
||||
const auto plan = preparedBindingPlan(variant, snapshot, request.context.pixel ? std::span<const std::uint8_t>(request.context.pixel->targetExportMapping) : std::span<const std::uint8_t>{}, request.target.registrationModules);
|
||||
const DescriptorBindingPlan* bindingPlan = &plan->bindings;
|
||||
std::shared_ptr<PreparedModuleEntry> entry;
|
||||
struct LocalModuleKeyStorage {};
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ struct SpirvTarget {
|
|||
bool narrowSubgroupClock = false;
|
||||
bool fixedPushSlots = false;
|
||||
bool float64DenormPreserve = false;
|
||||
bool registrationModules = false;
|
||||
};
|
||||
|
||||
struct BindingLayout {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ struct SpirvTargetOptions {
|
|||
bool nonConstantImageOffsets = false;
|
||||
bool narrowSubgroupClock = false;
|
||||
bool float64DenormPreserve = false;
|
||||
bool registrationModules = false;
|
||||
};
|
||||
|
||||
class SpirvEmitter {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ struct SpirvEmitterState {
|
|||
bool splitSubgroup = false;
|
||||
bool narrowSubgroupClock = false;
|
||||
bool float64DenormPreserve = false;
|
||||
bool registrationModules = false;
|
||||
std::uint32_t bufferMisalignment = 0;
|
||||
std::unordered_set<const IrValue*> sharedLaneValues;
|
||||
std::uint32_t laneHalf = 0;
|
||||
// The target's SPIR-V version and what the device accepts, for capabilities an emitter adds
|
||||
|
|
|
|||
|
|
@ -250,6 +250,7 @@ std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const Sh
|
|||
state.nonConstantImageOffsets = target.nonConstantImageOffsets;
|
||||
state.narrowSubgroupClock = target.narrowSubgroupClock;
|
||||
state.float64DenormPreserve = target.float64DenormPreserve;
|
||||
state.registrationModules = target.registrationModules;
|
||||
state.hostSubgroupSize = target.subgroupSize;
|
||||
state.splitSubgroup = program.WaveSize() == 32u && target.subgroupSize > 32u;
|
||||
if (state.splitSubgroup && (state.requirements.subgroupBallot || state.requirements.subgroupShuffle)) state.requirements.subgroupLocalInvocationId = true;
|
||||
|
|
|
|||
|
|
@ -57,9 +57,10 @@ RuntimeDescriptor descriptor(SpirvValueEmitContext& context, const IrValue& inst
|
|||
const auto* handle = instruction.Argument(0)->Resolve();
|
||||
if (handle->Opcode() != IrOpcode::GetBufferResource || handle->ArgumentCount() != 4u) context.Fail(instruction, "requires a runtime four-dword V#");
|
||||
const auto& memory = context.Memory(instruction);
|
||||
const bool runtime = memory.gpuDescriptor || state.registrationModules;
|
||||
const auto first = PipelineSpecialization::BufferBase + memory.resource * PipelineSpecialization::BufferWords;
|
||||
const auto word1 = memory.gpuDescriptor ? context.Arg(*handle, 1u) : state.module.SpecializationConstant(TypeU32(state), first, 0u);
|
||||
const auto word3 = memory.gpuDescriptor ? context.Arg(*handle, 3u) : state.module.SpecializationConstant(TypeU32(state), first + 1u, 0u);
|
||||
const auto word1 = runtime ? context.Arg(*handle, 1u) : state.module.SpecializationConstant(TypeU32(state), first, 0u);
|
||||
const auto word3 = runtime ? context.Arg(*handle, 3u) : state.module.SpecializationConstant(TypeU32(state), first + 1u, 0u);
|
||||
const auto u32 = TypeU32(state);
|
||||
auto base = ConstantU32(state, 0u);
|
||||
if (memory.gpuDescriptor || BufferAccessOf(instruction.Opcode()) == BufferAccess::Atomic) {
|
||||
|
|
@ -82,7 +83,8 @@ RuntimeDescriptor descriptor(SpirvValueEmitContext& context, const IrValue& inst
|
|||
}
|
||||
if (memory.gpuDescriptor) faultIf(context, instruction, invalid, base, 0u, BdaAbi::FaultReason::InvalidDescriptor);
|
||||
const auto stride = field(state, word1, 16u, 14u);
|
||||
const auto valid = memory.gpuDescriptor ? both(state, Unary(state, spv::OpLogicalNot, TypeBool(state), invalid), Binary(state, spv::OpINotEqual, TypeBool(state), base, BdaConstant(state, 0u))) : nonzero(state, state.module.SpecializationConstant(u32, first + 2u, 0u));
|
||||
const auto present = runtime ? both(state, nonzero(state, context.Arg(*handle, 2u)), Binary(state, spv::OpLogicalOr, TypeBool(state), nonzero(state, context.Arg(*handle, 0u)), nonzero(state, field(state, word1, 0u, 16u)))) : nonzero(state, state.module.SpecializationConstant(u32, first + 2u, 0u));
|
||||
const auto valid = memory.gpuDescriptor ? both(state, Unary(state, spv::OpLogicalNot, TypeBool(state), invalid), Binary(state, spv::OpINotEqual, TypeBool(state), base, BdaConstant(state, 0u))) : present;
|
||||
return {base, context.Arg(*handle, 2u), stride, word3, both(state, nonzero(state, stride), nonzero(state, field(state, word1, 31u, 1u))), index, valid};
|
||||
}
|
||||
|
||||
|
|
@ -140,8 +142,10 @@ MemoryResourceAccess directAccess(SpirvValueEmitContext& context, const IrValue&
|
|||
std::uint32_t baseMisalignment(SpirvValueEmitContext& context, const IrValue& instruction) {
|
||||
const auto& memory = context.Memory(instruction);
|
||||
if (memory.kind == ResourceKind::ScalarBuffer) return ConstantU32(context.state, 0u);
|
||||
const auto first = PipelineSpecialization::BufferBase + memory.resource * PipelineSpecialization::BufferWords;
|
||||
return context.state.module.SpecializationConstant(TypeU32(context.state), first + 3u, 0u);
|
||||
auto& state = context.state;
|
||||
if (state.bufferMisalignment != 0u) return state.bufferMisalignment;
|
||||
if (state.registrationModules) return Binary(state, spv::OpBitwiseAnd, TypeU32(state), context.Arg(*instruction.Argument(0)->Resolve(), 0u), ConstantU32(state, 3u));
|
||||
return state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::BufferBase + memory.resource * PipelineSpecialization::BufferWords + 3u, 0u);
|
||||
}
|
||||
|
||||
std::uint32_t directWordIndex(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t address) {
|
||||
|
|
@ -401,7 +405,7 @@ std::uint32_t formattedCall(SpirvValueEmitContext& context, const IrValue& instr
|
|||
auto function = state.formattedBufferFunctions.find(key);
|
||||
if (function == state.formattedBufferFunctions.end()) {
|
||||
const auto id = state.module.AllocateId();
|
||||
const auto functionType = store ? state.module.Type(spv::OpTypeFunction, resultType, u32, u32, u32, u32, u32, valueType) : state.module.Type(spv::OpTypeFunction, resultType, u32, u32, u32, u32, u32);
|
||||
const auto functionType = store ? state.module.Type(spv::OpTypeFunction, resultType, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, valueType) : state.module.Type(spv::OpTypeFunction, resultType, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32);
|
||||
const auto label = state.currentLabel;
|
||||
const auto byteOffset = state.memoryByteOffsets.at(element);
|
||||
struct Restore {
|
||||
|
|
@ -412,27 +416,23 @@ std::uint32_t formattedCall(SpirvValueEmitContext& context, const IrValue& instr
|
|||
~Restore() {
|
||||
state.module.EndHelperFunction();
|
||||
state.memoryByteOffsets.at(element) = byteOffset;
|
||||
state.bufferMisalignment = 0u;
|
||||
state.currentLabel = label;
|
||||
}
|
||||
};
|
||||
state.module.BeginHelperFunction();
|
||||
const Restore restore{state, element, label, byteOffset};
|
||||
state.module.AddFunction(spv::OpFunction, resultType, id, spv::FunctionControlDontInlineMask, functionType);
|
||||
std::array<std::uint32_t, 6> parameters{};
|
||||
for (std::uint32_t index = 0; index < (store ? 6u : 5u); ++index) {
|
||||
std::array<std::uint32_t, 11> parameters{};
|
||||
for (std::uint32_t index = 0; index < (store ? 11u : 10u); ++index) {
|
||||
parameters[index] = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpFunctionParameter, index == 5u ? valueType : u32, parameters[index]);
|
||||
state.module.AddFunction(spv::OpFunctionParameter, index == 10u ? valueType : u32, parameters[index]);
|
||||
}
|
||||
EmitLabel(state, state.module.AllocateId());
|
||||
state.memoryByteOffsets.at(element) = parameters[4];
|
||||
const auto first = PipelineSpecialization::BufferBase + memory.resource * PipelineSpecialization::BufferWords;
|
||||
const auto word1 = state.module.SpecializationConstant(u32, first, 0u);
|
||||
const auto word3 = state.module.SpecializationConstant(u32, first + 1u, 0u);
|
||||
const auto stride = field(state, word1, 16u, 14u);
|
||||
const auto swizzle = both(state, nonzero(state, stride), nonzero(state, field(state, word1, 31u, 1u)));
|
||||
const auto valid = nonzero(state, state.module.SpecializationConstant(u32, first + 2u, 0u));
|
||||
const RuntimeDescriptor shared{ConstantU32(state, 0u), parameters[3], stride, word3, swizzle, parameters[0], valid, parameters[1], parameters[2]};
|
||||
const auto value = formatSwitch(context, instruction, shared, components, store, store ? parameters[5] : 0u);
|
||||
state.bufferMisalignment = parameters[9];
|
||||
const RuntimeDescriptor shared{ConstantU32(state, 0u), parameters[3], parameters[5], parameters[6], nonzero(state, parameters[7]), parameters[0], nonzero(state, parameters[8]), parameters[1], parameters[2]};
|
||||
const auto value = formatSwitch(context, instruction, shared, components, store, store ? parameters[10] : 0u);
|
||||
if (store) state.module.AddFunction(spv::OpReturn);
|
||||
else state.module.AddFunction(spv::OpReturnValue, value);
|
||||
state.module.AddFunction(spv::OpFunctionEnd);
|
||||
|
|
@ -440,8 +440,12 @@ std::uint32_t formattedCall(SpirvValueEmitContext& context, const IrValue& instr
|
|||
}
|
||||
const auto offset = Binary(state, spv::OpIAdd, u32, context.Arg(instruction, 2u), ConstantU32(state, memory.offset));
|
||||
const auto result = state.module.AllocateId();
|
||||
if (store) state.module.AddFunction(spv::OpFunctionCall, resultType, result, function->second, resource.index, offset, context.Arg(instruction, 3u), resource.records, state.memoryByteOffsets.at(element), data);
|
||||
else state.module.AddFunction(spv::OpFunctionCall, resultType, result, function->second, resource.index, offset, context.Arg(instruction, 3u), resource.records, state.memoryByteOffsets.at(element));
|
||||
const auto flag = [&](std::uint32_t condition) { return Select(state, u32, condition, ConstantU32(state, 1u), ConstantU32(state, 0u)); };
|
||||
const auto swizzle = flag(resource.swizzle);
|
||||
const auto valid = flag(resource.valid);
|
||||
const auto misalignment = baseMisalignment(context, instruction);
|
||||
if (store) state.module.AddFunction(spv::OpFunctionCall, resultType, result, function->second, resource.index, offset, context.Arg(instruction, 3u), resource.records, state.memoryByteOffsets.at(element), resource.stride, resource.word3, swizzle, valid, misalignment, data);
|
||||
else state.module.AddFunction(spv::OpFunctionCall, resultType, result, function->second, resource.index, offset, context.Arg(instruction, 3u), resource.records, state.memoryByteOffsets.at(element), resource.stride, resource.word3, swizzle, valid, misalignment);
|
||||
return store ? ConstantU32(state, 0u) : result;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue