mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
Fix scalar buffer reads across sparse guest mappings
This commit is contained in:
@@ -81,8 +81,7 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) {
|
||||
}
|
||||
uses_gds |= memory.kind == IR::ResourceKind::Gds;
|
||||
uses_lds |= memory.kind == IR::ResourceKind::Lds;
|
||||
} else if (memory.kind == IR::ResourceKind::Buffer ||
|
||||
memory.kind == IR::ResourceKind::ScalarBuffer) {
|
||||
} else if (memory.kind == IR::ResourceKind::Buffer) {
|
||||
if (memory.resource >= program.info.buffers.size()) {
|
||||
Fail(program, "typed shader contains an invalid buffer resource");
|
||||
}
|
||||
|
||||
@@ -867,7 +867,7 @@ void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t LoadIndirectScalarBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
uint32_t LoadScalarBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
auto& state = ctx.state;
|
||||
const auto& handle = *inst.Arg(0).ResolveInstruction();
|
||||
const auto word1 = ctx.Arg(handle, 1);
|
||||
@@ -1545,29 +1545,8 @@ uint32_t EmitReadConst(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
}
|
||||
|
||||
void EmitReadConstBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
auto mem = ctx.Memory(inst);
|
||||
if (mem.planning_only) return;
|
||||
if (mem.kind == IR::ResourceKind::IndirectBuffer) {
|
||||
ctx.Define(inst, LoadIndirectScalarBuffer(ctx, inst));
|
||||
return;
|
||||
}
|
||||
auto& state = ctx.state;
|
||||
mem.kind = IR::ResourceKind::ScalarBuffer;
|
||||
auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), ctx.Arg(inst, 1),
|
||||
ConstantU32(state, 2));
|
||||
if (mem.offset >= 4u) {
|
||||
index = Binary(state, spv::OpIAdd, TypeU32(state), index, ConstantU32(state, mem.offset >> 2u));
|
||||
}
|
||||
const auto access = PrepareMemoryResourceAccess(state, mem);
|
||||
// Scalar buffer addressing aligns the base and each offset independently.
|
||||
const auto element = Binary(
|
||||
state, spv::OpIAdd, TypeU32(state), index,
|
||||
Binary(state, spv::OpShiftRightLogical, TypeU32(state), access.byte_offset, ConstantU32(state, 2)));
|
||||
const auto condition = EmitMemoryElementInBounds(state, access, element);
|
||||
ctx.Define(inst, EmitValueOrZeroIfCondition(state, condition, [&]() {
|
||||
return EmitNative<spv::OpLoad, IR::Type::U32>(
|
||||
state, EmitMemoryElementPointer(state, access, element));
|
||||
}));
|
||||
if (ctx.Memory(inst).planning_only) return;
|
||||
ctx.Define(inst, LoadScalarBuffer(ctx, inst));
|
||||
}
|
||||
|
||||
uint32_t EmitGetBufferResourceIndex(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
|
||||
@@ -269,10 +269,8 @@ MemoryResourceAccess PrepareMemoryResourceAccess(EmitterState& state, const IR::
|
||||
case IR::ResourceKind::Flat:
|
||||
case IR::ResourceKind::Global:
|
||||
EXIT("physical address memory must use the BDA emitter\n");
|
||||
case IR::ResourceKind::ScalarBuffer:
|
||||
case IR::ResourceKind::Buffer: {
|
||||
const auto bits = mem.kind == IR::ResourceKind::Buffer
|
||||
? IR::StorageBufferElementBits(state.program, mem) : 32u;
|
||||
const auto bits = IR::StorageBufferElementBits(state.program, mem);
|
||||
auto* buffer = &state.storage_buffers[2];
|
||||
if (bits == 8u) {
|
||||
buffer = &state.storage_buffers[0];
|
||||
|
||||
@@ -128,7 +128,6 @@ struct BufferResource {
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool formatted = false;
|
||||
bool scalar = false;
|
||||
bool live = false;
|
||||
uint32_t indirect_root = NoIndirectBuffer;
|
||||
uint32_t indirect_mapping_offset = 0;
|
||||
|
||||
@@ -1178,7 +1178,7 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) {
|
||||
}
|
||||
if (store == nullptr || visited.size() != program.blocks.size()) return {};
|
||||
for (const auto& buffer: program.info.buffers) {
|
||||
if (buffer.read && (!buffer.scalar || buffer.written)) return {};
|
||||
if (buffer.read) return {};
|
||||
}
|
||||
const auto& memory = program.memory_info.at(store->Flags<MemoryFlags>().index);
|
||||
UniformFillPlan result;
|
||||
@@ -1589,7 +1589,6 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
base.formatted ? descriptor.Format() : Prospero::BufferFormat::kInvalid;
|
||||
buffer.descriptor_swizzle = base.formatted ? descriptor.DstSelXYZW() : DstSel(4, 5, 6, 7);
|
||||
buffer.zero_stride_oob = descriptor.OutOfBounds() == 0u && stride == 0u;
|
||||
buffer.scalar_empty = base.scalar && descriptor.GetSize() == 0u;
|
||||
}
|
||||
snapshot.images.resize(program.info.images.size());
|
||||
specialization.images.resize(program.info.images.size());
|
||||
@@ -1787,17 +1786,6 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
|
||||
}
|
||||
|
||||
auto& memory_info = program.memory_info;
|
||||
// Resolve empty scalar-table words before folding the indirect descriptors that use them.
|
||||
for (auto* block: program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::ReadConstBuffer) continue;
|
||||
const auto& memory = memory_info[inst.Flags<MemoryFlags>().index];
|
||||
if (memory.kind == ResourceKind::ScalarBuffer &&
|
||||
specialization.buffers[memory.resource].scalar_empty &&
|
||||
buffers[memory.resource].indirect_root == BufferResource::NoIndirectBuffer)
|
||||
inst.ReplaceUsesWith(Value(0u));
|
||||
}
|
||||
}
|
||||
const ImageRemap image_remap(specialization);
|
||||
for (auto* block: program.blocks) {
|
||||
for (auto it = block->begin(); it != block->end(); ++it) {
|
||||
|
||||
@@ -13,7 +13,6 @@ struct ResourceSpecialization {
|
||||
Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid;
|
||||
uint32_t descriptor_swizzle = DstSel(4, 5, 6, 7);
|
||||
bool zero_stride_oob = false;
|
||||
bool scalar_empty = false;
|
||||
uint32_t indirect_root = BufferResource::NoIndirectBuffer;
|
||||
uint32_t indirect_mapping_offset = 0;
|
||||
uint32_t indirect_search_iterations = 0;
|
||||
|
||||
@@ -3735,8 +3735,6 @@ private:
|
||||
resource.written = resource.written || write;
|
||||
resource.atomic = resource.atomic || atomic;
|
||||
resource.formatted = resource.formatted || memory.formatted;
|
||||
resource.scalar = resource.scalar || op == ValueOpcode::ReadConstBuffer ||
|
||||
memory.kind == ResourceKind::ScalarBuffer;
|
||||
}
|
||||
|
||||
uint32_t AddImage(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) {
|
||||
@@ -3883,6 +3881,24 @@ private:
|
||||
if (memory.planning_only || IsIndirectPlanningMemory(flags.index)) {
|
||||
return;
|
||||
}
|
||||
if (op == ValueOpcode::ReadConstBuffer) {
|
||||
const auto* handle = inst.Arg(0).Resolve().TryInstruction();
|
||||
if (memory.kind != ResourceKind::ScalarBuffer || handle == nullptr ||
|
||||
handle->GetOpcode() != ValueOpcode::GetBufferResource || handle->NumArgs() != 4u) {
|
||||
Fail(flags.pc, "scalar buffer read requires a four-DWORD buffer descriptor");
|
||||
}
|
||||
// Scalar descriptors can span disjoint guest mappings. Keep their raw GPU operands
|
||||
// separate from handles that vector-resource planning may rewrite.
|
||||
auto* block = inst.Parent();
|
||||
const auto where = std::ranges::find_if(block->Instructions(),
|
||||
[&](const Inst& candidate) { return &candidate == &inst; });
|
||||
auto& scalar = *block->PrependNewInst(where, ValueOpcode::GetBufferResource,
|
||||
{handle->Arg(0), handle->Arg(1), handle->Arg(2), handle->Arg(3)});
|
||||
inst.SetArg(0, Value(&scalar));
|
||||
m_program.memory_info[flags.index].kind = ResourceKind::IndirectBuffer;
|
||||
m_info.uses_dma = true;
|
||||
return;
|
||||
}
|
||||
Inst* handle = nullptr;
|
||||
uint32_t source = 0;
|
||||
uint32_t resource = 0;
|
||||
@@ -3894,12 +3910,11 @@ private:
|
||||
source = indirect->source;
|
||||
} else if (!GetHandle(inst.Arg(0), ValueOpcode::GetBufferResource, 4, flags.pc,
|
||||
memory.resource * 4u, handle, source)) {
|
||||
if (memory.kind != (op == ValueOpcode::ReadConstBuffer ? ResourceKind::ScalarBuffer
|
||||
: ResourceKind::Buffer) ||
|
||||
if (memory.kind != ResourceKind::Buffer ||
|
||||
!memory.SupportsIndirectBufferLoad(op)) {
|
||||
Fail(flags.pc,
|
||||
"buffer descriptor is not a valid runtime value; GPU-selected access "
|
||||
"requires a scalar, raw 8/16-bit, raw DWORD x1/x2/x3/x4, or formatted X load");
|
||||
"requires a raw 8/16-bit, raw DWORD x1/x2/x3/x4, or formatted X load");
|
||||
}
|
||||
m_program.memory_info[flags.index].kind = ResourceKind::IndirectBuffer;
|
||||
m_info.uses_dma = true;
|
||||
|
||||
@@ -307,7 +307,7 @@ void Visit(Program& program, ShaderStageInputInfo input_info, InputUsage& inputs
|
||||
if (shared_access != SharedAccess::None) {
|
||||
info.uses_lds |= kind == ResourceKind::Lds;
|
||||
info.uses_gds |= kind == ResourceKind::Gds;
|
||||
} else if (kind == ResourceKind::Buffer || kind == ResourceKind::ScalarBuffer) {
|
||||
} else if (kind == ResourceKind::Buffer) {
|
||||
if (memory.resource >= info.buffers.size()) {
|
||||
return Fail("buffer operation has invalid resource metadata");
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@ bool IsRawRead(const ResourcePlan& values, const Inst& inst) {
|
||||
const auto& memory = values.memory_info[index];
|
||||
if (op != ValueOpcode::LoadBufferU32) {
|
||||
return (op == ValueOpcode::LoadAddressU32 && memory.kind == ResourceKind::ScalarAddress) ||
|
||||
(op == ValueOpcode::ReadConstBuffer && memory.kind == ResourceKind::ScalarBuffer);
|
||||
(op == ValueOpcode::ReadConstBuffer &&
|
||||
(memory.kind == ResourceKind::ScalarBuffer || memory.kind == ResourceKind::IndirectBuffer));
|
||||
}
|
||||
if (inst.NumArgs() != 5u || memory.kind != ResourceKind::Buffer || memory.typed ||
|
||||
memory.formatted || memory.coherent || memory.data_bits != 32u ||
|
||||
|
||||
Reference in New Issue
Block a user