mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f4b8a335d | |||
| b38b7454a6 | |||
| b65dc12208 | |||
| 58978d154a | |||
| 1f3adf5109 | |||
| 107d5f750a | |||
| f40755173f | |||
| 4e14eed770 |
+1
-1
@@ -244,7 +244,7 @@ file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
|
||||
"${KYTY_SOURCE_DIR}/loader/*.h"
|
||||
)
|
||||
|
||||
find_program(KYTY_GLSLANG_VALIDATOR glslangValidator REQUIRED)
|
||||
find_program(KYTY_GLSLANG_VALIDATOR NAMES glslangValidator glslang REQUIRED)
|
||||
set(gpu_tiler_shader_dir "${KYTY_SOURCE_DIR}/graphics/host_gpu/shaders")
|
||||
set(gpu_tiler_generated_dir "${PROJECT_BINARY_DIR}/gpu_tiler_shaders")
|
||||
set(gpu_tiler_shader_names
|
||||
|
||||
@@ -141,7 +141,8 @@ the Vulkan/SPIR-V validation rules.
|
||||
- Visual Studio 2022 or Build Tools 2022 with the **Desktop development with C++** workload and
|
||||
**C++ Clang tools for Windows** component
|
||||
- Qt 6 for MSVC 2022 64-bit, including Concurrent, Network, and Widgets
|
||||
- [glslang](https://github.com/KhronosGroup/glslang/releases) (`glslangValidator`) on `PATH`
|
||||
- [glslang](https://github.com/KhronosGroup/glslang/releases) (`glslang` or `glslangValidator`) on `PATH`
|
||||
- Python 3 on `PATH`; the bundled SPIRV-Tools runs it at configure time
|
||||
|
||||
The Microsoft C++ compiler (`cl.exe`) is not supported; use `clang-cl`.
|
||||
|
||||
@@ -176,7 +177,7 @@ build has no working sound and no gamepad hotplug:
|
||||
|
||||
```bash
|
||||
sudo apt-get install --no-install-recommends \
|
||||
clang lld ninja-build cmake git glslang-tools pkg-config \
|
||||
clang lld ninja-build cmake git glslang-tools python3 pkg-config \
|
||||
libgl1-mesa-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev \
|
||||
libxi-dev libxrandr-dev libxss-dev libxtst-dev libxkbcommon-dev \
|
||||
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev libwayland-dev wayland-protocols
|
||||
@@ -251,7 +252,7 @@ Requirements:
|
||||
|
||||
- An Apple Silicon Mac with Rosetta 2 installed (`softwareupdate --install-rosetta`)
|
||||
- Xcode (or the Command Line Tools)
|
||||
- Homebrew packages: `brew install cmake ninja glslang`
|
||||
- Homebrew packages: `brew install cmake ninja glslang python`
|
||||
- Qt 6 (Concurrent, Network, Widgets) with x86-64 support. The official Qt installation is
|
||||
universal and works; Homebrew's Qt is arm64-only and will not link
|
||||
|
||||
|
||||
@@ -617,6 +617,13 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
|
||||
ShaderParams pixel_params;
|
||||
if (pixel_active) {
|
||||
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
|
||||
if (!mesh_active && !tess_active && Prospero::IsRectList(user_config.GetPrimType())) {
|
||||
pixel_info.parameter_mode = ShaderPixelParameterMode::Rectangle;
|
||||
} else if (context.GetModeControl().provoking_vtx_last) {
|
||||
pixel_info.parameter_mode = ShaderPixelParameterMode::LastVertex;
|
||||
} else {
|
||||
pixel_info.parameter_mode = ShaderPixelParameterMode::FirstVertex;
|
||||
}
|
||||
const auto& blend = context.GetBlendControl(0);
|
||||
pixel_info.dual_source_blending =
|
||||
blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
|
||||
|
||||
@@ -177,6 +177,13 @@ uint32_t EmitAttribute(EmitterState& state, uint32_t attr, uint32_t chan) {
|
||||
return value;
|
||||
};
|
||||
if (input->per_vertex) {
|
||||
if (PixelParameterIsFlat(state, attr)) {
|
||||
const auto value = load_per_vertex(
|
||||
state.input_info.pixel->parameter_mode == ShaderPixelParameterMode::LastVertex ? 2u : 0u);
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, value);
|
||||
return bits;
|
||||
}
|
||||
const auto barycentric_kind = state.input_info.pixel->ps_no_perspective
|
||||
? IR::StageInputKind::BaryCoordNoPerspective
|
||||
: IR::StageInputKind::BaryCoordSmooth;
|
||||
@@ -231,8 +238,12 @@ uint32_t EmitInterpolationParameter(ValueEmitContext& ctx, uint32_t attr, uint32
|
||||
return value;
|
||||
};
|
||||
|
||||
const auto selected_vertex = (mode + 1u) % 3u;
|
||||
uint32_t value = load_vertex(selected_vertex);
|
||||
uint32_t selected_vertex = (mode + 1u) % 3u;
|
||||
if (mode == 2u && PixelParameterIsFlat(state, attr)) {
|
||||
selected_vertex =
|
||||
state.input_info.pixel->parameter_mode == ShaderPixelParameterMode::LastVertex ? 2u : 0u;
|
||||
}
|
||||
uint32_t value = load_vertex(selected_vertex);
|
||||
if (!PixelParameterIsCustom(state, attr) && mode < 2u) {
|
||||
const auto delta = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpFSub, TypeF32(state), delta, value, load_vertex(0));
|
||||
|
||||
@@ -997,7 +997,8 @@ constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = {
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
// Bitwise: WriteRawU32 inserts a byte or word destination like any other partial write.
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
};
|
||||
static_assert(sizeof(VOP2_SDWA_RULES) / sizeof(VOP2_SDWA_RULES[0]) ==
|
||||
static_cast<size_t>(Vop2SdwaProfile::Count));
|
||||
|
||||
@@ -227,15 +227,16 @@ void CollectPixelInputs(const Program& program, const ShaderPixelInputInfo* pixe
|
||||
}
|
||||
}
|
||||
}
|
||||
// Aliases of a vertex output share one SPIR-V interface variable. If any
|
||||
// alias reads raw vertices, interpolate the other aliases from those too.
|
||||
// Mixed interpolation modes share raw vertices at their guest export slot.
|
||||
// Rectangle expansion alone supplies separate flat and smooth outputs.
|
||||
for (uint32_t input = 0; input < pixel->input_num; input++) {
|
||||
for (uint32_t alias = 0; alias < pixel->input_num; alias++) {
|
||||
const bool same_mode = ShaderPixelParameterIsFlat(*pixel, input) ==
|
||||
ShaderPixelParameterIsFlat(*pixel, alias);
|
||||
if (ShaderPixelParameterMappedLocation(*pixel, input) ==
|
||||
ShaderPixelParameterMappedLocation(*pixel, alias) &&
|
||||
ShaderPixelParameterIsFlat(*pixel, input) ==
|
||||
ShaderPixelParameterIsFlat(*pixel, alias)) {
|
||||
per_vertex[input] = per_vertex[input] || per_vertex[alias];
|
||||
(pixel->parameter_mode != ShaderPixelParameterMode::Rectangle || same_mode)) {
|
||||
per_vertex[input] = per_vertex[input] || per_vertex[alias] || !same_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +245,8 @@ void CollectPixelInputs(const Program& program, const ShaderPixelInputInfo* pixe
|
||||
per_vertex[input]);
|
||||
}
|
||||
for (uint32_t input = 0; input < pixel->input_num; input++) {
|
||||
if (interpolated[input] && per_vertex[input]) {
|
||||
if (interpolated[input] && per_vertex[input] &&
|
||||
!ShaderPixelParameterIsFlat(*pixel, input)) {
|
||||
const auto kind = pixel->ps_no_perspective ? StageInputKind::BaryCoordNoPerspective
|
||||
: StageInputKind::BaryCoordSmooth;
|
||||
AddInput(info, kind, 0, 3,
|
||||
|
||||
@@ -663,6 +663,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.ps_front_face));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_ancillary));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_no_perspective));
|
||||
key.push_back(static_cast<uint32_t>(info.parameter_mode));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_pixel_kill_enable));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_depth_export_enable));
|
||||
key.push_back(static_cast<uint32_t>(info.ps_sample_mask_export_enable));
|
||||
|
||||
@@ -159,6 +159,7 @@ struct ShaderComputeInputInfo: ShaderWorkgroupInputInfo {
|
||||
};
|
||||
|
||||
enum class ShaderAlphaBlendSource : uint8_t { None, SourceAlpha, SourceAlphaOne, SourceAlphaZero };
|
||||
enum class ShaderPixelParameterMode : uint8_t { FirstVertex, LastVertex, Rectangle };
|
||||
|
||||
struct ShaderPixelInputInfo {
|
||||
uint32_t interpolator_settings[32] = {0};
|
||||
@@ -180,6 +181,7 @@ struct ShaderPixelInputInfo {
|
||||
bool ps_front_face = false;
|
||||
bool ps_ancillary = false;
|
||||
bool ps_no_perspective = false;
|
||||
ShaderPixelParameterMode parameter_mode = ShaderPixelParameterMode::FirstVertex;
|
||||
bool ps_pixel_kill_enable = false;
|
||||
bool ps_depth_export_enable = false;
|
||||
bool ps_sample_mask_export_enable = false;
|
||||
|
||||
@@ -30,6 +30,9 @@ uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, ui
|
||||
|
||||
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||
std::span<const uint32_t> active_inputs, uint32_t input) {
|
||||
if (info.parameter_mode != ShaderPixelParameterMode::Rectangle) {
|
||||
return ShaderPixelParameterMappedLocation(info, input);
|
||||
}
|
||||
std::array<bool, 32> used_locations {};
|
||||
for (const auto active_input: active_inputs) {
|
||||
used_locations[ShaderPixelParameterMappedLocation(info, active_input)] = true;
|
||||
@@ -42,8 +45,7 @@ uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||
auto& location = group_locations[group];
|
||||
if (location == UINT32_MAX) {
|
||||
location = mapped;
|
||||
// Smooth and custom interpolation read the same vertex output. Only
|
||||
// differing flat/smooth rectangle outputs need separate locations.
|
||||
// Rectangle expansion broadcasts flat aliases through separate outputs.
|
||||
if (group_locations[group ^ 1u] != UINT32_MAX) {
|
||||
location = 0;
|
||||
while (location < used_locations.size() && used_locations[location]) {
|
||||
|
||||
@@ -198,12 +198,6 @@ KernelSemaPrivate::Result KernelSemaPrivate::Wait(int need_count, uint32_t* ptr_
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
|
||||
int id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
if (m_count >= need_count) {
|
||||
m_count -= need_count;
|
||||
if (ptr_micros != nullptr) {
|
||||
@@ -216,6 +210,12 @@ KernelSemaPrivate::Result KernelSemaPrivate::Wait(int need_count, uint32_t* ptr_
|
||||
return Result::TimedOut;
|
||||
}
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
|
||||
int id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
WaitingThread waiter {};
|
||||
waiter.id = id;
|
||||
waiter.priority = (m_fifo_order ? 0 : Libs::LibKernel::PthreadGetCurrentPriorityForKernel());
|
||||
|
||||
+112
-57
@@ -24,8 +24,12 @@ namespace Libs::Controller {
|
||||
|
||||
LIB_NAME("Pad", "Pad");
|
||||
|
||||
constexpr int PAD_ERROR_INVALID_ARG = -2137915391; /* 0x80920001 */
|
||||
constexpr int PAD_ERROR_INVALID_HANDLE = -2137915389; /* 0x80920003 */
|
||||
constexpr int PAD_ERROR_INVALID_ARG = -2137915391; /* 0x80920001 */
|
||||
constexpr int PAD_ERROR_INVALID_HANDLE = -2137915389; /* 0x80920003 */
|
||||
constexpr int PAD_ERROR_ALREADY_OPENED = -2137915388; /* 0x80920004 */
|
||||
constexpr int PAD_ERROR_NOT_INITIALIZED = -2137915387; /* 0x80920005 */
|
||||
constexpr int PAD_ERROR_NO_HANDLE = -2137915384; /* 0x80920008 */
|
||||
constexpr int PAD_HANDLE = 1;
|
||||
|
||||
constexpr uint32_t RUMBLE_DURATION_MS = 0xffff;
|
||||
constexpr uint32_t RELEASE_FLUSH_MS = 50;
|
||||
@@ -107,6 +111,12 @@ public:
|
||||
|
||||
KYTY_CLASS_NO_COPY(GameController);
|
||||
|
||||
int InitPort();
|
||||
int OpenPort(int user_id, int type, int index);
|
||||
int GetPortHandle(int user_id, int type, int index);
|
||||
int ClosePort(int handle);
|
||||
int CheckPortHandle(int handle);
|
||||
|
||||
void Connect(int id);
|
||||
void Disconnect(int id);
|
||||
void Button(int id, uint32_t button, bool down);
|
||||
@@ -131,6 +141,9 @@ public:
|
||||
|
||||
private:
|
||||
static constexpr uint32_t STATES_MAX = 64;
|
||||
enum class PortState { Uninitialized, Closed, Open };
|
||||
|
||||
int CheckPortHandleLocked(int handle) const;
|
||||
|
||||
void CheckActive();
|
||||
void AddState();
|
||||
@@ -140,6 +153,7 @@ private:
|
||||
void UpdateTriggerEffectState();
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
PortState m_port_state = PortState::Uninitialized;
|
||||
std::vector<int> m_connected_ids;
|
||||
int m_active_id = -1;
|
||||
bool m_connected = false;
|
||||
@@ -925,21 +939,71 @@ int GetActiveControllerId() {
|
||||
return g_controller != nullptr ? g_controller->GetActiveControllerId() : -1;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadInit() {
|
||||
PRINT_NAME();
|
||||
static bool PadOpenArgsAreValid(int user_id, int type, int index) {
|
||||
// The host keyboard and gamepad feed one standard port for the configured user.
|
||||
return user_id == Config::GetUserId() && type == 0 && index == 0;
|
||||
}
|
||||
|
||||
int GameController::InitPort() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_port_state == PortState::Uninitialized) {
|
||||
m_port_state = PortState::Closed;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
static bool PadOpenArgsAreValid(int user_id, int type, int index) {
|
||||
constexpr int user_id_system = 0xff;
|
||||
constexpr int port_type_standard = 0;
|
||||
constexpr int port_type_special = 2;
|
||||
constexpr int port_type_remote = 16;
|
||||
const bool personal_port =
|
||||
user_id == Config::GetUserId() && (type == port_type_standard || type == port_type_special);
|
||||
const bool system_remote_control = user_id == user_id_system && type == port_type_remote;
|
||||
return index == 0 && (personal_port || system_remote_control);
|
||||
int GameController::OpenPort(int user_id, int type, int index) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_port_state == PortState::Uninitialized) {
|
||||
return PAD_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (!PadOpenArgsAreValid(user_id, type, index)) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (m_port_state == PortState::Open) {
|
||||
return PAD_ERROR_ALREADY_OPENED;
|
||||
}
|
||||
m_port_state = PortState::Open;
|
||||
return PAD_HANDLE;
|
||||
}
|
||||
|
||||
int GameController::GetPortHandle(int user_id, int type, int index) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_port_state == PortState::Uninitialized) {
|
||||
return PAD_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
return m_port_state == PortState::Open && PadOpenArgsAreValid(user_id, type, index) ? PAD_HANDLE
|
||||
: PAD_ERROR_NO_HANDLE;
|
||||
}
|
||||
|
||||
int GameController::CheckPortHandleLocked(int handle) const {
|
||||
if (m_port_state == PortState::Uninitialized) {
|
||||
return PAD_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
return m_port_state == PortState::Open && handle == PAD_HANDLE ? OK : PAD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
int GameController::CheckPortHandle(int handle) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
return CheckPortHandleLocked(handle);
|
||||
}
|
||||
|
||||
int GameController::ClosePort(int handle) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (const auto error = CheckPortHandleLocked(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
m_port_state = PortState::Closed;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int PadCheckHandle(int handle) {
|
||||
return g_controller->CheckPortHandle(handle);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadInit() {
|
||||
PRINT_NAME();
|
||||
return g_controller->InitPort();
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
|
||||
@@ -951,15 +1015,7 @@ int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
|
||||
"\t param = 0x%016" PRIx64 "\n",
|
||||
user_id, type, index, reinterpret_cast<uint64_t>(param));
|
||||
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
|
||||
if (!PadOpenArgsAreValid(user_id, type, index)) {
|
||||
return pad_error_invalid_arg;
|
||||
}
|
||||
|
||||
int handle = 1;
|
||||
|
||||
return handle;
|
||||
return g_controller->OpenPort(user_id, type, index);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index) {
|
||||
@@ -970,20 +1026,20 @@ int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index) {
|
||||
"\t index = %d\n",
|
||||
user_id, type, index);
|
||||
|
||||
constexpr int pad_error_device_no_handle = -2137915384; /* 0x80920008 */
|
||||
return g_controller->GetPortHandle(user_id, type, index);
|
||||
}
|
||||
|
||||
if (!PadOpenArgsAreValid(user_id, type, index)) {
|
||||
return pad_error_device_no_handle;
|
||||
}
|
||||
|
||||
return 1;
|
||||
int KYTY_SYSV_ABI PadClose(int handle) {
|
||||
PRINT_NAME();
|
||||
LOGF("\t handle = %d\n", handle);
|
||||
return g_controller->ClosePort(handle);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
LOGF("\t enable = %s\n", (enable ? "true" : "false"));
|
||||
@@ -995,8 +1051,8 @@ int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable) {
|
||||
int KYTY_SYSV_ABI PadSetAngularVelocityDeadbandState(int handle, bool enable) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
LOGF("\t enable = %s\n", (enable ? "true" : "false"));
|
||||
@@ -1007,8 +1063,8 @@ int KYTY_SYSV_ABI PadSetAngularVelocityDeadbandState(int handle, bool enable) {
|
||||
int KYTY_SYSV_ABI PadResetOrientation(int handle) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
g_controller->ResetOrientation();
|
||||
@@ -1018,18 +1074,17 @@ int KYTY_SYSV_ABI PadResetOrientation(int handle) {
|
||||
int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformation* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
|
||||
g_controller->GetConnectionInfo(&connected, &connected_count);
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (info == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
g_controller->GetConnectionInfo(&connected, &connected_count);
|
||||
|
||||
std::memset(info, 0, sizeof(*info));
|
||||
|
||||
info->touch_pixel_density = 44.86f;
|
||||
@@ -1048,8 +1103,8 @@ int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformati
|
||||
int KYTY_SYSV_ABI PadIsRemoteController(int handle, bool* is_remote) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (is_remote == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1062,8 +1117,8 @@ int KYTY_SYSV_ABI PadIsRemoteController(int handle, bool* is_remote) {
|
||||
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (data == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1084,8 +1139,8 @@ int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num) {
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(num < 1 || num > 64);
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (data == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1116,8 +1171,8 @@ int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num) {
|
||||
int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1135,8 +1190,8 @@ int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param) {
|
||||
int KYTY_SYSV_ABI PadResetLightBar(int handle) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -1145,8 +1200,8 @@ int KYTY_SYSV_ABI PadResetLightBar(int handle) {
|
||||
int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1160,8 +1215,8 @@ int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param) {
|
||||
int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const PadTriggerEffectParam* param) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
@@ -1175,8 +1230,8 @@ int KYTY_SYSV_ABI PadGetTriggerEffectState(int handle, PadTriggerEffectStateInfo
|
||||
|
||||
LOGF("\t handle = %d\n", handle);
|
||||
|
||||
if (handle != 1) {
|
||||
return PAD_ERROR_INVALID_HANDLE;
|
||||
if (const auto error = PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (info == nullptr) {
|
||||
return PAD_ERROR_INVALID_ARG;
|
||||
|
||||
@@ -81,9 +81,12 @@ enum class Setting { SpeakerVolume, VibrationIntensity, TriggerEffectIntensity }
|
||||
void CycleSetting(Setting setting);
|
||||
float GetSettingScale(Setting setting);
|
||||
|
||||
int PadCheckHandle(int handle);
|
||||
|
||||
int KYTY_SYSV_ABI PadInit();
|
||||
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param);
|
||||
int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index);
|
||||
int KYTY_SYSV_ABI PadClose(int handle);
|
||||
int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable);
|
||||
int KYTY_SYSV_ABI PadSetAngularVelocityDeadbandState(int handle, bool enable);
|
||||
int KYTY_SYSV_ABI PadResetOrientation(int handle);
|
||||
|
||||
+14
-13
@@ -19,6 +19,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -48,7 +49,7 @@ struct ResolvedPathInfo {
|
||||
uint32_t file_id = 0xffffffffu;
|
||||
uint64_t file_size = 0;
|
||||
bool is_dir = false;
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
};
|
||||
|
||||
static std::mutex g_mutex;
|
||||
@@ -56,7 +57,7 @@ static uint32_t g_next_submission_id =
|
||||
static uint32_t g_next_file_id = 1;
|
||||
static std::unordered_map<uint32_t, bool> g_submissions;
|
||||
static std::condition_variable g_submission_cv;
|
||||
static std::unordered_map<uint32_t, std::string> g_files;
|
||||
static std::unordered_map<uint32_t, std::filesystem::path> g_files;
|
||||
static std::unordered_map<uint32_t, uint64_t> g_file_sizes;
|
||||
static std::unordered_map<std::string, ResolvedPathInfo> g_resolved_paths;
|
||||
|
||||
@@ -113,7 +114,7 @@ static int ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
|
||||
return LibKernel::KERNEL_ERROR_ENAMETOOLONG;
|
||||
}
|
||||
|
||||
static bool TryGetHostPath(uint32_t file_id, std::string* out) {
|
||||
static bool TryGetHostPath(uint32_t file_id, std::filesystem::path* out) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
const auto it = g_files.find(file_id);
|
||||
if (it == g_files.end()) {
|
||||
@@ -133,7 +134,7 @@ static bool TryGetHostFileSize(uint32_t file_id, uint64_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int GetHostPathStat(const std::string& host_path, LibKernel::FileSystem::FileStat* st) {
|
||||
static int GetHostPathStat(const std::filesystem::path& host_path, LibKernel::FileSystem::FileStat* st) {
|
||||
if (st == nullptr) {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
@@ -194,7 +195,7 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
|
||||
if (!found) {
|
||||
const auto real_path = LibKernel::FileSystem::GetRealFilename(path);
|
||||
info.host_path = Common::PathToString(real_path);
|
||||
info.host_path = real_path;
|
||||
|
||||
if (Common::File::IsDirectoryExisting(real_path)) {
|
||||
info.is_dir = true;
|
||||
@@ -225,7 +226,7 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
}
|
||||
}
|
||||
if (log_missing) {
|
||||
LOGF("\tAPR resolve missing path: %s -> %s\n", guest_path, info.host_path.c_str());
|
||||
LOGF("\tAPR resolve missing path: %s -> %s\n", guest_path, Common::PathToString(info.host_path).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +409,7 @@ static int KYTY_SYSV_ABI GetFileStat(uint32_t file_id, LibKernel::FileSystem::Fi
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EINVAL);
|
||||
}
|
||||
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(file_id, &host_path)) {
|
||||
LOGF("\tAPR stat failed for unknown file id: 0x%08" PRIx32 "\n", file_id);
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_ENOENT);
|
||||
@@ -430,7 +431,7 @@ static int KYTY_SYSV_ABI GetFileSize(uint32_t file_id, uint64_t* size) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(file_id, &host_path)) {
|
||||
LOGF("\tAPR size failed for unknown file id: 0x%08" PRIx32 "\n", file_id);
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_ENOENT);
|
||||
@@ -1161,13 +1162,13 @@ static bool AppendCommandRecord(uint64_t command_buffer, CommandBufferState::Com
|
||||
return CommitCommandBufferRecord(command_buffer, &state, record_size);
|
||||
}
|
||||
|
||||
static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offset,
|
||||
static int ReadHostFileToGuest(const std::filesystem::path& host_path, uint64_t file_offset,
|
||||
uint64_t destination, uint64_t size, uint64_t* bytes_read);
|
||||
|
||||
static int ExecuteCommand(const CommandBufferState::Command& entry) {
|
||||
if (const auto* payload = std::get_if<CommandBufferState::ReadFileCommand>(&entry.data)) {
|
||||
const auto& command = *payload;
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(command.file_id, &host_path)) {
|
||||
LOGF("\tAPR submit failed for unknown file id: 0x%08" PRIx32 "\n", command.file_id);
|
||||
return LibKernel::KERNEL_ERROR_ENOENT;
|
||||
@@ -1178,7 +1179,7 @@ static int ExecuteCommand(const CommandBufferState::Command& entry) {
|
||||
command.size, &bytes_read);
|
||||
if (result != OK) {
|
||||
LOGF("\tAPR submit read failed: id=0x%08" PRIx32 ", result=0x%08" PRIx32 ", path=%s\n",
|
||||
command.file_id, static_cast<uint32_t>(result), host_path.c_str());
|
||||
command.file_id, static_cast<uint32_t>(result), Common::PathToString(host_path).c_str());
|
||||
return result;
|
||||
}
|
||||
} else if (const auto* payload =
|
||||
@@ -1380,7 +1381,7 @@ static bool AdvanceCommandBuffer(uint64_t command_buffer, uint64_t record_size)
|
||||
return CommitCommandBufferRecord(command_buffer, &state, record_size);
|
||||
}
|
||||
|
||||
static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offset,
|
||||
static int ReadHostFileToGuest(const std::filesystem::path& host_path, uint64_t file_offset,
|
||||
uint64_t destination, uint64_t size, uint64_t* bytes_read) {
|
||||
if (bytes_read == nullptr) {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
@@ -1396,7 +1397,7 @@ static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offse
|
||||
|
||||
Common::File file;
|
||||
if (!file.Open(host_path, Common::File::Mode::Read)) {
|
||||
LOGF("\tAPR read missing host file: %s\n", host_path.c_str());
|
||||
LOGF("\tAPR read missing host file: %s\n", Common::PathToString(host_path).c_str());
|
||||
return LibKernel::KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
const auto file_size = file.Size();
|
||||
|
||||
+17
-15
@@ -78,6 +78,8 @@ constexpr int SCE_FONT_TEXT_PARSER_RESULT_TERMINATE = 0;
|
||||
constexpr int SCE_FONT_TEXT_PARSER_RESULT_ERROR = -1;
|
||||
constexpr int SCE_FONT_WRITING_FORM_HORIZONTAL = 0x10;
|
||||
constexpr int FONT_BITMAP_MAX_DIM = 128;
|
||||
// TrueType glyphs are rasterized at the requested size; the limit only rejects absurd scales.
|
||||
constexpr int FONT_GLYPH_MAX_DIM = 4096;
|
||||
constexpr int FONT_ERROR_INVALID_PARAMETER = static_cast<int>(0x80460002u);
|
||||
constexpr int FONT_ERROR_INVALID_FONT_HANDLE = static_cast<int>(0x80460005u);
|
||||
constexpr int FONT_ERROR_NO_SUPPORT_CODE = static_cast<int>(0x80460041u);
|
||||
@@ -206,7 +208,7 @@ struct FontState {
|
||||
const uint8_t* ttf_data;
|
||||
uint32_t ttf_size;
|
||||
bool has_ttf;
|
||||
std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX_DIM> fallback_image;
|
||||
std::vector<uint8_t> fallback_image;
|
||||
FontTransImage trans_image;
|
||||
};
|
||||
|
||||
@@ -227,7 +229,7 @@ struct GlyphState {
|
||||
uint32_t code;
|
||||
int attribute;
|
||||
FontGlyphMetrics metrics;
|
||||
std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX_DIM> image;
|
||||
std::vector<uint8_t> image;
|
||||
FontTransImage trans_image;
|
||||
};
|
||||
|
||||
@@ -851,8 +853,8 @@ static bool get_stb_metrics(FontState* font, uint32_t code, FontGlyphMetrics* me
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_stb_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX_DIM>* image,
|
||||
FontTransImage* trans_image, uint32_t code, FontState* font) {
|
||||
static bool init_stb_image(std::vector<uint8_t>* image, FontTransImage* trans_image, uint32_t code,
|
||||
FontState* font) {
|
||||
if (image == nullptr || trans_image == nullptr || !ensure_stb_font(font)) {
|
||||
return false;
|
||||
}
|
||||
@@ -868,12 +870,12 @@ static bool init_stb_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP
|
||||
stbtt_GetCodepointBitmapBox(&font->font_info, static_cast<int>(code), scale_x, scale_y, &x0, &y0,
|
||||
&x1, &y1);
|
||||
|
||||
image->fill(0);
|
||||
|
||||
const auto width = static_cast<uint32_t>(std::clamp(x1 - x0, 0, FONT_BITMAP_MAX_DIM));
|
||||
const auto height = static_cast<uint32_t>(std::clamp(y1 - y0, 0, FONT_BITMAP_MAX_DIM));
|
||||
const auto width = static_cast<uint32_t>(std::clamp(x1 - x0, 0, FONT_GLYPH_MAX_DIM));
|
||||
const auto height = static_cast<uint32_t>(std::clamp(y1 - y0, 0, FONT_GLYPH_MAX_DIM));
|
||||
// Keep at least one byte so that blank glyphs still get a valid address.
|
||||
image->assign(std::max<size_t>(static_cast<size_t>(width) * height, 1), 0);
|
||||
trans_image->address = image->data();
|
||||
trans_image->width_byte = FONT_BITMAP_MAX_DIM;
|
||||
trans_image->width_byte = width;
|
||||
trans_image->image_width = width;
|
||||
trans_image->image_height = height;
|
||||
|
||||
@@ -882,7 +884,7 @@ static bool init_stb_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP
|
||||
}
|
||||
|
||||
stbtt_MakeCodepointBitmap(&font->font_info, image->data(), static_cast<int>(width),
|
||||
static_cast<int>(height), FONT_BITMAP_MAX_DIM, scale_x, scale_y,
|
||||
static_cast<int>(height), static_cast<int>(width), scale_x, scale_y,
|
||||
static_cast<int>(code));
|
||||
|
||||
return true;
|
||||
@@ -939,8 +941,8 @@ static uint32_t normalize_bitmap_font_code(uint32_t code) {
|
||||
return static_cast<uint32_t>('?');
|
||||
}
|
||||
|
||||
static void init_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX_DIM>* image,
|
||||
FontTransImage* trans_image, uint32_t code, const FontState* font) {
|
||||
static void init_image(std::vector<uint8_t>* image, FontTransImage* trans_image, uint32_t code,
|
||||
const FontState* font) {
|
||||
EXIT_NOT_IMPLEMENTED(image == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(trans_image == nullptr);
|
||||
|
||||
@@ -948,11 +950,11 @@ static void init_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX
|
||||
return;
|
||||
}
|
||||
|
||||
image->fill(0);
|
||||
code = normalize_bitmap_font_code(code);
|
||||
|
||||
const auto width = static_cast<uint32_t>(scaled_font_width(font));
|
||||
const auto height = static_cast<uint32_t>(scaled_font_height(font));
|
||||
image->assign(static_cast<size_t>(width) * height, 0);
|
||||
#if LIBAVUTIL_VERSION_MAJOR < 61
|
||||
const auto* vga_font = avpriv_vga16_font;
|
||||
#else
|
||||
@@ -965,13 +967,13 @@ static void init_image(std::array<uint8_t, FONT_BITMAP_MAX_DIM * FONT_BITMAP_MAX
|
||||
for (uint32_t x = 0; x < width; x++) {
|
||||
const uint32_t src_x = std::min<uint32_t>((x * 8u) / width, 7u);
|
||||
if ((row & (0x80u >> src_x)) != 0) {
|
||||
(*image)[y * FONT_BITMAP_MAX_DIM + x] = 0xff;
|
||||
(*image)[y * width + x] = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trans_image->address = image->data();
|
||||
trans_image->width_byte = FONT_BITMAP_MAX_DIM;
|
||||
trans_image->width_byte = width;
|
||||
trans_image->image_width = width;
|
||||
trans_image->image_height = height;
|
||||
}
|
||||
|
||||
+73
-3
@@ -1497,13 +1497,43 @@ namespace LibNpCommerce {
|
||||
|
||||
LIB_VERSION("NpCommerce", 1, "NpCommerce", 1, 1);
|
||||
|
||||
constexpr int COMMERCE_STATUS_NONE = 0;
|
||||
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
|
||||
constexpr int COMMERCE_STATUS_NONE = 0;
|
||||
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
|
||||
constexpr int COMMERCE_STATUS_FINISHED = 3;
|
||||
constexpr int COMMERCE_RESULT_USER_CANCELED = 1;
|
||||
|
||||
constexpr int COMMERCE_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
|
||||
constexpr int COMMERCE_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004u);
|
||||
constexpr int COMMERCE_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
|
||||
constexpr int COMMERCE_ERROR_PARAM_INVALID = static_cast<int>(0x80B8000Au);
|
||||
constexpr int COMMERCE_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
|
||||
|
||||
static int g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
struct CommerceDialogParam {
|
||||
uint8_t base_param[48];
|
||||
int32_t size;
|
||||
int32_t user_id;
|
||||
int32_t mode;
|
||||
uint32_t service_label;
|
||||
const char* const* targets;
|
||||
uint32_t num_targets;
|
||||
uint32_t padding;
|
||||
uint64_t features;
|
||||
void* user_data;
|
||||
uint8_t reserved[32];
|
||||
};
|
||||
|
||||
struct CommerceDialogResult {
|
||||
int32_t result;
|
||||
bool authorized;
|
||||
void* user_data;
|
||||
uint8_t reserved[32];
|
||||
};
|
||||
|
||||
static_assert(sizeof(CommerceDialogParam) == 128 && offsetof(CommerceDialogParam, user_data) == 88);
|
||||
static_assert(sizeof(CommerceDialogResult) == 48 && offsetof(CommerceDialogResult, user_data) == 8);
|
||||
|
||||
static int g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
static CommerceDialogResult g_commerce_result {};
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogInitialize() {
|
||||
PRINT_NAME();
|
||||
@@ -1520,9 +1550,47 @@ static int KYTY_SYSV_ABI NpCommerceDialogTerminate() {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
g_commerce_result = {};
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogOpen(const CommerceDialogParam* param) {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status == COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return COMMERCE_ERROR_ARG_NULL;
|
||||
}
|
||||
g_commerce_result = {};
|
||||
if (param->size == sizeof(CommerceDialogParam)) {
|
||||
g_commerce_result.user_data = param->user_data;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_FINISHED;
|
||||
if (param->size != sizeof(CommerceDialogParam) || param->mode < 0 || param->mode > 5 ||
|
||||
std::ranges::any_of(param->reserved, [](uint8_t value) { return value != 0; })) {
|
||||
g_commerce_result.result = COMMERCE_ERROR_PARAM_INVALID;
|
||||
return COMMERCE_ERROR_PARAM_INVALID;
|
||||
}
|
||||
g_commerce_result.result = COMMERCE_RESULT_USER_CANCELED;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogGetResult(CommerceDialogResult* result) {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status == COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (result == nullptr) {
|
||||
return COMMERCE_ERROR_ARG_NULL;
|
||||
}
|
||||
if (g_commerce_status != COMMERCE_STATUS_FINISHED) {
|
||||
return COMMERCE_ERROR_NOT_FINISHED;
|
||||
}
|
||||
*result = g_commerce_result;
|
||||
return result->result;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -1532,6 +1600,8 @@ static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
|
||||
LIB_DEFINE(InitNet_1_NpCommerce) {
|
||||
LIB_FUNC("0aR2aWmQal4", NpCommerceDialogInitialize);
|
||||
LIB_FUNC("m-I92Ab50W8", NpCommerceDialogTerminate);
|
||||
LIB_FUNC("DfSCDRA3EjY", NpCommerceDialogOpen);
|
||||
LIB_FUNC("r42bWcQbtZY", NpCommerceDialogGetResult);
|
||||
LIB_FUNC("LR5cwFMMCVE", NpCommerceDialogUpdateStatus);
|
||||
}
|
||||
|
||||
|
||||
+10
-20
@@ -19,7 +19,7 @@ static int KYTY_SYSV_ABI PadSetVibrationMode(int handle, int mode) {
|
||||
"\t mode = %d\n",
|
||||
handle, mode);
|
||||
|
||||
return 0;
|
||||
return Controller::PadCheckHandle(handle);
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI PadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(bool enabled) {
|
||||
@@ -66,11 +66,10 @@ static int KYTY_SYSV_ABI
|
||||
PadDeviceClassGetExtendedInformation(int handle, PadDeviceClassExtendedInformation* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
constexpr int pad_error_invalid_handle = -2137915389; /* 0x80920003 */
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
|
||||
if (handle != 1) {
|
||||
return pad_error_invalid_handle;
|
||||
if (const auto error = Controller::PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (info == nullptr) {
|
||||
return pad_error_invalid_arg;
|
||||
@@ -85,16 +84,15 @@ static int KYTY_SYSV_ABI PadDeviceClassParseData(int handle, const Controller::P
|
||||
PadDeviceClassData* class_data) {
|
||||
PRINT_NAME();
|
||||
|
||||
constexpr int pad_error_invalid_handle = -2137915389; /* 0x80920003 */
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
|
||||
LOGF("\t handle = %d\n"
|
||||
"\t data = 0x%016" PRIx64 "\n"
|
||||
"\t class_data = 0x%016" PRIx64 "\n",
|
||||
handle, reinterpret_cast<uint64_t>(data), reinterpret_cast<uint64_t>(class_data));
|
||||
|
||||
if (handle != 1) {
|
||||
return pad_error_invalid_handle;
|
||||
if (const auto error = Controller::PadCheckHandle(handle); error != OK) {
|
||||
return error;
|
||||
}
|
||||
if (data == nullptr || class_data == nullptr) {
|
||||
return pad_error_invalid_arg;
|
||||
@@ -122,15 +120,7 @@ static int KYTY_SYSV_ABI PadSetTiltCorrectionState(int handle, bool enabled) {
|
||||
"\t enabled = %d\n",
|
||||
handle, enabled ? 1 : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI PadClose(int handle) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t handle = %d\n", handle);
|
||||
|
||||
return OK;
|
||||
return Controller::PadCheckHandle(handle);
|
||||
}
|
||||
|
||||
// PPSA02385
|
||||
@@ -154,7 +144,7 @@ static int KYTY_SYSV_ABI PadGetExtControllerInformationStub(int handle, void* in
|
||||
"\t info = 0x%016" PRIx64 "\n",
|
||||
handle, reinterpret_cast<uint64_t>(info));
|
||||
|
||||
return OK;
|
||||
return Controller::PadCheckHandle(handle);
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI PadUnknownN3kSX62fgNo(uint64_t arg0, uint64_t arg1, uint64_t arg2,
|
||||
@@ -197,7 +187,7 @@ LIB_DEFINE(InitPad_1) {
|
||||
LIB_FUNC("znaWI0gpuo8", Controller::PadGetTriggerEffectState);
|
||||
LIB_FUNC("DscD1i9HX1w", Controller::PadResetLightBar);
|
||||
LIB_FUNC("RR4novUEENY", Controller::PadSetLightBar);
|
||||
LIB_FUNC("6ncge5+l5Qs", PadClose);
|
||||
LIB_FUNC("6ncge5+l5Qs", Controller::PadClose);
|
||||
LIB_FUNC("n3kSX62fgNo", PadUnknownN3kSX62fgNo);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,8 @@ struct Controller {
|
||||
now = 1000;
|
||||
haptics_handles_rumble = false;
|
||||
Initialize();
|
||||
Check(PadInit() == 0 && PadOpen(Config::GetUserId(), 0, 0, nullptr) == 1,
|
||||
"the standard controller port did not open");
|
||||
Connect(1);
|
||||
Check(GetSettingScale(Setting::SpeakerVolume) ==
|
||||
Config::GetControllerSpeakerVolume() / 50.0f &&
|
||||
@@ -143,6 +145,54 @@ void SetRumble(uint8_t large, uint8_t small) {
|
||||
Check(PadSetVibration(1, ¶m) == 0, "vibration request failed");
|
||||
}
|
||||
|
||||
void TestPadPortOwnership() {
|
||||
Initialize();
|
||||
const int user = Config::GetUserId();
|
||||
constexpr auto not_initialized = static_cast<int32_t>(0x80920005u);
|
||||
constexpr auto already_opened = static_cast<int32_t>(0x80920004u);
|
||||
constexpr auto invalid_handle = static_cast<int32_t>(0x80920003u);
|
||||
constexpr auto no_handle = static_cast<int32_t>(0x80920008u);
|
||||
PadData data {};
|
||||
Check(PadOpen(user, 0, 0, nullptr) == not_initialized &&
|
||||
PadGetHandle(user, 0, 0) == not_initialized &&
|
||||
PadReadState(1, &data) == not_initialized,
|
||||
"an uninitialized library exposed a controller port");
|
||||
Check(PadInit() == 0 && PadGetHandle(user, 0, 0) == no_handle,
|
||||
"initializing the library opened a controller port");
|
||||
const int handle = PadOpen(user, 0, 0, nullptr);
|
||||
Check(handle == 1 && PadInit() == 0 && PadGetHandle(user, 0, 0) == handle,
|
||||
"reinitializing the library lost the open port");
|
||||
for (int duplicate = 0; duplicate < 4; duplicate++) {
|
||||
const int rejected = PadOpen(user, 0, 0, nullptr);
|
||||
Check(rejected == already_opened,
|
||||
"opening one pad five times exposed duplicate connected controllers");
|
||||
Check(PadClose(rejected) == invalid_handle && PadGetHandle(user, 0, 0) == handle,
|
||||
"closing a rejected duplicate released the original owner");
|
||||
}
|
||||
Check(PadGetHandle(user + 1, 0, 0) == no_handle &&
|
||||
PadGetHandle(user, 2, 0) == no_handle && PadGetHandle(user, 0, 1) == no_handle,
|
||||
"a different port tuple aliased the open standard pad");
|
||||
SetButton(HOST_INPUT_CONTROLLER_ID, PAD_BUTTON_CROSS, true);
|
||||
Check(PadReadState(handle, &data) == 0 && data.connected &&
|
||||
data.buttons == PAD_BUTTON_CROSS,
|
||||
"keyboard Cross did not reach the owned port");
|
||||
SetButton(HOST_INPUT_CONTROLLER_ID, PAD_BUTTON_CROSS, false);
|
||||
Connect(1);
|
||||
SetButton(1, PAD_BUTTON_CROSS, true);
|
||||
Check(PadReadState(handle, &data) == 0 && data.buttons == PAD_BUTTON_CROSS,
|
||||
"the physical controller did not share the owned port");
|
||||
Disconnect(1);
|
||||
Check(PadGetHandle(user, 0, 0) == handle && PadOpen(user, 0, 0, nullptr) == already_opened,
|
||||
"host disconnection released guest port ownership");
|
||||
Check(PadClose(handle) == 0 && PadGetHandle(user, 0, 0) == no_handle &&
|
||||
PadReadState(handle, &data) == invalid_handle &&
|
||||
PadRead(handle, &data, 1) == invalid_handle && PadClose(handle) == invalid_handle,
|
||||
"a closed port retained a usable handle");
|
||||
Check(PadOpen(user, 0, 0, nullptr) == handle && PadReadState(handle, &data) == 0,
|
||||
"a closed port could not reopen");
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void TestSettingCycles() {
|
||||
Controller controller;
|
||||
for (auto setting:
|
||||
@@ -503,6 +553,7 @@ void TestTriggerEffectState() {
|
||||
|
||||
int main() {
|
||||
Config::Initialize();
|
||||
TestPadPortOwnership();
|
||||
TestSettingCycles();
|
||||
TestGlobalControllerLevels();
|
||||
// Each following test initializes another controller and requires strong defaults.
|
||||
|
||||
@@ -62,6 +62,10 @@ namespace Libs::LibNpWebApi2 {
|
||||
void InitNet_1_NpWebApi2(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace Libs::LibNpCommerce {
|
||||
void InitNet_1_NpCommerce(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
namespace FileSystem = Libs::LibKernel::FileSystem;
|
||||
@@ -385,6 +389,91 @@ void TestAioBatches() {
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close AIO read fixture");
|
||||
}
|
||||
|
||||
void TestNpCommerceDialog() {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibNpCommerce::InitNet_1_NpCommerce(&symbols);
|
||||
const auto find = [&](const char *nid) {
|
||||
const auto *symbol = symbols.FindByNid(nid, Loader::SymbolType::Func);
|
||||
Check(symbol != nullptr, "NpCommerce dialog exports resolve");
|
||||
return symbol->vaddr;
|
||||
};
|
||||
using Call = int (KYTY_SYSV_ABI *)();
|
||||
using Open = int (KYTY_SYSV_ABI *)(const void *);
|
||||
using GetResult = int (KYTY_SYSV_ABI *)(void *);
|
||||
const auto initialize = reinterpret_cast<Call>(find("0aR2aWmQal4"));
|
||||
const auto terminate = reinterpret_cast<Call>(find("m-I92Ab50W8"));
|
||||
const auto update = reinterpret_cast<Call>(find("LR5cwFMMCVE"));
|
||||
const auto open = reinterpret_cast<Open>(find("DfSCDRA3EjY"));
|
||||
const auto get_result = reinterpret_cast<GetResult>(find("r42bWcQbtZY"));
|
||||
struct BaseParam {
|
||||
uint64_t size;
|
||||
uint8_t reserved[36];
|
||||
uint32_t magic;
|
||||
};
|
||||
struct Param {
|
||||
BaseParam base;
|
||||
int32_t size, user_id, mode;
|
||||
uint32_t service_label;
|
||||
const char *const *targets;
|
||||
uint32_t num_targets, padding;
|
||||
uint64_t features;
|
||||
void *user_data;
|
||||
uint8_t reserved[32];
|
||||
} param {};
|
||||
struct Result {
|
||||
int32_t result;
|
||||
bool authorized;
|
||||
void *user_data;
|
||||
uint8_t reserved[32];
|
||||
} result {};
|
||||
static_assert(sizeof(Param) == 128 && offsetof(Param, user_data) == 88);
|
||||
static_assert(sizeof(Result) == 48 && offsetof(Result, user_data) == 8);
|
||||
param.base.size = sizeof(BaseParam);
|
||||
param.base.magic = static_cast<uint32_t>(0xC0D1A109u + reinterpret_cast<uintptr_t>(¶m.base));
|
||||
param.size = sizeof(param);
|
||||
param.user_id = 1;
|
||||
param.user_data = &symbols;
|
||||
Check(open(¶m) == static_cast<int>(0x80B80003u) &&
|
||||
get_result(&result) == static_cast<int>(0x80B80003u),
|
||||
"NpCommerce dialog calls require initialization");
|
||||
Check(initialize() == OK && update() == 1 &&
|
||||
get_result(&result) == static_cast<int>(0x80B80005u),
|
||||
"NpCommerce dialog result requires completion");
|
||||
Check(open(nullptr) == static_cast<int>(0x80B8000Du) && update() == 1 &&
|
||||
get_result(nullptr) == static_cast<int>(0x80B8000Du),
|
||||
"NpCommerce dialog rejects null arguments");
|
||||
Check(open(¶m) == OK && update() == 3, "NpCommerce dialog finishes without a store");
|
||||
param.user_data = nullptr;
|
||||
result.authorized = true;
|
||||
Check(get_result(&result) == 1 && result.result == 1 && !result.authorized &&
|
||||
result.user_data == &symbols,
|
||||
"NpCommerce cancellation returns the original user data");
|
||||
param.mode = 5;
|
||||
Check(open(¶m) == OK && get_result(&result) == 1 && !result.authorized &&
|
||||
result.user_data == nullptr,
|
||||
"NpCommerce dialog can reopen with fresh result data");
|
||||
const auto invalid_param = [&] {
|
||||
Check(open(¶m) == static_cast<int>(0x80B8000Au) && update() == 3 &&
|
||||
get_result(&result) == static_cast<int>(0x80B8000Au) &&
|
||||
result.result == static_cast<int>(0x80B8000Au) && !result.authorized,
|
||||
"NpCommerce dialog reports invalid parameters in the completion result");
|
||||
};
|
||||
param.mode = 99;
|
||||
invalid_param();
|
||||
param.mode = -1;
|
||||
invalid_param();
|
||||
param.mode = 0;
|
||||
param.size = 64;
|
||||
invalid_param();
|
||||
param.size = sizeof(param);
|
||||
param.reserved[0] = 1;
|
||||
invalid_param();
|
||||
param.reserved[0] = 0;
|
||||
Check(open(¶m) == OK && get_result(&result) == 1 && terminate() == OK && update() == 0 &&
|
||||
get_result(&result) == static_cast<int>(0x80B80003u),
|
||||
"NpCommerce dialog recovers from invalid parameters and terminates");
|
||||
}
|
||||
|
||||
void TestNpWebApi2Memory() {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibNpWebApi2::InitNet_1_NpWebApi2(&symbols);
|
||||
@@ -1644,6 +1733,7 @@ int main(int, char**) {
|
||||
CheckSocketWakeup();
|
||||
CheckEtherAddressFormatting();
|
||||
TestNpWebApi2Memory();
|
||||
TestNpCommerceDialog();
|
||||
graphics.reset();
|
||||
subsystems.Destroy();
|
||||
|
||||
|
||||
@@ -1353,6 +1353,7 @@ struct GraphicsCase {
|
||||
std::vector<u32> push_constants;
|
||||
std::vector<u32> pixel_interpolator_settings;
|
||||
bool pixel_no_perspective = false;
|
||||
bool provoking_vtx_last = false;
|
||||
std::vector<u32> vertices;
|
||||
bool pixel_ancillary = false;
|
||||
bool pixel_front_face = false;
|
||||
@@ -1449,6 +1450,20 @@ void CheckPixelParameterAliases() {
|
||||
pixel.interpolator_settings[1] = 0u;
|
||||
pixel.interpolator_settings[2] = 1u;
|
||||
const std::array<uint32_t, 3> active = {0, 1, 2};
|
||||
Require(name, "ordinary mixed aliases",
|
||||
ShaderPixelParameterLocation(pixel, active, 0) == 0u &&
|
||||
ShaderPixelParameterLocation(pixel, active, 1) == 0u &&
|
||||
ShaderPixelParameterLocation(pixel, active, 2) == 1u,
|
||||
"flat/smooth aliases must retain the real vertex export location");
|
||||
std::vector<uint32_t> ordinary_key, rectangle_key, provoking_key;
|
||||
BuildStageStaticKey(pixel, ordinary_key);
|
||||
pixel.parameter_mode = ShaderPixelParameterMode::LastVertex;
|
||||
BuildStageStaticKey(pixel, provoking_key);
|
||||
pixel.parameter_mode = ShaderPixelParameterMode::Rectangle;
|
||||
BuildStageStaticKey(pixel, rectangle_key);
|
||||
Require(name, "interpolation specialization",
|
||||
ordinary_key != rectangle_key && ordinary_key != provoking_key,
|
||||
"rectangle and provoking-vertex modes must participate in the shader key");
|
||||
Require(name, "reserved physical locations",
|
||||
ShaderPixelParameterLocation(pixel, active, 0) == 0u &&
|
||||
ShaderPixelParameterLocation(pixel, active, 1) == 2u &&
|
||||
@@ -1473,6 +1488,7 @@ void CheckRectListShaders() {
|
||||
vertex.stage.program = &vertex_program;
|
||||
vertex.stage.resources = &empty_snapshot;
|
||||
ShaderPixelInputInfo pixel{};
|
||||
pixel.parameter_mode = ShaderPixelParameterMode::Rectangle;
|
||||
pixel.input_num = 2;
|
||||
pixel.interpolator_settings[0] = 0x400u;
|
||||
pixel.interpolator_settings[1] = 0;
|
||||
@@ -1774,6 +1790,8 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
|
||||
? 1u
|
||||
: static_cast<u32>(test.pixel_interpolator_settings.size());
|
||||
pixel_info.ps_no_perspective = test.pixel_no_perspective;
|
||||
pixel_info.parameter_mode = test.provoking_vtx_last ? ShaderPixelParameterMode::LastVertex
|
||||
: ShaderPixelParameterMode::FirstVertex;
|
||||
pixel_info.ps_ancillary = test.pixel_ancillary;
|
||||
pixel_info.ps_front_face = test.pixel_front_face;
|
||||
pixel_info.ps_pos_w = test.pixel_position_w;
|
||||
@@ -16928,6 +16946,12 @@ public:
|
||||
raster.frontFace = vk::FrontFace::eCounterClockwise;
|
||||
raster.lineWidth = 1.0f;
|
||||
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{};
|
||||
if (test.provoking_vtx_last) {
|
||||
provoking_vertex.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
|
||||
raster.pNext = &provoking_vertex;
|
||||
}
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo multisample{};
|
||||
multisample.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
|
||||
multisample.rasterizationSamples = test.samples;
|
||||
@@ -22118,6 +22142,75 @@ TestCase Vop1SdwaNotPartialSourcesAndDestinations() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop2SdwaBitwiseByteDestinations(u32 wave_size) {
|
||||
using O = ShaderOpcode;
|
||||
// Low result bytes of AND, OR, XOR, XNOR for the two runtime inputs.
|
||||
constexpr std::array<u32, 4> result_bytes{0xa4u, 0xbeu, 0x1au, 0xe5u};
|
||||
constexpr std::array<u32, 4> alias_expected{
|
||||
0xc350e5a6u, 0xc3fce5a6u, 0xc3ace5a6u, 0xc353e5a6u};
|
||||
constexpr u32 sentinel = 0xa1b2c3d4u;
|
||||
TestCase test;
|
||||
test.name = wave_size == 64 ? "Vop2SdwaBitwiseByteDestinationsWave64"
|
||||
: "Vop2SdwaBitwiseByteDestinationsWave32";
|
||||
test.initial = {0xc3d4e5a6u, 0x9b785abcu};
|
||||
test.expected = test.initial;
|
||||
auto &code = test.code;
|
||||
const auto store = [&](u32 reg, u32 expected) {
|
||||
AppendStoreVgpr(&code, reg, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(expected);
|
||||
};
|
||||
for (u32 op = 0; op < 4; ++op) {
|
||||
AppendVMovU32(&code, 30, 0);
|
||||
AppendBufferLoadDword(&code, 12, 30);
|
||||
AppendVMovU32(&code, 30, 4);
|
||||
AppendBufferLoadDword(&code, 13, 30);
|
||||
for (u32 selector = 0; selector < 4; ++selector) {
|
||||
for (u32 unused = 0; unused < 3; ++unused) {
|
||||
AppendVMovLiteral(&code, 26, sentinel);
|
||||
code.push_back(EncodeVop2(0x1b + op, 26, 249, 13));
|
||||
code.push_back(EncodeVop2Sdwa(12, selector, unused));
|
||||
// RDNA2 table 88: pad, sign extend above/zero below, or preserve.
|
||||
const u32 shift = selector * 8u;
|
||||
u32 expected = result_bytes[op] << shift;
|
||||
if (unused == 1 && (result_bytes[op] & 0x80u) != 0) {
|
||||
expected = (0xffffff00u | result_bytes[op]) << shift;
|
||||
} else if (unused == 2) {
|
||||
expected |= sentinel & ~(0xffu << shift);
|
||||
}
|
||||
store(26, expected);
|
||||
}
|
||||
}
|
||||
// Read source byte2 and word1 before overwriting source0's byte2.
|
||||
code.push_back(EncodeVop2(0x1b + op, 12, 249, 13));
|
||||
code.push_back(EncodeVop2Sdwa(12, 2, 2, 2, 5));
|
||||
store(12, alias_expected[op]);
|
||||
// Source1 may also alias the destination.
|
||||
AppendVMovU32(&code, 30, 0);
|
||||
AppendBufferLoadDword(&code, 12, 30);
|
||||
code.push_back(EncodeVop2(0x1b + op, 13, 249, 13));
|
||||
code.push_back(EncodeVop2Sdwa(12, 1, 2));
|
||||
store(13, 0x9b7800bcu | (result_bytes[op] << 8u));
|
||||
for (u32 unused = 0; unused < 3; ++unused) {
|
||||
AppendVMovLiteral(&code, 26, sentinel);
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(0)));
|
||||
code.push_back(EncodeVop2(0x1b + op, 26, 249, 13));
|
||||
code.push_back(EncodeVop2Sdwa(12, 2, unused));
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
store(26, sentinel);
|
||||
}
|
||||
}
|
||||
AppendEnd(&code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD,
|
||||
O::V_AND_B32, O::V_OR_B32, O::V_XOR_B32, O::V_XNOR_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpBitwiseAnd", "OpBitwiseOr", "OpBitwiseXor", "OpNot",
|
||||
"OpBitFieldInsert", "OpBitFieldSExtract"};
|
||||
test.compute_info.wave_size = wave_size;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop1SdwaMovByteDestinations() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -35871,6 +35964,38 @@ GraphicsCase GraphicsSmoothRawInputAlias() {
|
||||
return test;
|
||||
}
|
||||
|
||||
GraphicsCase GraphicsSmoothFlatInputAlias(bool flat_first, bool provoking_last = false) {
|
||||
auto test = GraphicsSmoothRawInputAlias();
|
||||
test.name = provoking_last
|
||||
? (flat_first ? "GraphicsFlatSmoothAliasLast" : "GraphicsSmoothFlatAliasLast")
|
||||
: (flat_first ? "GraphicsFlatSmoothInputAlias" : "GraphicsSmoothFlatInputAlias");
|
||||
test.pixel_custom_interpolation_mask = 0;
|
||||
test.provoking_vtx_last = provoking_last;
|
||||
test.pixel_interpolator_settings = flat_first ? std::vector<u32>{0x400u, 0u}
|
||||
: std::vector<u32>{0u, 0x400u};
|
||||
const u32 smooth = flat_first ? 1u : 0u;
|
||||
const u32 flat = smooth ^ 1u;
|
||||
// One export carries smooth values (2,4,8) and packed light IDs (5,7,11).
|
||||
// Flat P0 preserves the ID bits; flat P1/P2 uses the same provoking vertex.
|
||||
test.vertices[4] = 0x3f800005u;
|
||||
test.vertices[10] = 0x3f800007u;
|
||||
test.vertices[16] = 0x3f80000bu;
|
||||
test.fragment_code = {EncodeVintrp(0x00, 0, smooth, 1, 0),
|
||||
EncodeVintrp(0x01, 0, smooth, 1, 1),
|
||||
EncodeVintrp(0x02, 1, flat, 2, 2),
|
||||
EncodeVop2(0x1b, 1, InlineU32(63), 1),
|
||||
EncodeVop1(0x06, 1, Vgpr(1)),
|
||||
EncodeVintrp(0x00, 2, flat, 1, 0),
|
||||
EncodeVintrp(0x01, 2, flat, 1, 1),
|
||||
EncodeExp0(0x00, 0xf), EncodeExp1(0, 1, 2, 0)};
|
||||
AppendEnd(&test.fragment_code);
|
||||
test.expected_pixel = {0x40700000u, provoking_last ? 0x41300000u : 0x40a00000u,
|
||||
provoking_last ? 0x41000000u : 0x40000000u, 0x40700000u};
|
||||
test.opcodes.insert(test.opcodes.end(),
|
||||
{ShaderOpcode::V_AND_B32, ShaderOpcode::V_CVT_F32_U32});
|
||||
return test;
|
||||
}
|
||||
|
||||
GraphicsCase GraphicsAncillaryLayer(bool front_face) {
|
||||
GraphicsCase test;
|
||||
test.name = front_face ? "GraphicsAncillaryAfterFrontFace" : "GraphicsAncillaryLayer";
|
||||
@@ -36278,6 +36403,8 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(Vop1SdwaNotCapturedByte0Source);
|
||||
AddCase(Vop1SdwaNotPreservesHighWordDestination);
|
||||
AddCase(Vop1SdwaNotPartialSourcesAndDestinations);
|
||||
cases.push_back(Vop2SdwaBitwiseByteDestinations(32));
|
||||
cases.push_back(Vop2SdwaBitwiseByteDestinations(64));
|
||||
AddCase(Vop1SdwaMovByteDestinations);
|
||||
cases.push_back(Vop1SdwaMovByteSources(32));
|
||||
cases.push_back(Vop1SdwaMovByteSources(64));
|
||||
@@ -36714,6 +36841,10 @@ std::vector<GraphicsCase> MakeGraphicsCases() {
|
||||
GraphicsPackedHalfInputAlias(false),
|
||||
GraphicsPackedHalfInputAlias(true),
|
||||
GraphicsSmoothRawInputAlias(),
|
||||
GraphicsSmoothFlatInputAlias(false),
|
||||
GraphicsSmoothFlatInputAlias(true),
|
||||
GraphicsSmoothFlatInputAlias(false, true),
|
||||
GraphicsSmoothFlatInputAlias(true, true),
|
||||
GraphicsAncillaryLayer(false),
|
||||
GraphicsAncillaryLayer(true),
|
||||
GraphicsAncillarySampleId(),
|
||||
@@ -42196,6 +42327,11 @@ int main(int argc, char **argv) {
|
||||
RunGraphicsCase(&vulkan, GraphicsPackedHalfInputAlias(false));
|
||||
RunGraphicsCase(&vulkan, GraphicsPackedHalfInputAlias(true));
|
||||
RunGraphicsCase(&vulkan, GraphicsSmoothRawInputAlias());
|
||||
RunGraphicsCase(&vulkan, GraphicsSmoothFlatInputAlias(false));
|
||||
RunGraphicsCase(&vulkan, GraphicsSmoothFlatInputAlias(true));
|
||||
RunGraphicsCase(&vulkan, GraphicsSmoothFlatInputAlias(false, true));
|
||||
RunGraphicsCase(&vulkan, GraphicsSmoothFlatInputAlias(true, true));
|
||||
RunGraphicsCase(&vulkan, GraphicsFlatInterpolatorExport());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--clip-control-only") == 0) {
|
||||
@@ -42350,6 +42486,13 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, ScalarZeroShiftWithRuntimeCount());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--sdwa-bitwise-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop2SdwaBitwiseByteDestinations(32));
|
||||
RunCase(&vulkan, Vop2SdwaBitwiseByteDestinations(64));
|
||||
RunCase(&vulkan, Vop1SdwaNotPartialSourcesAndDestinations());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--sdwa-mov-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop1SdwaMovByteDestinations());
|
||||
|
||||
Reference in New Issue
Block a user