perf(agc): avoid guest memory uploads for rect-list fault buffers
This commit is contained in:
parent
fa6d023b1b
commit
2539e9787f
|
|
@ -7,6 +7,7 @@ namespace AgcDriver::Graphics {
|
|||
|
||||
class BdaResources {
|
||||
public:
|
||||
explicit BdaResources(const Context& context);
|
||||
BdaResources(const Context& context, const GuestBufferMemory& memory);
|
||||
VkDescriptorBufferInfo Table() const;
|
||||
VkDescriptorBufferInfo Fault() const;
|
||||
|
|
@ -15,7 +16,7 @@ public:
|
|||
private:
|
||||
std::unique_ptr<Buffer> table;
|
||||
std::unique_ptr<Buffer> fault;
|
||||
std::size_t tableBytes;
|
||||
std::size_t tableBytes = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ private:
|
|||
GuestBufferMemory guestMemory;
|
||||
std::unique_ptr<BdaResources> bda;
|
||||
bool usesBda = false;
|
||||
bool usesFaultBuffer = false;
|
||||
VkDescriptorSetLayout _layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet _set = VK_NULL_HANDLE;
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
|
|
|
|||
|
|
@ -6,22 +6,27 @@
|
|||
|
||||
namespace AgcDriver::Graphics {
|
||||
|
||||
BdaResources::BdaResources(const Context& context, const GuestBufferMemory& memory) {
|
||||
BdaResources::BdaResources(const Context& context) {
|
||||
static_assert(std::endian::native == std::endian::little);
|
||||
Require(sizeof(ShaderRecompiler::BdaAbi::Fault) <= context.limits.maxStorageBufferRange, "BDA fault buffer exceeds storage buffer range limit");
|
||||
fault = std::make_unique<Buffer>(context, sizeof(ShaderRecompiler::BdaAbi::Fault), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
std::memset(fault->Bytes().data(), 0, fault->Bytes().size());
|
||||
}
|
||||
|
||||
BdaResources::BdaResources(const Context& context, const GuestBufferMemory& memory) : BdaResources(context) {
|
||||
const auto ranges = memory.AddressRanges();
|
||||
Require(ranges.size() <= std::numeric_limits<std::uint32_t>::max(), "BDA table range count overflow");
|
||||
Require(ranges.size() <= (std::numeric_limits<std::size_t>::max() - sizeof(ShaderRecompiler::BdaAbi::Header)) / sizeof(ShaderRecompiler::BdaAbi::Range), "BDA table size overflow");
|
||||
tableBytes = sizeof(ShaderRecompiler::BdaAbi::Header) + ranges.size() * sizeof(ShaderRecompiler::BdaAbi::Range);
|
||||
Require(tableBytes <= context.limits.maxStorageBufferRange && sizeof(ShaderRecompiler::BdaAbi::Fault) <= context.limits.maxStorageBufferRange, "BDA descriptors exceed storage buffer range limit");
|
||||
table = std::make_unique<Buffer>(context, tableBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
fault = std::make_unique<Buffer>(context, sizeof(ShaderRecompiler::BdaAbi::Fault), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
const ShaderRecompiler::BdaAbi::Header header{ShaderRecompiler::BdaAbi::Version, static_cast<std::uint32_t>(ranges.size()), sizeof(ShaderRecompiler::BdaAbi::Range), 0};
|
||||
std::memcpy(table->Bytes().data(), &header, sizeof(header));
|
||||
if (!ranges.empty()) std::memcpy(table->Bytes().data() + sizeof(header), ranges.data(), ranges.size() * sizeof(ranges.front()));
|
||||
std::memset(fault->Bytes().data(), 0, fault->Bytes().size());
|
||||
}
|
||||
|
||||
VkDescriptorBufferInfo BdaResources::Table() const {
|
||||
Require(table != nullptr, "BDA page table was not requested");
|
||||
return {table->Handle(), 0, tableBytes};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ void ShaderResources::prepareAddressBindings(std::span<const CompiledShader> sha
|
|||
const bool rectListFault = shader.stage == ShaderRecompiler::ShaderStage::TessellationControl && tables == 0 && faults == 1;
|
||||
Require((tables == faults || rectListFault) && tables <= 1 && faults <= 1, "invalid BDA table and fault descriptors");
|
||||
Require(shader.program->bdaAbiVersion == (faults == 0 ? 0u : ShaderRecompiler::BdaAbi::Version), "incompatible BDA ABI version");
|
||||
usesBda = usesBda || faults != 0;
|
||||
// Rect-list validation needs a fault buffer, but never accesses guest addresses.
|
||||
usesBda = usesBda || tables != 0;
|
||||
usesFaultBuffer = usesFaultBuffer || faults != 0;
|
||||
}
|
||||
if (usesBda) {
|
||||
Require(context.bufferDeviceAddress, "buffer device address is not enabled");
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ void ShaderResources::build(std::span<const CompiledShader> shaders, const Color
|
|||
Require(storageBuffers <= context.limits.maxDescriptorSetStorageBuffers, "pipeline descriptors exceed device limits");
|
||||
guestMemory.Upload(usesBda);
|
||||
if (usesBda) bda = std::make_unique<BdaResources>(context, guestMemory);
|
||||
else if (usesFaultBuffer) bda = std::make_unique<BdaResources>(context);
|
||||
std::vector<VkDescriptorSetLayoutBinding> description;
|
||||
for (const auto& binding : bindings) description.push_back(binding.layout);
|
||||
VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
|
|
|
|||
|
|
@ -58,6 +58,28 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) {
|
|||
auto disabled = context;
|
||||
disabled.bufferDeviceAddress = false;
|
||||
reject([&] { ShaderResources resources(disabled, compiled, snapshots); }, "not enabled");
|
||||
{
|
||||
// A rect-list fault buffer must not require BDA or consume guest snapshots.
|
||||
ShaderRecompiler::RecompileResult control;
|
||||
control.bdaAbiVersion = ShaderRecompiler::BdaAbi::Version;
|
||||
control.bindings = {binding(Role::FaultBuffer, 5)};
|
||||
auto writable = binding(Role::GuestBuffers, 6);
|
||||
writable.guestDescriptor = {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>(address >> 32) & 0xffffu, sizeof(guest), 0x31000000u};
|
||||
control.bindings.push_back(writable);
|
||||
const std::array<CompiledShader, 1> stages{{{ShaderRecompiler::ShaderStage::TessellationControl, &control, 0}}};
|
||||
const std::array<GuestMemorySnapshot, 1> unusedSnapshots{{{0, source}}};
|
||||
ShaderResources resources(disabled, stages, ColorTarget{}, 0, 0, unusedSnapshots);
|
||||
const auto fault = access.bytes(access.descriptor(5).buffer);
|
||||
for (const auto byte : fault) Require(byte == std::byte{}, "rect-list fault buffer was not initialized");
|
||||
const ShaderRecompiler::BdaAbi::Fault report{ShaderRecompiler::BdaAbi::FaultState::Ready, ShaderRecompiler::BdaAbi::FaultReason::InvalidRectangle, 0, 0, 0, 0, 0};
|
||||
std::memcpy(fault.data(), &report, sizeof(report));
|
||||
reject([&] { resources.WriteBack(); }, "rect-list requires");
|
||||
std::memset(fault.data(), 0, fault.size());
|
||||
changed = 456;
|
||||
std::memcpy(access.bytes(access.descriptor(6).buffer).data() + sizeof(std::uint32_t), &changed, sizeof(changed));
|
||||
resources.WriteBack();
|
||||
Require(guest[1] == changed, "rect-list fault-only path lost guest buffer writes");
|
||||
}
|
||||
{
|
||||
ShaderResources resources(context, compiled, snapshots);
|
||||
const auto table = access.bytes(access.descriptor(4).buffer);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVATOMICS_HPP
|
||||
#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVATOMICS_HPP
|
||||
|
||||
#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
void EmitDeviceAtomicMemoryBarrier(SpirvEmitterState& state);
|
||||
std::uint32_t EmitFloatAtomicReplacement(SpirvEmitterState& state, std::uint32_t old, std::uint32_t source, bool maxValue);
|
||||
std::uint32_t EmitShaderDataDwordLoad(SpirvEmitterState& state, std::uint32_t dwordIndex);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVFORMATCONVERSION_HPP
|
||||
#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVFORMATCONVERSION_HPP
|
||||
|
||||
#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
bool IsSignedFormatComponent(SpirvFormatComponentType type);
|
||||
std::uint32_t EmitUFloatToF32Bits(SpirvEmitterState& state, std::uint32_t raw, std::uint32_t bits);
|
||||
std::uint32_t NormalizeFormatComponent(SpirvEmitterState& state, const SpirvBufferFormatInfo& info, std::uint32_t component, std::uint32_t raw);
|
||||
std::uint32_t EmitTBufferBitcastU32ToI32(SpirvEmitterState& state, std::uint32_t value);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVINTERFACE_HPP
|
||||
#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVINTERFACE_HPP
|
||||
|
||||
#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp"
|
||||
#include "SpirvBackend/SpirvMemory/SpirvModule.hpp"
|
||||
#include "IntermediateRepresentation/IrValue.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings);
|
||||
void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings);
|
||||
void DefineModule(SpirvEmitterState& state);
|
||||
std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name);
|
||||
std::uint32_t ExecutionModelForStage(IrShaderStage stage);
|
||||
|
||||
std::uint32_t GlslStd450(SpirvEmitterState& state);
|
||||
std::uint32_t InputVariableForKind(const SpirvEmitterState& state, StageInputKind kind);
|
||||
const SpirvInputBinding* SpirvInputBindingForParameter(const SpirvEmitterState& state, std::uint32_t location);
|
||||
|
||||
std::uint32_t PixelParameterLocation(const SpirvEmitterState& state, std::uint32_t attr);
|
||||
bool PixelParameterIsFlat(const SpirvEmitterState& state, std::uint32_t attr);
|
||||
bool PixelParameterIsCustom(const SpirvEmitterState& state, std::uint32_t attr);
|
||||
|
||||
VertexInputScalarKind VertexParameterScalarKind(const SpirvEmitterState& state, std::uint32_t location);
|
||||
std::uint32_t VertexParameterComponentCount(const SpirvInputBinding& input);
|
||||
std::uint32_t VertexParameterScalarType(SpirvEmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
std::uint32_t OutputVariableForExport(const SpirvEmitterState& state, const ExportInfo& exp);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVMEMORYACCESS_HPP
|
||||
#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVMEMORYACCESS_HPP
|
||||
|
||||
#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
std::uint32_t LdsDwordCount(const SpirvEmitterState& state);
|
||||
std::uint32_t StorageBufferPackedStride(const SpirvEmitterState& state, const MemoryInfo& mem);
|
||||
IrBufferFormat StorageBufferFormat(const SpirvEmitterState& state, const MemoryInfo& mem);
|
||||
|
||||
void EmitMemoryOffsets(SpirvEmitterState& state);
|
||||
|
||||
MemoryResourceAccess PrepareMemoryResourceAccess(SpirvEmitterState& state, const MemoryInfo& mem);
|
||||
MemoryResourceAccess PrepareStorageBufferResourceAccess(SpirvEmitterState& state, const MemoryInfo& mem, std::uint32_t variable, std::uint32_t pointerType);
|
||||
|
||||
std::uint32_t EmitMemoryElementIndex(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t rawIndex);
|
||||
std::uint32_t EmitMemoryElementInBounds(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index);
|
||||
std::uint32_t EmitMemoryElementPointer(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index);
|
||||
std::uint32_t EmitStorageBufferElementPointer(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index, std::uint32_t pointerType);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue