Compare commits

...
Author SHA1 Message Date
Hashim Khanandnmzik bc2f077c37 launcher: quote emulator paths that contain spaces (#244)
* launcher: quote emulator paths that contain spaces

Run the Linux helper script as a bash file argument instead of bash -c,
and build a properly quoted cmd.exe /K command on Windows so install
folders like "Sony Playstation 5" still launch. Fixes #179.

* launcher: fix cmd command quoting

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-16 22:13:12 +02:00
Hashim Khan 498d63b620 docs: fix mismatched screenshot alt texts in README (#249)
Align Neptunia ReVerse, Hellboy, and Paleo Pines image alts with their
captions so screen readers and link previews describe the right titles.
2026-08-16 22:05:07 +02:00
Hashim Khanandnmzik 566a45017e window: toggle borderless fullscreen with F11 and Alt+Enter (#246)
* window: toggle borderless fullscreen with F11 and Alt+Enter

Match the existing SDL_WINDOW_FULLSCREEN_DESKTOP startup path so testers
can switch modes at runtime. Reserve F11 in host input mappings. Fixes #217.

* window: consume fullscreen hotkey input

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-15 04:07:07 +02:00
nmzik 7a40dad90f agc: ABI fixes 2026-08-11 17:44:57 +02:00
nmzik a9e843b38e ci: link IME common code into dialog tests 2026-08-11 16:48:20 +02:00
nmzik 9a76719544 gpu: validate depth and cube texture descriptors
Accept SDK-valid depth sampler modulation and validate cube storage images as complete six-layer groups.
2026-08-11 16:45:32 +02:00
nmzik 472c8213db gpu: accept CB/DB release without GL2 writeback 2026-08-11 16:12:38 +02:00
nmzik a4f9c022ff ampr: make virtual address range query non-destructive 2026-08-11 16:12:38 +02:00
nmzik 97514884b1 Unify stack walking 2026-08-11 16:12:38 +02:00
nmzik 4980efa9b7 refactor(subsystems&ime): RAII lifecycle 2026-08-11 16:12:38 +02:00
nmzik 34cf130e4d Refactor PM4 control register decoding 2026-08-11 16:12:38 +02:00
nmzik 21aa7b66ce graphics: replace raw GPU enum values with typed formats 2026-08-11 16:12:38 +02:00
nmzik 11b95bed20 graphics: simplify render target format and color mapping resolution 2026-08-11 16:12:38 +02:00
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
Ali Almohayaandnmzik 822ffba313 feat: identify build provenance in the crash log report (#196)
* feat: identify build provenance in the crash log report

* cmake: reuse git version dependency target

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-08 08:16:49 +02:00
Ali Almohaya 3c850b65c8 fix: stop a connected controller from disabling keyboard input (#194)
fix(input): stop a connected controller from disabling keyboard input
2026-08-08 08:05:52 +02:00
nmzik 698c9f802f renderer: remove global BufferCache locking 2026-08-08 08:02:35 +02:00
nmzik 1d5f3c8f66 gpu emulation: remove unnecessary Vulkan synchronization 2026-08-08 01:21:03 +02:00
nmzik ec78afb966 Revert "gpu: queue AGC suspend points"
This reverts commit 90bf3c9236.
2026-08-07 23:58:09 +02:00
nmzik 3a7656336e fix regression properly (fix pthread init abi) 2026-08-07 21:39:16 +02:00
nmzik 90bf3c9236 gpu: queue AGC suspend points 2026-08-07 21:02:31 +02:00
106 changed files with 4952 additions and 3054 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
+2
View File
@@ -378,6 +378,7 @@ add_executable(page_manager_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/PageManagerTests.cpp"
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
)
target_link_libraries(page_manager_tests common)
target_include_directories(page_manager_tests PRIVATE ${inc_headers})
add_executable(bit_array_tests EXCLUDE_FROM_ALL
@@ -387,6 +388,7 @@ target_include_directories(bit_array_tests PRIVATE ${inc_headers})
add_executable(ime_dialog_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/ImeDialogTests.cpp"
"${KYTY_SOURCE_DIR}/libs/imeCommon.cpp"
"${KYTY_SOURCE_DIR}/libs/imeDialog.cpp"
)
target_include_directories(ime_dialog_tests PRIVATE ${inc_headers})
+6 -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,
@@ -52,7 +55,7 @@ graphical glitches, low compatibility, and poor performance.
<tr>
<td align="center">
<strong>Neptunia ReVerse</strong><br>
<img src="docs/screenshots/ps5-04.png" width="300" alt="Minecraft Legends running in KytyPS5">
<img src="docs/screenshots/ps5-04.png" width="300" alt="Neptunia ReVerse running in KytyPS5">
</td>
<td align="center">
<strong>SILENT HILL: The Short Message</strong><br>
@@ -62,11 +65,11 @@ graphical glitches, low compatibility, and poor performance.
<tr>
<td align="center">
<strong>Hellboy</strong><br>
<img src="docs/screenshots/ps5-02.png" width="300" alt="Disgaea 6 running in KytyPS5">
<img src="docs/screenshots/ps5-02.png" width="300" alt="Hellboy running in KytyPS5">
</td>
<td align="center">
<strong>Paleo Pines</strong><br>
<img src="docs/screenshots/ps5-06.png" width="300" alt="Dreaming Sarah running in KytyPS5">
<img src="docs/screenshots/ps5-06.png" width="300" alt="Paleo Pines running in KytyPS5">
</td>
</tr>
</table>
+1
View File
@@ -24,6 +24,7 @@ target_link_libraries(common
Tracy::TracyClient
PRIVATE
cpuinfo
kyty_git_version
spdlog::spdlog
)
+3 -2
View File
@@ -3,6 +3,7 @@
#include "common/debug.h"
#include "common/logging/log.h"
#include "common/subsystems.h"
#include "kytyGitVersion.h"
#include <cstdio>
#include <cstdlib>
@@ -23,7 +24,7 @@ static std::string BuildFatalReport(const char* title, std::string_view text, co
DebugStack stack;
DebugStack::Trace(&stack);
std::string report = "--- Stack Trace ---\n";
std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n";
for (int i = PRINT_STACK_FROM; i < stack.depth; i++) {
report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM,
static_cast<uint64_t>(stack.GetAddr(i)));
@@ -34,7 +35,7 @@ static std::string BuildFatalReport(const char* title, std::string_view text, co
static int DbgReport(const char* title, std::string_view text, const char* file, int line) {
Log::WriteFatal(BuildFatalReport(title, text, file, line));
SubsystemsListSingleton::Instance()->ShutdownAll();
Subsystems::EmergencyShutdownActive();
return 1;
}
-21
View File
@@ -1,21 +0,0 @@
#include "common/commonSubsystem.h"
#include "common/byteBuffer.h" // IWYU pragma: associated
#include "common/common.h" // IWYU pragma: associated
#include "common/debug.h" // IWYU pragma: associated
#include "common/hash.h" // IWYU pragma: associated
#include "common/magicEnum.h" // IWYU pragma: associated
#include "common/singleton.h" // IWYU pragma: associated
#include "common/virtualMemory.h"
namespace Common {
KYTY_SUBSYSTEM_INIT(Common) {
VirtualMemory::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Common) {}
KYTY_SUBSYSTEM_DESTROY(Common) {}
} // namespace Common
-12
View File
@@ -1,12 +0,0 @@
#ifndef KYTY_COMMON_COMMON_SUBSYSTEM_H_
#define KYTY_COMMON_COMMON_SUBSYSTEM_H_
#include "common/subsystems.h"
namespace Common {
KYTY_SUBSYSTEM_DEFINE(Common);
} // namespace Common
#endif /* KYTY_COMMON_COMMON_SUBSYSTEM_H_ */
+4 -4
View File
@@ -9,15 +9,15 @@ namespace Config {
static std::unique_ptr<ConfigOptions> g_config;
KYTY_SUBSYSTEM_INIT(Config) {
void Initialize() {
EXIT_IF(g_config != nullptr);
g_config = std::make_unique<ConfigOptions>();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {}
KYTY_SUBSYSTEM_DESTROY(Config) {}
void Shutdown() {
g_config.reset();
}
void Load(const ConfigOptions& cfg) {
EXIT_IF(g_config == nullptr);
+8 -2
View File
@@ -2,7 +2,6 @@
#define KYTY_COMMON_EMULATOR_CONFIG_H_
#include "common/common.h"
#include "common/subsystems.h"
#include <filesystem>
#include <string>
@@ -10,7 +9,14 @@
namespace Config {
KYTY_SUBSYSTEM_DEFINE(Config);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Config";
static constexpr auto initialize = Config::Initialize;
static constexpr auto shutdown = Config::Shutdown;
};
enum class ShaderOptimizationType { None, Size, Performance };
+2 -8
View File
@@ -149,7 +149,7 @@ void WriteFatal(fmt::text_style style, std::string_view text) {
Flush();
}
KYTY_SUBSYSTEM_INIT(Log) {
void Initialize() {
g_initialized = true;
switch (Config::GetPrintfDirection()) {
case Config::OutputDirection::Silent: g_direction = Direction::Silent; break;
@@ -161,13 +161,7 @@ KYTY_SUBSYSTEM_INIT(Log) {
SetupLogger();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Log) {
Flush();
std::lock_guard lock(g_logger_mutex);
g_logger.reset();
}
KYTY_SUBSYSTEM_DESTROY(Log) {
void Shutdown() {
Flush();
std::lock_guard lock(g_logger_mutex);
g_logger.reset();
+9 -2
View File
@@ -2,7 +2,6 @@
#define KYTY_COMMON_LOGGING_LOG_H_
#include "common/common.h"
#include "common/subsystems.h"
#include <fmt/color.h>
#include <fmt/printf.h>
@@ -10,7 +9,15 @@
namespace Log {
KYTY_SUBSYSTEM_DEFINE(Log);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Log";
static constexpr auto initialize = Log::Initialize;
static constexpr auto shutdown = Log::Shutdown;
static constexpr auto emergency_shutdown = Log::Shutdown;
};
enum class Direction { Silent, Console, File };
+1 -49
View File
@@ -15,56 +15,8 @@
#include "common/assert.h"
#include "common/platform/sysDbg.h"
struct FrameS {
struct FrameS* next;
void* ret_addr;
};
int WalkStack(int z_stack_depth, void** z_stack_trace) {
CONTEXT context;
// KNONVOLATILE_CONTEXT_POINTERS NvContext;
PRUNTIME_FUNCTION runtime_function = nullptr;
PVOID handler_data = nullptr;
ULONG64 establisher_frame = 0;
ULONG64 image_base = 0;
RtlCaptureContext(&context);
int frame = 0;
while (true) {
if (frame >= z_stack_depth) {
break;
}
z_stack_trace[frame] = reinterpret_cast<void*>(context.Rip);
frame++;
runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, nullptr);
if (runtime_function == nullptr) {
break;
}
// RtlZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
RtlVirtualUnwind(0, image_base, context.Rip, runtime_function, &context, &handler_data,
&establisher_frame, nullptr /*&NvContext*/);
if (context.Rip == 0u) {
break;
}
}
return frame;
}
void SysStackWalk(void** stack, int* depth) {
int n = WalkStack(*depth, stack);
*depth = n;
// unwind_info_t info = {stack, 0, *depth};
// _Unwind_Backtrace(&trace_fcn, &info);
// *depth = info.depth;
*depth = static_cast<int>(CaptureStackBackTrace(0, static_cast<DWORD>(*depth), stack, nullptr));
}
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
+5 -14
View File
@@ -1,7 +1,6 @@
#include "common/profiler.h"
#include "common/emulatorConfig.h"
#include "common/subsystems.h"
#include <algorithm>
#include <common/TracyProtocol.hpp>
@@ -55,13 +54,7 @@ void SetThreadName(const char* name) {
}
}
void Close() {
if (tracy::ProfilerAvailable()) {
tracy::ShutdownProfiler();
}
}
KYTY_SUBSYSTEM_INIT(Profiler) {
void Initialize() {
switch (Config::GetProfilerDirection()) {
case Config::ProfilerDirection::Network:
if (!tracy::ProfilerAvailable()) {
@@ -78,12 +71,10 @@ KYTY_SUBSYSTEM_INIT(Profiler) {
}
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Profiler) {
Close();
}
KYTY_SUBSYSTEM_DESTROY(Profiler) {
Close();
void Shutdown() {
if (tracy::ProfilerAvailable()) {
tracy::ShutdownProfiler();
}
}
} // namespace Profiler
+9 -2
View File
@@ -2,7 +2,6 @@
#define KYTY_COMMON_PROFILER_H_
#include "common/common.h"
#include "common/subsystems.h"
#include <cstdint>
#include <optional>
@@ -42,7 +41,15 @@ private:
void EndBlock();
void SetThreadName(const char* name);
KYTY_SUBSYSTEM_DEFINE(Profiler);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Profiler";
static constexpr auto initialize = Profiler::Initialize;
static constexpr auto shutdown = Profiler::Shutdown;
static constexpr auto emergency_shutdown = Profiler::Shutdown;
};
} // namespace Profiler
+25 -214
View File
@@ -1,232 +1,43 @@
#include "common/subsystems.h"
#include "common/assert.h"
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
namespace Common {
class SubsystemPrivate {
public:
SubsystemPrivate() = default;
virtual ~SubsystemPrivate() = default;
static Subsystems* g_active_subsystems = nullptr;
KYTY_CLASS_NO_COPY(SubsystemPrivate);
Subsystems::Subsystems(bool print): m_print(print) {
m_active.reserve(16);
g_active_subsystems = this;
}
bool failed {false};
std::string fail_msg;
};
class SubsystemsListPrivate {
public:
explicit SubsystemsListPrivate(SubsystemsList& p): parent(p) {}
virtual ~SubsystemsListPrivate() = default;
void SetArgs(int argc, char** argv) {
this->m_argc = argc;
this->m_argv = argv;
Subsystems::~Subsystems() {
Destroy();
if (g_active_subsystems == this) {
g_active_subsystems = nullptr;
}
}
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
EXIT_IF(!s);
const char* name = s->Id();
EXIT_IF(FindByName(name) != nullptr);
auto nl = std::make_unique<SubsListStruct>();
nl->s = s;
nl->name = name;
nl->deps = nullptr;
nl->next = std::move(list);
nl->prev_init = nullptr;
auto* node = nl.get();
list = std::move(nl);
for (auto* dep: deps) {
const char* str = dep->Id();
auto l = std::make_unique<DepsListStruct>();
l->dep_name = str;
l->next = std::move(node->deps);
node->deps = std::move(l);
void Subsystems::Destroy() {
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
if (it->shutdown != nullptr) {
it->shutdown();
}
node->initialized = false;
}
m_active.clear();
}
bool InitAll(bool print_msg) {
while (SubsListStruct* n = FindNextToInitialize()) {
n->s->Init(&parent);
if (n->s->m_p->failed) {
fail_msg = n->s->m_p->fail_msg.c_str();
fail_name = n->name;
return false;
}
if (print_msg) {
printf("Initialized: %s\n", n->name);
}
n->initialized = true;
SubsListStruct* last = last_init;
last_init = n;
n->prev_init = last;
void Subsystems::EmergencyShutdown() {
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
if (it->emergency_shutdown != nullptr) {
it->emergency_shutdown();
}
return true;
}
m_active.clear();
}
void DestroyAll(bool print_msg) {
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
n->s->Destroy(&parent);
n->initialized = false;
if (print_msg) {
printf("Destroyed: %s\n", n->name);
}
}
last_init = nullptr;
void Subsystems::EmergencyShutdownActive() {
if (g_active_subsystems != nullptr) {
g_active_subsystems->EmergencyShutdown();
}
void ShutdownAll() {
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
n->s->UnexpectedShutdown(&parent);
n->initialized = false;
}
last_init = nullptr;
}
struct DepsListStruct {
const char* dep_name;
std::unique_ptr<DepsListStruct> next;
};
struct SubsListStruct {
Subsystem* s;
const char* name;
std::unique_ptr<DepsListStruct> deps;
std::unique_ptr<SubsListStruct> next;
SubsListStruct* prev_init;
bool initialized;
};
[[nodiscard]] SubsListStruct* FindByName(const char* name) const {
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
if (std::strcmp(n->name, name) == 0) {
return n;
}
}
return nullptr;
}
[[nodiscard]] SubsListStruct* FindNextToInitialize() const {
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
if (n->initialized) {
continue;
}
DepsListStruct* d = n->deps.get();
for (; d != nullptr; d = d->next.get()) {
SubsListStruct* s = FindByName(d->dep_name);
if ((s == nullptr) || !s->initialized) {
break;
}
}
if (d == nullptr) {
return n;
}
}
return nullptr;
}
KYTY_CLASS_NO_COPY(SubsystemsListPrivate);
std::unique_ptr<SubsListStruct> list;
SubsListStruct* last_init = nullptr;
int m_argc = 0;
char** m_argv = nullptr;
const char* fail_msg = nullptr;
const char* fail_name = nullptr;
SubsystemsList& parent;
};
SubsystemsList::SubsystemsList(): m_p(std::make_unique<SubsystemsListPrivate>(*this)) {}
SubsystemsList::~SubsystemsList() = default;
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
m_p->Add(s, deps);
}
bool SubsystemsList::InitAll(bool print_msg) {
return m_p->InitAll(print_msg);
}
void SubsystemsList::DestroyAll(bool print_msg) {
m_p->DestroyAll(print_msg);
}
int* SubsystemsList::GetArgc() {
return &m_p->m_argc;
}
char** SubsystemsList::GetArgv() {
return m_p->m_argv;
}
Subsystem::Subsystem(): m_p(std::make_unique<SubsystemPrivate>()) {}
Subsystem::~Subsystem() = default;
void Subsystem::Fail(const char* format, ...) {
va_list args {};
va_start(args, format);
va_list args_copy {};
va_copy(args_copy, args);
int len = std::vsnprintf(nullptr, 0, format, args_copy);
va_end(args_copy);
if (len > 0) {
std::string msg(static_cast<size_t>(len) + 1, '\0');
std::vsnprintf(msg.data(), msg.size(), format, args);
m_p->fail_msg = msg.c_str();
m_p->failed = true;
}
va_end(args);
}
const char* SubsystemsList::GetFailName() const {
return m_p->fail_name;
}
const char* SubsystemsList::GetFailMsg() const {
return m_p->fail_msg;
}
void SubsystemsList::SetArgs(int argc, char* argv[]) {
m_p->SetArgs(argc, argv);
}
void SubsystemsList::ShutdownAll() {
m_p->ShutdownAll();
}
} // namespace Common
+41 -70
View File
@@ -2,90 +2,61 @@
#define KYTY_COMMON_SUBSYSTEMS_H_
#include "common/common.h"
#include "common/singleton.h"
#include <initializer_list>
#include <memory>
#include <cstdio>
#include <vector>
namespace Common {
class Subsystem;
class SubsystemsListPrivate;
class SubsystemPrivate;
class SubsystemsList {
class Subsystems {
public:
SubsystemsList();
virtual ~SubsystemsList();
void SetArgs(int argc, char* argv[]);
using Callback = void (*)();
// void Add(Subsystem* s, const char* name, ...);
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps);
explicit Subsystems(bool print = false);
~Subsystems();
bool InitAll(bool print_msg = false);
void DestroyAll(bool print_msg = false);
template <typename Lifecycle>
void Initialize() {
Lifecycle::initialize();
m_active.push_back({ShutdownCallback<Lifecycle>(), EmergencyCallback<Lifecycle>()});
if (m_print) {
std::printf("Initialized: %s\n", Lifecycle::name);
}
}
int* GetArgc();
char** GetArgv();
void Destroy();
void EmergencyShutdown();
[[nodiscard]] const char* GetFailName() const;
[[nodiscard]] const char* GetFailMsg() const;
static void EmergencyShutdownActive();
void ShutdownAll();
static SubsystemsList* Instance() { return Common::Singleton<SubsystemsList>::Instance(); }
KYTY_CLASS_NO_COPY(SubsystemsList);
KYTY_CLASS_NO_COPY(Subsystems);
private:
std::unique_ptr<SubsystemsListPrivate> m_p;
template <typename Lifecycle>
static consteval Callback ShutdownCallback() {
if constexpr (requires { Lifecycle::shutdown; }) {
return Lifecycle::shutdown;
}
return nullptr;
}
template <typename Lifecycle>
static consteval Callback EmergencyCallback() {
if constexpr (requires { Lifecycle::emergency_shutdown; }) {
return Lifecycle::emergency_shutdown;
}
return nullptr;
}
struct Entry {
Callback shutdown;
Callback emergency_shutdown;
};
std::vector<Entry> m_active;
bool m_print;
};
using SubsystemsListSingleton = Common::Singleton<SubsystemsList>;
class Subsystem {
public:
Subsystem();
virtual ~Subsystem();
virtual const char* Id() = 0;
virtual void Init(SubsystemsList* parent) = 0;
virtual void Destroy(SubsystemsList* parent) = 0;
virtual void UnexpectedShutdown(SubsystemsList* parent) = 0;
friend class SubsystemsListPrivate;
KYTY_CLASS_NO_COPY(Subsystem);
protected:
void Fail(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3);
private:
std::unique_ptr<SubsystemPrivate> m_p;
};
#define KYTY_SUBSYSTEM_DEFINE(s) \
class s##Subsystem: public Common::Subsystem { \
public: \
static Subsystem* Instance() { return Common::Singleton<s##Subsystem>::Instance(); } \
const char* Id() { return #s; } \
void Init(Common::SubsystemsList* parent); \
void Destroy(Common::SubsystemsList* parent); \
void UnexpectedShutdown(Common::SubsystemsList* parent); \
}; \
typedef Common::Singleton<s##Subsystem> s##SubsystemSingleton;
#define KYTY_SUBSYSTEM_INIT(s) \
void s##Subsystem::Init([[maybe_unused]] Common::SubsystemsList* parent)
#define KYTY_SUBSYSTEM_DESTROY(s) \
void s##Subsystem::Destroy([[maybe_unused]] Common::SubsystemsList* parent)
#define KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(s) \
void s##Subsystem::UnexpectedShutdown([[maybe_unused]] Common::SubsystemsList* parent)
// #define KYTY_SUBSYSTEM_ADD(list, s, ...) list.push_back(s##SubsystemSingleton::Instance(), #s,
// __VA_ARGS__); #define KYTY_SUBSYSTEM_ADD2(list, ...) list.Add2(s##SubsystemSingleton::Instance(),
// __VA_ARGS__);
} // namespace Common
#endif /* KYTY_COMMON_SUBSYSTEMS_H_ */
+1 -5
View File
@@ -252,15 +252,11 @@ static thread_id_t g_main_thread;
static int g_main_thread_int;
static std::atomic<int> g_thread_counter = 0;
KYTY_SUBSYSTEM_INIT(Threads) {
void InitializeThreads() {
g_main_thread = std::this_thread::get_id();
g_main_thread_int = Thread::GetThreadIdUnique();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
KYTY_SUBSYSTEM_DESTROY(Threads) {}
Thread::Thread(thread_func_t func, void* arg)
: m_thread(std::make_unique<ThreadPrivate>(func, arg)) {
while (!m_thread->started) {
+1 -2
View File
@@ -2,14 +2,13 @@
#define KYTY_COMMON_THREADS_H_
#include "common/common.h"
#include "common/subsystems.h"
#include <memory>
#include <string>
namespace Common {
KYTY_SUBSYSTEM_DEFINE(Threads);
void InitializeThreads();
using thread_func_t = void (*)(void*);
using wait_poll_func_t = void (*)();
+18 -36
View File
@@ -2,7 +2,6 @@
#include "common/abi.h"
#include "common/assert.h"
#include "common/commonSubsystem.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
@@ -43,7 +42,7 @@ static void KytyClose() {
LOGF("done!\n");
Common::SubsystemsListSingleton::Instance()->ShutdownAll();
Common::Subsystems::EmergencyShutdownActive();
}
static void MountOrCreateDir(const std::filesystem::path& dir, const std::string& point) {
@@ -105,30 +104,13 @@ static void ClearDebugTextureFolder() {
}
}
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json) {
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json,
Common::Subsystems& subsystems) {
EXIT_IF(!Common::Thread::IsMainThread());
auto* slist = Common::SubsystemsList::Instance();
auto* audio = Libs::Audio::AudioSubsystem::Instance();
auto* config = Config::ConfigSubsystem::Instance();
auto* controller = Libs::Controller::ControllerSubsystem::Instance();
auto* core = Common::CommonSubsystem::Instance();
auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance();
auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance();
auto* log = Log::LogSubsystem::Instance();
auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance();
auto* network = Libs::Network::NetworkSubsystem::Instance();
auto* profiler = Profiler::ProfilerSubsystem::Instance();
auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance();
auto* timer = Loader::Timer::TimerSubsystem::Instance();
slist->Add(config, {core});
slist->InitAll(true);
subsystems.Initialize<Config::Lifecycle>();
Config::Load(cfg);
slist->Add(log, {core, config});
slist->InitAll(true);
subsystems.Initialize<Log::Lifecycle>();
if (Common::File::IsFileExisting(param_json)) {
Loader::SystemContentLoadParamSfo(param_json);
@@ -138,17 +120,16 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path&
}
}
slist->Add(audio, {core, log, pthread, memory});
slist->Add(controller, {core, log, config});
slist->Add(file_system, {core, log, pthread});
slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller});
slist->Add(memory, {core, log});
slist->Add(network, {core, log, pthread});
slist->Add(profiler, {core, config});
slist->Add(pthread, {core, log, timer});
slist->Add(timer, {core, log});
slist->InitAll(true);
// Initialization order is explicit; destruction is automatic and reversed.
subsystems.Initialize<Loader::Timer::Lifecycle>();
subsystems.Initialize<Libs::LibKernel::PthreadLifecycle>();
subsystems.Initialize<Profiler::Lifecycle>();
subsystems.Initialize<Libs::Network::Lifecycle>();
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
subsystems.Initialize<Libs::LibKernel::FileSystem::Lifecycle>();
subsystems.Initialize<Libs::Controller::Lifecycle>();
subsystems.Initialize<Libs::Audio::Lifecycle>();
subsystems.Initialize<Libs::Graphics::Lifecycle>();
}
static void LoadElf(const std::filesystem::path& elf, bool dbg_print_reloc = false,
@@ -189,8 +170,9 @@ void Run(const RunOptions& options) {
EXIT("ELF is required\n");
}
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
Init(options.config, param_json);
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
Common::Subsystems subsystems(true);
Init(options.config, param_json, subsystems);
ClearDebugTextureFolder();
@@ -103,6 +103,128 @@ void LogUnknownReleaseMemGcr(uint32_t gcr_cntl) {
} // namespace
static HW::BlendControl DecodeBlendControl(uint32_t value) {
HW::BlendControl r {};
r.color_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_SRCBLEND);
r.color_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_COMB_FCN);
r.color_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_DESTBLEND);
r.alpha_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_SRCBLEND);
r.alpha_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_COMB_FCN);
r.alpha_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_DESTBLEND);
r.separate_alpha_blend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, SEPARATE_ALPHA_BLEND) != 0;
r.enable = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ENABLE) != 0;
return r;
}
static HW::ClipControl DecodeClipControl(uint32_t value) {
HW::ClipControl r {};
r.user_clip_planes = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_ENA);
r.user_clip_plane_mode = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_MODE);
r.dx_clip_space = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
r.vertex_kill_any = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
r.min_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
r.max_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
r.user_clip_plane_negate_y = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
r.clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
r.user_clip_plane_cull_only = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
r.cull_on_clipping_error_disable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
r.linear_attribute_clip_enable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
r.force_viewport_index_from_vs_enable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
return r;
}
static HW::ColorControl DecodeColorControl(uint32_t value) {
HW::ColorControl r {};
r.mode = KYTY_PM4_GET(value, CB_COLOR_CONTROL, MODE);
r.op = KYTY_PM4_GET(value, CB_COLOR_CONTROL, ROP3);
return r;
}
static HW::DepthControl DecodeDepthControl(uint32_t value) {
HW::DepthControl r {};
r.stencil_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
r.z_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_ENABLE) != 0;
r.z_write_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
r.depth_bounds_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
r.zfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ZFUNC);
r.backface_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
r.stencilfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC);
r.stencilfunc_bf = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC_BF);
r.color_writes_on_depth_fail_enable =
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
r.color_writes_on_depth_pass_disable =
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
return r;
}
static HW::ModeControl DecodeModeControl(uint32_t value) {
HW::ModeControl r {};
r.cull_front = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
r.cull_back = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
r.face = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, FACE) != 0;
r.poly_mode = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_MODE);
r.polymode_front_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
r.polymode_back_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
r.poly_offset_front_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
r.poly_offset_back_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
r.vtx_window_offset_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
r.provoking_vtx_last = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
r.persp_corr_dis = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
return r;
}
static HW::RenderControl DecodeRenderControl(uint32_t value) {
HW::RenderControl r {};
r.depth_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
r.stencil_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
r.resummarize_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
r.stencil_compress_disable =
KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
r.depth_compress_disable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
r.copy_centroid = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_CENTROID) != 0;
r.copy_sample = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_SAMPLE);
return r;
}
static HW::ScanModeControl DecodeScanModeControl(uint32_t value) {
HW::ScanModeControl r {};
r.msaa_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
r.vport_scissor_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
r.line_stipple_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
return r;
}
static HW::StencilControl DecodeStencilControl(uint32_t value) {
HW::StencilControl r {};
r.stencil_fail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL);
r.stencil_zpass = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS);
r.stencil_zfail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL);
r.stencil_fail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL_BF);
r.stencil_zpass_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS_BF);
r.stencil_zfail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL_BF);
return r;
}
static HW::AaConfig ParseAaConfig(uint32_t value) {
HW::AaConfig r;
@@ -183,6 +305,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) {}
@@ -425,26 +551,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetBlendControl) {
uint32_t param = (cmd_offset - Pm4::CB_BLEND0_CONTROL) / 1;
HW::BlendControl r;
r.color_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT) &
Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK;
r.color_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_SHIFT) &
Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_MASK;
r.color_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_SHIFT) &
Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_MASK;
r.alpha_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_SHIFT) &
Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_MASK;
r.alpha_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_SHIFT) &
Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_MASK;
r.alpha_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_SHIFT) &
Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_MASK;
r.separate_alpha_blend = ((buffer[0] >> Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_SHIFT) &
Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_MASK) != 0;
r.enable = ((buffer[0] >> Pm4::CB_BLEND0_CONTROL_ENABLE_SHIFT) &
Pm4::CB_BLEND0_CONTROL_ENABLE_MASK) != 0;
cp.GetCtx().SetBlendControl(param, r);
cp.GetCtx().SetBlendControl(param, DecodeBlendControl(buffer[0]));
return 1;
}
@@ -453,38 +560,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetClipControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_CL_CLIP_CNTL);
HW::ClipControl r;
// r.user_clip_planes = buffer[0] & 0x3fu;
// r.user_clip_plane_mode = (buffer[0] >> 14u) & 0x3u;
// r.clip_space = (buffer[0] >> 19u) & 0x1u;
// r.vertex_kill_mode = (buffer[0] >> 21u) & 0x1u;
// r.min_z_clip_enable = (buffer[0] >> 26u) & 0x1u;
// r.max_z_clip_enable = (buffer[0] >> 27u) & 0x1u;
// r.user_clip_plane_negate_y = (buffer[0] & 0x00002000u) != 0;
// r.clip_enable = (buffer[0] & 0x00010000u) != 0;
// r.user_clip_plane_cull_only = (buffer[0] & 0x00020000u) != 0;
// r.cull_on_clipping_error_disable = (buffer[0] & 0x00100000u) != 0;
// r.linear_attribute_clip_enable = (buffer[0] & 0x01000000u) != 0;
// r.force_viewport_index_from_vs_enable = (buffer[0] & 0x02000000u) != 0;
r.user_clip_planes = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, UCP_ENA);
r.user_clip_plane_mode = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, PS_UCP_MODE);
r.dx_clip_space = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
r.vertex_kill_any = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
r.min_z_clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
r.max_z_clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
r.user_clip_plane_negate_y = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
r.clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
r.user_clip_plane_cull_only = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
r.cull_on_clipping_error_disable =
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
r.linear_attribute_clip_enable =
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
r.force_viewport_index_from_vs_enable =
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
cp.GetCtx().SetClipControl(r);
cp.GetCtx().SetClipControl(DecodeClipControl(buffer[0]));
return 1;
}
@@ -493,12 +569,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetColorControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::CB_COLOR_CONTROL);
HW::ColorControl r;
r.mode = KYTY_PM4_GET(buffer[0], CB_COLOR_CONTROL, MODE);
r.op = KYTY_PM4_GET(buffer[0], CB_COLOR_CONTROL, ROP3);
cp.GetCtx().SetColorControl(r);
cp.GetCtx().SetColorControl(DecodeColorControl(buffer[0]));
return 1;
}
@@ -522,9 +593,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetColorInfo) {
// r.format = (buffer[4] >> 2u) & 0x1fu;
// r.channel_type = (buffer[4] >> 8u) & 0x7u;
// r.channel_order = (buffer[4] >> 11u) & 0x3u;
r.format = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FORMAT);
r.channel_type = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, NUMBER_TYPE);
r.channel_order = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMP_SWAP);
r.format =
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FORMAT));
r.channel_type =
static_cast<Prospero::ChannelType>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, NUMBER_TYPE));
r.channel_order =
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMP_SWAP));
r.cmask_fast_clear_enable = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FAST_CLEAR) != 0;
r.fmask_compression_enable = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMPRESSION) != 0;
r.blend_clamp = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, BLEND_CLAMP) != 0;
@@ -556,22 +630,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetDepthControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_DEPTH_CONTROL);
HW::DepthControl r;
r.stencil_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
r.z_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_ENABLE) != 0;
r.z_write_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
r.depth_bounds_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
r.zfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, ZFUNC);
r.backface_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
r.stencilfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC);
r.stencilfunc_bf = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC_BF);
r.color_writes_on_depth_fail_enable =
KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
r.color_writes_on_depth_pass_disable =
KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
cp.GetCtx().SetDepthControl(r);
cp.GetCtx().SetDepthControl(DecodeDepthControl(buffer[0]));
return 1;
}
@@ -875,24 +934,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetModeControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_SU_SC_MODE_CNTL);
HW::ModeControl r;
r.cull_front = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
r.cull_back = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
r.face = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, FACE) != 0;
r.poly_mode = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_MODE);
r.polymode_front_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
r.polymode_back_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
r.poly_offset_front_enable =
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
r.poly_offset_back_enable =
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
r.vtx_window_offset_enable =
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
r.provoking_vtx_last = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
r.persp_corr_dis = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
cp.GetCtx().SetModeControl(r);
cp.GetCtx().SetModeControl(DecodeModeControl(buffer[0]));
return 1;
}
@@ -958,19 +1000,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_RENDER_CONTROL);
HW::RenderControl r;
r.depth_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
r.stencil_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
r.resummarize_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
r.stencil_compress_disable =
KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
r.depth_compress_disable =
KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
r.copy_centroid = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_CENTROID) != 0;
r.copy_sample = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_SAMPLE);
cp.GetCtx().SetRenderControl(r);
cp.GetCtx().SetRenderControl(DecodeRenderControl(buffer[0]));
return 1;
}
@@ -1038,9 +1068,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderTarget) {
// info.format = (buffer[4] >> 2u) & 0x1fu;
// info.channel_type = (buffer[4] >> 8u) & 0x7u;
// info.channel_order = (buffer[4] >> 11u) & 0x3u;
info.format = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FORMAT);
info.channel_type = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, NUMBER_TYPE);
info.channel_order = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMP_SWAP);
info.format =
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FORMAT));
info.channel_type =
static_cast<Prospero::ChannelType>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, NUMBER_TYPE));
info.channel_order =
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMP_SWAP));
info.cmask_fast_clear_enable = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FAST_CLEAR) != 0;
info.fmask_compression_enable = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMPRESSION) != 0;
info.blend_clamp = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, BLEND_CLAMP) != 0;
@@ -1062,10 +1095,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderTarget) {
// attrib.num_fragments = (buffer[5] >> 15u) & 0x3u;
attrib.force_dest_alpha_to_one =
KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FORCE_DST_ALPHA_1) != 0;
attrib.tile_mode = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, TILE_MODE_INDEX);
attrib.fmask_tile_mode = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX);
attrib.num_samples = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_SAMPLES);
attrib.num_fragments = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
attrib.tile_mode =
static_cast<Prospero::TileMode>(KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, TILE_MODE_INDEX));
attrib.fmask_tile_mode = static_cast<Prospero::TileMode>(
KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX));
attrib.num_samples = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_SAMPLES);
attrib.num_fragments = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
// dcc.max_uncompressed_block_size = (buffer[6] >> 2u) & 0x3u;
// dcc.max_compressed_block_size = (buffer[6] >> 5u) & 0x3u;
@@ -1158,13 +1193,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetScanModeControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_SC_MODE_CNTL_0);
HW::ScanModeControl r;
r.msaa_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
r.vport_scissor_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
r.line_stipple_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
cp.GetCtx().SetScanModeControl(r);
cp.GetCtx().SetScanModeControl(DecodeScanModeControl(buffer[0]));
return 1;
}
@@ -1204,16 +1233,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetStencilControl) {
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_STENCIL_CONTROL);
HW::StencilControl r;
r.stencil_fail = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILFAIL);
r.stencil_zpass = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZPASS);
r.stencil_zfail = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZFAIL);
r.stencil_fail_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILFAIL_BF);
r.stencil_zpass_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZPASS_BF);
r.stencil_zfail_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZFAIL_BF);
cp.GetCtx().SetStencilControl(r);
cp.GetCtx().SetStencilControl(DecodeStencilControl(buffer[0]));
return 1;
}
@@ -1564,7 +1584,7 @@ KYTY_HW_UC_PARSER(HwUcSetPrimitiveType) {
uint32_t prim_type = KYTY_PM4_GET(buffer[0], VGT_PRIMITIVE_TYPE, PRIM_TYPE);
cp.GetUcfg().SetPrimitiveType(prim_type);
cp.GetUcfg().SetPrimitiveType(static_cast<Prospero::PrimitiveType>(prim_type));
return 1;
}
@@ -2682,14 +2702,21 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
const bool gl2_writeback = ((gcr_cntl & GcrGl2Writeback) != 0);
auto trigger_interrupt = [&]() {
bool queued = false;
switch (interrupt_selector) {
case 0x00:
case 0x03: break;
case 0x01:
case 0x02:
case 0x04: cp.TriggerEopEventAtEndOfPipe(interrupt_context_id); break;
case 0x04:
cp.TriggerEopEventAtEndOfPipe(interrupt_context_id);
queued = true;
break;
default: EXIT("unknown release_mem interrupt selector\n");
}
if (queued) {
cp.BufferFlush();
}
};
if (data_sel == 0 || interrupt_selector == 4) {
@@ -2697,10 +2724,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
cp.EmitGlobalBarrier();
}
if (gl2_writeback) {
cp.SynchronizeGpu();
}
trigger_interrupt();
return 7;
@@ -2711,10 +2734,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
cp.EmitGlobalBarrier();
}
if (gl2_writeback) {
cp.SynchronizeGpu();
}
trigger_interrupt();
return 7;
@@ -2728,17 +2747,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
cache_policy = 0;
if (data_sel == 1) {
static std::atomic<uint32_t> log_count {0};
const auto dst_addr = reinterpret_cast<uint64_t>(dst_gpu_addr);
if ((dst_addr & 0xffffffffff000000ull) == 0x0000001021000000ull || log_count.load() < 64) {
if (log_count.fetch_add(1) < 256) {
LOGF(
"\t runtime release_mem32: dst=0x%016" PRIx64 ", value=0x%08" PRIx32
", action=0x%02" PRIx32 ", gcr=0x%04" PRIx32 ", cache_action=0x%08" PRIx32 "\n",
dst_addr, static_cast<uint32_t>(value), eop_event_type, gcr_cntl, cache_action);
}
}
eop_event_type = 0x2f;
event_index = 6;
auto event_source = 2u;
@@ -2753,7 +2761,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
if (data_sel == 5) {
if (gl2_writeback) {
cp.SynchronizeGpu();
cache_action = 0;
}
@@ -2764,7 +2771,9 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
cp.WriteAtEndOfPipe32(cache_policy, event_write_dest, eop_event_type, cache_action,
event_index, event_source, dst_gpu_addr, static_cast<uint32_t>(value),
interrupt_selector, interrupt_context_id);
cp.BufferFlush();
if (interrupt_selector == 0x01) {
cp.BufferFlush();
}
return 7;
}
@@ -3113,9 +3122,12 @@ void GraphicsInitJmpTablesCxIndirect() {
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
uint32_t slot = (cmd_offset - Pm4::CB_COLOR0_INFO) / 15;
HW::ColorInfo info;
info.format = KYTY_PM4_GET(value, CB_COLOR0_INFO, FORMAT);
info.channel_type = KYTY_PM4_GET(value, CB_COLOR0_INFO, NUMBER_TYPE);
info.channel_order = KYTY_PM4_GET(value, CB_COLOR0_INFO, COMP_SWAP);
info.format =
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(value, CB_COLOR0_INFO, FORMAT));
info.channel_type = static_cast<Prospero::ChannelType>(
KYTY_PM4_GET(value, CB_COLOR0_INFO, NUMBER_TYPE));
info.channel_order =
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(value, CB_COLOR0_INFO, COMP_SWAP));
info.cmask_fast_clear_enable = KYTY_PM4_GET(value, CB_COLOR0_INFO, FAST_CLEAR) != 0;
info.fmask_compression_enable = KYTY_PM4_GET(value, CB_COLOR0_INFO, COMPRESSION) != 0;
info.blend_clamp = KYTY_PM4_GET(value, CB_COLOR0_INFO, BLEND_CLAMP) != 0;
@@ -3140,10 +3152,12 @@ void GraphicsInitJmpTablesCxIndirect() {
HW::ColorAttrib attrib;
attrib.force_dest_alpha_to_one =
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FORCE_DST_ALPHA_1) != 0;
attrib.tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, TILE_MODE_INDEX);
attrib.fmask_tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX);
attrib.num_samples = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_SAMPLES);
attrib.num_fragments = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
attrib.tile_mode = static_cast<Prospero::TileMode>(
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, TILE_MODE_INDEX));
attrib.fmask_tile_mode = static_cast<Prospero::TileMode>(
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX));
attrib.num_samples = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_SAMPLES);
attrib.num_fragments = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
cp.GetCtx().SetColorAttrib(slot, attrib);
};
}
@@ -3297,8 +3311,9 @@ void GraphicsInitJmpTablesCxIndirect() {
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
uint32_t slot = (cmd_offset - Pm4::CB_COLOR0_ATTRIB3);
HW::ColorAttrib3 attrib3;
attrib3.depth = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, MIP0_DEPTH);
attrib3.tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, COLOR_SW_MODE);
attrib3.depth = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, MIP0_DEPTH);
attrib3.tile_mode = static_cast<Prospero::TileMode>(
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, COLOR_SW_MODE));
attrib3.dimension = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, RESOURCE_TYPE);
attrib3.cmask_pipe_aligned = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, CMASK_PIPE_ALIGNED);
attrib3.dcc_pipe_aligned = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, DCC_PIPE_ALIGNED);
@@ -3354,6 +3369,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) {
@@ -3692,39 +3713,12 @@ void GraphicsInitJmpTablesCxIndirect() {
for (uint32_t slot = 0; slot < 8; slot++) {
g_hw_ctx_indirect_func[Pm4::CB_BLEND0_CONTROL + slot] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
uint32_t param = cmd_offset - Pm4::CB_BLEND0_CONTROL;
HW::BlendControl r;
r.color_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_SRCBLEND);
r.color_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_COMB_FCN);
r.color_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_DESTBLEND);
r.alpha_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_SRCBLEND);
r.alpha_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_COMB_FCN);
r.alpha_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_DESTBLEND);
r.separate_alpha_blend =
KYTY_PM4_GET(value, CB_BLEND0_CONTROL, SEPARATE_ALPHA_BLEND) != 0;
r.enable = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ENABLE) != 0;
cp.GetCtx().SetBlendControl(param, r);
cp.GetCtx().SetBlendControl(param, DecodeBlendControl(value));
};
}
g_hw_ctx_indirect_func[Pm4::PA_SU_SC_MODE_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::ModeControl r;
r.cull_front = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
r.cull_back = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
r.face = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, FACE) != 0;
r.poly_mode = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_MODE);
r.polymode_front_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
r.polymode_back_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
r.poly_offset_front_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
r.poly_offset_back_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
r.vtx_window_offset_enable =
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
r.provoking_vtx_last = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
r.persp_corr_dis = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
cp.GetCtx().SetModeControl(r);
cp.GetCtx().SetModeControl(DecodeModeControl(value));
};
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
@@ -3892,28 +3886,11 @@ void GraphicsInitJmpTablesCxIndirect() {
};
g_hw_ctx_indirect_func[Pm4::DB_STENCIL_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::StencilControl r;
r.stencil_fail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL);
r.stencil_zpass = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS);
r.stencil_zfail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL);
r.stencil_fail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL_BF);
r.stencil_zpass_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS_BF);
r.stencil_zfail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL_BF);
cp.GetCtx().SetStencilControl(r);
cp.GetCtx().SetStencilControl(DecodeStencilControl(value));
};
g_hw_ctx_indirect_func[Pm4::DB_RENDER_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::RenderControl r;
r.depth_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
r.stencil_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
r.resummarize_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
r.stencil_compress_disable =
KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
r.depth_compress_disable =
KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
r.copy_centroid = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_CENTROID) != 0;
r.copy_sample = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_SAMPLE);
cp.GetCtx().SetRenderControl(r);
cp.GetCtx().SetRenderControl(DecodeRenderControl(value));
};
g_hw_ctx_indirect_func[Pm4::DB_STENCILREFMASK] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
@@ -3935,23 +3912,7 @@ void GraphicsInitJmpTablesCxIndirect() {
};
g_hw_ctx_indirect_func[Pm4::PA_CL_CLIP_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::ClipControl r;
r.user_clip_planes = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_ENA);
r.user_clip_plane_mode = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_MODE);
r.dx_clip_space = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
r.vertex_kill_any = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
r.min_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
r.max_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
r.user_clip_plane_negate_y = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
r.clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
r.user_clip_plane_cull_only = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
r.cull_on_clipping_error_disable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
r.linear_attribute_clip_enable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
r.force_viewport_index_from_vs_enable =
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
cp.GetCtx().SetClipControl(r);
cp.GetCtx().SetClipControl(DecodeClipControl(value));
};
g_hw_ctx_indirect_func[Pm4::PA_SU_LINE_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
@@ -3979,11 +3940,7 @@ void GraphicsInitJmpTablesCxIndirect() {
};
g_hw_ctx_indirect_func[Pm4::PA_SC_MODE_CNTL_0] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::ScanModeControl r;
r.msaa_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
r.vport_scissor_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
r.line_stipple_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
cp.GetCtx().SetScanModeControl(r);
cp.GetCtx().SetScanModeControl(DecodeScanModeControl(value));
};
g_hw_ctx_indirect_func[Pm4::PA_SC_MODE_CNTL_1] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreScanModeControl1(value);
@@ -4042,27 +3999,11 @@ void GraphicsInitJmpTablesCxIndirect() {
HwCtxIgnoreAlphaToMaskRegister(value);
};
g_hw_ctx_indirect_func[Pm4::CB_COLOR_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::ColorControl r;
r.mode = KYTY_PM4_GET(value, CB_COLOR_CONTROL, MODE);
r.op = KYTY_PM4_GET(value, CB_COLOR_CONTROL, ROP3);
cp.GetCtx().SetColorControl(r);
cp.GetCtx().SetColorControl(DecodeColorControl(value));
};
g_hw_ctx_indirect_func[Pm4::DB_DEPTH_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HW::DepthControl r;
r.stencil_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
r.z_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_ENABLE) != 0;
r.z_write_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
r.depth_bounds_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
r.zfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ZFUNC);
r.backface_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
r.stencilfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC);
r.stencilfunc_bf = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC_BF);
r.color_writes_on_depth_fail_enable =
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
r.color_writes_on_depth_pass_disable =
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
cp.GetCtx().SetDepthControl(r);
cp.GetCtx().SetDepthControl(DecodeDepthControl(value));
};
g_hw_ctx_indirect_func[Pm4::DB_EQAA] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
@@ -4452,6 +4393,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;
@@ -4463,7 +4409,7 @@ void GraphicsInitJmpTablesUcIndirect() {
g_hw_uc_indirect_func[Pm4::VGT_PRIMITIVE_TYPE] = [](KYTY_HW_UC_INDIRECT_ARGS) {
uint32_t prim_type = KYTY_PM4_GET(value, VGT_PRIMITIVE_TYPE, PRIM_TYPE);
cp.GetUcfg().SetPrimitiveType(prim_type);
cp.GetUcfg().SetPrimitiveType(static_cast<Prospero::PrimitiveType>(prim_type));
};
g_hw_uc_indirect_func[Pm4::VGT_INDEX_TYPE] = [](KYTY_HW_UC_INDIRECT_ARGS) {
cp.SetIndexType(value & 0x3u);
+30 -15
View File
@@ -3,15 +3,8 @@
#include "common/common.h"
#include <type_traits>
namespace Libs::Graphics::Prospero {
template <typename T>
constexpr auto GpuEnumValue(T value) {
return static_cast<std::underlying_type_t<T>>(value);
}
enum class PrimitiveType : uint32_t {
kNone = 0,
kPointList = 1,
@@ -115,32 +108,52 @@ enum class ChannelOrder : uint32_t {
kAltReversed = 3,
};
// Maps a shader's logical RGBA export to the host color-attachment components. Two bits per
// component keep this cheap to carry in shader and pipeline keys.
// Selects the logical shader component written to each physical color-attachment component.
// Two bits per component keep this cheap to carry in shader and pipeline keys.
struct ColorComponentMapping {
uint8_t packed = 0xe4u; // RGBA: 0, 1, 2, 3
static constexpr uint8_t kIdentity = 0xe4u;
uint8_t packed = kIdentity;
[[nodiscard]] constexpr uint32_t Map(uint32_t component) const {
return component < 4u ? (packed >> (component * 2u)) & 0x3u : component;
}
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t mask) const {
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t logical_mask) const {
uint32_t mapped = 0;
for (uint32_t component = 0; component < 4u; component++) {
mapped |= ((mask >> component) & 1u) << Map(component);
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
mapped |= ((logical_mask >> Map(physical_component)) & 1u) << physical_component;
}
return mapped;
}
[[nodiscard]] constexpr bool IsIdentity() const { return packed == 0xe4u; }
// This mapping selects an intermediate component; next selects the final logical component.
[[nodiscard]] constexpr ColorComponentMapping Then(ColorComponentMapping next) const {
ColorComponentMapping result {0u};
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
result.packed |= static_cast<uint8_t>(next.Map(Map(physical_component))
<< (physical_component * 2u));
}
return result;
}
[[nodiscard]] constexpr bool IsIdentity() const { return packed == kIdentity; }
bool operator==(const ColorComponentMapping&) const = default;
};
static_assert(sizeof(ColorComponentMapping) == sizeof(uint8_t));
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
inline constexpr ColorComponentMapping ColorMappingRgba {ColorComponentMapping::kIdentity};
inline constexpr ColorComponentMapping ColorMappingGr {0xe1u}; // 1, 0, 2, 3
inline constexpr ColorComponentMapping ColorMappingRabg {0x6cu}; // 0, 3, 2, 1
inline constexpr ColorComponentMapping ColorMappingRgab {0xb4u}; // 0, 1, 3, 2
inline constexpr ColorComponentMapping ColorMappingBgra {0xc6u}; // 2, 1, 0, 3
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
inline constexpr ColorComponentMapping ColorMappingAgba {0x27u}; // 3, 1, 2, 0
inline constexpr ColorComponentMapping ColorMappingArbg {0x63u}; // 3, 0, 2, 1
inline constexpr ColorComponentMapping ColorMappingAgbr {0x87u}; // 3, 1, 0, 2
inline constexpr ColorComponentMapping ColorMappingArgb {0x93u}; // 3, 0, 1, 2
enum class DepthFormat : uint32_t {
kInvalid = 0,
@@ -388,9 +401,11 @@ enum class BufferFormat : uint32_t {
k8Srgb = 128,
k8_8Srgb = 129,
k8_8_8_8Srgb = 130,
k10_10_10_2Float = 131,
k9_9_9_5Float = 132,
k5_6_5UNorm = 133,
k5_5_5_1UNorm = 134,
k1_5_5_5UNorm = 135,
k4_4_4_4UNorm = 136,
kFmask8_S4_F4 = 161,
kBc1UNorm = 169,
+167 -80
View File
@@ -3,129 +3,216 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include <array>
#include <bit>
namespace Libs::Graphics::Prospero {
namespace {
struct FormatInfo {
uint32_t format;
uint32_t bytes_per_element;
uint32_t block_compressed_bytes_per_block;
uint32_t render_target_bytes_per_element;
bool supported_sampled_texture_format;
bool unsigned_integer_texture_format;
BufferFormat format;
uint32_t bytes_per_element;
uint32_t block_compressed_bytes_per_block;
uint32_t render_target_bytes_per_element;
bool supported_sampled_texture_format;
bool unsigned_integer_texture_format;
};
constexpr FormatInfo kFormatInfo[] = {
{GpuEnumValue(BufferFormat::k8UNorm), 1, 0, 1, true, false},
{GpuEnumValue(BufferFormat::k8SNorm), 0, 0, 1, false, false},
{GpuEnumValue(BufferFormat::k8UInt), 1, 0, 1, true, true},
{GpuEnumValue(BufferFormat::k16UNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k16SNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k16UInt), 2, 0, 2, true, true},
{GpuEnumValue(BufferFormat::k16SInt), 2, 0, 2, false, false},
{GpuEnumValue(BufferFormat::k16Float), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k8_8UNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k8_8SNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k8_8UInt), 2, 0, 2, true, true},
{GpuEnumValue(BufferFormat::k8_8SInt), 2, 0, 2, false, false},
{GpuEnumValue(BufferFormat::k32UInt), 4, 0, 4, true, true},
{GpuEnumValue(BufferFormat::k32SInt), 4, 0, 4, false, false},
{GpuEnumValue(BufferFormat::k32Float), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k16_16UNorm), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k16_16SNorm), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k16_16UInt), 4, 0, 4, true, true},
{GpuEnumValue(BufferFormat::k16_16SInt), 4, 0, 4, false, false},
{GpuEnumValue(BufferFormat::k16_16Float), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k11_11_10Float), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k10_10_10_2UNorm), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k10_10_10_2UInt), 4, 0, 4, true, true},
{GpuEnumValue(BufferFormat::k8_8_8_8UNorm), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k8_8_8_8SNorm), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k8_8_8_8UInt), 4, 0, 4, true, true},
{GpuEnumValue(BufferFormat::k8_8_8_8SInt), 4, 0, 4, false, false},
{GpuEnumValue(BufferFormat::k32_32UInt), 8, 0, 8, true, true},
{GpuEnumValue(BufferFormat::k32_32SInt), 8, 0, 8, false, false},
{GpuEnumValue(BufferFormat::k32_32Float), 8, 0, 8, true, false},
{GpuEnumValue(BufferFormat::k16_16_16_16UNorm), 8, 0, 8, true, false},
{GpuEnumValue(BufferFormat::k16_16_16_16SNorm), 8, 0, 8, true, false},
{GpuEnumValue(BufferFormat::k16_16_16_16UInt), 8, 0, 8, true, true},
{GpuEnumValue(BufferFormat::k16_16_16_16SInt), 8, 0, 8, false, false},
{GpuEnumValue(BufferFormat::k16_16_16_16Float), 8, 0, 8, true, false},
{GpuEnumValue(BufferFormat::k32_32_32UInt), 12, 0, 12, true, true},
{GpuEnumValue(BufferFormat::k32_32_32SInt), 12, 0, 12, false, false},
{GpuEnumValue(BufferFormat::k32_32_32Float), 12, 0, 12, true, false},
{GpuEnumValue(BufferFormat::k32_32_32_32UInt), 16, 0, 16, true, true},
{GpuEnumValue(BufferFormat::k32_32_32_32SInt), 16, 0, 16, false, false},
{GpuEnumValue(BufferFormat::k32_32_32_32Float), 16, 0, 16, true, false},
{GpuEnumValue(BufferFormat::k8Srgb), 1, 0, 0, true, false},
{GpuEnumValue(BufferFormat::k8_8Srgb), 2, 0, 0, true, false},
{GpuEnumValue(BufferFormat::k8_8_8_8Srgb), 4, 0, 4, true, false},
{GpuEnumValue(BufferFormat::k9_9_9_5Float), 4, 0, 0, true, false},
{GpuEnumValue(BufferFormat::k5_6_5UNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k5_5_5_1UNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::k4_4_4_4UNorm), 2, 0, 2, true, false},
{GpuEnumValue(BufferFormat::kFmask8_S4_F4), 1, 0, 1, true, false},
{GpuEnumValue(BufferFormat::kBc1UNorm), 0, 8, 0, true, false},
{GpuEnumValue(BufferFormat::kBc1Srgb), 0, 8, 0, true, false},
{GpuEnumValue(BufferFormat::kBc2UNorm), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc2Srgb), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc3UNorm), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc3Srgb), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc4UNorm), 0, 8, 0, true, false},
{GpuEnumValue(BufferFormat::kBc4SNorm), 0, 8, 0, true, false},
{GpuEnumValue(BufferFormat::kBc5UNorm), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc5SNorm), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc6UFloat), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc6SFloat), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc7UNorm), 0, 16, 0, true, false},
{GpuEnumValue(BufferFormat::kBc7Srgb), 0, 16, 0, true, false},
{BufferFormat::k8UNorm, 1, 0, 1, true, false},
{BufferFormat::k8SNorm, 0, 0, 1, false, false},
{BufferFormat::k8UInt, 1, 0, 1, true, true},
{BufferFormat::k16UNorm, 2, 0, 2, true, false},
{BufferFormat::k16SNorm, 2, 0, 2, true, false},
{BufferFormat::k16UInt, 2, 0, 2, true, true},
{BufferFormat::k16SInt, 2, 0, 2, false, false},
{BufferFormat::k16Float, 2, 0, 2, true, false},
{BufferFormat::k8_8UNorm, 2, 0, 2, true, false},
{BufferFormat::k8_8SNorm, 2, 0, 2, true, false},
{BufferFormat::k8_8UInt, 2, 0, 2, true, true},
{BufferFormat::k8_8SInt, 2, 0, 2, false, false},
{BufferFormat::k32UInt, 4, 0, 4, true, true},
{BufferFormat::k32SInt, 4, 0, 4, false, false},
{BufferFormat::k32Float, 4, 0, 4, true, false},
{BufferFormat::k16_16UNorm, 4, 0, 4, true, false},
{BufferFormat::k16_16SNorm, 4, 0, 4, true, false},
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
{BufferFormat::k16_16SInt, 4, 0, 4, false, false},
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
{BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false},
{BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true},
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
{BufferFormat::k8_8_8_8SNorm, 4, 0, 4, true, false},
{BufferFormat::k8_8_8_8UInt, 4, 0, 4, true, true},
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, false, false},
{BufferFormat::k32_32UInt, 8, 0, 8, true, true},
{BufferFormat::k32_32SInt, 8, 0, 8, false, false},
{BufferFormat::k32_32Float, 8, 0, 8, true, false},
{BufferFormat::k16_16_16_16UNorm, 8, 0, 8, true, false},
{BufferFormat::k16_16_16_16SNorm, 8, 0, 8, true, false},
{BufferFormat::k16_16_16_16UInt, 8, 0, 8, true, true},
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, false, false},
{BufferFormat::k16_16_16_16Float, 8, 0, 8, true, false},
{BufferFormat::k32_32_32UInt, 12, 0, 12, true, true},
{BufferFormat::k32_32_32SInt, 12, 0, 12, false, false},
{BufferFormat::k32_32_32Float, 12, 0, 12, true, false},
{BufferFormat::k32_32_32_32UInt, 16, 0, 16, true, true},
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, false, false},
{BufferFormat::k32_32_32_32Float, 16, 0, 16, true, false},
{BufferFormat::k8Srgb, 1, 0, 0, true, false},
{BufferFormat::k8_8Srgb, 2, 0, 0, true, false},
{BufferFormat::k8_8_8_8Srgb, 4, 0, 4, true, false},
{BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false},
{BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false},
{BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false},
{BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false},
{BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false},
{BufferFormat::kFmask8_S4_F4, 1, 0, 1, true, false},
{BufferFormat::kBc1UNorm, 0, 8, 0, true, false},
{BufferFormat::kBc1Srgb, 0, 8, 0, true, false},
{BufferFormat::kBc2UNorm, 0, 16, 0, true, false},
{BufferFormat::kBc2Srgb, 0, 16, 0, true, false},
{BufferFormat::kBc3UNorm, 0, 16, 0, true, false},
{BufferFormat::kBc3Srgb, 0, 16, 0, true, false},
{BufferFormat::kBc4UNorm, 0, 8, 0, true, false},
{BufferFormat::kBc4SNorm, 0, 8, 0, true, false},
{BufferFormat::kBc5UNorm, 0, 16, 0, true, false},
{BufferFormat::kBc5SNorm, 0, 16, 0, true, false},
{BufferFormat::kBc6UFloat, 0, 16, 0, true, false},
{BufferFormat::kBc6SFloat, 0, 16, 0, true, false},
{BufferFormat::kBc7UNorm, 0, 16, 0, true, false},
{BufferFormat::kBc7Srgb, 0, 16, 0, true, false},
};
constexpr uint8_t RenderTargetTypeBit(ChannelType type) {
return static_cast<uint8_t>(1u << static_cast<uint32_t>(type));
}
constexpr auto kUNormType = RenderTargetTypeBit(ChannelType::kUNorm);
constexpr auto kIntegerTypes =
RenderTargetTypeBit(ChannelType::kUInt) | RenderTargetTypeBit(ChannelType::kSInt);
constexpr auto kNormalizedIntegerTypes =
kUNormType | RenderTargetTypeBit(ChannelType::kSNorm) | kIntegerTypes;
struct RenderTargetLayoutInfo {
ChannelLayout layout;
BufferFormat first_linear_format;
uint8_t linear_type_mask;
uint8_t components;
BufferFormat extra_format = BufferFormat::kInvalid;
ChannelType extra_type = ChannelType::kUNorm;
ChannelOrderSupport order_support = ChannelOrderSupport::kAll;
};
constexpr RenderTargetLayoutInfo kRenderTargetLayouts[] = {
{ChannelLayout::k8, BufferFormat::k8UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k8Srgb,
ChannelType::kSrgb},
{ChannelLayout::k16, BufferFormat::k16UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k16Float,
ChannelType::kFloat},
{ChannelLayout::k8_8, BufferFormat::k8_8UNorm, kNormalizedIntegerTypes, 2,
BufferFormat::k8_8Srgb, ChannelType::kSrgb},
{ChannelLayout::k32, BufferFormat::k32UInt, kIntegerTypes, 1, BufferFormat::k32Float,
ChannelType::kFloat},
{ChannelLayout::k16_16, BufferFormat::k16_16UNorm, kNormalizedIntegerTypes, 2,
BufferFormat::k16_16Float, ChannelType::kFloat},
{ChannelLayout::k11_11_10, BufferFormat::k11_11_10UNorm, kNormalizedIntegerTypes, 3,
BufferFormat::k11_11_10Float, ChannelType::kFloat},
{ChannelLayout::k10_11_11, BufferFormat::k10_11_11UNorm, kNormalizedIntegerTypes, 3,
BufferFormat::k10_11_11Float, ChannelType::kFloat},
{ChannelLayout::k2_10_10_10, BufferFormat::k2_10_10_10UNorm, kNormalizedIntegerTypes, 4},
{ChannelLayout::k10_10_10_2, BufferFormat::k10_10_10_2UNorm, kNormalizedIntegerTypes, 4},
{ChannelLayout::k8_8_8_8, BufferFormat::k8_8_8_8UNorm, kNormalizedIntegerTypes, 4,
BufferFormat::k8_8_8_8Srgb, ChannelType::kSrgb},
{ChannelLayout::k32_32, BufferFormat::k32_32UInt, kIntegerTypes, 2, BufferFormat::k32_32Float,
ChannelType::kFloat},
{ChannelLayout::k16_16_16_16, BufferFormat::k16_16_16_16UNorm, kNormalizedIntegerTypes, 4,
BufferFormat::k16_16_16_16Float, ChannelType::kFloat},
{ChannelLayout::k32_32_32_32, BufferFormat::k32_32_32_32UInt, kIntegerTypes, 4,
BufferFormat::k32_32_32_32Float, ChannelType::kFloat},
{ChannelLayout::k5_6_5, BufferFormat::k5_6_5UNorm, kUNormType, 3},
{ChannelLayout::k5_5_5_1, BufferFormat::k5_5_5_1UNorm, kUNormType, 4},
{ChannelLayout::k1_5_5_5, BufferFormat::k1_5_5_5UNorm, kUNormType, 4},
{ChannelLayout::k4_4_4_4, BufferFormat::k4_4_4_4UNorm, kUNormType, 4},
{ChannelLayout::k10_10_10_2Float, BufferFormat::kInvalid, 0, 4, BufferFormat::k10_10_10_2Float,
ChannelType::kFloat, ChannelOrderSupport::kStandardOnly},
};
const RenderTargetLayoutInfo* FindRenderTargetLayout(ChannelLayout layout) {
for (const auto& info: kRenderTargetLayouts) {
if (info.layout == layout) {
return &info;
}
}
return nullptr;
}
constexpr auto MakeFormatInfoLookup() {
constexpr uint32_t kMaxFormat = GpuEnumValue(BufferFormat::kBc7Srgb);
constexpr size_t kMaxFormat = static_cast<size_t>(BufferFormat::kBc7Srgb);
std::array<const FormatInfo*, kMaxFormat + 1> lookup {};
for (const auto& info: kFormatInfo) {
lookup[info.format] = &info;
lookup[static_cast<size_t>(info.format)] = &info;
}
return lookup;
}
constexpr auto kFormatInfoLookup = MakeFormatInfoLookup();
const FormatInfo* FindFormatInfo(uint32_t format) {
return format < kFormatInfoLookup.size() ? kFormatInfoLookup[format] : nullptr;
const FormatInfo* FindFormatInfo(BufferFormat format) {
const auto index = static_cast<size_t>(format);
return index < kFormatInfoLookup.size() ? kFormatInfoLookup[index] : nullptr;
}
} // namespace
uint32_t NumBytesPerElement(uint32_t format) {
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type) {
const auto* layout_info = FindRenderTargetLayout(layout);
const auto raw_type = static_cast<uint32_t>(type);
if (layout_info == nullptr || raw_type >= 8u) {
return {};
}
auto format = BufferFormat::kInvalid;
if (layout_info->extra_format != BufferFormat::kInvalid && type == layout_info->extra_type) {
format = layout_info->extra_format;
} else if ((layout_info->linear_type_mask & (1u << raw_type)) != 0) {
const auto first_type =
std::countr_zero(static_cast<uint32_t>(layout_info->linear_type_mask));
format = static_cast<BufferFormat>(static_cast<uint32_t>(layout_info->first_linear_format) +
raw_type - first_type);
}
if (format == BufferFormat::kInvalid) {
return {};
}
return {format, layout_info->components, layout_info->order_support};
}
uint32_t NumBytesPerElement(BufferFormat format) {
const auto* info = FindFormatInfo(format);
return info != nullptr ? info->bytes_per_element : 0;
}
uint32_t BlockCompressedBytesPerBlock(uint32_t format) {
uint32_t BlockCompressedBytesPerBlock(BufferFormat format) {
const auto* info = FindFormatInfo(format);
return info != nullptr ? info->block_compressed_bytes_per_block : 0;
}
uint32_t RenderTargetBytesPerElement(uint32_t format) {
uint32_t RenderTargetBytesPerElement(BufferFormat format) {
const auto* info = FindFormatInfo(format);
return info != nullptr ? info->render_target_bytes_per_element : 0;
}
bool IsSupportedTextureFormat(uint32_t format) {
bool IsSupportedTextureFormat(BufferFormat format) {
const auto* info = FindFormatInfo(format);
return info != nullptr && info->supported_sampled_texture_format;
}
bool IsUintTextureFormat(uint32_t format) {
bool IsUintTextureFormat(BufferFormat format) {
const auto* info = FindFormatInfo(format);
return info != nullptr && info->unsigned_integer_texture_format;
}
bool IsFmaskTextureFormat(uint32_t format) {
return format == GpuEnumValue(BufferFormat::kFmask8_S4_F4);
bool IsFmaskTextureFormat(BufferFormat format) {
return format == BufferFormat::kFmask8_S4_F4;
}
} // namespace Libs::Graphics::Prospero
+40 -7
View File
@@ -1,16 +1,49 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
#include "common/common.h"
#include "graphics/guest_gpu/gpu_defs.h"
namespace Libs::Graphics::Prospero {
uint32_t NumBytesPerElement(uint32_t format);
uint32_t BlockCompressedBytesPerBlock(uint32_t format);
uint32_t RenderTargetBytesPerElement(uint32_t format);
bool IsSupportedTextureFormat(uint32_t format);
bool IsUintTextureFormat(uint32_t format);
bool IsFmaskTextureFormat(uint32_t format);
enum class ChannelOrderSupport : uint8_t {
kNone,
kStandardOnly,
kAll,
};
struct RenderTargetFormatEncoding {
BufferFormat buffer_format = BufferFormat::kInvalid;
uint8_t components = 0;
ChannelOrderSupport order_support = ChannelOrderSupport::kNone;
[[nodiscard]] constexpr bool IsValid() const {
return buffer_format != BufferFormat::kInvalid && components >= 1u && components <= 4u &&
order_support != ChannelOrderSupport::kNone;
}
[[nodiscard]] constexpr bool SupportsOrder(ChannelOrder order) const {
switch (order_support) {
case ChannelOrderSupport::kStandardOnly: return order == ChannelOrder::kStandard;
case ChannelOrderSupport::kAll:
switch (order) {
case ChannelOrder::kStandard:
case ChannelOrder::kAlt:
case ChannelOrder::kReversed:
case ChannelOrder::kAltReversed: return true;
}
return false;
default: return false;
}
}
};
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type);
uint32_t NumBytesPerElement(BufferFormat format);
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
uint32_t RenderTargetBytesPerElement(BufferFormat format);
bool IsSupportedTextureFormat(BufferFormat format);
bool IsUintTextureFormat(BufferFormat format);
bool IsFmaskTextureFormat(BufferFormat format);
} // namespace Libs::Graphics::Prospero
+12 -17
View File
@@ -124,7 +124,6 @@ private:
uint64_t flip_request_id = 0;
};
void WaitLocked();
void Enqueue(Submission submission);
void WaitForIdle();
void ProcessCommands();
@@ -276,10 +275,8 @@ void GpuState::SubmitFlipPreparation(uint64_t request_id) {
void GpuState::Done() {
GpuMutexLock lock(m_submission_mutex);
if (IsGpuThread()) {
m_gfx_cp->BufferWait();
} else {
WaitLocked();
if (!IsGpuThread()) {
WaitForIdle();
}
m_graphics_done = true;
m_done_num++;
@@ -289,11 +286,6 @@ int GpuState::GetFrameNum() {
return m_done_num;
}
void GpuState::WaitLocked() {
WaitForIdle();
SendCommandSync([this] { m_gfx_cp->BufferWait(); });
}
CommandProcessor& GpuState::GetProcessor(uint32_t queue_id) {
EXIT_IF(queue_id >= QueueCount);
if (queue_id == 0) {
@@ -307,8 +299,6 @@ CommandProcessor& GpuState::GetProcessor(uint32_t queue_id) {
}
void CommandProcessor::Reset() {
BufferWait();
m_sh_ctx.Reset();
m_ucfg.Reset();
m_ctx.Reset();
@@ -385,7 +375,6 @@ void CommandProcessor::WaitDeDiff(uint32_t diff) {
}
void CommandProcessor::IncrementDe() {
BufferWait();
m_de_count++;
}
@@ -484,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);
@@ -1352,6 +1340,9 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
value >> 16u);
Sync::WriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst, value & 0xffffu,
value >> 16u);
if (with_interrupt) {
m_renderer.TriggerEopEvent(interrupt_context_id);
}
return;
}
} else if (eop_event_type == 0x04 && cache_action == 0x00 && event_index == 0x05) {
@@ -1393,9 +1384,14 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
case 0x00:
switch (eop_event_type) {
case 0x04:
if (event_index == 0x05) {
write64(false);
return;
}
break;
case 0x14:
case 0x28:
if ((eop_event_type == 0x04 && event_index == 0x05) ||
(eop_event_type == 0x28 && event_index == 0x00)) {
if (event_index == 0x00) {
write64(false);
return;
}
@@ -1554,7 +1550,6 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
event_index);
}
EmitGlobalBarrier();
SynchronizeGpu();
break;
// DbDataWritebackInvalidate, DbMetadataWritebackInvalidate, CbMetadataWritebackInvalidate.
case 0x0000002a:
+35 -35
View File
@@ -29,27 +29,27 @@ struct ColorView {
struct ColorInfo {
bool fmask_compression_enable = false;
// uint32_t fmask_compression_mode = 0;
bool fmask_data_compression_disable = false;
bool fmask_one_frag_mode = false;
bool cmask_fast_clear_enable = false;
bool dcc_compression_enable = false;
bool blend_clamp = false;
bool blend_bypass = false;
bool round_mode = false;
uint32_t cmask_is_linear = 0;
uint32_t cmask_addr_type = 0;
bool alt_tile_mode = false;
uint32_t format = 0;
uint32_t channel_type = 0;
uint32_t channel_order = 0;
bool fmask_data_compression_disable = false;
bool fmask_one_frag_mode = false;
bool cmask_fast_clear_enable = false;
bool dcc_compression_enable = false;
bool blend_clamp = false;
bool blend_bypass = false;
bool round_mode = false;
uint32_t cmask_is_linear = 0;
uint32_t cmask_addr_type = 0;
bool alt_tile_mode = false;
Prospero::ChannelLayout format = Prospero::ChannelLayout::kInvalid;
Prospero::ChannelType channel_type = Prospero::ChannelType::kUNorm;
Prospero::ChannelOrder channel_order = Prospero::ChannelOrder::kStandard;
};
struct ColorAttrib {
bool force_dest_alpha_to_one = false;
uint32_t tile_mode = 0;
uint32_t fmask_tile_mode = 0;
uint32_t num_samples = 0;
uint32_t num_fragments = 0;
bool force_dest_alpha_to_one = false;
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
Prospero::TileMode fmask_tile_mode = Prospero::TileMode::kLinear;
uint32_t num_samples = 0;
uint32_t num_fragments = 0;
};
struct ColorAttrib2 {
@@ -59,11 +59,11 @@ struct ColorAttrib2 {
};
struct ColorAttrib3 {
uint32_t depth = 0;
uint32_t tile_mode = 0;
uint32_t dimension = 0;
bool cmask_pipe_aligned = false;
bool dcc_pipe_aligned = false;
uint32_t depth = 0;
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
uint32_t dimension = 0;
bool cmask_pipe_aligned = false;
bool dcc_pipe_aligned = false;
};
struct ColorDccControl {
@@ -132,7 +132,7 @@ struct RenderTarget {
};
struct DepthZInfo {
uint32_t format = 0;
Prospero::DepthFormat format = Prospero::DepthFormat::kInvalid;
uint32_t num_samples = 0;
Prospero::TextureCompatiblePlaneCompression texture_compatibility =
Prospero::TextureCompatiblePlaneCompression::kDisable;
@@ -144,16 +144,16 @@ struct DepthZInfo {
[[nodiscard]] static DepthZInfo Decode(uint32_t value) {
DepthZInfo info;
info.format = value & 0x3u;
info.format = static_cast<Prospero::DepthFormat>(value & 0x3u);
info.num_samples = (value >> 2u) & 0x3u;
info.texture_compatibility = static_cast<Prospero::TextureCompatiblePlaneCompression>(
value & Prospero::GpuEnumValue(Prospero::TextureCompatiblePlaneCompression::kBitMask));
value & static_cast<uint32_t>(Prospero::TextureCompatiblePlaneCompression::kBitMask));
info.partially_resident = (value & 0x00001000u) != 0;
info.max_mip_level = static_cast<uint8_t>((value >> 16u) & 0x0fu);
info.expclear_enabled = (value & 0x08000000u) != 0;
info.htile_acceleration = (value & 0x20000000u) != 0;
info.z_compare_base = static_cast<Prospero::ZCompareBase>(
value & Prospero::GpuEnumValue(Prospero::ZCompareBase::kBitMask));
value & static_cast<uint32_t>(Prospero::ZCompareBase::kBitMask));
return info;
}
@@ -167,7 +167,7 @@ struct DepthZInfo {
};
struct DepthStencilInfo {
uint32_t format = 0;
Prospero::StencilFormat format = Prospero::StencilFormat::kInvalid;
Prospero::TextureCompatibleStencil texture_compatibility =
Prospero::TextureCompatibleStencil::kDisable;
bool expclear_enabled = false;
@@ -176,9 +176,9 @@ struct DepthStencilInfo {
[[nodiscard]] static DepthStencilInfo Decode(uint32_t value) {
DepthStencilInfo info;
info.format = value & 0x1u;
info.format = static_cast<Prospero::StencilFormat>(value & 0x1u);
info.texture_compatibility = static_cast<Prospero::TextureCompatibleStencil>(
value & Prospero::GpuEnumValue(Prospero::TextureCompatibleStencil::kBitMask));
value & static_cast<uint32_t>(Prospero::TextureCompatibleStencil::kBitMask));
info.partially_resident = (value & 0x00001000u) != 0;
info.expclear_enabled = (value & 0x08000000u) != 0;
info.htile_stencil_disabled = (value & 0x20000000u) != 0;
@@ -1029,8 +1029,8 @@ public:
void Reset() { *this = UserConfig(); }
void SetPrimitiveType(uint32_t prim_type) { m_prim_type = prim_type; }
[[nodiscard]] uint32_t GetPrimType() const { return m_prim_type; }
void SetPrimitiveType(Prospero::PrimitiveType prim_type) { m_prim_type = prim_type; }
[[nodiscard]] Prospero::PrimitiveType GetPrimType() const { return m_prim_type; }
void SetIndexOffset(uint32_t index_offset) { m_index_offset = index_offset; }
[[nodiscard]] uint32_t GetIndexOffset() const { return m_index_offset; }
void SetObjectId(uint32_t object_id) { m_object_id = object_id; }
@@ -1049,9 +1049,9 @@ public:
void SetGdsOaAddress(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].address = value; }
private:
uint32_t m_prim_type = 0;
uint32_t m_index_offset = 0;
uint32_t m_object_id = 0;
Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone;
uint32_t m_index_offset = 0;
uint32_t m_object_id = 0;
GeControl m_ge_cntl;
GeUserVgprEn m_ge_user_vgpr_en;
+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;
+38 -35
View File
@@ -265,7 +265,7 @@ static bool GetMipTailLayout(const TileBlockLayout& block, MipTailLayout& out) {
return false;
}
bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& out) {
bool TileGetTextureElementLayout(Prospero::BufferFormat format, TileTextureElementLayout& out) {
if (const auto bytes = Prospero::NumBytesPerElement(format); std::has_single_bit(bytes)) {
out = {bytes, 1, 1};
return true;
@@ -277,10 +277,10 @@ bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& out)
return false;
}
bool TileGetTextureBlockLayout(uint32_t format, uint32_t tile, bool volume,
bool TileGetTextureBlockLayout(Prospero::BufferFormat format, Prospero::TileMode tile, bool volume,
TileTextureBlockLayout& out) {
TileBlockFamily family = TileBlockFamily::Count;
switch (static_cast<Prospero::TileMode>(tile)) {
switch (tile) {
case Prospero::TileMode::kStandard256B:
if (!volume) family = TileBlockFamily::Standard256B;
break;
@@ -1130,15 +1130,18 @@ static bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& hti
return true;
}
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
uint32_t stencil_format, bool htile, TileSizeAlign& stencil_size,
TileSizeAlign& htile_size, TileSizeAlign& depth_size,
uint32_t num_fragments_log2) {
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
Prospero::DepthFormat z_format, Prospero::StencilFormat stencil_format,
bool htile, TileSizeAlign& stencil_size, TileSizeAlign& htile_size,
TileSizeAlign& depth_size, uint32_t num_fragments_log2) {
EXIT_IF(pitch != 0);
// Prospero derives uncompressed depth/stencil as independent 64 KiB block surfaces.
if (width > 0 && width <= 16384 && height > 0 && height <= 16384 &&
(z_format == 1 || z_format == 3) && stencil_format <= 1 && num_fragments_log2 <= 3) {
const uint32_t depth_bytes = z_format == 1 ? 2u : 4u;
(z_format == Prospero::DepthFormat::kZ16 || z_format == Prospero::DepthFormat::kZ32F) &&
(stencil_format == Prospero::StencilFormat::kInvalid ||
stencil_format == Prospero::StencilFormat::k8UInt) &&
num_fragments_log2 <= 3) {
const uint32_t depth_bytes = z_format == Prospero::DepthFormat::kZ16 ? 2u : 4u;
uint32_t depth_block_width = 0;
uint32_t depth_block_height = 0;
uint32_t stencil_block_width = 0;
@@ -1146,7 +1149,7 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t
const bool valid_blocks =
Gen5Msaa64KBBlockSizeFromElementBytes(depth_bytes, num_fragments_log2,
&depth_block_width, &depth_block_height) &&
(stencil_format == 0 ||
(stencil_format == Prospero::StencilFormat::kInvalid ||
Gen5Msaa64KBBlockSizeFromElementBytes(1, num_fragments_log2, &stencil_block_width,
&stencil_block_height));
const uint32_t fragments = 1u << num_fragments_log2;
@@ -1155,7 +1158,7 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t
AlignUp(height, depth_block_height) * depth_bytes * fragments
: 0;
const uint64_t stencil_bytes_total =
stencil_format == 1 && valid_blocks
stencil_format == Prospero::StencilFormat::k8UInt && valid_blocks
? static_cast<uint64_t>(AlignUp(width, stencil_block_width)) *
AlignUp(height, stencil_block_height) * fragments
: 0;
@@ -1163,7 +1166,7 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t
const bool htile_valid = !htile || TileGetHtileSize(width, height, calculated_htile);
if (depth_bytes_total <= UINT32_MAX && stencil_bytes_total <= UINT32_MAX && htile_valid) {
depth_size = {static_cast<uint32_t>(depth_bytes_total), 65536};
stencil_size = stencil_format == 1
stencil_size = stencil_format == Prospero::StencilFormat::k8UInt
? TileSizeAlign {static_cast<uint32_t>(stencil_bytes_total), 65536}
: TileSizeAlign {};
htile_size = calculated_htile;
@@ -1236,31 +1239,29 @@ bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitc
if (levels > max_levels) {
return false;
}
uint32_t format = 0;
auto format = Prospero::BufferFormat::kInvalid;
switch (bytes_per_element) {
case 1: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm); break;
case 2: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm); break;
case 4: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); break;
case 8: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float); break;
case 16: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float); break;
case 1: format = Prospero::BufferFormat::k8UNorm; break;
case 2: format = Prospero::BufferFormat::k16UNorm; break;
case 4: format = Prospero::BufferFormat::k32Float; break;
case 8: format = Prospero::BufferFormat::k16_16_16_16Float; break;
case 16: format = Prospero::BufferFormat::k32_32_32_32Float; break;
default: return false;
}
TileGetTextureSize(format, width, height, levels,
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget), &total_size,
level_sizes, padded_size);
TileGetTextureSize(format, width, height, levels, Prospero::TileMode::kRenderTarget,
&total_size, level_sizes, padded_size);
return total_size.size != 0 && total_size.align == 65536;
}
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t levels,
uint32_t tile, TileSizeAlign* total_size, TileSizeOffset* level_sizes,
TilePaddedSize* padded_size) {
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(levels == 0 || levels > 16);
TileTextureElementLayout element {};
if (static_cast<Prospero::TileMode>(tile) == Prospero::TileMode::kLinear &&
TileGetTextureElementLayout(format, element)) {
if (tile == Prospero::TileMode::kLinear && TileGetTextureElementLayout(format, element)) {
uint32_t mip_pitch[16] {};
uint32_t mip_height[16] {};
uint32_t mip_size[16] {};
@@ -1302,26 +1303,27 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32
}
if (total_size != nullptr && total_size->size == 0) {
std::vector<std::string> list;
list.push_back(fmt::format("format = {}", format));
list.push_back(fmt::format("format = {}", static_cast<uint32_t>(format)));
list.push_back(fmt::format("width = {}", width));
list.push_back(fmt::format("height = {}", height));
list.push_back(fmt::format("levels = {}", levels));
list.push_back(fmt::format("tile = {}", tile));
list.push_back(fmt::format("tile = {}", static_cast<uint32_t>(tile)));
EXIT("unknown format:\n%s\n", Common::Concat(list, '\n').c_str());
}
}
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
uint32_t levels, uint32_t tile, bool volume_texture,
TileSizeAlign& total_size) {
void TileGetTextureTotalSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t depth, uint32_t levels, Prospero::TileMode tile,
bool volume_texture, TileSizeAlign& total_size) {
EXIT_NOT_IMPLEMENTED(depth == 0);
if (volume_texture && static_cast<Prospero::TileMode>(tile) != Prospero::TileMode::kLinear) {
if (volume_texture && tile != Prospero::TileMode::kLinear) {
TileSurfaceLayout layout {};
const TileSurfaceDescription description {
format, tile, TileSurfaceDimension::Dim3D, width, height, depth, levels, 1};
if (!TileGetTiledTextureLayout(description, layout)) {
EXIT("unsupported 3D texture layout: format=%u tile=%u extent=%ux%ux%u levels=%u\n",
format, tile, width, height, depth, levels);
static_cast<uint32_t>(format), static_cast<uint32_t>(tile), width, height, depth,
levels);
}
EXIT_NOT_IMPLEMENTED(layout.total_size > UINT32_MAX);
total_size.size = static_cast<uint32_t>(layout.total_size);
@@ -1337,9 +1339,10 @@ void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, u
total_size.size = static_cast<uint32_t>(total);
}
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t tile) {
uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
Prospero::TileMode tile) {
uint32_t pitch = width;
switch (static_cast<Prospero::TileMode>(tile)) {
switch (tile) {
case Prospero::TileMode::kLinear: {
TileTextureElementLayout element {};
if (TileGetTextureElementLayout(format, element) && element.texel_width == 1 &&
+23 -21
View File
@@ -3,6 +3,7 @@
#include "common/abi.h"
#include "common/common.h"
#include "graphics/guest_gpu/gpu_defs.h"
namespace Libs::Graphics {
@@ -65,14 +66,14 @@ enum class TileSurfaceDimension : uint32_t {
};
struct TileSurfaceDescription {
uint32_t format = 0;
uint32_t tile_mode = 0;
TileSurfaceDimension dimension = TileSurfaceDimension::Dim2D;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1;
uint32_t levels = 1;
uint32_t layers = 1;
Prospero::BufferFormat format = Prospero::BufferFormat::kInvalid;
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
TileSurfaceDimension dimension = TileSurfaceDimension::Dim2D;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1;
uint32_t levels = 1;
uint32_t layers = 1;
};
// Width/height fields are measured in addressable elements, not texture texels.
@@ -98,8 +99,8 @@ struct TileSurfaceLayout {
bool TileGetBlockLayout(TileBlockFamily family, uint32_t bytes_per_element,
TileBlockLayout& layout);
bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& layout);
bool TileGetTextureBlockLayout(uint32_t format, uint32_t tile, bool volume,
bool TileGetTextureElementLayout(Prospero::BufferFormat format, TileTextureElementLayout& layout);
bool TileGetTextureBlockLayout(Prospero::BufferFormat format, Prospero::TileMode tile, bool volume,
TileTextureBlockLayout& layout);
bool TileGetTiledTextureLayout(const TileSurfaceDescription& description,
TileSurfaceLayout& layout);
@@ -110,10 +111,10 @@ bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t b
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
uint32_t block_z, uint32_t& byte_offset);
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
uint32_t stencil_format, bool htile, TileSizeAlign& stencil_size,
TileSizeAlign& htile_size, TileSizeAlign& depth_size,
uint32_t num_fragments_log2 = 0);
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
Prospero::DepthFormat z_format, Prospero::StencilFormat stencil_format,
bool htile, TileSizeAlign& stencil_size, TileSizeAlign& htile_size,
TileSizeAlign& depth_size, uint32_t num_fragments_log2 = 0);
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
uint32_t num_fragments_log2 = 0);
uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
@@ -125,13 +126,14 @@ bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t
uint32_t bytes_per_element, uint32_t levels,
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
TilePaddedSize* padded_size);
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t levels,
uint32_t tile, TileSizeAlign* total_size, TileSizeOffset* level_sizes,
TilePaddedSize* padded_size);
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
uint32_t levels, uint32_t tile, bool volume_texture,
TileSizeAlign& total_size);
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t tile);
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size);
void TileGetTextureTotalSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t depth, uint32_t levels, Prospero::TileMode tile,
bool volume_texture, TileSizeAlign& total_size);
uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
Prospero::TileMode tile);
} // namespace Libs::Graphics
+9 -8
View File
@@ -1,5 +1,6 @@
#include "graphics/host_gpu/pageManager.h"
#include "common/platform/sysDbg.h"
#include "graphics/host_gpu/regionDefinitions.h"
#include "kernel/memory.h"
@@ -52,20 +53,20 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
std::fputs("PageManager fail-fast: ", stderr);
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
std::fputc('\n', stderr);
#if !defined(__APPLE__)
void* frames[16] {};
int frame_count = static_cast<int>(std::size(frames));
SysStackWalk(frames, &frame_count);
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
void* frames[16] {};
const auto frame_count =
CaptureStackBackTrace(0, static_cast<DWORD>(std::size(frames)), frames, nullptr);
const auto image_base = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
for (uint16_t i = 0; i < frame_count; i++) {
for (int i = 0; i < frame_count; i++) {
const auto address = reinterpret_cast<uintptr_t>(frames[i]);
std::fprintf(stderr, " frame[%u]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
std::fprintf(stderr, " frame[%d]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
address, address >= image_base ? address - image_base : 0);
}
#elif !defined(__APPLE__)
void* frames[16] {};
const int frame_count = ::backtrace(frames, static_cast<int>(std::size(frames)));
#else
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
#endif
#endif
std::fflush(stderr);
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
+135 -189
View File
@@ -22,30 +22,9 @@ namespace Libs::Graphics {
namespace {
thread_local const void* g_cache_lock_owner = nullptr;
constexpr uint64_t MiB = 1024 * 1024;
constexpr uint64_t GdsBufferSize = 64 * 1024;
class FaultSafeCacheLock final {
public:
FaultSafeCacheLock(const void* owner, Common::Mutex& mutex): m_mutex(mutex) {
if (g_cache_lock_owner != nullptr) {
EXIT("BufferCache: recursive cache lock acquisition\n");
}
g_cache_lock_owner = owner;
m_mutex.Lock();
}
~FaultSafeCacheLock() {
m_mutex.Unlock();
g_cache_lock_owner = nullptr;
}
private:
Common::Mutex& m_mutex;
};
} // namespace
uint64_t BufferCache::AlignDown(uint64_t value) noexcept {
@@ -191,21 +170,18 @@ void BufferCache::QueueGarbageDownload(std::span<const DownloadCopy> copies, Ret
m_scheduler.DeferOperation(
[this, downloads = std::move(downloads), retire = std::move(retire)]() mutable {
PublishDownloads(downloads);
{
FaultSafeCacheLock lock(this, m_mutex);
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
// interval on those pages was downloaded and removed. Clearing the original
// query therefore cannot orphan a dirty sibling on an edge page.
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
}
m_memory_tracker.UntrackMemory(retire.address, retire.size);
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
// interval on those pages was downloaded and removed. Clearing the original
// query therefore cannot orphan a dirty sibling on an edge page.
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
}
m_memory_tracker.UntrackMemory(retire.address, retire.size);
});
}
@@ -276,9 +252,6 @@ void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
size > TRACKER_ADDRESS_SIZE - vaddr) {
EXIT("BufferCache: invalid memory-invalidation range\n");
}
if (!IsRegionRegistered(vaddr, size)) {
return;
}
m_memory_tracker.InvalidateRegion(vaddr, size,
[this, vaddr, size] { ReadMemory(vaddr, size, true); });
}
@@ -298,60 +271,58 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) {
}
void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) {
// CPU invalidation reaches this point only for a GPU-owned tracker page. Resolve the exact
// Buffer owner on the GPU thread so the cache index remains single-thread-owned.
if (is_write && !IsRegionRegistered(vaddr, size)) {
return;
}
std::vector<DownloadCopy> copies;
{
FaultSafeCacheLock lock(this, m_mutex);
m_memory_tracker.ForEachDownloadRange<false>(
vaddr, size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"memory invalidation");
},
[&](uint64_t address, uint64_t bytes) noexcept {
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
for (uint64_t copied = 0; copied < range.size;) {
const auto copy_address = range.address + copied;
auto owner = m_buffers.upper_bound(copy_address);
if (owner == m_buffers.begin()) {
EXIT("BufferCache: invalidation readback has no buffer owner\n");
}
auto& cached = *std::prev(owner)->second;
if (!cached.buffer->IsInBounds(copy_address, 1)) {
EXIT(
"BufferCache: invalidation readback is outside its buffer owner\n");
}
const auto copy_size = std::min(range.size - copied,
cached.vaddr + cached.size - copy_address);
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
copy_address, copy_size});
copied += copy_size;
m_memory_tracker.ForEachDownloadRange<false>(
vaddr, size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"memory invalidation");
},
[&](uint64_t address, uint64_t bytes) noexcept {
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
for (uint64_t copied = 0; copied < range.size;) {
const auto copy_address = range.address + copied;
auto owner = m_buffers.upper_bound(copy_address);
if (owner == m_buffers.begin()) {
EXIT("BufferCache: invalidation readback has no buffer owner\n");
}
auto& cached = *std::prev(owner)->second;
if (!cached.buffer->IsInBounds(copy_address, 1)) {
EXIT("BufferCache: invalidation readback is outside its buffer owner\n");
}
const auto copy_size =
std::min(range.size - copied, cached.vaddr + cached.size - copy_address);
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
copy_address, copy_size});
copied += copy_size;
}
});
if (copies.empty()) {
if (!is_write) {
return;
}
// A preceding read fault can consume the last GPU-owned copy after this write
// invalidation has already chosen to flush. Complete the CPU ownership handoff even
// though this callback no longer has bytes to download.
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
}
});
if (copies.empty()) {
if (!is_write) {
return;
}
// A preceding read fault can consume the last GPU-owned copy after this write invalidation
// has already chosen to flush. Complete the CPU ownership handoff even though this callback
// no longer has bytes to download.
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
return;
}
auto downloads = RecordDownloads(copies);
m_scheduler.FinishCurrent();
PublishDownloads(downloads);
{
FaultSafeCacheLock lock(this, m_mutex);
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
// The enumeration above covered whole dirty pages and every exact interval on them.
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
if (is_write) {
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
}
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
// The enumeration above covered whole dirty pages and every exact interval on them.
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
if (is_write) {
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
}
}
@@ -362,43 +333,40 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
std::vector<DownloadCopy> copies;
std::vector<std::pair<uint64_t, uint64_t>> modified_buffers;
std::vector<std::pair<uint64_t, uint64_t>> retired_buffers;
{
FaultSafeCacheLock lock(this, m_mutex);
for (const auto& [begin, cached]: m_buffers) {
if (vaddr < begin + cached->size && begin < vaddr + size) {
retired_buffers.emplace_back(begin, cached->size);
}
for (const auto& [begin, cached]: m_buffers) {
if (vaddr < begin + cached->size && begin < vaddr + size) {
retired_buffers.emplace_back(begin, cached->size);
}
for (const auto& [begin, cached]: m_buffers) {
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
continue;
}
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
if (dirty.empty()) {
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
}
modified_buffers.emplace_back(begin, cached->size);
}
for (const auto& [begin, cached]: m_buffers) {
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
continue;
}
for (const auto& [begin, bytes]: modified_buffers) {
auto owner = m_buffers.find(begin);
if (owner == m_buffers.end() || owner->second->size != bytes) {
EXIT("BufferCache: unmap owner changed during collection\n");
}
auto& cached = *owner->second;
m_memory_tracker.ForEachDownloadRange<false>(
begin, cached.size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"unmap");
},
[&](uint64_t address, uint64_t bytes) noexcept {
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
copies.push_back(
{cached.buffer, range.address - begin, range.address, range.size});
}
});
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
if (dirty.empty()) {
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
}
modified_buffers.emplace_back(begin, cached->size);
}
for (const auto& [begin, bytes]: modified_buffers) {
auto owner = m_buffers.find(begin);
if (owner == m_buffers.end() || owner->second->size != bytes) {
EXIT("BufferCache: unmap owner changed during collection\n");
}
auto& cached = *owner->second;
m_memory_tracker.ForEachDownloadRange<false>(
begin, cached.size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"unmap");
},
[&](uint64_t address, uint64_t bytes) noexcept {
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
copies.push_back(
{cached.buffer, range.address - begin, range.address, range.size});
}
});
}
if (!copies.empty()) {
auto downloads = RecordDownloads(copies);
@@ -409,29 +377,26 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
// command stream before removing such backing.
m_scheduler.FinishCurrent();
}
{
FaultSafeCacheLock lock(this, m_mutex);
for (const auto& [begin, bytes]: modified_buffers) {
m_gpu_modified_ranges.Subtract(begin, bytes);
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
}
for (const auto& [begin, bytes]: retired_buffers) {
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
}
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
EXIT("BufferCache: unmap retained dirty byte ranges\n");
}
m_memory_tracker.UntrackMemory(vaddr, size);
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
if (it->second->size > m_total_used_memory) {
EXIT("BufferCache: allocation accounting underflow\n");
}
m_total_used_memory -= it->second->size;
it = m_buffers.erase(it);
} else {
++it;
for (const auto& [begin, bytes]: modified_buffers) {
m_gpu_modified_ranges.Subtract(begin, bytes);
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
}
for (const auto& [begin, bytes]: retired_buffers) {
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
}
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
EXIT("BufferCache: unmap retained dirty byte ranges\n");
}
m_memory_tracker.UntrackMemory(vaddr, size);
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
if (it->second->size > m_total_used_memory) {
EXIT("BufferCache: allocation accounting underflow\n");
}
m_total_used_memory -= it->second->size;
it = m_buffers.erase(it);
} else {
++it;
}
}
}
@@ -539,29 +504,22 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
(void)m_texture_cache.InvalidateMemoryFromGPU(vaddr, size, true);
}
std::shared_ptr<Buffer> owner;
uint64_t offset = 0;
{
FaultSafeCacheLock lock(this, m_mutex);
auto& cached = GetOrCreateBuffer(command, vaddr, size);
std::vector<std::pair<uint64_t, uint64_t>> uploads;
m_memory_tracker.ForEachUploadRange(
vaddr, size, is_written,
[&](uint64_t address, uint64_t bytes) noexcept {
uploads.emplace_back(address, bytes);
},
[&]() noexcept {
for (const auto& [address, bytes]: uploads) {
Upload(command, *cached.buffer, cached.buffer->Offset(address),
reinterpret_cast<const void*>(address), bytes);
}
});
if (is_written) {
m_gpu_modified_ranges.Add(vaddr, size);
}
owner = cached.buffer;
offset = cached.buffer->Offset(vaddr);
auto& cached = GetOrCreateBuffer(command, vaddr, size);
std::vector<std::pair<uint64_t, uint64_t>> uploads;
m_memory_tracker.ForEachUploadRange(
vaddr, size, is_written,
[&](uint64_t address, uint64_t bytes) noexcept { uploads.emplace_back(address, bytes); },
[&]() noexcept {
for (const auto& [address, bytes]: uploads) {
Upload(command, *cached.buffer, cached.buffer->Offset(address),
reinterpret_cast<const void*>(address), bytes);
}
});
if (is_written) {
m_gpu_modified_ranges.Add(vaddr, size);
}
auto owner = cached.buffer;
uint64_t offset = cached.buffer->Offset(vaddr);
if (is_formatted && is_read && !is_written) {
(void)SynchronizeBufferFromImage(*owner, vaddr, size);
}
@@ -569,19 +527,14 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
}
std::shared_ptr<Buffer> BufferCache::ObtainNullBuffer() {
std::shared_ptr<Buffer> buffer;
{
FaultSafeCacheLock lock(this, m_mutex);
if (m_null_buffer != nullptr) {
return m_null_buffer;
}
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal,
0, AllFlags, 16);
buffer = m_null_buffer;
if (m_null_buffer != nullptr) {
return m_null_buffer;
}
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal, 0,
AllFlags, 16);
const std::array<uint8_t, 16> zeros {};
Upload(m_scheduler.Current(), *buffer, 0, zeros.data(), zeros.size());
return buffer;
Upload(m_scheduler.Current(), *m_null_buffer, 0, zeros.data(), zeros.size());
return m_null_buffer;
}
ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
@@ -599,11 +552,10 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
};
{
FaultSafeCacheLock lock(this, m_mutex);
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool has_dirty_buffer_source = !dirty.empty();
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool has_dirty_buffer_source = !dirty.empty();
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
"image source");
@@ -674,10 +626,9 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
}
m_staging_buffer.Commit();
FaultSafeCacheLock lock(this, m_mutex);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool has_dirty_buffer_source = !dirty.empty();
auto owner = find_owner();
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool has_dirty_buffer_source = !dirty.empty();
auto owner = find_owner();
if (has_dirty_buffer_source && owner == m_buffers.end()) {
EXIT("BufferCache: GPU-dirty image source lost its native owner\n");
}
@@ -710,8 +661,7 @@ void BufferCache::WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data)
}
Libs::LibKernel::Memory::WriteBacking(vaddr, data.data(), data.size());
FaultSafeCacheLock lock(this, m_mutex);
const auto end = vaddr + data.size();
const auto end = vaddr + data.size();
for (auto& [address, cached]: m_buffers) {
const auto cached_end = address + cached->size;
const auto begin = std::max(vaddr, address);
@@ -772,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))) {
@@ -836,7 +786,6 @@ bool BufferCache::IsRegionRegistered(uint64_t vaddr, uint64_t size) {
size > TRACKER_ADDRESS_SIZE - vaddr) {
EXIT("BufferCache: invalid registered-region query\n");
}
FaultSafeCacheLock lock(this, m_mutex);
// Cached buffers are ordered and non-overlapping. The last buffer beginning before the query
// end is therefore the only possible intersection.
const auto candidate = m_buffers.lower_bound(vaddr + size);
@@ -852,7 +801,6 @@ bool BufferCache::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
}
bool BufferCache::HasGpuDirtyBytes(uint64_t vaddr, uint64_t size) {
FaultSafeCacheLock lock(this, m_mutex);
return !m_gpu_modified_ranges.Intersections(vaddr, size).empty();
}
@@ -876,7 +824,6 @@ void BufferCache::RunGarbageCollector() {
std::vector<RetiredBuffer> retires;
std::vector<std::pair<RetiredBuffer, std::vector<DownloadCopy>>> dirty_retires;
{
FaultSafeCacheLock lock(this, m_mutex);
std::vector<uint64_t> candidates;
for (const auto& [address, owner]: m_buffers) {
const auto& cached = *owner;
@@ -924,7 +871,6 @@ void BufferCache::RunGarbageCollector() {
QueueGarbageDownload(copies, std::move(retire));
}
FaultSafeCacheLock lock(this, m_mutex);
for (const auto& retire: retires) {
auto found = m_buffers.find(retire.address);
if (found == m_buffers.end() || found->second->size != retire.size ||
+1 -2
View File
@@ -3,7 +3,6 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/threads.h"
#include "graphics/host_gpu/memoryTracker.h"
#include "graphics/host_gpu/rangeSet.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
@@ -60,6 +59,7 @@ public:
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds = false);
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds = false,
bool src_gds = false);
// Cache-index and exact dirty-range queries require GPU-thread serialization.
[[nodiscard]] bool IsRegionRegistered(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasGpuDirtyBytes(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
@@ -99,7 +99,6 @@ private:
GraphicContext& m_graphics;
CommandScheduler& m_scheduler;
Buffer m_gds_buffer;
Common::Mutex m_mutex;
std::shared_ptr<Buffer> m_null_buffer;
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
RangeSet m_gpu_modified_ranges;
+7 -7
View File
@@ -516,7 +516,7 @@ ImageId TextureCache::GetNullImage(const ImageDesc& desc) {
info.pitch = 1;
info.bytes_per_block = std::max(desc.info.bytes_per_block, 1u);
info.samples = 1;
info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
info.tile_mode = Prospero::TileMode::kLinear;
info.mip_layout[0] = {0, info.bytes_per_block, 1, 1};
const auto id = InsertImage(info);
m_null_images.emplace(format, id);
@@ -822,7 +822,8 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
requested.data.address, requested.resources.levels, requested.resources.layers,
cached.info.resources.levels, cached.info.resources.layers, requested.data.size,
cached.info.data.size, static_cast<uint32_t>(requested.type),
static_cast<uint32_t>(cached.info.type), requested.tile_mode, cached.info.tile_mode);
static_cast<uint32_t>(cached.info.type), static_cast<uint32_t>(requested.tile_mode),
static_cast<uint32_t>(cached.info.tile_mode));
}
if (requested.data.address > cached.info.data.address) {
@@ -905,7 +906,7 @@ TextureCache::ColorTransferPlan
TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
TransferDirection direction) const {
const auto& info = image.info;
uint32_t format = info.guest_format;
auto format = info.guest_format;
uint32_t layers = info.TransferLayers();
bool volume = info.IsVolume();
bool allow_depth_tile = direction == TransferDirection::Upload;
@@ -957,8 +958,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
info.resources.levels, layers, info.tile_mode,
info.data.size, allow_depth_tile, volume, owner);
plan.regions = TextureBuildImageCopies(plan.layout);
plan.tiled = static_cast<Prospero::TileMode>(plan.layout.surface.description.tile_mode) !=
Prospero::TileMode::kLinear;
plan.tiled = plan.layout.surface.description.tile_mode != Prospero::TileMode::kLinear;
if (plan.tiled) {
if (!TextureBuildGpuTileInfos(info.data.size, plan.regions, plan.layout,
info.resources.levels, plan.tiles)) {
@@ -1007,7 +1007,7 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
" size=0x%016" PRIx64 " format=%u tile=%u family=%u extent=%ux%ux%u "
"pitch=%u levels=%u layers=%u samples=%u\n",
static_cast<uint32_t>(desc.type), info.data.address, info.data.size,
info.guest_format, info.tile_mode,
static_cast<uint32_t>(info.guest_format), static_cast<uint32_t>(info.tile_mode),
static_cast<uint32_t>(plan.layout.surface.texture.block.family), info.extent.width,
info.extent.height, info.extent.depth, info.pitch, info.resources.levels,
info.resources.layers, info.samples);
@@ -1045,7 +1045,7 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
copy.bufferImageHeight = info.extent.height;
copy.imageSubresource = {vk::ImageAspectFlagBits::eDepth, 0, layer, 1};
copy.imageExtent = {info.extent.width, info.extent.height, 1};
if (static_cast<Prospero::TileMode>(info.tile_mode) != Prospero::TileMode::kLinear) {
if (info.tile_mode != Prospero::TileMode::kLinear) {
tiles.push_back({block.family, block.bytes_per_element, offset, full_slice_size, offset,
full_slice_size, 0, info.extent.width, info.extent.height, 1,
info.pitch});
@@ -101,8 +101,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
" attrib3_tile=0x%08" PRIx32 " attrib3_dim=0x%08" PRIx32 " fmt=0x%08" PRIx32
" nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 "\n",
rt_slot, rt.base.addr, mask, rt.attrib2.width, rt.attrib2.height,
rt.attrib3.tile_mode, rt.attrib3.dimension, rt.info.format, rt.info.channel_type,
rt.info.channel_order);
static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
static_cast<uint32_t>(rt.info.channel_order));
}
}
@@ -129,17 +130,16 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
if (volume && samples != 1) {
EXIT("multisampled 3D render targets are unsupported\n");
}
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
const bool standard64 =
rt.attrib3.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
const bool standard64 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard64KB;
switch (rt.attrib3.tile_mode) {
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget):
case Prospero::TileMode::kLinear:
case Prospero::TileMode::kStandard64KB:
case Prospero::TileMode::kRenderTarget:
tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode);
break;
default: EXIT("unknown tile mode: %u\n", rt.attrib3.tile_mode);
default: EXIT("unknown tile mode: %u\n", static_cast<uint32_t>(rt.attrib3.tile_mode));
}
if (!tile && levels > 1) {
EXIT("linear mipmapped render targets are unsupported\n");
@@ -180,8 +180,8 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
if (volume) {
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
} else if (standard64) {
pitch = TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float),
width, rt.attrib3.tile_mode);
pitch =
TileGetTexturePitch(Prospero::BufferFormat::k32Float, width, rt.attrib3.tile_mode);
} else {
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
}
@@ -207,7 +207,7 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
1};
if (!tile || !TileGetTiledTextureLayout(description, volume_layout)) {
EXIT("unsupported 3D render-target layout: %ux%ux%u levels=%u tile=%u\n", width, height,
depth, levels, rt.attrib3.tile_mode);
depth, levels, static_cast<uint32_t>(rt.attrib3.tile_mode));
}
size = volume_layout.block_slice_size;
backing_size = volume_layout.total_size;
@@ -215,9 +215,8 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
TileSizeAlign layout {};
bool valid_layout = false;
if (standard64) {
TileGetTextureSize(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), width,
height, levels, rt.attrib3.tile_mode, &layout, mip_sizes,
mip_padded);
TileGetTextureSize(Prospero::BufferFormat::k32Float, width, height, levels,
rt.attrib3.tile_mode, &layout, mip_sizes, mip_padded);
valid_layout = layout.size != 0 && layout.align == 65536;
} else {
valid_layout =
@@ -278,8 +277,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
" extent=%ux%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u"
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 " samples=%u tile=%s\n",
rt_slot, rt.base.addr, backing_size, width, height, depth, rt.view.current_mip_level,
view_extent.width, view_extent.height, levels, pitch, rt.info.format,
rt.info.channel_type, rt.info.channel_order, samples, tile ? "tiled" : "linear");
view_extent.width, view_extent.height, levels, pitch,
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
static_cast<uint32_t>(rt.info.channel_order), samples, tile ? "tiled" : "linear");
}
TextureCache::ImageDesc desc {};
+44 -37
View File
@@ -76,8 +76,8 @@ void uc_print(const char* func, const HW::UserConfig& uc) {
"\t en_user_vgpr1 = %s\n"
"\t en_user_vgpr2 = %s\n"
"\t en_user_vgpr3 = %s\n",
uc.GetPrimType(), uc.GetIndexOffset(), uc.GetObjectId(), ge_cntl.primitive_group_size,
ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false",
static_cast<uint32_t>(uc.GetPrimType()), uc.GetIndexOffset(), uc.GetObjectId(),
ge_cntl.primitive_group_size, ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false",
user_en.vgpr2 ? "true" : "false", user_en.vgpr3 ? "true" : "false");
}
@@ -141,11 +141,12 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
fmt::format("\t info.cmask_addr_type = 0x{:08x}\n", rt.info.cmask_addr_type));
dst.push_back(fmt::format("\t info.alt_tile_mode = {}\n",
rt.info.alt_tile_mode ? "true" : "false"));
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n", rt.info.format));
dst.push_back(
fmt::format("\t info.channel_type = 0x{:08x}\n", rt.info.channel_type));
dst.push_back(
fmt::format("\t info.channel_order = 0x{:08x}\n", rt.info.channel_order));
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n",
static_cast<uint32_t>(rt.info.format)));
dst.push_back(fmt::format("\t info.channel_type = 0x{:08x}\n",
static_cast<uint32_t>(rt.info.channel_type)));
dst.push_back(fmt::format("\t info.channel_order = 0x{:08x}\n",
static_cast<uint32_t>(rt.info.channel_order)));
dst.push_back(fmt::format("\t info.blend_bypa = {}\n",
rt.info.blend_bypass ? "true" : "false"));
dst.push_back(fmt::format("\t info.blend_clamp = {}\n",
@@ -154,10 +155,10 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
rt.info.round_mode ? "true" : "false"));
dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
rt.attrib.force_dest_alpha_to_one ? "true" : "false"));
dst.push_back(
fmt::format("\t attrib.tile_mode = 0x{:08x}\n", rt.attrib.tile_mode));
dst.push_back(
fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n", rt.attrib.fmask_tile_mode));
dst.push_back(fmt::format("\t attrib.tile_mode = 0x{:08x}\n",
static_cast<uint32_t>(rt.attrib.tile_mode)));
dst.push_back(fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n",
static_cast<uint32_t>(rt.attrib.fmask_tile_mode)));
dst.push_back(
fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples));
dst.push_back(
@@ -168,8 +169,8 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
dst.push_back(
fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels));
dst.push_back(fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth));
dst.push_back(
fmt::format("\t attrib3.tile_mode = 0x{:08x}\n", rt.attrib3.tile_mode));
dst.push_back(fmt::format("\t attrib3.tile_mode = 0x{:08x}\n",
static_cast<uint32_t>(rt.attrib3.tile_mode)));
dst.push_back(
fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension));
dst.push_back(fmt::format("\t attrib3.cmask_pipe_aligned = {}\n",
@@ -212,22 +213,22 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
return dst;
}
static bool RenderIsColorTileMode(uint32_t tile_mode) {
static bool RenderIsColorTileMode(Prospero::TileMode tile_mode) {
// AGC CxRenderTarget::TileMode shifted by CB_COLOR*_ATTRIB3.COLOR_SW_MODE.
switch (tile_mode) {
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B):
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB):
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
case Prospero::GpuEnumValue(Prospero::TileMode::kPrt):
case Prospero::GpuEnumValue(Prospero::TileMode::kDepth):
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget): return true;
case Prospero::TileMode::kLinear:
case Prospero::TileMode::kStandard256B:
case Prospero::TileMode::kStandard4KB:
case Prospero::TileMode::kStandard64KB:
case Prospero::TileMode::kPrt:
case Prospero::TileMode::kDepth:
case Prospero::TileMode::kRenderTarget: return true;
default: return false;
}
}
bool RenderIsColorTileModeLinear(uint32_t tile_mode) {
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode) {
return tile_mode == Prospero::TileMode::kLinear;
}
static bool RenderIsColorDimension(uint32_t dimension) {
@@ -361,7 +362,8 @@ static void RtCheck(const HW::RenderTarget& rt) {
}
}
if (!RenderIsColorTileMode(rt.attrib3.tile_mode)) {
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n", rt.attrib3.tile_mode);
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n",
static_cast<uint32_t>(rt.attrib3.tile_mode));
}
if (!RenderIsColorDimension(rt.attrib3.dimension)) {
EXIT("unknown PS5 render-target dimension: 0x%08" PRIx32 "\n", rt.attrib3.dimension);
@@ -456,13 +458,14 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
"\t height = 0x%08" PRIx32 "\n"
"\t size.x_max = 0x%04" PRIx16 "\n"
"\t size.y_max = 0x%04" PRIx16 "\n",
z.z_info.format, z.z_info.num_samples,
Prospero::GpuEnumValue(z.z_info.texture_compatibility),
static_cast<uint32_t>(z.z_info.format), z.z_info.num_samples,
static_cast<uint32_t>(z.z_info.texture_compatibility),
z.z_info.htile_acceleration ? "true" : "false",
z.z_info.expclear_enabled ? "true" : "false",
Prospero::GpuEnumValue(z.z_info.z_compare_base),
static_cast<uint32_t>(z.z_info.z_compare_base),
z.z_info.partially_resident ? "true" : "false", z.z_info.max_mip_level,
z.stencil_info.format, Prospero::GpuEnumValue(z.stencil_info.texture_compatibility),
static_cast<uint32_t>(z.stencil_info.format),
static_cast<uint32_t>(z.stencil_info.texture_compatibility),
z.stencil_info.htile_stencil_disabled ? "true" : "false",
z.stencil_info.expclear_enabled ? "true" : "false",
z.stencil_info.partially_resident ? "true" : "false", z.depth_info.addr5_swizzle_mask,
@@ -491,15 +494,16 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
EXIT_NOT_IMPLEMENTED(!z.z_info.HasValidTextureCompatibility());
EXIT_NOT_IMPLEMENTED(!z.stencil_info.HasValidTextureCompatibility());
if (z.z_info.format == 0) {
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0);
if (z.z_info.format == Prospero::DepthFormat::kInvalid) {
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kInvalid);
EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0);
EXIT_NOT_IMPLEMENTED(z.z_info.htile_acceleration != false);
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
} else {
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0x00000001 && z.z_info.format != 0x00000003);
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kZ16 &&
z.z_info.format != Prospero::DepthFormat::kZ32F);
if (z.z_info.num_samples != 0x00000000) {
static bool logged = false;
if (!logged) {
@@ -513,16 +517,17 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
}
if (z.stencil_info.format == 0) {
if (z.stencil_info.format == Prospero::StencilFormat::kInvalid) {
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
} else {
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0x00000001);
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != Prospero::StencilFormat::k8UInt);
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
}
if (z.z_info.format != 0 || z.stencil_info.format != 0) {
if (z.z_info.format != Prospero::DepthFormat::kInvalid ||
z.stencil_info.format != Prospero::StencilFormat::kInvalid) {
EXIT_NOT_IMPLEMENTED(z.depth_info.addr5_swizzle_mask != 0x00000000);
EXIT_NOT_IMPLEMENTED(z.depth_info.array_mode != 0x00000000);
@@ -557,7 +562,8 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
EXIT_NOT_IMPLEMENTED(z.z_read_base_addr != z.z_write_base_addr);
EXIT_NOT_IMPLEMENTED(z.stencil_read_base_addr != z.stencil_write_base_addr);
EXIT_NOT_IMPLEMENTED(z.z_write_base_addr == 0);
if (z.stencil_info.format != 0 && z.stencil_write_base_addr == 0) {
if (z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
z.stencil_write_base_addr == 0) {
LOGF("\t warning: stencil format is set without a stencil base address, continuing "
"without stencil attachment\n");
}
@@ -1218,8 +1224,9 @@ void hw_check(const RenderCommandBuffer& buffer) {
const auto& mrt = hw.GetRenderTarget(i);
LOGF("\t RT%u addr=0x%010" PRIx64 " mask=0x%x fmt=0x%08" PRIx32
" width=%u height=%u tile=%u\n",
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu, mrt.info.format,
mrt.attrib2.width + 1, mrt.attrib2.height + 1, mrt.attrib3.tile_mode);
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu,
static_cast<uint32_t>(mrt.info.format), mrt.attrib2.width + 1,
mrt.attrib2.height + 1, static_cast<uint32_t>(mrt.attrib3.tile_mode));
}
}
if (rc.depth_clear_enable && hw.GetDepthClearValue() != 0.0f &&
+1 -1
View File
@@ -34,7 +34,7 @@ void uc_print(const char* func, const HW::UserConfig& uc);
void uc_check(const HW::UserConfig& uc);
void sh_print(const char* func, const HW::Shader& uc);
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt);
bool RenderIsColorTileModeLinear(uint32_t tile_mode);
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode);
void hw_print(const RenderCommandBuffer& buffer);
void hw_check(const RenderCommandBuffer& buffer);
void LogDrawPhase(const char* draw_name, const char* phase);
@@ -67,9 +67,8 @@ static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t
}
static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
return fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
pass == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
depth_fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp);
constexpr auto replace_op = static_cast<uint8_t>(Prospero::StencilOp::kReplaceOp);
return fail == replace_op || pass == replace_op || depth_fail == replace_op;
}
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const RenderCommandBuffer& buffer,
@@ -103,15 +102,14 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
RenderDepthInfo& r) {
KYTY_PROFILER_FUNCTION();
(void)submit_id;
const auto& hw = buffer.GetRegisters();
const auto& z = hw.GetDepthRenderTarget();
const auto& rc = hw.GetRenderControl();
const auto& dc = hw.GetDepthControl();
const auto& sc = hw.GetStencilControl();
const auto& sm = hw.GetStencilMask();
const bool has_stencil =
z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid);
const bool depth_active =
const auto& hw = buffer.GetRegisters();
const auto& z = hw.GetDepthRenderTarget();
const auto& rc = hw.GetRenderControl();
const auto& dc = hw.GetDepthControl();
const auto& sc = hw.GetStencilControl();
const auto& sm = hw.GetStencilMask();
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
const bool depth_active =
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
const bool stencil_active = has_stencil && (dc.stencil_enable || rc.stencil_clear_enable);
if (!depth_active && !stencil_active) {
@@ -122,9 +120,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
DepthFatal("invalid PS5 depth texture-compatibility encoding");
}
const bool attachment_unbound =
z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kInvalid) &&
z.stencil_info.format == Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid) &&
z.z_info.num_samples == 0 &&
z.z_info.format == Prospero::DepthFormat::kInvalid &&
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
@@ -186,8 +183,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
DepthFatal("unsupported depth register state");
}
if (has_stencil) {
if (z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt) ||
!htile_stencil_compat || z.stencil_read_base_addr == 0 ||
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
z.stencil_read_base_addr == 0 ||
z.stencil_write_base_addr != z.stencil_read_base_addr ||
(z.stencil_read_base_addr & 0xffffu) != 0 || z.depth_view.stencil_write_disable) {
DepthFatal("unsupported stencil attachment state");
@@ -205,8 +202,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;
@@ -231,7 +226,7 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
DepthFatal("no host depth/stencil format supports required usage for %s",
VulkanToString(ideal_format).c_str());
}
const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format);
const auto guest_format = policy->guest_format;
const uint32_t bytes = policy->bytes_per_element;
const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
@@ -345,7 +340,7 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
desc.info.pitch = pitch;
desc.info.bytes_per_block = bytes;
desc.info.samples = samples;
desc.info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
desc.info.tile_mode = Prospero::TileMode::kDepth;
desc.info.mip_layout[0] = {0, r.depth_buffer_size, pitch, height};
desc.info.metadata.range = {r.htile_buffer_vaddr, r.htile_buffer_size};
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
@@ -646,13 +646,13 @@ void Validate(const ImageInfo& info) {
}
}
uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element) {
Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element) {
switch (bytes_per_element) {
case 1: return Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm);
case 2: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm);
case 4: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
case 8: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float);
case 16: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float);
case 1: return Prospero::BufferFormat::k8UNorm;
case 2: return Prospero::BufferFormat::k16UNorm;
case 4: return Prospero::BufferFormat::k32Float;
case 8: return Prospero::BufferFormat::k16_16_16_16Float;
case 16: return Prospero::BufferFormat::k32_32_32_32Float;
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
}
}
+2 -2
View File
@@ -188,8 +188,8 @@ private:
namespace ImageOps {
void Validate(const ImageInfo& info);
[[nodiscard]] uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element);
void Validate(const ImageInfo& info);
[[nodiscard]] Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element);
} // namespace ImageOps
@@ -67,14 +67,14 @@ struct ImageInfo {
ImageMetadataInfo metadata;
uint32_t htile_clear_mask = UINT32_MAX;
vk::Format pixel_format = vk::Format::eUndefined;
uint32_t guest_format = 0;
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
Prospero::ImageType type = Prospero::ImageType::kColor2D;
vk::Extent3D extent = {1, 1, 1};
ImageSubresources resources;
uint32_t pitch = 0;
uint32_t bytes_per_block = 0;
uint32_t samples = 1;
uint32_t tile_mode = 0;
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
bool bgra16 = false;
std::array<ImageMipInfo, 16> mip_layout {};
@@ -86,9 +86,7 @@ struct ImageInfo {
[[nodiscard]] bool IsBlock() const noexcept {
return Prospero::BlockCompressedBytesPerBlock(guest_format) != 0;
}
[[nodiscard]] bool IsTiled() const noexcept {
return tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
}
[[nodiscard]] bool IsTiled() const noexcept { return tile_mode != Prospero::TileMode::kLinear; }
[[nodiscard]] constexpr bool IsVolume() const noexcept {
return type == Prospero::ImageType::kColor3D;
}
@@ -235,9 +233,9 @@ inline constexpr std::array<DepthFormatPolicy, 2> DEPTH_FORMAT_POLICIES {{
}};
[[nodiscard]] inline constexpr const DepthFormatPolicy*
FindDepthFormatPolicy(uint32_t depth_format) noexcept {
FindDepthFormatPolicy(Prospero::DepthFormat depth_format) noexcept {
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
if (Prospero::GpuEnumValue(policy.depth_format) == depth_format) {
if (policy.depth_format == depth_format) {
return &policy;
}
}
@@ -245,9 +243,9 @@ FindDepthFormatPolicy(uint32_t depth_format) noexcept {
}
[[nodiscard]] inline constexpr const DepthFormatPolicy*
FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
FindGuestDepthFormatPolicy(Prospero::BufferFormat guest_format) noexcept {
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
if (Prospero::GpuEnumValue(policy.guest_format) == guest_format) {
if (policy.guest_format == guest_format) {
return &policy;
}
}
@@ -269,10 +267,11 @@ FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
return has_stencil ? policy.stencil_attachment_formats.front() : policy.depth_attachment_format;
}
[[nodiscard]] inline constexpr vk::Format DepthAttachmentFormat(uint32_t depth_format,
uint32_t stencil_format) noexcept {
[[nodiscard]] inline constexpr vk::Format
DepthAttachmentFormat(Prospero::DepthFormat depth_format,
Prospero::StencilFormat stencil_format) noexcept {
bool has_stencil = false;
switch (static_cast<Prospero::StencilFormat>(stencil_format)) {
switch (stencil_format) {
case Prospero::StencilFormat::kInvalid: break;
case Prospero::StencilFormat::k8UInt: has_stencil = true; break;
default: return vk::Format::eUndefined;
@@ -318,9 +317,9 @@ inline bool ImageInfo::IsDepth() const noexcept {
return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f);
}
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
uint32_t guest_format,
vk::Format view_format) noexcept {
[[nodiscard]] inline constexpr bool
IsSupportedSampledDepthFormat(vk::Format image_format, Prospero::BufferFormat guest_format,
vk::Format view_format) noexcept {
const auto* policy = FindGuestDepthFormatPolicy(guest_format);
return policy != nullptr && view_format == policy->sampled_view_format &&
(image_format == policy->depth_attachment_format ||
@@ -330,8 +329,7 @@ inline bool ImageInfo::IsDepth() const noexcept {
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
vk::Format view_format) noexcept {
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
if (IsSupportedSampledDepthFormat(image_format, Prospero::GpuEnumValue(policy.guest_format),
view_format)) {
if (IsSupportedSampledDepthFormat(image_format, policy.guest_format, view_format)) {
return true;
}
}
@@ -383,10 +381,10 @@ CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_t
}
struct VideoOutPixelFormatInfo {
vk::Format format = vk::Format::eUndefined;
uint32_t guest_format = 0;
uint32_t bytes_per_element = 0;
bool bgra16 = false;
vk::Format format = vk::Format::eUndefined;
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
uint32_t bytes_per_element = 0;
bool bgra16 = false;
};
struct VideoOutFormatPolicy {
@@ -396,23 +394,17 @@ struct VideoOutFormatPolicy {
inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {{
{0x8000000022000000ull,
{vk::Format::eR8G8B8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
false}},
{vk::Format::eR8G8B8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
{0x8000000000000000ull,
{vk::Format::eB8G8R8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
false}},
{vk::Format::eB8G8R8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
{0x8100000022000000ull,
{vk::Format::eA2B10G10R10UnormPack32,
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
{vk::Format::eA2B10G10R10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
{0x8100000000000000ull,
{vk::Format::eA2R10G10B10UnormPack32,
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
{vk::Format::eA2R10G10B10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
{0xc001000622000000ull,
{vk::Format::eR16G16B16A16Sfloat,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, false}},
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, false}},
{0xc001000600000000ull,
{vk::Format::eR16G16B16A16Sfloat,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, true}},
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, true}},
}};
[[nodiscard]] inline bool DecodeVideoOutPixelFormat(uint64_t pixel_format,
@@ -439,15 +431,15 @@ inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {
}
[[nodiscard]] inline constexpr bool
IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
IsSupportedDisplayRenderTargetTileMode(Prospero::TileMode tile_mode) noexcept {
return tile_mode == Prospero::TileMode::kRenderTarget;
}
[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) {
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
info.data.address == 0 || (info.data.address & 0xffffu) != 0 || info.extent.width == 0 ||
info.extent.height == 0 || info.bytes_per_block != 4 || info.resources.levels != 1 ||
info.resources.layers != 1 || info.samples != 1) {
if (info.tile_mode != Prospero::TileMode::kStandard64KB || info.data.address == 0 ||
(info.data.address & 0xffffu) != 0 || info.extent.width == 0 || info.extent.height == 0 ||
info.bytes_per_block != 4 || info.resources.levels != 1 || info.resources.layers != 1 ||
info.samples != 1) {
return false;
}
const auto expected_pitch =
@@ -460,7 +452,7 @@ IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
}
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept {
return info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
return info.tile_mode == Prospero::TileMode::kRenderTarget ||
IsSupportedStandard64RenderTarget(info);
}
@@ -14,156 +14,67 @@
namespace Libs::Graphics {
namespace {
struct RenderTargetFormatMapping {
Prospero::ChannelLayout layout;
Prospero::ChannelType type;
Prospero::ChannelOrder order;
RenderTargetFormatInfo info;
// Rows are channel order; columns are the number of physical components minus one. Unused
// selectors complete each entry to a permutation so logical write masks can be inverted.
constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = {
{Prospero::ColorMappingRgba, Prospero::ColorMappingRgba, Prospero::ColorMappingRgba,
Prospero::ColorMappingRgba},
{Prospero::ColorMappingGr, Prospero::ColorMappingRabg, Prospero::ColorMappingRgab,
Prospero::ColorMappingBgra},
{Prospero::ColorMappingBgra, Prospero::ColorMappingGr, Prospero::ColorMappingBgra,
Prospero::ColorMappingAbgr},
{Prospero::ColorMappingAgba, Prospero::ColorMappingArbg, Prospero::ColorMappingAgbr,
Prospero::ColorMappingArgb},
};
constexpr RenderTargetFormatMapping kRenderTargetFormats[] = {
{Prospero::ChannelLayout::k8,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR8Uint, 1}},
{Prospero::ChannelLayout::k8_8,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR8G8Unorm, 2}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR8G8B8A8Unorm, 4}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kSNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR8G8B8A8Snorm, 4}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kSrgb,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR8G8B8A8Srgb, 4}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kAlt,
{vk::Format::eB8G8R8A8Unorm, 4}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kSNorm,
Prospero::ChannelOrder::kAlt,
{vk::Format::eB8G8R8A8Snorm, 4}},
{Prospero::ChannelLayout::k8_8_8_8,
Prospero::ChannelType::kSrgb,
Prospero::ChannelOrder::kAlt,
{vk::Format::eB8G8R8A8Srgb, 4}},
{Prospero::ChannelLayout::k5_5_5_1,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR5G5B5A1UnormPack16, 2}},
{Prospero::ChannelLayout::k4_4_4_4,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kReversed,
{vk::Format::eB4G4R4A4UnormPack16, 2}},
{Prospero::ChannelLayout::k10_10_10_2,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eA2B10G10R10UnormPack32, 4}},
{Prospero::ChannelLayout::k10_10_10_2,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kAlt,
{vk::Format::eA2R10G10B10UnormPack32, 4}},
{Prospero::ChannelLayout::k11_11_10,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eB10G11R11UfloatPack32, 4}},
{Prospero::ChannelLayout::k5_6_5,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eB5G6R5UnormPack16, 2}},
{Prospero::ChannelLayout::k16,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16Unorm, 2}},
{Prospero::ChannelLayout::k16,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16Uint, 2}},
{Prospero::ChannelLayout::k16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16Sfloat, 2}},
{Prospero::ChannelLayout::k16_16,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16Unorm, 4}},
{Prospero::ChannelLayout::k16_16,
Prospero::ChannelType::kSNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16Snorm, 4}},
{Prospero::ChannelLayout::k16_16,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16Uint, 4}},
{Prospero::ChannelLayout::k16_16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16Sfloat, 4}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16B16A16Unorm, 8}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16B16A16Uint, 8}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16B16A16Sfloat, 8}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kAlt,
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingBgra}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kReversed,
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingAbgr}},
{Prospero::ChannelLayout::k32,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR32Sfloat, 4}},
{Prospero::ChannelLayout::k32_32,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR32G32Uint, 8}},
{Prospero::ChannelLayout::k32_32,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR32G32Sfloat, 8}},
{Prospero::ChannelLayout::k32_32_32_32,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR32G32B32A32Sfloat, 16}},
struct RenderTargetHostFormatInfo {
vk::Format format = vk::Format::eUndefined;
Prospero::ColorComponentMapping host_to_storage;
};
RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat guest_format,
Prospero::ChannelOrder order) {
if (order == Prospero::ChannelOrder::kAlt) {
switch (guest_format) {
case Prospero::BufferFormat::k8_8_8_8UNorm:
return {vk::Format::eB8G8R8A8Unorm, Prospero::ColorMappingBgra};
case Prospero::BufferFormat::k8_8_8_8SNorm:
return {vk::Format::eB8G8R8A8Snorm, Prospero::ColorMappingBgra};
case Prospero::BufferFormat::k8_8_8_8Srgb:
return {vk::Format::eB8G8R8A8Srgb, Prospero::ColorMappingBgra};
case Prospero::BufferFormat::k10_10_10_2UNorm:
return {vk::Format::eA2R10G10B10UnormPack32, Prospero::ColorMappingBgra};
default: break;
}
}
switch (guest_format) {
case Prospero::BufferFormat::k5_5_5_1UNorm:
return {vk::Format::eA1R5G5B5UnormPack16, Prospero::ColorMappingBgra};
case Prospero::BufferFormat::k1_5_5_5UNorm:
return {vk::Format::eR5G5B5A1UnormPack16, Prospero::ColorMappingAbgr};
case Prospero::BufferFormat::k4_4_4_4UNorm:
return {vk::Format::eR4G4B4A4UnormPack16, Prospero::ColorMappingAbgr};
default: return {VulkanFormat(guest_format), {}};
}
}
} // namespace
// TODO: cleanup!
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t raw_layout, uint32_t raw_type,
uint32_t raw_order) {
const auto layout = static_cast<Prospero::ChannelLayout>(raw_layout);
const auto type = static_cast<Prospero::ChannelType>(raw_type);
const auto order = static_cast<Prospero::ChannelOrder>(raw_order);
if (layout == Prospero::ChannelLayout::k8 && type == Prospero::ChannelType::kUNorm &&
raw_order <= Prospero::GpuEnumValue(Prospero::ChannelOrder::kAltReversed)) {
return {vk::Format::eR8Unorm, 1};
}
for (const auto& mapping: kRenderTargetFormats) {
if (mapping.layout == layout && mapping.type == type && mapping.order == order) {
return mapping.info;
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
Prospero::ChannelType type,
Prospero::ChannelOrder order) {
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
const auto host = ResolveRenderTargetHostFormat(encoding.buffer_format, order);
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
if (host.format != vk::Format::eUndefined && bytes != 0) {
const auto order_mapping =
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
return {host.format, bytes, host.host_to_storage.Then(order_mapping)};
}
}
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", raw_layout,
raw_type, raw_order);
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
}
namespace {
@@ -208,12 +119,12 @@ vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
return components;
}
vk::Format TextureGetFormat(uint32_t fmt) {
const auto vk_format = VulkanFormat(fmt);
vk::Format TextureGetFormat(Prospero::BufferFormat format) {
const auto vk_format = VulkanFormat(format);
if (vk_format != vk::Format::eUndefined) {
return vk_format;
}
EXIT("unknown format: fmt = %u\n", fmt);
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
return vk::Format::eUndefined;
}
@@ -271,13 +182,14 @@ uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
} // namespace
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32_t height,
uint32_t levels, uint32_t depth, uint32_t tile,
uint64_t upload_size, bool allow_depth_tile,
bool volume_texture, const char* owner) {
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
uint32_t height, uint32_t levels, uint32_t depth,
Prospero::TileMode tile, uint64_t upload_size,
bool allow_depth_tile, bool volume_texture,
const char* owner) {
TextureUploadLayout layout {};
layout.surface.description = {
fmt,
format,
tile,
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
width,
@@ -287,35 +199,39 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32
volume_texture ? 1u : depth,
};
const auto& description = layout.surface.description;
const auto tile_mode = static_cast<Prospero::TileMode>(description.tile_mode);
const auto tile_mode = description.tile_mode;
TileTextureElementLayout element {};
if (fmt == 0) {
if (format == Prospero::BufferFormat::kInvalid) {
EXIT("%s: legacy texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64
" extent=%ux%u levels=%u\n",
owner, description.tile_mode, upload_size, width, height, levels);
owner, static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
levels);
}
switch (tile_mode) {
case Prospero::TileMode::kLinear:
if (!TileGetTextureElementLayout(fmt, element)) {
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner, fmt);
if (!TileGetTextureElementLayout(format, element)) {
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
static_cast<uint32_t>(format));
}
break;
case Prospero::TileMode::kDepth:
if (!allow_depth_tile || !TileGetTiledTextureLayout(description, layout.surface)) {
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
owner, static_cast<uint32_t>(fmt), description.tile_mode, upload_size, width,
height, levels);
owner, static_cast<uint32_t>(format),
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
levels);
}
break;
default:
if (!TileGetTiledTextureLayout(description, layout.surface)) {
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
owner, static_cast<uint32_t>(fmt), description.tile_mode, upload_size, width,
height, levels);
owner, static_cast<uint32_t>(format),
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
levels);
}
break;
}
@@ -324,12 +240,12 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32
element = {layout.surface.texture.block.bytes_per_element,
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
}
layout.pitch = TileGetTexturePitch(fmt, width, description.tile_mode);
layout.pitch = TileGetTexturePitch(format, width, description.tile_mode);
if (tile_mode == Prospero::TileMode::kLinear) {
TileSizeOffset level_sizes[16] {};
TilePaddedSize padded_sizes[16] {};
TileGetTextureSize(fmt, width, height, levels, description.tile_mode, nullptr, level_sizes,
padded_sizes);
TileGetTextureSize(format, width, height, levels, description.tile_mode, nullptr,
level_sizes, padded_sizes);
for (uint32_t level = 0; level < levels; ++level) {
layout.mips[level] = {level_sizes[level].offset, level_sizes[level].size,
padded_sizes[level].width, padded_sizes[level].height};
@@ -356,10 +272,9 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
const auto depth = volume_texture ? description.depth : description.layers;
uint32_t mip_width = description.width;
uint32_t mip_height = description.height;
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(description.tile_mode) !=
Prospero::TileMode::kLinear
? description.width
: layout.pitch;
uint32_t mip_pitch = volume_texture && description.tile_mode != Prospero::TileMode::kLinear
? description.width
: layout.pitch;
std::vector<vk::BufferImageCopy> regions;
regions.reserve(GetTextureRegionCount(depth, description.levels, volume_texture));
@@ -376,8 +291,7 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
1};
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
region.imageExtent = {mip_width, mip_height, 1};
const bool linear = static_cast<Prospero::TileMode>(description.tile_mode) ==
Prospero::TileMode::kLinear;
const bool linear = description.tile_mode == Prospero::TileMode::kLinear;
if (linear) {
region.bufferRowLength = layout.mips[i].row_length;
region.bufferImageHeight = layout.mips[i].image_height;
@@ -35,12 +35,15 @@ struct TextureUploadLayout {
};
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
vk::Format TextureGetFormat(uint32_t fmt);
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t layout, uint32_t type, uint32_t order);
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32_t height,
uint32_t levels, uint32_t depth, uint32_t tile,
uint64_t upload_size, bool allow_depth_tile,
bool volume_texture, const char* owner);
vk::Format TextureGetFormat(Prospero::BufferFormat format);
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
Prospero::ChannelType type,
Prospero::ChannelOrder order);
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
uint32_t height, uint32_t levels, uint32_t depth,
Prospero::TileMode tile, uint64_t upload_size,
bool allow_depth_tile, bool volume_texture,
const char* owner);
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t levels,
@@ -59,7 +59,7 @@ static void CopyNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& so
}
static Prospero::ImageType TextureType(const ShaderTextureResource& descriptor) {
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
const auto type = descriptor.Type();
return type == Prospero::ImageType::kCube ? Prospero::ImageType::kColor2DArray : type;
}
@@ -227,14 +227,14 @@ bool IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& r
return image.usage.video_out && image.info.resources.layers == 1 &&
IsSupportedSampledColorResource(resource) &&
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
descriptor.Depth() == 0 && descriptor.BaseArray5() == 0;
descriptor.Type() == Prospero::ImageType::kColor2D && descriptor.Depth() == 0 &&
descriptor.BaseArray5() == 0;
}
bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, const Image& image) {
const auto width = static_cast<uint32_t>(descriptor.Width5()) + 1u;
const auto height = static_cast<uint32_t>(descriptor.Height5()) + 1u;
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
const auto type = descriptor.Type();
const bool multisampled = IsMultisampledTexture(type);
const auto samples = multisampled ? 1u << descriptor.LastLevel() : 1u;
const auto pitch =
@@ -269,9 +269,8 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c
(supported_2d || supported_array || supported_cube || supported_msaa_2d ||
supported_msaa_array) &&
levels_ok && descriptor.MinLod() == 0 &&
descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
descriptor.BCSwizzle() == 0 && (!descriptor.MsaaDepth() || multisampled) &&
pitch >= width && pitch == image.info.pitch;
descriptor.TileMode() == Prospero::TileMode::kDepth && descriptor.BCSwizzle() == 0 &&
(!descriptor.MsaaDepth() || multisampled) && pitch >= width && pitch == image.info.pitch;
}
bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, const Image& image) {
@@ -283,13 +282,13 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
(static_cast<uint32_t>(descriptor.TileMode()) << 20u) |
(static_cast<uint32_t>(descriptor.Type()) << 28u);
const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u);
const uint32_t field5_expected =
0x00700000u | (static_cast<uint32_t>(descriptor.MaxMip()) << 4u);
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
descriptor.fields[3] == field3_expected &&
descriptor.fields[4] == field4_expected &&
descriptor.fields[5] == field5_expected;
const uint32_t field5_expected = (static_cast<uint32_t>(descriptor.PerfMod5()) << 20u) |
(static_cast<uint32_t>(descriptor.MaxMip()) << 4u);
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
descriptor.fields[3] == field3_expected &&
descriptor.fields[4] == field4_expected &&
descriptor.fields[5] == field5_expected;
if (!common || (descriptor.fields[6] == 0 && descriptor.fields[7] != 0)) {
return false;
}
@@ -300,8 +299,8 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
const uint32_t expected_control = htile_control | (descriptor.MsaaDepth() ? (1u << 10u) : 0u);
const auto metadata_addr = descriptor.MetaAddr() << 8u;
return (descriptor.fields[6] & 0x00ffffffu) == expected_control && metadata_addr != 0 &&
descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
image.info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
descriptor.TileMode() == Prospero::TileMode::kDepth &&
image.info.tile_mode == Prospero::TileMode::kDepth &&
image.info.metadata.kind == ImageMetadataKind::Htile &&
image.info.metadata.range.Valid() && image.info.metadata.range.address == metadata_addr;
}
@@ -332,23 +331,21 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind),
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
resource.read, resource.written, resource.atomic, resource.depth_compare,
descriptor.Format(), descriptor.DstSelXYZW(),
static_cast<uint32_t>(descriptor.Format()), descriptor.DstSelXYZW(),
image == nullptr ? static_cast<int>(vk::Format::eUndefined)
: static_cast<int>(image->info.pixel_format),
static_cast<int>(view_format), image == nullptr ? 0u : image->info.resources.layers,
descriptor.Type(), descriptor.BaseArray5(), descriptor.Depth(), descriptor_pitch,
image == nullptr ? 0u : image->info.pitch, descriptor.Base40(), size, descriptor.fields[0],
descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[4],
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
static_cast<uint32_t>(descriptor.Type()), descriptor.BaseArray5(), descriptor.Depth(),
descriptor_pitch, image == nullptr ? 0u : image->info.pitch, descriptor.Base40(), size,
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
}
static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource,
const ShaderTextureResource& descriptor) {
const auto tile = descriptor.TileMode();
const bool is_color_1d =
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1D);
const bool is_color_1d_array =
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray);
const auto tile = descriptor.TileMode();
const bool is_color_1d = descriptor.Type() == Prospero::ImageType::kColor1D;
const bool is_color_1d_array = descriptor.Type() == Prospero::ImageType::kColor1DArray;
const bool valid_1d_slice =
(is_color_1d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
(is_color_1d_array && descriptor.BaseArray5() <= descriptor.Depth());
@@ -358,24 +355,27 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray &&
is_color_1d_array && descriptor.Height5() == 0 &&
descriptor.BaseArray5() <= descriptor.Depth();
const bool is_color_2d =
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
const bool is_color_2d_array =
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray);
const bool is_color_2d = descriptor.Type() == Prospero::ImageType::kColor2D;
const bool is_color_2d_array = descriptor.Type() == Prospero::ImageType::kColor2DArray;
const bool valid_2d_slice =
(is_color_2d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
const bool is_2d =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && valid_2d_slice;
const bool is_cube = resource.cube && descriptor.Type() == Prospero::ImageType::kCube &&
descriptor.Width5() == descriptor.Height5() &&
descriptor.BaseArray5() <= descriptor.Depth() &&
(descriptor.Depth() - descriptor.BaseArray5() + 1u) % 6u == 0;
const bool is_2d_array =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth();
((!resource.cube && is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth()) ||
is_cube);
const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D &&
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor3D) &&
descriptor.Type() == Prospero::ImageType::kColor3D &&
descriptor.BaseArray5() == 0;
TileTextureBlockLayout tile_layout {};
bool supported_tile = false;
switch (static_cast<Prospero::TileMode>(tile)) {
switch (tile) {
case Prospero::TileMode::kLinear: supported_tile = true; break;
case Prospero::TileMode::kDepth:
supported_tile =
@@ -435,14 +435,12 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
const bool encoding_ok = IsSupportedStorageTextureEncoding(descriptor);
const bool uint_resource =
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
const bool raw_sint_storage =
format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) && uint_resource &&
resource.written && !resource.read && !resource.atomic;
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
resource.written && !resource.read && !resource.atomic;
const bool format_ok =
raw_sint_storage ||
(Prospero::IsSupportedTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format) &&
(!resource.atomic || format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt)));
raw_sint_storage || (Prospero::IsSupportedTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format) &&
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
return;
}
@@ -461,24 +459,23 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
IsValidImageSwizzle(descriptor.DstSelXYZW()), descriptor.Base40(), size,
static_cast<uint32_t>(descriptor.Width5()) + 1u,
static_cast<uint32_t>(descriptor.Height5()) + 1u,
static_cast<uint32_t>(descriptor.Depth()) + 1u, descriptor.Type(), format,
descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written,
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
static_cast<uint32_t>(descriptor.Depth()) + 1u, static_cast<uint32_t>(descriptor.Type()),
static_cast<uint32_t>(format), static_cast<uint32_t>(descriptor.TileMode()),
descriptor.DstSelXYZW(), resource.read, resource.written, descriptor.fields[0],
descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[4],
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
}
struct NullImageSpec {
vk::Format format;
uint32_t guest_format;
vk::Format format;
Prospero::BufferFormat guest_format;
};
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
return uint_image ? NullImageSpec {vk::Format::eR32Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt)}
: NullImageSpec {vk::Format::eR32Sfloat,
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float)};
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
: NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
}
static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource,
@@ -504,7 +501,7 @@ static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::Image
}
static void PopulateTextureMipLayout(ImageInfo& info) {
if (info.IsVolume() && info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear)) {
if (info.IsVolume() && info.tile_mode != Prospero::TileMode::kLinear) {
TileSurfaceLayout surface {};
const TileSurfaceDescription description {
info.guest_format, info.tile_mode, TileSurfaceDimension::Dim3D, info.extent.width,
@@ -631,10 +628,9 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
const bool multisampled = IsMultisampledTexture(type);
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
const auto tile = descriptor.TileMode();
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
const bool msaa_tile =
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
const bool depth_tile = tile == Prospero::TileMode::kDepth;
const bool msaa_tile = depth_tile || tile == Prospero::TileMode::kRenderTarget;
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
(multisampled &&
(base_level != 0 || last_level == 0 || last_level > 3 ||
@@ -644,7 +640,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
base_level, last_level, levels, descriptor.MaxMip(), descriptor.Type(), tile,
base_level, last_level, levels, descriptor.MaxMip(),
static_cast<uint32_t>(descriptor.Type()), static_cast<uint32_t>(tile),
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
@@ -664,7 +661,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) &&
!sampled_numeric_class) {
EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n",
static_cast<uint32_t>(resource.kind), format, address);
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(format), address);
}
const bool volume = type == Prospero::ImageType::kColor3D;
@@ -694,15 +691,14 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
ValidateStorageTexture(resource, descriptor, size.size);
}
const auto pixel_format = TextureGetFormat(format);
const auto storage_view_format =
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
? vk::Format::eR32Uint
: SrgbStorageViewFormat(pixel_format);
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
? storage_view_format
: pixel_format;
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
const auto pixel_format = TextureGetFormat(format);
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
? vk::Format::eR32Uint
: SrgbStorageViewFormat(pixel_format);
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
? storage_view_format
: pixel_format;
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
TextureCache::ImageDesc desc {};
desc.info.data = {address, size.size};
desc.info.pixel_format = pixel_format;
@@ -24,6 +24,8 @@
namespace Libs::Graphics {
// IDK: maybe we can remove it?
constexpr uint8_t kTemporaryVertexAttribFormat113 =
static_cast<uint8_t>(Prospero::VertexAttribFormat::k16_16SInt);
constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u;
static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_components) {
@@ -88,8 +90,9 @@ static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_
static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format, uint32_t& size,
uint32_t used_components) {
auto fmt = res.Format();
if (fmt == Prospero::GpuEnumValue(Prospero::VertexAttribFormat::k16_16SInt)) {
const auto fmt = res.Format();
const auto raw_format = res.RawFormat();
if (raw_format == kTemporaryVertexAttribFormat113) {
static bool logged_113 = false;
if (!logged_113) {
LOGF("InputFormat: temporary: accepting invalid PS5 buffer format 113 as "
@@ -99,12 +102,12 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
format = vk::Format::eR32G32B32A32Sfloat;
size = 4;
if (NarrowInputFormat(format, size, used_components)) {
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", fmt,
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
VulkanToString(format).c_str(), used_components);
}
return;
}
if (fmt == kTemporaryPs5BufferFormat121) {
if (raw_format == kTemporaryPs5BufferFormat121) {
static bool logged_121 = false;
if (!logged_121) {
LOGF("InputFormat: accepting PS5 buffer format 121 as vk::Format::eR16G16Sfloat\n");
@@ -113,13 +116,13 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
format = vk::Format::eR16G16Sfloat;
size = 2;
if (NarrowInputFormat(format, size, used_components)) {
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", fmt,
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
VulkanToString(format).c_str(), used_components);
}
return;
}
switch (static_cast<Prospero::BufferFormat>(fmt)) {
switch (fmt) {
case Prospero::BufferFormat::k32_32_32_32Float:
format = vk::Format::eR32G32B32A32Sfloat;
size = 4;
@@ -329,7 +332,7 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
size = 1;
break;
default:
EXIT("unknown format: fmt = %u\n", fmt);
EXIT("unknown format: fmt = %u\n", raw_format);
format = vk::Format::eUndefined;
size = 4;
break;
@@ -953,11 +956,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;
@@ -93,7 +93,7 @@ bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t grou
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(raw);
if (!resource.formatted || !resource.written || resource.read || resource.atomic ||
resource.scalar || resource.max_byte_extent != 16 || descriptor.Stride() != 16 ||
descriptor.Format() != Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32UInt) ||
descriptor.Format() != Prospero::BufferFormat::k32_32_32_32UInt ||
descriptor.SwizzleEnabled() || descriptor.IndexStride() != 0 || descriptor.AddTid() ||
resource.packed_stride != descriptor.PackedStride() || raw.dword_count != 4 ||
program.user_data_base != 0 || resources.user_data.size() != 8) {
@@ -246,7 +246,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
LOGF(" CS buffer[%u]: source=%u usage=%s addr=0x%012" PRIx64
" stride=%u records=%u format=%u\n",
i, buffer.source, buffer.written ? "read-write" : "read-only", r.Base48(),
r.Stride(), r.NumRecords(), r.Format());
r.Stride(), r.NumRecords(), r.RawFormat());
}
for (uint32_t i = 0; i < program.info.images.size(); i++) {
const auto& image = program.info.images[i];
@@ -258,13 +258,13 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
image.kind == ShaderRecompiler::IR::ResourceKind::ImageUint)
? "true"
: "false",
r.Base40(), static_cast<uint32_t>(r.Type()), r.Format(),
r.Base40(), static_cast<uint32_t>(r.Type()), static_cast<uint32_t>(r.Format()),
static_cast<uint32_t>(r.Width5()) + 1u, static_cast<uint32_t>(r.Height5()) + 1u,
static_cast<uint32_t>(r.Depth()) + 1u,
std::max<uint32_t>(static_cast<uint32_t>(r.LastLevel()),
static_cast<uint32_t>(r.MaxMip())) +
1u,
r.TileMode());
static_cast<uint32_t>(r.TileMode()));
}
for (uint32_t i = 0; i < program.info.samplers.size(); i++) {
const auto r = DecodeNativeDescriptor<ShaderSamplerResource>(resources.samplers[i]);
+17 -17
View File
@@ -123,7 +123,7 @@ static void LogFramebufferSkip(const char* draw_name, const RenderColorInfo& col
log_id, draw_name, RenderColorTypeName(color.type), color.base_addr, color.buffer_size,
color.image_id ? "yes" : "no", VulkanToString(depth.format).c_str(),
depth.image_id ? "yes" : "no", depth.vaddr_num, ctx.GetRenderTargetMask(),
ucfg.GetPrimType(), index_count, flags);
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags);
}
static void LogMrtState(const char* draw_name, const RenderCommandBuffer& buffer,
@@ -179,10 +179,12 @@ static void LogMrtState(const char* draw_name, const RenderCommandBuffer& buffer
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32
" width=%u height=%u tile=%u"
" blend=%s src=%u dst=%u alpha_src=%u alpha_dst=%u\n",
log_id, i, rt.base.addr, ctm, csm, ps_input_info.target_output_mode[i], rt.info.format,
rt.info.channel_type, rt.info.channel_order, rt.attrib2.width + 1,
rt.attrib2.height + 1, rt.attrib3.tile_mode, bc.enable ? "true" : "false",
bc.color_srcblend, bc.color_destblend, bc.alpha_srcblend, bc.alpha_destblend);
log_id, i, rt.base.addr, ctm, csm, ps_input_info.target_output_mode[i],
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
static_cast<uint32_t>(rt.info.channel_order), rt.attrib2.width + 1,
rt.attrib2.height + 1, static_cast<uint32_t>(rt.attrib3.tile_mode),
bc.enable ? "true" : "false", bc.color_srcblend, bc.color_destblend, bc.alpha_srcblend,
bc.alpha_destblend);
}
}
@@ -225,7 +227,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
"scissor=(%d,%d)-(%d,%d)\n",
log_id, buffer.GetContext().GetGpu().GetFrameNum(), draw_name,
RenderColorTypeName(color.type), color.base_addr, extent.width, extent.height,
ucfg.GetPrimType(), index_count, flags, ctx.GetRenderTargetMask(),
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags, ctx.GetRenderTargetMask(),
color.color_clear_enable ? "true" : "false", color.color_clear_value.float32[0],
color.color_clear_value.float32[1], color.color_clear_value.float32[2],
color.color_clear_value.float32[3], cc.mode, cc.op, bc.enable ? "true" : "false",
@@ -289,8 +291,7 @@ static void LogDrawInputState(const RenderCommandBuffer& buffer, const RenderCol
const auto& rd = vs_input_info.resources_dst[res_index];
const auto offset = b.attr_offsets[ai];
if (offset + 4u <= b.stride &&
r.Format() ==
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm)) {
r.Format() == Prospero::BufferFormat::k8_8_8_8UNorm) {
uint32_t packed = 0;
std::memcpy(&packed, rec_bytes + offset, sizeof(packed));
const auto r8 = (packed >> 0u) & 0xffu;
@@ -844,7 +845,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
topology = vk::PrimitiveTopology::ePointList;
switch (static_cast<Prospero::PrimitiveType>(ucfg.GetPrimType())) {
switch (ucfg.GetPrimType()) {
case Prospero::PrimitiveType::kNone: return false;
case Prospero::PrimitiveType::kPointList:
topology = vk::PrimitiveTopology::ePointList;
@@ -867,14 +868,14 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
break;
case Prospero::PrimitiveType::kRectListLegacy:
if (!auto_draw) {
EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
}
topology = vk::PrimitiveTopology::eTriangleStrip;
break;
case Prospero::PrimitiveType::kQuadListLegacy:
topology = vk::PrimitiveTopology::eTriangleFan;
break;
default: EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
default: EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
}
return true;
@@ -1051,7 +1052,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
const DrawEmitInfo& emit) {
EXIT_IF(draw.name == nullptr);
switch (static_cast<Prospero::PrimitiveType>(ucfg.GetPrimType())) {
switch (ucfg.GetPrimType()) {
case Prospero::PrimitiveType::kPointList:
case Prospero::PrimitiveType::kLineList:
case Prospero::PrimitiveType::kLineStrip:
@@ -1069,7 +1070,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
break;
case Prospero::PrimitiveType::kRectListLegacy:
if (emit.indexed) {
EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
}
// Sarah
EXIT_NOT_IMPLEMENTED(!(draw.index_count == 3 && vs_input_info.buffers_num == 0));
@@ -1087,7 +1088,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
}
}
break;
default: EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
default: EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
}
}
@@ -1387,9 +1388,8 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
}
LogDrawStateIfNeeded(buffer, draw, state, false,
ucfg.GetPrimType() ==
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
0, nullptr);
ucfg.GetPrimType() == Prospero::PrimitiveType::kRectListLegacy, 0,
nullptr);
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
static_cast<int32_t>(first_vertex);
+5 -5
View File
@@ -83,11 +83,11 @@ constexpr FormatMapping kFormatMappings[] = {
};
constexpr auto MakeFormatLookup() {
constexpr auto kMaxFormat = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc7Srgb);
constexpr auto kMaxFormat = static_cast<size_t>(Prospero::BufferFormat::kBc7Srgb);
std::array<vk::Format, kMaxFormat + 1> lookup {};
lookup.fill(vk::Format::eUndefined);
for (const auto& mapping: kFormatMappings) {
lookup[Prospero::GpuEnumValue(mapping.guest)] = mapping.host;
lookup[static_cast<size_t>(mapping.guest)] = mapping.host;
}
return lookup;
}
@@ -112,9 +112,9 @@ std::string VulkanToString(vk::QueueFlags value) {
return vk::to_string(value);
}
vk::Format VulkanFormat(uint32_t guest_format) {
return guest_format < kFormatLookup.size() ? kFormatLookup[guest_format]
: vk::Format::eUndefined;
vk::Format VulkanFormat(Prospero::BufferFormat guest_format) {
const auto index = static_cast<size_t>(guest_format);
return index < kFormatLookup.size() ? kFormatLookup[index] : vk::Format::eUndefined;
}
void RequireVulkanSuccess(vk::Result result, const char* operation) {
+3 -1
View File
@@ -10,6 +10,8 @@
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include "graphics/guest_gpu/gpu_defs.h"
#include <cstdint>
#include <string>
#include <vector>
@@ -29,7 +31,7 @@ std::string VulkanToString(vk::Result value);
std::string VulkanToString(vk::Format value);
std::string VulkanToString(vk::ImageLayout value);
std::string VulkanToString(vk::QueueFlags value);
vk::Format VulkanFormat(uint32_t guest_format);
vk::Format VulkanFormat(Prospero::BufferFormat guest_format);
void RequireVulkanSuccess(vk::Result result, const char* operation);
template <typename T, typename Enumerator>
@@ -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,79 @@ namespace Libs::Graphics {
namespace {
namespace Ime = Libs::Dialog::ImeDialog;
namespace CoreIme = Libs::Ime;
namespace DialogIme = Libs::Dialog::ImeDialog;
namespace Ime {
using Type = ImeCommon::Type;
using EnterLabel = ImeCommon::EnterLabel;
using Alignment = ImeCommon::Alignment;
using ExternalAction = ImeCommon::ExternalAction;
using ExternalInput = ImeCommon::ExternalInput;
using HostSnapshot = ImeCommon::HostSnapshot;
constexpr uint32_t OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE;
constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = ImeCommon::OPTION_NO_AUTO_CAPITALIZE;
constexpr uint32_t OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD;
constexpr uint32_t OPTION_FIXED_POSITION = ImeCommon::OPTION_FIXED_POSITION;
constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = ImeCommon::OPTION_DISABLE_POSITION_ADJUST;
constexpr uint32_t OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K;
constexpr uint32_t DISABLE_DEVICE_CONTROLLER = ImeCommon::DISABLE_DEVICE_CONTROLLER;
constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = ImeCommon::DISABLE_DEVICE_EXT_KEYBOARD;
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;
}
if (CoreIme::GetHostSnapshot(snapshot)) {
snapshot->generation = PackCoreGeneration(snapshot->generation);
return true;
}
return DialogIme::GetHostSnapshot(snapshot);
}
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) {
return IsCoreGeneration(generation)
? CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(input))
: DialogIme::HostQueueExternalInput(generation, std::move(input));
}
} // namespace Ime
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
@@ -114,28 +187,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 +226,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 +383,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 +391,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 +423,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 +548,7 @@ bool ProcessImeDialogInput(const SDL_Event& event) {
}
}
struct ImeDialogOverlay::Impl {
struct ImeOverlay::Impl {
explicit Impl(GraphicContext& context): graphics(context) {}
~Impl() {
@@ -488,7 +571,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 +763,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 +939,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_
+1 -2
View File
@@ -534,8 +534,7 @@ Graphics::ImageInfo BufferAttributeGroup::ImageInfo(const VideoOutBuffer& buffer
if (!Graphics::DecodeVideoOutPixelFormat(attribute.pixel_format, pixel_format)) {
EXIT("unsupported video-out pixel format: 0x%016" PRIx64 "\n", attribute.pixel_format);
}
const auto tile_mode =
Graphics::Prospero::GpuEnumValue(Graphics::Prospero::TileMode::kRenderTarget);
const auto tile_mode = Graphics::Prospero::TileMode::kRenderTarget;
const auto pitch =
Graphics::TileGetTexturePitch(pixel_format.guest_format, attribute.width, tile_mode);
Graphics::TileSizeAlign total {};
+86 -36
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) {
@@ -97,8 +119,9 @@ public:
}
const bool reserved =
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1;
if (binding.pad_button == 0 || reserved ||
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1 ||
binding.key == SDLK_F11;
if (binding.control == INVALID_CONTROL || reserved ||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
EXIT("Invalid input mapping: %s\n", value.c_str());
}
@@ -107,19 +130,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 +158,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 +214,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 +290,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 +299,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 -17
View File
@@ -22,7 +22,6 @@
#include "common/logging/log.h"
#include "common/profiler.h"
#include "common/stringUtils.h"
#include "common/subsystems.h"
#include "common/systemInfo.h"
#include "common/threads.h"
#include "common/timer.h"
@@ -31,13 +30,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 +343,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 +616,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 +805,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 +841,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();
@@ -22,7 +22,6 @@
#include "common/logging/log.h"
#include "common/profiler.h"
#include "common/stringUtils.h"
#include "common/subsystems.h"
#include "common/systemInfo.h"
#include "common/threads.h"
#include "common/timer.h"
@@ -31,7 +30,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 +606,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 +1010,7 @@ void WindowContext::RecreateSurface() {
}
WindowContext::~WindowContext() {
ShutdownImeDialogInput();
ShutdownImeInput();
presenter.reset();
LibKernel::Memory::InstallGpuResources(nullptr);
render_context.reset();
+43 -17
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"
@@ -219,7 +219,24 @@ static void GameEventTerminate(WindowLoopState& game) {
game.need_exit = true;
}
static void ToggleDesktopFullscreen() {
if (g_window == nullptr || g_window->window == nullptr) {
return;
}
const auto flags = static_cast<uint32_t>(SDL_GetWindowFlags(g_window->window));
const bool fullscreen =
(flags & static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0u;
const auto mode =
fullscreen ? 0u : static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
if (SDL_SetWindowFullscreen(g_window->window, mode) != 0) {
LOGF("Toggle fullscreen failed: %s\n", SDL_GetError());
}
}
static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
static SDL_Keycode fullscreen_key = SDLK_UNKNOWN;
#ifdef KYTY_DBG_INPUT
LOGF("Key: time = %.04f, %s%s, %s%s, %s, scan = %d, key = %d, mod = %04" PRIx16 "\n",
key.timestamp_seconds, (key.down ? "down" : ""), (key.up ? "up" : ""),
@@ -237,13 +254,30 @@ static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
RenderDocRequestCapture();
}
break;
case SDLK_F11:
if (!key.repeat) {
ToggleDesktopFullscreen();
}
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
if (!key.repeat && (key.mod & KMOD_ALT) != 0) {
ToggleDesktopFullscreen();
fullscreen_key = key.key_code;
}
break;
default: break;
}
}
#endif
if ((key.down || key.up) && !key.repeat) {
const bool fullscreen_key_event =
fullscreen_key != SDLK_UNKNOWN && key.key_code == fullscreen_key;
if (fullscreen_key_event && key.up) {
fullscreen_key = SDLK_UNKNOWN;
}
if ((key.down || key.up) && !key.repeat && !fullscreen_key_event) {
HostInputKey(key.key_code, key.down);
}
}
@@ -477,7 +511,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 +817,7 @@ static void WindowCreate(WindowContext& context) {
EXIT("%s\n", SDL_GetError());
}
HostInputInit();
InitializeImeDialogInput();
InitializeImeInput();
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
@@ -938,14 +972,6 @@ void WindowContext::UpdateTitle() {
static constexpr auto build_type = "Unknown";
#endif
#if defined(KYTY_OFFICIAL_BUILD)
static constexpr auto build_label = "Official build " KYTY_RELEASE_TAG;
#elif defined(KYTY_FORK_BUILD)
static constexpr auto build_label = "Fork build " KYTY_BUILD_REPOSITORY " " KYTY_GIT_HASH;
#else
static constexpr auto build_label = "Source build " KYTY_GIT_HASH;
#endif
const auto now = Common::Timer::QueryPerformanceCounter();
const auto frequency = Common::Timer::QueryPerformanceFrequency();
frame_num++;
@@ -957,11 +983,11 @@ void WindowContext::UpdateTitle() {
fps_frames = 0;
}
auto fps = fmt::format("[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", 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.
@@ -893,7 +893,8 @@ uint32_t EmitByteAddress(EmitterState& state, const IR::Instruction& inst, uint3
uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryInfo& mem,
uint32_t use_pc);
uint32_t StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc);
Prospero::BufferFormat StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
uint32_t use_pc);
bool ShouldApplyBufferAddTid(const IR::Instruction& inst);
@@ -153,8 +153,8 @@ uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryIn
return state.program.info.buffers[mem.resource].packed_stride;
}
uint32_t StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
uint32_t use_pc) {
Prospero::BufferFormat StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
uint32_t use_pc) {
(void)use_pc;
if (mem.resource >= state.program.info.buffers.size()) {
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::Buffers, mem.resource,
@@ -749,8 +749,7 @@ Prospero::BufferFormat FormattedBufferFormat(const EmitterState& state,
const IR::Instruction& inst) {
return inst.memory.typed
? Format::DecodeTBufferFormat(inst.memory.data_format, inst.memory.number_format)
: static_cast<Prospero::BufferFormat>(
StorageBufferFormat(state, inst.memory, inst.pc));
: StorageBufferFormat(state, inst.memory, inst.pc);
}
IR::Instruction WithFormatComponentByteOffset(const IR::Instruction& inst,
@@ -64,26 +64,26 @@ VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint3
}
switch (state.vertex_input_info->resources[location].Format()) {
case 5:
case 11:
case 18:
case 20:
case 27:
case 60:
case 62:
case 69:
case 72:
case 75: return VertexInputScalarKind::Uint;
case 6:
case 12:
case 19:
case 21:
case 28:
case 61:
case 63:
case 70:
case 73:
case 76: return VertexInputScalarKind::Sint;
case Prospero::BufferFormat::k8UInt:
case Prospero::BufferFormat::k16UInt:
case Prospero::BufferFormat::k8_8UInt:
case Prospero::BufferFormat::k32UInt:
case Prospero::BufferFormat::k16_16UInt:
case Prospero::BufferFormat::k8_8_8_8UInt:
case Prospero::BufferFormat::k32_32UInt:
case Prospero::BufferFormat::k16_16_16_16UInt:
case Prospero::BufferFormat::k32_32_32UInt:
case Prospero::BufferFormat::k32_32_32_32UInt: return VertexInputScalarKind::Uint;
case Prospero::BufferFormat::k8SInt:
case Prospero::BufferFormat::k16SInt:
case Prospero::BufferFormat::k8_8SInt:
case Prospero::BufferFormat::k32SInt:
case Prospero::BufferFormat::k16_16SInt:
case Prospero::BufferFormat::k8_8_8_8SInt:
case Prospero::BufferFormat::k32_32SInt:
case Prospero::BufferFormat::k16_16_16_16SInt:
case Prospero::BufferFormat::k32_32_32SInt:
case Prospero::BufferFormat::k32_32_32_32SInt: return VertexInputScalarKind::Sint;
default: return VertexInputScalarKind::Float;
}
}
@@ -220,13 +220,13 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
}
return false;
}
const auto format = (descriptor.dwords[1] >> 20u) & 0x1ffu;
const bool raw_sint_storage =
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
!image.read && !image.atomic;
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
const auto uint_program = image.kind == ResourceKind::ImageUint ||
image.kind == ResourceKind::StorageImageUint;
const auto format =
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
!image.read && !image.atomic;
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
const auto uint_program = image.kind == ResourceKind::ImageUint ||
image.kind == ResourceKind::StorageImageUint;
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
if (error != nullptr) {
*error = fmt::format(
@@ -415,12 +415,12 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
image.kind == ResourceKind::StorageImageUint) {
image.storage_swizzle = DescriptorImageSwizzle(descriptor);
}
const auto format = (descriptor.dwords[1] >> 20u) & 0x1ffu;
const auto format =
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
const bool storage = image.kind == ResourceKind::StorageImage ||
image.kind == ResourceKind::StorageImageUint;
const bool raw_sint_storage =
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
!image.read && !image.atomic;
storage && format == Prospero::BufferFormat::k32SInt && !image.read && !image.atomic;
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
if (uint_image) {
switch (image.kind) {
+12 -11
View File
@@ -3,6 +3,7 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
#include "graphics/shader/shader.h"
@@ -276,17 +277,17 @@ struct SrtPlan {
struct BufferResource {
static constexpr uint32_t NoImageAlias = UINT32_MAX;
uint32_t source = 0;
uint32_t first_use_pc = 0;
uint32_t max_byte_extent = 0;
uint32_t packed_stride = 0;
uint32_t descriptor_format = 0;
uint32_t image_alias = NoImageAlias;
bool read = false;
bool written = false;
bool atomic = false;
bool formatted = false;
bool scalar = false;
uint32_t source = 0;
uint32_t first_use_pc = 0;
uint32_t max_byte_extent = 0;
uint32_t packed_stride = 0;
Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid;
uint32_t image_alias = NoImageAlias;
bool read = false;
bool written = false;
bool atomic = false;
bool formatted = false;
bool scalar = false;
bool operator==(const BufferResource& other) const = default;
};
+17 -13
View File
@@ -535,7 +535,8 @@ static void ShaderDetectBuffers(ShaderVertexInputInfo& info) {
}
}
static uint32_t VertexAttribFormatToBufferFormat(uint32_t format) {
static Prospero::BufferFormat
VertexAttribFormatToBufferFormat(Prospero::VertexAttribFormat format) {
struct FormatMap {
Prospero::VertexAttribFormat vertex;
Prospero::BufferFormat buffer;
@@ -633,12 +634,12 @@ static uint32_t VertexAttribFormatToBufferFormat(uint32_t format) {
};
for (const auto& entry: format_map) {
if (format == Prospero::GpuEnumValue(entry.vertex)) {
return Prospero::GpuEnumValue(entry.buffer);
if (format == entry.vertex) {
return entry.buffer;
}
}
return format;
return static_cast<Prospero::BufferFormat>(static_cast<uint32_t>(format));
}
static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info,
@@ -659,8 +660,9 @@ static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info,
LOGF("reg = %u, size = %u, va[%u] = 0x%08" PRIx32 "\n", reg, size, i, attrib[in.semantic]);
size_t index = attrib[in.semantic] & 0x1fu;
uint32_t format = (attrib[in.semantic] >> 5u) & 0x1ffu;
size_t index = attrib[in.semantic] & 0x1fu;
auto format =
static_cast<Prospero::VertexAttribFormat>((attrib[in.semantic] >> 5u) & 0x1ffu);
uint32_t offset = (attrib[in.semantic] >> 14u) & 0xfffu;
uint32_t fetch_index = (attrib[in.semantic] >> 26u) & 0x1u;
@@ -690,17 +692,19 @@ static void ShaderApplyAttribSemantics(ShaderVertexInputInfo& info,
r.fields[1] = sharp[1];
r.fields[2] = sharp[2];
r.fields[3] = sharp[3];
if (format != 0) {
if (format != Prospero::VertexAttribFormat::kInvalid) {
auto buffer_format = VertexAttribFormatToBufferFormat(format);
static std::atomic<uint64_t> log_count = 0;
auto log_id = log_count.fetch_add(1);
if (log_id < 64) {
LOGF("\t temporary: PS5 vertex attrib semantic %u uses attrib format %u -> buffer "
"format %u, offset %u, buffer index %zu\n",
static_cast<uint32_t>(in.semantic), format, buffer_format, offset, index);
static_cast<uint32_t>(in.semantic), static_cast<uint32_t>(format),
static_cast<uint32_t>(buffer_format), offset, index);
}
const auto buffer_format_raw = static_cast<uint32_t>(buffer_format);
r.fields[3] = (r.fields[3] & ~((0x7fu << 12u) | 0xfffu)) |
((buffer_format & 0x7fu) << 12u) | DstSel(4, 5, 6, 7);
((buffer_format_raw & 0x7fu) << 12u) | DstSel(4, 5, 6, 7);
}
if (offset != 0) {
r.UpdateAddress48(r.Base48() + offset);
@@ -1011,7 +1015,7 @@ static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Prog
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
const auto& buffer = program.info.buffers[i];
ret += fmt::format(" b{}[stride={} format={}]", i, buffer.packed_stride,
buffer.descriptor_format);
static_cast<uint32_t>(buffer.descriptor_format));
}
for (uint32_t i = 0; i < program.info.images.size(); i++) {
const auto& image = program.info.images[i];
@@ -1047,7 +1051,7 @@ static void ShaderAppendNativeSpecialization(std::vector<uint32_t>&
ids.push_back(static_cast<uint32_t>(program.info.buffers.size()));
for (const auto& buffer: program.info.buffers) {
ids.push_back(buffer.packed_stride);
ids.push_back(buffer.descriptor_format);
ids.push_back(static_cast<uint32_t>(buffer.descriptor_format));
}
ids.push_back(static_cast<uint32_t>(program.info.images.size()));
for (const auto& image: program.info.images) {
@@ -1250,7 +1254,7 @@ void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info) {
r.DstSelX(), r.DstSelY(), r.DstSelZ(), r.DstSelW());
LOGF("\t\t Format() = %" PRIu8 "\n"
"\t\t OutOfBounds() = %" PRIu8 "\n",
r.Format(), r.OutOfBounds());
r.RawFormat(), r.OutOfBounds());
LOGF("\t\t AddTid() = %s\n", r.AddTid() ? "true" : "false");
}
@@ -1596,7 +1600,7 @@ ShaderId ShaderGetIdVS(const HW::VertexShaderInfo& regs, const ShaderVertexInput
ret.ids.push_back(r.DstSelY());
ret.ids.push_back(r.DstSelZ());
ret.ids.push_back(r.DstSelW());
ret.ids.push_back(r.Format());
ret.ids.push_back(r.RawFormat());
ret.ids.push_back(r.OutOfBounds());
ret.ids.push_back(static_cast<uint32_t>(r.AddTid()));
}
+1 -1
View File
@@ -29,7 +29,7 @@ ResourceDescriptorType ShaderClassifyResourceDescriptor(const uint32_t* desc) {
default: break;
}
return (raw_type & Prospero::GpuEnumValue(Prospero::ImageType::kColor1D)) == 0
return (raw_type & static_cast<uint32_t>(Prospero::ImageType::kColor1D)) == 0
? ResourceDescriptorType::Buffer
: ResourceDescriptorType::Unused;
}
+25 -15
View File
@@ -3,6 +3,7 @@
#include "common/abi.h"
#include "common/common.h"
#include "graphics/guest_gpu/gpu_defs.h"
namespace Libs::Graphics {
@@ -61,7 +62,10 @@ struct ShaderBufferResource {
[[nodiscard]] uint64_t Base48() const {
return (fields[0] | (static_cast<uint64_t>(fields[1]) << 32u)) & 0xFFFFFFFFFFFFu;
}
[[nodiscard]] uint8_t Format() const { return (fields[3] >> 12u) & 0x7Fu; }
[[nodiscard]] uint8_t RawFormat() const { return (fields[3] >> 12u) & 0x7Fu; }
[[nodiscard]] Prospero::BufferFormat Format() const {
return static_cast<Prospero::BufferFormat>(RawFormat());
}
[[nodiscard]] uint8_t OutOfBounds() const { return (fields[3] >> 28u) & 0x3u; }
[[nodiscard]] uint8_t Type() const { return (fields[3] >> 30u) & 0x3u; }
};
@@ -69,25 +73,31 @@ struct ShaderBufferResource {
struct ShaderTextureResource {
uint32_t fields[8] = {0};
[[nodiscard]] uint16_t MinLod() const { return (fields[1] >> 8u) & 0xFFFu; }
[[nodiscard]] uint8_t DstSelX() const { return (fields[3] >> 0u) & 0x7u; }
[[nodiscard]] uint8_t DstSelY() const { return (fields[3] >> 3u) & 0x7u; }
[[nodiscard]] uint8_t DstSelZ() const { return (fields[3] >> 6u) & 0x7u; }
[[nodiscard]] uint8_t DstSelW() const { return (fields[3] >> 9u) & 0x7u; }
[[nodiscard]] uint32_t DstSelXY() const { return (fields[3] >> 0u) & 0x3Fu; }
[[nodiscard]] uint32_t DstSelXYZ() const { return (fields[3] >> 0u) & 0x1FFu; }
[[nodiscard]] uint32_t DstSelXYZW() const { return (fields[3] >> 0u) & 0xFFFu; }
[[nodiscard]] uint8_t BaseLevel() const { return (fields[3] >> 12u) & 0xFu; }
[[nodiscard]] uint8_t LastLevel() const { return (fields[3] >> 16u) & 0xFu; }
[[nodiscard]] uint8_t TileMode() const { return (fields[3] >> 20u) & 0x1Fu; }
[[nodiscard]] uint8_t Type() const { return (fields[3] >> 28u) & 0xFu; }
[[nodiscard]] uint16_t MinLod() const { return (fields[1] >> 8u) & 0xFFFu; }
[[nodiscard]] uint8_t DstSelX() const { return (fields[3] >> 0u) & 0x7u; }
[[nodiscard]] uint8_t DstSelY() const { return (fields[3] >> 3u) & 0x7u; }
[[nodiscard]] uint8_t DstSelZ() const { return (fields[3] >> 6u) & 0x7u; }
[[nodiscard]] uint8_t DstSelW() const { return (fields[3] >> 9u) & 0x7u; }
[[nodiscard]] uint32_t DstSelXY() const { return (fields[3] >> 0u) & 0x3Fu; }
[[nodiscard]] uint32_t DstSelXYZ() const { return (fields[3] >> 0u) & 0x1FFu; }
[[nodiscard]] uint32_t DstSelXYZW() const { return (fields[3] >> 0u) & 0xFFFu; }
[[nodiscard]] uint8_t BaseLevel() const { return (fields[3] >> 12u) & 0xFu; }
[[nodiscard]] uint8_t LastLevel() const { return (fields[3] >> 16u) & 0xFu; }
[[nodiscard]] Prospero::TileMode TileMode() const {
return static_cast<Prospero::TileMode>((fields[3] >> 20u) & 0x1Fu);
}
[[nodiscard]] Prospero::ImageType Type() const {
return static_cast<Prospero::ImageType>((fields[3] >> 28u) & 0xFu);
}
[[nodiscard]] uint16_t Depth() const { return (fields[4] >> 0u) & 0x1FFFu; }
[[nodiscard]] uint64_t Base40() const {
return ((fields[0] | (static_cast<uint64_t>(fields[1]) << 32u)) & 0xFFFFFFFFFFu) << 8u;
}
[[nodiscard]] bool IsNull() const { return Base40() == 0; }
[[nodiscard]] uint16_t Format() const { return (fields[1] >> 20u) & 0x1FFu; }
[[nodiscard]] bool IsNull() const { return Base40() == 0; }
[[nodiscard]] Prospero::BufferFormat Format() const {
return static_cast<Prospero::BufferFormat>((fields[1] >> 20u) & 0x1FFu);
}
[[nodiscard]] uint16_t Width5() const {
return ((fields[1] >> 30u) & 0x3u) | (((fields[2] >> 0u) & 0xFFFu) << 2u);
}
+8 -6
View File
@@ -337,21 +337,23 @@ std::filesystem::path MountPoints::GetRealDirectory(const std::string& mounted_d
return mounted_directory;
}
KYTY_SUBSYSTEM_INIT(FileSystem) {
void Initialize() {
g_mount_points = new MountPoints;
g_files = new FileDescriptors;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(FileSystem) {
void EmergencyShutdown() {
if (g_files != nullptr) {
g_files->CloseAll();
}
}
KYTY_SUBSYSTEM_DESTROY(FileSystem) {
if (g_files != nullptr) {
g_files->CloseAll();
}
void Shutdown() {
EmergencyShutdown();
delete g_files;
delete g_mount_points;
g_files = nullptr;
g_mount_points = nullptr;
}
void Mount(const std::filesystem::path& folder, const std::string& point) {
+10 -2
View File
@@ -4,7 +4,6 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/stringUtils.h"
#include "common/subsystems.h"
#include "kernel/pthread.h"
#include <filesystem>
@@ -33,7 +32,16 @@ struct FileStat {
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(KernelTimespec)));
};
KYTY_SUBSYSTEM_DEFINE(FileSystem);
void Initialize();
void Shutdown();
void EmergencyShutdown();
struct Lifecycle {
static constexpr const char* name = "FileSystem";
static constexpr auto initialize = Libs::LibKernel::FileSystem::Initialize;
static constexpr auto shutdown = Libs::LibKernel::FileSystem::Shutdown;
static constexpr auto emergency_shutdown = Libs::LibKernel::FileSystem::EmergencyShutdown;
};
void Mount(const std::filesystem::path& folder, const std::string& point);
void Umount(const std::string& folder_or_point);
+2 -4
View File
@@ -976,7 +976,7 @@ static bool SelfTestSub64SharedPlaceholderAlias() {
static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size);
KYTY_SUBSYSTEM_INIT(Memory) {
void Initialize() {
g_flexible_memory_size_frozen = true;
VirtualMemory::Init();
g_guest_address_space = std::make_unique<GuestAddressSpace>(PhysicalMemory::TotalSize());
@@ -988,9 +988,7 @@ KYTY_SUBSYSTEM_INIT(Memory) {
EXIT_IF(!SelfTestSub64SharedPlaceholderAlias());
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
KYTY_SUBSYSTEM_DESTROY(Memory) {
void Shutdown() {
g_pooled_memory.reset();
g_flexible_memory.reset();
g_physical_memory.reset();
+8 -2
View File
@@ -3,7 +3,6 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
#include "common/virtualMemory.h"
namespace Libs::Graphics {
@@ -13,7 +12,14 @@ enum class PageFaultAccess;
namespace Libs::LibKernel::Memory {
KYTY_SUBSYSTEM_DEFINE(Memory);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Memory";
static constexpr auto initialize = Libs::LibKernel::Memory::Initialize;
static constexpr auto shutdown = Libs::LibKernel::Memory::Shutdown;
};
using callback_func_t = void (*)(uintptr_t addr, size_t size);
+15 -5
View File
@@ -1124,7 +1124,7 @@ void* PthreadCreateMainGuestStack() {
return stack_top;
}
KYTY_SUBSYSTEM_INIT(Pthread) {
void Initialize() {
PRINT_NAME_ENABLE(false);
EXIT_IF(g_pthread_context != nullptr);
@@ -1157,10 +1157,6 @@ KYTY_SUBSYSTEM_INIT(Pthread) {
thread.Detach();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Pthread) {}
KYTY_SUBSYSTEM_DESTROY(Pthread) {}
static int PthreadAttrCopy(PthreadAttr* dst, const PthreadAttr* src) {
if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) {
return KERNEL_ERROR_EINVAL;
@@ -4420,6 +4416,13 @@ int KYTY_SYSV_ABI pthread_cond_signal(LibKernel::PthreadCond* cond) {
return POSIX_PTHREAD_CALL(LibKernel::PthreadCondSignal(cond));
}
int KYTY_SYSV_ABI pthread_cond_init(LibKernel::PthreadCond* cond,
const LibKernel::PthreadCondattr* attr) {
PRINT_NAME();
return POSIX_PTHREAD_CALL(LibKernel::PthreadCondInit(cond, attr, nullptr));
}
int KYTY_SYSV_ABI pthread_condattr_setclock(LibKernel::PthreadCondattr* attr,
LibKernel::KernelClockid clock_id) {
PRINT_NAME();
@@ -4528,6 +4531,13 @@ int KYTY_SYSV_ABI pthread_rwlock_destroy(LibKernel::PthreadRwlock* rwlock) {
return POSIX_PTHREAD_CALL(LibKernel::PthreadRwlockDestroy(rwlock));
}
int KYTY_SYSV_ABI pthread_rwlock_init(LibKernel::PthreadRwlock* rwlock,
const LibKernel::PthreadRwlockattr* attr) {
PRINT_NAME();
return POSIX_PTHREAD_CALL(LibKernel::PthreadRwlockInit(rwlock, attr, nullptr));
}
int KYTY_SYSV_ABI pthread_key_create(LibKernel::PthreadKey* key,
LibKernel::pthread_key_destructor_func_t destructor) {
PRINT_NAME();
+8 -2
View File
@@ -3,7 +3,6 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
// IWYU pragma: no_include <pthread.h>
@@ -19,7 +18,12 @@ namespace Libs {
namespace LibKernel {
KYTY_SUBSYSTEM_DEFINE(Pthread);
void Initialize();
struct PthreadLifecycle {
static constexpr const char* name = "Pthread";
static constexpr auto initialize = Libs::LibKernel::Initialize;
};
struct PthreadAttrPrivate;
struct PthreadPrivate;
@@ -269,6 +273,8 @@ int KYTY_SYSV_ABI pthread_rwlock_rdlock(LibKernel::PthreadRwlock* rwlock);
int KYTY_SYSV_ABI pthread_rwlock_unlock(LibKernel::PthreadRwlock* rwlock);
int KYTY_SYSV_ABI pthread_rwlock_wrlock(LibKernel::PthreadRwlock* rwlock);
int KYTY_SYSV_ABI pthread_rwlock_destroy(LibKernel::PthreadRwlock* rwlock);
int KYTY_SYSV_ABI pthread_rwlock_init(LibKernel::PthreadRwlock* rwlock,
const LibKernel::PthreadRwlockattr* attr);
int KYTY_SYSV_ABI pthread_key_create(LibKernel::PthreadKey* key,
LibKernel::pthread_key_destructor_func_t destructor);
int KYTY_SYSV_ABI pthread_key_delete(LibKernel::PthreadKey key);
+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;
+11
View File
@@ -1,2 +1,13 @@
#include "cmake_config.h"
#define KYTY_GIT_VERSION "@KYTY_GIT_VERSION@"
#define KYTY_GIT_HASH "@KYTY_GIT_HASH@"
/* Build provenance, shown in the window title and in crash reports. */
#if defined(KYTY_OFFICIAL_BUILD)
#define KYTY_BUILD_LABEL "Official build " KYTY_RELEASE_TAG
#elif defined(KYTY_FORK_BUILD)
#define KYTY_BUILD_LABEL "Fork build " KYTY_BUILD_REPOSITORY " " KYTY_GIT_HASH
#else
#define KYTY_BUILD_LABEL "Source build " KYTY_GIT_HASH
#endif
+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;
}
+24 -6
View File
@@ -335,6 +335,23 @@ static bool FindTerminal(QString* program, QStringList* prefix) {
}
#endif
#if defined(_WIN32)
// Quote one token for cmd.exe so paths with spaces survive /K parsing.
static QString WinCmdQuote(QString value) {
value.replace(QLatin1Char('"'), QStringLiteral("\\\""));
return QLatin1Char('"') + value + QLatin1Char('"');
}
static QString BuildWinCmdKCommand(const QString& interpreter, const QStringList& args) {
QString command = WinCmdQuote(QDir::toNativeSeparators(interpreter));
for (const auto& arg: args) {
command += QLatin1Char(' ');
command += WinCmdQuote(arg);
}
return command;
}
#endif
void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
const auto& interpreter = m_p->GetInterpreter();
@@ -359,22 +376,23 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
{
QString terminal;
QStringList terminal_prefix;
// Pass the script as a file argument (not bash -c) so paths with spaces work.
if (FindTerminal(&terminal, &terminal_prefix)) {
process->setProgram(terminal);
process->setArguments(terminal_prefix + QStringList {"bash", "-c", bash_file_name});
process->setArguments(terminal_prefix + QStringList {"bash", bash_file_name});
} else {
// Run without a terminal as a fallback.
process->setProgram(QStringLiteral("bash"));
process->setArguments({QStringLiteral("-c"), bash_file_name});
process->setArguments({bash_file_name});
}
}
#elif defined(_WIN32)
{
// Use nativeArguments so Qt does not re-quote the /K command string.
process->setProgram(CMD_EXE);
QStringList process_args;
process_args << QStringLiteral("/K") << interpreter;
process_args += args;
process->setArguments(process_args);
process->setArguments({});
process->setNativeArguments(QStringLiteral("/K \"") +
BuildWinCmdKCommand(interpreter, args) + QLatin1Char('"'));
}
#else
process->setProgram(interpreter);
+169 -66
View File
@@ -26,6 +26,7 @@
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <mutex>
@@ -42,7 +43,7 @@ namespace Libs::Graphics {
static RenderContext* g_renderer = nullptr;
KYTY_SUBSYSTEM_INIT(Graphics) {
void Initialize() {
// Some games lock up if this is not called first
if (Config::RenderDocEnabled()) {
RenderDocInit();
@@ -58,9 +59,7 @@ KYTY_SUBSYSTEM_INIT(Graphics) {
ShaderInit();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Graphics) {}
KYTY_SUBSYSTEM_DESTROY(Graphics) {
void Shutdown() {
EXIT_IF(g_renderer == nullptr);
g_renderer->ShutdownGpu();
VideoOut::VideoOutShutdown();
@@ -1075,18 +1074,18 @@ int KYTY_SYSV_ABI GraphicsSetUcRegIndirectPatchAddRegisters(uint32_t* cmd, uint3
static uint32_t GraphicsPrimitiveTypeToGsOut(uint32_t prim_type) {
switch (static_cast<Prospero::PrimitiveType>(prim_type)) {
case Prospero::PrimitiveType::kPointList:
return Prospero::GpuEnumValue(Prospero::GsOutputPrimitiveType::kPoints);
return static_cast<uint32_t>(Prospero::GsOutputPrimitiveType::kPoints);
case Prospero::PrimitiveType::kLineList:
case Prospero::PrimitiveType::kLineStrip:
case Prospero::PrimitiveType::kLineListAdjacency:
case Prospero::PrimitiveType::kLineStripAdjacency:
case Prospero::PrimitiveType::kLineLoop:
return Prospero::GpuEnumValue(Prospero::GsOutputPrimitiveType::kLines);
return static_cast<uint32_t>(Prospero::GsOutputPrimitiveType::kLines);
case Prospero::PrimitiveType::kRectList:
return Prospero::GpuEnumValue(Prospero::GsOutputPrimitiveType::k2dRectangle);
return static_cast<uint32_t>(Prospero::GsOutputPrimitiveType::k2dRectangle);
case Prospero::PrimitiveType::kRectListLegacy:
return Prospero::GpuEnumValue(Prospero::GsOutputPrimitiveType::kRectList);
default: return Prospero::GpuEnumValue(Prospero::GsOutputPrimitiveType::kTriangles);
return static_cast<uint32_t>(Prospero::GsOutputPrimitiveType::kRectList);
default: return static_cast<uint32_t>(Prospero::GsOutputPrimitiveType::kTriangles);
}
}
@@ -1180,6 +1179,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 +1554,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) {
@@ -3769,12 +3857,6 @@ struct TessellationDriverState {
static TessellationDriverState g_tessellation_driver_state {};
struct Packet {
uint32_t* addr;
uint32_t dw_num;
uint8_t pad[4];
};
static bool dcb_has_queued_interrupt(const uint32_t* dcb, uint32_t size_in_dwords) {
if (dcb == nullptr) {
return false;
@@ -3812,11 +3894,11 @@ int KYTY_SYSV_ABI GraphicsDriverSubmitDcb(const Packet* packet) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(packet == nullptr);
EXIT_NOT_IMPLEMENTED(packet->pad[0] != 0);
LOGF("\t addr = 0x%016" PRIx64 "\n"
"\t dw_num = 0x%08" PRIx32 "\n",
reinterpret_cast<uint64_t>(packet->addr), packet->dw_num);
"\t dw_num = 0x%08" PRIx32 "\n"
"\t flags = 0x%02" PRIx8 "\n",
reinterpret_cast<uint64_t>(packet->addr), packet->dw_num, packet->flags);
submit_dcb(packet->addr, packet->dw_num);
@@ -3845,51 +3927,9 @@ int KYTY_SYSV_ABI GraphicsDriverSubmitMultiDcbs(uint32_t* const* dcb_gpu_addrs,
"\t size[%" PRIu32 "] = 0x%08" PRIx32 "\n",
i, reinterpret_cast<uint64_t>(dcb), i, size_in_dwords);
if (dcb == nullptr) {
continue;
if (dcb != nullptr) {
submit_dcb(dcb, size_in_dwords);
}
submit_dcb(dcb, size_in_dwords);
}
return OK;
}
int KYTY_SYSV_ABI GraphicsDriverSubmitCommandBuffer(uint32_t queue, uint32_t* dcb,
uint32_t size_in_dwords) {
PRINT_NAME();
LOGF("\t queue = 0x%08" PRIx32 "\n"
"\t dcb = 0x%016" PRIx64 "\n"
"\t size = 0x%08" PRIx32 "\n",
queue, reinterpret_cast<uint64_t>(dcb), size_in_dwords);
if (dcb == nullptr || size_in_dwords == 0) {
return OK;
}
submit_dcb(dcb, size_in_dwords);
return OK;
}
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiCommandBuffers(uint32_t queue, uint32_t* const* dcbs,
const uint32_t* sizes_in_dwords,
uint32_t count) {
PRINT_NAME();
LOGF("\t queue = 0x%08" PRIx32 "\n"
"\t count = %" PRIu32 "\n",
queue, count);
if (count == 0) {
return OK;
}
if (dcbs == nullptr || sizes_in_dwords == nullptr) {
return LibKernel::KERNEL_ERROR_EINVAL;
}
for (uint32_t i = 0; i < count; i++) {
GraphicsDriverSubmitCommandBuffer(queue, dcbs[i], sizes_in_dwords[i]);
}
return OK;
@@ -3928,10 +3968,74 @@ static void submit_acb(uint32_t queue, uint32_t* acb, uint32_t size_in_dwords) {
g_renderer->GetGpu().SubmitCompute(queue, acb, size_in_dwords, trigger_interrupt_on_done);
}
static uint32_t get_driver_queue(const void* queue_context) {
uint32_t queue = 0;
std::memcpy(&queue, static_cast<const uint8_t*>(queue_context) + 4, sizeof(queue));
return queue;
}
static void submit_command_buffer(uint32_t queue, uint32_t* commands, uint32_t size_in_dwords) {
if (commands == nullptr || size_in_dwords == 0) {
return;
}
if (queue >= 0x20 && queue < 0x58) {
submit_acb(queue, commands, size_in_dwords);
} else {
submit_dcb(commands, size_in_dwords);
}
}
int KYTY_SYSV_ABI GraphicsDriverSubmitCommandBuffer(void* queue_context, const Packet* packet) {
PRINT_NAME();
LOGF("\t queue_context = 0x%016" PRIx64 "\n"
"\t packet = 0x%016" PRIx64 "\n",
reinterpret_cast<uint64_t>(queue_context), reinterpret_cast<uint64_t>(packet));
if (queue_context == nullptr || packet == nullptr) {
return LibKernel::KERNEL_ERROR_EINVAL;
}
const uint32_t queue = get_driver_queue(queue_context);
LOGF("\t queue = 0x%08" PRIx32 "\n"
"\t addr = 0x%016" PRIx64 "\n"
"\t size = 0x%08" PRIx32 "\n"
"\t flags = 0x%02" PRIx8 "\n",
queue, reinterpret_cast<uint64_t>(packet->addr), packet->dw_num, packet->flags);
submit_command_buffer(queue, packet->addr, packet->dw_num);
return OK;
}
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiCommandBuffers(void* queue_context,
uint32_t* const* command_buffers,
const uint32_t* sizes_in_dwords,
uint32_t count) {
PRINT_NAME();
LOGF("\t queue_context = 0x%016" PRIx64 "\n"
"\t count = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(queue_context), count);
if (count == 0) {
return OK;
}
if (queue_context == nullptr || command_buffers == nullptr || sizes_in_dwords == nullptr) {
return LibKernel::KERNEL_ERROR_EINVAL;
}
const uint32_t queue = get_driver_queue(queue_context);
for (uint32_t i = 0; i < count; i++) {
submit_command_buffer(queue, command_buffers[i], sizes_in_dwords[i]);
}
return OK;
}
int KYTY_SYSV_ABI GraphicsDriverSubmitAcb(uint32_t queue, const Packet* packet) {
PRINT_NAME();
LOGF("\t queue = 0x%08" PRIx32 "\n"
LOGF("\t queue = 0x%08" PRIx32 "\n"
"\t packet = 0x%016" PRIx64 "\n",
queue, reinterpret_cast<uint64_t>(packet));
@@ -3941,10 +4045,9 @@ int KYTY_SYSV_ABI GraphicsDriverSubmitAcb(uint32_t queue, const Packet* packet)
LOGF("\t acb = 0x%016" PRIx64 "\n"
"\t size = 0x%08" PRIx32 "\n"
"\t flags = 0x%02" PRIx8 "\n",
reinterpret_cast<uint64_t>(packet->addr), packet->dw_num, packet->pad[0]);
reinterpret_cast<uint64_t>(packet->addr), packet->dw_num, packet->flags);
submit_acb(queue, packet->addr, packet->dw_num);
return OK;
}
+26 -8
View File
@@ -3,7 +3,6 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
#include "kernel/eventQueue.h"
namespace Libs::Graphics {
@@ -21,7 +20,14 @@ struct MemoryRange {
uint64_t m_size;
};
KYTY_SUBSYSTEM_DEFINE(Graphics);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Graphics";
static constexpr auto initialize = Libs::Graphics::Initialize;
static constexpr auto shutdown = Libs::Graphics::Shutdown;
};
void GraphicsDbgDumpDcb(const char* type, uint32_t num_dw, uint32_t* cmd_buffer);
@@ -56,6 +62,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);
@@ -283,17 +291,27 @@ uint32_t* KYTY_SYSV_ABI GraphicsDcbSetFlip(CommandBuffer* buf, uint32_t video_ou
namespace Gen5Driver {
struct Packet;
struct Packet {
uint32_t* addr;
uint32_t dw_num;
uint8_t flags;
uint8_t reserved[3];
};
static_assert(sizeof(Packet) == 0x10);
static_assert(offsetof(Packet, addr) == 0x0);
static_assert(offsetof(Packet, dw_num) == 0x8);
static_assert(offsetof(Packet, flags) == 0xc);
int KYTY_SYSV_ABI GraphicsDriverSubmitDcb(const Packet* packet);
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiDcbs(uint32_t* const* dcb_gpu_addrs,
const uint32_t* dcb_sizes_in_dwords,
uint32_t count);
int KYTY_SYSV_ABI GraphicsDriverSubmitCommandBuffer(uint32_t queue, uint32_t* dcb,
uint32_t size_in_dwords);
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiCommandBuffers(uint32_t queue, uint32_t* const* dcbs,
const uint32_t* sizes_in_dwords,
uint32_t count);
int KYTY_SYSV_ABI GraphicsDriverSubmitCommandBuffer(void* queue_context, const Packet* packet);
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiCommandBuffers(void* queue_context,
uint32_t* const* command_buffers,
const uint32_t* sizes_in_dwords,
uint32_t count);
int KYTY_SYSV_ABI GraphicsDriverSubmitAcb(uint32_t queue, const Packet* packet);
int KYTY_SYSV_ABI GraphicsDriverSubmitMultiAcbs(uint32_t queue, uint32_t* const* acbs,
const uint32_t* sizes_in_dwords, uint32_t count);
+11 -13
View File
@@ -181,15 +181,13 @@ uint32_t AudioOutOutputs(const OutputParam* params, uint32_t num, bool blocking)
} // namespace AudioInternal
KYTY_SUBSYSTEM_INIT(Audio) {
void Initialize() {
EXIT_IF(g_audio != nullptr);
g_audio = new Audio;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Audio) {}
KYTY_SUBSYSTEM_DESTROY(Audio) {
void Shutdown() {
delete g_audio;
g_audio = nullptr;
}
@@ -1461,14 +1459,14 @@ struct Ngs2CustomSamplerRackOption {
};
union Ngs2RackOptionUnion {
Ngs2RackOption common;
Ngs2SamplerRackOption sampler;
Ngs2MasteringRackOption mastering;
Ngs2SubmixerRackOption submixer;
Ngs2ReverbRackOption reverb;
Ngs2CustomSubmixerRackOption custom_submixer;
Ngs2RackOption common;
Ngs2SamplerRackOption sampler;
Ngs2MasteringRackOption mastering;
Ngs2SubmixerRackOption submixer;
Ngs2ReverbRackOption reverb;
Ngs2CustomSubmixerRackOption custom_submixer;
Ngs2CustomMasteringRackOption custom_mastering;
Ngs2CustomSamplerRackOption custom_sampler;
Ngs2CustomSamplerRackOption custom_sampler;
};
struct Ngs2ContextBufferInfo {
@@ -2653,8 +2651,8 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat
switch (voice->rack->type) {
case Ngs2RackType::Submixer: {
EXIT_NOT_IMPLEMENTED(state_size != sizeof(Ngs2SubmixerVoiceState));
auto* submixer = reinterpret_cast<Ngs2SubmixerVoiceState*>(state);
*submixer = {};
auto* submixer = reinterpret_cast<Ngs2SubmixerVoiceState*>(state);
*submixer = {};
submixer->voice_state.state_flags = Ngs2GetStateFlags(voice);
LOGF("\t state_flags = %u\n", submixer->voice_state.state_flags);
break;
+8 -3
View File
@@ -3,11 +3,16 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
namespace Libs::Audio {
KYTY_SUBSYSTEM_DEFINE(Audio);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Audio";
static constexpr auto initialize = Libs::Audio::Initialize;
static constexpr auto shutdown = Libs::Audio::Shutdown;
};
namespace AudioOut {
+659 -240
View File
File diff suppressed because it is too large. Load diff
+22 -8
View File
@@ -112,16 +112,17 @@ static void pad_fill_data(PadData* data, const ControllerState& state, bool conn
data->device_unique_data_len = 0;
}
KYTY_SUBSYSTEM_INIT(Controller) {
void Initialize() {
EXIT_IF(g_controller != nullptr);
g_controller = new GameController;
g_controller->Connect(HOST_INPUT_CONTROLLER_ID);
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Controller) {}
KYTY_SUBSYSTEM_DESTROY(Controller) {}
void Shutdown() {
delete g_controller;
g_controller = nullptr;
}
void GameController::Connect(int id) {
Common::LockGuard lock(m_mutex);
@@ -208,7 +209,8 @@ void GameController::AddState(const ControllerState& state) {
void GameController::Button(int id, uint32_t button, bool down) {
Common::LockGuard lock(m_mutex);
if (m_active_id == id) {
// The keyboard shares the player-1 pad with the active gamepad.
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
@@ -226,7 +228,7 @@ void GameController::Button(int id, uint32_t button, bool down) {
void GameController::Axis(int id, Controller::Axis axis, int value) {
Common::LockGuard lock(m_mutex);
if (m_active_id == id) {
if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) {
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
@@ -355,6 +357,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();
@@ -366,7 +380,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;
}
@@ -385,7 +399,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;
}
+8 -3
View File
@@ -3,11 +3,16 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
namespace Libs::Controller {
KYTY_SUBSYSTEM_DEFINE(Controller);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Controller";
static constexpr auto initialize = Libs::Controller::Initialize;
static constexpr auto shutdown = Libs::Controller::Shutdown;
};
constexpr int HOST_INPUT_CONTROLLER_ID = -1000;
+869
View File
@@ -0,0 +1,869 @@
#include "libs/ime.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "loader/symbolDatabase.h"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstring>
#include <deque>
#include <mutex>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace Libs::Ime {
namespace {
constexpr int Error(uint32_t value) {
return static_cast<int32_t>(value);
}
constexpr int ERROR_BUSY = Error(0x80bc0001);
constexpr int ERROR_NOT_OPENED = Error(0x80bc0002);
constexpr int ERROR_CONNECTION_FAILED = Error(0x80bc0004);
constexpr int ERROR_EVENT_OVERFLOW = Error(0x80bc0007);
constexpr int ERROR_INVALID_TEXT = Error(0x80bc0006);
constexpr int ERROR_INVALID_USER_ID = Error(0x80bc0010);
constexpr int ERROR_INVALID_TYPE = Error(0x80bc0011);
constexpr int ERROR_INVALID_LANGUAGES = Error(0x80bc0012);
constexpr int ERROR_INVALID_ENTER_LABEL = Error(0x80bc0013);
constexpr int ERROR_INVALID_INPUT_METHOD = Error(0x80bc0014);
constexpr int ERROR_INVALID_OPTION = Error(0x80bc0015);
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = Error(0x80bc0016);
constexpr int ERROR_INVALID_TEXT_BUFFER = Error(0x80bc0017);
constexpr int ERROR_INVALID_POSX = Error(0x80bc0018);
constexpr int ERROR_INVALID_POSY = Error(0x80bc0019);
constexpr int ERROR_INVALID_HALIGN = Error(0x80bc001a);
constexpr int ERROR_INVALID_VALIGN = Error(0x80bc001b);
constexpr int ERROR_INVALID_EXTENDED = Error(0x80bc001c);
constexpr int ERROR_INVALID_WORK = Error(0x80bc0020);
constexpr int ERROR_INVALID_HANDLER = Error(0x80bc0022);
constexpr int ERROR_NO_RESOURCE_ID = Error(0x80bc0023);
constexpr int ERROR_INVALID_MODE = Error(0x80bc0024);
constexpr int ERROR_INVALID_PARAM = Error(0x80bc0030);
constexpr int ERROR_INVALID_ADDRESS = Error(0x80bc0031);
constexpr int ERROR_INVALID_RESERVED = Error(0x80bc0032);
constexpr uint32_t VALID_OPTIONS = 0x00007bff;
constexpr uint32_t VALID_EXTENDED_OPTIONS = 0x00004fde;
constexpr uint32_t VALID_EXT_KEYBOARD_MODE = 0x1c000003;
constexpr uint32_t VALID_KEYBOARD_OPTIONS = 0x0000003f;
constexpr uint32_t VALID_KEYBOARD_MODE = 0x0000007f;
constexpr uint64_t VALID_LANGUAGES = 0x00000001ff1fffffULL;
constexpr uint32_t OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE;
constexpr uint32_t OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD;
constexpr uint32_t OPTION_EXT_KEYBOARD = ImeCommon::OPTION_EXT_KEYBOARD;
constexpr uint32_t OPTION_EXPANDED_PREEDIT = ImeCommon::OPTION_EXPANDED_PREEDIT;
constexpr uint32_t OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K;
constexpr uint32_t EXT_OPTION_HIDE_KEY_PANEL = ImeCommon::EXT_OPTION_HIDE_KEY_PANEL;
constexpr size_t EVENT_QUEUE_CAPACITY = 128;
struct QueuedEvent {
Event event {};
bool text_payload = false;
std::u16string text;
};
struct State {
bool open = false;
bool event_overflow = false;
uint64_t generation = 0;
uint64_t revision = 0;
Param param {};
ExtendedParam extended {};
void* arg = nullptr;
EventHandler handler = nullptr;
ImeCommon::TextEditEngine editor;
std::deque<QueuedEvent> events;
std::vector<ExternalInput> external_inputs;
};
struct KeyboardState {
bool open = false;
void* arg = nullptr;
EventHandler handler = nullptr;
std::deque<QueuedEvent> events;
};
std::mutex g_mutex;
State g_state;
KeyboardState g_keyboard_state;
std::atomic<bool> g_open {false};
std::atomic<uint64_t> g_revision {0};
std::atomic<VisibilityCallback> g_visibility_callback {nullptr};
bool AllZero(const int8_t* data, size_t size) {
return std::all_of(data, data + size, [](int8_t value) { return value == 0; });
}
bool ValidateText(const char16_t* text, uint32_t length, bool multiline, uint32_t* actual_length) {
if (text == nullptr) {
return false;
}
*actual_length = 0;
for (uint32_t i = 0; i < length; i++) {
const char16_t value = text[i];
if (value == u'\0') {
return true;
}
if (!multiline && (value == u'\n' || value == u'\r')) {
return false;
}
if (value >= 0xd800 && value <= 0xdbff) {
if (++i >= length || text[i] < 0xdc00 || text[i] > 0xdfff) {
return false;
}
} else if (value >= 0xdc00 && value <= 0xdfff) {
return false;
}
*actual_length = i + 1;
}
return true;
}
void NotifyVisibility(bool visible, uint64_t generation) {
if (const auto callback = g_visibility_callback.load(std::memory_order_acquire);
callback != nullptr) {
callback(visible, generation);
}
}
void UpdateRevisionLocked() {
g_state.revision++;
g_revision.store(g_state.revision, std::memory_order_release);
}
void SyncGuestText(char16_t* work, char16_t* input, uint32_t max_length, std::u16string_view text) {
const size_t size = std::min<size_t>(text.size(), max_length);
if (work != nullptr) {
std::memcpy(work, text.data(), size * sizeof(char16_t));
work[size] = u'\0';
}
if (input != nullptr) {
std::memcpy(input, text.data(), size * sizeof(char16_t));
input[size] = u'\0';
}
}
void SyncTextBuffersLocked() {
SyncGuestText(static_cast<char16_t*>(g_state.param.work), g_state.param.input_text_buffer,
g_state.editor.GetMaxLength(), g_state.editor.GetText());
}
bool QueueEventLocked(QueuedEvent event) {
if (g_state.events.size() == EVENT_QUEUE_CAPACITY) {
g_state.event_overflow = true;
return false;
}
g_state.events.push_back(event);
return true;
}
QueuedEvent MakeTextEventLocked(uint32_t id, uint32_t edit_index, int32_t edit_length) {
QueuedEvent queued;
queued.event.id = id;
queued.event.param.text.str = static_cast<char16_t*>(g_state.param.work);
queued.event.param.text.caret_index = g_state.editor.GetCursor();
queued.event.param.text.area_num = 1;
queued.event.param.text.text_area[0] = {TextAreaMode::Edit, edit_index, edit_length};
queued.text_payload = true;
queued.text = g_state.editor.GetText();
return queued;
}
bool CommitFilteredText(uint64_t generation, uint64_t revision, std::u16string old_text,
ImeCommon::TextEditEngine candidate, TextFilter filter, bool multiline) {
if (filter != nullptr) {
std::u16string filtered;
if (ImeCommon::RunTextFilter(filter, candidate.GetText(), candidate.GetMaxLength(),
&filtered) &&
ImeCommon::IsValidInputText(filtered, multiline)) {
candidate.ReplaceText(std::move(filtered), candidate.GetCursor());
}
}
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation || g_state.revision != revision ||
g_state.editor.GetText() != old_text || candidate.GetText() == old_text) {
return false;
}
const auto edit = ImeCommon::ComputeEditDelta(old_text, candidate.GetText());
g_state.editor = std::move(candidate);
QueueEventLocked(MakeTextEventLocked(1, edit.index, edit.length));
UpdateRevisionLocked();
return true;
}
void ApplyExternalInputs();
bool ReadInitialText(const Param& param, std::u16string* text) {
uint32_t length = 0;
while (length <= param.max_text_length && param.input_text_buffer[length] != u'\0') {
length++;
}
if (length > param.max_text_length) {
return false;
}
uint32_t actual = 0;
if (!ValidateText(param.input_text_buffer, length, (param.option & OPTION_MULTILINE) != 0,
&actual)) {
return false;
}
text->assign(param.input_text_buffer, param.input_text_buffer + actual);
return true;
}
bool RangesOverlap(uintptr_t first, size_t first_size, uintptr_t second, size_t second_size) {
if (first > UINTPTR_MAX - first_size || second > UINTPTR_MAX - second_size) {
return true;
}
return first < second + second_size && second < first + first_size;
}
int ValidateExtended(const ExtendedParam* extended) {
if (extended == nullptr) {
return OK;
}
if ((extended->option & ~VALID_EXTENDED_OPTIONS) != 0 ||
((extended->option & 0x00004000) != 0 && (extended->option & 0x00000080) == 0) ||
extended->priority > 3 || extended->disable_device > 7 ||
(extended->ext_keyboard_mode & ~VALID_EXT_KEYBOARD_MODE) != 0 ||
!AllZero(extended->reserved, sizeof(extended->reserved))) {
return ERROR_INVALID_EXTENDED;
}
return OK;
}
int ValidateOpen(const Param* param, const ExtendedParam* extended, std::u16string* text) {
if (param == nullptr) {
return ERROR_INVALID_ADDRESS;
}
if (param->user_id < 0 || param->user_id == 0xff) {
return ERROR_INVALID_USER_ID;
}
if (static_cast<uint32_t>(param->type) > static_cast<uint32_t>(Type::Number)) {
return ERROR_INVALID_TYPE;
}
if ((param->supported_languages & ~VALID_LANGUAGES) != 0) {
return ERROR_INVALID_LANGUAGES;
}
if (static_cast<uint32_t>(param->enter_label) > static_cast<uint32_t>(EnterLabel::Go)) {
return ERROR_INVALID_ENTER_LABEL;
}
if (param->input_method != 0) {
return ERROR_INVALID_INPUT_METHOD;
}
if ((param->option & ~VALID_OPTIONS) != 0) {
return ERROR_INVALID_OPTION;
}
const bool multiline = (param->option & OPTION_MULTILINE) != 0;
const bool password = (param->option & OPTION_PASSWORD) != 0;
if ((multiline && password) ||
(multiline && param->type != Type::Default && param->type != Type::BasicLatin) ||
(password && param->type != Type::BasicLatin && param->type != Type::Number)) {
return ERROR_INVALID_PARAM;
}
if (param->max_text_length == 0 || param->max_text_length > MAX_TEXT_LENGTH) {
return ERROR_INVALID_MAX_TEXT_LENGTH;
}
if (param->input_text_buffer == nullptr) {
return ERROR_INVALID_TEXT_BUFFER;
}
const bool over_2k = (param->option & OPTION_USE_OVER_2K) != 0;
const float max_x = over_2k ? 3840.0f : 1920.0f;
const float max_y = over_2k ? 2160.0f : 1080.0f;
if (!std::isfinite(param->posx) || param->posx < 0.0f || param->posx >= max_x) {
return ERROR_INVALID_POSX;
}
if (!std::isfinite(param->posy) || param->posy < 0.0f || param->posy >= max_y) {
return ERROR_INVALID_POSY;
}
if (static_cast<uint32_t>(param->horizontal_alignment) > 2) {
return ERROR_INVALID_HALIGN;
}
if (static_cast<uint32_t>(param->vertical_alignment) > 2) {
return ERROR_INVALID_VALIGN;
}
const int extended_result = ValidateExtended(extended);
if (extended_result != OK) {
return extended_result;
}
if (param->work == nullptr || (reinterpret_cast<uintptr_t>(param->work) & 3u) != 0) {
return ERROR_INVALID_WORK;
}
if (param->handler == nullptr) {
return ERROR_INVALID_HANDLER;
}
if (!AllZero(param->reserved, sizeof(param->reserved))) {
return ERROR_INVALID_RESERVED;
}
const size_t text_size =
static_cast<size_t>(param->max_text_length +
((param->option & OPTION_EXPANDED_PREEDIT) != 0 ? 0x79u : 0x1fu)) *
sizeof(char16_t);
if (RangesOverlap(reinterpret_cast<uintptr_t>(param->input_text_buffer), text_size,
reinterpret_cast<uintptr_t>(param->work), WORK_BUFFER_SIZE)) {
return ERROR_INVALID_PARAM;
}
return ReadInitialText(*param, text) ? OK : ERROR_INVALID_TEXT;
}
} // namespace
void KYTY_SYSV_ABI ImeParamInit(Param* param) {
if (param == nullptr) {
return;
}
std::memset(param, 0, sizeof(*param));
param->user_id = -1;
}
int KYTY_SYSV_ABI ImeGetPanelSize(const Param* param, uint32_t* width, uint32_t* height) {
if (param == nullptr || width == nullptr || height == nullptr) {
return ERROR_INVALID_ADDRESS;
}
if (static_cast<uint32_t>(param->type) > static_cast<uint32_t>(Type::Number)) {
return ERROR_INVALID_TYPE;
}
if ((param->option & ~VALID_OPTIONS) != 0) {
return ERROR_INVALID_OPTION;
}
*width = param->type == Type::Number ? 370 : 793;
*height = param->type == Type::Number ? 402 : 408;
if ((param->option & OPTION_USE_OVER_2K) != 0) {
*width *= 2;
*height *= 2;
}
return OK;
}
int KYTY_SYSV_ABI ImeOpen(const Param* param, const ExtendedParam* extended) {
uint64_t generation = 0;
{
std::scoped_lock lock(g_mutex);
if (g_state.open) {
return ERROR_BUSY;
}
std::u16string text;
const int validation = ValidateOpen(param, extended, &text);
if (validation != OK) {
return validation;
}
const uint64_t next_generation = g_state.generation + 1;
const uint64_t next_revision = g_state.revision + 1;
g_state = {};
g_state.open = true;
g_state.generation = next_generation;
g_state.revision = next_revision;
g_state.param = *param;
if (extended != nullptr) {
g_state.extended = *extended;
}
g_state.arg = param->arg;
g_state.handler = param->handler;
g_state.editor.Reset(param->type, param->option, param->max_text_length, std::move(text));
std::memset(param->work, 0, WORK_BUFFER_SIZE);
SyncTextBuffersLocked();
QueuedEvent open;
open.event.id = 0;
open.event.param.rect.x = param->posx;
open.event.param.rect.y = param->posy;
open.event.param.rect.width = param->type == Type::Number ? 370 : 793;
open.event.param.rect.height = param->type == Type::Number ? 402 : 408;
if ((param->option & OPTION_USE_OVER_2K) != 0) {
open.event.param.rect.width *= 2;
open.event.param.rect.height *= 2;
}
QueueEventLocked(std::move(open));
generation = g_state.generation;
g_open.store(true, std::memory_order_release);
g_revision.store(g_state.revision, std::memory_order_release);
}
NotifyVisibility(true, generation);
return OK;
}
int KYTY_SYSV_ABI ImeUpdate(EventHandler handler) {
bool apply_inputs = false;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open && !g_keyboard_state.open) {
return ERROR_NOT_OPENED;
}
const bool update_ime = g_state.open && handler == g_state.handler;
const bool update_keyboard = g_keyboard_state.open && handler == g_keyboard_state.handler;
if (!update_ime && !update_keyboard) {
return ERROR_NOT_OPENED;
}
apply_inputs = update_ime;
}
if (apply_inputs) {
ApplyExternalInputs();
}
struct Dispatch {
void* arg;
QueuedEvent queued;
char16_t* work;
char16_t* input;
uint32_t max_length;
uint64_t generation;
};
std::vector<Dispatch> dispatch;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open && !g_keyboard_state.open) {
return ERROR_NOT_OPENED;
}
const bool update_ime = g_state.open && handler == g_state.handler;
const bool update_keyboard = g_keyboard_state.open && handler == g_keyboard_state.handler;
if (!update_ime && !update_keyboard) {
return ERROR_NOT_OPENED;
}
if (update_ime && std::exchange(g_state.event_overflow, false)) {
return ERROR_EVENT_OVERFLOW;
}
if (update_ime) {
while (!g_state.events.empty()) {
dispatch.push_back({g_state.arg, std::move(g_state.events.front()),
static_cast<char16_t*>(g_state.param.work),
g_state.param.input_text_buffer, g_state.editor.GetMaxLength(),
g_state.generation});
g_state.events.pop_front();
}
}
if (update_keyboard) {
while (!g_keyboard_state.events.empty()) {
dispatch.push_back({g_keyboard_state.arg,
std::move(g_keyboard_state.events.front()), nullptr, nullptr, 0,
0});
g_keyboard_state.events.pop_front();
}
}
}
for (auto& item: dispatch) {
if (item.generation != 0) {
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != item.generation) {
continue;
}
}
if (item.queued.text_payload) {
SyncGuestText(item.work, item.input, item.max_length, item.queued.text);
item.queued.event.param.text.str = item.work;
}
handler(item.arg, &item.queued.event);
}
return OK;
}
int KYTY_SYSV_ABI ImeClose() {
uint64_t generation = 0;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open) {
return ERROR_NOT_OPENED;
}
generation = g_state.generation;
const uint64_t revision = g_state.revision + 1;
g_state = {};
g_state.generation = generation;
g_state.revision = revision;
g_open.store(false, std::memory_order_release);
g_revision.store(revision, std::memory_order_release);
}
NotifyVisibility(false, generation);
return OK;
}
int KYTY_SYSV_ABI ImeSetText(const char16_t* text, uint32_t length) {
std::scoped_lock lock(g_mutex);
if (!g_state.open) {
return ERROR_NOT_OPENED;
}
if (text == nullptr) {
return ERROR_INVALID_ADDRESS;
}
const uint32_t clamped_length = std::min(length, g_state.editor.GetMaxLength());
uint32_t actual_length = 0;
if (!ValidateText(text, clamped_length, (g_state.editor.GetOption() & OPTION_MULTILINE) != 0,
&actual_length)) {
return ERROR_INVALID_TEXT;
}
g_state.editor.ReplaceText(std::u16string(text, text + actual_length),
g_state.editor.GetCursor());
SyncTextBuffersLocked();
UpdateRevisionLocked();
return OK;
}
int KYTY_SYSV_ABI ImeSetCaret(const Caret* caret) {
std::scoped_lock lock(g_mutex);
if (!g_state.open) {
return ERROR_NOT_OPENED;
}
if (caret == nullptr) {
return ERROR_INVALID_ADDRESS;
}
if (caret->index > g_state.editor.GetText().size()) {
return ERROR_INVALID_PARAM;
}
g_state.editor.SetCursor(caret->index);
UpdateRevisionLocked();
return OK;
}
int KYTY_SYSV_ABI ImeSetTextGeometry(TextAreaMode mode, const TextGeometry* geometry) {
std::scoped_lock lock(g_mutex);
if (!g_state.open) {
return ERROR_NOT_OPENED;
}
if (geometry == nullptr) {
return ERROR_INVALID_ADDRESS;
}
const bool over_2k = (g_state.editor.GetOption() & OPTION_USE_OVER_2K) != 0;
const float max_x = over_2k ? 3840.0f : 1920.0f;
const float max_y = over_2k ? 2160.0f : 1080.0f;
if (!std::isfinite(geometry->x) || !std::isfinite(geometry->y) || geometry->x < 0.0f ||
geometry->x >= max_x || geometry->y < 0.0f || geometry->y >= max_y ||
(mode != TextAreaMode::Preedit && mode != TextAreaMode::Select)) {
return ERROR_INVALID_PARAM;
}
return OK;
}
int KYTY_SYSV_ABI ImeKeyboardOpen(int32_t user_id, const KeyboardParam* param) {
std::scoped_lock lock(g_mutex);
if (param == nullptr) {
return ERROR_INVALID_ADDRESS;
}
if (user_id == -1) {
return ERROR_INVALID_USER_ID;
}
if (param->handler == nullptr) {
return ERROR_INVALID_HANDLER;
}
if ((param->option & ~VALID_KEYBOARD_OPTIONS) != 0) {
return ERROR_INVALID_OPTION;
}
if (!AllZero(param->reserved1, sizeof(param->reserved1)) ||
!AllZero(param->reserved2, sizeof(param->reserved2))) {
return ERROR_INVALID_RESERVED;
}
if (g_keyboard_state.open) {
return ERROR_BUSY;
}
g_keyboard_state = {};
g_keyboard_state.open = true;
g_keyboard_state.arg = param->arg;
g_keyboard_state.handler = param->handler;
QueuedEvent open;
open.event.id = 256;
open.event.param.resource_id_array.user_id = user_id;
g_keyboard_state.events.push_back(std::move(open));
return OK;
}
int KYTY_SYSV_ABI ImeKeyboardClose(int32_t user_id) {
std::scoped_lock lock(g_mutex);
if (!g_keyboard_state.open) {
return ERROR_NOT_OPENED;
}
if (user_id == -1) {
return ERROR_INVALID_USER_ID;
}
g_keyboard_state = {};
return OK;
}
int KYTY_SYSV_ABI ImeKeyboardGetResourceId(int32_t user_id, KeyboardResourceIdArray* resource_ids) {
if (resource_ids == nullptr) {
return ERROR_INVALID_ADDRESS;
}
if (user_id == -1) {
return ERROR_INVALID_USER_ID;
}
std::memset(resource_ids, 0, sizeof(*resource_ids));
resource_ids->user_id = user_id;
std::scoped_lock lock(g_mutex);
return g_keyboard_state.open ? ERROR_CONNECTION_FAILED : ERROR_NOT_OPENED;
}
int KYTY_SYSV_ABI ImeKeyboardGetInfo(uint32_t resource_id, KeyboardInfo* info) {
if (info == nullptr) {
return ERROR_INVALID_ADDRESS;
}
std::scoped_lock lock(g_mutex);
if (!g_keyboard_state.open) {
return ERROR_NOT_OPENED;
}
(void)resource_id;
return ERROR_NO_RESOURCE_ID;
}
int KYTY_SYSV_ABI ImeKeyboardSetMode(int32_t user_id, uint32_t mode) {
if (user_id == -1) {
return ERROR_INVALID_USER_ID;
}
std::scoped_lock lock(g_mutex);
if (!g_keyboard_state.open) {
return ERROR_NOT_OPENED;
}
return (mode & ~VALID_KEYBOARD_MODE) == 0 ? OK : ERROR_INVALID_MODE;
}
VisualState GetVisualState() noexcept {
return {g_open.load(std::memory_order_acquire), g_revision.load(std::memory_order_acquire)};
}
void SetVisibilityCallback(VisibilityCallback callback) noexcept {
g_visibility_callback.store(callback, std::memory_order_release);
}
bool GetHostSnapshot(HostSnapshot* snapshot) {
if (snapshot == nullptr) {
return false;
}
std::scoped_lock lock(g_mutex);
if (!g_state.open) {
return false;
}
snapshot->generation = g_state.generation;
snapshot->type = g_state.param.type;
snapshot->enter_label = g_state.param.enter_label;
snapshot->option = g_state.editor.GetOption();
snapshot->max_text_length = g_state.editor.GetMaxLength();
snapshot->cursor = g_state.editor.GetCursor();
snapshot->disable_device = g_state.extended.disable_device;
snapshot->key_panel_visible = (g_state.editor.GetOption() & OPTION_EXT_KEYBOARD) == 0 ||
(g_state.extended.option & EXT_OPTION_HIDE_KEY_PANEL) == 0;
snapshot->posx = g_state.param.posx;
snapshot->posy = g_state.param.posy;
snapshot->horizontal_alignment = g_state.param.horizontal_alignment;
snapshot->vertical_alignment = g_state.param.vertical_alignment;
snapshot->panel_width = g_state.param.type == Type::Number ? 370 : 793;
snapshot->panel_height = g_state.param.type == Type::Number ? 402 : 408;
if ((g_state.editor.GetOption() & OPTION_USE_OVER_2K) != 0) {
snapshot->panel_width *= 2;
snapshot->panel_height *= 2;
}
snapshot->text = g_state.editor.GetText();
snapshot->title.clear();
snapshot->placeholder.clear();
return true;
}
static bool ApplyInsertText(uint64_t generation, std::u16string_view text) {
uint64_t revision = 0;
bool multiline = false;
TextFilter filter = nullptr;
std::u16string old_text;
ImeCommon::TextEditEngine candidate;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation || text.empty()) {
return false;
}
multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0;
if (!ImeCommon::IsValidInputText(text, multiline)) {
return false;
}
candidate = g_state.editor;
if (!candidate.Insert(text)) {
return false;
}
revision = g_state.revision;
filter = g_state.param.filter;
old_text = g_state.editor.GetText();
}
return CommitFilteredText(generation, revision, std::move(old_text), std::move(candidate),
filter, multiline);
}
static bool ApplyBackspace(uint64_t generation) {
uint64_t revision = 0;
bool multiline = false;
TextFilter filter = nullptr;
std::u16string old_text;
ImeCommon::TextEditEngine candidate;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation) {
return false;
}
candidate = g_state.editor;
if (!candidate.Backspace()) {
return false;
}
revision = g_state.revision;
multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0;
filter = g_state.param.filter;
old_text = g_state.editor.GetText();
}
return CommitFilteredText(generation, revision, std::move(old_text), std::move(candidate),
filter, multiline);
}
static bool ApplyMoveCursor(uint64_t generation, int delta) {
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation || delta == 0) {
return false;
}
const uint32_t old_cursor = g_state.editor.GetCursor();
if (!g_state.editor.MoveCursor(delta)) {
return false;
}
const uint32_t new_cursor = g_state.editor.GetCursor();
const uint32_t direction = new_cursor > old_cursor ? 2 : 1;
const uint32_t steps = static_cast<uint32_t>(
std::abs(static_cast<int>(new_cursor) - static_cast<int>(old_cursor)));
for (uint32_t i = 0; i < steps; i++) {
QueuedEvent event;
event.event.id = 2;
event.event.param.caret_move = direction;
QueueEventLocked(std::move(event));
}
UpdateRevisionLocked();
return true;
}
static bool ApplyAccept(uint64_t generation) {
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation) {
return false;
}
QueuedEvent event;
event.event.id = 5;
return QueueEventLocked(std::move(event));
}
static bool ApplyCancel(uint64_t generation) {
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation) {
return false;
}
QueuedEvent event;
event.event.id = 4;
return QueueEventLocked(std::move(event));
}
bool HostInsertText(uint64_t generation, std::u16string_view text) {
ExternalInput input;
input.action = ExternalAction::Text;
input.text = text;
return HostQueueExternalInput(generation, std::move(input));
}
bool HostBackspace(uint64_t generation) {
ExternalInput input;
input.action = ExternalAction::Backspace;
return HostQueueExternalInput(generation, std::move(input));
}
bool HostAccept(uint64_t generation) {
ExternalInput input;
input.action = ExternalAction::Accept;
return HostQueueExternalInput(generation, std::move(input));
}
bool HostCancel(uint64_t generation) {
ExternalInput input;
input.action = ExternalAction::Cancel;
return HostQueueExternalInput(generation, std::move(input));
}
bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation) {
return false;
}
if (g_state.external_inputs.size() == EVENT_QUEUE_CAPACITY) {
g_state.event_overflow = true;
return false;
}
g_state.external_inputs.push_back(std::move(input));
return true;
}
namespace {
void ApplyExternalInputs() {
uint64_t generation = 0;
ExtKeyboardFilter filter = nullptr;
int32_t user_id = -1;
bool multiline = false;
std::vector<ExternalInput> inputs;
{
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.external_inputs.empty()) {
return;
}
generation = g_state.generation;
filter = g_state.extended.ext_keyboard_filter;
user_id = g_state.param.user_id;
multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0;
inputs.swap(g_state.external_inputs);
}
for (auto& input: inputs) {
{
std::scoped_lock lock(g_mutex);
if (!g_state.open || g_state.generation != generation) {
break;
}
}
input.key.user_id = user_id;
bool accepted = true;
if (filter != nullptr && input.key.status != 0) {
uint16_t keycode = input.key.keycode;
uint32_t status = input.key.status;
if (filter(&input.key, &keycode, &status, nullptr) == 0) {
accepted = ImeCommon::ApplyKeyboardFilterOutput(&input, keycode, status, multiline);
}
}
if (!accepted) {
continue;
}
switch (input.action) {
case ExternalAction::None: break;
case ExternalAction::Text: ApplyInsertText(generation, input.text); break;
case ExternalAction::Backspace: ApplyBackspace(generation); break;
case ExternalAction::MoveLeft: ApplyMoveCursor(generation, -1); break;
case ExternalAction::MoveRight: ApplyMoveCursor(generation, 1); break;
case ExternalAction::Cancel: ApplyCancel(generation); break;
case ExternalAction::Accept: ApplyAccept(generation); break;
case ExternalAction::Newline: ApplyInsertText(generation, u"\n"); break;
}
}
}
} // namespace
#if !defined(KYTY_IME_TESTS)
LIB_VERSION("Ime", 1, "Ime", 1, 1);
LIB_DEFINE(InitPlatform_1_Ime) {
LIB_FUNC("ieCNrVrzKd4", ImeSetText);
LIB_FUNC("WLxUN2WMim8", ImeSetCaret);
LIB_FUNC("WmYDzdC4EHI", ImeParamInit);
LIB_FUNC("ziPDcIjO0Vk", ImeGetPanelSize);
LIB_FUNC("RPydv-Jr1bc", ImeOpen);
LIB_FUNC("-4GCfYdNF1s", ImeUpdate);
LIB_FUNC("TmVP8LzcFcY", ImeClose);
LIB_FUNC("TXYHFRuL8UY", ImeSetTextGeometry);
LIB_FUNC("eaFXjfJv3xs", ImeKeyboardOpen);
LIB_FUNC("PMVehSlfZ94", ImeKeyboardClose);
LIB_FUNC("dKadqZFgKKQ", ImeKeyboardGetResourceId);
LIB_FUNC("VkqLPArfFdc", ImeKeyboardGetInfo);
LIB_FUNC("ua+13Hk9kKs", ImeKeyboardSetMode);
}
#endif
} // namespace Libs::Ime
+174
View File
@@ -0,0 +1,174 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
#include "libs/imeCommon.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 = ImeCommon::MAX_TEXT_LENGTH;
using Type = ImeCommon::Type;
using EnterLabel = ImeCommon::EnterLabel;
using Alignment = ImeCommon::Alignment;
struct Event;
using Keycode = ImeCommon::Keycode;
using TextFilter = ImeCommon::TextFilter;
using EventHandler = void(KYTY_SYSV_ABI*)(void* arg, const Event* event);
using ExtKeyboardFilter = ImeCommon::ExtKeyboardFilter;
using Color = ImeCommon::Color;
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];
};
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];
};
using ExtendedParam = ImeCommon::ExtendedParam;
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];
};
using ExternalAction = ImeCommon::ExternalAction;
using ExternalInput = ImeCommon::ExternalInput;
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);
using VisualState = ImeCommon::VisualState;
using HostSnapshot = ImeCommon::HostSnapshot;
using VisibilityCallback = ImeCommon::VisibilityCallback;
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_
+259
View File
@@ -0,0 +1,259 @@
#include "libs/imeCommon.h"
#include <algorithm>
#include <vector>
namespace Libs::ImeCommon {
namespace {
constexpr bool IsHighSurrogate(char16_t value) {
return value >= 0xd800 && value <= 0xdbff;
}
constexpr bool IsLowSurrogate(char16_t value) {
return value >= 0xdc00 && value <= 0xdfff;
}
char16_t HidCharacter(uint16_t keycode, uint32_t status) {
const bool shift = (status & 0x00002200) != 0;
const bool caps = (status & 0x00020000) != 0;
if (keycode >= 4 && keycode <= 29) {
return static_cast<char16_t>(((shift != caps) ? u'A' : u'a') + keycode - 4);
}
if (keycode >= 30 && keycode <= 39) {
static constexpr char16_t plain[] = u"1234567890";
static constexpr char16_t shifted[] = u"!@#$%^&*()";
return (shift ? shifted : plain)[keycode - 30];
}
if (keycode == 44) {
return u' ';
}
if (keycode >= 45 && keycode <= 56) {
static constexpr char16_t plain[] = u"-=[]\\#;'`,./";
static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?";
return (shift ? shifted : plain)[keycode - 45];
}
if (keycode >= 89 && keycode <= 98) {
static constexpr char16_t keypad[] = u"1234567890";
return keypad[keycode - 89];
}
return keycode == 99 ? u'.' : u'\0';
}
} // namespace
bool IsValidUtf16(std::u16string_view text) {
for (size_t i = 0; i < text.size(); i++) {
if (IsHighSurrogate(text[i])) {
if (++i >= text.size() || !IsLowSurrogate(text[i])) {
return false;
}
} else if (IsLowSurrogate(text[i])) {
return false;
}
}
return true;
}
bool IsValidInputText(std::u16string_view text, bool multiline) {
if (!IsValidUtf16(text)) {
return false;
}
return std::none_of(text.begin(), text.end(), [multiline](char16_t value) {
return value == u'\0' || (!multiline && (value == u'\n' || value == u'\r'));
});
}
bool IsAllowedInput(char16_t value, Type type, uint32_t option) {
if (value == u'\r' || value == u'\0') {
return false;
}
if (value == u'\n') {
return (option & OPTION_MULTILINE) != 0;
}
if (type == Type::Number) {
return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.';
}
if (type == Type::BasicLatin) {
return value >= u' ' && value <= u'~';
}
return true;
}
uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) {
cursor = std::min<uint32_t>(cursor, static_cast<uint32_t>(text.size()));
if (cursor > 0 && cursor < text.size() && IsHighSurrogate(text[cursor - 1]) &&
IsLowSurrogate(text[cursor])) {
cursor++;
}
return cursor;
}
void ClampText(std::u16string* text, uint32_t limit) {
if (text->size() <= limit) {
return;
}
text->resize(limit);
if (!text->empty() && IsHighSurrogate(text->back())) {
text->pop_back();
}
}
EditDelta ComputeEditDelta(std::u16string_view before, std::u16string_view after) {
size_t prefix = 0;
while (prefix < before.size() && prefix < after.size() && before[prefix] == after[prefix]) {
prefix++;
}
size_t before_tail = before.size();
size_t after_tail = after.size();
while (before_tail > prefix && after_tail > prefix &&
before[before_tail - 1] == after[after_tail - 1]) {
before_tail--;
after_tail--;
}
return {static_cast<uint32_t>(prefix),
static_cast<int32_t>(after_tail - prefix) - static_cast<int32_t>(before_tail - prefix)};
}
bool RunTextFilter(TextFilter filter, const std::u16string& source, uint32_t output_capacity,
std::u16string* output) {
if (filter == nullptr || output == nullptr) {
return false;
}
std::vector<char16_t> buffer(static_cast<size_t>(output_capacity) + 1, u'\0');
uint32_t output_length = output_capacity;
if (filter(buffer.data(), &output_length, source.c_str(),
static_cast<uint32_t>(source.size())) != 0 ||
output_length > output_capacity ||
!IsValidUtf16(std::u16string_view(buffer.data(), output_length))) {
return false;
}
output->assign(buffer.data(), buffer.data() + output_length);
return true;
}
bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status,
bool multiline) {
constexpr uint32_t KEYCODE_VALID = 0x00000001;
constexpr uint32_t CHARACTER_VALID = 0x00000002;
if (keycode == input->key.keycode && status == input->key.status) {
return true;
}
if ((status & KEYCODE_VALID) == 0 || keycode == 0) {
return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0;
}
input->key.keycode = keycode;
input->key.status = status;
input->text.clear();
switch (keycode) {
case 40:
case 88:
case 158:
input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept;
break;
case 41: input->action = ExternalAction::Cancel; break;
case 42:
case 187: input->action = ExternalAction::Backspace; break;
case 43:
case 186: input->action = ExternalAction::None; break;
case 79: input->action = ExternalAction::MoveRight; break;
case 80: input->action = ExternalAction::MoveLeft; break;
default: {
const char16_t character = HidCharacter(keycode, status);
if (character == u'\0') {
return false;
}
input->action = ExternalAction::Text;
input->key.character = character;
input->text.push_back(character);
break;
}
}
return true;
}
void TextEditEngine::Reset(Type type, uint32_t option, uint32_t max_length, std::u16string text) {
m_type = type;
m_option = option;
m_max_length = max_length;
ClampText(&text, max_length);
m_text = std::move(text);
m_cursor = static_cast<uint32_t>(m_text.size());
}
bool TextEditEngine::Insert(std::u16string_view text, EditDelta* edit) {
if (text.empty() || !IsValidUtf16(text)) {
return false;
}
std::u16string allowed;
allowed.reserve(text.size());
for (const char16_t value: text) {
if (IsAllowedInput(value, m_type, m_option)) {
allowed.push_back(value);
}
}
const size_t available = m_max_length - m_text.size();
if (allowed.empty() || available == 0) {
return false;
}
allowed.resize(std::min(allowed.size(), available));
if (!allowed.empty() && IsHighSurrogate(allowed.back())) {
allowed.pop_back();
}
if (allowed.empty()) {
return false;
}
if (edit != nullptr) {
*edit = {m_cursor, static_cast<int32_t>(allowed.size())};
}
m_text.insert(m_cursor, allowed);
m_cursor += static_cast<uint32_t>(allowed.size());
return true;
}
bool TextEditEngine::Backspace(EditDelta* edit) {
if (m_cursor == 0) {
return false;
}
uint32_t first = m_cursor - 1;
if (first > 0 && IsLowSurrogate(m_text[first]) && IsHighSurrogate(m_text[first - 1])) {
first--;
}
if (edit != nullptr) {
*edit = {first, -static_cast<int32_t>(m_cursor - first)};
}
m_text.erase(first, m_cursor - first);
m_cursor = first;
return true;
}
bool TextEditEngine::MoveCursor(int delta) {
if (delta == 0) {
return false;
}
int next = std::clamp(static_cast<int>(m_cursor) + delta, 0, static_cast<int>(m_text.size()));
if (delta < 0 && next > 0 && next < static_cast<int>(m_text.size()) &&
IsLowSurrogate(m_text[next]) && IsHighSurrogate(m_text[next - 1])) {
next--;
} else if (delta > 0 && next > 0 && next < static_cast<int>(m_text.size()) &&
IsHighSurrogate(m_text[next - 1]) && IsLowSurrogate(m_text[next])) {
next++;
}
if (next == static_cast<int>(m_cursor)) {
return false;
}
m_cursor = static_cast<uint32_t>(next);
return true;
}
void TextEditEngine::ReplaceText(std::u16string text, uint32_t cursor) {
ClampText(&text, m_max_length);
m_text = std::move(text);
m_cursor = NormalizeCursor(m_text, cursor);
}
void TextEditEngine::SetCursor(uint32_t cursor) {
m_cursor = NormalizeCursor(m_text, cursor);
}
} // namespace Libs::ImeCommon
+159
View File
@@ -0,0 +1,159 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_
#include "common/abi.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace Libs::ImeCommon {
constexpr uint32_t MAX_TEXT_LENGTH = 2048;
constexpr uint32_t OPTION_MULTILINE = 0x00000001;
constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = 0x00000002;
constexpr uint32_t OPTION_PASSWORD = 0x00000004;
constexpr uint32_t OPTION_EXT_KEYBOARD = 0x00000010;
constexpr uint32_t OPTION_FIXED_POSITION = 0x00000040;
constexpr uint32_t OPTION_DISABLE_POSITION_ADJUST = 0x00000800;
constexpr uint32_t OPTION_EXPANDED_PREEDIT = 0x00001000;
constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000;
constexpr uint32_t EXT_OPTION_HIDE_KEY_PANEL = 0x00000400;
constexpr uint32_t DISABLE_DEVICE_CONTROLLER = 0x00000001;
constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002;
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 Color {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
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;
};
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 ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode,
uint32_t* out_status, void* reserved);
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];
};
enum class ExternalAction : uint8_t {
None,
Text,
Backspace,
MoveLeft,
MoveRight,
Cancel,
Accept,
Newline,
};
struct ExternalInput {
Keycode key {};
ExternalAction action = ExternalAction::None;
std::u16string text;
};
struct VisualState {
bool active;
uint64_t revision;
};
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;
};
using VisibilityCallback = void (*)(bool visible, uint64_t generation);
struct EditDelta {
uint32_t index = 0;
int32_t length = 0;
};
class TextEditEngine {
public:
void Reset(Type type, uint32_t option, uint32_t max_length, std::u16string text);
bool Insert(std::u16string_view text, EditDelta* edit = nullptr);
bool Backspace(EditDelta* edit = nullptr);
bool MoveCursor(int delta);
void ReplaceText(std::u16string text, uint32_t cursor);
void SetCursor(uint32_t cursor);
[[nodiscard]] Type GetType() const { return m_type; }
[[nodiscard]] uint32_t GetOption() const { return m_option; }
[[nodiscard]] uint32_t GetMaxLength() const { return m_max_length; }
[[nodiscard]] uint32_t GetCursor() const { return m_cursor; }
[[nodiscard]] const std::u16string& GetText() const { return m_text; }
private:
Type m_type = Type::Default;
uint32_t m_option = 0;
uint32_t m_max_length = 0;
uint32_t m_cursor = 0;
std::u16string m_text;
};
[[nodiscard]] bool IsValidUtf16(std::u16string_view text);
[[nodiscard]] bool IsValidInputText(std::u16string_view text, bool multiline);
[[nodiscard]] bool IsAllowedInput(char16_t value, Type type, uint32_t option);
[[nodiscard]] uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor);
void ClampText(std::u16string* text, uint32_t limit);
[[nodiscard]] EditDelta ComputeEditDelta(std::u16string_view before, std::u16string_view after);
[[nodiscard]] bool RunTextFilter(TextFilter filter, const std::u16string& source,
uint32_t output_capacity, std::u16string* output);
[[nodiscard]] bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode,
uint32_t status, bool multiline);
} // namespace Libs::ImeCommon
#endif // EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_
+19 -205
View File
@@ -53,9 +53,8 @@ struct State {
ExtendedParam extended {};
bool input_changed = false;
bool commit_pending = false;
uint32_t cursor = 0;
std::u16string original_text;
std::u16string current_text;
ImeCommon::TextEditEngine editor;
std::u16string title;
std::u16string placeholder;
std::vector<ExternalInput> external_inputs;
@@ -71,8 +70,6 @@ bool AllZero(const int8_t* data, size_t size) {
return std::all_of(data, data + size, [](int8_t value) { return value == 0; });
}
bool IsValidUtf16(std::u16string_view text);
bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) {
out->clear();
if (text == nullptr) {
@@ -82,7 +79,7 @@ bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) {
for (uint32_t i = 0; i <= limit; ++i) {
const char16_t value = text[i];
if (value == u'\0') {
return IsValidUtf16(*out);
return ImeCommon::IsValidUtf16(*out);
}
if (i == limit) {
return false;
@@ -92,128 +89,6 @@ bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) {
return false;
}
bool IsValidUtf16(std::u16string_view text) {
for (size_t i = 0; i < text.size(); i++) {
const char16_t current = text[i];
if (current >= 0xd800 && current <= 0xdbff) {
if (++i >= text.size() || text[i] < 0xdc00 || text[i] > 0xdfff) {
return false;
}
} else if (current >= 0xdc00 && current <= 0xdfff) {
return false;
}
}
return true;
}
bool IsAllowedInput(char16_t value, Type type, uint32_t option) {
if (value == u'\r') {
return false;
}
if (value == u'\n') {
return (option & OPTION_MULTILINE) != 0;
}
if (value == u'\0') {
return false;
}
if (type == Type::Number) {
return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.';
}
if (type == Type::BasicLatin) {
return value >= u' ' && value <= u'~';
}
return true;
}
char16_t HidCharacter(uint16_t keycode, uint32_t status) {
const bool shift = (status & 0x00002200) != 0;
const bool caps = (status & 0x00020000) != 0;
if (keycode >= 4 && keycode <= 29) {
const bool upper = shift != caps;
return static_cast<char16_t>((upper ? u'A' : u'a') + keycode - 4);
}
if (keycode >= 30 && keycode <= 39) {
static constexpr char16_t plain[] = u"1234567890";
static constexpr char16_t shifted[] = u"!@#$%^&*()";
return (shift ? shifted : plain)[keycode - 30];
}
if (keycode == 44) {
return u' ';
}
if (keycode >= 45 && keycode <= 56) {
static constexpr char16_t plain[] = u"-=[]\\#;'`,./";
static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?";
return (shift ? shifted : plain)[keycode - 45];
}
if (keycode >= 89 && keycode <= 98) {
static constexpr char16_t keypad[] = u"1234567890";
return keypad[keycode - 89];
}
if (keycode == 99) {
return u'.';
}
return u'\0';
}
bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status,
bool multiline) {
constexpr uint32_t KEYCODE_VALID = 0x00000001;
constexpr uint32_t CHARACTER_VALID = 0x00000002;
if (keycode == input->key.keycode && status == input->key.status) {
return true;
}
if ((status & KEYCODE_VALID) == 0 || keycode == 0) {
return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0;
}
input->key.keycode = keycode;
input->key.status = status;
input->text.clear();
switch (keycode) {
case 40:
case 88:
case 158:
input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept;
break;
case 41: input->action = ExternalAction::Cancel; break;
case 42:
case 187: input->action = ExternalAction::Backspace; break;
case 43:
case 186: input->action = ExternalAction::None; break;
case 79: input->action = ExternalAction::MoveRight; break;
case 80: input->action = ExternalAction::MoveLeft; break;
default: {
const char16_t character = HidCharacter(keycode, status);
if (character == u'\0') {
return false;
}
input->action = ExternalAction::Text;
input->key.character = character;
input->text.push_back(character);
break;
}
}
return true;
}
void ClampText(std::u16string* text, uint32_t limit) {
if (text->size() <= limit) {
return;
}
text->resize(limit);
if (!text->empty() && text->back() >= 0xd800 && text->back() <= 0xdbff) {
text->pop_back();
}
}
uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) {
cursor = std::min<uint32_t>(cursor, static_cast<uint32_t>(text.size()));
if (cursor > 0 && cursor < text.size() && text[cursor - 1] >= 0xd800 &&
text[cursor - 1] <= 0xdbff && text[cursor] >= 0xdc00 && text[cursor] <= 0xdfff) {
cursor++;
}
return cursor;
}
void WriteGuestText(const State& state, const std::u16string& text) {
if (state.param.input_text_buffer == nullptr) {
return;
@@ -355,7 +230,7 @@ void ApplyFilterAndCommit() {
return;
}
filter = g_state.input_changed ? g_state.param.filter : nullptr;
source = g_state.current_text;
source = g_state.editor.GetText();
max_length = g_state.param.max_text_length;
generation = g_state.generation;
revision = g_state.revision;
@@ -363,14 +238,10 @@ void ApplyFilterAndCommit() {
}
if (filter != nullptr) {
std::vector<char16_t> output(IME_DIALOG_MAX_TEXT_LENGTH + 1, u'\0');
uint32_t output_length = IME_DIALOG_MAX_TEXT_LENGTH;
if (filter(output.data(), &output_length, source.c_str(),
static_cast<uint32_t>(source.size())) == 0 &&
output_length <= IME_DIALOG_MAX_TEXT_LENGTH &&
IsValidUtf16(std::u16string_view(output.data(), output_length))) {
source.assign(output.data(), output.data() + output_length);
ClampText(&source, max_length);
std::u16string filtered;
if (ImeCommon::RunTextFilter(filter, source, IME_DIALOG_MAX_TEXT_LENGTH, &filtered)) {
source = std::move(filtered);
ImeCommon::ClampText(&source, max_length);
}
}
@@ -386,13 +257,12 @@ void ApplyFilterAndCommit() {
return;
}
if (!g_state.input_changed) {
g_state.current_text = std::move(source);
g_state.cursor = NormalizeCursor(g_state.current_text, g_state.cursor);
g_state.editor.ReplaceText(std::move(source), g_state.editor.GetCursor());
}
const auto& committed =
g_state.status == Status::Finished && g_state.end_status != EndStatus::Ok
? g_state.original_text
: g_state.current_text;
: g_state.editor.GetText();
WriteGuestText(g_state, committed);
g_state.commit_pending = false;
}
@@ -451,8 +321,8 @@ void ApplyExternalInputs() {
uint16_t output_keycode = input.key.keycode;
uint32_t output_status = input.key.status;
if (filter(&input.key, &output_keycode, &output_status, nullptr) == 0) {
accepted =
ApplyKeyboardFilterOutput(&input, output_keycode, output_status, multiline);
accepted = ImeCommon::ApplyKeyboardFilterOutput(&input, output_keycode,
output_status, multiline);
}
}
if (!accepted) {
@@ -528,9 +398,9 @@ int KYTY_SYSV_ABI ImeDialogInit(const Param* param, const ExtendedParam* extende
if (extended != nullptr) {
g_state.extended = *extended;
}
g_state.original_text = initial;
g_state.current_text = std::move(initial);
g_state.cursor = static_cast<uint32_t>(g_state.current_text.size());
g_state.original_text = initial;
g_state.editor.Reset(param->type, param->option, param->max_text_length,
std::move(initial));
g_state.title = std::move(title);
g_state.placeholder = std::move(placeholder);
g_state.commit_pending = true;
@@ -664,7 +534,7 @@ bool GetHostSnapshot(HostSnapshot* snapshot) {
snapshot->enter_label = g_state.param.enter_label;
snapshot->option = g_state.param.option;
snapshot->max_text_length = g_state.param.max_text_length;
snapshot->cursor = g_state.cursor;
snapshot->cursor = g_state.editor.GetCursor();
snapshot->disable_device = g_state.extended.disable_device;
snapshot->key_panel_visible = (g_state.param.option & OPTION_EXT_KEYBOARD) == 0 ||
(g_state.extended.option & 0x00000400) == 0;
@@ -674,7 +544,7 @@ bool GetHostSnapshot(HostSnapshot* snapshot) {
snapshot->vertical_alignment = g_state.param.vertical_alignment;
ComputePanelSize(g_state.param, &g_state.extended, &snapshot->panel_width,
&snapshot->panel_height);
snapshot->text = g_state.current_text;
snapshot->text = g_state.editor.GetText();
snapshot->title = g_state.title;
snapshot->placeholder = g_state.placeholder;
return true;
@@ -682,38 +552,9 @@ bool GetHostSnapshot(HostSnapshot* snapshot) {
bool HostInsertText(uint64_t generation, std::u16string_view text) {
std::scoped_lock lock(g_mutex);
if (!MatchRunningGeneration(generation) || text.empty()) {
if (!MatchRunningGeneration(generation) || !g_state.editor.Insert(text)) {
return false;
}
if (!IsValidUtf16(text)) {
return false;
}
std::u16string allowed;
allowed.reserve(text.size());
for (const char16_t value: text) {
if (IsAllowedInput(value, g_state.param.type, g_state.param.option)) {
allowed.push_back(value);
}
}
if (allowed.empty()) {
return false;
}
const size_t available = g_state.param.max_text_length - g_state.current_text.size();
if (available == 0) {
return false;
}
std::u16string insertion(std::u16string_view(allowed).substr(0, available));
if (insertion.size() < allowed.size() && !insertion.empty() && insertion.back() >= 0xd800 &&
insertion.back() <= 0xdbff) {
insertion.pop_back();
}
if (insertion.empty()) {
return false;
}
std::u16string candidate = g_state.current_text;
candidate.insert(g_state.cursor, insertion);
g_state.current_text = std::move(candidate);
g_state.cursor += static_cast<uint32_t>(insertion.size());
g_state.input_changed = true;
g_state.commit_pending = true;
return true;
@@ -721,17 +562,9 @@ bool HostInsertText(uint64_t generation, std::u16string_view text) {
bool HostBackspace(uint64_t generation) {
std::scoped_lock lock(g_mutex);
if (!MatchRunningGeneration(generation) || g_state.cursor == 0) {
if (!MatchRunningGeneration(generation) || !g_state.editor.Backspace()) {
return false;
}
uint32_t first = g_state.cursor - 1;
if (first > 0 && g_state.current_text[first] >= 0xdc00 &&
g_state.current_text[first] <= 0xdfff && g_state.current_text[first - 1] >= 0xd800 &&
g_state.current_text[first - 1] <= 0xdbff) {
first--;
}
g_state.current_text.erase(first, g_state.cursor - first);
g_state.cursor = first;
g_state.input_changed = true;
g_state.commit_pending = true;
return true;
@@ -739,26 +572,7 @@ bool HostBackspace(uint64_t generation) {
bool HostMoveCursor(uint64_t generation, int delta) {
std::scoped_lock lock(g_mutex);
if (!MatchRunningGeneration(generation) || delta == 0) {
return false;
}
int next = std::clamp(static_cast<int>(g_state.cursor) + delta, 0,
static_cast<int>(g_state.current_text.size()));
if (delta < 0 && next > 0 && next < static_cast<int>(g_state.current_text.size()) &&
g_state.current_text[next] >= 0xdc00 && g_state.current_text[next] <= 0xdfff &&
g_state.current_text[next - 1] >= 0xd800 && g_state.current_text[next - 1] <= 0xdbff) {
next--;
} else if (delta > 0 && next > 0 && next < static_cast<int>(g_state.current_text.size()) &&
g_state.current_text[next - 1] >= 0xd800 &&
g_state.current_text[next - 1] <= 0xdbff && g_state.current_text[next] >= 0xdc00 &&
g_state.current_text[next] <= 0xdfff) {
next++;
}
if (next == static_cast<int>(g_state.cursor)) {
return false;
}
g_state.cursor = static_cast<uint32_t>(next);
return true;
return MatchRunningGeneration(generation) && g_state.editor.MoveCursor(delta);
}
bool HostAccept(uint64_t generation) {
+25 -99
View File
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_
#include "common/abi.h"
#include "libs/imeCommon.h"
#include <cstddef>
#include <cstdint>
@@ -10,53 +10,43 @@
namespace Libs::Dialog::ImeDialog {
constexpr uint32_t IME_DIALOG_MAX_TEXT_LENGTH = 2048;
constexpr uint32_t IME_DIALOG_MAX_TEXT_LENGTH = ImeCommon::MAX_TEXT_LENGTH;
constexpr uint32_t IME_DIALOG_MAX_TITLE_LENGTH = 128;
constexpr uint32_t IME_DIALOG_MAX_PLACEHOLDER_LENGTH = 64;
enum class Status : uint32_t { None = 0, Running = 1, Finished = 2 };
enum class EndStatus : uint32_t { Ok = 0, UserCanceled = 1, Aborted = 2 };
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 };
using Type = ImeCommon::Type;
using EnterLabel = ImeCommon::EnterLabel;
using Alignment = ImeCommon::Alignment;
enum Option : uint32_t {
OPTION_MULTILINE = 0x00000001,
OPTION_NO_AUTO_CAPITALIZE = 0x00000002,
OPTION_PASSWORD = 0x00000004,
OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE,
OPTION_NO_AUTO_CAPITALIZE = ImeCommon::OPTION_NO_AUTO_CAPITALIZE,
OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD,
OPTION_LANGUAGES_FORCED = 0x00000008,
OPTION_EXT_KEYBOARD = 0x00000010,
OPTION_EXT_KEYBOARD = ImeCommon::OPTION_EXT_KEYBOARD,
OPTION_NO_LEARNING = 0x00000020,
OPTION_FIXED_POSITION = 0x00000040,
OPTION_FIXED_POSITION = ImeCommon::OPTION_FIXED_POSITION,
OPTION_DISABLE_COPY_PASTE = 0x00000080,
OPTION_DISABLE_RESUME = 0x00000100,
OPTION_DISABLE_AUTO_SPACE = 0x00000200,
OPTION_DISABLE_POSITION_ADJ = 0x00000800,
OPTION_EXPANDED_PREEDIT = 0x00001000,
OPTION_DISABLE_POSITION_ADJ = ImeCommon::OPTION_DISABLE_POSITION_ADJUST,
OPTION_EXPANDED_PREEDIT = ImeCommon::OPTION_EXPANDED_PREEDIT,
OPTION_JAPANESE_CAPS_LOCK = 0x00002000,
OPTION_USE_OVER_2K = 0x00004000,
OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K,
};
enum DisableDevice : uint32_t {
DISABLE_DEVICE_CONTROLLER = 0x00000001,
DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002,
DISABLE_DEVICE_CONTROLLER = ImeCommon::DISABLE_DEVICE_CONTROLLER,
DISABLE_DEVICE_EXT_KEYBOARD = ImeCommon::DISABLE_DEVICE_EXT_KEYBOARD,
DISABLE_DEVICE_REMOTE_OSK = 0x00000004,
};
struct Color {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
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 ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode,
uint32_t* out_status, void* reserved);
using Color = ImeCommon::Color;
using Keycode = ImeCommon::Keycode;
using TextFilter = ImeCommon::TextFilter;
using ExtKeyboardFilter = ImeCommon::ExtKeyboardFilter;
struct Param {
int32_t user_id;
@@ -82,24 +72,7 @@ struct Result {
int8_t reserved[12];
};
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];
};
using ExtendedParam = ImeCommon::ExtendedParam;
struct PositionAndForm {
uint32_t type;
@@ -111,32 +84,8 @@ struct PositionAndForm {
uint32_t height;
};
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;
};
enum class ExternalAction : uint8_t {
None,
Text,
Backspace,
MoveLeft,
MoveRight,
Cancel,
Accept,
Newline,
};
struct ExternalInput {
Keycode key;
ExternalAction action;
std::u16string text;
};
using ExternalAction = ImeCommon::ExternalAction;
using ExternalInput = ImeCommon::ExternalInput;
static_assert(sizeof(Param) == 0x60);
static_assert(offsetof(Param, input_text_buffer) == 0x28);
@@ -147,32 +96,9 @@ static_assert(offsetof(ExtendedParam, additional_dictionary_path) == 0x30);
static_assert(sizeof(PositionAndForm) == 0x1c);
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;
std::u16string title;
std::u16string placeholder;
};
using VisibilityCallback = void (*)(bool visible, uint64_t generation);
using VisualState = ImeCommon::VisualState;
using HostSnapshot = ImeCommon::HostSnapshot;
using VisibilityCallback = ImeCommon::VisibilityCallback;
int KYTY_SYSV_ABI ImeDialogGetPanelSize(const Param* param, uint32_t* width, uint32_t* height);
int KYTY_SYSV_ABI ImeDialogGetPanelSizeExtended(const Param* param, const ExtendedParam* extended,
-6
View File
@@ -2517,12 +2517,6 @@ static void KYTY_SYSV_ABI AmmGetVirtualAddressRanges(uint64_t* va_start, uint64_
uint64_t* multimap_va_end) {
PRINT_NAME();
void* reserve_addr = reinterpret_cast<void*>(AMM_VA_START);
if (LibKernel::Memory::KernelReserveVirtualRange(&reserve_addr, AMM_VA_SIZE, AMM_MAP_FIXED,
0x10000) == OK) {
(void)LibKernel::Memory::KernelSetVirtualRangeName(reserve_addr, AMM_VA_SIZE, "AMM");
}
if (va_start != nullptr) {
AprShared::WriteGuest(reinterpret_cast<uint64_t>(va_start), AMM_VA_START);
}
+1 -1
View File
@@ -226,7 +226,7 @@ static void PrintAbortPointerArrayCandidate(const char* name, uint64_t addr) {
PrintAbortPointerArrayCandidate("arg5", arg5);
if (rbp != 0) {
Common::Singleton<Loader::RuntimeLinker>::Instance()->StackTrace(rbp);
Common::Singleton<Loader::RuntimeLinker>::Instance()->StackTrace(rbp, rsp);
}
if (rsp != 0) {
+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);
+2 -2
View File
@@ -2172,9 +2172,11 @@ LIB_DEFINE(InitLibKernel_1_Posix) {
LIB_FUNC("ltCfaGr2JGE", Posix::pthread_mutex_destroy);
LIB_FUNC("mkx2fVhNMsg", Posix::pthread_cond_broadcast);
LIB_FUNC("2MOy+rUfuhQ", Posix::pthread_cond_signal);
LIB_FUNC("0TyVk4MSLt0", Posix::pthread_cond_init);
LIB_FUNC("Op8TBGY5KHg", Posix::pthread_cond_wait);
LIB_FUNC("Z4QosVuAsA0", Posix::pthread_once);
LIB_FUNC("1471ajPzxh0", Posix::pthread_rwlock_destroy);
LIB_FUNC("ytQULN-nhL4", Posix::pthread_rwlock_init);
LIB_FUNC("sIlRvQqsN2Y", Posix::pthread_rwlock_wrlock);
LIB_FUNC("SFxTMOfuCkE", LibKernel::PthreadRwlockTryrdlock);
LIB_FUNC("XhWHn6P5R7U", LibKernel::PthreadRwlockTrywrlock);
@@ -3211,7 +3213,6 @@ LIB_DEFINE(InitLibKernel_1_Pthread) {
LIB_FUNC("FXPWHNk8Of0", LibKernel::PthreadAttrGetschedparam);
LIB_FUNC("6ULAa0fq4jA", LibKernel::PthreadRwlockInit);
LIB_FUNC("ytQULN-nhL4", LibKernel::PthreadRwlockInit);
LIB_FUNC("BB+kb08Tl9A", LibKernel::PthreadRwlockDestroy);
LIB_FUNC("Ox9i0c7L5w0", LibKernel::PthreadRwlockRdlock);
LIB_FUNC("iGjsr1WAtI0", LibKernel::PthreadRwlockRdlock);
@@ -3227,7 +3228,6 @@ LIB_DEFINE(InitLibKernel_1_Pthread) {
LIB_FUNC("h-OifiouBd8", LibKernel::PthreadRwlockattrSettype);
LIB_FUNC("2Tb92quprl0", LibKernel::PthreadCondInit);
LIB_FUNC("0TyVk4MSLt0", LibKernel::PthreadCondInit);
LIB_FUNC("g+PZd2hiacg", LibKernel::PthreadCondDestroy);
LIB_FUNC("RXXqi4CtF8w", LibKernel::PthreadCondDestroy);
LIB_FUNC("WKAXJ4XBPQ4", LibKernel::PthreadCondWait);
-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);
+5 -4
View File
@@ -237,15 +237,16 @@ private:
static Network* g_net = nullptr;
KYTY_SUBSYSTEM_INIT(Network) {
void Initialize() {
EXIT_IF(g_net != nullptr);
g_net = new Network;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Network) {}
KYTY_SUBSYSTEM_DESTROY(Network) {}
void Shutdown() {
delete g_net;
g_net = nullptr;
}
int Network::PoolCreate(const char* name, int size) {
Common::LockGuard lock(m_mutex);
+8 -3
View File
@@ -3,11 +3,16 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/subsystems.h"
namespace Libs::Network {
KYTY_SUBSYSTEM_DEFINE(Network);
void Initialize();
void Shutdown();
struct Lifecycle {
static constexpr const char* name = "Network";
static constexpr auto initialize = Libs::Network::Initialize;
static constexpr auto shutdown = Libs::Network::Shutdown;
};
namespace Net {
+26 -52
View File
@@ -672,48 +672,39 @@ static Common::VirtualMemory::Mode GetMode(Elf64_Word flags) {
}
}
struct FrameS {
FrameS* next;
uintptr_t ret_addr;
struct GuestStackFrame {
GuestStackFrame* next;
uintptr_t return_address;
};
static void KYTY_SYSV_ABI StackwalkX86(uint64_t rbp, void** stack, int* depth, uintptr_t stack_addr,
size_t stack_size, uintptr_t code_addr, size_t code_size) {
auto* frame = reinterpret_cast<FrameS*>(rbp);
static bool IsReadableRange(uint64_t addr, uint64_t size);
int d = *depth;
int i = 0;
static int WalkGuestStack(uint64_t rbp, uint64_t rsp, void** stack, int capacity) {
constexpr uintptr_t STACK_SIZE = 1024u * 1024u;
const uintptr_t code_start = SYSTEM_RESERVED + CODE_BASE_OFFSET;
const uintptr_t stack_end = rsp + STACK_SIZE;
if (stack == nullptr || capacity <= 0 || rsp == 0 || rbp < rsp || stack_end < rsp ||
g_desired_base_addr <= code_start) {
return 0;
}
for (; i < d; i++) {
if (!(reinterpret_cast<uintptr_t>(frame) >= stack_addr &&
reinterpret_cast<uintptr_t>(frame) < stack_addr + stack_size)) {
auto* frame = reinterpret_cast<GuestStackFrame*>(rbp);
int depth = 0;
while (depth < capacity) {
const auto frame_addr = reinterpret_cast<uintptr_t>(frame);
if (frame_addr < rsp || frame_addr > stack_end - sizeof(GuestStackFrame) ||
!IsReadableRange(frame_addr, sizeof(GuestStackFrame)) ||
frame->return_address < code_start || frame->return_address >= g_desired_base_addr) {
break;
}
if (!(frame->ret_addr >= code_addr && frame->ret_addr < code_addr + code_size)) {
stack[depth++] = reinterpret_cast<void*>(frame->return_address);
if (reinterpret_cast<uintptr_t>(frame->next) <= frame_addr) {
break;
}
stack[i] = reinterpret_cast<void*>(frame->ret_addr);
frame = frame->next;
}
*depth = i;
}
static void KYTY_SYSV_ABI SysStackWalkX86(uint64_t rbp, uint64_t rsp, void** stack, int* depth) {
if (rsp == 0 || rbp < rsp) {
*depth = 0;
return;
}
StackwalkX86(rbp, stack, depth, rsp, 1024u * 1024u, SYSTEM_RESERVED + CODE_BASE_OFFSET,
g_desired_base_addr - (SYSTEM_RESERVED + CODE_BASE_OFFSET));
}
void KYTY_SYSV_ABI SysStackWalkX86(uint64_t rbp, void** stack, int* depth) {
SysStackWalkX86(rbp, rbp, stack, depth);
return depth;
}
// Probe diagnostic ranges without raising another fault.
@@ -961,22 +952,7 @@ static bool KytyExceptionHandler(const Common::HostException::ExceptionInfo& exc
if (info->type == Common::HostException::ExceptionType::AccessViolation) {
if (info->rbp != 0) {
void* stack[20];
int depth = 20;
SysStackWalkX86(info->rbp, info->rsp, stack, &depth);
LOGF("Stack trace [thread = %d]:\n", Common::Thread::GetThreadIdUnique());
for (int i = 0; i < depth; i++) {
auto vaddr = reinterpret_cast<uint64_t>(stack[i]);
auto* p =
Common::Singleton<Loader::RuntimeLinker>::Instance()->FindProgramByAddr(vaddr);
LOGF("[%d] %016" PRIx64 ", off=%016" PRIx64 ", %s\n", i, vaddr,
(p == nullptr ? 0 : vaddr - p->base_vaddr),
(p == nullptr ? "???"
: Common::FilenameWithoutDirectory(
Common::PathToGenericString(p->file_name))
.c_str()));
}
Common::Singleton<Loader::RuntimeLinker>::Instance()->StackTrace(info->rbp, info->rsp);
}
auto dump_guest_qwords = [](const char* name, uint64_t addr) {
@@ -1896,11 +1872,9 @@ Program* RuntimeLinker::FindProgramByAddr(uint64_t vaddr) {
return nullptr;
}
void RuntimeLinker::StackTrace(uint64_t frame_ptr) {
void RuntimeLinker::StackTrace(uint64_t frame_ptr, uint64_t stack_ptr) {
void* stack[20];
int depth = 20;
SysStackWalkX86(frame_ptr, stack, &depth);
int depth = WalkGuestStack(frame_ptr, stack_ptr, stack, static_cast<int>(std::size(stack)));
LOGF("Stack trace [thread = %d]:\n", Common::Thread::GetThreadIdUnique());
+1 -1
View File
@@ -183,7 +183,7 @@ public:
static uint8_t* TlsGetAddr(Program* program);
static void DeleteTls(Program* program, int thread_id);
void StackTrace(uint64_t frame_ptr);
void StackTrace(uint64_t frame_ptr, uint64_t stack_ptr);
private:
static void LoadProgramToMemory(Program* program);
-9
View File
@@ -2,21 +2,12 @@
#include "common/abi.h"
#include "common/dateTime.h"
#include "common/subsystems.h"
#include "loader/timer.h"
namespace Loader::Timer {
static Common::Timer g_timer;
KYTY_SUBSYSTEM_INIT(Timer) {
Start();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Timer) {}
KYTY_SUBSYSTEM_DESTROY(Timer) {}
void Start() {
g_timer.Start();
}
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more