Compare commits

...
Author SHA1 Message Date
Ali Almohaya ca7c8f0074 agc: fix linux build of gs oversubscription clamps (#232) 2026-08-11 07:38:22 +02:00
Pouare514andnmzik d7063d0b81 vulkan: remove validation layer configuration from device creation (#221)
* vulkan: remove validation layer configuration from device creation

* Remove accidental dependency gitlinks

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-09 23:05:31 +02:00
nmzik 091e6b31dc avplayer: decode media on bounded worker queues 2026-08-09 06:49:25 +02:00
nmzik 4cd6132758 gpu: allow byte-granular memory DMA 2026-08-09 05:31:18 +02:00
nmzik 9c263a9886 gpu: accept disabled default clip planes 2026-08-09 05:23:43 +02:00
nmzik c11fc96019 pad: accept system remote-control port 2026-08-09 05:15:40 +02:00
nmzik 4f2b5eb749 libSystemService fixes 2026-08-09 04:44:43 +02:00
nmzik d7013dc2b3 add left/right joystick remapping 2026-08-09 04:24:47 +02:00
nmzik 0bbe301120 add website/compatibility db 2026-08-09 04:08:32 +02:00
nmzik 37aa6f88b0 ime: implement libIme and shared host overlay 2026-08-09 02:58:49 +02:00
nmzik e3890febaa agc: new abi 2026-08-09 01:32:32 +02:00
nmzik a4da2a9f00 json2: implement typed Value constructor 2026-08-09 01:09:41 +02:00
nmzik 3005a210c6 renderer: ignore inactive HTile depth address 2026-08-08 23:55:45 +02:00
nmzik 027433fcdc Update game status report form 2026-08-08 21:19:22 +02:00
DeathWhispers 7ff9e4907f Don't declare colorWriteEnable dynamic state on depth-only pipelines (#160)
fix(gpu): drop colorWriteEnable dynamic state on depth-only pipelines

Pipelines were created with VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT
unconditionally on non-Apple platforms, but the draw path only calls
vkCmdSetColorWriteEnableEXT when there is at least one color attachment.
A depth-only draw therefore reached vkCmdDrawIndexed with the dynamic
state declared and never set, which violates
VUID-vkCmdDrawIndexed-None-07749.

With validation enabled the error is fatal, so any title that issues a
depth-only pass aborts. The launcher enables validation by default
(vulkan_validation_enabled = true), so this is the default configuration:
PAC-MAN WORLD Re-PAC crashes from the launcher while the same build runs
normally with --vulkan-validation false.

Gate the declaration on the same color_count the draw path uses. Both
sides read state.color_count from the same draw, so the two conditions
are now identical by construction.
2026-08-08 08:21:18 +02:00
Stefanos Costaandnmzik 87428d7a4b kernel: do not release a waiter on a zero-count syncOnAddress wake (linux only) (#168)
* kernel: do not release a waiter on a zero-count syncOnAddress wake

sceKernelSyncOnAddressWake with a count of 0 must release no waiters. On
Linux it released exactly one.

WakeLinux passes the count straight to FUTEX_WAKE, which does not treat 0
as "wake nobody": the kernel's wake loop tests `if (++ret >= nr_wake) break`
after waking a waiter, so any waiter present is released before the loop can
break. A standalone futex program confirms it -- FUTEX_WAKE with val = 0
returns 1, and the waiter observes its wait return 0 with the word unchanged.

WakePortable already short-circuits count == 0, so the two backends of the
same ABI disagreed, with only the Linux one wrong.

TestWakeZeroIsNoOp in SyncOnAddressTests covers exactly this and fails on
Linux without the fix (15 consecutive failures on the assertion "zero-count
wake releases no waiters"); with the fix the suite passes 15 consecutive runs.

* kernel: shorten zero-count futex comment

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-08 08:17:56 +02:00
30 changed files with 2525 additions and 494 deletions

No files matched your search

@@ -1,6 +1,6 @@
name: Game Emulation Bug Report
description: Report KytyPS5 game boot, crash, freeze, rendering, audio, input, or gameplay behavior problems.
title: "[GAME BUG]: "
name: Game Emulation Status Report
description: Submit a KytyPS5 game compatibility result, including boot, rendering, audio, input, or gameplay behavior.
title: "[GAME STATUS]: "
body:
- type: markdown
attributes:
@@ -10,6 +10,7 @@ body:
Before submitting, please:
- Search existing issues first.
- Re-test with a clean game dump.
- Use the latest KytyPS5 build available to you.
- Attach the complete log file instead of edited snippets.
- type: markdown
@@ -44,6 +45,28 @@ body:
validations:
required: true
- type: dropdown
id: compatibility-status
attributes:
label: Compatibility status
description: Select the furthest point the game consistently reaches on this build.
options:
- "Doesn't boot"
- Logo
- Main menu
- In game
validations:
required: true
- type: markdown
attributes:
value: |
**Status guide**
- **Doesn't boot:** The game does not show its first logo or startup screen.
- **Logo:** The game shows a logo or startup screen but does not reach the main menu.
- **Main menu:** The game reaches its menus but does not enter gameplay.
- **In game:** The game reaches controllable gameplay, even if bugs prevent completion.
- type: input
id: regression-builds
attributes:
@@ -54,22 +77,22 @@ body:
- type: markdown
attributes:
value: |
## Problem details
## Test result
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Describe the failure or incorrect behavior as directly as possible.
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue."
label: Result details
description: Describe what worked, what failed, and any visible or audible problems.
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue. Audio plays correctly until the freeze."
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: List the shortest path that triggers the problem from a fresh KytyPS5 start.
label: Steps to reproduce the result
description: List the shortest path to the reported status or failure from a fresh KytyPS5 start.
placeholder: |
1. Open KytyPS5.
2. Boot the game.
@@ -81,7 +104,7 @@ body:
id: expected-behavior
attributes:
label: Expected behavior
description: Explain what should happen at the same point in the game.
description: Explain what should happen at the same point in the game. If there is no known problem, write "No known issue".
placeholder: "Example: The save should load and gameplay should continue normally."
validations:
required: true
+3
View File
@@ -31,6 +31,9 @@ macOS support is experimental. The emulator is built for x86-64 and runs on Appl
Rosetta 2, with Vulkan provided by MoltenVK. A small number of titles have been verified in-game
on Apple Silicon hardware; see [Building on macOS](#building-on-macos).
Community game test results are available in the
[KytyPS5 Compatibility List](https://kytyps5.github.io/).
## Bugs and Issues
The project is in an early stage, so please be mindful when opening new issues. Expect crashes,
@@ -183,6 +183,10 @@ static void HwCtxIgnoreAaMaskRegister([[maybe_unused]] uint32_t cmd_offset,
static void HwCtxIgnoreAlphaToMaskRegister([[maybe_unused]] uint32_t value) {}
static void HwCtxIgnoreDisabledUserClipPlane(CommandProcessor& cp, uint32_t value) {
EXIT_NOT_IMPLEMENTED(value != 0 || cp.GetCtx().GetClipControl().user_clip_planes != 0);
}
static void HwCtxIgnoreDrawPayloadControl([[maybe_unused]] uint32_t value) {}
static void HwCtxIgnoreObjprimIdControl([[maybe_unused]] uint32_t value) {}
@@ -3343,6 +3347,12 @@ void GraphicsInitJmpTablesCxIndirect() {
};
}
for (auto cmd_offset = Pm4::PA_CL_UCP_0_X; cmd_offset <= Pm4::PA_CL_UCP_5_W; cmd_offset++) {
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDisabledUserClipPlane(cp, value);
};
}
for (auto cmd_offset = Pm4::PA_SC_VPORT_SCISSOR_0_TL;
cmd_offset <= Pm4::PA_SC_VPORT_SCISSOR_15_TL; cmd_offset += 2) {
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
@@ -4441,6 +4451,11 @@ void GraphicsInitJmpTablesUcIndirect() {
r.vertex_group_size = KYTY_PM4_GET(value, GE_CNTL, VERT_GRP_SIZE);
cp.GetUcfg().SetGeControl(r);
};
g_hw_uc_indirect_func[Pm4::GE_PC_ALLOC] = [](KYTY_HW_UC_INDIRECT_ARGS) {
(void)cp;
(void)cmd_offset;
(void)value;
};
g_hw_uc_indirect_func[Pm4::GE_USER_VGPR_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) {
HW::GeUserVgprEn r;
-1
View File
@@ -473,7 +473,6 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
if (num_bytes == 0) {
return;
}
EXIT_NOT_IMPLEMENTED((num_bytes & 3u) != 0);
EXIT_NOT_IMPLEMENTED(dst_cache_policy > 3);
EXIT_NOT_IMPLEMENTED(src_cache_policy > 3);
EXIT_NOT_IMPLEMENTED(wait_for_previous > 1);
+2
View File
@@ -372,6 +372,7 @@ constexpr uint32_t PA_CL_UCP_0_X = 0x16F;
constexpr uint32_t PA_CL_UCP_0_Y = 0x170;
constexpr uint32_t PA_CL_UCP_0_Z = 0x171;
constexpr uint32_t PA_CL_UCP_0_W = 0x172;
constexpr uint32_t PA_CL_UCP_5_W = 0x186;
constexpr uint32_t SPI_PS_INPUT_CNTL_0 = 0x191;
constexpr uint32_t SPI_PS_INPUT_CNTL_31 = 0x1B0;
constexpr uint32_t SPI_VS_OUT_CONFIG = 0x1B1;
@@ -1047,6 +1048,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C;
constexpr uint32_t GE_USER_VGPR2 = 0x25D;
constexpr uint32_t GE_USER_VGPR3 = 0x25E;
constexpr uint32_t GE_STEREO_CNTL = 0x25F;
constexpr uint32_t GE_PC_ALLOC = 0x260;
constexpr uint32_t GE_USER_VGPR_EN = 0x262;
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0;
+2 -2
View File
@@ -722,8 +722,8 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
const bool dst_memory = !dst_gds;
const bool src_memory = !src_gds;
if ((dst_memory && dst_vaddr == 0) || (src_memory && src_vaddr == 0) || size == 0 ||
((dst_vaddr | src_vaddr | size) & 3u) != 0 || size > UINT64_MAX - dst_vaddr ||
size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
((dst_gds || src_gds) && ((dst_vaddr | src_vaddr | size) & 3u) != 0) ||
size > UINT64_MAX - dst_vaddr || size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
(dst_gds == src_gds && src_vaddr < dst_vaddr + size && dst_vaddr < src_vaddr + size) ||
(dst_gds && (dst_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - dst_vaddr)) ||
(src_gds && (src_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - src_vaddr))) {
@@ -205,8 +205,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
if (z.depth_view.slice_max >= 32) {
DepthFatal("HTile clear tracking supports at most 32 slices");
}
} else if (z.htile_data_base_addr != 0) {
DepthFatal("HTile address without an enabled tile surface");
}
const bool size_xy_valid = z.size.valid;
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
@@ -953,11 +953,17 @@ void CreatePipelineInternal(
vk::DynamicState::eStencilReference,
vk::DynamicState::eStencilWriteMask,
#if !defined(__APPLE__)
// Keep last so depth-only pipelines can omit this dynamic state.
vk::DynamicState::eColorWriteEnableEXT, // unsupported by MoltenVK; static mask instead
#endif
};
const auto dynamic_states_count =
auto dynamic_states_count =
static_cast<uint32_t>(sizeof(dynamic_states) / sizeof(dynamic_states[0]));
#if !defined(__APPLE__)
if (static_params.color_count == 0) {
dynamic_states_count--;
}
#endif
vk::PipelineDynamicStateCreateInfo dynamic_state {};
dynamic_state.sType = vk::StructureType::ePipelineDynamicStateCreateInfo;
@@ -1,36 +0,0 @@
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
#include "common/common.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <memory>
union SDL_Event;
namespace Libs::Graphics {
struct GraphicContext;
void InitializeImeDialogInput();
void ShutdownImeDialogInput();
bool ProcessImeDialogInput(const SDL_Event& event);
class ImeDialogOverlay final {
public:
explicit ImeDialogOverlay(GraphicContext& graphics);
~ImeDialogOverlay();
KYTY_CLASS_NO_COPY(ImeDialogOverlay);
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
void Record(vk::CommandBuffer command, vk::ImageView target);
void ReleaseVulkan();
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace Libs::Graphics
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
@@ -1,4 +1,4 @@
#include "graphics/presentation/imeDialogOverlay.h"
#include "graphics/presentation/imeOverlay.h"
#include "SDL.h"
#include "common/assert.h"
@@ -7,6 +7,7 @@
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include "libs/controller.h"
#include "libs/ime.h"
#include "libs/imeDialog.h"
#include <algorithm>
@@ -26,7 +27,181 @@ namespace Libs::Graphics {
namespace {
namespace Ime = Libs::Dialog::ImeDialog;
namespace CoreIme = Libs::Ime;
namespace DialogIme = Libs::Dialog::ImeDialog;
namespace Ime {
enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 };
enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 };
enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 };
enum class ExternalAction : uint8_t {
None,
Text,
Backspace,
MoveLeft,
MoveRight,
Cancel,
Accept,
Newline,
};
struct Keycode {
uint16_t keycode = 0;
char16_t character = u'\0';
uint32_t status = 0;
uint32_t type = 0;
int32_t user_id = 0;
uint32_t resource_id = 0;
uint64_t timestamp = 0;
};
struct ExternalInput {
Keycode key;
ExternalAction action = ExternalAction::None;
std::u16string text;
};
struct HostSnapshot {
uint64_t generation = 0;
Type type = Type::Default;
EnterLabel enter_label = EnterLabel::Default;
uint32_t option = 0;
uint32_t max_text_length = 0;
uint32_t cursor = 0;
uint32_t disable_device = 0;
bool key_panel_visible = true;
float posx = 0.0f;
float posy = 0.0f;
Alignment horizontal_alignment = Alignment::Start;
Alignment vertical_alignment = Alignment::Start;
uint32_t panel_width = 0;
uint32_t panel_height = 0;
std::u16string text;
std::u16string title;
std::u16string placeholder;
};
constexpr uint32_t OPTION_MULTILINE = 0x00000001;
constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = 0x00000002;
constexpr uint32_t OPTION_PASSWORD = 0x00000004;
constexpr uint32_t OPTION_FIXED_POSITION = 0x00000040;
constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = 0x00000800;
constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000;
constexpr uint32_t DISABLE_DEVICE_CONTROLLER = 0x00000001;
constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002;
constexpr uint64_t CORE_GENERATION_BIT = uint64_t {1} << 63;
uint64_t PackCoreGeneration(uint64_t generation) {
return generation | CORE_GENERATION_BIT;
}
bool IsCoreGeneration(uint64_t generation) {
return (generation & CORE_GENERATION_BIT) != 0;
}
uint64_t UnpackGeneration(uint64_t generation) {
return generation & ~CORE_GENERATION_BIT;
}
bool GetHostSnapshot(HostSnapshot* snapshot) {
if (snapshot == nullptr) {
return false;
}
CoreIme::HostSnapshot core;
if (CoreIme::GetHostSnapshot(&core)) {
snapshot->generation = PackCoreGeneration(core.generation);
snapshot->type = static_cast<Type>(core.type);
snapshot->enter_label = static_cast<EnterLabel>(core.enter_label);
snapshot->option = core.option;
snapshot->max_text_length = core.max_text_length;
snapshot->cursor = core.cursor;
snapshot->disable_device = core.disable_device;
snapshot->key_panel_visible = core.key_panel_visible;
snapshot->posx = core.posx;
snapshot->posy = core.posy;
snapshot->horizontal_alignment = static_cast<Alignment>(core.horizontal_alignment);
snapshot->vertical_alignment = static_cast<Alignment>(core.vertical_alignment);
snapshot->panel_width = core.panel_width;
snapshot->panel_height = core.panel_height;
snapshot->text = std::move(core.text);
snapshot->title.clear();
snapshot->placeholder.clear();
return true;
}
DialogIme::HostSnapshot dialog;
if (!DialogIme::GetHostSnapshot(&dialog)) {
return false;
}
snapshot->generation = dialog.generation;
snapshot->type = static_cast<Type>(dialog.type);
snapshot->enter_label = static_cast<EnterLabel>(dialog.enter_label);
snapshot->option = dialog.option;
snapshot->max_text_length = dialog.max_text_length;
snapshot->cursor = dialog.cursor;
snapshot->disable_device = dialog.disable_device;
snapshot->key_panel_visible = dialog.key_panel_visible;
snapshot->posx = dialog.posx;
snapshot->posy = dialog.posy;
snapshot->horizontal_alignment = static_cast<Alignment>(dialog.horizontal_alignment);
snapshot->vertical_alignment = static_cast<Alignment>(dialog.vertical_alignment);
snapshot->panel_width = dialog.panel_width;
snapshot->panel_height = dialog.panel_height;
snapshot->text = std::move(dialog.text);
snapshot->title = std::move(dialog.title);
snapshot->placeholder = std::move(dialog.placeholder);
return true;
}
bool HostInsertText(uint64_t generation, std::u16string_view text) {
return IsCoreGeneration(generation)
? CoreIme::HostInsertText(UnpackGeneration(generation), text)
: DialogIme::HostInsertText(generation, text);
}
bool HostBackspace(uint64_t generation) {
return IsCoreGeneration(generation) ? CoreIme::HostBackspace(UnpackGeneration(generation))
: DialogIme::HostBackspace(generation);
}
bool HostAccept(uint64_t generation) {
return IsCoreGeneration(generation) ? CoreIme::HostAccept(UnpackGeneration(generation))
: DialogIme::HostAccept(generation);
}
bool HostCancel(uint64_t generation) {
return IsCoreGeneration(generation) ? CoreIme::HostCancel(UnpackGeneration(generation))
: DialogIme::HostCancel(generation);
}
bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
if (!IsCoreGeneration(generation)) {
DialogIme::ExternalInput dialog;
dialog.key.keycode = input.key.keycode;
dialog.key.character = input.key.character;
dialog.key.status = input.key.status;
dialog.key.type = input.key.type;
dialog.key.user_id = input.key.user_id;
dialog.key.resource_id = input.key.resource_id;
dialog.key.timestamp = input.key.timestamp;
dialog.action = static_cast<DialogIme::ExternalAction>(input.action);
dialog.text = std::move(input.text);
return DialogIme::HostQueueExternalInput(generation, std::move(dialog));
}
CoreIme::ExternalInput core;
core.key.keycode = input.key.keycode;
core.key.character = input.key.character;
core.key.status = input.key.status;
core.key.type = input.key.type;
core.key.user_id = input.key.user_id;
core.key.resource_id = input.key.resource_id;
core.key.timestamp = input.key.timestamp;
core.action = static_cast<CoreIme::ExternalAction>(input.action);
core.text = std::move(input.text);
return CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(core));
}
} // namespace Ime
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
@@ -114,28 +289,25 @@ void RetryVisibilityWakeup() {
}
}
void OnVisibilityChanged(bool visible, uint64_t generation) {
void RefreshVisibility() {
std::scoped_lock visibility_lock(g_visibility_mutex);
if (!g_input_lifecycle_active) {
return;
}
if (generation < g_generation || (generation == g_generation && visible == g_active)) {
Ime::HostSnapshot snapshot;
const bool visible = Ime::GetHostSnapshot(&snapshot);
const uint64_t generation = visible ? snapshot.generation : g_generation;
if (visible == g_active && (!visible || generation == g_generation)) {
return;
}
g_generation = generation;
bool capture_controller = false;
bool capture_keyboard = false;
bool multiline = false;
if (visible) {
Ime::HostSnapshot snapshot;
if (Ime::GetHostSnapshot(&snapshot) && snapshot.generation == generation) {
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
} else {
visible = false;
}
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
}
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
if (capture_controller || was_controller) {
@@ -156,6 +328,14 @@ void OnVisibilityChanged(bool visible, uint64_t generation) {
}
}
void OnCoreVisibilityChanged(bool, uint64_t) {
RefreshVisibility();
}
void OnDialogVisibilityChanged(bool, uint64_t) {
RefreshVisibility();
}
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
@@ -305,7 +485,7 @@ void CheckVulkanResult(VkResult result) {
} // namespace
void InitializeImeDialogInput() {
void InitializeImeInput() {
const Uint32 type = SDL_RegisterEvents(1);
EXIT_IF(type == static_cast<Uint32>(-1));
{
@@ -313,15 +493,14 @@ void InitializeImeDialogInput() {
g_input_lifecycle_active = true;
g_visibility_event.store(type, std::memory_order_release);
}
Ime::SetVisibilityCallback(OnVisibilityChanged);
Ime::HostSnapshot snapshot;
if (Ime::GetHostSnapshot(&snapshot)) {
OnVisibilityChanged(true, snapshot.generation);
}
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
RefreshVisibility();
}
void ShutdownImeDialogInput() {
Ime::SetVisibilityCallback(nullptr);
void ShutdownImeInput() {
CoreIme::SetVisibilityCallback(nullptr);
DialogIme::SetVisibilityCallback(nullptr);
{
std::scoped_lock lock(g_visibility_mutex);
g_input_lifecycle_active = false;
@@ -346,7 +525,13 @@ void ShutdownImeDialogInput() {
}
}
bool ProcessImeDialogInput(const SDL_Event& event) {
ImeVisualState GetImeVisualState() noexcept {
const auto core = CoreIme::GetVisualState();
const auto dialog = DialogIme::GetVisualState();
return {core.active || dialog.active, core.revision + dialog.revision};
}
bool ProcessImeInput(const SDL_Event& event) {
RetryVisibilityWakeup();
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
VisibilityUpdate update {};
@@ -465,7 +650,7 @@ bool ProcessImeDialogInput(const SDL_Event& event) {
}
}
struct ImeDialogOverlay::Impl {
struct ImeOverlay::Impl {
explicit Impl(GraphicContext& context): graphics(context) {}
~Impl() {
@@ -488,7 +673,7 @@ struct ImeDialogOverlay::Impl {
io.LogFilename = nullptr;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.BackendPlatformName = "Kyty ImeDialog input";
io.BackendPlatformName = "Kyty IME input";
ImGui::StyleColorsDark();
auto& style = ImGui::GetStyle();
style.WindowRounding = 10.0f;
@@ -680,7 +865,7 @@ struct ImeDialogOverlay::Impl {
ImGui::SetNextWindowSize({width, height}, ImGuiCond_Always);
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings;
ImGui::Begin("##ImeDialog", nullptr, flags);
ImGui::Begin("##Ime", nullptr, flags);
const std::u16string title = snapshot.title.empty() ? u"Enter text" : snapshot.title;
if (!snapshot.key_panel_visible) {
@@ -856,20 +1041,19 @@ struct ImeDialogOverlay::Impl {
std::chrono::steady_clock::time_point last_frame;
};
ImeDialogOverlay::ImeDialogOverlay(GraphicContext& graphics)
: m_impl(std::make_unique<Impl>(graphics)) {}
ImeOverlay::ImeOverlay(GraphicContext& graphics): m_impl(std::make_unique<Impl>(graphics)) {}
ImeDialogOverlay::~ImeDialogOverlay() = default;
ImeOverlay::~ImeOverlay() = default;
bool ImeDialogOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
bool ImeOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
return m_impl->PrepareFrame(extent, format, image_count);
}
void ImeDialogOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
void ImeOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
m_impl->Record(command, target);
}
void ImeDialogOverlay::ReleaseVulkan() {
void ImeOverlay::ReleaseVulkan() {
m_impl->ReleaseVulkan();
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
#include "common/common.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <memory>
union SDL_Event;
namespace Libs::Graphics {
struct GraphicContext;
struct ImeVisualState {
bool active;
uint64_t revision;
};
void InitializeImeInput();
void ShutdownImeInput();
bool ProcessImeInput(const SDL_Event& event);
ImeVisualState GetImeVisualState() noexcept;
class ImeOverlay final {
public:
explicit ImeOverlay(GraphicContext& graphics);
~ImeOverlay();
KYTY_CLASS_NO_COPY(ImeOverlay);
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
void Record(vk::CommandBuffer command, vk::ImageView target);
void ReleaseVulkan();
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace Libs::Graphics
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
+84 -35
View File
@@ -18,35 +18,57 @@ namespace Libs::Graphics {
namespace {
struct ControlInfo {
std::string_view name;
uint32_t button = 0;
Controller::Axis axis = Controller::Axis::AxisMax;
bool positive = false;
};
static constexpr std::array CONTROL_INFO = {
ControlInfo {"L3", Controller::PAD_BUTTON_L3},
ControlInfo {"R3", Controller::PAD_BUTTON_R3},
ControlInfo {"Options", Controller::PAD_BUTTON_OPTIONS},
ControlInfo {"Up", Controller::PAD_BUTTON_UP},
ControlInfo {"Right", Controller::PAD_BUTTON_RIGHT},
ControlInfo {"Down", Controller::PAD_BUTTON_DOWN},
ControlInfo {"Left", Controller::PAD_BUTTON_LEFT},
ControlInfo {"L2", Controller::PAD_BUTTON_L2},
ControlInfo {"R2", Controller::PAD_BUTTON_R2},
ControlInfo {"L1", Controller::PAD_BUTTON_L1},
ControlInfo {"R1", Controller::PAD_BUTTON_R1},
ControlInfo {"Triangle", Controller::PAD_BUTTON_TRIANGLE},
ControlInfo {"Circle", Controller::PAD_BUTTON_CIRCLE},
ControlInfo {"Cross", Controller::PAD_BUTTON_CROSS},
ControlInfo {"Square", Controller::PAD_BUTTON_SQUARE},
ControlInfo {"TouchPad", Controller::PAD_BUTTON_TOUCH_PAD},
ControlInfo {"LeftStickLeft", 0, Controller::Axis::LeftX, false},
ControlInfo {"LeftStickRight", 0, Controller::Axis::LeftX, true},
ControlInfo {"LeftStickUp", 0, Controller::Axis::LeftY, false},
ControlInfo {"LeftStickDown", 0, Controller::Axis::LeftY, true},
ControlInfo {"RightStickLeft", 0, Controller::Axis::RightX, false},
ControlInfo {"RightStickRight", 0, Controller::Axis::RightX, true},
ControlInfo {"RightStickUp", 0, Controller::Axis::RightY, false},
ControlInfo {"RightStickDown", 0, Controller::Axis::RightY, true},
};
constexpr std::size_t INVALID_CONTROL = CONTROL_INFO.size();
struct Binding {
SDL_Keycode key = SDLK_UNKNOWN;
uint8_t mouse_button = 0;
uint32_t pad_button = 0;
std::size_t control = INVALID_CONTROL;
};
static constexpr std::array PAD_BUTTONS = {
std::pair {std::string_view("L3"), Controller::PAD_BUTTON_L3},
std::pair {std::string_view("R3"), Controller::PAD_BUTTON_R3},
std::pair {std::string_view("Options"), Controller::PAD_BUTTON_OPTIONS},
std::pair {std::string_view("Up"), Controller::PAD_BUTTON_UP},
std::pair {std::string_view("Right"), Controller::PAD_BUTTON_RIGHT},
std::pair {std::string_view("Down"), Controller::PAD_BUTTON_DOWN},
std::pair {std::string_view("Left"), Controller::PAD_BUTTON_LEFT},
std::pair {std::string_view("L2"), Controller::PAD_BUTTON_L2},
std::pair {std::string_view("R2"), Controller::PAD_BUTTON_R2},
std::pair {std::string_view("L1"), Controller::PAD_BUTTON_L1},
std::pair {std::string_view("R1"), Controller::PAD_BUTTON_R1},
std::pair {std::string_view("Triangle"), Controller::PAD_BUTTON_TRIANGLE},
std::pair {std::string_view("Circle"), Controller::PAD_BUTTON_CIRCLE},
std::pair {std::string_view("Cross"), Controller::PAD_BUTTON_CROSS},
std::pair {std::string_view("Square"), Controller::PAD_BUTTON_SQUARE},
std::pair {std::string_view("TouchPad"), Controller::PAD_BUTTON_TOUCH_PAD},
};
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; });
return static_cast<std::size_t>(std::distance(CONTROL_INFO.begin(), info));
}
uint32_t PadButtonFromName(std::string_view name) {
const auto button = std::find_if(PAD_BUTTONS.begin(), PAD_BUTTONS.end(),
[name](const auto& item) { return item.first == name; });
return button != PAD_BUTTONS.end() ? button->second : 0;
const ControlInfo& Info(std::size_t control) {
EXIT_IF(control >= CONTROL_INFO.size());
return CONTROL_INFO[control];
}
SDL_Keycode NormalizeKey(SDL_Keycode key) {
@@ -74,7 +96,7 @@ uint8_t MouseButtonFromName(std::string_view name) {
}
bool Conflicts(const Binding& first, const Binding& second) {
return first.pad_button == second.pad_button ||
return first.control == second.control ||
(first.key != SDLK_UNKNOWN && first.key == second.key) ||
(first.mouse_button != 0 && first.mouse_button == second.mouse_button);
}
@@ -88,7 +110,7 @@ public:
Binding binding;
if (split != std::string_view::npos) {
binding.pad_button = PadButtonFromName(entry.substr(0, split));
binding.control = ControlFromName(entry.substr(0, split));
const auto host_input = entry.substr(split + 1);
binding.mouse_button = MouseButtonFromName(host_input);
if (binding.mouse_button == 0) {
@@ -98,7 +120,7 @@ public:
const bool reserved =
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1;
if (binding.pad_button == 0 || reserved ||
if (binding.control == INVALID_CONTROL || reserved ||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
EXIT("Invalid input mapping: %s\n", value.c_str());
}
@@ -107,19 +129,19 @@ public:
}
[[nodiscard]] bool Custom() const { return m_custom; }
[[nodiscard]] uint32_t FindKey(int key_code) const {
[[nodiscard]] std::size_t FindKey(int key_code) const {
key_code = NormalizeKey(static_cast<SDL_Keycode>(key_code));
const auto binding =
std::find_if(m_bindings.begin(), m_bindings.begin() + m_size,
[key_code](const auto& item) { return item.key == key_code; });
return binding != m_bindings.begin() + m_size ? binding->pad_button : 0;
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
}
[[nodiscard]] uint32_t FindMouseButton(uint8_t mouse_button) const {
[[nodiscard]] std::size_t FindMouseButton(uint8_t mouse_button) const {
const auto binding = std::find_if(
m_bindings.begin(), m_bindings.begin() + m_size,
[mouse_button](const auto& item) { return item.mouse_button == mouse_button; });
return binding != m_bindings.begin() + m_size ? binding->pad_button : 0;
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
}
private:
@@ -135,9 +157,9 @@ private:
m_bindings[m_size++] = binding;
}
std::array<Binding, PAD_BUTTONS.size()> m_bindings {};
std::size_t m_size = 0;
bool m_custom;
std::array<Binding, CONTROL_INFO.size()> m_bindings {};
std::size_t m_size = 0;
bool m_custom;
};
const InputMap& GetInputMap() {
@@ -191,6 +213,33 @@ void SetStickAxis(Controller::Axis axis, bool negative, bool positive) {
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID, axis, value);
}
void SetControl(std::size_t control, bool down) {
if (control == INVALID_CONTROL) {
return;
}
const auto& info = Info(control);
if (info.button != 0) {
SetButton(info.button, down);
return;
}
struct AxisKeys {
bool negative = false;
bool positive = false;
};
static std::array<AxisKeys, 4> axes;
const auto axis = static_cast<std::size_t>(info.axis);
EXIT_IF(axis >= axes.size());
if (info.positive) {
axes[axis].positive = down;
} else {
axes[axis].negative = down;
}
SetStickAxis(info.axis, axes[axis].negative, axes[axis].positive);
}
void DefaultKeyboardInput(int key_code, bool down) {
static StickKeys left;
static StickKeys right;
@@ -240,7 +289,7 @@ void HostInputInit() {
void HostInputKey(int key_code, bool down) {
const auto& map = GetInputMap();
if (map.Custom()) {
SetButton(map.FindKey(key_code), down);
SetControl(map.FindKey(key_code), down);
} else {
DefaultKeyboardInput(key_code, down);
}
@@ -249,7 +298,7 @@ void HostInputKey(int key_code, bool down) {
void HostInputMouseButton(uint8_t mouse_button, bool down) {
const auto& map = GetInputMap();
if (map.Custom() && mouse_button != 0) {
SetButton(map.FindMouseButton(mouse_button), down);
SetControl(map.FindMouseButton(mouse_button), down);
}
}
+15 -16
View File
@@ -31,13 +31,12 @@
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/presentation/imeDialogOverlay.h"
#include "graphics/presentation/imeOverlay.h"
#include "graphics/presentation/presenter.h"
#include "graphics/presentation/renderDoc.h"
#include "graphics/presentation/videoOut.h"
#include "graphics/presentation/window/windowInternal.h"
#include "libs/controller.h"
#include "libs/imeDialog.h"
#include "loader/systemContent.h"
#include <algorithm>
@@ -345,17 +344,17 @@ private:
void Destroy();
void RefreshSurfaceSize();
WindowContext& m_window;
vk::SwapchainKHR m_handle = nullptr;
vk::Format m_format = vk::Format::eUndefined;
vk::Extent2D m_extent {};
std::vector<vk::Image> m_images;
std::vector<vk::ImageView> m_image_views;
std::vector<vk::Semaphore> m_image_acquired;
std::vector<vk::Semaphore> m_render_complete;
std::unique_ptr<ImeDialogOverlay> m_ime_overlay;
uint32_t m_image_index = static_cast<uint32_t>(-1);
uint32_t m_frame_index = 0;
WindowContext& m_window;
vk::SwapchainKHR m_handle = nullptr;
vk::Format m_format = vk::Format::eUndefined;
vk::Extent2D m_extent {};
std::vector<vk::Image> m_images;
std::vector<vk::ImageView> m_image_views;
std::vector<vk::Semaphore> m_image_acquired;
std::vector<vk::Semaphore> m_render_complete;
std::unique_ptr<ImeOverlay> m_ime_overlay;
uint32_t m_image_index = static_cast<uint32_t>(-1);
uint32_t m_frame_index = 0;
};
struct Presenter::Impl {
@@ -618,7 +617,7 @@ Swapchain::Status Swapchain::AcquireNextImage() {
bool Swapchain::PrepareImeOverlay() {
if (m_ime_overlay == nullptr) {
m_ime_overlay = std::make_unique<ImeDialogOverlay>(m_window.graphic_ctx);
m_ime_overlay = std::make_unique<ImeOverlay>(m_window.graphic_ctx);
}
return m_ime_overlay->PrepareFrame(m_extent, m_format, ImageCount());
}
@@ -807,7 +806,7 @@ bool Presenter::IsGuestPaused() const noexcept {
}
bool Presenter::NeedsImeRefresh() const noexcept {
const auto visual = Dialog::ImeDialog::GetVisualState();
const auto visual = GetImeVisualState();
return visual.active ||
visual.revision != m_impl->presented_ime_revision.load(std::memory_order_acquire);
}
@@ -843,7 +842,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
m_impl->RecoverSwapchain(Swapchain::Status::Recreate);
}
const auto ime_visual = Dialog::ImeDialog::GetVisualState();
const auto ime_visual = GetImeVisualState();
auto& swapchain = m_impl->swapchain;
for (uint32_t attempt = 0; attempt < 2; attempt++) {
auto status = swapchain.AcquireNextImage();
@@ -31,7 +31,7 @@
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/presentation/imeDialogOverlay.h"
#include "graphics/presentation/imeOverlay.h"
#include "graphics/presentation/presenter.h"
#include "graphics/presentation/renderDoc.h"
#include "graphics/presentation/videoOut.h"
@@ -607,10 +607,6 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
create_info.flags = {};
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;
create_info.enabledLayerCount =
(r.enable_validation_layers ? static_cast<uint32_t>(r.required_layers.size()) : 0);
create_info.ppEnabledLayerNames =
(r.enable_validation_layers ? r.required_layers.data() : nullptr);
create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
create_info.ppEnabledExtensionNames = device_extensions.data();
create_info.pEnabledFeatures = &device_features;
@@ -1015,7 +1011,7 @@ void WindowContext::RecreateSurface() {
}
WindowContext::~WindowContext() {
ShutdownImeDialogInput();
ShutdownImeInput();
presenter.reset();
LibKernel::Memory::InstallGpuResources(nullptr);
render_context.reset();
+8 -9
View File
@@ -31,7 +31,7 @@
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/presentation/imeDialogOverlay.h"
#include "graphics/presentation/imeOverlay.h"
#include "graphics/presentation/renderDoc.h"
#include "graphics/presentation/window/hostInput.h"
#include "graphics/presentation/window/windowInternal.h"
@@ -477,7 +477,7 @@ void WindowContext::ProcessEvent(double time_s) {
auto& game = loop;
auto* event = &game.event;
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
if (ProcessImeDialogInput(*event)) {
if (ProcessImeInput(*event)) {
return;
}
@@ -783,7 +783,7 @@ static void WindowCreate(WindowContext& context) {
EXIT("%s\n", SDL_GetError());
}
HostInputInit();
InitializeImeDialogInput();
InitializeImeInput();
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
@@ -938,7 +938,6 @@ void WindowContext::UpdateTitle() {
static constexpr auto build_type = "Unknown";
#endif
const auto now = Common::Timer::QueryPerformanceCounter();
const auto frequency = Common::Timer::QueryPerformanceFrequency();
frame_num++;
@@ -950,11 +949,11 @@ void WindowContext::UpdateTitle() {
fps_frames = 0;
}
auto fps = fmt::format("[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL,
build_type, (has_title ? title : ""), (has_title ? ", " : ""),
(has_title_id ? title_id : ""), (has_title_id ? ", " : ""),
(has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""), device_name,
processor_name, frame_num, current_fps);
auto fps = fmt::format(
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type,
(has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""),
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
device_name, processor_name, frame_num, current_fps);
#if defined(__APPLE__)
// AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing.
+5
View File
@@ -119,6 +119,11 @@ int WaitLinux(volatile T* address, T expected, const uint32_t* timeout_micros,
}
int WakeLinux(volatile void* address, int32_t count) {
// The legacy FUTEX_WAKE path may wake one waiter when count is zero.
if (count == 0) {
return OK;
}
const auto result = syscall(SYS_futex, const_cast<void*>(address), FUTEX_WAKE_PRIVATE, count,
nullptr, nullptr, 0);
return result < 0 ? KERNEL_ERROR_EINVAL : OK;
+4 -5
View File
@@ -21,11 +21,10 @@
namespace {
constexpr char FILE_NAME[] = "compatibility_db.json";
constexpr char URL[] =
"https://github.com/Nmzik/KytyPS5/releases/download/compat-db/compatibility_db.json";
constexpr int RETRY_COUNT = 3;
constexpr int RETRY_DELAY_MS = 750;
constexpr char FILE_NAME[] = "compatibility_db.json";
constexpr char URL[] = "https://kytyps5.github.io/data/compatibility.json";
constexpr int RETRY_COUNT = 3;
constexpr int RETRY_DELAY_MS = 750;
struct LoadResult {
QMap<QString, CompatibilityEntry> entries;
+19 -13
View File
@@ -18,17 +18,25 @@ namespace {
constexpr int BINDING_COLUMN = 1;
struct PadButton {
struct PadControl {
const char* id;
const char* label;
const char* default_binding;
};
constexpr PadButton PAD_BUTTONS[] = {
constexpr PadControl PAD_CONTROLS[] = {
{"Up", "D-pad Up", "Up"},
{"Down", "D-pad Down", "Down"},
{"Left", "D-pad Left", "Left"},
{"Right", "D-pad Right", "Right"},
{"LeftStickUp", "Left stick Up", "W"},
{"LeftStickDown", "Left stick Down", "S"},
{"LeftStickLeft", "Left stick Left", "A"},
{"LeftStickRight", "Left stick Right", "D"},
{"RightStickUp", "Right stick Up", "T"},
{"RightStickDown", "Right stick Down", "G"},
{"RightStickLeft", "Right stick Left", "F"},
{"RightStickRight", "Right stick Right", "H"},
{"Triangle", "Triangle", "I"},
{"Circle", "Circle", "L"},
{"Cross", "Cross", "J"},
@@ -190,16 +198,14 @@ QHash<QString, QString> ParseMapping(const QStringList& mapping) {
InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* parent)
: QDialog(parent), m_customized(!mapping.isEmpty()) {
setWindowTitle(tr("Input Mapping"));
resize(440, 520);
resize(460, 650);
auto* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("Map keyboard or mouse input to DualSense buttons.\n"
"Defaults: left stick W/A/S/D, right stick T/F/G/H."),
this));
layout->addWidget(new QLabel(tr("Map keyboard or mouse input to DualSense controls."), this));
m_bindings = new QTreeWidget(this);
m_bindings->setColumnCount(2);
m_bindings->setHeaderLabels({tr("DualSense button"), tr("Host input")});
m_bindings->setHeaderLabels({tr("DualSense control"), tr("Host input")});
m_bindings->setRootIsDecorated(false);
m_bindings->setSelectionMode(QAbstractItemView::SingleSelection);
m_bindings->header()->setSectionResizeMode(0, QHeaderView::Stretch);
@@ -207,12 +213,12 @@ InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* pare
layout->addWidget(m_bindings);
const auto parsed = ParseMapping(mapping);
for (const auto& button: PAD_BUTTONS) {
for (const auto& control: PAD_CONTROLS) {
auto* item = new QTreeWidgetItem(m_bindings);
item->setText(0, tr(button.label));
item->setData(0, Qt::UserRole, QString::fromLatin1(button.id));
SetBinding(item, m_customized ? parsed.value(QString::fromLatin1(button.id))
: QString::fromLatin1(button.default_binding));
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));
}
m_bindings->setCurrentItem(m_bindings->topLevelItem(0));
@@ -286,7 +292,7 @@ void InputMappingDialog::ClearBinding() {
void InputMappingDialog::RestoreDefaults() {
for (int index = 0; index < m_bindings->topLevelItemCount(); index++) {
SetBinding(m_bindings->topLevelItem(index),
QString::fromLatin1(PAD_BUTTONS[index].default_binding));
QString::fromLatin1(PAD_CONTROLS[index].default_binding));
}
m_customized = false;
}
+91 -1
View File
@@ -26,6 +26,7 @@
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <mutex>
@@ -1180,6 +1181,95 @@ int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegiste
return OK;
}
struct GsOccupancyLimits {
uint32_t vertex;
uint32_t export_count;
};
static GsOccupancyLimits get_gs_occupancy_limits(const Shader* gs, uint32_t vertex_capacity,
uint32_t export_capacity) {
const auto* onchip =
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::VGT_GS_ONCHIP_CNTL);
const auto* subgroup =
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::GE_NGG_SUBGRP_CNTL);
const auto* vs_out =
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::SPI_VS_OUT_CONFIG);
const auto* cl_out =
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::PA_CL_VS_OUT_CNTL);
const auto* max_output = find_shader_register(gs->cx_registers, gs->num_cx_registers,
Pm4::GE_MAX_OUTPUT_PER_SUBGROUP);
const uint32_t output_words = ((max_output->value & 0x3ffu) + 31u) >> 5u;
const uint32_t subgroup_work =
((((onchip->value >> 11u) & 0x7ffu) * (subgroup->value & 0x1ffu)) + 31u) >> 5u;
uint32_t waves = std::max(subgroup_work, output_words);
if ((gs->specials->vgt_shader_stages_en.value & 0x00400000u) == 0) {
waves >>= 1u;
}
waves = std::max(waves, 1u);
const uint32_t exports = 1u + ((cl_out->value >> 21u) & 1u) + ((cl_out->value >> 22u) & 1u) +
((cl_out->value >> 23u) & 1u);
const uint32_t export_limit = (export_capacity / exports) * 4u;
const uint32_t vertex_limit = (vs_out->value & 0x80u) != 0
? 2048u
: vertex_capacity / (((vs_out->value >> 2u) & 0xfu) + 1u);
return {waves * (vertex_limit / output_words), waves * (export_limit / output_words)};
}
int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs,
uint32_t budget, float factor) {
PRINT_NAME();
constexpr uint32_t FULL_PC_OVERSUBSCRIPTION = 0x7ffu;
constexpr uint32_t FULL_SH_OVERSUBSCRIPTION = 0x007f0000u;
regs[0] = {Pm4::GE_PC_ALLOC, 0u};
regs[1] = {Pm4::SPI_SHADER_PGM_RSRC4_GS, 0u};
if (budget == 0u) {
return OK;
}
if (budget == UINT32_MAX) {
regs[0].value |= FULL_PC_OVERSUBSCRIPTION;
regs[1].value |= FULL_SH_OVERSUBSCRIPTION;
return OK;
}
const auto base = get_gs_occupancy_limits(gs, 1024u, 128u);
const auto expanded = get_gs_occupancy_limits(gs, 2048u, 382u);
const auto base_min = std::min(base.vertex, base.export_count);
const auto limit_shift =
(gs->specials->vgt_shader_stages_en.value & 0x00400000u) != 0 ? 5u : 6u;
const auto expanded_limit =
std::min({expanded.vertex, expanded.export_count, budget >> limit_shift, 1024u});
const auto headroom = expanded_limit > base_min ? expanded_limit - base_min : 0u;
const auto target = static_cast<uint32_t>(
std::fma(factor, static_cast<float>(headroom), static_cast<float>(base_min)));
if (target <= base_min) {
return OK;
}
if (target < base.export_count) {
const auto range = std::max(expanded.vertex - base.vertex, 1u);
auto value = static_cast<uint32_t>(
std::min<uint64_t>((static_cast<uint64_t>(target - base.vertex) << 10u) / range, 1024));
value = std::max(value, 1u);
regs[0].value = (regs[0].value & ~FULL_PC_OVERSUBSCRIPTION) |
(((value << 1u) - 1u) & FULL_PC_OVERSUBSCRIPTION);
regs[1].value |= FULL_SH_OVERSUBSCRIPTION;
} else {
const auto range = std::max(expanded.export_count - base.export_count, 1u);
const auto value = static_cast<uint32_t>(
std::min<uint64_t>((static_cast<uint64_t>(target - base.export_count) * 127u) / range, 127));
regs[0].value |= FULL_PC_OVERSUBSCRIPTION;
regs[1].value = (regs[1].value & ~FULL_SH_OVERSUBSCRIPTION) | (value << 16u);
}
return OK;
}
static uint32_t shader_semantic_word(const ShaderSemantic& semantic) {
return ((semantic.semantic & 0xffu) << 0u) | ((semantic.hardware_mapping & 0xffu) << 8u) |
((semantic.size_in_elements & 0xfu) << 16u) | ((semantic.is_f16 & 0x3u) << 20u) |
@@ -1466,7 +1556,7 @@ uint32_t* KYTY_SYSV_ABI GraphicsDcbContextStateOp(CommandBuffer* buf, uint32_t o
buf->DbgDump();
uint32_t* first = nullptr;
uint32_t* first = nullptr;
const auto append = [&](uint32_t segment_size_dw) {
auto* segment = buf->AllocateDW(segment_size_dw);
if (segment == nullptr) {
+2
View File
@@ -56,6 +56,8 @@ int KYTY_SYSV_ABI GraphicsCreatePrimState(ShaderRegister* cx_regs, ShaderRegis
const Shader* hs, const Shader* gs, uint32_t prim_type);
int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_regs,
uint32_t prim_type);
int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs,
uint32_t budget, float factor);
int KYTY_SYSV_ABI GraphicsCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs,
const Shader* ps);
int KYTY_SYSV_ABI GraphicsGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type);
+659 -240
View File
File diff suppressed because it is too large. Load diff
+14 -2
View File
@@ -356,6 +356,18 @@ int KYTY_SYSV_ABI PadInit() {
return OK;
}
static bool PadOpenArgsAreValid(int user_id, int type, int index) {
constexpr int user_id_initial = 1000;
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 == user_id_initial && (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 KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
PRINT_NAME();
@@ -367,7 +379,7 @@ int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) {
if (!PadOpenArgsAreValid(user_id, type, index)) {
return pad_error_invalid_arg;
}
@@ -386,7 +398,7 @@ int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index) {
constexpr int pad_error_device_no_handle = -2137915384; /* 0x80920008 */
if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) {
if (!PadOpenArgsAreValid(user_id, type, index)) {
return pad_error_device_no_handle;
}
+1020
View File
File diff suppressed because it is too large. Load diff
+245
View File
@@ -0,0 +1,245 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
#include "common/abi.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace Libs::Ime {
constexpr uint32_t WORK_BUFFER_SIZE = 20 * 1024;
constexpr uint32_t MAX_TEXT_LENGTH = 2048;
enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 };
enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 };
enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 };
struct Event;
struct Keycode;
using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length,
const char16_t* source_text,
uint32_t source_text_length);
using EventHandler = void(KYTY_SYSV_ABI*)(void* arg, const Event* event);
using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode,
uint32_t* out_status, void* reserved);
struct Color {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
struct Caret {
float x;
float y;
uint32_t height;
uint32_t index;
};
struct TextGeometry {
float x;
float y;
uint32_t width;
uint32_t height;
};
struct Rect {
float x;
float y;
uint32_t width;
uint32_t height;
};
enum class TextAreaMode : uint32_t { Disable = 0, Edit = 1, Preedit = 2, Select = 3 };
struct TextAreaProperty {
TextAreaMode mode;
uint32_t index;
int32_t length;
};
struct EditText {
char16_t* str;
uint32_t caret_index;
uint32_t area_num;
TextAreaProperty text_area[4];
};
struct KeyboardResourceIdArray {
int32_t user_id;
uint32_t resource_id[5];
};
struct Keycode {
uint16_t keycode;
char16_t character;
uint32_t status;
uint32_t type;
int32_t user_id;
uint32_t resource_id;
uint64_t timestamp;
};
union EventParam {
Rect rect;
EditText text;
uint32_t caret_move;
Keycode keycode;
KeyboardResourceIdArray resource_id_array;
uint64_t reserved[8];
};
struct Event {
uint32_t id;
EventParam param;
};
struct Param {
int32_t user_id;
Type type;
uint64_t supported_languages;
EnterLabel enter_label;
uint32_t input_method;
TextFilter filter;
uint32_t option;
uint32_t max_text_length;
char16_t* input_text_buffer;
float posx;
float posy;
Alignment horizontal_alignment;
Alignment vertical_alignment;
void* work;
void* arg;
EventHandler handler;
int8_t reserved[8];
};
struct ExtendedParam {
uint32_t option;
Color color_base;
Color color_line;
Color color_text_field;
Color color_preedit;
Color color_button_default;
Color color_button_function;
Color color_button_symbol;
Color color_text;
Color color_special;
uint32_t priority;
const char* additional_dictionary_path;
ExtKeyboardFilter ext_keyboard_filter;
uint32_t disable_device;
uint32_t ext_keyboard_mode;
int8_t reserved[60];
};
struct KeyboardParam {
uint32_t option;
int8_t reserved1[4];
void* arg;
EventHandler handler;
int8_t reserved2[8];
};
struct KeyboardInfo {
int32_t user_id;
uint32_t device;
uint32_t type;
uint32_t repeat_delay;
uint32_t repeat_rate;
uint32_t status;
int8_t reserved[12];
};
enum class ExternalAction : uint8_t {
None,
Text,
Backspace,
MoveLeft,
MoveRight,
Cancel,
Accept,
Newline,
};
struct ExternalInput {
Keycode key;
ExternalAction action;
std::u16string text;
};
static_assert(sizeof(Caret) == 0x10);
static_assert(sizeof(TextGeometry) == 0x10);
static_assert(sizeof(TextAreaProperty) == 0x0c);
static_assert(sizeof(EditText) == 0x40);
static_assert(sizeof(EventParam) == 0x40);
static_assert(alignof(EventParam) == 0x08);
static_assert(sizeof(Event) == 0x48);
static_assert(offsetof(Event, param) == 0x08);
static_assert(sizeof(Param) == 0x60);
static_assert(offsetof(Param, input_text_buffer) == 0x28);
static_assert(offsetof(Param, work) == 0x40);
static_assert(offsetof(Param, handler) == 0x50);
static_assert(sizeof(ExtendedParam) == 0x88);
static_assert(offsetof(ExtendedParam, additional_dictionary_path) == 0x30);
static_assert(sizeof(KeyboardParam) == 0x20);
static_assert(sizeof(KeyboardResourceIdArray) == 0x18);
static_assert(sizeof(KeyboardInfo) == 0x24);
static_assert(sizeof(Keycode) == 0x20);
struct VisualState {
bool active;
uint64_t revision;
};
struct HostSnapshot {
uint64_t generation;
Type type;
EnterLabel enter_label;
uint32_t option;
uint32_t max_text_length;
uint32_t cursor;
uint32_t disable_device;
bool key_panel_visible;
float posx;
float posy;
Alignment horizontal_alignment;
Alignment vertical_alignment;
uint32_t panel_width;
uint32_t panel_height;
std::u16string text;
};
using VisibilityCallback = void (*)(bool visible, uint64_t generation);
void KYTY_SYSV_ABI ImeParamInit(Param* param);
int KYTY_SYSV_ABI ImeGetPanelSize(const Param* param, uint32_t* width, uint32_t* height);
int KYTY_SYSV_ABI ImeOpen(const Param* param, const ExtendedParam* extended);
int KYTY_SYSV_ABI ImeUpdate(EventHandler handler);
int KYTY_SYSV_ABI ImeClose();
int KYTY_SYSV_ABI ImeSetText(const char16_t* text, uint32_t length);
int KYTY_SYSV_ABI ImeSetCaret(const Caret* caret);
int KYTY_SYSV_ABI ImeSetTextGeometry(TextAreaMode mode, const TextGeometry* geometry);
int KYTY_SYSV_ABI ImeKeyboardOpen(int32_t user_id, const KeyboardParam* param);
int KYTY_SYSV_ABI ImeKeyboardClose(int32_t user_id);
int KYTY_SYSV_ABI ImeKeyboardGetResourceId(int32_t user_id, KeyboardResourceIdArray* resource_ids);
int KYTY_SYSV_ABI ImeKeyboardGetInfo(uint32_t resource_id, KeyboardInfo* info);
int KYTY_SYSV_ABI ImeKeyboardSetMode(int32_t user_id, uint32_t mode);
VisualState GetVisualState() noexcept;
void SetVisibilityCallback(VisibilityCallback callback) noexcept;
bool GetHostSnapshot(HostSnapshot* snapshot);
bool HostInsertText(uint64_t generation, std::u16string_view text);
bool HostBackspace(uint64_t generation);
bool HostAccept(uint64_t generation);
bool HostCancel(uint64_t generation);
bool HostQueueExternalInput(uint64_t generation, ExternalInput input);
} // namespace Libs::Ime
#endif // EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
+1
View File
@@ -171,6 +171,7 @@ LIB_DEFINE(InitGraphicsDriver_1) {
LIB_FUNC("vRoArM9zaIk", Gen5::GraphicsSetUcRegIndirectPatchAddRegisters);
LIB_FUNC("D9sr1xGUriE", Gen5::GraphicsCreatePrimState);
LIB_FUNC("Y3ymLfZ1384", Gen5::GraphicsUpdatePrimState);
LIB_FUNC("NKIzURsgV7I", Gen5::GraphicsGetGsOversubscription);
LIB_FUNC("HV4j+E0MBHE", Gen5::GraphicsCreateInterpolantMapping);
LIB_FUNC("V++UgBtQhn0", Gen5::GraphicsGetDataPacketPayloadAddress);
LIB_FUNC("s+VGAMDQ0AQ", Gen5::GraphicsGetDataPacketPayloadRange);
+23 -10
View File
@@ -167,6 +167,19 @@ static JsonObject* JsonObjectNew() {
return object;
}
static void JsonValueSetEmptyType(JsonValue* self, uint32_t type) {
if (self == nullptr) {
return;
}
self->type = type;
switch (type) {
case JsonValueTypeString: self->string = JsonStringNew(""); break;
case JsonValueTypeArray: self->array = JsonArrayNew(); break;
case JsonValueTypeObject: self->object = JsonObjectNew(); break;
default: self->uinteger = 0; break;
}
}
static void JsonValueDelete(JsonValue* self) {
if (self != nullptr) {
JsonValueClear(self);
@@ -497,6 +510,14 @@ static void* KYTY_SYSV_ABI JsonValueCtor(void* self) {
return self;
}
static JsonValue* KYTY_SYSV_ABI JsonValueTypeCtor(JsonValue* self, uint32_t type) {
PRINT_NAME();
JsonValueInit(self);
JsonValueSetEmptyType(self, type);
return self;
}
static JsonValue* KYTY_SYSV_ABI JsonValueBoolCtor(JsonValue* self, bool value) {
PRINT_NAME();
@@ -637,16 +658,7 @@ static void KYTY_SYSV_ABI JsonValueSetType(JsonValue* self, uint32_t type) {
PRINT_NAME();
JsonValueClear(self);
if (self == nullptr) {
return;
}
self->type = type;
switch (type) {
case JsonValueTypeString: self->string = JsonStringNew(""); break;
case JsonValueTypeArray: self->array = JsonArrayNew(); break;
case JsonValueTypeObject: self->object = JsonObjectNew(); break;
default: self->uinteger = 0; break;
}
JsonValueSetEmptyType(self, type);
}
static uint32_t KYTY_SYSV_ABI JsonValueGetType(const JsonValue* self) {
@@ -939,6 +951,7 @@ LIB_DEFINE(InitNet_1_Json2) {
LIB_FUNC("OcAgPxcq5Vk", LibJson2::JsonMemAllocatorDtor);
LIB_FUNC("qBMjqyBn3OM", LibJson2::JsonValueCtor);
LIB_FUNC("-wa17B7TGnw", LibJson2::JsonValueCtor);
LIB_FUNC("CbrT3dwDILo", LibJson2::JsonValueTypeCtor);
LIB_FUNC("WTtYf+cNnXI", LibJson2::JsonValueDtor);
LIB_FUNC("0eUrW9JAxM0", LibJson2::JsonValueDtor);
LIB_FUNC("S5JxQnoGF3E", LibJson2::JsonParserParse);
-72
View File
@@ -3804,77 +3804,6 @@ LIB_DEFINE(InitPlatform_1_Random) {
} // namespace LibRandom
namespace LibIme {
LIB_VERSION("Ime", 1, "Ime", 1, 1);
static int KYTY_SYSV_ABI ImeKeyboardOpen(int user_id, const void* param) {
PRINT_NAME();
LOGF("\t user_id = %d\n"
"\t param = 0x%016" PRIx64 "\n",
user_id, reinterpret_cast<uint64_t>(param));
return 0;
}
static int KYTY_SYSV_ABI ImeKeyboardClose(int user_id) {
PRINT_NAME();
LOGF("\t user_id = %d\n", user_id);
return 0;
}
static int KYTY_SYSV_ABI ImeKeyboardGetResourceId(int user_id, void* resource_id_array) {
PRINT_NAME();
LOGF("\t user_id = %d\n"
"\t resource_id_array = 0x%016" PRIx64 "\n",
user_id, reinterpret_cast<uint64_t>(resource_id_array));
return 0;
}
static int KYTY_SYSV_ABI ImeKeyboardGetInfo(uint32_t resource_id, void* info) {
PRINT_NAME();
LOGF("\t resource_id = 0x%08" PRIx32 "\n"
"\t info = 0x%016" PRIx64 "\n",
resource_id, reinterpret_cast<uint64_t>(info));
return 0;
}
static int KYTY_SYSV_ABI ImeKeyboardSetMode(int user_id, uint32_t mode) {
PRINT_NAME();
LOGF("\t user_id = %d\n"
"\t mode = 0x%08" PRIx32 "\n",
user_id, mode);
return 0;
}
static int KYTY_SYSV_ABI ImeUpdate(void* handler) {
PRINT_NAME();
// LOGF("\t handler = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(handler));
return 0;
}
LIB_DEFINE(InitPlatform_1_Ime) {
LIB_FUNC("eaFXjfJv3xs", LibIme::ImeKeyboardOpen);
LIB_FUNC("PMVehSlfZ94", LibIme::ImeKeyboardClose);
LIB_FUNC("dKadqZFgKKQ", LibIme::ImeKeyboardGetResourceId);
LIB_FUNC("VkqLPArfFdc", LibIme::ImeKeyboardGetInfo);
LIB_FUNC("ua+13Hk9kKs", LibIme::ImeKeyboardSetMode);
LIB_FUNC("-4GCfYdNF1s", LibIme::ImeUpdate);
}
} // namespace LibIme
namespace LibRemoteplay {
LIB_VERSION("Remoteplay", 1, "Remoteplay", 1, 1);
@@ -3994,7 +3923,6 @@ LIB_DEFINE(InitPlatform_1_SharePlay) {
LIB_DEFINE(InitPlatform_1) {
LibRandom::InitPlatform_1_Random(s);
LibIme::InitPlatform_1_Ime(s);
LibRemoteplay::InitPlatform_1_Remoteplay(s);
LibWebBrowserDialog::InitPlatform_1_WebBrowserDialog(s);
LibGameLiveStreaming::InitPlatform_1_GameLiveStreaming(s);
+8 -1
View File
@@ -23,6 +23,7 @@ namespace SystemService {
[[maybe_unused]] constexpr int PARAM_ID_SUMMERTIME = 5;
[[maybe_unused]] constexpr int PARAM_ID_SYSTEM_NAME = 6;
[[maybe_unused]] constexpr int PARAM_ID_GAME_PARENTAL_LEVEL = 7;
[[maybe_unused]] constexpr int PARAM_ID_CC_ENABLE = 100;
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
@@ -33,6 +34,9 @@ namespace SystemService {
[[maybe_unused]] constexpr int PARAM_TIME_FORMAT_12HOUR = 0;
[[maybe_unused]] constexpr int PARAM_TIME_FORMAT_24HOUR = 1;
[[maybe_unused]] constexpr int PARAM_CC_DISABLED = 0;
[[maybe_unused]] constexpr int PARAM_CC_ENABLED = 1;
[[maybe_unused]] constexpr int MAX_SYSTEM_NAME_LENGTH = 65;
[[maybe_unused]] constexpr int PARAM_GAME_PARENTAL_OFF = 0;
@@ -97,9 +101,12 @@ static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
case PARAM_ID_TIME_ZONE: v = +180; break;
case PARAM_ID_SUMMERTIME: v = 0; break;
case PARAM_ID_GAME_PARENTAL_LEVEL: v = PARAM_GAME_PARENTAL_OFF; break;
case PARAM_ID_CC_ENABLE: v = PARAM_CC_DISABLED; break;
case PARAM_ID_SCREEN_READER: v = 0; break;
case PARAM_ID_ENTER_BUTTON_ASSIGN: v = PARAM_ENTER_BUTTON_ASSIGN_CROSS; break;
default: EXIT("unknown param_id: %d\n", param_id);
default:
LOGF_COLOR(Log::Color::Yellow, " unsupported param_id %d, using 0\n", param_id);
break;
}
LOGF(" %d = %d\n", param_id, v);
+5
View File
@@ -34,6 +34,10 @@ namespace LibKeyboard {
LIB_DEFINE(InitKeyboard_1);
} // namespace LibKeyboard
namespace Ime {
LIB_DEFINE(InitPlatform_1_Ime);
} // namespace Ime
namespace LibUlt {
LIB_DEFINE(InitUlt_1);
} // namespace LibUlt
@@ -89,6 +93,7 @@ void InitAll(Loader::SymbolDatabase* s) {
LIB_LOAD(InitLibKernel_1);
LIB_LOAD(LibMouse::InitMouse_1);
LIB_LOAD(LibKeyboard::InitKeyboard_1);
LIB_LOAD(Ime::InitPlatform_1_Ime);
LIB_LOAD(InitNet_1);
LIB_LOAD(InitPad_1);
LIB_LOAD(InitPlayGo_1);
+1 -1
View File
@@ -67,7 +67,7 @@ static void PrintUsage() {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
::printf(" --redzone Protect the guest SysV red zone.\n");
#endif
::printf(" --keymap <Pad=Key> DualSense mapping; may be repeated.\n");
::printf(" --keymap <Control=Input> DualSense mapping; may be repeated.\n");
::printf(" --rd Enable RenderDoc capture.\n");
}