Compare 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 Strongandnmzik 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
CheesyPoofs3460andnmzik 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
nmzik d1eee7b3a9 emulate dualsense touchpad + refactor (ps1 emulation) 2026-09-01 02:21:47 +04:00
nmzikandMac 1342c6f1e1 shader: took fixes from PR 427
- apply c8f34f9's ResourceTracking portability fix
- complete V_CMP_LT_U64 and S_FLBIT_I32_B32 support
- add the sceAmprCommandBufferWaitOnAddress NID from f625a47
- add decoder and compute tests for both shader operations

Co-authored-by: Mac <me@itsmemac.com>
2026-08-31 23:49:21 +04:00
GodsWill Devandnmzik 6453bafd1d renderer: ignore inactive stencil registers for depth-only targets (#425)
graphics: ignore inactive Hi-Stencil state

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-31 22:39:44 +04:00
48 changed files with 995 additions and 705 deletions

No files matched your search

+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);
@@ -177,10 +177,7 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
(z.stencil_read_base_addr & 0xffffu) != 0) {
DepthFatal("unsupported stencil attachment state");
}
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0 ||
!htile_stencil_compat ||
z.stencil_info.texture_compatibility !=
Prospero::TextureCompatibleStencil::kDisable) {
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
DepthFatal("stencil state without an active stencil attachment");
}
if (has_htile) {
@@ -11,9 +11,11 @@
namespace Libs::Graphics {
// Without a stencil plane, Hi-Stencil fields are inactive. An active plane is compatible when
// Hi-Stencil is disabled or HTile backing is present.
inline constexpr bool depth_htile_stencil_acceleration_compatible(bool has_stencil, bool has_htile,
bool htile_stencil_disabled) {
return htile_stencil_disabled || (has_stencil && has_htile);
return !has_stencil || htile_stencil_disabled || has_htile;
}
struct RenderDepthInfo {
@@ -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;
+22 -10
View File
@@ -29,6 +29,7 @@ struct ControlInfo {
uint32_t button = 0;
Controller::Axis axis = Controller::Axis::AxisMax;
bool positive = false;
float touch_x = 0.0f;
};
static constexpr std::array CONTROL_INFO = {
@@ -47,7 +48,10 @@ static constexpr std::array CONTROL_INFO = {
ControlInfo {"Circle", Controller::PAD_BUTTON_CIRCLE},
ControlInfo {"Cross", Controller::PAD_BUTTON_CROSS},
ControlInfo {"Square", Controller::PAD_BUTTON_SQUARE},
ControlInfo {"TouchPad", Controller::PAD_BUTTON_TOUCH_PAD},
ControlInfo {"TouchPad", Controller::PAD_BUTTON_TOUCH_PAD, Controller::Axis::AxisMax, false,
0.25f},
ControlInfo {"TouchPadRight", Controller::PAD_BUTTON_TOUCH_PAD, Controller::Axis::AxisMax,
false, 0.75f},
ControlInfo {"LeftStickLeft", 0, Controller::Axis::LeftX, false},
ControlInfo {"LeftStickRight", 0, Controller::Axis::LeftX, true},
ControlInfo {"LeftStickUp", 0, Controller::Axis::LeftY, false},
@@ -83,11 +87,6 @@ std::size_t ControlFromName(std::string_view name) {
return static_cast<std::size_t>(std::distance(CONTROL_INFO.begin(), info));
}
const ControlInfo& Info(std::size_t control) {
EXIT_IF(control >= CONTROL_INFO.size());
return CONTROL_INFO[control];
}
SDL_Keycode NormalizeKey(SDL_Keycode key) {
switch (key) {
case SDLK_RSHIFT: return SDLK_LSHIFT;
@@ -205,6 +204,10 @@ void SetButton(uint32_t button, bool down) {
}
}
void SetTouchPad(float x, bool down) {
Controller::ControllerTouchPad(Controller::HOST_INPUT_CONTROLLER_ID, 0, down, x, 0.5f);
}
uint32_t DefaultKeyboardButton(int key_code) {
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
case SDLK_UP: return Controller::PAD_BUTTON_UP;
@@ -221,8 +224,6 @@ uint32_t DefaultKeyboardButton(int key_code) {
case SDLK_LCTRL: return Controller::PAD_BUTTON_R3;
case SDLK_RETURN:
case SDLK_RETURN2: return Controller::PAD_BUTTON_OPTIONS;
case SDLK_BACKSPACE:
case SDLK_TAB: return Controller::PAD_BUTTON_TOUCH_PAD;
default: return 0;
}
}
@@ -235,7 +236,12 @@ struct StickKeys {
};
void SetStickAxis(Controller::Axis axis, bool negative, bool positive) {
const int value = negative == positive ? 128 : negative ? 0 : 255;
int value = 128;
if (negative && !positive) {
value = 0;
} else if (positive && !negative) {
value = 255;
}
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID, axis, value);
}
@@ -244,7 +250,11 @@ void SetControl(std::size_t control, bool down) {
return;
}
const auto& info = Info(control);
const auto& info = CONTROL_INFO[control];
if (info.button == Controller::PAD_BUTTON_TOUCH_PAD) {
SetTouchPad(info.touch_x, down);
return;
}
if (info.button != 0) {
SetButton(info.button, down);
return;
@@ -271,6 +281,8 @@ void DefaultKeyboardInput(int key_code, bool down) {
static StickKeys right;
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
case SDLK_BACKSPACE: SetTouchPad(0.25f, down); return;
case SDLK_TAB: SetTouchPad(0.75f, down); return;
case SDLK_a:
left.left = down;
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
+11 -2
View File
@@ -76,7 +76,6 @@ static uint32_t ControllerButtonToPadButton(int button) {
case SDL_CONTROLLER_BUTTON_B: return Controller::PAD_BUTTON_CIRCLE;
case SDL_CONTROLLER_BUTTON_X: return Controller::PAD_BUTTON_SQUARE;
case SDL_CONTROLLER_BUTTON_Y: return Controller::PAD_BUTTON_TRIANGLE;
case SDL_CONTROLLER_BUTTON_BACK: return Controller::PAD_BUTTON_TOUCH_PAD;
case SDL_CONTROLLER_BUTTON_START: return Controller::PAD_BUTTON_OPTIONS;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return Controller::PAD_BUTTON_L3;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return Controller::PAD_BUTTON_R3;
@@ -86,6 +85,7 @@ static uint32_t ControllerButtonToPadButton(int button) {
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return Controller::PAD_BUTTON_DOWN;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return Controller::PAD_BUTTON_LEFT;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return Controller::PAD_BUTTON_RIGHT;
case SDL_CONTROLLER_BUTTON_TOUCHPAD: return Controller::PAD_BUTTON_TOUCH_PAD;
default: return 0;
}
}
@@ -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) {
@@ -705,6 +704,16 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADMOTION:
case SDL_CONTROLLERTOUCHPADUP:
if (event->ctouchpad.touchpad == 0) {
Controller::ControllerTouchPad(event->ctouchpad.which, event->ctouchpad.finger,
event->ctouchpad.type != SDL_CONTROLLERTOUCHPADUP,
event->ctouchpad.x, event->ctouchpad.y);
}
break;
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMAPPED: {
@@ -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)};
}
@@ -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,
@@ -395,6 +396,7 @@ enum class Opcode {
V_CMP_GE_U32,
V_CMP_T_U32,
V_CMP_EQ_I64,
V_CMP_LT_U64,
V_CMP_EQ_U64,
V_CMP_GT_U64,
V_CMP_NE_U64,
@@ -240,8 +240,9 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
{0xb5u, Opcode::V_CMPX_NE_I64, false}, {0xd1u, Opcode::V_CMPX_LT_U32},
{0xd2u, Opcode::V_CMPX_EQ_U32}, {0xd3u, Opcode::V_CMPX_LE_U32},
{0xd4u, Opcode::V_CMPX_GT_U32}, {0xd5u, Opcode::V_CMPX_NE_U32},
{0xd6u, Opcode::V_CMPX_GE_U32}, {0xe2u, Opcode::V_CMP_EQ_U64, false},
{0xe4u, Opcode::V_CMP_GT_U64, false}, {0xe5u, Opcode::V_CMP_NE_U64, false},
{0xd6u, Opcode::V_CMPX_GE_U32}, {0xe1u, Opcode::V_CMP_LT_U64, false},
{0xe2u, Opcode::V_CMP_EQ_U64, false}, {0xe4u, Opcode::V_CMP_GT_U64, false},
{0xe5u, Opcode::V_CMP_NE_U64, false},
{0xf5u, Opcode::V_CMPX_NE_U64, false}, {0xc9u, Opcode::V_CMP_LT_F16},
{0xcau, Opcode::V_CMP_EQ_F16}, {0xcbu, Opcode::V_CMP_LE_F16},
{0xccu, Opcode::V_CMP_GT_F16}, {0xcdu, Opcode::V_CMP_LG_F16},
@@ -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) {
@@ -181,6 +181,7 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
case O::S_XNOR_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseXor32, false, true, true);
case O::S_FF1_I32_B64: return S_FF1_I32_B64(inst);
case O::S_FLBIT_I32_B32: return V_FFBH_32(inst, false);
case O::S_FLBIT_I32_B64: return S_FLBIT_I32_B64(inst);
case O::S_BITSET0_B32: return S_BITSET_B32(inst, false);
@@ -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,
@@ -99,6 +99,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_CMP_EQ_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, false);
return true;
case O::V_CMP_LT_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan64, IR::Type::U64, false, false);
return true;
case O::V_CMP_GT_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, false);
return true;
@@ -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)
@@ -139,7 +139,8 @@ public:
}
std::erase_if(m_program.dynamic_reads, [&](Value value) {
const auto* inst = value.Resolve().TryInstruction();
return std::ranges::any_of(m_indirect_images, [&](const IndirectImagePlan& plan) {
return std::any_of(m_indirect_images.begin(), m_indirect_images.end(),
[&](const IndirectImagePlan& plan) {
return std::ranges::find(plan.reads, inst) != plan.reads.end();
});
});
@@ -432,14 +433,16 @@ private:
const IndirectImagePlan* FindIndirectImage(const Inst& handle) const {
const auto found =
std::ranges::find_if(m_indirect_images, [&](const IndirectImagePlan& plan) {
std::find_if(m_indirect_images.begin(), m_indirect_images.end(),
[&](const IndirectImagePlan& plan) {
return plan.handle == &handle;
});
return found == m_indirect_images.end() ? nullptr : &*found;
}
bool IsIndirectPlanningMemory(uint32_t index) const {
return std::ranges::any_of(m_indirect_images, [&](const IndirectImagePlan& plan) {
return std::any_of(m_indirect_images.begin(), m_indirect_images.end(),
[&](const IndirectImagePlan& plan) {
return std::ranges::find(plan.memory, index) != plan.memory.end();
});
}
@@ -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;
};
+2 -1
View File
@@ -51,7 +51,8 @@ constexpr PadControl PAD_CONTROLS[] = {
{"L3", "L3", "Left Shift"},
{"R3", "R3", "Left Ctrl"},
{"Options", "Options", "Return"},
{"TouchPad", "Touch pad", "Backspace"},
{"TouchPad", "Touch pad left (SELECT)", "Backspace"},
{"TouchPadRight", "Touch pad right (START)", "Tab"},
};
QString KeypadName(int key) {
+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;
}
+105 -101
View File
@@ -45,15 +45,22 @@ struct PadVibrationParam {
};
struct ControllerState {
struct Touch {
uint8_t id = 0;
bool down = false;
uint16_t x = 0;
uint16_t y = 0;
};
uint64_t time = 0;
uint32_t buttons = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
Touch touch[2];
};
class GameController {
public:
GameController() = default;
virtual ~GameController() = default;
GameController() = default;
KYTY_CLASS_NO_COPY(GameController);
@@ -62,6 +69,7 @@ public:
void Button(int id, uint32_t button, bool down);
void Axis(int id, Axis axis, int value);
void RightStick(int id, int x, int y);
void TouchPad(int id, int finger, bool down, float x, float y);
void ResetInputState();
void GetConnectionInfo(bool* flag, int* count);
void SetVibration(uint8_t large_motor, uint8_t small_motor);
@@ -71,51 +79,49 @@ public:
private:
static constexpr uint32_t STATES_MAX = 64;
struct StatePrivate {
bool obtained = false;
};
void CheckActive();
[[nodiscard]] ControllerState GetLastState() const;
void AddState(const ControllerState& state);
void CheckActive();
void AddState();
Common::Mutex m_mutex;
std::vector<int> m_connected_ids;
int m_active_id = -1;
bool m_connected = false;
int m_connected_count = 0;
ControllerState m_state;
ControllerState m_states[STATES_MAX];
StatePrivate m_private[STATES_MAX];
ControllerState m_last_state;
uint32_t m_states_num = 0;
uint32_t m_first_state = 0;
bool m_obtained[STATES_MAX] {};
uint32_t m_states_num = 0;
uint32_t m_first_state = 0;
uint8_t m_next_touch_id = 1;
};
static GameController* g_controller = nullptr;
static uint8_t pad_connected_count_to_u8(int connected_count) {
return static_cast<uint8_t>(connected_count > 255 ? 255 : connected_count);
}
static void pad_fill_data(PadData* data, const ControllerState& state, bool connected,
int connected_count) {
EXIT_IF(data == nullptr);
std::memset(data, 0, sizeof(*data));
data->buttons = state.buttons;
data->left_stick_x = state.axes[static_cast<int>(Axis::LeftX)];
data->left_stick_y = state.axes[static_cast<int>(Axis::LeftY)];
data->right_stick_x = state.axes[static_cast<int>(Axis::RightX)];
data->right_stick_y = state.axes[static_cast<int>(Axis::RightY)];
data->analog_buttons_l2 = state.axes[static_cast<int>(Axis::TriggerLeft)];
data->analog_buttons_r2 = state.axes[static_cast<int>(Axis::TriggerRight)];
data->orientation_w = 1.0f;
data->touch_data_touch0_id = 1;
data->touch_data_touch1_id = 2;
data->buttons = state.buttons;
data->left_stick_x = state.axes[static_cast<int>(Axis::LeftX)];
data->left_stick_y = state.axes[static_cast<int>(Axis::LeftY)];
data->right_stick_x = state.axes[static_cast<int>(Axis::RightX)];
data->right_stick_y = state.axes[static_cast<int>(Axis::RightY)];
data->analog_buttons_l2 = state.axes[static_cast<int>(Axis::TriggerLeft)];
data->analog_buttons_r2 = state.axes[static_cast<int>(Axis::TriggerRight)];
data->orientation_w = 1.0f;
for (const auto& touch: state.touch) {
if (touch.down) {
auto& output = data->touch_data.touch[data->touch_data.touch_num++];
output.x = touch.x;
output.y = touch.y;
output.id = touch.id;
}
}
data->connected = connected;
data->timestamp = state.time;
data->connected_count = pad_connected_count_to_u8(connected_count);
data->connected_count = static_cast<uint8_t>(std::min(connected_count, 255));
data->device_unique_data_len = 0;
}
@@ -155,7 +161,6 @@ void GameController::Disconnect(int id) {
}
void GameController::CheckActive() {
bool reset = false;
int new_active_id = -1;
bool new_connected = false;
@@ -170,46 +175,29 @@ void GameController::CheckActive() {
new_connected = true;
}
const bool was_connected = m_connected;
if (m_connected != new_connected || m_active_id != new_active_id) {
m_active_id = new_active_id;
m_connected = new_connected;
if (!was_connected && new_connected) {
m_connected_count++;
}
reset = true;
if (m_connected == new_connected && m_active_id == new_active_id) {
return;
}
if (reset) {
m_states_num = 0;
m_last_state = ControllerState();
if (!m_connected && new_connected) {
m_connected_count++;
}
m_active_id = new_active_id;
m_connected = new_connected;
m_state = {};
m_states_num = 0;
m_first_state = 0;
m_next_touch_id = 1;
}
ControllerState GameController::GetLastState() const {
if (m_states_num == 0) {
return m_last_state;
}
auto last = (m_first_state + m_states_num - 1) % STATES_MAX;
return m_states[last];
}
void GameController::AddState(const ControllerState& state) {
void GameController::AddState() {
if (m_states_num >= STATES_MAX) {
m_states_num = STATES_MAX - 1;
m_first_state = (m_first_state + 1) % STATES_MAX;
}
auto index = (m_first_state + m_states_num) % STATES_MAX;
m_states[index] = state;
m_last_state = state;
m_private[index].obtained = false;
const auto index = (m_first_state + m_states_num) % STATES_MAX;
m_states[index] = m_state;
m_obtained[index] = false;
m_states_num++;
}
@@ -218,17 +206,11 @@ void GameController::Button(int id, uint32_t button, bool down) {
// The keyboard shares the player-1 pad with the active gamepad.
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto state = GetLastState();
m_state.time = LibKernel::KernelGetProcessTime();
state.time = LibKernel::KernelGetProcessTime();
m_state.buttons = down ? m_state.buttons | button : m_state.buttons & ~button;
if (down) {
state.buttons |= button;
} else {
state.buttons &= ~button;
}
AddState(state);
AddState();
}
}
@@ -236,33 +218,25 @@ void GameController::Axis(int id, Controller::Axis axis, int value) {
Common::LockGuard lock(m_mutex);
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
m_state.time = LibKernel::KernelGetProcessTime();
int axis_id = static_cast<int>(axis);
EXIT_IF(axis_id < 0 || axis_id >= static_cast<int>(Controller::Axis::AxisMax));
state.axes[axis_id] = value;
m_state.axes[axis_id] = value;
uint32_t trigger = 0;
if (axis == Controller::Axis::TriggerLeft) {
if (value > 0) {
state.buttons |= PAD_BUTTON_L2;
} else {
state.buttons &= ~PAD_BUTTON_L2;
}
trigger = PAD_BUTTON_L2;
} else if (axis == Controller::Axis::TriggerRight) {
trigger = PAD_BUTTON_R2;
}
if (trigger != 0) {
m_state.buttons = value > 0 ? m_state.buttons | trigger : m_state.buttons & ~trigger;
}
if (axis == Controller::Axis::TriggerRight) {
if (value > 0) {
state.buttons |= PAD_BUTTON_R2;
} else {
state.buttons &= ~PAD_BUTTON_R2;
}
}
AddState(state);
AddState();
}
}
@@ -270,21 +244,45 @@ void GameController::RightStick(int id, int x, int y) {
Common::LockGuard lock(m_mutex);
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
state.axes[static_cast<int>(Axis::RightX)] = x;
state.axes[static_cast<int>(Axis::RightY)] = y;
AddState(state);
m_state.time = LibKernel::KernelGetProcessTime();
m_state.axes[static_cast<int>(Axis::RightX)] = x;
m_state.axes[static_cast<int>(Axis::RightY)] = y;
AddState();
}
}
void GameController::TouchPad(int id, int finger, bool down, float x, float y) {
if (finger < 0 || finger >= 2) {
return;
}
Common::LockGuard lock(m_mutex);
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto& touch = m_state.touch[finger];
m_state.time = LibKernel::KernelGetProcessTime();
if (down && !touch.down) {
touch.id = m_next_touch_id;
m_next_touch_id = m_next_touch_id == 127 ? 1 : m_next_touch_id + 1;
}
touch.down = down;
touch.x = static_cast<uint16_t>(std::clamp(x, 0.0f, 1.0f) * 1920.0f);
touch.y = static_cast<uint16_t>(std::clamp(y, 0.0f, 1.0f) * 943.0f);
if (id == HOST_INPUT_CONTROLLER_ID) {
m_state.buttons = down ? m_state.buttons | PAD_BUTTON_TOUCH_PAD
: m_state.buttons & ~PAD_BUTTON_TOUCH_PAD;
}
AddState();
}
}
void GameController::ResetInputState() {
Common::LockGuard lock(m_mutex);
ControllerState state {};
state.time = LibKernel::KernelGetProcessTime();
m_states_num = 0;
m_first_state = 0;
AddState(state);
m_state = {};
m_state.time = LibKernel::KernelGetProcessTime();
m_states_num = 0;
m_first_state = 0;
m_next_touch_id = 1;
AddState();
}
void GameController::SetVibration(uint8_t large_motor, uint8_t small_motor) {
@@ -325,7 +323,7 @@ void GameController::ReadState(ControllerState* state, bool* flag, int* count) {
*flag = m_connected;
*count = m_connected_count;
*state = GetLastState();
*state = m_state;
}
int GameController::ReadStates(ControllerState* states, int states_num, bool* flag, int* count) {
@@ -348,8 +346,8 @@ int GameController::ReadStates(ControllerState* states, int states_num, bool* fl
break;
}
auto index = (m_first_state + i) % STATES_MAX;
if (!m_private[index].obtained) {
m_private[index].obtained = true;
if (!m_obtained[index]) {
m_obtained[index] = true;
states[ret_num++] = m_states[index];
}
@@ -390,6 +388,12 @@ void ControllerRightStick(int id, int x, int y) {
g_controller->RightStick(id, x, y);
}
void ControllerTouchPad(int id, int finger, bool down, float x, float y) {
EXIT_IF(g_controller == nullptr);
g_controller->TouchPad(id, finger, down, x, y);
}
void ControllerResetInputState() {
EXIT_IF(g_controller == nullptr);
g_controller->ResetInputState();
@@ -508,7 +512,7 @@ int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformati
info->stick_dead_zone_left = controller_get_axis(-32768, 32767, 8000) - 128;
info->stick_dead_zone_right = controller_get_axis(-32768, 32767, 8000) - 128;
info->connection_type = 0;
info->connected_count = pad_connected_count_to_u8(connected_count);
info->connected_count = static_cast<uint8_t>(std::min(connected_count, 255));
info->connected = connected;
info->device_class = 0;
+8 -1
View File
@@ -51,7 +51,13 @@ struct PadLightBarParam;
inline int controller_get_axis(int min, int max, int value) {
int v = (255 * (value - min)) / (max - min);
return (v < 0 ? 0 : (v > 255 ? 255 : v));
if (v < 0) {
return 0;
}
if (v > 255) {
return 255;
}
return v;
}
void ControllerConnect(int id);
@@ -59,6 +65,7 @@ void ControllerDisconnect(int id);
void ControllerButton(int id, uint32_t button, bool down);
void ControllerAxis(int id, Axis axis, int value);
void ControllerRightStick(int id, int x, int y);
void ControllerTouchPad(int id, int finger, bool down, float x, float y);
void ControllerResetInputState();
int KYTY_SYSV_ABI PadInit();
+1
View File
@@ -2665,6 +2665,7 @@ LIB_DEFINE(InitAmpr_1_Ampr) {
LIB_FUNC("dXPaz65HNmk", Ampr::CommandBufferPushMarker);
LIB_FUNC("mv0O8Zg0woU", Ampr::CommandBufferPopMarker);
LIB_FUNC("DLfoNxTFNVk", Ampr::CommandBufferWaitOnAddress);
LIB_FUNC("V7GQTEeUfhw", Ampr::CommandBufferWaitOnAddress);
LIB_FUNC("cQb8Zr8Q0Y0", Ampr::CommandBufferWaitOnCounter);
LIB_FUNC("j0+3uJMxYJY", Ampr::CommandBufferWriteAddress);
LIB_FUNC("sJXyWHjP-F8", Ampr::CommandBufferWriteAddressOnCompletion);
+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);
+43 -39
View File
@@ -5,46 +5,50 @@
namespace Libs::Controller {
struct PadTouch {
uint16_t x;
uint16_t y;
uint8_t id;
uint8_t reserve[3];
};
struct PadTouchData {
uint8_t touch_num;
uint8_t reserve[3];
uint32_t reserve1;
PadTouch touch[2];
};
struct PadData {
uint32_t buttons;
uint8_t left_stick_x;
uint8_t left_stick_y;
uint8_t right_stick_x;
uint8_t right_stick_y;
uint8_t analog_buttons_l2;
uint8_t analog_buttons_r2;
uint8_t padding[2];
float orientation_x;
float orientation_y;
float orientation_z;
float orientation_w;
float acceleration_x;
float acceleration_y;
float acceleration_z;
float angular_velocity_x;
float angular_velocity_y;
float angular_velocity_z;
uint8_t touch_data_touch_num;
uint8_t touch_data_reserve[3];
uint32_t touch_data_reserve1;
uint16_t touch_data_touch0_x;
uint16_t touch_data_touch0_y;
uint8_t touch_data_touch0_id;
uint8_t touch_data_touch0_reserve[3];
uint16_t touch_data_touch1_x;
uint16_t touch_data_touch1_y;
uint8_t touch_data_touch1_id;
uint8_t touch_data_touch1_reserve[3];
bool connected;
uint64_t timestamp;
uint32_t extension_unit_data_extension_unit_id;
uint8_t extension_unit_data_reserve[1];
uint8_t extension_unit_data_data_length;
uint8_t extension_unit_data_data[10];
uint8_t connected_count;
uint8_t reserve[2];
uint8_t device_unique_data_len;
uint8_t device_unique_data[12];
uint32_t buttons;
uint8_t left_stick_x;
uint8_t left_stick_y;
uint8_t right_stick_x;
uint8_t right_stick_y;
uint8_t analog_buttons_l2;
uint8_t analog_buttons_r2;
uint8_t padding[2];
float orientation_x;
float orientation_y;
float orientation_z;
float orientation_w;
float acceleration_x;
float acceleration_y;
float acceleration_z;
float angular_velocity_x;
float angular_velocity_y;
float angular_velocity_z;
PadTouchData touch_data;
bool connected;
uint64_t timestamp;
uint32_t extension_unit_data_extension_unit_id;
uint8_t extension_unit_data_reserve[1];
uint8_t extension_unit_data_data_length;
uint8_t extension_unit_data_data[10];
uint8_t connected_count;
uint8_t reserve[2];
uint8_t device_unique_data_len;
uint8_t device_unique_data[12];
};
static_assert(sizeof(PadData) == 120);
+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;
}
}
}
+319 -204
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;
@@ -8358,12 +8457,17 @@ public:
RenderExecutorTestAccess::ResetBindings(executor);
constexpr uint64_t depth_only_address = base + 0x140000;
constexpr uint64_t depth_only_htile_address = base + 0x150000;
constexpr uint32_t captured_depth_only_z_info = 0x22900983u;
constexpr uint32_t captured_depth_only_stencil_info = 0x00100980u;
HW::DepthRenderTarget depth_only_target{};
depth_only_target.z_info.format = Prospero::DepthFormat::kZ32F;
depth_only_target.z_info.z_compare_base = Prospero::ZCompareBase::kZMax;
depth_only_target.stencil_info.htile_stencil_disabled = true;
depth_only_target.z_info =
HW::DepthZInfo::Decode(captured_depth_only_z_info);
depth_only_target.stencil_info =
HW::DepthStencilInfo::Decode(captured_depth_only_stencil_info);
depth_only_target.z_read_base_addr = depth_only_address;
depth_only_target.z_write_base_addr = depth_only_address;
depth_only_target.htile_data_base_addr = depth_only_htile_address;
depth_only_target.size = {63, 63, true};
registers.SetDepthRenderTarget(depth_only_target);
HW::DepthControl depth_only_control{};
@@ -8382,6 +8486,7 @@ public:
Require(
name, "depth-only target with stale stencil state",
depth_only.image_id && depth_only.format == vk::Format::eD32Sfloat &&
depth_only.htile &&
depth_only.depth_test_enable && depth_only.depth_write_enable &&
depth_only.depth_clear_enable &&
!depth_only.stencil_test_enable &&
@@ -8389,14 +8494,16 @@ public:
depth_only.stencil_buffer_vaddr == 0 &&
depth_only.stencil_buffer_size == 0 &&
depth_only.desc.info.stencil.Empty() &&
depth_only.htile_buffer_vaddr == depth_only_htile_address &&
depth_only.desc.info.metadata.range.address ==
depth_only_htile_address &&
depth_only.depth_buffer_size != 0 &&
depth_only_address + depth_only.depth_buffer_size <=
base + allocation_size &&
depth_only.vaddr_num == 1 &&
depth_only.AttachmentWriteAspects() ==
vk::ImageAspectFlagBits::eDepth,
"raw stencil test or clear state leaked into a depth-only "
"attachment");
"inactive stencil state changed a depth-only HTile attachment");
RenderExecutorTestAccess::ResetBindings(executor);
const auto sampled_width = depth_only.desc.info.extent.width - 1u;
@@ -12733,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;
@@ -12952,6 +12905,36 @@ TestCase ScalarFlbitI32B64Gpu() {
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarFlbitI32B32Gpu() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendSMovLiteral(&code, 0, 0x00000000u);
code.push_back(EncodeSop1(0x15, 20, 0));
AppendSMovLiteral(&code, 0, 0x0000ccccu);
code.push_back(EncodeSop1(0x15, 21, 0));
AppendSMovLiteral(&code, 0, 0x80000000u);
code.push_back(EncodeSop1(0x15, 22, 0));
AppendSMovLiteral(&code, 0, 0x7fffffffu);
code.push_back(EncodeSop1(0x15, 23, 0));
AppendStoreSgpr(&code, 20, 0);
AppendStoreSgpr(&code, 21, 1);
AppendStoreSgpr(&code, 22, 2);
AppendStoreSgpr(&code, 23, 3);
AppendEnd(&code);
return {"ScalarFlbitI32B32Gpu",
code,
{},
{0xffffffffu, 16, 0, 1},
{O::S_MOV_B32, O::S_FLBIT_I32_B32, O::V_MOV_B32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarSaveExecOps() {
using O = ShaderOpcode;
@@ -12985,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,
@@ -13012,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}};
}
@@ -15137,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;
@@ -16083,6 +16092,36 @@ TestCase VectorVop3CompareGtU64OnGpu() {
return test;
}
TestCase VectorVopcCompareLtU64OnGpu() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovU32(&code, 0, 5);
AppendVMovU32(&code, 1, 0);
AppendVMovU32(&code, 2, 1);
AppendSMovLiteral(&code, 4, 4);
AppendSMovLiteral(&code, 5, 0);
code.push_back(EncodeVopc(0xe1, 4, 0));
code.push_back(EncodeVop2(0x01, 3, InlineU32(0), 2));
AppendStoreVgpr(&code, 3, 0);
AppendSMovLiteral(&code, 4, 6);
code.push_back(EncodeVopc(0xe1, 4, 0));
code.push_back(EncodeVop2(0x01, 4, InlineU32(0), 2));
AppendStoreVgpr(&code, 4, 1);
AppendEnd(&code);
TestCase test{"VectorVopcCompareLtU64OnGpu",
code,
{},
{1, 0},
{O::V_MOV_B32, O::S_MOV_B32, O::V_CMP_LT_U64,
O::V_CNDMASK_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
test.decoded_counts = {{"V_CMP_LT_U64 vcc_lo, s4, v0", 2}};
return test;
}
TestCase VectorVop3CompareEqI64OnGpu() {
using O = ShaderOpcode;
@@ -16668,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;
@@ -21135,11 +21150,9 @@ std::vector<TestCase> MakeCases() {
AddCase(ScalarCompareOps);
AddCase(ScalarShiftAddAndMaskOps);
AddCase(ScalarNotB64UpdatesScc);
AddCase(ScalarMovB64PreservesRawSpecialData);
AddCase(Scalar64RawSpecialOps);
AddCase(ScalarMovB32SpecialAliases);
AddCase(ScalarQuadmaskB64);
AddCase(ScalarFlbitI32B64Gpu);
AddCase(ScalarFlbitI32B32Gpu);
AddCase(ScalarSaveExecOps);
AddCase(ScalarOrn2SaveexecUsesSourceOrNotExec);
AddCase(ScalarGetpcWritesNextInstructionPc);
@@ -21206,6 +21219,7 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorPermlane16FetchInactiveZero);
AddCase(VectorPermlane16FetchInactiveFi);
AddCase(VectorDppQuadPermuteReverse);
AddCase(VectorDppRowXmask);
AddCase(VectorDppBankMaskPreservesDestination);
AddCase(VectorDppBoundsControlZeroPreservesDestination);
AddCase(Vop3LdexpSourceModifier);
@@ -21230,6 +21244,7 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorVop3CompareEqI64OnGpu);
AddCase(VectorVop3CompareEqU64OnGpu);
AddCase(VectorVop3CompareGtU64OnGpu);
AddCase(VectorVopcCompareLtU64OnGpu);
AddCase(VectorVop3CompareNeU64OnGpu);
AddCase(VectorVopcCmpxNeU64CapturedExecMask);
AddCase(VectorVop3CmpxNeI64CapturedExecMask);
@@ -21252,7 +21267,6 @@ std::vector<TestCase> MakeCases() {
AddCase(BranchVccnzUsesInvocationMask);
AddCase(BranchVccnzUsesCarryProducedInvocationMask);
AddCase(ScalarMemoryLoadVariants);
AddCase(ScalarLoadUsesRawExecAddress);
AddCase(ScalarLoadSignedImmediateOffsetAddsSoffset);
AddCase(ScalarLoadAlignsComponentsAndMasksAddress);
AddCase(BufferLoadStore);
@@ -23792,18 +23806,18 @@ void CheckNativeMsaaState() {
}
void CheckDepthHtileStencilCompatibility() {
Require("DepthHtileStencilCompatibility", "inactive stencil plane",
depth_htile_stencil_acceleration_compatible(false, true, false),
"inactive Hi-Stencil state was treated as an attachment");
Require("DepthHtileStencilCompatibility", "disabled acceleration",
depth_htile_stencil_acceleration_compatible(false, false, true),
depth_htile_stencil_acceleration_compatible(true, false, true),
"disabled Hi-Stencil state was rejected");
Require("DepthHtileStencilCompatibility", "PS5 stencil plus HTile",
depth_htile_stencil_acceleration_compatible(true, true, false),
"valid PS5 Hi-Stencil attachment was rejected");
Require("DepthHtileStencilCompatibility", "missing stencil plane",
!depth_htile_stencil_acceleration_compatible(false, true, false),
"Hi-Stencil without a stencil plane was silently admitted");
Require("DepthHtileStencilCompatibility", "missing HTile metadata",
!depth_htile_stencil_acceleration_compatible(true, false, false),
"Hi-Stencil without HTile metadata was silently admitted");
"active Hi-Stencil without HTile metadata was admitted");
std::printf("[host] %-32s ok\n", "DepthHtileStencilCompatibility");
}
@@ -24486,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 &&
@@ -24500,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,
@@ -24531,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);
@@ -24693,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 *);
@@ -25291,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;
}
@@ -25329,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();
@@ -25471,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());
+82 -17
View File
@@ -3969,6 +3969,37 @@ void TestNewShaderDecoderArchitecture() {
cmp_eq_u64.src1.kind == OperandKind::VccLo,
"decoder rejected the captured VOP3 V_CMP_EQ_U64 instruction");
const uint32_t completed_pr_opcodes[] = {
EncodeSop1(0x15, 0, 1), // s_flbit_i32_b32 s0, s1
EncodeVopc(0xe1, 128, 0), // v_cmp_lt_u64 vcc, 0, v[0:1]
EncodeSopp(0x01, 0),
};
Instruction flbit_i32_b32;
ShaderRecompiler::Decoder::DecodeInstruction(completed_pr_opcodes, 0u,
flbit_i32_b32);
Instruction cmp_lt_u64;
ShaderRecompiler::Decoder::DecodeInstruction(completed_pr_opcodes, 1u,
cmp_lt_u64);
Check(flbit_i32_b32.family == Family::SOP1 &&
flbit_i32_b32.opcode == Opcode::S_FLBIT_I32_B32 &&
flbit_i32_b32.dst.kind == OperandKind::Sgpr &&
flbit_i32_b32.dst.reg == 0u &&
flbit_i32_b32.src0.kind == OperandKind::Sgpr &&
flbit_i32_b32.src0.reg == 1u &&
cmp_lt_u64.family == Family::VOPC &&
cmp_lt_u64.opcode == Opcode::V_CMP_LT_U64 &&
cmp_lt_u64.dst.kind == OperandKind::VccLo,
"decoder rejected completed PR #427 scalar/vector opcodes");
const auto completed_options = MakeCompileOptions(ShaderType::Compute);
const auto completed_result =
ShaderRecompiler::Recompile(completed_pr_opcodes, completed_options);
Check(Common::ContainsStr(completed_result.decoded_dump,
"S_FLBIT_I32_B32 s0, s1") &&
Common::ContainsStr(completed_result.decoded_dump,
"V_CMP_LT_U64 vcc_lo, 0, v0"),
"completed PR #427 opcodes were absent from the decoded shader");
const uint32_t ffbh_i32_code[] = {0x7e1c770eu};
Instruction ffbh_i32;
ShaderRecompiler::Decoder::DecodeInstruction(ffbh_i32_code, 0u, ffbh_i32);
@@ -8780,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{};
@@ -9028,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) {
@@ -9046,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();
@@ -9064,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:
@@ -9075,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");
@@ -9906,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
@@ -11837,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
@@ -11883,6 +11947,7 @@ int main() {
TestNewShaderRecompilerSpirvSizeBaselines();
TestDemandDrivenSpirvDeclarations();
TestNewShaderRecompilerSMovB32();
TestNewShaderRecompilerClipDisabledPosition();
TestNewShaderRecompilerAuxPositionExports();
TestNewShaderRecompilerNativeWideScalarMemoryIr();
TestNewShaderRecompilerNativeWideBufferIr();