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:
Mac
2026-08-31 23:41:38 +05:30
parent b56807b75d
commit ed44795511
18 changed files with 205 additions and 36 deletions
@@ -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;
+7 -1
View File
@@ -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) {