mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a87cfedb5 | ||
|
|
5bd447d2a2 | ||
|
|
a4fc58e908 |
No files matched your search
@@ -1,18 +1,24 @@
|
||||
#include "graphics/presentation/window/hostInput.h"
|
||||
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_keyboard.h"
|
||||
#include "SDL_keycode.h"
|
||||
#include "SDL_mouse.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_video.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "libs/controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -60,6 +66,17 @@ struct Binding {
|
||||
std::size_t control = INVALID_CONTROL;
|
||||
};
|
||||
|
||||
constexpr int MOUSE_POLL_INTERVAL_MS = 33;
|
||||
constexpr std::string_view MOUSE_SENSITIVITY = "MouseSensitivity=";
|
||||
|
||||
struct MouseJoystickState {
|
||||
bool enabled = false;
|
||||
bool output = false;
|
||||
uint64_t next_poll = 0;
|
||||
};
|
||||
|
||||
MouseJoystickState g_mouse;
|
||||
|
||||
std::size_t ControlFromName(std::string_view name) {
|
||||
const auto info = std::find_if(CONTROL_INFO.begin(), CONTROL_INFO.end(),
|
||||
[name](const auto& item) { return item.name == name; });
|
||||
@@ -103,10 +120,17 @@ bool Conflicts(const Binding& first, const Binding& second) {
|
||||
|
||||
class InputMap {
|
||||
public:
|
||||
InputMap(): m_custom(!Config::GetKeymap().empty()) {
|
||||
InputMap() {
|
||||
for (const auto& value: Config::GetKeymap()) {
|
||||
const std::string_view entry = value;
|
||||
const auto split = entry.find('=');
|
||||
if (entry.starts_with(MOUSE_SENSITIVITY)) {
|
||||
const float sensitivity =
|
||||
std::strtof(value.c_str() + MOUSE_SENSITIVITY.size(), nullptr);
|
||||
m_mouse_sensitivity =
|
||||
std::clamp(std::isfinite(sensitivity) ? sensitivity : 1.0f, 0.1f, 5.0f);
|
||||
continue;
|
||||
}
|
||||
const auto split = entry.find('=');
|
||||
|
||||
Binding binding;
|
||||
if (split != std::string_view::npos) {
|
||||
@@ -118,9 +142,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const bool reserved =
|
||||
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1 ||
|
||||
binding.key == SDLK_F11;
|
||||
const bool reserved = binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE ||
|
||||
binding.key == SDLK_F1 || binding.key == SDLK_F7 ||
|
||||
binding.key == SDLK_F11;
|
||||
if (binding.control == INVALID_CONTROL || reserved ||
|
||||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
|
||||
EXIT("Invalid input mapping: %s\n", value.c_str());
|
||||
@@ -128,7 +152,8 @@ public:
|
||||
Add(binding);
|
||||
}
|
||||
}
|
||||
[[nodiscard]] bool Custom() const { return m_custom; }
|
||||
[[nodiscard]] bool Custom() const { return m_size != 0; }
|
||||
[[nodiscard]] float MouseSensitivity() const { return m_mouse_sensitivity; }
|
||||
|
||||
[[nodiscard]] std::size_t FindKey(int key_code) const {
|
||||
key_code = NormalizeKey(static_cast<SDL_Keycode>(key_code));
|
||||
@@ -159,8 +184,8 @@ private:
|
||||
}
|
||||
|
||||
std::array<Binding, CONTROL_INFO.size()> m_bindings {};
|
||||
std::size_t m_size = 0;
|
||||
bool m_custom;
|
||||
std::size_t m_size = 0;
|
||||
float m_mouse_sensitivity = 1.0f;
|
||||
};
|
||||
|
||||
const InputMap& GetInputMap() {
|
||||
@@ -281,6 +306,33 @@ void DefaultKeyboardInput(int key_code, bool down) {
|
||||
default: SetButton(DefaultKeyboardButton(key_code), down); return;
|
||||
}
|
||||
}
|
||||
|
||||
void MouseToJoystick(int delta_x, int delta_y) {
|
||||
const double distance = std::hypot(delta_x, delta_y);
|
||||
const double scale =
|
||||
std::clamp(distance * GetInputMap().MouseSensitivity() + 16.0, 64.0, 128.0) / distance;
|
||||
const auto map_axis = [scale](int delta) {
|
||||
return std::clamp(128 + static_cast<int>(std::lround(delta * scale)), 0, 255);
|
||||
};
|
||||
Controller::ControllerRightStick(Controller::HOST_INPUT_CONTROLLER_ID, map_axis(delta_x),
|
||||
map_axis(delta_y));
|
||||
}
|
||||
|
||||
void CenterMouseStick() {
|
||||
if (!g_mouse.output) {
|
||||
return;
|
||||
}
|
||||
Controller::ControllerRightStick(Controller::HOST_INPUT_CONTROLLER_ID, 128, 128);
|
||||
g_mouse.output = false;
|
||||
}
|
||||
|
||||
bool SetRelativeMouseMode(bool enabled) {
|
||||
if (SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE) == 0) {
|
||||
return true;
|
||||
}
|
||||
LOGF("Mouse-to-joystick relative mode failed: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void HostInputInit() {
|
||||
@@ -303,4 +355,63 @@ void HostInputMouseButton(uint8_t mouse_button, bool down) {
|
||||
}
|
||||
}
|
||||
|
||||
void HostInputToggleMouseToJoystick() {
|
||||
if (g_mouse.enabled) {
|
||||
SetRelativeMouseMode(false);
|
||||
CenterMouseStick();
|
||||
g_mouse = {};
|
||||
LOGF("Mouse to right stick: disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetRelativeMouseMode(true)) {
|
||||
return;
|
||||
}
|
||||
int ignored_x = 0;
|
||||
int ignored_y = 0;
|
||||
SDL_GetRelativeMouseState(&ignored_x, &ignored_y);
|
||||
g_mouse.enabled = true;
|
||||
g_mouse.next_poll = SDL_GetTicks64() + MOUSE_POLL_INTERVAL_MS;
|
||||
LOGF("Mouse to right stick: enabled (F7 to release)\n");
|
||||
}
|
||||
|
||||
int PollMouse(uint64_t now_ms) {
|
||||
if (now_ms < g_mouse.next_poll) {
|
||||
return static_cast<int>(g_mouse.next_poll - now_ms);
|
||||
}
|
||||
g_mouse.next_poll = now_ms + MOUSE_POLL_INTERVAL_MS;
|
||||
|
||||
int delta_x = 0;
|
||||
int delta_y = 0;
|
||||
SDL_GetRelativeMouseState(&delta_x, &delta_y);
|
||||
if (delta_x == 0 && delta_y == 0) {
|
||||
CenterMouseStick();
|
||||
return MOUSE_POLL_INTERVAL_MS;
|
||||
}
|
||||
|
||||
MouseToJoystick(delta_x, delta_y);
|
||||
g_mouse.output = true;
|
||||
return MOUSE_POLL_INTERVAL_MS;
|
||||
}
|
||||
|
||||
bool HostInputWaitEvent(SDL_Event* event) {
|
||||
if (!g_mouse.enabled || SDL_GetKeyboardFocus() == nullptr) {
|
||||
CenterMouseStick();
|
||||
if (SDL_WaitEvent(event) == 0) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const int timeout_ms = PollMouse(SDL_GetTicks64());
|
||||
SDL_ClearError();
|
||||
if (SDL_WaitEventTimeout(event, timeout_ms) != 0) {
|
||||
return true;
|
||||
}
|
||||
if (SDL_GetError()[0] != '\0') {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -3,11 +3,15 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
void HostInputInit();
|
||||
void HostInputKey(int key_code, bool down);
|
||||
void HostInputMouseButton(uint8_t mouse_button, bool down);
|
||||
void HostInputInit();
|
||||
void HostInputKey(int key_code, bool down);
|
||||
void HostInputMouseButton(uint8_t mouse_button, bool down);
|
||||
void HostInputToggleMouseToJoystick();
|
||||
[[nodiscard]] bool HostInputWaitEvent(SDL_Event* event);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -507,6 +507,13 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
auto& game = loop;
|
||||
auto* event = &game.event;
|
||||
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
|
||||
if ((event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) &&
|
||||
event->key.keysym.sym == SDLK_F7) {
|
||||
if (event->type == SDL_KEYDOWN && event->key.repeat == 0) {
|
||||
HostInputToggleMouseToJoystick();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ProcessImeInput(*event)) {
|
||||
return;
|
||||
}
|
||||
@@ -783,8 +790,8 @@ void WindowContext::Run() {
|
||||
timer.Resume();
|
||||
}
|
||||
|
||||
if (SDL_WaitEvent(&loop.event) == 0) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
if (!HostInputWaitEvent(&loop.event)) {
|
||||
continue;
|
||||
}
|
||||
ProcessEvent(timer.GetTimeS());
|
||||
}
|
||||
|
||||
@@ -266,7 +266,8 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
requirements.subgroup_local_invocation_id = true;
|
||||
break;
|
||||
}
|
||||
case IR::ValueOpcode::SwizzleU32: {
|
||||
case IR::ValueOpcode::SwizzleU32:
|
||||
case IR::ValueOpcode::BpermuteU32: {
|
||||
MarkBallot();
|
||||
requirements.subgroup_shuffle = true;
|
||||
requirements.subgroup_local_invocation_id = true;
|
||||
|
||||
@@ -30,6 +30,19 @@ uint32_t AndCondition(EmitterState& state, uint32_t lhs, uint32_t rhs) {
|
||||
return Binary(state, OpLogicalAnd, TypeBool(state), lhs, rhs);
|
||||
}
|
||||
|
||||
uint32_t EmitDsMaskedLaneRead(EmitterState& state, uint32_t source, uint32_t target,
|
||||
uint32_t exec) {
|
||||
const auto shuffled = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), shuffled,
|
||||
ConstantU32(state, ScopeSubgroup), source, target});
|
||||
const auto source_exec = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, TypeBool(state), source_exec,
|
||||
ConstantU32(state, ScopeSubgroup), exec, target});
|
||||
const auto source_active =
|
||||
AndCondition(state, source_exec, EmitSubgroupLaneActiveBool(state, target));
|
||||
return Select(state, TypeU32(state), source_active, shuffled, ConstantU32(state, 0));
|
||||
}
|
||||
|
||||
uint32_t BufferByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
uint32_t index, uint32_t offset, uint32_t soffset) {
|
||||
auto& state = ctx.state;
|
||||
@@ -1181,24 +1194,27 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
ctx.Define(inst, AppendConsume(ctx, inst, op == IR::ValueOpcode::DataAppend));
|
||||
return true;
|
||||
}
|
||||
if (op == IR::ValueOpcode::SwizzleU32) {
|
||||
ctx.state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, ctx.Arg(inst, 0)});
|
||||
const auto source = ctx.state.builder.AllocateId();
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpLoad, TypeU32(ctx.state), source, ctx.scratch_u32_variable});
|
||||
const auto target =
|
||||
EmitDsSwizzleTargetLane(state, EmitSubgroupLocalInvocationId(state),
|
||||
inst.Arg(1).IsImmediate() ? inst.Arg(1).U32() : 0);
|
||||
const auto shuffled = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, TypeU32(state), shuffled,
|
||||
ConstantU32(state, ScopeSubgroup), source, target});
|
||||
const auto source_exec = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformShuffle, TypeBool(state), source_exec,
|
||||
ConstantU32(state, ScopeSubgroup), ctx.Arg(inst, 2), target});
|
||||
const auto source_active =
|
||||
AndCondition(state, source_exec, EmitSubgroupLaneActiveBool(state, target));
|
||||
ctx.Define(inst,
|
||||
Select(state, TypeU32(state), source_active, shuffled, ConstantU32(state, 0)));
|
||||
if (op == IR::ValueOpcode::SwizzleU32 || op == IR::ValueOpcode::BpermuteU32) {
|
||||
uint32_t source = ctx.Arg(inst, 0);
|
||||
uint32_t target = 0;
|
||||
if (op == IR::ValueOpcode::SwizzleU32) {
|
||||
state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, source});
|
||||
source = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpLoad, TypeU32(state), source, ctx.scratch_u32_variable});
|
||||
target = EmitDsSwizzleTargetLane(state, EmitSubgroupLocalInvocationId(state),
|
||||
inst.Arg(1).IsImmediate() ? inst.Arg(1).U32() : 0);
|
||||
} else {
|
||||
const auto index = Binary(
|
||||
state, OpBitwiseAnd, TypeU32(state),
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), ctx.Arg(inst, 1),
|
||||
ConstantU32(state, 2)),
|
||||
ConstantU32(state, 31));
|
||||
const auto base =
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), EmitSubgroupLocalInvocationId(state),
|
||||
ConstantU32(state, ~31u));
|
||||
target = Binary(state, OpBitwiseOr, TypeU32(state), base, index);
|
||||
}
|
||||
ctx.Define(inst, EmitDsMaskedLaneRead(state, source, target, ctx.Arg(inst, 2)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -107,6 +107,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||
{0xa6u, Opcode::DS_READ_U16_D16, 1, 16},
|
||||
{0xa7u, Opcode::DS_READ_U16_D16_HI, 1, 16},
|
||||
{0xb0u, Opcode::DS_WRITE_ADDTID_B32, 1, 32}, {0xb1u, Opcode::DS_READ_ADDTID_B32, 1, 32},
|
||||
{0xb3u, Opcode::DS_BPERMUTE_B32, 1, 32},
|
||||
{0xdeu, Opcode::DS_WRITE_B96, 3, 32}, {0xdfu, Opcode::DS_WRITE_B128, 4, 32},
|
||||
{0xfeu, Opcode::DS_READ_B96, 3, 32}, {0xffu, Opcode::DS_READ_B128, 4, 32},
|
||||
};
|
||||
@@ -195,6 +196,7 @@ uint32_t DsSourceCount(Opcode opcode) {
|
||||
case Opcode::DS_WRITE2ST64_B64:
|
||||
case Opcode::DS_MIN_F32:
|
||||
case Opcode::DS_MAX_F32: return 3u;
|
||||
case Opcode::DS_BPERMUTE_B32: return 2u;
|
||||
case Opcode::DS_READ_ADDTID_B32:
|
||||
case Opcode::DS_CONSUME:
|
||||
case Opcode::DS_APPEND: return 0u;
|
||||
@@ -395,9 +397,10 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle FFT mode is not implemented");
|
||||
}
|
||||
if (inst.gds &&
|
||||
(inst.opcode == Opcode::DS_SWIZZLE_B32 || inst.opcode == Opcode::DS_WRITE_ADDTID_B32 ||
|
||||
(inst.opcode == Opcode::DS_SWIZZLE_B32 || inst.opcode == Opcode::DS_BPERMUTE_B32 ||
|
||||
inst.opcode == Opcode::DS_WRITE_ADDTID_B32 ||
|
||||
inst.opcode == Opcode::DS_READ_ADDTID_B32)) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle/addtid is available only for LDS");
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS lane operation is available only for LDS");
|
||||
}
|
||||
if (inst.opcode == Opcode::DS_WRITE_ADDTID_B32 && data1 != 0u) {
|
||||
SetUnsupported(inst, Family::DS, opcode,
|
||||
|
||||
@@ -661,6 +661,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DS_MIN_F32:
|
||||
case Opcode::DS_MAX_F32:
|
||||
case Opcode::DS_SWIZZLE_B32:
|
||||
case Opcode::DS_BPERMUTE_B32:
|
||||
case Opcode::DS_READ_I8:
|
||||
case Opcode::DS_READ_U8:
|
||||
case Opcode::DS_READ_I16:
|
||||
|
||||
@@ -179,6 +179,7 @@ enum class Opcode {
|
||||
V_CEIL_F16,
|
||||
V_TRUNC_F16,
|
||||
V_RNDNE_F16,
|
||||
V_COS_F16,
|
||||
V_SIN_F32,
|
||||
V_COS_F32,
|
||||
V_NOT_B32,
|
||||
@@ -492,6 +493,7 @@ enum class Opcode {
|
||||
DS_MIN_F32,
|
||||
DS_MAX_F32,
|
||||
DS_SWIZZLE_B32,
|
||||
DS_BPERMUTE_B32,
|
||||
DS_CONSUME,
|
||||
DS_APPEND,
|
||||
DS_READ_I8,
|
||||
|
||||
@@ -137,6 +137,7 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
|
||||
{0x5cu, Opcode::V_CEIL_F16},
|
||||
{0x5du, Opcode::V_TRUNC_F16},
|
||||
{0x5eu, Opcode::V_RNDNE_F16},
|
||||
{0x61u, Opcode::V_COS_F16},
|
||||
};
|
||||
|
||||
constexpr auto VOP1_OPS = Detail::MakeOpcodeTable<0x100>(VOP1_OPCODE_LIST);
|
||||
@@ -188,6 +189,7 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
|
||||
{0x5cu, Opcode::V_CEIL_F16},
|
||||
{0x5du, Opcode::V_TRUNC_F16},
|
||||
{0x5eu, Opcode::V_RNDNE_F16},
|
||||
{0x61u, Opcode::V_COS_F16},
|
||||
};
|
||||
|
||||
constexpr auto VOP3_ENCODED_VOP1_OPS = Detail::MakeOpcodeTable<0x80>(VOP3_ENCODED_VOP1_OPCODE_LIST);
|
||||
@@ -482,6 +484,7 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_CEIL_F16:
|
||||
case Opcode::V_TRUNC_F16:
|
||||
case Opcode::V_RNDNE_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_SIN_F32:
|
||||
case Opcode::V_COS_F32: return true;
|
||||
default: return false;
|
||||
@@ -548,6 +551,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_RNDNE_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_COS_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_CVT_U32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_I32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_RPI_I32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(),
|
||||
@@ -832,6 +837,7 @@ bool IsVop1FloatResultOpcode(Opcode opcode) {
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_SIN_F32:
|
||||
case Opcode::V_COS_F32: return true;
|
||||
default: return false;
|
||||
|
||||
@@ -63,6 +63,30 @@ bool Translator::Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_COS_F16(const Decoder::Instruction& inst) {
|
||||
const auto argument = ReadF16AsF32(inst.src0);
|
||||
auto result = IR::F32(ir.Emit(IR::ValueOpcode::FPCos, {argument}));
|
||||
|
||||
// The architectural quarter-cycle result is exactly zero. Evaluating cos(pi/2) with a
|
||||
// rounded F32 PI can otherwise produce a half-precision subnormal instead.
|
||||
const auto fraction = IR::F32(ir.Emit(IR::ValueOpcode::FPFract32, {argument}));
|
||||
const auto quarter = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::FPOrdEqual32, {fraction, IR::Value::F32(0.25f)}));
|
||||
const auto three_quarters = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::FPOrdEqual32, {fraction, IR::Value::F32(0.75f)}));
|
||||
result = IR::F32(ir.Emit(IR::ValueOpcode::SelectF32,
|
||||
{ir.LogicalOr(quarter, three_quarters), IR::Value::F32(0.0f), result}));
|
||||
|
||||
const auto magnitude =
|
||||
ir.BitwiseAnd(ir.BitCastU32(argument), IR::U32(IR::Value(0x7fffffffu)));
|
||||
const auto infinite = ir.IEqual(magnitude, IR::U32(IR::Value(0x7f800000u)));
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool reverse) {
|
||||
const auto lhs = ReadF16AsF32(reverse ? inst.src1 : inst.src0);
|
||||
|
||||
@@ -878,6 +878,13 @@ bool Translator::DS_SWIZZLE_B32(const Decoder::Instruction& inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::DS_BPERMUTE_B32(const Decoder::Instruction& inst) {
|
||||
const auto address = ir.IAdd(ReadU32(inst.src0), IR::U32(IR::Value(inst.offset)));
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::BpermuteU32,
|
||||
{ReadU32(inst.src1), address, ir.GetExec()}));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error) {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
@@ -1036,6 +1043,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
|
||||
case Decoder::Opcode::DS_MAX_F32:
|
||||
return DS_MINMAX_F32(inst, IR::ValueOpcode::SharedAtomicFMax32);
|
||||
case Decoder::Opcode::DS_SWIZZLE_B32: return DS_SWIZZLE_B32(inst);
|
||||
case Decoder::Opcode::DS_BPERMUTE_B32: return DS_BPERMUTE_B32(inst);
|
||||
case Decoder::Opcode::DS_CONSUME:
|
||||
return DS_APPEND_CONSUME(inst, IR::ValueOpcode::DataConsume);
|
||||
case Decoder::Opcode::DS_APPEND:
|
||||
|
||||
@@ -109,6 +109,7 @@ private:
|
||||
bool DS_APPEND_CONSUME(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
|
||||
bool DS_ADDTID(const Decoder::Instruction& inst, bool write);
|
||||
bool DS_SWIZZLE_B32(const Decoder::Instruction& inst);
|
||||
bool DS_BPERMUTE_B32(const Decoder::Instruction& inst);
|
||||
|
||||
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
|
||||
IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper,
|
||||
@@ -148,6 +149,7 @@ private:
|
||||
bool quiet_snan);
|
||||
bool Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool invalid_negative);
|
||||
bool V_COS_F16(const Decoder::Instruction& inst);
|
||||
bool Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool reverse);
|
||||
bool Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
|
||||
bool mix);
|
||||
|
||||
@@ -365,6 +365,7 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
|
||||
case O::V_CEIL_F16: return Float16Unary(inst, IR::ValueOpcode::FPCeil32, false);
|
||||
case O::V_TRUNC_F16: return Float16Unary(inst, IR::ValueOpcode::FPTrunc32, false);
|
||||
case O::V_RNDNE_F16: return Float16Unary(inst, IR::ValueOpcode::FPRoundEven32, false);
|
||||
case O::V_COS_F16: return V_COS_F16(inst);
|
||||
case O::V_MIN3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMinTri32, false, false);
|
||||
case O::V_MAX3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMaxTri32, false, false);
|
||||
case O::V_MED3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMedTri32, false, false);
|
||||
|
||||
@@ -188,6 +188,7 @@ VALUE_OPCODE(ReadFirstLane, U32, U32, U1)
|
||||
VALUE_OPCODE(ReadLane, U32, U32, U32)
|
||||
VALUE_OPCODE(WriteLane, U32, U32, U32, U32)
|
||||
VALUE_OPCODE(Permlane16U32, U32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(BpermuteU32, U32, U32, U32, U1)
|
||||
|
||||
// Typed shader resources and memory operations. Descriptor state is represented by SSA values;
|
||||
// dense bindings are assigned by resource tracking.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QStringList>
|
||||
|
||||
class QPushButton;
|
||||
class QDoubleSpinBox;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QWidget;
|
||||
@@ -22,10 +23,11 @@ private:
|
||||
void SetBinding(QTreeWidgetItem* item, const QString& binding);
|
||||
void UpdateButtons();
|
||||
|
||||
QTreeWidget* m_bindings = nullptr;
|
||||
QPushButton* m_change_button = nullptr;
|
||||
QPushButton* m_clear_button = nullptr;
|
||||
bool m_customized = false;
|
||||
QTreeWidget* m_bindings = nullptr;
|
||||
QPushButton* m_change_button = nullptr;
|
||||
QPushButton* m_clear_button = nullptr;
|
||||
QDoubleSpinBox* m_sensitivity = nullptr;
|
||||
bool m_custom_bindings = false;
|
||||
};
|
||||
|
||||
#endif /* LAUNCHER_INCLUDE_INPUT_MAPPING_DIALOG_H_ */
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHash>
|
||||
#include <QHeaderView>
|
||||
@@ -16,7 +17,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int BINDING_COLUMN = 1;
|
||||
constexpr int BINDING_COLUMN = 1;
|
||||
constexpr auto DEFAULT_MOUSE_SENSITIVITY = 1.0;
|
||||
constexpr char MOUSE_SENSITIVITY[] = "MouseSensitivity=";
|
||||
|
||||
struct PadControl {
|
||||
const char* id;
|
||||
@@ -128,7 +131,8 @@ public:
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
m_label = new QLabel(
|
||||
tr("Press a key or mouse button.\nSpace and F1 are reserved; Esc cancels."), this);
|
||||
tr("Press a key or mouse button.\nSpace, F1, F7, and F11 are reserved; Esc cancels."),
|
||||
this);
|
||||
m_label->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(m_label);
|
||||
}
|
||||
@@ -144,7 +148,8 @@ protected:
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
if (event->key() == Qt::Key_Space || event->key() == Qt::Key_F1) {
|
||||
if (event->key() == Qt::Key_Space || event->key() == Qt::Key_F1 ||
|
||||
event->key() == Qt::Key_F7 || event->key() == Qt::Key_F11) {
|
||||
m_label->setText(tr("That key is reserved by the emulator."));
|
||||
return;
|
||||
}
|
||||
@@ -177,6 +182,9 @@ private:
|
||||
QHash<QString, QString> ParseMapping(const QStringList& mapping) {
|
||||
QHash<QString, QString> result;
|
||||
for (const auto& entry: mapping) {
|
||||
if (entry.startsWith(QLatin1String(MOUSE_SENSITIVITY))) {
|
||||
continue;
|
||||
}
|
||||
const auto separator = entry.indexOf(QLatin1Char('='));
|
||||
if (separator > 0 && separator + 1 < entry.size()) {
|
||||
const auto binding = entry.mid(separator + 1);
|
||||
@@ -193,15 +201,42 @@ QHash<QString, QString> ParseMapping(const QStringList& mapping) {
|
||||
return result;
|
||||
}
|
||||
|
||||
double ParseMouseSensitivity(const QStringList& mapping) {
|
||||
for (const auto& entry: mapping) {
|
||||
if (entry.startsWith(QLatin1String(MOUSE_SENSITIVITY))) {
|
||||
return entry.mid(sizeof(MOUSE_SENSITIVITY) - 1).toDouble();
|
||||
}
|
||||
}
|
||||
return DEFAULT_MOUSE_SENSITIVITY;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* parent)
|
||||
: QDialog(parent), m_customized(!mapping.isEmpty()) {
|
||||
: QDialog(parent) {
|
||||
setWindowTitle(tr("Input Mapping"));
|
||||
resize(460, 650);
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
layout->addWidget(new QLabel(tr("Map keyboard or mouse input to DualSense controls."), this));
|
||||
layout->addWidget(
|
||||
new QLabel(tr("Map keyboard or mouse buttons to DualSense controls.\n"
|
||||
"Press F7 in-game to toggle mouse movement on the right stick."),
|
||||
this));
|
||||
|
||||
const auto parsed = ParseMapping(mapping);
|
||||
m_custom_bindings = !parsed.isEmpty();
|
||||
|
||||
auto* sensitivity_layout = new QHBoxLayout;
|
||||
sensitivity_layout->addWidget(new QLabel(tr("Mouse sensitivity"), this));
|
||||
m_sensitivity = new QDoubleSpinBox(this);
|
||||
m_sensitivity->setRange(0.1, 5.0);
|
||||
m_sensitivity->setSingleStep(0.1);
|
||||
m_sensitivity->setDecimals(1);
|
||||
m_sensitivity->setSuffix(QStringLiteral("x"));
|
||||
m_sensitivity->setValue(ParseMouseSensitivity(mapping));
|
||||
sensitivity_layout->addWidget(m_sensitivity);
|
||||
sensitivity_layout->addStretch();
|
||||
layout->addLayout(sensitivity_layout);
|
||||
|
||||
m_bindings = new QTreeWidget(this);
|
||||
m_bindings->setColumnCount(2);
|
||||
@@ -212,13 +247,12 @@ InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* pare
|
||||
m_bindings->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
layout->addWidget(m_bindings);
|
||||
|
||||
const auto parsed = ParseMapping(mapping);
|
||||
for (const auto& control: PAD_CONTROLS) {
|
||||
auto* item = new QTreeWidgetItem(m_bindings);
|
||||
item->setText(0, tr(control.label));
|
||||
item->setData(0, Qt::UserRole, QString::fromLatin1(control.id));
|
||||
SetBinding(item, m_customized ? parsed.value(QString::fromLatin1(control.id))
|
||||
: QString::fromLatin1(control.default_binding));
|
||||
SetBinding(item, m_custom_bindings ? parsed.value(QString::fromLatin1(control.id))
|
||||
: QString::fromLatin1(control.default_binding));
|
||||
}
|
||||
m_bindings->setCurrentItem(m_bindings->topLevelItem(0));
|
||||
|
||||
@@ -248,16 +282,19 @@ InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* pare
|
||||
|
||||
QStringList InputMappingDialog::Mapping() const {
|
||||
QStringList result;
|
||||
if (!m_customized) {
|
||||
return result;
|
||||
}
|
||||
for (int index = 0; index < m_bindings->topLevelItemCount(); index++) {
|
||||
const auto* item = m_bindings->topLevelItem(index);
|
||||
const auto binding = item->data(BINDING_COLUMN, Qt::UserRole).toString();
|
||||
if (!binding.isEmpty()) {
|
||||
result.append(item->data(0, Qt::UserRole).toString() + QLatin1Char('=') + binding);
|
||||
if (m_custom_bindings) {
|
||||
for (int index = 0; index < m_bindings->topLevelItemCount(); index++) {
|
||||
const auto* item = m_bindings->topLevelItem(index);
|
||||
const auto binding = item->data(BINDING_COLUMN, Qt::UserRole).toString();
|
||||
if (!binding.isEmpty()) {
|
||||
result.append(item->data(0, Qt::UserRole).toString() + QLatin1Char('=') + binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_sensitivity->value() != DEFAULT_MOUSE_SENSITIVITY) {
|
||||
result.append(QLatin1String(MOUSE_SENSITIVITY) +
|
||||
QString::number(m_sensitivity->value(), 'f', 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -281,12 +318,12 @@ void InputMappingDialog::ChangeBinding() {
|
||||
}
|
||||
}
|
||||
SetBinding(item, dialog.Binding());
|
||||
m_customized = true;
|
||||
m_custom_bindings = true;
|
||||
}
|
||||
|
||||
void InputMappingDialog::ClearBinding() {
|
||||
SetBinding(m_bindings->currentItem(), {});
|
||||
m_customized = true;
|
||||
m_custom_bindings = true;
|
||||
}
|
||||
|
||||
void InputMappingDialog::RestoreDefaults() {
|
||||
@@ -294,7 +331,8 @@ void InputMappingDialog::RestoreDefaults() {
|
||||
SetBinding(m_bindings->topLevelItem(index),
|
||||
QString::fromLatin1(PAD_CONTROLS[index].default_binding));
|
||||
}
|
||||
m_customized = false;
|
||||
m_sensitivity->setValue(DEFAULT_MOUSE_SENSITIVITY);
|
||||
m_custom_bindings = false;
|
||||
}
|
||||
|
||||
void InputMappingDialog::SetBinding(QTreeWidgetItem* item, const QString& binding) {
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
void Disconnect(int id);
|
||||
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 ResetInputState();
|
||||
void GetConnectionInfo(bool* flag, int* count);
|
||||
void SetVibration(uint8_t large_motor, uint8_t small_motor);
|
||||
@@ -265,6 +266,18 @@ void GameController::Axis(int id, Controller::Axis axis, int value) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::ResetInputState() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
ControllerState state {};
|
||||
@@ -371,6 +384,12 @@ void ControllerAxis(int id, Axis axis, int value) {
|
||||
g_controller->Axis(id, axis, value);
|
||||
}
|
||||
|
||||
void ControllerRightStick(int id, int x, int y) {
|
||||
EXIT_IF(g_controller == nullptr);
|
||||
|
||||
g_controller->RightStick(id, x, y);
|
||||
}
|
||||
|
||||
void ControllerResetInputState() {
|
||||
EXIT_IF(g_controller == nullptr);
|
||||
g_controller->ResetInputState();
|
||||
|
||||
@@ -58,6 +58,7 @@ void ControllerConnect(int id);
|
||||
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 ControllerResetInputState();
|
||||
|
||||
int KYTY_SYSV_ABI PadInit();
|
||||
|
||||
@@ -11896,6 +11896,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_MAD_MIXLO_F16:
|
||||
case Opcode::V_MAD_MIXHI_F16:
|
||||
case Opcode::DS_MIN_F32:
|
||||
@@ -12029,6 +12030,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::DS_XOR_RTN_B32:
|
||||
case Opcode::DS_WRXCHG_RTN_B32:
|
||||
case Opcode::DS_SWIZZLE_B32:
|
||||
case Opcode::DS_BPERMUTE_B32:
|
||||
case Opcode::DS_READ_I8:
|
||||
case Opcode::DS_READ_U8:
|
||||
case Opcode::DS_READ_I16:
|
||||
@@ -14488,6 +14490,45 @@ TestCase VectorSpecialF16Ops() {
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase VectorCosF16CapturedSdwaAndEdges() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 4, 0x34001234u); // high=+0.25h, low sentinel
|
||||
code.push_back(0x7e08c2f9u);
|
||||
code.push_back(0x00051504u); // captured v_cos_f16 v4.word1, v4.word1
|
||||
AppendVMovLiteral(&code, 5, 0x00008000u); // -0.0h
|
||||
code.push_back(EncodeVop1(0x61, 6, Vgpr(5)));
|
||||
AppendVMovLiteral(&code, 7, 0x00007bffu); // max finite
|
||||
code.push_back(EncodeVop1(0x61, 8, Vgpr(7)));
|
||||
AppendVMovLiteral(&code, 9, 0x00007c00u); // +inf
|
||||
code.push_back(EncodeVop1(0x61, 10, Vgpr(9)));
|
||||
AppendVMovLiteral(&code, 11, 0x00000001u); // minimum positive denormal
|
||||
code.push_back(EncodeVop1(0x61, 12, Vgpr(11)));
|
||||
AppendVMovLiteral(&code, 13, 0x00003800u); // +0.5h
|
||||
code.push_back(EncodeVop1(0x61, 14, Vgpr(13)));
|
||||
for (u32 i = 0; i < 6; i++) {
|
||||
AppendStoreVgpr(&code, 4 + i * 2, i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test{"VectorCosF16CapturedSdwaAndEdges",
|
||||
code,
|
||||
{},
|
||||
{0x00001234u, 0x00003c00u, 0x00003c00u, 0x0000fe00u,
|
||||
0x00003c00u, 0x0000bc00u},
|
||||
{O::V_MOV_B32, O::V_COS_F16, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"V_COS_F16", 6},
|
||||
{"V_COS_F16 v4.sdwa(sel=5,sext=0), "
|
||||
"v4.sdwa(sel=5,sext=0)",
|
||||
1}};
|
||||
test.ir_counts = {{" = FPCos ", 6}};
|
||||
test.required_spirv = {" Fract ", " Cos ", " PackHalf2x16 "};
|
||||
test.forbidden_spirv = {"OpCapability Float16"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorWritelaneIgnoresExecMask() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -18558,6 +18599,89 @@ TestCase DsSwizzleInvalidSourceLaneZero() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsBpermuteCapturedExecOffsetAndWrap() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
code.push_back(EncodeVop2(0x1a, 30, InlineU32(2), 0));
|
||||
AppendBufferLoadDword(&code, 17, 30);
|
||||
AppendVMovLiteral(&code, 1, 0xdeadbeefu);
|
||||
AppendVMovU32(&code, 3, 100);
|
||||
code.push_back(EncodeVop2(0x25, 3, Vgpr(0), 3));
|
||||
code.push_back(EncodeSop1(0x04, 4, 126));
|
||||
code.push_back(EncodeVop2(0x1b, 18, InlineU32(1), 0));
|
||||
code.push_back(EncodeVopc(0xc2, InlineU32(1), 18));
|
||||
code.push_back(EncodeSop1(0x04, 126, 106));
|
||||
code.push_back(0xdacc0000u);
|
||||
code.push_back(0x01000311u); // ds_bpermute_b32 v1, v17, v3
|
||||
code.push_back(EncodeSop1(0x04, 126, 4));
|
||||
AppendStoreVgprAtLaneDwordOffset(&code, 1, 0, 4);
|
||||
|
||||
AppendVMovU32(&code, 17, 124);
|
||||
code.push_back(EncodeDs0(0xb3, 4));
|
||||
code.push_back(EncodeDs1(2, 3, 17));
|
||||
AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, 8);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "DsBpermuteCapturedExecOffsetAndWrap";
|
||||
test.code = code;
|
||||
test.initial = {0, 0, 12, 4, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
test.expected = {0, 0, 12, 4,
|
||||
0xdeadbeefu, 0, 0xdeadbeefu, 101,
|
||||
100, 100, 100, 100};
|
||||
test.opcodes = {O::V_LSHLREV_B32, O::BUFFER_LOAD_DWORD,
|
||||
O::V_MOV_B32, O::V_ADD_NC_U32,
|
||||
O::V_AND_B32, O::V_CMP_EQ_U32,
|
||||
O::S_MOV_B64,
|
||||
O::DS_BPERMUTE_B32, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM};
|
||||
test.required_spirv = {"OpGroupNonUniformShuffle",
|
||||
"OpGroupNonUniformBallot"};
|
||||
test.forbidden_spirv = {" Workgroup", "OpControlBarrier",
|
||||
"OpMemoryBarrier"};
|
||||
test.decoded_counts = {{"DS_BPERMUTE_B32", 2}};
|
||||
test.ir_counts = {{"BpermuteU32", 2}};
|
||||
test.compute_info.threads_num[0] = 4;
|
||||
test.compute_info.threads_num[1] = 1;
|
||||
test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.wave_size = 32;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsBpermuteWave64UsesIndependentHalves() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 3, 100);
|
||||
code.push_back(EncodeVop2(0x25, 3, Vgpr(0), 3));
|
||||
AppendVMovU32(&code, 17, 128);
|
||||
code.push_back(0xdacc0000u);
|
||||
code.push_back(0x01000311u); // ds_bpermute_b32 v1, v17, v3
|
||||
AppendStoreVgprAtLaneDwordOffset(&code, 1, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "DsBpermuteWave64UsesIndependentHalves";
|
||||
test.code = code;
|
||||
test.initial = std::vector<u32>(64, 0);
|
||||
test.expected = std::vector<u32>(32, 100);
|
||||
test.expected.insert(test.expected.end(), 32, 132);
|
||||
test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32,
|
||||
O::DS_BPERMUTE_B32, O::V_LSHLREV_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpGroupNonUniformShuffle"};
|
||||
test.compute_info.threads_num[0] = 64;
|
||||
test.compute_info.threads_num[1] = 1;
|
||||
test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.wave_size = 64;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferAtomicVariants() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -20604,6 +20728,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorCvtU16F16Sdwa);
|
||||
AddCase(VectorMinMaxMed3F16Ops);
|
||||
AddCase(VectorSpecialF16Ops);
|
||||
AddCase(VectorCosF16CapturedSdwaAndEdges);
|
||||
AddCase(VectorWritelaneIgnoresExecMask);
|
||||
AddCase(VectorReadlaneFromInactiveWrittenLane);
|
||||
AddCase(VectorLaneWave32RuntimeSelectorWraps);
|
||||
@@ -20746,6 +20871,8 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(DsMiscVariants);
|
||||
AddCase(DsFloatMinMaxUsesSeparateCompareOperand);
|
||||
AddCase(DsSwizzleInvalidSourceLaneZero);
|
||||
AddCase(DsBpermuteCapturedExecOffsetAndWrap);
|
||||
AddCase(DsBpermuteWave64UsesIndependentHalves);
|
||||
AddCase(BufferAtomicVariants);
|
||||
AddCase(BufferAtomicGlc0DoesNotReturnOldValue);
|
||||
AddCase(BufferAtomicFMinExactRawGlcModes);
|
||||
|
||||
@@ -4031,6 +4031,22 @@ void TestNewShaderDecoderArchitecture() {
|
||||
ffbh_i32.src0.reg == 14u,
|
||||
"decoder rejected the captured VOP1 V_FFBH_I32 instruction");
|
||||
|
||||
const uint32_t cos_f16_code[] = {0x7e08c2f9u, 0x00051504u};
|
||||
Instruction cos_f16;
|
||||
Check(DecodeInstruction(cos_f16_code, 0u, cos_f16, &error), error.c_str());
|
||||
Check(cos_f16.family == Family::VOP1 &&
|
||||
cos_f16.opcode == Opcode::V_COS_F16 &&
|
||||
cos_f16.word_count == 2u && cos_f16.src_count == 1u &&
|
||||
cos_f16.dst.kind == OperandKind::Vgpr && cos_f16.dst.reg == 4u &&
|
||||
cos_f16.dst.sdwa_sel == 5u &&
|
||||
cos_f16.dst.sdwa_dst_unused == 2u && !cos_f16.dst.clamp &&
|
||||
cos_f16.dst.omod == 0u &&
|
||||
cos_f16.src0.kind == OperandKind::Vgpr &&
|
||||
cos_f16.src0.reg == 4u && cos_f16.src0.sdwa_sel == 5u &&
|
||||
!cos_f16.src0.sdwa_sext && !cos_f16.src0.negate &&
|
||||
!cos_f16.src0.absolute,
|
||||
"decoder rejected the captured VOP1 SDWA V_COS_F16 instruction");
|
||||
|
||||
const uint32_t literal_code[] = {EncodeVop1(0x01, 2, 255u), 0x12345678u};
|
||||
Instruction literal;
|
||||
Check(DecodeInstruction(literal_code, 0u, literal, &error), error.c_str());
|
||||
@@ -4123,6 +4139,18 @@ void TestNewShaderDecoderArchitecture() {
|
||||
Check(boot.opcode == Opcode::DS_SWIZZLE_B32 && boot.offset == 0xc480u,
|
||||
"DS decoder rejected a captured boot-shader instruction");
|
||||
|
||||
const uint32_t bpermute_ds[] = {0xdacc0000u, 0x01000311u};
|
||||
Instruction bpermute;
|
||||
Check(DecodeInstruction(bpermute_ds, 0u, bpermute, &error), error.c_str());
|
||||
Check(bpermute.family == Family::DS &&
|
||||
bpermute.opcode == Opcode::DS_BPERMUTE_B32 &&
|
||||
bpermute.word_count == 2u && bpermute.src_count == 2u &&
|
||||
bpermute.data_dwords == 1u && bpermute.data_bits == 32u &&
|
||||
bpermute.offset == 0u && !bpermute.gds &&
|
||||
bpermute.dst.reg == 1u && bpermute.src0.reg == 17u &&
|
||||
bpermute.src1.reg == 3u,
|
||||
"DS decoder rejected or misdecoded captured DS_BPERMUTE_B32");
|
||||
|
||||
const uint32_t d16_hi_ds[] = {0xda9c0000u, 0x05000006u};
|
||||
Instruction d16_hi;
|
||||
Check(DecodeInstruction(d16_hi_ds, 0u, d16_hi, &error), error.c_str());
|
||||
@@ -12083,6 +12111,12 @@ void TestNewShaderRecompilerUnsupportedMemoryDecode() {
|
||||
CheckNewDecoderUnsupported(mtbuf_unknown,
|
||||
static_cast<uint32_t>(std::size(mtbuf_unknown)),
|
||||
"MTBUF", "opcode=0x08");
|
||||
|
||||
const uint32_t bpermute_gds[] = {
|
||||
EncodeDs0(0xb3) | (1u << 17u), EncodeDs1(1, 3, 17), 0xbf810000u};
|
||||
CheckNewDecoderUnsupported(bpermute_gds,
|
||||
static_cast<uint32_t>(std::size(bpermute_gds)),
|
||||
"DS", "available only for LDS");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerFlatUserPointerUsesDma() {
|
||||
|
||||
Reference in new issue
Block a user