Compare commits

...

12 Commits

Author SHA1 Message Date
nmzik 0c4dfd565e renderer: emulate clip-disabled screen-space positions 2026-09-01 21:39:11 +04:00
nmzik 0c8d7a1e49 shader: DPP row-xmask 2026-09-01 21:02:19 +04:00
nmzik a2150c70bb network: implement Linux getsockname 2026-09-01 20:50:57 +04:00
nmzik 160cf5352c http: new abi 2026-09-01 20:50:57 +04:00
nmzik 3b6b3c2040 pm4: handle reserved depth control fields 2026-09-01 20:50:57 +04:00
nmzik 63dd3917e2 reduce runtime logging 2026-09-01 20:50:57 +04:00
nmzik 2333c235f1 loader: TLS patch fix 2026-09-01 20:50:57 +04:00
nmzik dd062ceeeb prevent Escape from exiting 2026-09-01 07:50:37 +04:00
nmzik e591a66b67 Revert "shader: preserve raw VCC bits in S_MOV_B64 (#418)"
This reverts commit 6758d67942.

An EXEC pair loses its mask provenance and falls into the raw high-word path. The saved all-ones high word then keeps waterfall branches true and wedges GPU work.
2026-09-01 07:50:37 +04:00
Brandon Strong b8a459e83d graphics: do not arm a fast clear from a read-modify-write dispatch (#428)
* graphics: do not arm a fast clear from a read-modify-write dispatch

* tests: cover compute metadata clear classification

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-01 05:48:06 +04:00
nmzik 7684c343d6 guest_gpu: fix private AGC register regression 2026-09-01 04:24:47 +04:00
CheesyPoofs3460 e0c055fd74 renderer: decode R32_SFLOAT packed colour clears (#436)
* renderer: decode R32_SFLOAT packed colour clears

DecodePackedColorClear handled R32Uint, R32Sint and the 8/10-bit formats but fell through to the default false case for R32Sfloat. metadata_clear_supported then went false, the clear value was discarded, and the target kept its previous contents. Decode the packed word as float bits, matching DecodePackedDepthClear directly below, and reject non-finite patterns rather than materializing them.

* renderer: preserve all R32_SFLOAT clear bits

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-01 03:37:55 +04:00
33 changed files with 671 additions and 532 deletions
+2
View File
@@ -574,6 +574,8 @@ if(BUILD_TESTING)
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --storage-sampled-only)
add_test(NAME texture_cache_depth_readback
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-readback-only)
add_test(NAME compute_meta_clear_classification
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --compute-meta-clear-only)
add_test(NAME buffer_cache_dirty_gc
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --buffer-cache-gc-only)
@@ -136,7 +136,6 @@ static HW::ColorControl DecodeColorControl(uint32_t value) {
static HW::DepthControl DecodeDepthControl(uint32_t value) {
HW::DepthControl r {};
EXIT_NOT_IMPLEMENTED((value & 0xc0000000u) != 0);
r.stencil_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
r.z_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_ENABLE) != 0;
@@ -3635,6 +3634,9 @@ void GraphicsInitJmpTablesShIndirect() {
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PACE_ID_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
g_hw_sh_indirect_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
@@ -3644,6 +3646,12 @@ void GraphicsInitJmpTablesShIndirect() {
g_hw_sh_indirect_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_CHKSUM_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
g_hw_sh_indirect_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
HwShIgnoreShaderRegister(cmd_offset, value);
};
@@ -3818,6 +3826,11 @@ void GraphicsInitJmpTablesUcIndirect() {
r.vertex_group_size = KYTY_PM4_GET(value, GE_CNTL, VERT_GRP_SIZE);
cp.GetUcfg().SetGeControl(r);
};
g_hw_uc_indirect_func[Pm4::UC_PARAMETER_OVERSUBSCRIPTION] = [](KYTY_HW_UC_INDIRECT_ARGS) {
(void)cp;
(void)cmd_offset;
(void)value;
};
g_hw_uc_indirect_func[Pm4::GE_USER_VGPR_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) {
HW::GeUserVgprEn r;
r.vgpr1 = KYTY_PM4_GET(value, GE_USER_VGPR_EN, EN_USER_VGPR1) != 0;
+3 -2
View File
@@ -834,8 +834,9 @@ constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_0 = 0xB2;
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_3 = 0xB5;
constexpr uint32_t SPI_SHADER_PGM_LO_ES = 0xC8;
constexpr uint32_t SPI_SHADER_PGM_HI_ES = 0xC9;
// Retained only as an opaque AGC compiler-record tag; 0x100 is not a PS5 SH register.
// Private AGC registers used by shader binaries submitted through SET_SH_REG_INDIRECT.
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100;
constexpr uint32_t SPI_SHADER_PGM_RSRC4_HS = 0x101;
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_HS = 0x102;
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_HS = 0x103;
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_HS = 0x107;
@@ -976,7 +977,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C;
constexpr uint32_t GE_USER_VGPR2 = 0x25D;
constexpr uint32_t GE_USER_VGPR3 = 0x25E;
constexpr uint32_t GE_STEREO_CNTL = 0x25F;
constexpr uint32_t GE_PC_ALLOC = 0x260;
constexpr uint32_t UC_PARAMETER_OVERSUBSCRIPTION = 0x260;
constexpr uint32_t GE_USER_VGPR_EN = 0x262;
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0;
+2 -2
View File
@@ -490,7 +490,7 @@ static void ClipCheck(const HW::ClipControl& c) {
// dx_linear_attr_clip_enable preserves linear (noperspective) attributes at clip-generated
// vertices, which Vulkan provides as part of clipping and interpolation.
EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0 || c.user_clip_plane_mode != 0 ||
c.vertex_kill_any || c.user_clip_plane_negate_y || c.clip_disable ||
c.vertex_kill_any || c.user_clip_plane_negate_y ||
c.user_clip_plane_cull_only || c.cull_on_clipping_error_disable ||
c.force_viewport_index_from_vs_enable);
}
@@ -1079,7 +1079,7 @@ void hw_check(const CommandBuffer& buffer) {
AaCheck(aa, ac);
log_phase("done");
if (RenderTargetMaskHasBoundMrt(buffer)) {
if (graphics_debug_dump_enabled() && RenderTargetMaskHasBoundMrt(buffer)) {
LOGF("MRT render target mask: 0x%08" PRIx32 "\n", hw.GetRenderTargetMask());
for (uint32_t i = 0; i < 8; i++) {
const auto& mrt = hw.GetRenderTarget(i);
@@ -431,6 +431,10 @@ IsSupportedDisplayRenderTargetTileMode(Prospero::TileMode tile_mode) noexcept {
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
};
switch (format) {
// A single-plane float target carries its clear as raw float bits, the same encoding the
// depth decoder below uses. Without this the clear is discarded and the target keeps stale
// contents.
case vk::Format::eR32Sfloat: next.float32[0] = std::bit_cast<float>(packed); break;
case vk::Format::eR32Uint: next.uint32[0] = packed; break;
case vk::Format::eR32Sint: next.int32[0] = static_cast<int32_t>(packed); break;
case vk::Format::eR8G8B8A8Srgb:
@@ -116,7 +116,7 @@ struct PipelineCache::ProgramCache {
}
static constexpr std::size_t MaxStaticKeyWords =
5 + ShaderVertexInputInfo::RES_MAX * 17;
12 + ShaderVertexInputInfo::RES_MAX * 17;
template <typename InputInfo>
ShaderProgram Get(const ShaderParams& params, InputInfo& input_info) {
@@ -340,8 +340,23 @@ void PipelineCache::Save() {
ShaderProgram PipelineCache::GetVertexProgram(const HW::VertexShaderInfo& regs,
const HW::ShaderRegisters& sh,
const HW::Context& context,
ShaderVertexInputInfo& input_info) {
const auto params = PrepareProgram(regs, sh, input_info);
if (context.GetClipControl().clip_disable) {
const auto& viewport = context.GetScreenViewport().viewports[0];
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
auto& clip = input_info.clip_space;
clip.scale[0] = viewport.xscale;
clip.scale[1] = viewport.yscale;
clip.offset[0] = viewport.xoffset;
clip.offset[1] = viewport.yoffset;
clip.half_extent[0] =
static_cast<float>(std::min(limits.maxViewportDimensions[0], 16384u)) * 0.5f;
clip.half_extent[1] =
static_cast<float>(std::min(limits.maxViewportDimensions[1], 16384u)) * 0.5f;
clip.enabled = true;
}
Common::LockGuard lock(m_mutex);
return m_program_cache->Get(params, input_info);
}
@@ -130,6 +130,7 @@ public:
ShaderProgram GetVertexProgram(const HW::VertexShaderInfo& regs,
const HW::ShaderRegisters& sh,
const HW::Context& context,
ShaderVertexInputInfo& input_info);
ShaderProgram
GetPixelProgram(const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh,
@@ -54,7 +54,9 @@ bool RenderExecutor::TryConsumeComputeMetaClear(const ShaderComputeInputInfo& in
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
const auto& resource = program.info.buffers[i];
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[i]);
if (!resource.written && cache.IsMeta(descriptor.Base48())) {
// A metadata resource that is also read is not proven to be a full overwrite. Execute it
// conservatively instead of replacing the dispatch with a coarse full-surface clear.
if (cache.IsMeta(descriptor.Base48()) && (!resource.written || resource.read)) {
return false;
}
}
+14 -6
View File
@@ -331,10 +331,18 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
const auto final_scissor = calc_final_scissor(vp, ctx.GetScanModeControl(), framebuffer_extent);
vk::Viewport viewport {};
viewport.x = vp.viewports[0].xoffset - vp.viewports[0].xscale;
viewport.y = vp.viewports[0].yoffset - vp.viewports[0].yscale;
viewport.width = vp.viewports[0].xscale * 2.0f;
viewport.height = vp.viewports[0].yscale * 2.0f;
if (ctx.GetClipControl().clip_disable) {
const auto& limits = buffer.GetGraphics().GetPhysicalDeviceProperties().limits;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(std::min(limits.maxViewportDimensions[0], 16384u));
viewport.height = static_cast<float>(std::min(limits.maxViewportDimensions[1], 16384u));
} else {
viewport.x = vp.viewports[0].xoffset - vp.viewports[0].xscale;
viewport.y = vp.viewports[0].yoffset - vp.viewports[0].yscale;
viewport.width = vp.viewports[0].xscale * 2.0f;
viewport.height = vp.viewports[0].yscale * 2.0f;
}
viewport.minDepth = vp.viewports[0].zoffset;
viewport.maxDepth = vp.viewports[0].zscale + vp.viewports[0].zoffset;
vk_buffer.setViewport(0, 1, &viewport);
@@ -1013,8 +1021,8 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool
LogDrawPhase(draw.name, "GetVertexProgram");
}
auto& pipeline_cache = buffer.GetContext().GetPipelineCache();
state.vertex_program =
pipeline_cache.GetVertexProgram(vertex_shader_info, shader_regs, state.vs_input_info);
state.vertex_program = pipeline_cache.GetVertexProgram(vertex_shader_info, shader_regs, ctx,
state.vs_input_info);
if (!state.ps_active) {
return;
@@ -242,7 +242,6 @@ static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX
if (key.down) {
switch (key.key_code) {
case SDLK_ESCAPE: game.need_exit = true; break;
case SDLK_SPACE: SetPause(game, !game.paused.load(std::memory_order_acquire)); break;
case SDLK_F1:
if (!key.repeat) {
@@ -408,6 +408,41 @@ void EmitAuxPositionExport(ValueEmitContext& ctx, uint32_t data, const IR::Expor
}
}
uint32_t ConvertClipCoordinate(EmitterState& state, uint32_t coordinate, float scale,
float offset, float half_extent) {
const auto window = state.builder.AllocateId();
const auto biased = state.builder.AllocateId();
const auto divided = state.builder.AllocateId();
const auto ndc = state.builder.AllocateId();
state.builder.AddFunction(
{OpFMul, TypeF32(state), window, coordinate, ConstantF32Value(state, scale)});
state.builder.AddFunction(
{OpFAdd, TypeF32(state), biased, window, ConstantF32Value(state, offset)});
state.builder.AddFunction(
{OpFDiv, TypeF32(state), divided, biased, ConstantF32Value(state, half_extent)});
state.builder.AddFunction(
{OpFSub, TypeF32(state), ndc, divided, ConstantF32Value(state, 1.0f)});
return ndc;
}
uint32_t ConvertPositionToClipSpace(EmitterState& state, uint32_t position) {
const auto& transform = state.input_info.vertex->clip_space;
uint32_t components[4] {};
for (uint32_t i = 0; i < 4; i++) {
components[i] = state.builder.AllocateId();
state.builder.AddFunction(
{OpCompositeExtract, TypeF32(state), components[i], position, i});
}
components[0] = ConvertClipCoordinate(state, components[0], transform.scale[0],
transform.offset[0], transform.half_extent[0]);
components[1] = ConvertClipCoordinate(state, components[1], transform.scale[1],
transform.offset[1], transform.half_extent[1]);
const auto converted = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeConstruct, TypeF32Vector(state, 4), converted,
components[0], components[1], components[2], components[3]});
return converted;
}
void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
const auto& exp = ctx.Export(inst);
@@ -467,6 +502,9 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) {
}
}
if (exp.kind == IR::ExportTargetKind::Position) {
if (state.stage == ShaderType::Vertex && state.input_info.vertex->clip_space.enabled) {
value = ConvertPositionToClipSpace(state, value);
}
const auto pointer = state.builder.AllocateId();
state.builder.AddFunction(
{OpAccessChain, TypePointer(state, StorageClassOutput, TypeF32Vector(state, 4)),
@@ -110,6 +110,12 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
if (control == 0x141u) {
return EmitDppMirrorTargetLane(state, subid, true);
}
if (control >= 0x160u && control <= 0x16fu) {
const auto target = state.builder.AllocateId();
state.builder.AddFunction(
{OpBitwiseXor, TypeU32(state), target, subid, ConstantU32(state, control & 0xfu)});
return {target, EmitTrueBool(state)};
}
return {subid, EmitTrueBool(state)};
}
@@ -12,7 +12,6 @@ void Translator::EmitCompareResult(const Decoder::Instruction& inst, IR::U1 valu
if (cmpx) {
const auto mask = BallotMask(masked);
ir.SetExec(masked);
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
return;
@@ -24,28 +24,18 @@ Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
bool negate_exec, bool negate_source, bool write_64) {
if (write_64) {
const auto exec_operand = ConditionOperand(Decoder::OperandKind::ExecLo);
const auto old = ReadScalarU64(exec_operand);
const auto bit_operation = operation == IR::ValueOpcode::LogicalAnd
? IR::ValueOpcode::BitwiseAnd32
: IR::ValueOpcode::BitwiseOr32;
const auto result = BinaryScalarU64(old, ReadScalarU64(inst.src0), operation, bit_operation,
negate_exec, negate_source, false);
WriteScalarU64(inst.dst, old);
WriteScalarU64(exec_operand, result);
ir.SetScc(ScalarU64NonZero(result));
return;
}
const auto old = ir.GetExec();
const auto src = ReadMask(inst.src0);
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
WriteMask(inst.dst, old);
if (write_64) {
WriteMask64(inst.dst, old);
} else {
WriteMask(inst.dst, old);
}
const auto mask = BallotMask(result);
ir.SetExec(result);
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
ir.SetScc(result);
@@ -201,21 +191,30 @@ void Translator::S_CSELECT_B32(const Decoder::Instruction& inst) {
}
void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
const auto condition = ir.GetScc();
const auto lhs = ReadScalarU64(inst.src0);
const auto rhs = ReadScalarU64(inst.src1);
WriteScalarU64(inst.dst, SelectScalarU64(condition, lhs, rhs));
const auto condition = ir.GetScc();
const auto lhs = ReadU32Pair(inst.src0);
const auto rhs = ReadU32Pair(inst.src1);
const auto selected_mask = IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
const auto selected_mask_valid =
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
if (IsExecOrVcc(inst.dst)) {
WriteMask64(inst.dst, selected_mask);
return;
}
WriteU32Pair(inst.dst,
{ir.Select(condition, lhs[0], rhs[0]), ir.Select(condition, lhs[1], rhs[1])});
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, selected_mask);
ir.SetScalarMaskTag(dst, selected_mask_valid);
}
}
void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers) {
if (inst.src0.kind == Decoder::OperandKind::Sgpr &&
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.reg == inst.dst.reg) {
return;
}
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
if (inst.src0.kind != inst.dst.kind) {
WriteRawU32(inst.dst, ReadRawU32(inst.src0));
}
WriteMask(inst.dst, ReadMask(inst.src0));
} else if (apply_float_modifiers && (inst.src0.negate || inst.src0.absolute)) {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
} else {
@@ -224,17 +223,50 @@ void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modi
}
void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
WriteScalarU64(inst.dst, ReadScalarU64(inst.src0));
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
WriteMask64(inst.dst, ReadMask(inst.src0));
return;
}
const bool scalar_copy =
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.kind == Decoder::OperandKind::Sgpr;
IR::U1 source_mask;
IR::U1 source_mask_valid;
if (scalar_copy) {
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
}
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
if (scalar_copy) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
}
}
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
const auto source = ReadScalarU64(inst.src0);
const auto raw = ExtractU64(
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ir.ConstructU64(source.raw[0], source.raw[1])})));
const auto invocation = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {source.invocation}));
const auto result = MakeScalarU64Result(raw, invocation, source.mask_valid);
WriteScalarU64(inst.dst, result);
ir.SetScc(ScalarU64NonZero(result));
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
const auto mask_valid = ReadMaskValid(inst.src0);
const auto invocation_result =
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
const auto result =
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
}
return;
}
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
WriteMask64(inst.dst, result);
ir.SetScc(result);
}
void Translator::V_MOVRELS_B32(const Decoder::Instruction& inst) {
@@ -77,15 +77,68 @@ bool Translator::PackedInteger16MinMax(const Decoder::Instruction& inst, IR::Val
return true;
}
IR::U1 Translator::U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool negate_rhs, bool negate_result) {
const auto lhs = ReadMask(inst.src0);
auto rhs = ReadMask(inst.src1);
if (negate_rhs) {
rhs = ir.LogicalNot(rhs);
}
auto result = IR::U1(ir.Emit(opcode, {lhs, rhs}));
return negate_result ? ir.LogicalNot(result) : result;
}
bool Translator::S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result,
bool unary) {
const auto lhs = ReadScalarU64(inst.src0);
const auto result = unary ? NotScalarU64(lhs)
: BinaryScalarU64(lhs, ReadScalarU64(inst.src1), logical_opcode,
bit_opcode, false, negate_rhs, negate_result);
WriteScalarU64(inst.dst, result);
ir.SetScc(ScalarU64NonZero(result));
const auto invocation_result =
unary ? ir.LogicalNot(ReadMask(inst.src0))
: U64MaskBinary(inst, logical_opcode, negate_rhs, negate_result);
const auto is_exec_or_vcc = [](const Decoder::Operand& operand) {
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi:
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: return true;
default: return false;
}
};
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
(inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
WriteMask64(inst.dst, invocation_result);
ir.SetScc(invocation_result);
return true;
}
const auto lhs = ReadU32Pair(inst.src0);
auto mask_valid = ReadMaskValid(inst.src0);
if (inst.src_count > 1u) {
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src1));
}
std::array<IR::U32, 2> result;
if (unary) {
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
} else {
const auto rhs = ReadU32Pair(inst.src1);
for (uint32_t component = 0; component < 2u; component++) {
auto value = ir.Emit(
bit_opcode, {lhs[component], negate_rhs ? IR::Value(ir.BitwiseNot(rhs[component]))
: IR::Value(rhs[component])});
result[component] = negate_result ? ir.BitwiseNot(IR::U32(value)) : IR::U32(value);
}
}
WriteU32Pair(inst.dst, result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u)));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
}
return true;
}
@@ -55,14 +55,11 @@ uint32_t ResourceIndexFromOperand(const Decoder::Operand& operand) {
}
}
Decoder::Operand RawScalarLoadBase(const Decoder::Operand& operand) {
if (operand.kind != Decoder::OperandKind::M0) {
return operand;
uint32_t RawScalarLoadBase(const Decoder::Operand& operand) {
if (operand.kind == Decoder::OperandKind::Sgpr) {
return operand.reg;
}
auto result = operand;
// The compressed SBASE value 62 decodes as code 124, but means NULL for S_LOAD.
result.kind = Decoder::OperandKind::Null;
return result;
return operand.kind == Decoder::OperandKind::VccLo ? 106u : 0u;
}
ResourceKind FlatSegmentResourceKind(uint32_t segment) {
@@ -322,9 +319,8 @@ Translator::AddressOperands Translator::ReadAddressOperands(const Decoder::Instr
return {GetAddressResource(low, high), low, high};
}
IR::Value Translator::GetScalarAddressResource(const Decoder::Operand& base) {
const auto raw = ReadU32Pair(base);
return GetAddressResource(raw[0], raw[1]);
IR::Value Translator::GetScalarAddressResource(uint32_t base) {
return GetAddressResource(ReadScalarCode(base), ReadScalarCode(base + 1u));
}
IR::Value Translator::GetImageResource(const IR::MemoryInfo& memory) {
@@ -151,8 +151,11 @@ IR::U32 Translator::ReadScalarCode(uint32_t code) {
case 106u: return ir.GetVccLo();
case 107u: return ir.GetVccHi();
case 124u: return ir.GetM0();
case 126u: return ir.GetExecLo();
case 127u: return ir.GetExecHi();
case 126u:
case 127u: {
const auto mask = BallotMask(ir.GetExec());
return mask[code - 126u];
}
default: return IR::U32(IR::Value(0u));
}
}
@@ -312,23 +315,19 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
case Decoder::OperandKind::VccLo:
ir.SetVccLo(IR::U32(value));
ir.SetVcc(ThreadBit(IR::U32(value)));
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
break;
case Decoder::OperandKind::VccHi:
ir.SetVccHi(IR::U32(value));
ir.SetVcc(ThreadBit(ir.GetVccLo()));
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
break;
case Decoder::OperandKind::M0: ir.SetM0(IR::U32(value)); break;
case Decoder::OperandKind::ExecLo:
ir.SetExecLo(IR::U32(value));
ir.SetExec(ThreadBit(IR::U32(value)));
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
break;
case Decoder::OperandKind::ExecHi:
ir.SetExecHi(IR::U32(value));
ir.SetExec(ThreadBit(ir.GetExecLo()));
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
break;
case Decoder::OperandKind::Scc:
ir.SetScc(ir.INotEqual(value, IR::U32(IR::Value(0u))));
@@ -373,7 +372,6 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
case Decoder::OperandKind::ExecHi: {
const auto mask = BallotMask(IR::U1(value));
ir.SetExec(IR::U1(value));
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
return;
@@ -382,7 +380,6 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
case Decoder::OperandKind::VccHi: {
const auto mask = BallotMask(IR::U1(value));
ir.SetVcc(IR::U1(value));
ir.SetVccMaskValidTag(IR::U1(IR::Value(true)));
ir.SetVccLo(mask[0]);
ir.SetVccHi(mask[1]);
return;
@@ -583,13 +580,11 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
ir.SetExec(thread_bit);
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
ir.SetExecLo(value[0]);
ir.SetExecHi(value[1]);
return;
case Decoder::OperandKind::VccLo:
ir.SetVcc(thread_bit);
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
ir.SetVccLo(value[0]);
ir.SetVccHi(value[1]);
return;
@@ -600,87 +595,6 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
WriteRawU32(OffsetOperand(operand, 1), value[1]);
}
Translator::ScalarU64 Translator::ReadScalarU64(const Decoder::Operand& operand) {
return {ReadU32Pair(operand), ReadMask(operand), ReadMaskValid(operand)};
}
Translator::ScalarU64 Translator::MakeScalarU64Mask(IR::U1 value) {
return {BallotMask(value), value, IR::U1(IR::Value(true))};
}
Translator::ScalarU64 Translator::MakeScalarU64Result(const std::array<IR::U32, 2>& raw,
IR::U1 invocation, IR::U1 mask_valid) {
return {raw,
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation, ThreadBit(raw[0])})),
mask_valid};
}
Translator::ScalarU64 Translator::SelectScalarU64(IR::U1 condition, const ScalarU64& lhs,
const ScalarU64& rhs) {
return {
{ir.Select(condition, lhs.raw[0], rhs.raw[0]),
ir.Select(condition, lhs.raw[1], rhs.raw[1])},
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, lhs.invocation, rhs.invocation})),
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, lhs.mask_valid, rhs.mask_valid}))};
}
Translator::ScalarU64 Translator::NotScalarU64(const ScalarU64& value) {
return MakeScalarU64Result({ir.BitwiseNot(value.raw[0]), ir.BitwiseNot(value.raw[1])},
ir.LogicalNot(value.invocation), value.mask_valid);
}
Translator::ScalarU64 Translator::BinaryScalarU64(const ScalarU64& lhs, const ScalarU64& rhs,
IR::ValueOpcode logical_opcode,
IR::ValueOpcode bit_opcode, bool negate_lhs,
bool negate_rhs, bool negate_result) {
auto lhs_mask = negate_lhs ? ir.LogicalNot(lhs.invocation) : lhs.invocation;
auto rhs_mask = negate_rhs ? ir.LogicalNot(rhs.invocation) : rhs.invocation;
auto mask = IR::U1(ir.Emit(logical_opcode, {lhs_mask, rhs_mask}));
if (negate_result) {
mask = ir.LogicalNot(mask);
}
std::array<IR::U32, 2> raw;
for (uint32_t component = 0; component < raw.size(); component++) {
auto lhs_raw = negate_lhs ? ir.BitwiseNot(lhs.raw[component]) : lhs.raw[component];
auto rhs_raw = negate_rhs ? ir.BitwiseNot(rhs.raw[component]) : rhs.raw[component];
raw[component] = IR::U32(ir.Emit(bit_opcode, {lhs_raw, rhs_raw}));
if (negate_result) {
raw[component] = ir.BitwiseNot(raw[component]);
}
}
return MakeScalarU64Result(raw, mask, ir.LogicalAnd(lhs.mask_valid, rhs.mask_valid));
}
void Translator::WriteScalarU64(const Decoder::Operand& operand, const ScalarU64& value) {
WriteU32Pair(operand, value.raw);
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi:
ir.SetExec(value.invocation);
ir.SetExecMaskTag(value.mask_valid);
break;
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi:
ir.SetVcc(value.invocation);
ir.SetVccMaskValidTag(value.mask_valid);
break;
case Decoder::OperandKind::Sgpr: {
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
ir.SetThreadBitScalarReg(reg, value.invocation);
ir.SetScalarMaskTag(reg, value.mask_valid);
break;
}
default: break;
}
}
IR::U1 Translator::ScalarU64NonZero(const ScalarU64& value) {
const auto raw_nonzero =
ir.INotEqual(ir.BitwiseOr(value.raw[0], value.raw[1]), IR::U32(IR::Value(0u)));
return IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {value.mask_valid, value.invocation, raw_nonzero}));
}
IR::U1 Translator::ThreadBit(IR::U32 low) {
return ir.INotEqual(low, IR::U32(IR::Value(0u)));
}
@@ -736,9 +650,9 @@ IR::U1 Translator::ReadMaskValid(const Decoder::Operand& operand) {
case Decoder::OperandKind::Sgpr:
return ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(operand.reg));
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi: return ir.GetExecMaskTag();
case Decoder::OperandKind::ExecHi:
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: return ir.GetVccMaskValidTag();
case Decoder::OperandKind::VccHi:
case Decoder::OperandKind::VccZ:
case Decoder::OperandKind::ExecZ:
case Decoder::OperandKind::Scc: return IR::U1(IR::Value(true));
@@ -771,7 +685,6 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
case Decoder::OperandKind::ExecHi: {
const auto mask = BallotMask(value);
ir.SetExec(value);
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
return;
@@ -780,7 +693,6 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
case Decoder::OperandKind::VccHi: {
const auto mask = BallotMask(value);
ir.SetVcc(value);
ir.SetVccMaskValidTag(IR::U1(IR::Value(true)));
ir.SetVccLo(mask[0]);
ir.SetVccHi(mask[1]);
return;
@@ -794,7 +706,25 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
}
void Translator::WriteMask64(const Decoder::Operand& operand, IR::U1 value) {
WriteScalarU64(operand, MakeScalarU64Mask(value));
if (operand.kind != Decoder::OperandKind::Sgpr) {
WriteMask(operand, value);
return;
}
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
const auto mask = BallotMask(value);
ir.SetThreadBitScalarReg(reg, value);
ir.SetScalarMaskTag(reg, IR::U1(IR::Value(true)));
if (IR::RegIndex(reg) > 0u) {
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(IR::RegIndex(reg) - 1u),
IR::U1(IR::Value(false)));
}
ir.SetScalarReg(reg, mask[0]);
if (IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
ir.SetScalarReg(high, mask[1]);
ir.SetThreadBitScalarReg(high, IR::U1(IR::Value(false)));
ir.SetScalarMaskTag(high, IR::U1(IR::Value(false)));
}
}
void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 value) {
@@ -1095,7 +1025,6 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
entry_ir.SetScalarMaskTag(reg, IR::U1(IR::Value(false)));
}
entry_ir.SetExec(IR::U1(IR::Value(true)));
entry_ir.SetExecMaskTag(IR::U1(IR::Value(true)));
entry_ir.SetExecLo(IR::U32(IR::Value(1u)));
entry_ir.SetExecHi(IR::U32(IR::Value(0u)));
if (options.stage == ShaderType::Compute) {
@@ -19,14 +19,6 @@ public:
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
private:
struct ScalarU64 {
std::array<IR::U32, 2> raw;
// Raw words and Kyty's per-invocation mask model remain independent.
// mask_valid selects invocation for logical consumers and SCC.
IR::U1 invocation;
IR::U1 mask_valid;
};
Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
@@ -56,25 +48,14 @@ private:
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
ScalarU64 ReadScalarU64(const Decoder::Operand& operand);
ScalarU64 MakeScalarU64Mask(IR::U1 value);
ScalarU64 MakeScalarU64Result(const std::array<IR::U32, 2>& raw, IR::U1 invocation,
IR::U1 mask_valid);
ScalarU64 SelectScalarU64(IR::U1 condition, const ScalarU64& lhs, const ScalarU64& rhs);
ScalarU64 NotScalarU64(const ScalarU64& value);
ScalarU64 BinaryScalarU64(const ScalarU64& lhs, const ScalarU64& rhs,
IR::ValueOpcode logical_opcode, IR::ValueOpcode bit_opcode,
bool negate_lhs, bool negate_rhs, bool negate_result);
void WriteScalarU64(const Decoder::Operand& operand, const ScalarU64& value);
IR::U1 ScalarU64NonZero(const ScalarU64& value);
IR::U1 ReadCondition(const Decoder::Operand& operand);
IR::U32 ConditionBit(const Decoder::Operand& operand);
IR::U1 ReadMask(const Decoder::Operand& operand);
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
IR::U1 ReadCondition(const Decoder::Operand& operand);
IR::U32 ConditionBit(const Decoder::Operand& operand);
IR::U1 ReadMask(const Decoder::Operand& operand);
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
IR::MemoryFlags AddMemoryInfo(const IR::MemoryInfo& memory, uint32_t pc);
IR::ExportFlags AddExportInfo(const Decoder::Instruction& inst);
@@ -92,7 +73,7 @@ private:
IR::U32 GetResourceDword(uint32_t index, uint32_t dword);
IR::Value GetBufferResource(const IR::MemoryInfo& memory);
IR::Value GetAddressResource(IR::Value low, IR::Value high);
IR::Value GetScalarAddressResource(const Decoder::Operand& base);
IR::Value GetScalarAddressResource(uint32_t base);
IR::Value GetImageResource(const IR::MemoryInfo& memory);
IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
@@ -194,6 +175,8 @@ private:
bool PackedInteger16MinMax(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
bool S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result, bool unary);
IR::U1 U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool negate_rhs,
bool negate_result);
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
bool reverse, bool mask_shift_count, bool update_scc);
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
@@ -76,14 +76,6 @@ void IREmitter::SetExec(U1 value) {
Emit(ValueOpcode::SetExec, {value});
}
U1 IREmitter::GetExecMaskTag() {
return U1(Emit(ValueOpcode::GetExecMaskTag));
}
void IREmitter::SetExecMaskTag(U1 value) {
Emit(ValueOpcode::SetExecMaskTag, {value});
}
U32 IREmitter::GetExecLo() {
return U32(Emit(ValueOpcode::GetExecLo));
}
@@ -108,14 +100,6 @@ void IREmitter::SetVcc(U1 value) {
Emit(ValueOpcode::SetVcc, {value});
}
U1 IREmitter::GetVccMaskValidTag() {
return U1(Emit(ValueOpcode::GetVccMaskValidTag));
}
void IREmitter::SetVccMaskValidTag(U1 value) {
Emit(ValueOpcode::SetVccMaskValidTag, {value});
}
U32 IREmitter::GetVccLo() {
return U32(Emit(ValueOpcode::GetVccLo));
}
@@ -39,16 +39,12 @@ public:
void SetScc(U1 value);
U1 GetExec();
void SetExec(U1 value);
U1 GetExecMaskTag();
void SetExecMaskTag(U1 value);
U32 GetExecLo();
void SetExecLo(U32 value);
U32 GetExecHi();
void SetExecHi(U32 value);
U1 GetVcc();
void SetVcc(U1 value);
U1 GetVccMaskValidTag();
void SetVccMaskValidTag(U1 value);
U32 GetVccLo();
void SetVccLo(U32 value);
U32 GetVccHi();
@@ -33,16 +33,12 @@ bool IsRegisterStatePseudo(ValueOpcode opcode) {
case ValueOpcode::SetScc:
case ValueOpcode::GetExec:
case ValueOpcode::SetExec:
case ValueOpcode::GetExecMaskTag:
case ValueOpcode::SetExecMaskTag:
case ValueOpcode::GetExecLo:
case ValueOpcode::SetExecLo:
case ValueOpcode::GetExecHi:
case ValueOpcode::SetExecHi:
case ValueOpcode::GetVcc:
case ValueOpcode::SetVcc:
case ValueOpcode::GetVccMaskValidTag:
case ValueOpcode::SetVccMaskValidTag:
case ValueOpcode::GetVccLo:
case ValueOpcode::SetVccLo:
case ValueOpcode::GetVccHi:
@@ -23,16 +23,12 @@ VALUE_OPCODE(GetScc, U1)
VALUE_OPCODE(SetScc, Void, U1)
VALUE_OPCODE(GetExec, U1)
VALUE_OPCODE(SetExec, Void, U1)
VALUE_OPCODE(GetExecMaskTag, U1)
VALUE_OPCODE(SetExecMaskTag, Void, U1)
VALUE_OPCODE(GetExecLo, U32)
VALUE_OPCODE(SetExecLo, Void, U32)
VALUE_OPCODE(GetExecHi, U32)
VALUE_OPCODE(SetExecHi, Void, U32)
VALUE_OPCODE(GetVcc, U1)
VALUE_OPCODE(SetVcc, Void, U1)
VALUE_OPCODE(GetVccMaskValidTag, U1)
VALUE_OPCODE(SetVccMaskValidTag, Void, U1)
VALUE_OPCODE(GetVccLo, U32)
VALUE_OPCODE(SetVccLo, Void, U32)
VALUE_OPCODE(GetVccHi, U32)
@@ -22,9 +22,6 @@ struct ScalarMaskTag {
struct ExecTag {
auto operator<=>(const ExecTag&) const = default;
};
struct ExecMaskTag {
auto operator<=>(const ExecMaskTag&) const = default;
};
struct ExecLoTag {
auto operator<=>(const ExecLoTag&) const = default;
};
@@ -34,9 +31,6 @@ struct ExecHiTag {
struct VccTag {
auto operator<=>(const VccTag&) const = default;
};
struct VccMaskValidTag {
auto operator<=>(const VccMaskValidTag&) const = default;
};
struct VccLoTag {
auto operator<=>(const VccLoTag&) const = default;
};
@@ -51,9 +45,9 @@ struct GotoVariable {
auto operator<=>(const GotoVariable&) const = default;
};
using Variable = std::variant<ScalarReg, ThreadBitScalarReg, ScalarMaskTag, VectorReg, GotoVariable,
SccTag, ExecTag, ExecMaskTag, ExecLoTag, ExecHiTag, VccTag,
VccMaskValidTag, VccLoTag, VccHiTag, M0Tag>;
using Variable =
std::variant<ScalarReg, ThreadBitScalarReg, ScalarMaskTag, VectorReg, GotoVariable, SccTag,
ExecTag, ExecLoTag, ExecHiTag, VccTag, VccLoTag, VccHiTag, M0Tag>;
using ValueMap = std::unordered_map<Block*, Value>;
struct DefTable {
@@ -105,16 +99,12 @@ struct DefTable {
void Set(Block* block, SccTag, Value value) { scc[block] = value; }
const Value& Get(Block* block, ExecTag) { return exec[block]; }
void Set(Block* block, ExecTag, Value value) { exec[block] = value; }
const Value& Get(Block* block, ExecMaskTag) { return exec_mask_tag[block]; }
void Set(Block* block, ExecMaskTag, Value value) { exec_mask_tag[block] = value; }
const Value& Get(Block* block, ExecLoTag) { return exec_lo[block]; }
void Set(Block* block, ExecLoTag, Value value) { exec_lo[block] = value; }
const Value& Get(Block* block, ExecHiTag) { return exec_hi[block]; }
void Set(Block* block, ExecHiTag, Value value) { exec_hi[block] = value; }
const Value& Get(Block* block, VccTag) { return vcc[block]; }
void Set(Block* block, VccTag, Value value) { vcc[block] = value; }
const Value& Get(Block* block, VccMaskValidTag) { return vcc_mask_valid_tag[block]; }
void Set(Block* block, VccMaskValidTag, Value value) { vcc_mask_valid_tag[block] = value; }
const Value& Get(Block* block, VccLoTag) { return vcc_lo[block]; }
void Set(Block* block, VccLoTag, Value value) { vcc_lo[block] = value; }
const Value& Get(Block* block, VccHiTag) { return vcc_hi[block]; }
@@ -124,11 +114,9 @@ struct DefTable {
ValueMap scc;
ValueMap exec;
ValueMap exec_mask_tag;
ValueMap exec_lo;
ValueMap exec_hi;
ValueMap vcc;
ValueMap vcc_mask_valid_tag;
ValueMap vcc_lo;
ValueMap vcc_hi;
ValueMap m0;
@@ -156,9 +144,6 @@ ValueOpcode UndefOpcode(SccTag) {
ValueOpcode UndefOpcode(ExecTag) {
return ValueOpcode::UndefU1;
}
ValueOpcode UndefOpcode(ExecMaskTag) {
return ValueOpcode::UndefU1;
}
ValueOpcode UndefOpcode(ExecLoTag) {
return ValueOpcode::UndefU32;
}
@@ -168,9 +153,6 @@ ValueOpcode UndefOpcode(ExecHiTag) {
ValueOpcode UndefOpcode(VccTag) {
return ValueOpcode::UndefU1;
}
ValueOpcode UndefOpcode(VccMaskValidTag) {
return ValueOpcode::UndefU1;
}
ValueOpcode UndefOpcode(VccLoTag) {
return ValueOpcode::UndefU32;
}
@@ -202,9 +184,6 @@ Value InitialValue(SccTag) {
Value InitialValue(ExecTag) {
return Value(false);
}
Value InitialValue(ExecMaskTag) {
return Value(false);
}
Value InitialValue(ExecLoTag) {
return Value(0u);
}
@@ -214,9 +193,6 @@ Value InitialValue(ExecHiTag) {
Value InitialValue(VccTag) {
return Value(false);
}
Value InitialValue(VccMaskValidTag) {
return Value(false);
}
Value InitialValue(VccLoTag) {
return Value(0u);
}
@@ -375,15 +351,9 @@ void VisitInstruction(Pass& pass, Block* block, Inst& inst) {
break;
case ValueOpcode::SetScc: pass.Write(SccTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetExec: pass.Write(ExecTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetExecMaskTag:
pass.Write(ExecMaskTag {}, block, inst.Arg(0));
break;
case ValueOpcode::SetExecLo: pass.Write(ExecLoTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetExecHi: pass.Write(ExecHiTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetVcc: pass.Write(VccTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetVccMaskValidTag:
pass.Write(VccMaskValidTag {}, block, inst.Arg(0));
break;
case ValueOpcode::SetVccLo: pass.Write(VccLoTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetVccHi: pass.Write(VccHiTag {}, block, inst.Arg(0)); break;
case ValueOpcode::SetM0: pass.Write(M0Tag {}, block, inst.Arg(0)); break;
@@ -405,15 +375,9 @@ void VisitInstruction(Pass& pass, Block* block, Inst& inst) {
break;
case ValueOpcode::GetScc: inst.ReplaceUsesWith(pass.Read(SccTag {}, block)); break;
case ValueOpcode::GetExec: inst.ReplaceUsesWith(pass.Read(ExecTag {}, block)); break;
case ValueOpcode::GetExecMaskTag:
inst.ReplaceUsesWith(pass.Read(ExecMaskTag {}, block));
break;
case ValueOpcode::GetExecLo: inst.ReplaceUsesWith(pass.Read(ExecLoTag {}, block)); break;
case ValueOpcode::GetExecHi: inst.ReplaceUsesWith(pass.Read(ExecHiTag {}, block)); break;
case ValueOpcode::GetVcc: inst.ReplaceUsesWith(pass.Read(VccTag {}, block)); break;
case ValueOpcode::GetVccMaskValidTag:
inst.ReplaceUsesWith(pass.Read(VccMaskValidTag {}, block));
break;
case ValueOpcode::GetVccLo: inst.ReplaceUsesWith(pass.Read(VccLoTag {}, block)); break;
case ValueOpcode::GetVccHi: inst.ReplaceUsesWith(pass.Read(VccHiTag {}, block)); break;
case ValueOpcode::GetM0: inst.ReplaceUsesWith(pass.Read(M0Tag {}, block)); break;
+17 -1
View File
@@ -25,6 +25,7 @@
#include <algorithm>
#include <atomic>
#include <bit>
#include <cstdio>
#include <filesystem>
#include <fmt/format.h>
@@ -571,7 +572,10 @@ static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info,
uint32_t reg = in.hardware_mapping;
uint32_t size = in.size_in_elements;
LOGF("reg = %u, size = %u, va[%u] = 0x%08" PRIx32 "\n", reg, size, i, attrib[in.semantic]);
if (Config::GraphicsDebugDumpEnabled()) {
LOGF("reg = %u, size = %u, va[%u] = 0x%08" PRIx32 "\n", reg, size, i,
attrib[in.semantic]);
}
size_t index = attrib[in.semantic] & 0x1fu;
auto format =
@@ -894,6 +898,18 @@ void BuildStageStaticKey(const ShaderVertexInputInfo& info, std::vector<uint32_t
key.push_back(info.resources_num);
key.push_back(info.scratch_size_dwords);
key.push_back(info.pa_cl_vs_out_cntl);
key.push_back(static_cast<uint32_t>(info.clip_space.enabled));
if (info.clip_space.enabled) {
for (const float value: info.clip_space.scale) {
key.push_back(std::bit_cast<uint32_t>(value));
}
for (const float value: info.clip_space.offset) {
key.push_back(std::bit_cast<uint32_t>(value));
}
for (const float value: info.clip_space.half_extent) {
key.push_back(std::bit_cast<uint32_t>(value));
}
}
for (int i = 0; i < info.resources_num; i++) {
const auto& resource = info.resources[i];
+8
View File
@@ -68,6 +68,13 @@ inline uint8_t GetDstSel(uint32_t swizzle, uint32_t channel) {
return (swizzle >> (channel * 3u)) & 0x7u;
}
struct ShaderClipSpaceTransform {
float scale[2] = {};
float offset[2] = {};
float half_extent[2] = {};
bool enabled = false;
};
struct ShaderVertexInputInfo {
static constexpr int RES_MAX = 32;
@@ -84,6 +91,7 @@ struct ShaderVertexInputInfo {
uint32_t scratch_size_dwords = 0;
uint32_t param_export_mask = 0;
uint32_t pa_cl_vs_out_cntl = 0;
ShaderClipSpaceTransform clip_space;
bool fetch_external = false;
bool fetch_embedded = false;
};
+48 -37
View File
@@ -44,6 +44,13 @@ namespace Libs::Graphics {
static RenderContext* g_renderer = nullptr;
template <typename... Args>
static void AgcTrace(const char* format, const Args&... args) {
if (Config::GraphicsDebugDumpEnabled()) {
LOGF(format, args...);
}
}
void Initialize() {
// Some games lock up if this is not called first
if (Config::RenderDocEnabled()) {
@@ -363,6 +370,10 @@ void* KYTY_SYSV_ABI AgcGetRegisterDefaults2Internal(uint32_t ver) {
}
static void dbg_dump_shader(const Shader* h) {
if (!Config::GraphicsDebugDumpEnabled()) {
return;
}
LOGF("\t file_header = 0x%08" PRIx32 "\n"
"\t version = 0x%08" PRIx32 "\n"
"\t user_data = 0x%016" PRIx64 "\n",
@@ -952,7 +963,7 @@ int KYTY_SYSV_ABI AgcSetCxRegIndirectPatchSetAddress(uint32_t*
const volatile ShaderRegister* regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t regs = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(cmd), reinterpret_cast<uint64_t>(regs));
@@ -968,7 +979,7 @@ int KYTY_SYSV_ABI AgcSetShRegIndirectPatchSetAddress(uint32_t*
const volatile ShaderRegister* regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t regs = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(cmd), reinterpret_cast<uint64_t>(regs));
@@ -984,7 +995,7 @@ int KYTY_SYSV_ABI AgcSetUcRegIndirectPatchSetAddress(uint32_t*
const volatile ShaderRegister* regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t regs = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(cmd), reinterpret_cast<uint64_t>(regs));
@@ -999,7 +1010,7 @@ int KYTY_SYSV_ABI AgcSetUcRegIndirectPatchSetAddress(uint32_t*
int KYTY_SYSV_ABI AgcSetCxRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1011,7 +1022,7 @@ int KYTY_SYSV_ABI AgcSetCxRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_
int KYTY_SYSV_ABI AgcSetShRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1023,7 +1034,7 @@ int KYTY_SYSV_ABI AgcSetShRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_
int KYTY_SYSV_ABI AgcSetUcRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1035,7 +1046,7 @@ int KYTY_SYSV_ABI AgcSetUcRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_
int KYTY_SYSV_ABI AgcSetCxRegIndirectPatchAddRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1047,7 +1058,7 @@ int KYTY_SYSV_ABI AgcSetCxRegIndirectPatchAddRegisters(uint32_t* cmd, uint32_t n
int KYTY_SYSV_ABI AgcSetShRegIndirectPatchAddRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1059,7 +1070,7 @@ int KYTY_SYSV_ABI AgcSetShRegIndirectPatchAddRegisters(uint32_t* cmd, uint32_t n
int KYTY_SYSV_ABI AgcSetUcRegIndirectPatchAddRegisters(uint32_t* cmd, uint32_t num_regs) {
PRINT_NAME();
LOGF("\t cmd = 0x%016" PRIx64 "\n"
AgcTrace("\t cmd = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cmd), num_regs);
@@ -1090,7 +1101,7 @@ int KYTY_SYSV_ABI AgcCreatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc
const Shader* hs, const Shader* gs, uint32_t prim_type) {
PRINT_NAME();
LOGF("\t cx_regs = 0x%016" PRIx64 "\n"
AgcTrace("\t cx_regs = 0x%016" PRIx64 "\n"
"\t uc_regs = 0x%016" PRIx64 "\n"
"\t hs = 0x%016" PRIx64 "\n"
"\t gs = 0x%016" PRIx64 "\n"
@@ -1158,7 +1169,7 @@ int KYTY_SYSV_ABI AgcUpdatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc
uint32_t prim_type) {
PRINT_NAME();
LOGF("\t cx_regs = 0x%016" PRIx64 "\n"
AgcTrace("\t cx_regs = 0x%016" PRIx64 "\n"
"\t uc_regs = 0x%016" PRIx64 "\n"
"\t prim_type = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cx_regs), reinterpret_cast<uint64_t>(uc_regs), prim_type);
@@ -1220,7 +1231,7 @@ int KYTY_SYSV_ABI AgcGetGsOversubscription(ShaderRegister* regs, const Shader* g
constexpr uint32_t FULL_PC_OVERSUBSCRIPTION = 0x7ffu;
constexpr uint32_t FULL_SH_OVERSUBSCRIPTION = 0x007f0000u;
regs[0] = {Pm4::GE_PC_ALLOC, 0u};
regs[0] = {Pm4::UC_PARAMETER_OVERSUBSCRIPTION, 0u};
regs[1] = {Pm4::SPI_SHADER_PGM_RSRC4_GS, 0u};
if (budget == 0u) {
@@ -1363,7 +1374,7 @@ int KYTY_SYSV_ABI AgcCreateInterpolantMapping(ShaderRegister* regs, const Shader
const Shader* ps) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t gs = 0x%016" PRIx64 "\n"
"\t ps = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(regs), reinterpret_cast<uint64_t>(gs),
@@ -1419,7 +1430,7 @@ int KYTY_SYSV_ABI AgcCreateInterpolantMapping2(ShaderRegister* regs, const Shade
const Shader* ps) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t gs = 0x%016" PRIx64 "\n"
"\t ps = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(regs), reinterpret_cast<uint64_t>(gs),
@@ -1942,7 +1953,7 @@ uint32_t* KYTY_SYSV_ABI AgcCbSetShRegistersDirect(CommandBuffer*
uint32_t num_regs) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(regs), num_regs);
@@ -2009,7 +2020,7 @@ uint32_t* KYTY_SYSV_ABI AgcCbSetUcRegistersDirect(CommandBuffer*
uint32_t num_regs) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(regs), num_regs);
@@ -2396,7 +2407,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer*
uint32_t num_regs) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(regs), num_regs);
@@ -2420,7 +2431,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer*
uint32_t num_regs) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(regs), num_regs);
@@ -2444,7 +2455,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer*
uint32_t num_regs) {
PRINT_NAME();
LOGF("\t regs = 0x%016" PRIx64 "\n"
AgcTrace("\t regs = 0x%016" PRIx64 "\n"
"\t num_regs = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(regs), num_regs);
@@ -2467,7 +2478,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexSize(CommandBuffer* buf, uint8_t index_siz
uint8_t cache_policy) {
PRINT_NAME();
LOGF("\t index_size = 0x%" PRIx8 "\n"
AgcTrace("\t index_size = 0x%" PRIx8 "\n"
"\t cache_policy = 0x%" PRIx8 "\n",
index_size, cache_policy);
@@ -2488,7 +2499,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexSize(CommandBuffer* buf, uint8_t index_siz
uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexBuffer(CommandBuffer* buf, uint64_t index_addr) {
PRINT_NAME();
LOGF("\t index_addr = 0x%016" PRIx64 "\n", index_addr);
AgcTrace("\t index_addr = 0x%016" PRIx64 "\n", index_addr);
EXIT_NOT_IMPLEMENTED(buf == nullptr);
EXIT_NOT_IMPLEMENTED((index_addr & 1u) != 0);
@@ -2509,7 +2520,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexBuffer(CommandBuffer* buf, uint64_t index_
uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexCount(CommandBuffer* buf, uint32_t index_count) {
PRINT_NAME();
LOGF("\t index_count = 0x%" PRIx32 "\n", index_count);
AgcTrace("\t index_count = 0x%" PRIx32 "\n", index_count);
EXIT_NOT_IMPLEMENTED(buf == nullptr);
@@ -2528,7 +2539,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexCount(CommandBuffer* buf, uint32_t index_c
uint32_t* KYTY_SYSV_ABI AgcDcbSetNumInstances(CommandBuffer* buf, uint32_t num_instances) {
PRINT_NAME();
LOGF("\t num_instances = 0x%" PRIx32 "\n", num_instances);
AgcTrace("\t num_instances = 0x%" PRIx32 "\n", num_instances);
if (buf == nullptr) {
return nullptr;
@@ -2624,7 +2635,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndex(CommandBuffer* buf, uint32_t index_count
const volatile void* index_addr, uint64_t modifier) {
PRINT_NAME();
LOGF("\t index_count = 0x%" PRIx32 "\n"
AgcTrace("\t index_count = 0x%" PRIx32 "\n"
"\t index_addr = 0x%016" PRIx64 "\n"
"\t modifier = 0x%016" PRIx64 "\n",
index_count, reinterpret_cast<uint64_t>(index_addr), modifier);
@@ -2663,7 +2674,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexMultiInstanced(CommandBuffer* buf, uint32
uint32_t instance_count, uint64_t modifier) {
PRINT_NAME();
LOGF("\t index_count = 0x%" PRIx32 "\n"
AgcTrace("\t index_count = 0x%" PRIx32 "\n"
"\t index_addr = 0x%016" PRIx64 "\n"
"\t instance_count = 0x%" PRIx32 "\n"
"\t object_ids = 0x%016" PRIx64 "\n"
@@ -2739,7 +2750,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexAuto(CommandBuffer* buf, uint32_t index_c
uint64_t modifier) {
PRINT_NAME();
LOGF("\t index_count = 0x%" PRIx32 "\n"
AgcTrace("\t index_count = 0x%" PRIx32 "\n"
"\t modifier = 0x%016" PRIx64 "\n",
index_count, modifier);
@@ -2768,7 +2779,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexOffset(CommandBuffer* buf, uint32_t index
uint32_t index_count, uint64_t modifier) {
PRINT_NAME();
LOGF("\t index_offset = 0x%" PRIx32 "\n"
AgcTrace("\t index_offset = 0x%" PRIx32 "\n"
"\t index_count = 0x%" PRIx32 "\n"
"\t modifier = 0x%016" PRIx64 "\n",
index_offset, index_count, modifier);
@@ -2826,7 +2837,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t dat
uint64_t modifier) {
PRINT_NAME();
LOGF("\t data_offset = 0x%" PRIx32 "\n"
AgcTrace("\t data_offset = 0x%" PRIx32 "\n"
"\t modifier = 0x%016" PRIx64 "\n",
data_offset_in_bytes, modifier);
@@ -2853,7 +2864,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirect(CommandBuffer* buf, uint32_t data_off
uint64_t modifier) {
PRINT_NAME();
LOGF("\t data_offset = 0x%" PRIx32 "\n"
AgcTrace("\t data_offset = 0x%" PRIx32 "\n"
"\t modifier = 0x%016" PRIx64 "\n",
data_offset_in_bytes, modifier);
@@ -2884,7 +2895,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirectMulti(CommandBuffer* buf,
uint32_t stride_in_bytes, uint64_t modifier) {
PRINT_NAME();
LOGF("\t data_offset = 0x%" PRIx32 "\n"
AgcTrace("\t data_offset = 0x%" PRIx32 "\n"
"\t count_indirect = 0x%" PRIx32 "\n"
"\t max_count_or_count = 0x%" PRIx32 "\n"
"\t count_addr = 0x%016" PRIx64 "\n"
@@ -2947,7 +2958,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf,
uint32_t stride_in_bytes, uint64_t modifier) {
PRINT_NAME();
LOGF("\t data_offset = 0x%" PRIx32 "\n"
AgcTrace("\t data_offset = 0x%" PRIx32 "\n"
"\t count_indirect = 0x%" PRIx32 "\n"
"\t max_count_or_count = 0x%" PRIx32 "\n"
"\t count_addr = 0x%016" PRIx64 "\n"
@@ -3801,7 +3812,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWriteData(CommandBuffer* buf, uint8_t dst, uint8_t
uint8_t write_confirm) {
PRINT_NAME();
LOGF("\t dst = 0x%02" PRIx8 "\n"
AgcTrace("\t dst = 0x%02" PRIx8 "\n"
"\t cache_policy = 0x%02" PRIx8 "\n"
"\t address_or_offset = 0x%016" PRIx64 "\n"
"\t data = 0x%016" PRIx64 "\n"
@@ -4054,7 +4065,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
uint64_t mask, uint32_t poll_cycles) {
PRINT_NAME();
LOGF("\t size = 0x%02" PRIx8 "\n"
AgcTrace("\t size = 0x%02" PRIx8 "\n"
"\t compare_function = 0x%02" PRIx8 "\n"
"\t op = 0x%02" PRIx8 "\n"
"\t cache_policy = 0x%02" PRIx8 "\n"
@@ -4201,7 +4212,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetFlip(CommandBuffer* buf, uint32_t video_out_han
int64_t flip_arg) {
PRINT_NAME();
LOGF("\t video_out_handle = %" PRIu32 "\n"
AgcTrace("\t video_out_handle = %" PRIu32 "\n"
"\t display_buffer_index = %" PRId32 "\n"
"\t flip_mode = %" PRIu32 "\n"
"\t flip_arg = %" PRId64 "\n",
@@ -4252,7 +4263,7 @@ int KYTY_SYSV_ABI AgcDriverSubmitDcb(const Packet* packet) {
EXIT_NOT_IMPLEMENTED(packet == nullptr);
LOGF("\t addr = 0x%016" PRIx64 "\n"
AgcTrace("\t addr = 0x%016" PRIx64 "\n"
"\t dw_num = 0x%08" PRIx32 "\n"
"\t flags = 0x%02" PRIx8 "\n",
reinterpret_cast<uint64_t>(packet->addr), packet->dw_num, packet->flags);
@@ -4266,7 +4277,7 @@ int KYTY_SYSV_ABI AgcDriverSubmitMultiDcbs(uint32_t* const* dcb_gpu_addrs,
const uint32_t* dcb_sizes_in_dwords, uint32_t count) {
PRINT_NAME();
LOGF("\t count = %" PRIu32 "\n", count);
AgcTrace("\t count = %" PRIu32 "\n", count);
if (count == 0) {
return OK;
@@ -4279,7 +4290,7 @@ int KYTY_SYSV_ABI AgcDriverSubmitMultiDcbs(uint32_t* const* dcb_gpu_addrs,
auto* dcb = dcb_gpu_addrs[i];
uint32_t size_in_dwords = dcb_sizes_in_dwords[i];
LOGF("\t dcb[%" PRIu32 "] = 0x%016" PRIx64 "\n"
AgcTrace("\t dcb[%" PRIu32 "] = 0x%016" PRIx64 "\n"
"\t size[%" PRIu32 "] = 0x%08" PRIx32 "\n",
i, reinterpret_cast<uint64_t>(dcb), i, size_in_dwords);
-6
View File
@@ -2931,7 +2931,6 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
auto* submixer = reinterpret_cast<Ngs2SubmixerVoiceState*>(state);
*submixer = {};
submixer->voice_state.state_flags = Ngs2GetStateFlags(voice);
LOGF("\t state_flags = %u\n", submixer->voice_state.state_flags);
break;
}
case Ngs2RackType::CustomMastering: {
@@ -2942,7 +2941,6 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
std::memset(state, 0, state_size);
auto* mastering = reinterpret_cast<Ngs2CustomMasteringVoiceState*>(state);
mastering->voice_state.state_flags = Ngs2GetStateFlags(voice);
LOGF("\t state_flags = %u\n", mastering->voice_state.state_flags);
break;
}
case Ngs2RackType::Sampler:
@@ -2957,7 +2955,6 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
state->state_flags = Ngs2GetStateFlags(voice);
if (state_size < sizeof(Ngs2SamplerVoiceState)) {
LOGF("\t state_flags = %u\n", state->state_flags);
break;
}
@@ -2968,7 +2965,6 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
sampler->num_decoded_samples = 0;
sampler->user_data = 0;
sampler->waveform_data = nullptr;
LOGF("\t state_flags = %u\n", sampler->voice_state.state_flags);
break;
}
default: EXIT("unknown type: %s\n", Common::EnumName(voice->rack->type).c_str());
@@ -2989,8 +2985,6 @@ int KYTY_SYSV_ABI Ngs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state
*state_flags = Ngs2GetStateFlags(voice);
LOGF("\t state_flags = %u\n", *state_flags);
return OK;
}
+1
View File
@@ -635,6 +635,7 @@ LIB_DEFINE(InitNet_1_Http) {
LIB_FUNC("P6A3ytpsiYc", Http::HttpDeleteConnection);
LIB_FUNC("tsGVru3hCe8", Http::HttpCreateRequest);
LIB_FUNC("Aeu5wVKkF9w", Http::HttpCreateRequest);
LIB_FUNC("rGNm+FjIXKk", Http::HttpCreateRequest2);
LIB_FUNC("Cnp77podkCU", Http::HttpCreateRequestWithURL2);
LIB_FUNC("qe7oZ+v4PWA", Http::HttpDeleteRequest);
LIB_FUNC("PTiFIUxCpJc", Http::HttpSetRequestContentLength);
+26 -12
View File
@@ -1055,8 +1055,7 @@ static int ConvertGuestSockaddr(const void* addr, uint32_t addrlen, sockaddr_sto
return 0;
}
#if defined(_WIN32)
static int ConvertHostSockaddr(const sockaddr_storage* addr, int addrlen, void* out,
static int ConvertHostSockaddr(const sockaddr_storage* addr, SocketLength addrlen, void* out,
uint32_t* out_len) {
EXIT_IF(addr == nullptr);
EXIT_IF(out_len == nullptr);
@@ -1066,7 +1065,8 @@ static int ConvertHostSockaddr(const sockaddr_storage* addr, int addrlen, void*
return -1;
}
if (addr->ss_family != AF_INET || addrlen < static_cast<int>(sizeof(sockaddr_in))) {
if (addr->ss_family != AF_INET ||
addrlen < static_cast<SocketLength>(sizeof(sockaddr_in))) {
*Posix::GetErrorAddr() = Posix::POSIX_EOPNOTSUPP;
return -1;
}
@@ -1088,7 +1088,6 @@ static int ConvertHostSockaddr(const sockaddr_storage* addr, int addrlen, void*
*out_len = guest_addrlen;
return 0;
}
#endif
static bool GetSocketBackend(int guest_fd, NativeSocket* out) {
EXIT_IF(out == nullptr);
@@ -1851,19 +1850,13 @@ int KYTY_SYSV_ABI Getsockname(int s, void* addr, uint32_t* addrlen) {
return -1;
}
#if defined(_WIN32)
sockaddr_storage host_addr {};
int host_addrlen = sizeof(host_addr);
if (::getsockname(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen) ==
SOCKET_ERROR) {
SocketLength host_addrlen = sizeof(host_addr);
if (::getsockname(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen) != 0) {
return SetHostSocketError();
}
return ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen);
#else
*Posix::GetErrorAddr() = Posix::POSIX_ENOSYS;
return -1;
#endif
}
int KYTY_SYSV_ABI Getsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) {
@@ -2818,6 +2811,27 @@ int KYTY_SYSV_ABI HttpCreateRequest(int conn_id, int method, const char* path,
return id.ToInt();
}
int KYTY_SYSV_ABI HttpCreateRequest2(int conn_id, const char* method, const char* path,
uint64_t content_length) {
PRINT_NAME();
LOGF("\t conn_id = %d\n"
"\t method = %s\n"
"\t path = %s\n"
"\t content_length = %" PRIu64 "\n",
conn_id, method, path, content_length);
EXIT_IF(g_net == nullptr);
if (method == nullptr || path == nullptr) {
return HTTP_ERROR_INVALID_VALUE;
}
auto id =
g_net->HttpCreateRequestWithURL2(Network::Id(conn_id), method, path, content_length);
return id.IsValid() ? id.ToInt() : HTTP_ERROR_OUT_OF_MEMORY;
}
int KYTY_SYSV_ABI HttpCreateRequestWithURL2(int conn_id, const char* method, const char* url,
uint64_t content_length) {
PRINT_NAME();
+2
View File
@@ -106,6 +106,8 @@ int KYTY_SYSV_ABI HttpCreateConnectionWithURL(int tmpl_id, const char* url, int
int KYTY_SYSV_ABI HttpDeleteConnection(int conn_id);
int KYTY_SYSV_ABI HttpCreateRequest(int conn_id, int method, const char* path,
uint64_t content_length);
int KYTY_SYSV_ABI HttpCreateRequest2(int conn_id, const char* method, const char* path,
uint64_t content_length);
int KYTY_SYSV_ABI HttpCreateRequestWithURL2(int conn_id, const char* method, const char* url,
uint64_t content_length);
int KYTY_SYSV_ABI HttpSetRequestContentLength(int request_id, uint64_t content_length);
+5 -8
View File
@@ -1166,7 +1166,6 @@ static void PatchProgram(Program* program, uint64_t address, uint64_t size) {
prefix_count++;
}
const size_t inst_size = prefix_count + Jit::Call9::GetSize();
if (inst_ptr + Jit::Call9::GetSize() > start_ptr + size) {
break;
}
@@ -1175,20 +1174,18 @@ static void PatchProgram(Program* program, uint64_t address, uint64_t size) {
if (memcmp(inst_ptr, tls_pattern, 3) == 0 && (modrm & 0xc7u) == 0x04u &&
inst_ptr[4] == tls_pattern[4] &&
*reinterpret_cast<const uint32_t*>(inst_ptr + 5) == 0) {
LOGF("Patch tls at addr: [%016" PRIx64 "]\n", reinterpret_cast<uint64_t>(ptr));
LOGF("Patch tls at addr: [%016" PRIx64 "]\n",
reinterpret_cast<uint64_t>(inst_ptr));
const auto reg = (modrm >> 3u) & 7u;
EXIT_NOT_IMPLEMENTED(reg == 4u);
auto* code = new (ptr) Jit::Call9;
// A raw scan can encounter a 0x66 in the preceding instruction.
auto* code = new (inst_ptr) Jit::Call9;
code->SetFunc(reg == 0
? program->tls.handler_vaddr
: program->tls.handler_vaddr + Jit::TlsRegStub::GetOffset(reg));
if (inst_size > Jit::Call9::GetSize()) {
std::memset(ptr + Jit::Call9::GetSize(), 0x90,
inst_size - Jit::Call9::GetSize());
}
ptr += inst_size - 1;
ptr += prefix_count + Jit::Call9::GetSize() - 1;
}
}
}
+239 -194
View File
@@ -357,10 +357,23 @@ struct TextureCacheTestAccess {
return cache.TryDownloadImage(id);
}
static void RegisterHtileMeta(TextureCache &cache, uint64_t address) {
std::lock_guard lock(cache.m_lock);
auto &metadata = cache.m_surface_metas[address];
metadata.type = TextureCache::MetaDataInfo::Type::HTile;
metadata.clear_mask = 0;
}
static TileManager &Tiler(TextureCache &cache) { return cache.m_tiler; }
};
struct RenderExecutorTestAccess {
static bool TryConsumeComputeMetaClear(RenderExecutor &executor,
const ShaderComputeInputInfo &input,
const CommandBuffer &buffer) {
return executor.TryConsumeComputeMetaClear(input, buffer);
}
static TextureBinding
ResolveTexture(RenderExecutor &executor,
const ShaderRecompiler::IR::ImageResource &resource,
@@ -3778,6 +3791,92 @@ public:
std::printf("[host] %-32s ok\n", name);
}
void CheckComputeMetaClearClassification() {
constexpr const char *name = "ComputeMetaClearClassification";
constexpr uint64_t read_only_meta = 0x0000000204201f00ull;
constexpr uint64_t read_write_meta = 0x0000000204202000ull;
constexpr uint64_t write_only_meta = 0x0000000204202100ull;
EnsureRuntimeContext();
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &texture_cache = context.GetTextureCache();
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, read_only_meta);
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, read_write_meta);
TextureCacheTestAccess::RegisterHtileMeta(texture_cache, write_only_meta);
Require(name, "HTile fixture",
texture_cache.IsMeta(read_only_meta) &&
texture_cache.IsMeta(read_write_meta) &&
texture_cache.IsMeta(write_only_meta) &&
!texture_cache.IsMetaCleared(read_only_meta, 0) &&
!texture_cache.IsMetaCleared(read_write_meta, 0) &&
!texture_cache.IsMetaCleared(write_only_meta, 0),
"test HTile entries were not registered in their initial state");
const auto MakeInput = [](uint64_t address, bool read, bool written) {
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
program->stage = ShaderType::Compute;
ShaderRecompiler::IR::BufferResource resource{};
resource.read = read;
resource.written = written;
resource.formatted = true;
program->info.buffers.push_back(resource);
ShaderBufferResource descriptor{};
descriptor.UpdateAddress48(address);
descriptor.fields[1] |= 16u << 16u;
descriptor.fields[2] = 8;
descriptor.fields[3] =
DstSel(4, 5, 6, 7) |
(static_cast<uint32_t>(Prospero::BufferFormat::k32_32_32_32UInt)
<< 12u);
ShaderRecompiler::IR::DescriptorValue value{};
value.dword_count = 4;
std::copy_n(descriptor.fields, value.dword_count, value.dwords.begin());
auto snapshot =
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
snapshot->buffers.push_back(value);
ShaderComputeInputInfo input{};
input.stage.program = std::move(program);
input.stage.resources = std::move(snapshot);
return input;
};
auto &executor = context.GetRenderExecutor();
auto &command = scheduler.Current();
const auto read_only_input = MakeInput(read_only_meta, true, false);
const bool read_only_consumed =
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
executor, read_only_input, command);
Require(name, "read-only dispatch preserved",
!read_only_consumed &&
!texture_cache.IsMetaCleared(read_only_meta, 0),
"a metadata read-only dispatch changed logical clear state");
const auto read_write_input = MakeInput(read_write_meta, true, true);
const bool read_write_consumed =
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
executor, read_write_input, command);
Require(name, "read/write dispatch preserved",
!read_write_consumed &&
!texture_cache.IsMetaCleared(read_write_meta, 0),
"a metadata read/modify/write dispatch was replaced by a clear");
const auto write_only_input = MakeInput(write_only_meta, false, true);
const bool write_only_consumed =
RenderExecutorTestAccess::TryConsumeComputeMetaClear(
executor, write_only_input, command);
Require(name, "write-only fill consumed",
write_only_consumed &&
texture_cache.IsMetaCleared(write_only_meta, 0),
"a metadata write-only fill was not consumed as a clear");
scheduler.Finish();
std::printf("[host] %-32s ok\n", name);
}
void CheckUnifiedTextureCacheFlow() {
constexpr const char *name = "UnifiedTextureCacheFlow";
constexpr uintptr_t base = 0x0000000200600000ull;
@@ -12741,160 +12840,6 @@ TestCase ScalarNotB64UpdatesScc() {
O::S_ENDPGM}};
}
TestCase ScalarMovB64PreservesRawSpecialData() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendSMovLiteral(&code, 0, 0x09500000u);
AppendSMovLiteral(&code, 1, 0x81234567u);
code.push_back(EncodeSop1(0x04, 106, 0)); // s_mov_b64 vcc, s[0:1]
code.push_back(EncodeSop1(0x04, 2, 106)); // s_mov_b64 s[2:3], vcc
code.push_back(EncodeSop1(0x08, 4, 2)); // s_not_b64 s[4:5], s[2:3]
code.push_back(EncodeSop2(0x0a, 6, InlineU32(1), InlineU32(0)));
AppendStoreSgprPair(&code, 2, 0);
AppendStoreSgprPair(&code, 4, 2);
AppendStoreSgpr(&code, 6, 4);
AppendSMovLiteral(&code, 8, 0x09500001u);
AppendSMovLiteral(&code, 9, 0x81234567u);
code.push_back(EncodeSop1(0x04, 126, 8)); // s_mov_b64 exec, s[8:9]
code.push_back(EncodeSop1(0x04, 10, 126)); // s_mov_b64 s[10:11], exec
code.push_back(EncodeSop1(0x08, 12, 10)); // s_not_b64 s[12:13], s[10:11]
code.push_back(EncodeSop2(0x0a, 14, InlineU32(1), InlineU32(0)));
AppendStoreSgprPair(&code, 10, 5);
AppendStoreSgprPair(&code, 12, 7);
AppendStoreSgpr(&code, 14, 9);
AppendEnd(&code);
return {"ScalarMovB64PreservesRawSpecialData",
code,
{},
{0x09500000u, 0x81234567u, 0xf6afffffu, 0x7edcba98u, 1u,
0x09500001u, 0x81234567u, 0xf6affffeu, 0x7edcba98u, 1u},
{O::S_MOV_B32, O::S_MOV_B64, O::S_NOT_B64, O::S_CSELECT_B32,
O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase Scalar64RawSpecialOps() {
using O = ShaderOpcode;
std::vector<u32> code;
const auto capture_scc = [&](u32 dst) {
code.push_back(EncodeSop2(0x0a, dst, InlineU32(1), InlineU32(0)));
};
AppendSMovLiteral(&code, 0, 0x09500001u);
AppendSMovLiteral(&code, 1, 0x81234567u);
AppendSMovLiteral(&code, 2, 0xff00ff01u);
AppendSMovLiteral(&code, 3, 0x0f0f0f0fu);
code.push_back(EncodeSop1(0x04, 126, 0));
code.push_back(EncodeSop2(0x0f, 106, 126, 2));
code.push_back(EncodeSop1(0x04, 4, 106));
capture_scc(6);
code.push_back(EncodeSop1(0x08, 106, 106));
code.push_back(EncodeSop1(0x04, 8, 106));
capture_scc(10);
AppendSMovLiteral(&code, 12, 0x80000011u);
AppendSMovLiteral(&code, 13, 0x80000001u);
code.push_back(EncodeSop1(0x04, 106, 12));
code.push_back(EncodeSop1(0x0a, 106, 106));
code.push_back(EncodeSop1(0x04, 14, 106));
capture_scc(16);
code.push_back(EncodeSop1(0x0a, 26, 193u));
capture_scc(28);
code.push_back(EncodeSMovB32(18, InlineU32(1)));
code.push_back(EncodeSMovB32(19, InlineU32(0)));
code.push_back(EncodeSopc(0x06, InlineU32(1), InlineU32(1)));
code.push_back(EncodeSop2(0x0b, 126, 0, 18));
code.push_back(EncodeSop1(0x04, 20, 126));
code.push_back(EncodeSop1(0x08, 106, 126));
code.push_back(EncodeSop1(0x04, 22, 106));
capture_scc(24);
code.push_back(EncodeSop1(0x04, 126, 18));
AppendStoreSgprPair(&code, 4, 0);
AppendStoreSgpr(&code, 6, 2);
AppendStoreSgprPair(&code, 8, 3);
AppendStoreSgpr(&code, 10, 5);
AppendStoreSgprPair(&code, 14, 6);
AppendStoreSgpr(&code, 16, 8);
AppendStoreSgprPair(&code, 26, 9);
AppendStoreSgpr(&code, 28, 11);
AppendStoreSgprPair(&code, 20, 12);
AppendStoreSgprPair(&code, 22, 14);
AppendStoreSgpr(&code, 24, 16);
AppendEnd(&code);
return {"Scalar64RawSpecialOps",
code,
{},
{0x09000001u, 0x01030507u, 1, 0xf6fffffeu, 0xfefcfaf8u, 1,
0xf00000ffu, 0xf000000fu, 1, 0xffffffffu, 0xffffffffu, 1,
0x09500001u, 0x81234567u, 0xf6affffeu, 0x7edcba98u, 1},
{O::S_MOV_B32, O::S_MOV_B64, O::S_AND_B64, O::S_NOT_B64,
O::S_CSELECT_B32, O::S_WQM_B64, O::S_CMP_EQ_U32, O::S_CSELECT_B64,
O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarMovB32SpecialAliases() {
using O = ShaderOpcode;
std::vector<u32> code;
const auto capture_scc = [&](u32 dst) {
code.push_back(EncodeSop2(0x0a, dst, InlineU32(1), InlineU32(0)));
};
AppendSMovLiteral(&code, 0, 0x09500001u);
AppendSMovLiteral(&code, 1, 0x81234567u);
AppendSMovLiteral(&code, 2, 0xaaaaaaaau);
AppendSMovLiteral(&code, 3, 0xbbbbbbbbu);
code.push_back(EncodeSMovB32(4, InlineU32(1)));
code.push_back(EncodeSMovB32(5, InlineU32(0)));
code.push_back(EncodeSop1(0x04, 126, 0));
code.push_back(EncodeSop1(0x04, 106, 2));
code.push_back(EncodeSMovB32(107, 126));
code.push_back(EncodeSop1(0x04, 6, 106));
code.push_back(EncodeSop1(0x08, 8, 106));
capture_scc(10);
AppendSMovLiteral(&code, 12, 0x13579bdfu);
AppendSMovLiteral(&code, 13, 0x2468ace0u);
code.push_back(EncodeSop1(0x04, 106, 12));
code.push_back(EncodeSMovB32(126, 106));
code.push_back(EncodeSop1(0x04, 14, 126));
code.push_back(EncodeSop1(0x04, 126, 4));
code.push_back(EncodeVopc(0xc2, InlineU32(0), 0));
code.push_back(EncodeSMovB32(106, 106));
code.push_back(EncodeSop1(0x08, 16, 106));
capture_scc(18);
code.push_back(EncodeSop1(0x04, 20, 106));
code.push_back(EncodeSMovB32(20, 20));
code.push_back(EncodeSop1(0x08, 22, 20));
capture_scc(24);
AppendStoreSgprPair(&code, 6, 0);
AppendStoreSgprPair(&code, 8, 2);
AppendStoreSgpr(&code, 10, 4);
AppendStoreSgprPair(&code, 14, 5);
AppendStoreSgpr(&code, 18, 7);
AppendStoreSgpr(&code, 24, 8);
AppendEnd(&code);
return {"ScalarMovB32SpecialAliases",
code,
{},
{0xaaaaaaaau, 0x09500001u, 0x55555555u, 0xf6affffeu, 1, 0x13579bdfu,
0x81234567u, 0, 0},
{O::S_MOV_B32, O::S_MOV_B64, O::S_NOT_B64, O::S_CSELECT_B32,
O::V_CMP_EQ_U32, O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarQuadmaskB64() {
using O = ShaderOpcode;
@@ -13023,7 +12968,8 @@ TestCase ScalarSaveExecOps() {
return {"ScalarSaveExecOps",
code,
{},
{1, 0, 1, 0, 0xffffffffu, 0xffffffffu, 1, 1, 0, 1, 0, 0, 0},
// EXEC and saved EXEC values are invocation-local Booleans.
{1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0},
{O::S_MOV_B32, O::S_AND_SAVEEXEC_B64, O::S_ORN2_SAVEEXEC_B64,
O::S_ANDN1_SAVEEXEC_B64, O::S_AND_SAVEEXEC_B32,
O::S_ANDN1_SAVEEXEC_B32, O::S_MOV_B64, O::V_MOV_B32,
@@ -13050,7 +12996,7 @@ TestCase ScalarOrn2SaveexecUsesSourceOrNotExec() {
return {"ScalarOrn2SaveexecUsesSourceOrNotExec",
code,
{},
{0x0000000cu, 0x80000000u, 0xfffffff3u, 0x7fffffffu, 1},
{1, 0, 1, 0, 1},
{O::S_MOV_B32, O::S_ORN2_SAVEEXEC_B64, O::S_MOV_B64, O::V_MOV_B32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
@@ -15175,6 +15121,31 @@ TestCase VectorDppQuadPermuteReverse() {
return test;
}
TestCase VectorDppRowXmask() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovU32(&code, 1, 100);
code.push_back(EncodeVop2(0x25, 2, 250, 1));
code.push_back(EncodeVop2Dpp(0, 0x164));
code.push_back(EncodeVop2(0x1a, 3, InlineU32(2), 0));
AppendBufferStoreDword(&code, 2, 3);
AppendEnd(&code);
TestCase test;
test.name = "VectorDppRowXmask";
test.code = code;
test.expected = {104, 105, 106, 107, 100, 101, 102, 103};
test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, O::V_LSHLREV_B32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.compute_info.threads_num[0] = 8;
test.compute_info.threads_num[1] = 1;
test.compute_info.threads_num[2] = 1;
test.compute_info.thread_ids_num = 1;
test.has_compute_info = true;
return test;
}
TestCase VectorDppBankMaskPreservesDestination() {
using O = ShaderOpcode;
@@ -16736,30 +16707,6 @@ TestCase ScalarMemoryLoadVariants() {
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarLoadUsesRawExecAddress() {
using O = ShaderOpcode;
std::vector<u32> code;
code.push_back(EncodeSMovB32(0, InlineU32(0)));
code.push_back(EncodeSMovB32(1, InlineU32(0)));
code.push_back(EncodeSMovB32(2, InlineU32(4)));
code.push_back(EncodeSMovB32(3, InlineU32(0)));
code.push_back(EncodeSop1(0x04, 126, 2));
code.push_back(EncodeSmem0(0x00, 20, 63));
code.push_back(EncodeSmem1(0));
code.push_back(EncodeSMovB32(2, InlineU32(1)));
code.push_back(EncodeSop1(0x04, 126, 2));
AppendStoreSgpr(&code, 20, 0);
AppendEnd(&code);
return {"ScalarLoadUsesRawExecAddress",
code,
{0x11111111u, 0x22222222u},
{0x22222222u, 0x22222222u},
{O::S_MOV_B32, O::S_MOV_B64, O::S_LOAD_DWORD, O::V_MOV_B32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarLoadSignedImmediateOffsetAddsSoffset() {
using O = ShaderOpcode;
@@ -21203,9 +21150,6 @@ std::vector<TestCase> MakeCases() {
AddCase(ScalarCompareOps);
AddCase(ScalarShiftAddAndMaskOps);
AddCase(ScalarNotB64UpdatesScc);
AddCase(ScalarMovB64PreservesRawSpecialData);
AddCase(Scalar64RawSpecialOps);
AddCase(ScalarMovB32SpecialAliases);
AddCase(ScalarQuadmaskB64);
AddCase(ScalarFlbitI32B64Gpu);
AddCase(ScalarFlbitI32B32Gpu);
@@ -21275,6 +21219,7 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorPermlane16FetchInactiveZero);
AddCase(VectorPermlane16FetchInactiveFi);
AddCase(VectorDppQuadPermuteReverse);
AddCase(VectorDppRowXmask);
AddCase(VectorDppBankMaskPreservesDestination);
AddCase(VectorDppBoundsControlZeroPreservesDestination);
AddCase(Vop3LdexpSourceModifier);
@@ -21322,7 +21267,6 @@ std::vector<TestCase> MakeCases() {
AddCase(BranchVccnzUsesInvocationMask);
AddCase(BranchVccnzUsesCarryProducedInvocationMask);
AddCase(ScalarMemoryLoadVariants);
AddCase(ScalarLoadUsesRawExecAddress);
AddCase(ScalarLoadSignedImmediateOffsetAddsSoffset);
AddCase(ScalarLoadAlignsComponentsAndMasksAddress);
AddCase(BufferLoadStore);
@@ -24556,10 +24500,10 @@ void CheckPm4NativeTargetGeometryRegisters(RenderContext &renderer) {
// Removed GCN shader resource/checksum/queue registers. Numeric offsets keep
// this check independent of the deleted legacy names.
constexpr std::array<uint32_t, 22> legacy_shader_slots{
0x000u, 0x001u, 0x002u, 0x003u, 0x030u, 0x081u, 0x0b0u, 0x0bcu,
0x100u, 0x101u, 0x130u, 0x14au, 0x14bu, 0x20eu, 0x20fu, 0x210u,
0x211u, 0x216u, 0x217u, 0x219u, 0x21au, 0x27du,
constexpr std::array<uint32_t, 19> legacy_shader_slots{
0x000u, 0x001u, 0x002u, 0x003u, 0x030u, 0x0b0u, 0x0bcu, 0x130u,
0x14au, 0x14bu, 0x20eu, 0x20fu, 0x210u, 0x211u, 0x216u, 0x217u,
0x219u, 0x21au, 0x27du,
};
for (const auto offset : legacy_shader_slots) {
legacy_slots_are_unhandled &= g_hw_sh_func[offset] == nullptr &&
@@ -24570,12 +24514,6 @@ void CheckPm4NativeTargetGeometryRegisters(RenderContext &renderer) {
g_hw_sh_indirect_func[offset] == nullptr;
}
constexpr std::array<uint32_t, 1> legacy_uconfig_slots{0x0260u};
for (const auto offset : legacy_uconfig_slots) {
legacy_slots_are_unhandled &= g_hw_uc_func[offset] == nullptr &&
g_hw_uc_indirect_func[offset] == nullptr;
}
std::array<uint32_t, 3> index_size_packet{
KYTY_PM4(3, Pm4::IT_SET_UCONFIG_REG_INDEX, Pm4::R_ZERO),
0x20000000u | Pm4::VGT_INDEX_TYPE,
@@ -24601,6 +24539,54 @@ void CheckPm4NativeTargetGeometryRegisters(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", "Pm4NativeTargetGeometry");
}
void CheckPm4PrivateAgcShaderRegisters(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
std::array<uint32_t, 6> registers{
Pm4::SPI_SHADER_PGM_RSRC4_GS, 0x0badc0deu,
Pm4::SPI_SHADER_PGM_CHKSUM_HS, 0x12345678u,
Pm4::SPI_SHADER_PGM_RSRC4_HS, 0x87654321u,
};
const auto address = reinterpret_cast<uint64_t>(registers.data());
std::array<uint32_t, 5> command{
KYTY_PM4(5, Pm4::IT_SET_SH_REG_INDIRECT, Pm4::R_ZERO),
static_cast<uint32_t>(address), static_cast<uint32_t>(address >> 32u),
0x80000000u, 3u,
};
Pm4Execution execution;
const bool handlers_present =
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] != nullptr &&
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_CHKSUM_HS] != nullptr &&
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_HS] != nullptr;
Require("Pm4PrivateAgcShaderRegisters", "private shader register stream",
handlers_present &&
processor.Process(execution, command) == Pm4ProcessResult::Complete,
"private AGC shader registers were rejected by SET_SH_REG_INDIRECT");
std::printf("[host] %-32s ok\n", "Pm4PrivateAgcShaderRegisters");
}
void CheckPm4PrivateAgcUconfigRegisters(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
std::array<uint32_t, 2> registers{
Pm4::UC_PARAMETER_OVERSUBSCRIPTION, 0x12345678u,
};
const auto address = reinterpret_cast<uint64_t>(registers.data());
std::array<uint32_t, 5> command{
KYTY_PM4(5, Pm4::IT_SET_UCONFIG_REG_INDIRECT, Pm4::R_ZERO),
static_cast<uint32_t>(address), static_cast<uint32_t>(address >> 32u),
0x80000000u, 1u,
};
Pm4Execution execution;
const bool handler_present =
g_hw_uc_indirect_func[Pm4::UC_PARAMETER_OVERSUBSCRIPTION] != nullptr;
Require("Pm4PrivateAgcUconfigRegisters", "private UCONFIG register stream",
handler_present &&
processor.Process(execution, command) == Pm4ProcessResult::Complete,
"private AGC UCONFIG register was rejected by SET_UCONFIG_REG_INDIRECT");
std::printf("[host] %-32s ok\n", "Pm4PrivateAgcUconfigRegisters");
}
void CheckPm4DirectShaderRegisterFallback(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
@@ -24763,6 +24749,54 @@ void CheckPm4PolygonOffsetRegisters(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", "Pm4PolygonOffset");
}
void CheckPm4DepthControlHighBits(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
std::array<uint32_t, 3> direct{
KYTY_PM4(3, Pm4::IT_SET_CONTEXT_REG, Pm4::R_ZERO),
Pm4::DB_DEPTH_CONTROL,
0x80000072u,
};
Pm4Execution direct_execution;
const bool direct_complete =
processor.Process(direct_execution, direct) == Pm4ProcessResult::Complete;
const auto &direct_control = processor.GetCtx().GetDepthControl();
const bool direct_decoded =
!direct_control.stencil_enable && direct_control.z_enable &&
!direct_control.z_write_enable && !direct_control.depth_bounds_enable &&
direct_control.zfunc == 7u && !direct_control.backface_enable &&
direct_control.stencilfunc == 0u && direct_control.stencilfunc_bf == 0u;
std::array<uint32_t, 2> registers{
Pm4::DB_DEPTH_CONTROL,
0x4070078du,
};
const auto address = reinterpret_cast<uint64_t>(registers.data());
std::array<uint32_t, 5> indirect{
KYTY_PM4(5, Pm4::IT_SET_CONTEXT_REG_INDIRECT, Pm4::R_ZERO),
static_cast<uint32_t>(address), static_cast<uint32_t>(address >> 32u),
0x80000000u, 1u,
};
Pm4Execution indirect_execution;
const bool indirect_complete =
processor.Process(indirect_execution, indirect) ==
Pm4ProcessResult::Complete;
const auto &indirect_control = processor.GetCtx().GetDepthControl();
const bool indirect_decoded =
indirect_control.stencil_enable && !indirect_control.z_enable &&
indirect_control.z_write_enable && indirect_control.depth_bounds_enable &&
indirect_control.zfunc == 0u && indirect_control.backface_enable &&
indirect_control.stencilfunc == 7u &&
indirect_control.stencilfunc_bf == 7u;
Require("Pm4DepthControlHighBits", "preserved high bits",
direct_complete && direct_decoded && indirect_complete &&
indirect_decoded,
"DB_DEPTH_CONTROL high bits rejected or changed decoded PS5 state");
std::printf("[host] %-32s ok\n", "Pm4DepthControlHighBits");
}
struct AgcCommandBufferLayout {
using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t,
void *);
@@ -25361,8 +25395,11 @@ int main(int argc, char **argv) {
if (argc == 2 && std::strcmp(argv[1], "--context-state-only") == 0) {
VulkanHarness vulkan;
CheckPm4NativeTargetGeometryRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
return 0;
}
@@ -25399,6 +25436,11 @@ int main(int argc, char **argv) {
vulkan.CheckUnifiedTextureCacheFlow();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--compute-meta-clear-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckComputeMetaClearClassification();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckUnifiedImageViewCache();
@@ -25541,10 +25583,13 @@ int main(int argc, char **argv) {
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer());
CheckPm4NativeTargetGeometryRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4DirectShaderRegisterFallback(vulkan.RuntimeRenderer());
CheckPm4GuardBandRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
+51 -17
View File
@@ -8811,6 +8811,45 @@ void TestNewShaderRecompilerExpVertexOutputs() {
CheckSpirvBinaryValidates(result.spirv);
}
void TestNewShaderRecompilerClipDisabledPosition() {
const uint32_t shader[] = {
EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), 0xbf810000u,
};
const auto compile = [&](const ShaderVertexInputInfo &vertex) {
auto options = MakeCompileOptions(ShaderType::Vertex);
options.input_info.vertex = &vertex;
return ShaderRecompiler::Recompile(shader, options);
};
ShaderVertexInputInfo regular{};
auto clipped = regular;
clipped.clip_space = {
.scale = {640.0f, 360.0f},
.offset = {640.0f, 360.0f},
.half_extent = {8192.0f, 8192.0f},
.enabled = true,
};
const auto regular_result = compile(regular);
const auto clipped_result = compile(clipped);
CheckSpirvBinaryValidates(clipped_result.spirv);
Check(SpirvInstructionOpcodeCount(regular_result.spirv, 136u) == 0u &&
SpirvInstructionOpcodeCount(clipped_result.spirv, 133u) == 2u &&
SpirvInstructionOpcodeCount(clipped_result.spirv, 129u) == 2u &&
SpirvInstructionOpcodeCount(clipped_result.spirv, 136u) == 2u &&
SpirvInstructionOpcodeCount(clipped_result.spirv, 131u) == 2u,
"clip-disabled position export did not convert both screen coordinates to NDC");
Check(MakeStageStaticKey(regular) != MakeStageStaticKey(clipped),
"clip-disabled vertex shader shared the regular shader cache key");
auto disabled = clipped;
disabled.clip_space.enabled = false;
Check(MakeStageStaticKey(regular) == MakeStageStaticKey(disabled),
"disabled clip-space payload affected the shader cache key");
auto shifted = clipped;
shifted.clip_space.offset[0] += 1.0f;
Check(MakeStageStaticKey(clipped) != MakeStageStaticKey(shifted),
"clip-disabled viewport transform is absent from the shader cache key");
}
void TestNewShaderRecompilerAuxPositionExports() {
const auto compile = [](std::span<const uint32_t> shader, uint32_t control) {
ShaderVertexInputInfo vertex{};
@@ -9059,12 +9098,10 @@ void TestTypedEntryStateIsMinimal() {
translate_options);
uint32_t set_exec = 0;
uint32_t set_exec_mask_tag = 0;
uint32_t set_exec_lo = 0;
uint32_t set_exec_hi = 0;
uint32_t ballots = 0;
IR::Value exec;
IR::Value exec_mask_tag;
IR::Value exec_lo;
IR::Value exec_hi;
for (const auto *block : values.blocks) {
@@ -9077,10 +9114,6 @@ void TestTypedEntryStateIsMinimal() {
set_exec++;
exec = inst.Arg(0).Resolve();
break;
case IR::ValueOpcode::SetExecMaskTag:
set_exec_mask_tag++;
exec_mask_tag = inst.Arg(0).Resolve();
break;
case IR::ValueOpcode::SetExecLo:
set_exec_lo++;
exec_lo = inst.Arg(0).Resolve();
@@ -9095,7 +9128,6 @@ void TestTypedEntryStateIsMinimal() {
case IR::ValueOpcode::SetVectorRegister:
case IR::ValueOpcode::SetScc:
case IR::ValueOpcode::SetVcc:
case IR::ValueOpcode::SetVccMaskValidTag:
case IR::ValueOpcode::SetVccLo:
case IR::ValueOpcode::SetVccHi:
case IR::ValueOpcode::SetM0:
@@ -9106,9 +9138,8 @@ void TestTypedEntryStateIsMinimal() {
}
}
}
Check(set_exec == 1u && set_exec_mask_tag == 1u && set_exec_lo == 1u &&
set_exec_hi == 1u && ballots == 0u && exec.IsImmediate() &&
exec.U1() && exec_mask_tag.IsImmediate() && exec_mask_tag.U1() &&
Check(set_exec == 1u && set_exec_lo == 1u && set_exec_hi == 1u &&
ballots == 0u && exec.IsImmediate() && exec.U1() &&
exec_lo.IsImmediate() && exec_lo.U32() == 1u &&
exec_hi.IsImmediate() && exec_hi.U32() == 0u,
"typed entry is not the local {1,0} invocation mask");
@@ -9937,16 +9968,18 @@ void TestNewShaderRecompilerPerInvocationMasksWithoutMirrors() {
result = ShaderRecompiler::Recompile(wqm_shader, options);
CheckSpirvBinaryValidates(result.spirv);
const auto wqm_source = DisassembleSpirvBinary(result.spirv);
Check(!Common::ContainsStr(wqm_source, "OpGroupNonUniformBallot"),
"raw scalar WQM reconstructed its source from invocation provenance");
Check(Common::ContainsStr(wqm_source, "OpCapability GroupNonUniformBallot") &&
Common::ContainsStr(wqm_source, "OpGroupNonUniformBallot"),
"per-invocation scalar WQM omitted its subgroup ballot capability");
Check(SpirvInstructionOpcodeCount(result.spirv, 132u) == 2u,
"wave64 WQM did not compact exactly two ballot words");
auto wave32_options = options;
wave32_options.wave_size = 32u;
result = ShaderRecompiler::Recompile(wqm_shader, wave32_options);
CheckSpirvBinaryValidates(result.spirv);
Check(!Common::ContainsStr(DisassembleSpirvBinary(result.spirv),
"OpGroupNonUniformBallot"),
"wave32 raw scalar WQM reconstructed invocation provenance");
Check(SpirvInstructionOpcodeCount(result.spirv, 132u) == 1u,
"wave32 WQM retained the unused high ballot-word expansion");
const uint32_t cross_lane_shader[] = {
EncodeSop2(0x25, 126, 132, 128), // s_bfm_b64 exec, 4, 0
@@ -11868,8 +11901,8 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
.conditional_branches = 1,
.ballots = 1},
ShaderType::Vertex);
Check(Common::ContainsStr(wqm_result.ir_dump, "WqmU64"),
"WQM size fixture no longer reaches raw WqmU64 IR");
Check(Common::ContainsStr(wqm_result.ir_dump, "WqmMask"),
"WQM size fixture no longer reaches per-invocation WqmMask IR");
const uint32_t dispatcher[] = {
EncodeSopp(0x05, 2), // entry -> B, fallthrough A
@@ -11914,6 +11947,7 @@ int main() {
TestNewShaderRecompilerSpirvSizeBaselines();
TestDemandDrivenSpirvDeclarations();
TestNewShaderRecompilerSMovB32();
TestNewShaderRecompilerClipDisabledPosition();
TestNewShaderRecompilerAuxPositionExports();
TestNewShaderRecompilerNativeWideScalarMemoryIr();
TestNewShaderRecompilerNativeWideBufferIr();