mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
shader: remaining decode and emitter fixes
Decode and SPIR-V emitter corrections this title's shaders need: image and memory operand handling, scalar ALU forms, and the IR plumbing that goes with them.
This commit is contained in:
@@ -53,6 +53,14 @@ uint32_t Builder::Import(const char* name) {
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t Builder::DebugString(const char* text) {
|
||||
const auto id = AllocateId();
|
||||
std::vector<uint32_t> operands = {id};
|
||||
AppendString(operands, text);
|
||||
AppendInstruction(m_debug_strings, 7u, operands);
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t Builder::Type(uint32_t opcode, std::initializer_list<uint32_t> operands) {
|
||||
return Type(opcode, std::vector<uint32_t>(operands));
|
||||
}
|
||||
@@ -244,7 +252,8 @@ std::vector<uint32_t> Builder::Build() const {
|
||||
std::vector<uint32_t> module;
|
||||
module.reserve(5u + m_capabilities.size() + m_extensions.size() + m_ext_inst_imports.size() +
|
||||
m_memory_model.size() + m_entry_points.size() + m_execution_modes.size() +
|
||||
m_debug.size() + m_annotations.size() + m_declarations.size() +
|
||||
m_debug_strings.size() + m_debug.size() + m_annotations.size() +
|
||||
m_declarations.size() +
|
||||
m_functions.size());
|
||||
|
||||
module.push_back(0x07230203u);
|
||||
@@ -259,6 +268,7 @@ std::vector<uint32_t> Builder::Build() const {
|
||||
module.insert(module.end(), m_memory_model.begin(), m_memory_model.end());
|
||||
module.insert(module.end(), m_entry_points.begin(), m_entry_points.end());
|
||||
module.insert(module.end(), m_execution_modes.begin(), m_execution_modes.end());
|
||||
module.insert(module.end(), m_debug_strings.begin(), m_debug_strings.end());
|
||||
module.insert(module.end(), m_debug.begin(), m_debug.end());
|
||||
module.insert(module.end(), m_annotations.begin(), m_annotations.end());
|
||||
module.insert(module.end(), m_declarations.begin(), m_declarations.end());
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
void RequireCapability(uint32_t capability);
|
||||
void RequireExtension(const char* name);
|
||||
uint32_t Import(const char* name);
|
||||
uint32_t DebugString(const char* text);
|
||||
uint32_t Type(uint32_t opcode, std::initializer_list<uint32_t> operands = {});
|
||||
uint32_t Type(uint32_t opcode, const std::vector<uint32_t>& operands);
|
||||
uint32_t DecoratedType(uint32_t opcode, std::initializer_list<uint32_t> operands,
|
||||
@@ -73,6 +74,7 @@ private:
|
||||
std::vector<uint32_t> m_memory_model;
|
||||
std::vector<uint32_t> m_entry_points;
|
||||
std::vector<uint32_t> m_execution_modes;
|
||||
std::vector<uint32_t> m_debug_strings;
|
||||
std::vector<uint32_t> m_debug;
|
||||
std::vector<uint32_t> m_annotations;
|
||||
std::vector<uint32_t> m_declarations;
|
||||
|
||||
@@ -245,6 +245,9 @@ void AnalyzeProgramRequirements(IR::Program& program) {
|
||||
}
|
||||
}
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::ValueOpcode::ReadClockRealtime64:
|
||||
requirements.shader_clock = true;
|
||||
break;
|
||||
case IR::ValueOpcode::Ballot: MarkBallot(); break;
|
||||
case IR::ValueOpcode::DppMoveU32:
|
||||
case IR::ValueOpcode::ReadFirstLane:
|
||||
|
||||
@@ -75,6 +75,28 @@ uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value)
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
|
||||
uint32_t EmitReadClockRealtime64(EmitterState& state) {
|
||||
const auto clock = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpReadClockKHR, TypeU32Vector(state, 2), clock, ConstantU32(state, ScopeDevice)});
|
||||
|
||||
const auto low = state.builder.AllocateId();
|
||||
const auto high = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, clock, 0});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, clock, 1});
|
||||
|
||||
constexpr uint32_t ClockShift = 3;
|
||||
|
||||
const auto low_shifted = NewBinary(state, OpShiftRightLogical, TypeU32(state), low,
|
||||
ConstantU32(state, ClockShift));
|
||||
const auto high_carried = NewBinary(state, OpShiftLeftLogical, TypeU32(state), high,
|
||||
ConstantU32(state, 32u - ClockShift));
|
||||
const auto low_result = NewBinary(state, OpBitwiseOr, TypeU32(state), low_shifted, high_carried);
|
||||
const auto high_result = NewBinary(state, OpShiftRightLogical, TypeU32(state), high,
|
||||
ConstantU32(state, ClockShift));
|
||||
return MakePair(state, low_result, high_result);
|
||||
}
|
||||
|
||||
uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
@@ -438,6 +460,9 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::ISub32: return binary(OpISub, IR::Type::U32);
|
||||
case IR::ValueOpcode::IMul32: return binary(OpIMul, IR::Type::U32);
|
||||
case IR::ValueOpcode::UDiv32: return binary(OpUDiv, IR::Type::U32);
|
||||
case IR::ValueOpcode::ReadClockRealtime64:
|
||||
ctx.Define(inst, EmitReadClockRealtime64(state));
|
||||
return true;
|
||||
case IR::ValueOpcode::IAdd64:
|
||||
ctx.Define(inst, EmitAdd64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1)));
|
||||
return true;
|
||||
|
||||
@@ -219,9 +219,12 @@ uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& m
|
||||
ImageViewKind view) {
|
||||
(void)use_pc;
|
||||
const bool integer = mem.kind == IR::ResourceKind::ImageUint;
|
||||
const auto kind = SampledBindingKind(integer, view);
|
||||
const bool depth = mem.resource < state.program.info.images.size() &&
|
||||
state.program.info.images[mem.resource].depth_compare;
|
||||
const auto kind = SampledBindingKind(integer, view, depth);
|
||||
const auto binding = ResourceForDescriptor(state, kind, mem.resource);
|
||||
const auto variable = state.sampled_image_variables[SampledImageIndex(integer, view)];
|
||||
const auto variable =
|
||||
state.sampled_image_variables[SampledImageIndex(integer, view, depth)];
|
||||
const auto pointer_type = state.builder.Type(
|
||||
OpTypePointer, {StorageClassUniformConstant, ImageViewImageType(state, view, integer)});
|
||||
const auto pointer =
|
||||
@@ -252,7 +255,11 @@ uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, uint32
|
||||
const auto sampler = LoadSamplerDescriptor(state, mem.sampler, use_pc);
|
||||
if (image == 0 || sampler == 0) {
|
||||
ExitDescriptorBindingFailure(
|
||||
state, SampledBindingKind(mem.kind == IR::ResourceKind::ImageUint, view), mem.resource,
|
||||
state,
|
||||
SampledBindingKind(mem.kind == IR::ResourceKind::ImageUint, view,
|
||||
mem.resource < state.program.info.images.size() &&
|
||||
state.program.info.images[mem.resource].depth_compare),
|
||||
mem.resource,
|
||||
"sampled image or sampler descriptor load failed");
|
||||
}
|
||||
const auto sampled_image = state.builder.AllocateId();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <cinttypes>
|
||||
#include <atomic>
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||
|
||||
|
||||
@@ -293,18 +293,37 @@ PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::Memo
|
||||
return {.resource = resource, .index = EmitMemoryElementIndex(ctx.state, resource, raw_index)};
|
||||
}
|
||||
|
||||
constexpr uint32_t MemoryAccessVolatile = 0x1;
|
||||
|
||||
void AddLoad(ValueEmitContext& ctx, uint32_t type, uint32_t result, uint32_t pointer, bool glc) {
|
||||
if (glc) {
|
||||
ctx.state.builder.AddFunction({OpLoad, type, result, pointer, MemoryAccessVolatile});
|
||||
} else {
|
||||
ctx.state.builder.AddFunction({OpLoad, type, result, pointer});
|
||||
}
|
||||
}
|
||||
|
||||
void AddStore(ValueEmitContext& ctx, uint32_t pointer, uint32_t data, bool glc) {
|
||||
if (glc) {
|
||||
ctx.state.builder.AddFunction({OpStore, pointer, data, MemoryAccessVolatile});
|
||||
} else {
|
||||
ctx.state.builder.AddFunction({OpStore, pointer, data});
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t index);
|
||||
uint32_t index, bool glc);
|
||||
|
||||
uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t address, uint32_t index, uint32_t bits, bool sign_extend);
|
||||
uint32_t address, uint32_t index, uint32_t bits, bool sign_extend,
|
||||
bool glc);
|
||||
|
||||
uint32_t LoadWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource) {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index, mem.glc); });
|
||||
}
|
||||
|
||||
uint32_t LoadWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) {
|
||||
@@ -324,21 +343,38 @@ uint32_t LoadSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
return LoadSubwordInBounds(ctx, access.resource, address, access.index, bits,
|
||||
sign_extend);
|
||||
sign_extend, mem.glc);
|
||||
});
|
||||
}
|
||||
|
||||
void EmitLoadPrintf(ValueEmitContext& ctx, uint32_t index, uint32_t value) {
|
||||
constexpr uint64_t wanted = 0ull;
|
||||
if (wanted == 0 || ctx.state.program.shader_hash != wanted) {
|
||||
return;
|
||||
}
|
||||
auto& state = ctx.state;
|
||||
state.builder.RequireExtension("SPV_KHR_non_semantic_info");
|
||||
const auto set = state.builder.Import("NonSemantic.DebugPrintf");
|
||||
const auto fmt = state.builder.DebugString("kyty glc load idx=%u val=%u");
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpExtInst, TypeVoid(state), result, set, 1u, fmt, index, value});
|
||||
}
|
||||
|
||||
uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t index) {
|
||||
uint32_t index, bool glc) {
|
||||
const auto value = ctx.state.builder.AllocateId();
|
||||
const auto pointer = EmitMemoryElementPointer(ctx.state, resource, index);
|
||||
ctx.state.builder.AddFunction({OpLoad, TypeU32(ctx.state), value, pointer});
|
||||
AddLoad(ctx, TypeU32(ctx.state), value, pointer, glc);
|
||||
if (glc) {
|
||||
EmitLoadPrintf(ctx, index, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t address, uint32_t index, uint32_t bits, bool sign_extend) {
|
||||
const auto word = LoadWordInBounds(ctx, resource, index);
|
||||
uint32_t address, uint32_t index, uint32_t bits, bool sign_extend,
|
||||
bool glc) {
|
||||
const auto word = LoadWordInBounds(ctx, resource, index, glc);
|
||||
const auto byte =
|
||||
Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), address, ConstantU32(ctx.state, 3));
|
||||
const auto shift =
|
||||
@@ -486,6 +522,7 @@ uint32_t AtomicUpdate(EmitterState& state, uint32_t pointer, IR::ResourceKind ki
|
||||
const auto scope = kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
const auto memory = kind == IR::ResourceKind::Lds ? MemorySemanticsWorkgroupMemory
|
||||
: MemorySemanticsUniformMemory;
|
||||
|
||||
const auto preheader = state.builder.AllocateId();
|
||||
const auto header = state.builder.AllocateId();
|
||||
const auto cont = state.builder.AllocateId();
|
||||
@@ -569,16 +606,14 @@ void StoreWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
EmitIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, access.resource, access.index),
|
||||
data});
|
||||
AddStore(ctx, EmitMemoryElementPointer(ctx.state, access.resource, access.index),
|
||||
data, mem.glc);
|
||||
});
|
||||
}
|
||||
|
||||
void StoreWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, uint32_t index,
|
||||
uint32_t data) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, resource, index), data});
|
||||
uint32_t data, bool glc) {
|
||||
AddStore(ctx, EmitMemoryElementPointer(ctx.state, resource, index), data, glc);
|
||||
}
|
||||
|
||||
void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) {
|
||||
@@ -874,11 +909,11 @@ uint32_t LoadFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
return LoadFormattedComponent(
|
||||
ctx, mem, plan.format, plan.info, output_component,
|
||||
[&](uint32_t component) {
|
||||
return LoadWordInBounds(ctx, plan.resource, plan.indices[component]);
|
||||
return LoadWordInBounds(ctx, plan.resource, plan.indices[component], mem.glc);
|
||||
},
|
||||
[&](uint32_t component, uint32_t bits, bool sign_extend) {
|
||||
return LoadSubwordInBounds(ctx, plan.resource, plan.addresses[component],
|
||||
plan.indices[component], bits, sign_extend);
|
||||
plan.indices[component], bits, sign_extend, mem.glc);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -910,7 +945,7 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
|
||||
plan.indices[component], bits, data);
|
||||
} else {
|
||||
StoreWordInBounds(ctx, plan.resource, plan.indices[component], data);
|
||||
StoreWordInBounds(ctx, plan.resource, plan.indices[component], data, mem.glc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,7 +1028,7 @@ uint32_t LoadWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
values[component] = EmitValueOrZeroIfCondition(
|
||||
state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index, mem.glc); });
|
||||
}
|
||||
return ConstructU32Composite(state, components, values);
|
||||
});
|
||||
@@ -1015,7 +1050,7 @@ void StoreWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
||||
EmitIfCondition(state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
[&]() {
|
||||
StoreWordInBounds(ctx, access.resource, access.index,
|
||||
ctx.Arg(inst, component + 1u));
|
||||
ctx.Arg(inst, component + 1u), mem.glc);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1124,9 +1159,8 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto condition = EmitMemoryElementInBounds(state, access, element);
|
||||
ctx.Define(inst, EmitValueOrZeroIfCondition(state, condition, [&]() {
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpLoad, TypeU32(state), value,
|
||||
EmitMemoryElementPointer(state, access, element)});
|
||||
AddLoad(ctx, TypeU32(state), value,
|
||||
EmitMemoryElementPointer(state, access, element), mem.glc);
|
||||
return value;
|
||||
}));
|
||||
return true;
|
||||
@@ -1201,7 +1235,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, source});
|
||||
source = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, TypeU32(state), source, ctx.scratch_u32_variable});
|
||||
target = EmitDsSwizzleTargetLane(state, EmitSubgroupLocalInvocationId(state),
|
||||
target = EmitDsSwizzleTargetLane(state, EmitRawSubgroupInvocationId(state),
|
||||
inst.Arg(1).IsImmediate() ? inst.Arg(1).U32() : 0);
|
||||
} else {
|
||||
const auto index = Binary(
|
||||
@@ -1210,7 +1244,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
ConstantU32(state, 2)),
|
||||
ConstantU32(state, 31));
|
||||
const auto base =
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), EmitSubgroupLocalInvocationId(state),
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), EmitRawSubgroupInvocationId(state),
|
||||
ConstantU32(state, ~31u));
|
||||
target = Binary(state, OpBitwiseOr, TypeU32(state), base, index);
|
||||
}
|
||||
|
||||
@@ -188,9 +188,12 @@ void DefineDescriptorVariables(EmitterState& state) {
|
||||
TypeStorageBufferPointer(state), StorageClassStorageBuffer);
|
||||
}
|
||||
for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const bool integer = i >= SampledImageViewKindCount;
|
||||
const auto kind = SampledBindingKind(integer, view);
|
||||
const bool depth = i == SampledDepthImageIndex;
|
||||
const auto view =
|
||||
depth ? ImageViewKind::Dim2D
|
||||
: static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const bool integer = !depth && i >= SampledImageViewKindCount;
|
||||
const auto kind = SampledBindingKind(integer, view, depth);
|
||||
if (DescriptorBinding(state, kind) == nullptr) {
|
||||
continue;
|
||||
}
|
||||
@@ -607,11 +610,15 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
||||
"sampled_uint_2d_array",
|
||||
"sampled_uint_3d",
|
||||
"sampled_uint_2d_msaa",
|
||||
"sampled_uint_2d_msaa_array"};
|
||||
"sampled_uint_2d_msaa_array",
|
||||
"sampled_depth_2d"};
|
||||
for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) {
|
||||
const auto view = static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
const bool depth = i == SampledDepthImageIndex;
|
||||
const auto view =
|
||||
depth ? ImageViewKind::Dim2D
|
||||
: static_cast<ImageViewKind>(i % SampledImageViewKindCount);
|
||||
Decorate(state.sampled_image_variables[i], SampledNames[i],
|
||||
SampledBindingKind(i >= SampledImageViewKindCount, view));
|
||||
SampledBindingKind(!depth && i >= SampledImageViewKindCount, view, depth));
|
||||
}
|
||||
constexpr const char* StorageNames[] = {"storage_1d",
|
||||
"storage_1d_array",
|
||||
@@ -695,6 +702,10 @@ void DefineModule(EmitterState& state) {
|
||||
if (state.requirements.subgroup_ballot) {
|
||||
state.builder.RequireCapability(CapabilityGroupNonUniformBallot);
|
||||
}
|
||||
if (state.requirements.shader_clock) {
|
||||
state.builder.RequireCapability(CapabilityShaderClockKHR);
|
||||
state.builder.RequireExtension("SPV_KHR_shader_clock");
|
||||
}
|
||||
if (state.requirements.subgroup_shuffle) {
|
||||
state.builder.RequireCapability(CapabilityGroupNonUniformShuffle);
|
||||
}
|
||||
|
||||
@@ -94,9 +94,17 @@ void EmitReturn(ValueEmitContext& ctx) {
|
||||
ctx.state.builder.AddFunction({OpReturn});
|
||||
}
|
||||
|
||||
constexpr uint32_t kMaxLoopIterations = 0u;
|
||||
|
||||
constexpr size_t kLoopBoundMaxBlocks = 200ULL;
|
||||
|
||||
constexpr size_t kUnmergedLogMinBlocks = 100ULL;
|
||||
constexpr uint32_t kUnmergedLogLimit = 24u;
|
||||
|
||||
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
const IR::BlockInfo& info) {
|
||||
const auto& term = info.terminator;
|
||||
bool merged = false;
|
||||
const auto emit_merge = [&]() {
|
||||
if (term.loop_header) {
|
||||
const auto* merge = TargetBlock(ctx.program, term.merge_block);
|
||||
@@ -104,12 +112,14 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
if (merge != nullptr && cont != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpLoopMerge, ctx.Label(merge), ctx.Label(cont), LoopControlNone});
|
||||
merged = true;
|
||||
}
|
||||
} else if (term.kind == CFG::TerminatorKind::ConditionalBranch &&
|
||||
term.merge_block != UINT32_MAX) {
|
||||
if (const auto* merge = TargetBlock(ctx.program, term.merge_block); merge != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpSelectionMerge, ctx.Label(merge), SelectionControlNone});
|
||||
merged = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -305,13 +315,34 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
||||
const auto next_pc = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpPhi, TypeU32(state), pc, initial_pc, initial_parent, next_pc,
|
||||
dispatcher.continue_label});
|
||||
const bool bound_loop =
|
||||
kMaxLoopIterations != 0 && ctx.program.blocks.size() <= kLoopBoundMaxBlocks;
|
||||
const auto iter = bound_loop ? state.builder.AllocateId() : 0;
|
||||
const auto next_iter = bound_loop ? state.builder.AllocateId() : 0;
|
||||
if (bound_loop) {
|
||||
state.builder.AddFunction({OpPhi, TypeU32(state), iter, ConstantU32(state, 0),
|
||||
initial_parent, next_iter, dispatcher.continue_label});
|
||||
}
|
||||
const auto done = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpIEqual, TypeBool(state), done, pc, ConstantU32(ctx.state, UINT32_MAX)});
|
||||
auto stop = done;
|
||||
if (bound_loop) {
|
||||
const auto under = state.builder.AllocateId();
|
||||
const auto over = state.builder.AllocateId();
|
||||
const auto stopped = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpIAdd, TypeU32(state), next_iter, iter, ConstantU32(state, 1)});
|
||||
state.builder.AddFunction({OpULessThan, TypeBool(state), under, next_iter,
|
||||
ConstantU32(state, kMaxLoopIterations)});
|
||||
state.builder.AddFunction({OpLogicalNot, TypeBool(state), over, under});
|
||||
state.builder.AddFunction({OpLogicalOr, TypeBool(state), stopped, done, over});
|
||||
stop = stopped;
|
||||
}
|
||||
state.builder.AddFunction(
|
||||
{OpLoopMerge, dispatcher.merge_label, dispatcher.continue_label, LoopControlNone});
|
||||
state.builder.AddFunction(
|
||||
{OpBranchConditional, done, dispatcher.merge_label, dispatcher.select_label});
|
||||
{OpBranchConditional, stop, dispatcher.merge_label, dispatcher.select_label});
|
||||
|
||||
EmitLabel(state, dispatcher.select_label);
|
||||
state.builder.AddFunction(
|
||||
|
||||
@@ -244,6 +244,7 @@ Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const Mim
|
||||
case 0x08u: return Opcode::IMAGE_STORE;
|
||||
case 0x09u: return Opcode::IMAGE_STORE_MIP;
|
||||
case 0x0eu: return Opcode::IMAGE_GET_RESINFO;
|
||||
case 0xe6u: return Opcode::IMAGE_BVH_INTERSECT_RAY;
|
||||
case 0x60u: return Opcode::IMAGE_GET_LOD;
|
||||
default: return Opcode::UNSUPPORTED;
|
||||
}
|
||||
|
||||
@@ -234,6 +234,14 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.glc = ((word0 >> 16u) & 1u) != 0;
|
||||
inst.family = Family::SMEM;
|
||||
inst.opcode_id = opcode;
|
||||
if (opcode == 0x25u) {
|
||||
inst.opcode = Opcode::S_MEMREALTIME;
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
inst.src_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* info = Detail::FindOpcode(SMEM_OPS, opcode);
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
|
||||
@@ -45,6 +45,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = {
|
||||
{0x10u, Opcode::S_BCNT1_I32_B64},
|
||||
{0x13u, Opcode::S_FF1_I32_B32},
|
||||
{0x14u, Opcode::S_FF1_I32_B64},
|
||||
{0x15u, Opcode::S_FLBIT_I32_B32},
|
||||
{0x16u, Opcode::S_FLBIT_I32_B64},
|
||||
{0x1bu, Opcode::S_BITSET0_B32},
|
||||
{0x1du, Opcode::S_BITSET1_B32},
|
||||
|
||||
@@ -476,6 +476,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::S_ABS_I32:
|
||||
case Opcode::S_BREV_B32:
|
||||
case Opcode::S_BCNT1_I32_B32:
|
||||
case Opcode::S_FLBIT_I32_B32:
|
||||
case Opcode::S_FF1_I32_B32:
|
||||
case Opcode::S_FF1_I32_B64:
|
||||
case Opcode::S_NOT_B64:
|
||||
|
||||
@@ -45,6 +45,7 @@ enum class Opcode {
|
||||
S_BCNT1_I32_B64,
|
||||
S_FF1_I32_B32,
|
||||
S_FF1_I32_B64,
|
||||
S_FLBIT_I32_B32,
|
||||
S_FLBIT_I32_B64,
|
||||
S_BITREPLICATE_B64_B32,
|
||||
S_QUADMASK_B64,
|
||||
@@ -400,6 +401,8 @@ enum class Opcode {
|
||||
V_CMP_NE_U64,
|
||||
V_CMPX_NE_I64,
|
||||
V_CMPX_NE_U64,
|
||||
V_CMP_LT_U64,
|
||||
S_MEMREALTIME,
|
||||
V_CMPX_LT_U32,
|
||||
V_CMPX_EQ_U32,
|
||||
V_CMPX_LE_U32,
|
||||
@@ -526,6 +529,7 @@ enum class Opcode {
|
||||
DS_WRITE_ADDTID_B32,
|
||||
DS_READ_ADDTID_B32,
|
||||
IMAGE_GET_RESINFO,
|
||||
IMAGE_BVH_INTERSECT_RAY,
|
||||
IMAGE_GET_LOD,
|
||||
IMAGE_LOAD,
|
||||
IMAGE_LOAD_MIP,
|
||||
|
||||
@@ -89,9 +89,15 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, true, false);
|
||||
return true;
|
||||
case O::S_CMP_EQ_U64:
|
||||
if (S_CMP_MASK_ANY(inst, false)) {
|
||||
return true;
|
||||
}
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, true, false);
|
||||
return true;
|
||||
case O::S_CMP_LG_U64:
|
||||
if (S_CMP_MASK_ANY(inst, true)) {
|
||||
return true;
|
||||
}
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, true, false);
|
||||
return true;
|
||||
|
||||
|
||||
@@ -283,6 +283,7 @@ enum class DescriptorBindingKind {
|
||||
Sampled2DMsaa,
|
||||
Sampled2DMsaaArray,
|
||||
Sampled3D,
|
||||
SampledDepth2D,
|
||||
SampledUint1D,
|
||||
SampledUint1DArray,
|
||||
SampledUint2D,
|
||||
@@ -328,6 +329,9 @@ DescriptorBindingForImage(const ImageResource& image) {
|
||||
|
||||
switch (image.kind) {
|
||||
case ResourceKind::Image:
|
||||
if (image.depth_compare && image.dimension == Dimension::Dim2D) {
|
||||
return Kind::SampledDepth2D;
|
||||
}
|
||||
switch (image.dimension) {
|
||||
case Dimension::Dim1D: return Kind::Sampled1D;
|
||||
case Dimension::Dim1DArray: return Kind::Sampled1DArray;
|
||||
@@ -417,7 +421,8 @@ struct BindingLayout {
|
||||
};
|
||||
|
||||
struct ShaderInfo {
|
||||
static constexpr uint32_t MaxBuffers = 32;
|
||||
static constexpr uint32_t MaxBuffers = 64;
|
||||
static constexpr uint32_t MaxAddresses = 32;
|
||||
static constexpr uint32_t MaxImages = 32;
|
||||
static constexpr uint32_t MaxSamplers = 32;
|
||||
static constexpr uint32_t MaxSampledPairs = 64;
|
||||
@@ -445,6 +450,7 @@ struct SpirvRequirements {
|
||||
bool function_lds = false;
|
||||
bool function_scratch = false;
|
||||
bool pixel_valid_mask = false;
|
||||
bool shader_clock = false;
|
||||
};
|
||||
|
||||
struct BlockInfo {
|
||||
|
||||
@@ -132,6 +132,7 @@ VALUE_OPCODE(SLessThan32, U1, U32, U32)
|
||||
VALUE_OPCODE(SLessThan64, U1, U64, U64)
|
||||
VALUE_OPCODE(ULessThan32, U1, U32, U32)
|
||||
VALUE_OPCODE(ULessThan64, U1, U64, U64)
|
||||
VALUE_OPCODE(ReadClockRealtime64, U64)
|
||||
VALUE_OPCODE(IEqual32, U1, U32, U32)
|
||||
VALUE_OPCODE(IEqual64, U1, U64, U64)
|
||||
VALUE_OPCODE(SLessThanEqual32, U1, U32, U32)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
@@ -412,6 +413,21 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> DeriveFlatWindowBase(const Program& program, const ResourceSnapshot& next) {
|
||||
uint64_t lowest = 0;
|
||||
for (uint32_t index = 0; index < program.info.buffers.size() && index < next.buffers.size();
|
||||
index++) {
|
||||
ShaderBufferResource descriptor {};
|
||||
if (!DecodeBufferDescriptor(next.buffers[index], descriptor)) {
|
||||
continue;
|
||||
}
|
||||
if (const auto base = descriptor.Base48(); base != 0 && (lowest == 0 || base < lowest)) {
|
||||
lowest = base;
|
||||
}
|
||||
}
|
||||
return lowest != 0 ? std::optional<uint64_t> {lowest} : std::nullopt;
|
||||
}
|
||||
|
||||
bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
ResourceSnapshot& snapshot) {
|
||||
if (!program.resource_tracking_complete) {
|
||||
|
||||
Reference in New Issue
Block a user