Compare commits

...
Author SHA1 Message Date
nmzik ba55ba5dc5 Allow storage access to PS5 cubemap faces 2026-09-21 00:49:54 +04:00
nmzik c997cdb837 Skip zero-valued indirect CX 0x24a 2026-09-21 00:49:54 +04:00
Kody Schram fe4f9423e3 Significantly Reduce Linux Release Size (#738)
Added symbol stripping for linux build, drops fs from 120 mb to 20mb
2026-09-20 21:26:24 +04:00
nmzik 81af130147 Avoid game list rescans on settings changes/Remove Space pause shortcut 2026-09-20 21:07:36 +04:00
Saadiandnmzik ada0514084 launcher: add a game list refresh action (#683)
* launcher: add a game list refresh action

Dropping new games into an already-configured game folder was only
picked up after restarting the launcher or re-saving the global
settings dialog. Add a refresh toolbar button, an F5 shortcut, and a
"Refresh game list" context-menu entry, all triggering a rescan of the
game folders.

* launcher: do not rescan the game list while a game is running

Clearing cfgs_list destroys the running ConfigurationItem: the running
state is lost and the replacement items allow editing or deleting a
game whose process is still active. Skip the rescan while any item is
running (CodeRabbit review finding for the refresh action).

* launcher: preserve game state when refreshing the list

Share one refresh action across the toolbar, keyboard shortcut, and context menu. Retain running items during rescans, update their settings for the next launch, and preserve selection, filtering, and sort order.

Commit active inline editors before rebuilding rows to avoid losing comments or accessing destroyed configuration data. Tie editor callbacks to item lifetime and guard the context-menu item reference.

Validation: launcher build and startup check passed; 32 Qt interaction checks passed.

* launcher: remove refresh from the game context menu

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-20 20:44:40 +04:00
Ilyasandnmzik d7da244475 Add automatic sccache/ccache detection to CMake and remove deprecated Ninja setup action (#734)
* remove outdated ninja setup action

* Remove deprecated ninja setup action

The seanmiddleditch/gha-setup-ninja action has been archived by its
maintainer, who explicitly states that Ninja is now pre-installed on
all GitHub-hosted runners. This removes the action from all three
build jobs (Windows, macOS, Linux) and removes ninja from the package
install lists on macOS and Linux.

* Add automatic ccache/sccache detection to CMakeLists.txt

Detects sccache (preferred on Windows/MSVC) or ccache at configure time
and sets CMAKE_C_COMPILER_LAUNCHER / CMAKE_CXX_COMPILER_LAUNCHER
automatically. Works for local builds without manual flags.

Priority: sccache > ccache (sccache handles MSVC PDBs better).

* Delete github-actions-version-research.md

* Respect user-specified CMAKE_C_COMPILER_LAUNCHER / CMAKE_CXX_COMPILER_LAUNCHER

Only auto-detect sccache/ccache if no launcher was explicitly provided
via -DCMAKE_C_COMPILER_LAUNCHER=... or -DCMAKE_CXX_COMPILER_LAUNCHER=...

* Preserve explicit compiler launcher settings per language

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-20 19:49:59 +04:00
Elijaandnmzik 549495575c shader: add S_BITCMP0_B64 and S_BITCMP1_B64 (#688)
* shader: add S_BITCMP0_B64 and S_BITCMP1_B64

* test: move S_BITCMP_B64 coverage to compute suite

* test: use the existing compute CTest entry for bit comparisons

* test: remove the dedicated S_BITCMP_B64 command-line selector

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-20 19:36:12 +04:00
Maayan080andnmzik c3cc8454b9 common: remove unused recursive file enumeration (#681)
common: don't follow reparse points in the Windows recursive file walk

SysFileFindFiles recurses into every entry carrying
FILE_ATTRIBUTE_DIRECTORY, which on Windows includes directory symlinks
and junctions. A junction pointing at one of its own ancestors makes the
walk recurse until the stack runs out.

The Linux implementation already guards against this: it uses lstat
rather than stat specifically so a symlink is never followed into a
cycle. Skip reparse-point entries on Windows for the same reason.

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-20 18:50:57 +04:00
Saadi b8ad1d8c4b common: return real Win32 cache flags from GetCacheAccessType (#673)
GetCacheAccessType returned the sys_file_cache_type_t enum values as
CreateFileW flags: SYS_FILE_CACHE_SEQUENTIAL_SCAN (2) became
FILE_ATTRIBUTE_HIDDEN and SYS_FILE_CACHE_AUTO (0) lost the normal
attributes, and no FILE_FLAG_SEQUENTIAL_SCAN hint was ever applied.
Return FILE_FLAG_SEQUENTIAL_SCAN / FILE_ATTRIBUTE_NORMAL instead,
mirroring the POSIX fadvise handling on Linux.
2026-09-20 18:47:25 +04:00
Elija cfb110e0aa shader: allow partial SDWA destinations for V_NOT_B32 (#684) 2026-09-20 18:21:40 +04:00
nmzik e930657bf6 tesselation support in GUI 2026-09-20 17:16:19 +04:00
nmzik 0ad9149c90 Fix three-vertex rectangle rendering 2026-09-20 17:16:19 +04:00
nmzik 25f2699523 Fix video aspect ratio 2026-09-20 17:16:19 +04:00
nmzik a849f25376 Make tessellation optional 2026-09-20 17:16:19 +04:00
nmzik fa51da4d76 Fix AvPlayer end-of-file handling 2026-09-20 17:16:19 +04:00
nmzik 6b79b7a003 Fix AvPlayer startup state 2026-09-20 17:16:19 +04:00
nmzik 102a10f0fe Fix signed sampler comparisons 2026-09-20 17:16:19 +04:00
nmzik 8d66c4ef6d user service: return logged-out error for unavailable user names 2026-09-20 14:03:08 +04:00
nmzikandAugusto Domingos 8cecaf4e30 Merge PR #716: Fix Unicode paths on Windows
Preserve native Windows paths for filesystem operations, command-line options,
and game icons. Complete Unicode handling in recursive file listings and file
logging, restore unmounting by host directory, and extend regression coverage.

Original implementation by Augusto Domingos (@AghastyGD); retain all five of
his PR commits and include the reviewed follow-up fixes.

Closes #714

Co-authored-by: Augusto Domingos <augustodomingosvasco@gmail.com>
2026-09-20 02:14:22 +04:00
nmzik 82b2711cdf audio: play ATRAC9 sampler blocks 2026-09-20 01:47:43 +04:00
nmzik 35a275e97d avplayer: signal when playback ends 2026-09-20 01:47:43 +04:00
nmzik 303a0203b6 audio: play NGS2 PCM buffers 2026-09-20 01:47:43 +04:00
cfbaee07f0 Persist SaveDataMemory2 save data to disk (fixes Astro Bot progress not saving) (#659)
* Persist save-data memory across restarts

* Fix SetSaveDataMemory2 data loss, split saves per user, add atomic writes everywhere

- SaveDataSetSaveDataMemory2 treated data_num=0 as "no entries" even when
  a valid data pointer was supplied. Astro Bot calls this with data_num=0
  meaning "one entry, at data[0]" (an implicit-count convention), so every
  real save write from it was silently dropped as a no-op. Default
  data_num to 1 when data is non-null, matching that convention.
- SaveDataSetupSaveDataMemory2/Get/SetSaveDataMemory2 rejected any call
  that included a non-null icon pointer. Icon persistence isn't
  implemented, but titles are allowed to pass one anyway; rejecting the
  whole call over it made every title that does so fail outright. Ignore
  icon instead of rejecting.
- SaveDataMount3/SaveDataTransferringMount/SaveDataDelete built their
  save-data path from title+dir_name only, dropping the user_id PS5
  local users are keyed by (unlike the newer SetSaveDataMemory2 layout,
  which already nests by user_id). Nest these under user_id too so saves
  from different local users can no longer collide. No migration for the
  old flat layout - the project is still pre-release/experimental.
- Classic SaveDataMount3-mounted titles (Blasphemous, Dead Cells, ...)
  write their save files directly through the guest's normal file I/O
  once mounted, bypassing SaveData's own write path entirely, so they
  had none of the crash-safety SetSaveDataMemory2 already had. Added the
  same protection at the file-system layer: a guest file opened under a
  /savedataN mount that recreates the file from scratch (O_CREAT and/or
  O_TRUNC) is now written to a .tmp shadow file and atomically replaces
  the real one on close, instead of being truncated in place.
- Both write paths now back up the file they're about to replace into a
  sibling "backups" folder (same filename, no added suffix) before the
  atomic replace, so a manual recovery copy of the previous save always
  survives a write - even one that "succeeds" but wasn't wanted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix save-memory compatibility and preserve existing saves

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 00:51:22 +04:00
Augusto Domingos fe45265fcd preserve native paths when creating directories 2026-09-19 17:53:15 +02:00
Augusto Domingos 8b294eb5b8 Add unicode path regression coverage 2026-09-19 15:48:30 +02:00
Augusto Domingos 07c9978ac6 keep game icon paths native in windows 2026-09-19 14:45:24 +02:00
Augusto Domingos b61b3f2a40 handle UTF-8 command-line paths 2026-09-19 14:12:35 +02:00
Augusto Domingos 001dfea501 Preserve native windows paths in filesystem operations 2026-09-19 13:11:55 +02:00
nmzik f100f785da libs: fill controller and broadcast status outputs 2026-09-19 05:16:16 +04:00
nmzik ba1854bbdc net: start pool IDs at 1 2026-09-19 04:59:28 +04:00
nmzik ea6aae8577 shader: V_MAD_I16 2026-09-19 04:43:48 +04:00
nmzik 5b7d3343cf shader: V_CMP_NLT_F16 2026-09-18 12:25:13 +04:00
nmzik 394e638905 shader: V_MUL_LO_U16 2026-09-18 11:21:57 +04:00
nmzik c2b7b0b58c Revert "BC7-RGBA32_UINT views (Vulkan validation)"
This reverts commit 165234b159.
2026-09-18 11:12:36 +04:00
nmzik 1d2f59dfa4 ajm: reject invalid batch storage 2026-09-18 09:11:55 +04:00
nmzikandBlackCAT 007d7ea2eb audio: infer stereo RAW AAC layout from channel-pair packets
Co-authored-by: BlackCAT <52720489+psnwd@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
nmzikandshadowbeat070 95d3d9d3ad shader: fix packed integer negation
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
bfc8cd0e0b shader: DPP8 moves
Co-authored-by: foufouadi <attar.foued@gmail.com>
Co-authored-by: fxpw <fxpw@outlook.com>
2026-09-18 09:11:55 +04:00
165234b159 BC7-RGBA32_UINT views (Vulkan validation)
Co-authored-by: Techx3 <56268269+Techx3@users.noreply.github.com>
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
Co-authored-by: SueryRD <the.ai.tester2@gmail.com>
Co-authored-by: BlackCAT <52720489+psnwd@users.noreply.github.com>
2026-09-18 09:11:55 +04:00
nmzik 6493674726 shader: dual-source blending (Vulkan) 2026-09-18 09:11:55 +04:00
nmzik cbedf5707f UDP P2P sockets 2026-09-18 09:11:55 +04:00
nmzik 57c5b206ca fix network errno 2026-09-18 09:11:55 +04:00
nmzik 87b4417aca fix initial NP sign-in callback 2026-09-18 09:11:55 +04:00
nmzik 104530eafc fix Windows build (#680)
fix Windows Vulkan barrier macro
2026-09-17 21:32:54 +04:00
nmzik 6cfde37e98 avoid extra allocations 2026-09-17 20:35:36 +04:00
nmzik c5cebc1d19 remove unused helpers and the -64 suffix 2026-09-17 20:35:36 +04:00
nmzik 697b18b366 replace ByteBuffer with std::vector 2026-09-17 20:35:36 +04:00
nmzik 5b2935e36c remove the extra virtual memory layer 2026-09-17 20:35:36 +04:00
nmzik 5dc644edb4 clean up graphics helpers 2026-09-17 20:35:36 +04:00
nmzik 1211958b4e clean up runtime helpers 2026-09-17 20:35:36 +04:00
nmzik 218e7200bb clean up launcher helpers 2026-09-17 20:35:36 +04:00
nmzik 2bf78a94e0 remove string wrappers 2026-09-17 20:35:36 +04:00
nmzikandSaadi cc8e02380f remove magic_enum wrappers
Co-authored-by: Saadi <cosxmos@gmail.com>
2026-09-17 20:35:36 +04:00
nmzik c66d977c1d shader: encode SNORM16 formatted stores for skinning buffers 2026-09-17 17:40:05 +04:00
nmzik 4b77686566 shader: V_CMP_NGT_F16 2026-09-17 16:56:32 +04:00
c0sxm0s a6144f794b launcher: report cheat file save failures (#669)
PatchesDialog::Save silently ignored QSaveFile failures, leaving the
status label showing the old "Loaded N cheat(s)" text so the user could
not tell that their selection was not saved. Report the failure in the
status label and the log instead.
2026-09-17 16:13:35 +04:00
Mininaut bab1c3614c libc: treat negative dynamic printf precision as omitted (#625) 2026-09-17 15:18:04 +04:00
c0sxm0s de1545e19a launcher: wire cancel->reject in C++; document glslang for Windows (#663)
uic from Qt 6.12.0 crashes (0xC0000005) on any non-empty <connections>
block in .ui files. Move the cancel_button -> reject connection from
configuration_edit_dialog.ui into configurationEditDialog.cpp, matching
how the other buttons are already connected there.

Also document the glslang (glslangValidator) requirement for Windows
builds; CMake requires it via find_program(... REQUIRED).
2026-09-17 15:13:20 +04:00
Komaryandnmzik ce629e0391 input: add gyroscope and motion sensor support (#634)
input: add controller motion sensor support

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-17 12:28:12 +04:00
BlackCAT 6c03b4aaa5 vscode: point IntelliSense at clang instead of MSVC (#564)
* vscode: point IntelliSense at clang instead of MSVC

KYTY_SYSV_ABI expands to __attribute__((sysv_abi)) with no MSVC fallback,
which is correct for a project that rejects cl.exe at configure time. Nothing
told the C/C++ extension that, so it fell back to windows-msvc-x64 and
flagged every guest-facing function in the tree with "__attribute__ is not a
type name".

Set the IntelliSense mode, compiler path and C++ standard to match the
compiler the build actually uses, and export a compile database for it to
read.

* vscode: resolve the clang toolchain through the VS developer environment

The IntelliSense settings named a specific Visual Studio install, so the
compiler and Ninja only resolved on machines with VS 18 Community under
Program Files. Let CMake Tools source the developer environment instead,
which locates the toolchain through vswhere, and drop the explicit
compilerPath so cpptools takes the compiler from compile_commands.json.

Also point C_Cpp.default.compileCommands at the build directory this
workspace actually configures, and drop CMAKE_EXPORT_COMPILE_COMMANDS
because CMake Tools already exports the database by default.
2026-09-17 11:38:40 +04:00
nmzik 0627e23e92 coderabbit: make it quieter 2026-09-17 11:26:44 +04:00
4e6f4f99d3 loader: emulate immediate SSE4a instructions on macOS (#635)
* loader: emulate immediate SSE4a instructions on macOS

* tests: simplify macOS SSE4a test integration

Keep the Darwin target, CTest registration, and aggregate dependency in
one conditional block without a separate target list. Preserve the
standalone target when BUILD_TESTING is disabled.

Lowercase the test filename and diagnostic prefix, and drop unrelated
formatting changes from the instruction emulator.

Credit Brandon Strong's related SSE4a work in
https://github.com/KytyPS5/KytyPS5/pull/467.

Co-authored-by: Brandon Strong <brandostrong@gmail.com>

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
Co-authored-by: Brandon Strong <brandostrong@gmail.com>
2026-09-17 11:23:50 +04:00
Elijaandnmzik f30afc184e shader: support S_ASHR_I64 (#658)
* shader: implement S_ASHR_I64 with compute coverage

* tests: use existing compute suite for S_ASHR_I64

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-17 10:48:05 +04:00
228cf3fbf4 Build the launcher on Qt below 6.6 and cap the reference-clock log (#653)
* Fix launcher build on Qt versions below 6.6

QVulkanInstance::NoPortabilityDrivers is only available since Qt 6.6, so
guard its use the same way as the Apple-specific case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Stop logging every reference-clock copy

CopyData of the clock counter runs on every GPU wait, so the handler wrote
one line per wait: a single SpongeBob boot that never reaches a frame logs
1312 of them, and the count grows with run time. Keep the first 64 lines
for diagnostics; the copy itself is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove reference-clock logging comment

---------

Co-authored-by: YuhiAida <21134775+YuhiAida@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
2026-09-17 10:30:47 +04:00
nmzik 47957faa16 Match stencil associations by guest range and extent
PPSA30279
2026-09-17 09:01:36 +04:00
nmzik 235e24d44f Recognize null-destination SWAPPC shader handoffs
Credits:
BlackCAT (@psnwd):
https://github.com/KytyPS5/KytyPS5/pull/613
YuhiAida (@YuhiAida):
https://github.com/KytyPS5/KytyPS5/pull/655
2026-09-17 08:45:21 +04:00
nmzik e0ecffed70 Approximate explicit-LOD texture gathers at mip zero
Credits:
YuhiAida (@YuhiAida):
https://github.com/KytyPS5/KytyPS5/pull/588
https://github.com/KytyPS5/KytyPS5/pull/589
https://github.com/KytyPS5/KytyPS5/pull/655
frans (@francoisatt):
https://github.com/KytyPS5/KytyPS5/pull/590
BlackCAT (@psnwd):
https://github.com/KytyPS5/KytyPS5/pull/613
shadowbeat070 (@shadowbeat070):
https://github.com/KytyPS5/KytyPS5/pull/654
Pouare514 (@Pouare514):
https://github.com/KytyPS5/KytyPS5/pull/29
https://github.com/KytyPS5/KytyPS5/pull/32
2026-09-17 08:39:57 +04:00
nmzik fa913ec1a7 Accept EXPCLEAR acceleration on expanded depth attachments 2026-09-17 08:25:55 +04:00
nmzik 49b2eedd1c Fix command buffer capacity calculation
Credits:
YuhiAida (@YuhiAida):
https://github.com/KytyPS5/KytyPS5/pull/588
https://github.com/KytyPS5/KytyPS5/pull/589
https://github.com/KytyPS5/KytyPS5/pull/652
Crimson (@Crimson3076):
https://github.com/KytyPS5/KytyPS5/pull/598
2026-09-17 08:16:59 +04:00
139 changed files with 5646 additions and 3411 deletions

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
# Keep inherited review profiles, automatic reviews, tools, and pre-merge checks.
inheritance: true
reviews:
# Trim supplemental output and keep the remaining walkthrough collapsed.
high_level_summary: false
review_status: false
review_details: false
collapse_walkthrough: true
changed_files_summary: false
sequence_diagrams: false
estimate_code_review_effort: false
related_issues: false
related_prs: false
in_progress_fortune: false
poem: false
enable_prompt_for_ai_agents: false
# Hide standard finishing-touch controls; this also disables their commands.
finishing_touches:
docstrings:
enabled: false
unit_tests:
enabled: false
simplify:
enabled: false
autofix:
enabled: false
fix_ci:
enabled: false
resolve_merge_conflict:
enabled: false
chat:
art: false
+1 -5
View File
@@ -43,9 +43,6 @@ jobs:
- name: Setup Visual Studio environment
uses: ilammy/msvc-dev-cmd@v1
- name: Setup Ninja
uses: seanmiddleditch/gha-setup-ninja@v5
- name: Locate vcpkg
id: vcpkg
shell: pwsh
@@ -155,7 +152,7 @@ jobs:
- name: Install build dependencies
shell: bash
run: brew install glslang ninja
run: brew install glslang
- name: Install Qt 6.10.3
uses: jurplel/install-qt-action@v4
@@ -318,7 +315,6 @@ jobs:
libxrandr-dev \
libxss-dev \
lld \
ninja-build \
pkg-config \
wayland-protocols
+6 -4
View File
@@ -2,15 +2,17 @@
"cmake.sourceDirectory": "${workspaceFolder}",
"cmake.buildDirectory": "${workspaceFolder}/_Build/vscode-clang",
"cmake.generator": "Ninja",
"cmake.useVsDeveloperEnvironment": "always",
"cmake.environment": {
"CC": "clang-cl",
"CXX": "clang-cl",
"PATH": "${env:ProgramFiles}/Microsoft Visual Studio/18/Community/VC/Tools/Llvm/x64/bin;${env:ProgramFiles}/Microsoft Visual Studio/18/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja;${env:PATH}"
"CXX": "clang-cl"
},
"cmake.configureSettings": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_PREFIX_PATH": "C:/Qt/6.11.0/msvc2022_64"
}
},
"C_Cpp.default.intelliSenseMode": "windows-clang-x64",
"C_Cpp.default.compileCommands": "${workspaceFolder}/_Build/vscode-clang/compile_commands.json",
"C_Cpp.default.cppStandard": "c++20"
}
+3
View File
@@ -86,6 +86,9 @@ set(SPDLOG_BUILD_WARNINGS OFF CACHE BOOL "" FORCE)
set(SPDLOG_INSTALL OFF CACHE BOOL "" FORCE)
set(SPDLOG_FMT_EXTERNAL ON CACHE BOOL "" FORCE)
set(SPDLOG_NO_EXCEPTIONS ON CACHE BOOL "" FORCE)
if(WIN32)
set(SPDLOG_WCHAR_FILENAMES ON CACHE BOOL "" FORCE)
endif()
add_subdirectory(spdlog EXCLUDE_FROM_ALL)
if (NOT TARGET FFmpeg::ffmpeg)
+44
View File
@@ -12,6 +12,22 @@ endif()
project(Kyty)
find_program(SCCACHE_PROGRAM sccache)
find_program(CCACHE_PROGRAM ccache)
foreach(lang IN ITEMS C CXX)
# An explicitly empty launcher disables caching for this language.
if(NOT DEFINED CMAKE_${lang}_COMPILER_LAUNCHER)
if(SCCACHE_PROGRAM)
set(CMAKE_${lang}_COMPILER_LAUNCHER "${SCCACHE_PROGRAM}")
message(STATUS "sccache enabled for ${lang}: ${SCCACHE_PROGRAM}")
elseif(CCACHE_PROGRAM)
set(CMAKE_${lang}_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
message(STATUS "ccache enabled for ${lang}: ${CCACHE_PROGRAM}")
endif()
endif()
endforeach()
set(KYTY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set(KYTY_TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")
set(KYTY_THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty")
@@ -450,6 +466,12 @@ add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
target_link_libraries(audio_out2_port_tests common fmt::fmt)
target_include_directories(audio_out2_port_tests PRIVATE ${inc_headers})
add_executable(save_data_memory_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/SaveDataMemoryTests.cpp"
)
target_link_libraries(save_data_memory_tests common fmt::fmt)
target_include_directories(save_data_memory_tests PRIVATE ${inc_headers})
add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/EventQueueLifetimeTests.cpp"
"${KYTY_SOURCE_DIR}/kernel/eventQueue.cpp"
@@ -534,6 +556,7 @@ if(BUILD_TESTING)
add_test(NAME kernel_file_system COMMAND $<TARGET_FILE:kernel_file_system_tests>)
add_test(NAME sync_on_address COMMAND $<TARGET_FILE:sync_on_address_tests>)
add_test(NAME audio_out2_port COMMAND $<TARGET_FILE:audio_out2_port_tests>)
add_test(NAME save_data_memory COMMAND $<TARGET_FILE:save_data_memory_tests>)
add_test(NAME shader_recompiler_compute COMMAND $<TARGET_FILE:shader_recompiler_compute_tests>)
add_test(NAME shader_recompiler_alignbyte
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --alignbyte-only)
@@ -601,6 +624,21 @@ if(BUILD_TESTING)
)
endif()
# Test Darwin signal-context restoration for SSE4a instructions.
if(APPLE AND (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
(NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")))
add_executable(macos_sse4a_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/macosSse4aTests.cpp"
"${KYTY_SOURCE_DIR}/loader/x64InstructionEmulator.cpp"
)
target_link_libraries(macos_sse4a_tests common fmt::fmt Zydis::Zydis)
target_include_directories(macos_sse4a_tests PRIVATE ${inc_headers})
if(BUILD_TESTING)
add_test(NAME macos_sse4a COMMAND $<TARGET_FILE:macos_sse4a_tests>)
set_tests_properties(macos_sse4a PROPERTIES TIMEOUT 15)
add_dependencies(kyty_tests macos_sse4a_tests)
endif()
endif()
add_executable(kyty_emulator "${KYTY_SOURCE_DIR}/main.cpp" ${kyty_emulator_src})
configure_macos_guest_address_space(kyty_emulator)
@@ -643,6 +681,12 @@ elseif(WIN32 OR LINUX)
set_target_properties(kyty_emulator PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS} -Wl,-Map=${KYTY_EMULATOR_MAP_LINK_PATH}")
endif()
if(LINUX)
target_link_options(kyty_emulator PRIVATE
$<$<CONFIG:Release>:-Wl,--strip-all>
)
endif()
if(APPLE)
# The emulator writes x86-64 trampolines/PLT stubs into memory and executes them, and
# runs guest code under Rosetta. That requires the JIT / unsigned-executable-memory
+1
View File
@@ -137,6 +137,7 @@ the Vulkan/SPIR-V validation rules.
- Visual Studio 2022 or Build Tools 2022 with the **Desktop development with C++** workload and
**C++ Clang tools for Windows** component
- Qt 6 for MSVC 2022 64-bit, including Concurrent, Network, and Widgets
- [glslang](https://github.com/KhronosGroup/glslang/releases) (`glslangValidator`) on `PATH`
The Microsoft C++ compiler (`cl.exe`) is not supported; use `clang-cl`.
-166
View File
@@ -1,166 +0,0 @@
#ifndef KYTY_COMMON_BYTEBUFFER_H_
#define KYTY_COMMON_BYTEBUFFER_H_
#include "common/assert.h"
#include "common/common.h"
#include <algorithm>
#include <cstddef> // IWYU pragma: export
#include <cstring>
#include <utility>
#include <vector>
namespace Common {
using Byte = std::byte;
class ByteBuffer {
public:
using DataType = std::vector<Byte>;
static constexpr uint32_t INVALID_INDEX = static_cast<uint32_t>(-1);
using iterator = DataType::iterator;
using const_iterator = DataType::const_iterator;
ByteBuffer() = default;
ByteBuffer(const ByteBuffer&) = default;
ByteBuffer(ByteBuffer&&) = default;
ByteBuffer& operator=(const ByteBuffer&) = default;
ByteBuffer& operator=(ByteBuffer&&) = default;
explicit ByteBuffer(uint32_t size): m_data(size) {}
ByteBuffer(std::initializer_list<uint8_t> list) {
for (const uint8_t& e: list) {
m_data.emplace_back(static_cast<Byte>(e));
}
}
explicit ByteBuffer(const void* buf, uint32_t size)
: ByteBuffer(size) // @suppress("Ambiguous problem")
{
EXIT_IF(buf == nullptr && size != 0);
if (size != 0) {
std::memcpy(m_data.data(), buf, size);
}
}
[[nodiscard]] uint32_t Size() const { return static_cast<uint32_t>(m_data.size()); }
[[nodiscard]] bool IsEmpty() const { return m_data.empty(); }
void Clear() { m_data.clear(); }
void Add(const Byte& val) { m_data.push_back(val); }
void Add(Byte&& val) { m_data.push_back(std::move(val)); }
void Add(const Byte* val, uint32_t num) {
EXIT_IF(val == nullptr && num != 0);
m_data.insert(m_data.end(), val, val + num);
}
void Add(const ByteBuffer& v) {
if (this == &v) {
const ByteBuffer copy(v);
Add(copy.GetDataConst(), copy.Size());
return;
}
Add(v.GetDataConst(), v.Size());
}
void InsertAt(uint32_t index, const Byte& val) {
EXIT_IF(index > Size());
m_data.insert(m_data.begin() + index, val);
}
void InsertAt(uint32_t index, Byte&& val) {
EXIT_IF(index > Size());
m_data.insert(m_data.begin() + index, std::move(val));
}
[[nodiscard]] uint32_t Find(const Byte& t) const {
const auto it = std::find(m_data.begin(), m_data.end(), t);
return (it == m_data.end() ? INVALID_INDEX
: static_cast<uint32_t>(std::distance(m_data.begin(), it)));
}
[[nodiscard]] bool Contains(const Byte& t) const { return Find(t) != INVALID_INDEX; }
bool Remove(const Byte& t) {
const auto it = std::find(m_data.begin(), m_data.end(), t);
if (it == m_data.end()) {
return false;
}
m_data.erase(it);
return true;
}
bool RemoveAt(uint32_t index, uint32_t count = 1) {
if (index >= Size() || count == 0) {
return false;
}
const uint32_t erase_count = std::min(count, Size() - index);
m_data.erase(m_data.begin() + index, m_data.begin() + index + erase_count);
return true;
}
[[nodiscard]] bool IndexValid(uint32_t index) const { return index < Size(); }
Byte& operator[](uint32_t index) {
EXIT_IF(index >= Size());
return m_data[index];
}
const Byte& operator[](uint32_t index) const {
EXIT_IF(index >= Size());
return m_data[index];
}
[[nodiscard]] const Byte& At(uint32_t index) const {
EXIT_IF(index >= Size());
return m_data[index];
}
Byte* GetData() { return m_data.data(); }
[[nodiscard]] const Byte* GetData() const { return m_data.data(); }
[[nodiscard]] const Byte* GetDataConst() const { return m_data.data(); }
void Sort() {
std::sort(m_data.begin(), m_data.end(), [](Byte a, Byte b) {
return std::to_integer<uint8_t>(a) < std::to_integer<uint8_t>(b);
});
}
template <typename OP>
void Sort(OP&& comp_func) {
std::sort(m_data.begin(), m_data.end(), std::forward<OP>(comp_func));
}
bool operator==(const ByteBuffer& s) const { return m_data == s.m_data; }
bool operator!=(const ByteBuffer& s) const { return !(*this == s); }
iterator begin() { return m_data.begin(); } // NOLINT(readability-identifier-naming)
iterator end() { return m_data.end(); } // NOLINT(readability-identifier-naming)
[[nodiscard]] const_iterator begin() const {
return m_data.begin();
} // NOLINT(readability-identifier-naming)
[[nodiscard]] const_iterator end() const {
return m_data.end();
} // NOLINT(readability-identifier-naming)
[[nodiscard]] const_iterator cbegin() const {
return m_data.cbegin();
} // NOLINT(readability-identifier-naming)
[[nodiscard]] const_iterator cend() const {
return m_data.cend();
} // NOLINT(readability-identifier-naming)
private:
DataType m_data;
};
} // namespace Common
#endif /* KYTY_COMMON_BYTEBUFFER_H_ */
-4
View File
@@ -24,8 +24,4 @@ std::string Debug::GetLinker() {
#endif
}
std::string Debug::GetBitness() {
return "64";
}
} // namespace Common
-1
View File
@@ -10,7 +10,6 @@ namespace Common {
namespace Debug {
std::string GetCompiler();
std::string GetLinker();
std::string GetBitness();
} // namespace Debug
} // namespace Common
+4
View File
@@ -135,6 +135,10 @@ bool ReadbackLinearImagesEnabled() {
return g_config->readback_linear_images;
}
bool TessellationEnabled() {
return g_config->tessellation_enabled;
}
bool PlayGoHackEnabled() {
return g_config->playgo_hack_enabled;
}
+2
View File
@@ -66,6 +66,7 @@ struct ConfigOptions {
bool gpu_assisted_validation_enabled = false;
bool renderdoc_enabled = false;
bool readback_linear_images = false;
bool tessellation_enabled = false;
bool playgo_hack_enabled = false;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
bool red_zone_protection_enabled = false;
@@ -110,6 +111,7 @@ bool GpuAssistedValidationEnabled();
bool RenderDocEnabled();
bool ReadbackLinearImagesEnabled();
bool TessellationEnabled();
bool PlayGoHackEnabled();
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
bool RedZoneProtectionEnabled();
+51 -121
View File
@@ -136,10 +136,6 @@ bool File::OpenInMem(void* buf, uint32_t buf_size) {
return true;
}
bool File::OpenInMem(ByteBuffer& buf) {
return OpenInMem(buf.GetData(), buf.Size());
}
bool File::CreateInMem() {
EXIT_IF(m_p->f != nullptr);
@@ -249,88 +245,78 @@ void File::Printf(const char* format, ...) {
Write(s.data(), static_cast<uint32_t>(s.size()));
}
static std::filesystem::path WithoutTrailingSeparator(
const std::filesystem::path& path) {
if (!path.empty() && !path.has_filename() &&
path != path.root_path()) {
return path.parent_path();
}
return path;
}
bool File::IsDirectoryExisting(const std::filesystem::path& path) {
auto path_str = PathToGenericString(path);
return SysFileIsDirectoryExisting(
Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1) : path_str);
return SysFileIsDirectoryExisting(WithoutTrailingSeparator(path));
}
bool File::IsFileExisting(const std::filesystem::path& name) {
return SysFileIsFileExisting(name);
}
bool File::CreateDirectory(
const std::filesystem::path& path) // @suppress("Member declaration not found")
{
auto path_str = PathToGenericString(path);
return SysFileCreateDirectory(Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1)
: path_str);
bool File::CreateDirectory(const std::filesystem::path& path) {
return SysFileCreateDirectory(
WithoutTrailingSeparator(path));
}
bool File::DeleteDirectory(const std::filesystem::path& path) {
auto path_str = PathToGenericString(path);
return SysFileDeleteDirectory(Common::EndsWith(path_str, "/") ? Common::RemoveLast(path_str, 1)
: path_str);
return SysFileDeleteDirectory(
WithoutTrailingSeparator(path));
}
bool File::CreateDirectories(const std::filesystem::path& path) {
std::string real_path = Common::ReplaceChar(PathToGenericString(path), '\\', '/');
const auto normalized_path =
WithoutTrailingSeparator(path);
std::vector<std::string> list = Common::Split(real_path, "/");
auto current_path = normalized_path.root_path();
std::string p;
for (const auto& component:
normalized_path.relative_path()) {
current_path /= component;
for (uint32_t si = 0; si < list.size(); si++) {
const std::string& s = list[si];
if (IsDirectoryExisting(current_path)) {
continue;
}
if (si != 0 || Common::StartsWith(real_path, "/")) {
p += "/";
}
if (!CreateDirectory(current_path)) {
return false;
}
}
p += s;
if (IsDirectoryExisting(p)) {
continue;
}
if (!CreateDirectory(p)) // @suppress("Invalid arguments")
{
return false;
}
}
return true;
return true;
}
bool File::DeleteDirectories(const std::filesystem::path& path) {
std::string real_path = Common::ReplaceChar(PathToGenericString(path), '\\', '/');
const auto normalized_path =
WithoutTrailingSeparator(path);
std::vector<std::string> list = Common::Split(real_path, "/");
std::vector<std::filesystem::path> directories;
auto current_path = normalized_path.root_path();
std::string p;
std::vector<std::string> list2;
for (const auto& component:
normalized_path.relative_path()) {
current_path /= component;
directories.push_back(current_path);
}
for (uint32_t si = 0; si < list.size(); si++) {
const std::string& s = list[si];
for (auto it = directories.rbegin();
it != directories.rend(); ++it) {
if (!DeleteDirectory(*it)) {
return false;
}
}
if (si != 0 || Common::StartsWith(real_path, "/")) {
p += "/";
}
p += s;
list2.push_back(p);
}
uint32_t num = list2.size();
for (uint32_t si = num - 1; si < num; si--) {
if (!DeleteDirectory(list2[si])) {
return false;
}
}
return true;
return true;
}
bool File::DeleteFile(
@@ -345,7 +331,7 @@ bool File::Flush() {
return SysFileFlush(*m_p->f);
}
ByteBuffer File::ReadWholeBuffer() {
std::vector<std::byte> File::ReadWholeBuffer() {
EXIT_IF(IsInvalid());
EXIT_IF(Tell() != 0);
@@ -353,25 +339,14 @@ ByteBuffer File::ReadWholeBuffer() {
EXIT_IF((s >> 32u) != 0);
ByteBuffer buf(s);
const auto read_size = static_cast<uint32_t>(s);
std::vector<std::byte> buf(read_size);
Read(buf.GetData(), s);
Read(buf.data(), read_size);
return buf;
}
ByteBuffer File::Read(uint32_t size) {
ByteBuffer buf(size);
uint32_t b = 0;
Read(buf.GetData(), size, &b);
buf.RemoveAt(b, size - b);
return buf;
}
void File::Write(const ByteBuffer& buf, uint32_t* bytes_written) {
Write(buf.GetDataConst(), buf.Size(), bytes_written);
}
DateTime File::GetLastAccessTimeUTC(const std::filesystem::path& name) {
SysTimeStruct t {};
SysFileToSystemTimeUtc(SysFileGetLastAccessTimeUtc(name), t);
@@ -470,51 +445,6 @@ bool File::SetLastAccessAndWriteTimeUTC(const std::filesystem::path& name, const
return SysFileSetLastAccessAndWriteTimeUtc(name, af, wf);
}
std::vector<File::FindInfo> File::FindFiles(const std::filesystem::path& path) {
std::vector<sys_file_find_t> files;
SysFileFindFiles(path, files);
auto path_str = PathToGenericString(path);
uint32_t len = static_cast<uint32_t>(path_str.size());
if (!Common::EndsWith(path_str, "/") && !Common::EndsWith(path_str, "\\")) {
len++;
}
std::vector<File::FindInfo> ret;
ret.reserve(files.size());
for (const auto& f: files) {
File::FindInfo r {};
r.path_with_name = f.path_with_name;
r.rel_path_with_name = Common::Mid(PathToGenericString(f.path_with_name), len);
r.size = f.size;
SysTimeStruct at {};
SysTimeStruct wt {};
SysFileToSystemTimeUtc(f.last_access_time, at);
SysFileToSystemTimeUtc(f.last_write_time, wt);
if (!at.is_invalid && !wt.is_invalid) {
r.last_access_time = DateTime(Date(at.Year, at.Month, at.Day),
Time(at.Hour, at.Minute, at.Second, at.Milliseconds));
r.last_write_time = DateTime(Date(wt.Year, wt.Month, wt.Day),
Time(wt.Hour, wt.Minute, wt.Second, wt.Milliseconds));
}
// printf("%s, %s, %" PRIu64", %s, %s\n", r.path_with_name.c_str(),
// r.rel_path_with_name.c_str(), r.size, r.last_access_time.ToString().c_str(),
// r.last_write_time.ToString().c_str());
ret.push_back(std::move(r));
}
return ret;
}
std::vector<File::DirEntry> File::GetDirEntries(const std::filesystem::path& path) {
std::vector<sys_dir_entry_t> files;
+4 -16
View File
@@ -1,10 +1,10 @@
#ifndef KYTY_COMMON_FILE_H_
#define KYTY_COMMON_FILE_H_
#include "common/byteBuffer.h"
#include "common/common.h"
#include "common/dateTime.h"
#include <cstddef>
#include <filesystem>
#include <memory>
#include <string>
@@ -30,14 +30,6 @@ class File {
public:
enum class Mode { Read, Write, ReadWrite, WriteRead };
struct FindInfo {
std::filesystem::path path_with_name;
std::filesystem::path rel_path_with_name;
DateTime last_access_time;
DateTime last_write_time;
uint64_t size;
};
struct DirEntry {
std::string name;
bool is_file;
@@ -51,7 +43,6 @@ public:
bool Create(const std::filesystem::path& name);
bool Open(const std::filesystem::path& name, Mode mode);
bool OpenInMem(void* buf, uint32_t buf_size);
bool OpenInMem(ByteBuffer& buf); // NOLINT(google-runtime-references)
bool CreateInMem();
void Close();
@@ -73,14 +64,12 @@ public:
void GetLastAccessAndWriteTimeUTC(DateTime* access, DateTime* write);
void Read(void* data, uint32_t size, uint32_t* bytes_read = nullptr);
ByteBuffer Read(uint32_t size);
void Write(const void* data, uint32_t size, uint32_t* bytes_written = nullptr);
void Write(const ByteBuffer& buf, uint32_t* bytes_written = nullptr);
void Read(void* data, uint32_t size, uint32_t* bytes_read = nullptr);
void Write(const void* data, uint32_t size, uint32_t* bytes_written = nullptr);
void Printf(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3);
ByteBuffer ReadWholeBuffer();
std::vector<std::byte> ReadWholeBuffer();
static uint64_t Size(const std::filesystem::path& name);
@@ -101,7 +90,6 @@ public:
static bool SetLastAccessAndWriteTimeUTC(const std::filesystem::path& name,
const DateTime& access, const DateTime& write);
static std::vector<FindInfo> FindFiles(const std::filesystem::path& path);
static std::vector<DirEntry> GetDirEntries(const std::filesystem::path& path);
static bool CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
static bool RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
-97
View File
@@ -54,103 +54,6 @@ inline uint32_t hash(const void* key, uint32_t key_len) {
return hash;
}
inline uint32_t hash8(uint8_t key) {
uint32_t hash = 0;
uint8_t* ptr = &key;
hash += ptr[0];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += (hash << 3u);
hash ^= (hash >> 11u);
hash += (hash << 15u);
return hash;
}
inline uint32_t hash16(uint16_t key) {
uint32_t hash = 0;
auto* ptr = reinterpret_cast<uint8_t*>(&key);
hash += ptr[0];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[1];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += (hash << 3u);
hash ^= (hash >> 11u);
hash += (hash << 15u);
return hash;
}
inline uint32_t hash32(uint32_t key) {
uint32_t hash = 0;
auto* ptr = reinterpret_cast<uint8_t*>(&key);
hash += ptr[0];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[1];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[2];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[3];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += (hash << 3u);
hash ^= (hash >> 11u);
hash += (hash << 15u);
return hash;
}
inline uint32_t hash64(uint64_t key) {
uint32_t hash = 0;
auto* ptr = reinterpret_cast<uint8_t*>(&key);
hash += ptr[0];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[1];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[2];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[3];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[4];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[5];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[6];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += ptr[7];
hash += (hash << 10u);
hash ^= (hash >> 6u);
hash += (hash << 3u);
hash ^= (hash >> 11u);
hash += (hash << 15u);
return hash;
}
} // namespace Common
#endif /* KYTY_COMMON_HASH_H_ */
+1 -2
View File
@@ -3,7 +3,6 @@
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include "common/stringUtils.h"
#include <cstdio>
#include <filesystem>
@@ -47,7 +46,7 @@ std::shared_ptr<spdlog::logger> MakeFileLogger(std::string name
}
auto sink =
std::make_shared<spdlog::sinks::basic_file_sink_mt>(Common::PathToString(path), true);
std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.native(), true);
return MakeLogger(std::move(name), std::move(sink));
}
-42
View File
@@ -1,42 +0,0 @@
#ifndef KYTY_COMMON_MAGICENUM_H_
#define KYTY_COMMON_MAGICENUM_H_
#include "common/common.h"
#include "common/stringUtils.h"
#include "magic_enum.hpp" // IWYU pragma: export
namespace Common {
template <typename E>
inline std::string EnumName(E v) {
auto str = magic_enum::enum_name(v);
return std::string(str.data(), str.length());
}
template <typename E>
inline std::string EnumName8(E v) {
auto str = magic_enum::enum_name(v);
return std::string(str.data(), str.length());
}
template <typename E>
inline E EnumValue(const std::string& str, E default_value) {
auto v = magic_enum::enum_cast<E>(str.c_str());
if (v.has_value()) {
return v.value();
}
return default_value;
}
#define KYTY_ENUM_RANGE(e, mx, mn) \
namespace magic_enum::customize { \
template <> \
struct enum_range<e> { \
static constexpr int min = (mx); \
static constexpr int max = (mn); \
}; \
}
} // namespace Common
#endif /* KYTY_COMMON_MAGICENUM_H_ */
-11
View File
@@ -18,14 +18,6 @@ enum sys_file_cache_type_t {
SYS_FILE_CACHE_SEQUENTIAL_SCAN = 2 // NOLINT(readability-identifier-naming)
};
// NOLINTNEXTLINE(readability-identifier-naming)
struct sys_file_find_t {
std::filesystem::path path_with_name;
SysFileTimeStruct last_access_time;
SysFileTimeStruct last_write_time;
uint64_t size;
};
// NOLINTNEXTLINE(readability-identifier-naming)
struct sys_dir_entry_t {
std::string name;
@@ -52,7 +44,6 @@ uint64_t SysFileTell(sys_file_t& f); // NOLINT(google-runt
bool SysFileTruncate(sys_file_t& f, uint64_t size); // NOLINT(google-runtime-references)
bool SysFileUnlink(sys_file_t& f,
const std::filesystem::path& name); // NOLINT(google-runtime-references)
void SysFileWrite(uint32_t n, sys_file_t& f); // NOLINT(google-runtime-references)
uint64_t SysFileSize(const std::filesystem::path& file_name);
bool SysFileIsError(sys_file_t& f); // NOLINT(google-runtime-references)
bool SysFileIsDirectoryExisting(const std::filesystem::path& path);
@@ -76,8 +67,6 @@ bool SysFileSetLastWriteTimeUtc(const std::filesystem::path& name, SysFileTimeSt
bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
SysFileTimeStruct& access, SysFileTimeStruct& write);
// NOLINTNEXTLINE(google-runtime-references)
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out);
// NOLINTNEXTLINE(google-runtime-references)
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out);
bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
+1 -49
View File
@@ -11,6 +11,7 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <dirent.h>
#include <fcntl.h>
#include <filesystem>
@@ -151,10 +152,6 @@ void SysFileWrite(const void* data, uint32_t size, sys_file_t& f, uint32_t* byte
}
}
void SysFileWrite(uint32_t n, sys_file_t& f) {
SysFileWrite(&n, 4, f);
}
sys_file_t* SysFileCreate(const std::filesystem::path& file_name) {
auto* ret = new sys_file_t;
@@ -613,51 +610,6 @@ bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
// }
}
// Recursively collect regular files.
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out) {
auto real_path = get_internal_name(path);
DIR* dir = opendir(real_path.string().c_str());
if (dir == nullptr) {
return;
}
for (const dirent* entry = readdir(dir); entry != nullptr; entry = readdir(dir)) {
const std::string file_name(entry->d_name);
if (file_name == "." || file_name == "..") {
continue;
}
auto child = real_path / file_name;
struct stat s {};
// lstat, so a symlink is never followed into a cycle during the recursive walk.
if (0 != lstat(child.string().c_str(), &s)) {
continue;
}
if (S_ISDIR(s.st_mode)) {
SysFileFindFiles(child, out);
} else if (S_ISREG(s.st_mode)) {
sys_file_find_t r {};
r.path_with_name = child;
r.size = static_cast<uint64_t>(s.st_size);
r.last_access_time.is_invalid = false;
r.last_access_time.time = s.st_atime;
r.last_access_time.nanos = KYTY_STAT_ATIME_NS(s);
r.last_write_time.is_invalid = false;
r.last_write_time.time = s.st_mtime;
r.last_write_time.nanos = KYTY_STAT_MTIME_NS(s);
out.push_back(r);
}
}
closedir(dir);
}
// Keep "." and ".." to match FindFirstFileW.
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out) {
auto real_path = get_internal_name(path);
+29 -31
View File
@@ -5,7 +5,6 @@
#else
#include "common/assert.h"
#include "common/platform/sysVirtual.h"
#include "common/virtualMemory.h"
#include <atomic>
@@ -28,12 +27,12 @@
#define KYTY_FIXED_NOREPLACE
#endif
namespace Common {
namespace Common::VirtualMemory {
static pthread_mutex_t g_virtual_mutex {};
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
void SysVirtualInit() {
void Init() {
pthread_mutexattr_t attr {};
pthread_mutexattr_init(&attr);
@@ -48,19 +47,19 @@ void SysVirtualInit() {
g_allocs = new std::map<uintptr_t, size_t>;
}
static int get_protection_flag(VirtualMemory::Mode mode) {
static int get_protection_flag(Mode mode) {
int protect = PROT_NONE;
switch (mode) {
case VirtualMemory::Mode::Read: protect = PROT_READ; break;
case VirtualMemory::Mode::Write:
case VirtualMemory::Mode::ReadWrite: protect = PROT_READ | PROT_WRITE; break; // NOLINT
case VirtualMemory::Mode::Execute: protect = PROT_EXEC; break;
case VirtualMemory::Mode::ExecuteRead: protect = PROT_EXEC | PROT_READ; break; // NOLINT
case VirtualMemory::Mode::ExecuteWrite:
case VirtualMemory::Mode::ExecuteReadWrite:
case Mode::Read: protect = PROT_READ; break;
case Mode::Write:
case Mode::ReadWrite: protect = PROT_READ | PROT_WRITE; break; // NOLINT
case Mode::Execute: protect = PROT_EXEC; break;
case Mode::ExecuteRead: protect = PROT_EXEC | PROT_READ; break; // NOLINT
case Mode::ExecuteWrite:
case Mode::ExecuteReadWrite:
protect = PROT_EXEC | PROT_WRITE | PROT_READ;
break; // NOLINT
case VirtualMemory::Mode::NoAccess:
case Mode::NoAccess:
default: protect = PROT_NONE; break;
}
return protect;
@@ -130,7 +129,7 @@ static void* map_anonymous(uintptr_t addr, size_t size, int protect, int flags)
return mmap(nullptr, size, protect, flags, -1, 0); // NOLINT
}
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
@@ -154,8 +153,7 @@ static uintptr_t align_up(uintptr_t addr, uint64_t alignment) {
return (addr + alignment - 1) & ~(alignment - 1);
}
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
uint64_t alignment) {
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
if (alignment == 0) {
return 0;
}
@@ -224,7 +222,7 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
}
if (ptr == MAP_FAILED) {
return SysVirtualAllocAligned(address, size, mode, alignment << 1u);
return AllocAligned(address, size, mode, alignment << 1u);
}
pthread_mutex_lock(&g_virtual_mutex);
@@ -281,7 +279,7 @@ static bool is_mapped(void* ptr, size_t length) {
}
#endif
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
@@ -318,15 +316,15 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
return false;
}
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
return SysVirtualProtect(address, size, mode);
bool Commit(uint64_t address, uint64_t size, Mode mode) {
return Protect(address, size, mode);
}
uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
return SysVirtualReserveAligned(address, size, 1);
uint64_t Reserve(uint64_t address, uint64_t size) {
return ReserveAligned(address, size, 1);
}
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
if (alignment == 0) {
return 0;
}
@@ -395,7 +393,7 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
}
if (ptr == MAP_FAILED) {
return SysVirtualReserveAligned(address, size, alignment << 1u);
return ReserveAligned(address, size, alignment << 1u);
}
pthread_mutex_lock(&g_virtual_mutex);
@@ -405,7 +403,7 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
return ret_addr;
}
bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
bool ReserveFixed(uint64_t address, uint64_t size) {
EXIT_IF(g_allocs == nullptr);
auto addr = static_cast<uintptr_t>(address);
@@ -441,9 +439,9 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
return false;
}
bool SysVirtualDecommit(uint64_t address, uint64_t size) {
bool Decommit(uint64_t address, uint64_t size) {
// Drop physical pages while preserving the reservation.
if (!SysVirtualProtect(address, size, VirtualMemory::Mode::NoAccess)) {
if (!Protect(address, size, Mode::NoAccess)) {
return false;
}
@@ -467,7 +465,7 @@ bool SysVirtualDecommit(uint64_t address, uint64_t size) {
return true;
}
bool SysVirtualFree(uint64_t address) {
bool Free(uint64_t address) {
EXIT_IF(g_allocs == nullptr);
size_t size = 0;
@@ -487,7 +485,7 @@ bool SysVirtualFree(uint64_t address) {
return munmap(reinterpret_cast<void*>(addr), size) == 0;
}
bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
bool FreeRange(uint64_t address, uint64_t size) {
EXIT_IF(g_allocs == nullptr);
if (size == 0 || (address & 0xfffu) != 0 || (size & 0xfffu) != 0) {
return false;
@@ -543,7 +541,7 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
return true;
}
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
bool Protect(uint64_t address, uint64_t size, Mode mode) {
const auto addr = static_cast<uintptr_t>(address);
const auto page_start = addr >> 12u;
const auto page_end = (addr + size - 1) >> 12u;
@@ -551,10 +549,10 @@ bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode
get_protection_flag(mode)) == 0;
}
bool SysVirtualFlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
bool FlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
return true;
}
} // namespace Common
} // namespace Common::VirtualMemory
#endif
-53
View File
@@ -1,53 +0,0 @@
#ifndef KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_
#define KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_
#include "common/common.h"
#include <type_traits>
inline uint16_t SwapByteOrder16(uint16_t value) {
uint16_t hi = value << 8u;
uint16_t lo = value >> 8u;
return hi | lo;
}
inline uint32_t SwapByteOrder32(uint32_t value) {
return __builtin_bswap32(value);
}
inline uint64_t SwapByteOrder64(uint64_t value) {
return __builtin_bswap64(value);
}
template <typename T>
inline void SwapByteOrder(T& x) {
if (sizeof(x) == 2) {
if (std::is_signed_v<T>) {
x = static_cast<std::make_signed_t<T>>(
SwapByteOrder16(static_cast<std::make_unsigned_t<uint16_t>>(x)));
} else {
x = SwapByteOrder16(x);
}
}
if (sizeof(x) == 4) {
if (std::is_signed_v<T>) {
x = static_cast<std::make_signed_t<T>>(
SwapByteOrder32(static_cast<std::make_unsigned_t<uint32_t>>(x)));
} else {
x = SwapByteOrder32(x);
}
}
if (sizeof(x) == 8) {
if (std::is_signed_v<T>) {
x = static_cast<std::make_signed_t<T>>(
SwapByteOrder64(static_cast<std::make_unsigned_t<uint64_t>>(x)));
} else {
x = SwapByteOrder64(x);
}
}
}
template <typename T>
inline void NoSwapByteOrder(T& x) {}
#endif /* KYTY_COMMON_PLATFORM_SYSSWAPBYTEORDER_H_ */
+1 -5
View File
@@ -151,10 +151,6 @@ inline void SysTimeTToSystem(time_t t, SysTimeStruct& s) {
SysFileToSystemTimeUtc(ft, s);
}
inline time_t SysTimegm(struct tm* tm) {
return timegm(tm);
}
inline void SysSystemToFileTimeUtc(const SysTimeStruct& f, SysFileTimeStruct& t) {
struct tm i {};
@@ -165,7 +161,7 @@ inline void SysSystemToFileTimeUtc(const SysTimeStruct& f, SysFileTimeStruct& t)
i.tm_min = f.Minute;
i.tm_sec = f.Second;
t.is_invalid = (f.is_invalid || (t.time = SysTimegm(&i)) == static_cast<time_t>(-1));
t.is_invalid = (f.is_invalid || (t.time = ::timegm(&i)) == static_cast<time_t>(-1));
}
// Retrieves the current local date and time.
-27
View File
@@ -1,27 +0,0 @@
#ifndef KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_
#define KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_
#include "common/common.h"
#include "common/virtualMemory.h"
namespace Common {
void SysVirtualInit();
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
uint64_t alignment);
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
uint64_t SysVirtualReserve(uint64_t address, uint64_t size);
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment);
bool SysVirtualReserveFixed(uint64_t address, uint64_t size);
bool SysVirtualDecommit(uint64_t address, uint64_t size);
bool SysVirtualFree(uint64_t address);
bool SysVirtualFreeRange(uint64_t address, uint64_t size);
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size);
} // namespace Common
#endif /* KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_ */
+7 -65
View File
@@ -14,6 +14,7 @@
#include "common/stringUtils.h"
#include <cstdlib>
#include <cstring>
#include <vector>
// NOLINTNEXTLINE(readability-identifier-naming)
@@ -56,10 +57,10 @@ static DWORD GetCacheAccessType(sys_file_cache_type_t t) {
}
if (t == SYS_FILE_CACHE_SEQUENTIAL_SCAN) {
return SYS_FILE_CACHE_SEQUENTIAL_SCAN;
return FILE_FLAG_SEQUENTIAL_SCAN;
}
return SYS_FILE_CACHE_AUTO;
return FILE_ATTRIBUTE_NORMAL;
}
void SysFileRead(void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_read) {
@@ -135,10 +136,6 @@ void SysFileWrite(const void* data, uint32_t size, sys_file_t& f, uint32_t* byte
}
}
void SysFileWrite(uint32_t n, sys_file_t& f) {
SysFileWrite(&n, 4, f);
}
sys_file_t* SysFileCreate(const std::filesystem::path& file_name) {
auto* ret = new sys_file_t;
@@ -479,66 +476,11 @@ bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
return ok;
}
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out) {
std::string real_path = Common::ReplaceChar(Common::PathToGenericString(path), '\\', '/');
if (!Common::EndsWith(real_path, "/")) {
real_path += "/";
}
std::string pattern = real_path + "*";
HANDLE h = nullptr;
WIN32_FIND_DATAW data;
auto wide_pattern = std::filesystem::path(pattern).wstring();
h = FindFirstFileW(wide_pattern.c_str(), &data);
if (h == INVALID_HANDLE_VALUE) {
return;
}
do {
std::filesystem::path file_name(data.cFileName);
auto file_name_str = Common::PathToString(file_name);
if (file_name_str == "." || file_name_str == "..") {
continue;
}
if ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) != 0u) {
SysFileFindFiles(std::filesystem::path(real_path) / file_name, out);
} else {
sys_file_find_t r {};
r.path_with_name = std::filesystem::path(real_path) / file_name;
r.size = (static_cast<uint64_t>(data.nFileSizeHigh) << 32u) +
static_cast<uint64_t>(data.nFileSizeLow);
r.last_access_time.is_invalid = false;
r.last_access_time.time = data.ftLastAccessTime;
r.last_write_time.is_invalid = false;
r.last_write_time.time = data.ftLastWriteTime;
out.push_back(r);
}
} while (FindNextFileW(h, &data) != 0);
FindClose(h);
}
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out) {
std::string real_path = Common::ReplaceChar(Common::PathToGenericString(path), '\\', '/');
if (!Common::EndsWith(real_path, "/")) {
real_path += "/";
}
const auto pattern = path / L"*";
std::string pattern = real_path + "*";
HANDLE h = nullptr;
WIN32_FIND_DATAW data;
auto wide_pattern = std::filesystem::path(pattern).wstring();
h = FindFirstFileW(wide_pattern.c_str(), &data);
WIN32_FIND_DATAW data {};
HANDLE h = FindFirstFileW(pattern.c_str(), &data);
if (h == INVALID_HANDLE_VALUE) {
return;
@@ -552,7 +494,7 @@ void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entr
r.is_file = ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) == 0u);
r.name = Common::PathToString(file_name);
out.push_back(r);
out.push_back(std::move(r));
} while (FindNextFileW(h, &data) != 0);
+31 -33
View File
@@ -10,7 +10,6 @@
#include <windows.h> // IWYU pragma: keep
#include "common/assert.h"
#include "common/platform/sysVirtual.h"
#include "common/virtualMemory.h"
// IWYU pragma: no_include <basetsd.h>
@@ -22,33 +21,33 @@
// IWYU pragma: no_include <winerror.h>
// IWYU pragma: no_include <wtypes.h>
namespace Common {
namespace Common::VirtualMemory {
static DWORD GetProtectionFlag(VirtualMemory::Mode mode) {
static DWORD GetProtectionFlag(Mode mode) {
DWORD protect = PAGE_NOACCESS;
switch (mode) {
case VirtualMemory::Mode::Read: protect = PAGE_READONLY; break;
case Mode::Read: protect = PAGE_READONLY; break;
case VirtualMemory::Mode::Write:
case VirtualMemory::Mode::ReadWrite: protect = PAGE_READWRITE; break;
case Mode::Write:
case Mode::ReadWrite: protect = PAGE_READWRITE; break;
case VirtualMemory::Mode::Execute: protect = PAGE_EXECUTE; break;
case Mode::Execute: protect = PAGE_EXECUTE; break;
case VirtualMemory::Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
case Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
case VirtualMemory::Mode::ExecuteWrite:
case VirtualMemory::Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
case Mode::ExecuteWrite:
case Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
case VirtualMemory::Mode::NoAccess:
case Mode::NoAccess:
default: protect = PAGE_NOACCESS; break;
}
return protect;
}
void SysVirtualInit() {}
void Init() {}
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
auto ptr = (address == 0 ? SysVirtualAllocAligned(address, size, mode, 1)
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
auto ptr = (address == 0 ? AllocAligned(address, size, mode, 1)
: reinterpret_cast<uintptr_t>(VirtualAlloc(
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
@@ -59,7 +58,7 @@ uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mo
if (err != ERROR_INVALID_ADDRESS) {
printf("VirtualAlloc() failed: 0x%08" PRIx32 "\n", err);
} else {
return SysVirtualAllocAligned(address, size, mode, 1);
return AllocAligned(address, size, mode, 1);
}
}
return ptr;
@@ -80,8 +79,7 @@ static uint64_t AlignUp(uint64_t addr, uint64_t alignment) {
return (addr + alignment - 1) & ~(alignment - 1);
}
uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
uint64_t alignment) {
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
if (alignment == 0) {
printf("VirtualAlloc2 failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return 0;
@@ -135,13 +133,13 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
printf("VirtualAlloc2(alignment = 0x%016" PRIx64 ") failed: 0x%08" PRIx32 "\n",
alignment, err);
} else {
return SysVirtualAllocAligned(address, size, mode, alignment << 1u);
return AllocAligned(address, size, mode, alignment << 1u);
}
}
return ptr;
}
bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE), GetProtectionFlag(mode)));
@@ -161,7 +159,7 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
return true;
}
bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
bool Commit(uint64_t address, uint64_t size, Mode mode) {
auto ptr = reinterpret_cast<uintptr_t>(
VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT), GetProtectionFlag(mode)));
@@ -179,8 +177,8 @@ bool SysVirtualCommit(uint64_t address, uint64_t size, VirtualMemory::Mode mode)
return true;
}
uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
auto ptr = (address == 0 ? SysVirtualReserveAligned(address, size, 1)
uint64_t Reserve(uint64_t address, uint64_t size) {
auto ptr = (address == 0 ? ReserveAligned(address, size, 1)
: reinterpret_cast<uintptr_t>(VirtualAlloc(
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_RESERVE), PAGE_NOACCESS)));
@@ -190,13 +188,13 @@ uint64_t SysVirtualReserve(uint64_t address, uint64_t size) {
if (err != ERROR_INVALID_ADDRESS) {
printf("VirtualAlloc(MEM_RESERVE) failed: 0x%08" PRIx32 "\n", err);
} else {
return SysVirtualReserveAligned(address, size, 1);
return ReserveAligned(address, size, 1);
}
}
return ptr;
}
uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
if (alignment == 0) {
printf("VirtualAlloc2(MEM_RESERVE) failed: 0x%08" PRIx32 "\n",
static_cast<uint32_t>(GetLastError()));
@@ -250,13 +248,13 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
"\n",
alignment, err);
} else {
return SysVirtualReserveAligned(address, size, alignment << 1u);
return ReserveAligned(address, size, alignment << 1u);
}
}
return ptr;
}
bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
bool ReserveFixed(uint64_t address, uint64_t size) {
auto ptr = reinterpret_cast<uintptr_t>(
VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_RESERVE), PAGE_NOACCESS));
@@ -276,7 +274,7 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
return true;
}
bool SysVirtualDecommit(uint64_t address, uint64_t size) {
bool Decommit(uint64_t address, uint64_t size) {
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
MEM_DECOMMIT) == 0) {
printf("VirtualFree(MEM_DECOMMIT) failed: 0x%08" PRIx32 "\n",
@@ -286,7 +284,7 @@ bool SysVirtualDecommit(uint64_t address, uint64_t size) {
return true;
}
bool SysVirtualFree(uint64_t address) {
bool Free(uint64_t address) {
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) ==
0) {
printf("VirtualFree() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
@@ -295,7 +293,7 @@ bool SysVirtualFree(uint64_t address) {
return true;
}
bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
bool FreeRange(uint64_t address, uint64_t size) {
if (address == 0 || size == 0) {
return false;
}
@@ -313,10 +311,10 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
}
current = next;
}
return current - address == size && SysVirtualFree(address);
return current - address == size && Free(address);
}
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
bool Protect(uint64_t address, uint64_t size, Mode mode) {
DWORD old_protect = 0;
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
GetProtectionFlag(mode), &old_protect) == 0) {
@@ -326,7 +324,7 @@ bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode
return true;
}
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size) {
bool FlushInstructionCache(uint64_t address, uint64_t size) {
if (::FlushInstructionCache(GetCurrentProcess(),
reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)),
size) == 0) {
@@ -337,6 +335,6 @@ bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size) {
return true;
}
} // namespace Common
} // namespace Common::VirtualMemory
#endif
+8 -178
View File
@@ -1,7 +1,6 @@
#ifndef KYTY_COMMON_STRING_UTILS_H_
#define KYTY_COMMON_STRING_UTILS_H_
#include "common/byteBuffer.h"
#include "common/common.h"
#include <algorithm>
@@ -9,7 +8,6 @@
#include <charconv>
#include <codecvt>
#include <filesystem>
#include <fmt/format.h>
#include <locale>
#include <string>
#include <string_view>
@@ -18,8 +16,6 @@
namespace Common {
constexpr uint32_t FIND_INVALID_INDEX = static_cast<uint32_t>(-1);
inline std::string PathToString(const std::filesystem::path& path) {
#if defined(__cpp_char8_t)
auto u8 = path.u8string();
@@ -38,8 +34,12 @@ inline std::string PathToGenericString(const std::filesystem::path& path) {
#endif
}
inline bool IsSpace(char c) {
return std::isspace(static_cast<unsigned char>(c)) != 0;
inline std::filesystem::path PathFromUtf8(std::string_view text) {
#if defined(__cpp_char8_t)
return std::filesystem::path(std::u8string(text.begin(), text.end()));
#else
return std::filesystem::u8path(text.begin(), text.end());
#endif
}
inline bool EqualNoCase(std::string_view lhs, std::string_view rhs) {
@@ -63,65 +63,6 @@ inline std::string ToLower(std::string_view text) {
return ret;
}
inline bool ContainsStr(std::string_view text, std::string_view needle) {
return text.find(needle) != std::string_view::npos;
}
inline bool StartsWith(std::string_view text, std::string_view prefix) {
return text.starts_with(prefix);
}
inline bool StartsWith(std::string_view text, char prefix) {
return !text.empty() && text.front() == prefix;
}
inline bool EndsWith(std::string_view text, std::string_view suffix) {
return text.ends_with(suffix);
}
inline bool EndsWith(std::string_view text, char suffix) {
return !text.empty() && text.back() == suffix;
}
inline bool ContainsChar(std::string_view text, char needle) {
return text.find(needle) != std::string_view::npos;
}
inline bool IndexValid(std::string_view text, uint32_t index) {
return index < text.size();
}
inline uint32_t FindIndex(std::string_view text, std::string_view needle, size_t from = 0) {
const auto pos = text.find(needle, from);
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
}
inline uint32_t FindIndex(std::string_view text, char needle, size_t from = 0) {
const auto pos = text.find(needle, from);
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
}
inline uint32_t FindLastIndex(std::string_view text, std::string_view needle,
size_t from = FIND_INVALID_INDEX) {
if (needle.empty()) {
return 0;
}
if (from == FIND_INVALID_INDEX || from > text.size()) {
from = text.size();
}
const auto pos = text.rfind(needle, from);
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
}
inline uint32_t FindLastIndex(std::string_view text, char needle,
size_t from = FIND_INVALID_INDEX) {
if (from == FIND_INVALID_INDEX || from > text.size()) {
from = text.size();
}
const auto pos = text.rfind(needle, from);
return pos == std::string_view::npos ? FIND_INVALID_INDEX : static_cast<uint32_t>(pos);
}
inline std::string Mid(std::string_view text, size_t first, size_t count = std::string_view::npos) {
if (first >= text.size()) {
return {};
@@ -129,49 +70,18 @@ inline std::string Mid(std::string_view text, size_t first, size_t count = std::
return std::string(text.substr(first, count));
}
inline std::string Left(std::string_view text, size_t count) {
return Mid(text, 0, count);
}
inline std::string Right(std::string_view text, size_t count) {
if (count >= text.size()) {
return std::string(text);
}
return Mid(text, text.size() - count, count);
}
inline std::string RemoveLast(std::string_view text, size_t count) {
if (count >= text.size()) {
return {};
}
return Left(text, text.size() - count);
return std::string(text.substr(0, text.size() - count));
}
inline std::string RemoveFirst(std::string_view text, size_t count) {
if (count >= text.size()) {
return {};
}
return Right(text, text.size() - count);
}
inline std::string TrimRight(std::string_view text) {
size_t end = text.size();
while (end > 0 && IsSpace(text[end - 1])) {
end--;
}
return std::string(text.substr(0, end));
}
inline std::string TrimLeft(std::string_view text) {
size_t first = 0;
while (first < text.size() && IsSpace(text[first])) {
first++;
}
return std::string(text.substr(first));
}
inline std::string Trim(std::string_view text) {
return TrimRight(TrimLeft(text));
return std::string(text.substr(count));
}
inline std::string ReplaceChar(std::string_view text, char old_char, char new_char) {
@@ -204,16 +114,6 @@ inline std::string FilenameWithoutDirectory(std::string_view text) {
return pos == std::string_view::npos ? std::string(text) : std::string(text.substr(pos + 1));
}
inline std::string FilenameWithoutExtension(std::string_view text) {
const auto pos = text.find_last_of('.');
return pos == std::string_view::npos ? std::string(text) : std::string(text.substr(0, pos));
}
inline std::string ExtensionWithoutFilename(std::string_view text) {
const auto pos = text.find_last_of('.');
return pos == std::string_view::npos ? std::string() : std::string(text.substr(pos));
}
inline std::string FixFilenameSlash(std::string_view text) {
return ReplaceChar(text, '\\', '/');
}
@@ -257,65 +157,12 @@ inline std::vector<std::string> Split(std::string_view text, char sep, bool keep
return Split(text, std::string_view(&sep, 1), keep_empty);
}
inline std::string Concat(const std::vector<std::string>& list, std::string_view sep) {
std::string ret;
for (size_t i = 0; i < list.size(); i++) {
if (i != 0) {
ret += sep;
}
ret += list[i];
}
return ret;
}
inline std::string Concat(const std::vector<std::string>& list, char sep) {
return Concat(list, std::string_view(&sep, 1));
}
inline uint32_t ToUint32(std::string_view text, int base = 10) {
uint32_t value = 0;
std::from_chars(text.data(), text.data() + text.size(), value, base);
return value;
}
inline uint64_t ToUint64(std::string_view text, int base = 10) {
uint64_t value = 0;
std::from_chars(text.data(), text.data() + text.size(), value, base);
return value;
}
inline int32_t ToInt32(std::string_view text, int base = 10) {
int32_t value = 0;
std::from_chars(text.data(), text.data() + text.size(), value, base);
return value;
}
inline int64_t ToInt64(std::string_view text, int base = 10) {
int64_t value = 0;
std::from_chars(text.data(), text.data() + text.size(), value, base);
return value;
}
inline double ToDouble(std::string_view text) {
return std::stod(std::string(text));
}
inline float ToFloat(std::string_view text) {
return std::stof(std::string(text));
}
inline std::string SafeCsv(std::string_view text) {
const bool add_space =
!text.empty() && (text.front() == '+' || text.front() == '=' || text.front() == '-');
if (text.find('"') != std::string_view::npos || text.find(';') != std::string_view::npos ||
text.find('+') != std::string_view::npos || text.find('=') != std::string_view::npos ||
text.find('-') != std::string_view::npos) {
return "\"" + std::string(add_space ? " " : "") +
ReplaceStr(std::string(text), "\"", "\"\"") + "\"";
}
return std::string(text);
}
inline std::string Utf16ToUtf8(std::u16string_view utf16) {
if (utf16.empty()) {
return {};
@@ -324,23 +171,6 @@ inline std::string Utf16ToUtf8(std::u16string_view utf16) {
return convert.to_bytes(utf16.data(), utf16.data() + utf16.size());
}
inline ByteBuffer HexToBin(std::string_view text) {
ByteBuffer ret;
for (size_t i = 0; i + 1 < text.size(); i += 2) {
ret.Add(static_cast<Byte>((ToUint32(text.substr(i, 2), 16) & 0xffu)));
}
return ret;
}
inline std::string HexFromBin(const ByteBuffer& bin) {
std::string ret;
ret.reserve(bin.Size() * 2);
for (uint32_t i = 0; i < bin.Size(); i++) {
ret += fmt::format("{:02X}", std::to_integer<uint8_t>(bin.At(i)));
}
return ret;
}
} // namespace Common
#endif /* KYTY_COMMON_STRING_UTILS_H_ */
-4
View File
@@ -253,10 +253,6 @@ void Thread::Detach() {
m_thread->m_thread.detach();
}
void Thread::Sleep(uint32_t millis) {
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
}
void Thread::SleepMicro(uint32_t micros) {
#ifdef KYTY_WIN_CS
SleepHighResolution100ns(static_cast<uint64_t>(micros) * 10);
-1
View File
@@ -31,7 +31,6 @@ public:
// The id is unique and can't be reused by another thread.
[[nodiscard]] int GetUniqueId() const;
static void Sleep(uint32_t millis);
static void SleepMicro(uint32_t micros);
static void SleepNano(uint64_t nanos);
static bool IsMainThread();
-17
View File
@@ -62,23 +62,6 @@ double Timer::GetTimeS() const {
return (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
// return time in ticks
uint64_t Timer::GetTicks() const {
if (m_is_paused) {
return (m_PauseTime - m_StartTime);
}
uint64_t current_time = 0;
SysQueryPerformanceCounter(&current_time);
return (current_time - m_StartTime);
}
// return ticks frequency
uint64_t Timer::GetFrequency() const {
return m_Frequency;
}
uint64_t Timer::QueryPerformanceFrequency() {
uint64_t ret = 0;
SysQueryPerformanceFrequency(&ret);
-6
View File
@@ -24,12 +24,6 @@ public:
// return time in seconds
[[nodiscard]] double GetTimeS() const;
// return time in ticks
[[nodiscard]] uint64_t GetTicks() const;
// return ticks frequency
[[nodiscard]] uint64_t GetFrequency() const;
KYTY_CLASS_NO_COPY(Timer);
[[nodiscard]] static uint64_t QueryPerformanceFrequency();
-63
View File
@@ -1,63 +0,0 @@
#include "common/virtualMemory.h"
#include "common/platform/sysVirtual.h"
namespace Common {
namespace VirtualMemory {
void Init() {
SysVirtualInit();
}
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode) {
return SysVirtualAlloc(address, size, mode);
}
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment) {
return SysVirtualAllocAligned(address, size, mode, alignment);
}
bool AllocFixed(uint64_t address, uint64_t size, Mode mode) {
return SysVirtualAllocFixed(address, size, mode);
}
bool Commit(uint64_t address, uint64_t size, Mode mode) {
return SysVirtualCommit(address, size, mode);
}
uint64_t Reserve(uint64_t address, uint64_t size) {
return SysVirtualReserve(address, size);
}
uint64_t ReserveAligned(uint64_t address, uint64_t size, uint64_t alignment) {
return SysVirtualReserveAligned(address, size, alignment);
}
bool ReserveFixed(uint64_t address, uint64_t size) {
return SysVirtualReserveFixed(address, size);
}
bool Decommit(uint64_t address, uint64_t size) {
return SysVirtualDecommit(address, size);
}
bool Free(uint64_t address) {
return SysVirtualFree(address);
}
bool FreeRange(uint64_t address, uint64_t size) {
return SysVirtualFreeRange(address, size);
}
bool Protect(uint64_t address, uint64_t size, Mode mode) {
return SysVirtualProtect(address, size, mode);
}
bool FlushInstructionCache(uint64_t address, uint64_t size) {
return SysVirtualFlushInstructionCache(address, size);
}
} // namespace VirtualMemory
} // namespace Common
@@ -2017,6 +2017,14 @@ KYTY_CP_OP_PARSER(CpOpIndirectCxRegs) {
auto pfunc = g_hw_ctx_indirect_func[cmd_offset & (Pm4::CX_NUM - 1)];
if (pfunc == nullptr) {
if (raw_cmd_offset == 0x24au && value == 0u) {
static std::atomic_flag logged = ATOMIC_FLAG_INIT;
if (!logged.test_and_set(std::memory_order_relaxed)) {
Log::WriteToConsoleAndLog(
"\t diagnostic: ignoring indirect CX {0x24a, 0}; hardware effect unresolved\n");
}
continue;
}
EXIT("unknown cx reg at %05" PRIx32 ": 0x%" PRIx32 "\n", num_dw - dw, cmd_offset);
}
+4
View File
@@ -26,6 +26,10 @@ enum class PrimitiveType : uint32_t {
kPolygon = 21,
};
constexpr bool IsRectList(PrimitiveType type) {
return type == PrimitiveType::kRectList || type == PrimitiveType::kRectListLegacy;
}
enum class IndexType : uint32_t {
kIndex16 = 0,
kIndex32 = 1,
+6 -2
View File
@@ -386,8 +386,12 @@ void CommandProcessor::WriteReferenceClock(uint64_t dst_address, uint32_t num_by
}
const auto value = Sync::ReadReferenceClock();
std::memcpy(reinterpret_cast<void*>(dst_address), &value, num_bytes);
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64 " size=%u\n",
dst_address, value, num_bytes);
static std::atomic<uint32_t> clock_log_count {0};
if (clock_log_count.fetch_add(1) < 64) {
LOGF("\t copy_data reference clock: dst=0x%016" PRIx64 " value=0x%016" PRIx64
" size=%u\n",
dst_address, value, num_bytes);
}
}
void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cache_policy,
+2 -10
View File
@@ -4,15 +4,12 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/profiler.h"
#include "common/stringUtils.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/gpu_format.h"
#include <algorithm>
#include <array>
#include <bit>
#include <fmt/format.h>
#include <vector>
namespace Libs::Graphics {
@@ -1322,13 +1319,8 @@ void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t
return;
}
if (total_size != nullptr && total_size->size == 0) {
std::vector<std::string> list;
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 = {}", static_cast<uint32_t>(tile)));
EXIT("unknown format:\n%s\n", Common::Concat(list, '\n').c_str());
EXIT("unknown format:\nformat = %u\nwidth = %u\nheight = %u\nlevels = %u\ntile = %u\n",
static_cast<uint32_t>(format), width, height, levels, static_cast<uint32_t>(tile));
}
}
+1
View File
@@ -16,6 +16,7 @@
#include <windows.h>
#undef min
#undef max
#undef MemoryBarrier
#elif defined(__APPLE__)
#include <pthread.h>
#elif defined(__linux__)
+2 -1
View File
@@ -1237,7 +1237,8 @@ void TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) {
ImageId association {};
for (const auto id: FindImagesInRegion(stencil.address, stencil.size, false)) {
const auto owner = m_slot_images.try_get(id);
if (owner != nullptr && owner->info.data.address == stencil.address) {
if (owner != nullptr && owner->info.data == stencil &&
owner->info.extent == depth.info.extent) {
association = id;
}
}
@@ -159,8 +159,8 @@ static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
z.depth_view.slice_max);
}
if (z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
z.z_info.partially_resident ||
// EXPCLEAR permits an HTile acceleration state; the host attachment is already expanded.
if (z.z_info.partially_resident ||
z.stencil_info.partially_resident || z.z_info.max_mip_level != 0 ||
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
depth_address == 0 || (depth_address & 0xffffu) != 0) {
@@ -252,10 +252,10 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
const bool is_2d =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && valid_2d_slice;
// Storage cube coordinates address individual faces, including partial cube views.
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;
descriptor.BaseArray5() <= descriptor.Depth();
const bool is_2d_array =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
((!resource.cube && is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth()) ||
@@ -593,6 +593,21 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
ShaderParams pixel_params;
if (pixel_active) {
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
const auto& blend = context.GetBlendControl(0);
const auto is_dual_source = [](uint8_t factor) {
return factor >= static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color) &&
factor <= static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
};
pixel_info.dual_source_blending =
blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
(is_dual_source(blend.color_srcblend) || is_dual_source(blend.color_destblend) ||
(blend.separate_alpha_blend &&
(is_dual_source(blend.alpha_srcblend) || is_dual_source(blend.alpha_destblend))));
if (pixel_info.dual_source_blending) {
// MRT1 supplies a second blend source for the same render target as MRT0.
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
pixel_info.target_export_mapping[1] = pixel_info.target_export_mapping[0];
}
}
if (context.GetClipControl().clip_disable) {
const auto& viewport = context.GetScreenViewport().viewports[0];
@@ -740,8 +755,7 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
static_params.depth_bounds_test_enable = depth.depth_bounds_test_enable;
static_params.depth_min_bounds = depth.depth_min_bounds;
static_params.depth_max_bounds = depth.depth_max_bounds;
const bool rect_list =
command.GetUserConfig().GetPrimType() == Prospero::PrimitiveType::kRectList;
const bool rect_list = Prospero::IsRectList(command.GetUserConfig().GetPrimType());
static_params.cull_back = !rect_list && mc.cull_back;
static_params.cull_front = !rect_list && mc.cull_front;
static_params.face = mc.face;
@@ -46,8 +46,8 @@ vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages) {
return result;
}
VulkanMemoryBarrier MakeShaderWriteDependency() {
VulkanMemoryBarrier barrier {};
vk::MemoryBarrier MakeShaderWriteDependency() {
vk::MemoryBarrier barrier {};
barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite |
vk::AccessFlagBits::eVertexAttributeRead |
@@ -58,15 +58,15 @@ VulkanMemoryBarrier MakeShaderWriteDependency() {
return barrier;
}
VulkanMemoryBarrier MakeShaderAccessDependency() {
VulkanMemoryBarrier barrier {};
vk::MemoryBarrier MakeShaderAccessDependency() {
vk::MemoryBarrier barrier {};
barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
return barrier;
}
VulkanMemoryBarrier MakeShaderWriteHazardDependency() {
VulkanMemoryBarrier barrier {};
vk::MemoryBarrier MakeShaderWriteHazardDependency() {
vk::MemoryBarrier barrier {};
barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
return barrier;
@@ -10,9 +10,9 @@ struct ShaderStageRuntime;
vk::ShaderStageFlagBits NativeShaderStage(ShaderType stage);
vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages);
VulkanMemoryBarrier MakeShaderAccessDependency();
VulkanMemoryBarrier MakeShaderWriteHazardDependency();
VulkanMemoryBarrier MakeShaderWriteDependency();
vk::MemoryBarrier MakeShaderAccessDependency();
vk::MemoryBarrier MakeShaderWriteHazardDependency();
vk::MemoryBarrier MakeShaderWriteDependency();
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer);
bool HasShaderBufferWrites(const ShaderStageRuntime& runtime);
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
@@ -225,7 +225,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
return;
}
if (!ShaderAddressValid(sh_ctx.GetCs().cs_regs.data_addr)) {
if (sh_ctx.GetCs().cs_regs.data_addr == 0) {
return;
}
+14 -26
View File
@@ -2,6 +2,7 @@
#include "common/assert.h"
#include "common/common.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
#include "common/profiler.h"
@@ -421,7 +422,7 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
static bool DrawHasValidVertexShader(const HW::Shader& sh_ctx) {
const auto& vs = sh_ctx.GetVs();
return ShaderAddressValid(vs.es_regs.data_addr);
return vs.es_regs.data_addr != 0;
}
static bool PixelShaderHasDepthOrCoverageSideEffects(const HW::ShaderRegisters& sh_regs) {
@@ -608,7 +609,7 @@ static bool DrawHasActivePixelShader(const CommandBuffer& buffer) {
const auto& ctx = buffer.GetRegisters();
const auto& sh_regs = ctx.GetShaderRegisters();
const bool has_color_output = (ctx.GetRenderTargetMask() & sh_regs.m_cbShaderMask) != 0;
return ShaderAddressValid(buffer.GetShaders().GetPs().ps_regs.data_addr) &&
return buffer.GetShaders().GetPs().ps_regs.data_addr != 0 &&
(has_color_output || PixelShaderHasDepthOrCoverageSideEffects(sh_regs));
}
@@ -789,8 +790,7 @@ static void SetDrawDebugPhase(CommandBuffer& buffer, uint64_t submit_id, const D
draw.instance_count, draw.first_instance);
}
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
vk::PrimitiveTopology& topology) {
static bool GetDrawTopology(const HW::UserConfig& ucfg, vk::PrimitiveTopology& topology) {
topology = vk::PrimitiveTopology::ePointList;
@@ -813,14 +813,11 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
topology = vk::PrimitiveTopology::eTriangleStrip;
break;
case Prospero::PrimitiveType::kPatch:
if (!Config::TessellationEnabled()) return false;
[[fallthrough]];
case Prospero::PrimitiveType::kRectList:
topology = vk::PrimitiveTopology::ePatchList;
break;
case Prospero::PrimitiveType::kRectListLegacy:
if (!auto_draw) {
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
}
topology = vk::PrimitiveTopology::eTriangleStrip;
topology = vk::PrimitiveTopology::ePatchList;
break;
case Prospero::PrimitiveType::kQuadListLegacy:
topology = vk::PrimitiveTopology::eTriangleFan;
@@ -999,8 +996,7 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo
return;
}
if (!draw.IsIndexed() &&
buffer.GetUserConfig().GetPrimType() != Prospero::PrimitiveType::kRectListLegacy) {
if (!draw.IsIndexed() && !Prospero::IsRectList(buffer.GetUserConfig().GetPrimType())) {
return;
}
@@ -1013,8 +1009,7 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo
}
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
const DrawEmitInfo& emit) {
const DrawCallInfo& draw, const DrawEmitInfo& emit) {
switch (ucfg.GetPrimType()) {
case Prospero::PrimitiveType::kPointList:
case Prospero::PrimitiveType::kLineList:
@@ -1023,6 +1018,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
case Prospero::PrimitiveType::kTriFan:
case Prospero::PrimitiveType::kTriStrip:
case Prospero::PrimitiveType::kRectList:
case Prospero::PrimitiveType::kRectListLegacy:
case Prospero::PrimitiveType::kPatch:
if (draw.IsIndexed()) {
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
@@ -1032,14 +1028,6 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
emit.first_instance);
}
break;
case Prospero::PrimitiveType::kRectListLegacy:
if (draw.IsIndexed()) {
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));
vk_buffer.draw(4, draw.instance_count, emit.first_vertex, emit.first_instance);
break;
case Prospero::PrimitiveType::kQuadListLegacy:
EXIT_NOT_IMPLEMENTED((draw.index_count & 0x3u) != 0);
for (uint32_t i = 0; i < draw.index_count; i += 4) {
@@ -1182,7 +1170,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
if (mesh_active) {
vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1);
} else {
EmitDrawPrimitives(ucfg, vk_buffer, state.vertex_info[0], draw, emit);
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
}
if (!draw.IsIndexed()) {
@@ -1258,7 +1246,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
hw_check(buffer);
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
if (!GetDrawTopology(ucfg, false, topology)) {
if (!GetDrawTopology(ucfg, topology)) {
return;
}
@@ -1368,7 +1356,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
args.vertex_count, args.instance_count, args.first_instance};
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
if (!GetDrawTopology(ucfg, true, topology)) {
if (!GetDrawTopology(ucfg, topology)) {
ResetBindings();
return;
}
@@ -1378,7 +1366,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
return;
}
const bool rect_list = ucfg.GetPrimType() == Prospero::PrimitiveType::kRectList;
const bool rect_list = Prospero::IsRectList(ucfg.GetPrimType());
if (rect_list && state.vertex_info[0].buffers_num == 0 &&
state.vertex_info[0].stage.program->param_export_mask == 0 &&
state.ps_input_info.input_num != 0) {
-2
View File
@@ -24,8 +24,6 @@
namespace Libs::Graphics {
using VulkanMemoryBarrier = vk::MemoryBarrier;
vk::Format VulkanFormat(Prospero::BufferFormat guest_format);
void RequireVulkanSuccess(vk::Result result, const char* operation);
vk::ShaderModule CompileSPV(std::span<const uint32_t> code, vk::Device device);
@@ -141,9 +141,8 @@ public:
}
}
const bool reserved = binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE ||
binding.key == SDLK_F1 || binding.key == SDLK_F7 ||
binding.key == SDLK_F11;
const bool reserved = binding.key == SDLK_ESCAPE || binding.key == SDLK_F1 ||
binding.key == SDLK_F7 || binding.key == SDLK_F11;
if (binding.control == INVALID_CONTROL || reserved ||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
EXIT("Invalid input mapping: %s\n", value.c_str());
@@ -22,9 +22,6 @@
// IWYU pragma: no_include <intrin.h>
#define KYTY_ENABLE_DEBUG_PRINTF
#define KYTY_DBG_INPUT
namespace Libs::Graphics {
struct Presenter::Frame {
@@ -49,7 +49,6 @@
// IWYU pragma: no_include <intrin.h>
#define KYTY_ENABLE_DEBUG_PRINTF
#define KYTY_DBG_INPUT
namespace Libs::Graphics {
@@ -611,10 +610,11 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
graphics.mesh_shader_properties.maxMeshOutputPrimitives,
graphics.mesh_shader_properties.maxMeshSharedMemorySize);
}
// VulkanFindPhysicalDevice already checked the required creation features. These two
// VulkanFindPhysicalDevice already checked the required creation features. These
// requirements are specific to this creation path and are not part of device selection.
EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderInt64 != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.vertexPipelineStoresAndAtomics != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.dualSrcBlend != VK_TRUE);
vk::PhysicalDeviceFeatures device_features {};
device_features.fragmentStoresAndAtomics = VK_TRUE;
device_features.samplerAnisotropy = VK_TRUE;
@@ -625,6 +625,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
device_features.shaderStorageImageWriteWithoutFormat = VK_TRUE;
device_features.shaderImageGatherExtended = VK_TRUE;
device_features.independentBlend = VK_TRUE;
device_features.dualSrcBlend = VK_TRUE;
device_features.tessellationShader = VK_TRUE;
device_features.sampleRateShading = VK_TRUE;
device_features.depthBiasClamp = VK_TRUE;
+28 -21
View File
@@ -27,6 +27,7 @@
#include "common/systemInfo.h"
#include "common/threads.h"
#include "common/timer.h"
#include "common/stringUtils.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
@@ -45,6 +46,7 @@
#include <string>
#include <vector>
#include <vulkan/vk_platform.h>
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_SIMD
@@ -52,7 +54,6 @@
// IWYU pragma: no_include <intrin.h>
#define KYTY_ENABLE_DEBUG_PRINTF
#define KYTY_DBG_INPUT
namespace Libs::Graphics {
@@ -180,12 +181,6 @@ struct EventDisplay {
DisplayOrientation orientation;
};
constexpr uint32_t KYTY_SDL_BUTTON_LMASK = SDL_BUTTON_LMASK; // NOLINT(hicpp-signed-bitwise)
constexpr uint32_t KYTY_SDL_BUTTON_MMASK = SDL_BUTTON_MMASK; // NOLINT(hicpp-signed-bitwise)
constexpr uint32_t KYTY_SDL_BUTTON_RMASK = SDL_BUTTON_RMASK; // NOLINT(hicpp-signed-bitwise)
constexpr uint32_t KYTY_SDL_BUTTON_X1MASK = SDL_BUTTON_X1MASK; // NOLINT(hicpp-signed-bitwise)
constexpr uint32_t KYTY_SDL_BUTTON_X2MASK = SDL_BUTTON_X2MASK; // NOLINT(hicpp-signed-bitwise)
namespace {
std::unique_ptr<WindowContext> g_window;
@@ -193,7 +188,6 @@ std::unique_ptr<WindowContext> g_window;
} // namespace
constexpr const char* KYTY_SDL_WINDOW_CAPTION = "Game";
constexpr int KYTY_SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED; /*NOLINT(hicpp-signed-bitwise)*/
static void SetPause(WindowLoopState& game, bool flag) {
LOGF("Pause: %s\n", flag ? "true" : "false");
@@ -228,7 +222,7 @@ static void ToggleDesktopFullscreen() {
}
}
static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
static void GameEventKeyboard(const EventKeyboard& key) {
static SDL_Keycode fullscreen_key = SDLK_UNKNOWN;
#ifdef KYTY_DBG_INPUT
@@ -241,7 +235,6 @@ static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX
if (key.down) {
switch (key.key_code) {
case SDLK_SPACE: SetPause(game, !game.paused.load(std::memory_order_acquire)); break;
case SDLK_F1:
if (!key.repeat) {
RenderDocRequestCapture();
@@ -545,7 +538,7 @@ void WindowContext::ProcessEvent(double time_s) {
key.mod = event->key.keysym.mod;
key.timestamp_seconds = time_s;
GameEventKeyboard(game, key);
GameEventKeyboard(key);
break;
}
@@ -614,11 +607,11 @@ void WindowContext::ProcessEvent(double time_s) {
mb.down = false;
mb.up = false;
mb.left = ((event->motion.state & KYTY_SDL_BUTTON_LMASK) != 0u);
mb.middle = ((event->motion.state & KYTY_SDL_BUTTON_MMASK) != 0u);
mb.right = ((event->motion.state & KYTY_SDL_BUTTON_RMASK) != 0u);
mb.x1 = ((event->motion.state & KYTY_SDL_BUTTON_X1MASK) != 0u);
mb.x2 = ((event->motion.state & KYTY_SDL_BUTTON_X2MASK) != 0u);
mb.left = ((event->motion.state & SDL_BUTTON_LMASK) != 0u);
mb.middle = ((event->motion.state & SDL_BUTTON_MMASK) != 0u);
mb.right = ((event->motion.state & SDL_BUTTON_RMASK) != 0u);
mb.x1 = ((event->motion.state & SDL_BUTTON_X1MASK) != 0u);
mb.x2 = ((event->motion.state & SDL_BUTTON_X2MASK) != 0u);
mb.touch = (event->motion.which == SDL_TOUCH_MOUSEID);
mb.pressed = false;
mb.released = false;
@@ -713,6 +706,20 @@ void WindowContext::ProcessEvent(double time_s) {
}
break;
case SDL_CONTROLLERSENSORUPDATE: {
const auto& sensor = event->csensor;
if (sensor.sensor == SDL_SENSOR_ACCEL || sensor.sensor == SDL_SENSOR_GYRO) {
Controller::SetSensor(sensor.which,
sensor.sensor == SDL_SENSOR_ACCEL ? Controller::Sensor::Accel
: Controller::Sensor::Gyro,
sensor.data,
sensor.timestamp_us != 0
? sensor.timestamp_us
: static_cast<uint64_t>(sensor.timestamp) * 1000);
}
break;
}
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMAPPED: {
@@ -854,8 +861,8 @@ static void WindowCreate(WindowContext& context) {
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
}
#endif
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, KYTY_SDL_WINDOWPOS_CENTERED,
KYTY_SDL_WINDOWPOS_CENTERED, width, height, window_flags);
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, window_flags);
if (context.window == nullptr) {
EXIT("%s\n", SDL_GetError());
@@ -939,10 +946,10 @@ struct WindowIcon {
}
};
static void WindowLoadPngIcon(const std::string& path, WindowIcon* icon) {
static void WindowLoadPngIcon(const std::filesystem::path& path, WindowIcon* icon) {
Common::File f;
if (!f.Open(path, Common::File::Mode::Read)) {
EXIT("Can't open icon file %s\n", path.c_str());
EXIT("Can't open icon file %s\n", Common::PathToString(path).c_str());
}
int width = 0;
@@ -968,7 +975,7 @@ void WindowContext::UpdateIcon() {
static bool icon_loaded = false;
if (!icon_loaded) {
std::string icon_path;
std::filesystem::path icon_path;
if (Loader::SystemContentGetIconPath(&icon_path)) {
WindowLoadPngIcon(icon_path, &icon);
}
@@ -105,8 +105,6 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
lanes->erase(first, last);
}
using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
bool IsDecodedSgpr(const Decoder::Operand& op) {
return op.kind == Decoder::OperandKind::Sgpr || op.kind == Decoder::OperandKind::VccLo ||
op.kind == Decoder::OperandKind::VccHi;
@@ -210,13 +208,12 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) {
return static_cast<int>((raw_offset + static_cast<uint32_t>(dword) * 4u) / 16u);
}
EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded,
const ShaderVertexInputInfo* input_info,
uint32_t user_data_base, uint32_t user_data_count,
uint32_t wave_size) {
Frontend::EmbeddedFetchPlan DetectEmbeddedVertexFetch(
const Decoder::Program& decoded, const ShaderVertexInputInfo* input_info,
uint32_t user_data_base, uint32_t user_data_count, uint32_t wave_size) {
const uint32_t vertex_index_reg = input_info->logical_stage == ShaderType::Local ? 2u : 5u;
const uint32_t instance_index_reg = input_info->logical_stage == ShaderType::Local ? 5u : 8u;
EmbeddedFetchData data;
Frontend::EmbeddedFetchPlan data;
data.loads.reserve(input_info->resources_num);
int32_t vertex_offset_candidate = -1;
int32_t instance_offset_candidate = -1;
@@ -560,7 +557,7 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
static_cast<uint64_t>(cfg.natural_loops.size()), phase_ms());
}
EmbeddedFetchData embedded_fetch;
Frontend::EmbeddedFetchPlan embedded_fetch;
if ((options.stage == ShaderType::Vertex || options.stage == ShaderType::Local) &&
options.input_info.vertex != nullptr && options.input_info.vertex->fetch_embedded) {
embedded_fetch = DetectEmbeddedVertexFetch(
@@ -138,7 +138,7 @@ uint32_t EmitDppWriteCondition(ValueEmitContext& ctx, const IR::DppMoveFlags& fl
state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), masks_ok, bank_ok, row_ok);
uint32_t writable = masks_ok;
if (!flags.bound_control) {
const auto target = EmitDppTargetLane(state, flags.control);
const auto target = EmitDppTargetLane(state, flags);
const auto bounded = state.builder.AllocateId();
state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), bounded, writable,
target.valid);
@@ -611,7 +611,7 @@ uint32_t EmitUndefU1(EmitterState& state, const IR::Inst& inst) {
uint32_t EmitDppMoveU32(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
const auto flags = inst.Flags<IR::DppMoveFlags>();
const auto target = EmitDppTargetLane(state, flags.control);
const auto target = EmitDppTargetLane(state, flags);
const auto shuffled = ctx.Shuffle(inst, 0, target.lane);
if (flags.fetch_inactive) {
return shuffled;
@@ -2,24 +2,26 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control) {
const auto quad_base = state.builder.AllocateId();
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
uint32_t lane_bits) {
const auto lane_mask = (1u << lane_bits) - 1u;
const auto group_base = state.builder.AllocateId();
const auto lane = state.builder.AllocateId();
const auto shift = state.builder.AllocateId();
const auto selected0 = state.builder.AllocateId();
const auto selected = state.builder.AllocateId();
const auto target = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), quad_base, subid,
ConstantU32(state, 0xfffffffcu));
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), group_base, subid,
ConstantU32(state, ~lane_mask));
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid,
ConstantU32(state, 3));
state.builder.AddFunction(spv::OpShiftLeftLogical, TypeU32(state), shift, lane,
ConstantU32(state, 1));
ConstantU32(state, lane_mask));
state.builder.AddFunction(spv::OpIMul, TypeU32(state), shift, lane,
ConstantU32(state, lane_bits));
state.builder.AddFunction(spv::OpShiftRightLogical, TypeU32(state), selected0,
ConstantU32(state, control), shift);
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), selected, selected0,
ConstantU32(state, 3));
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, quad_base, selected);
ConstantU32(state, lane_mask));
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, group_base, selected);
return {target, ConstantBool(state, true)};
}
@@ -89,10 +91,14 @@ DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool
return {target, ConstantBool(state, true)};
}
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags) {
const auto subid = EmitSubgroupLocalInvocationId(state);
const auto control = flags.control;
if (flags.dpp8) {
return EmitDppPermTargetLane(state, subid, control, 3u);
}
if (control <= 0xffu) {
return EmitDppQuadPermTargetLane(state, subid, control);
return EmitDppPermTargetLane(state, subid, control, 2u);
}
if (control >= 0x101u && control <= 0x10fu) {
return EmitDppRowShiftTargetLane(state, subid, control & 0xfu, true);
@@ -3,7 +3,9 @@
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
#include <algorithm>
#include <atomic>
#include <bit>
#include <cstdio>
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace {
@@ -646,6 +648,13 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto coord =
CoordF32(ctx, mem, *address, layout.coord, dimension_info.coordinate_components);
if (op == IR::ValueOpcode::ImageGatherRaw) {
if (HasFlag(mem, Decoder::ImageSampleFlagLod)) {
static std::atomic_flag warned = ATOMIC_FLAG_INIT;
if (!warned.test_and_set(std::memory_order_relaxed)) {
std::fputs("Warning: approximating IMAGE_GATHER4_L at mip level 0; explicit LOD is ignored.\n",
stderr);
}
}
if (dimension == ImageDimension::Dim1D) {
if (dref || !HasFlag(mem, Decoder::ImageSampleFlagLevelZero) ||
HasFlag(mem, Decoder::ImageSampleFlagOffset) ||
@@ -308,8 +308,6 @@ uint32_t ConstantI32(EmitterState& state, int32_t value);
uint32_t ConstantF32(EmitterState& state, uint32_t bits);
uint32_t FloatBits(float value);
uint32_t ConstantF32Value(EmitterState& state, float value);
uint32_t ConstantBool(EmitterState& state, bool value);
@@ -329,7 +327,8 @@ void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst);
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index = 0);
uint32_t MeshPrimitivePointer(EmitterState& state);
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control);
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
uint32_t lane_bits);
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
bool left);
@@ -338,7 +337,7 @@ DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subi
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row);
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control);
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags);
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
@@ -514,31 +514,6 @@ void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem)
});
}
void FormattedStorePrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
uint32_t component, const MemoryResourceAccess& resource,
uint32_t data) {
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
if (info.type == Format::ComponentType::Unknown) {
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, component), resource, data);
return;
}
if (component >= info.component_count) return;
const auto bits = info.component_bits[component];
const auto component_mem = RebaseFormattedComponent(mem, info, component);
if (bits == 8u || bits == 16u) {
StoreSubwordPrepared(ctx, inst, component_mem, resource, bits, data);
} else {
StoreWordPrepared(ctx, inst, component_mem, resource, data);
}
}
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
FormattedStorePrepared(ctx, inst, mem, 0u, resource, ctx.Arg(inst, inst.NumArgs() - 2));
});
}
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
switch (opcode) {
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
@@ -729,6 +704,11 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
uint32_t data) {
if (component >= plan.info.component_count) return;
const auto bits = plan.info.component_bits[component];
if (plan.info.type == Format::ComponentType::Snorm && bits == 16u) {
const auto value = EmitBitCastF32U32(ctx.state, data);
data = EmitPackSnorm2x16(
ctx.state, EmitCompositeConstructF32x2(ctx.state, value, ConstantF32Value(ctx.state, 0.0f)));
}
if (bits == 8u || bits == 16u) {
StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component],
plan.indices[component], bits, data);
@@ -737,6 +717,23 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
}
}
void FormattedStore(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
EmitIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
const auto data = ctx.Arg(inst, inst.NumArgs() - 2);
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
if (info.type == Format::ComponentType::Unknown) {
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource, data);
return;
}
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, 1u,
FormattedAccess::Store);
EmitIfCondition(ctx.state, plan.in_bounds, [&]() {
StoreFormattedInBounds(ctx, mem, plan, 0u, data);
});
});
}
uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
auto& state = ctx.state;
return EmitValueOrDefaultIfCondition(
@@ -1,6 +1,7 @@
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include <algorithm>
#include <bit>
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -256,14 +257,8 @@ uint32_t ConstantF32(EmitterState& state, uint32_t bits) {
return state.builder.Constant(spv::OpConstant, TypeF32(state), bits);
}
uint32_t FloatBits(float value) {
uint32_t bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
return bits;
}
uint32_t ConstantF32Value(EmitterState& state, float value) {
return ConstantF32(state, FloatBits(value));
return ConstantF32(state, std::bit_cast<uint32_t>(value));
}
uint32_t ConstantBool(EmitterState& state, bool value) {
@@ -558,8 +553,17 @@ void DefineOutputs(EmitterState& state) {
const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
binding.debug_name.c_str());
const bool dual_source = binding.kind == IR::StageOutputKind::Mrt &&
state.program.stage == ShaderType::Pixel &&
state.input_info.pixel->dual_source_blending;
EXIT_NOT_IMPLEMENTED(dual_source && binding.index > 1);
state.builder.AddAnnotation(spv::OpDecorate, binding.variable_id,
spv::DecorationLocation, binding.location);
spv::DecorationLocation,
dual_source ? 0u : binding.location);
if (dual_source) {
state.builder.AddAnnotation(spv::OpDecorate, binding.variable_id,
spv::DecorationIndex, binding.index);
}
break;
}
}
@@ -3,6 +3,7 @@
#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h"
#include <algorithm>
#include <bit>
namespace Libs::Graphics::ShaderRecompiler::Decoder {
namespace {
@@ -175,6 +176,7 @@ constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = {
};
constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = {
{0x44u, Opcode::IMAGE_GATHER4_L, ImageSampleFlagLod},
{0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero},
{0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare},
{0x4fu, Opcode::IMAGE_GATHER4_C_LZ,
@@ -202,18 +204,6 @@ constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCO
constexpr auto MIMG_GATHER_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_GATHER_OPCODE_LIST);
constexpr auto MIMG_ATOMIC_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_ATOMIC_OPCODE_LIST);
const MimgSampleInfo* LookupSample(uint32_t opcode) {
return Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
}
const MimgGatherInfo* LookupGather(uint32_t opcode) {
return Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
}
const Detail::OpcodeMap* LookupAtomic(uint32_t opcode) {
return Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
}
Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather,
const Detail::OpcodeMap* atomic) {
if (sample != nullptr) {
@@ -279,10 +269,6 @@ uint32_t CountDmaskComponents(uint32_t dmask) {
return count != 0 ? count : 1u;
}
bool IsSingleDmaskBit(uint32_t dmask) {
return dmask != 0u && (dmask & (dmask - 1u)) == 0u;
}
} // namespace
ImageAddressComponent ImageAddressComponentLayout(uint32_t flags, uint32_t component) {
@@ -331,9 +317,9 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
const bool r128 = ((word0 >> 15u) & 0x1u) != 0u;
const bool a16 = ((word1 >> 30u) & 0x1u) != 0u;
const bool d16 = ((word1 >> 31u) & 0x1u) != 0u;
const auto* sample = LookupSample(opcode);
const auto* gather = LookupGather(opcode);
const auto* atomic = LookupAtomic(opcode);
const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
const auto* gather = Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
const auto* atomic = Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
inst.pc = pc;
inst.word_count = word_count;
@@ -363,7 +349,7 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::MIMG, opcode, "MIMG opcode is not implemented");
}
if (gather != nullptr && !IsSingleDmaskBit(inst.dmask)) {
if (gather != nullptr && !std::has_single_bit(inst.dmask)) {
SetUnsupported(inst, Family::MIMG, opcode,
"MIMG image gather requires exactly one dmask bit");
}
@@ -381,7 +367,7 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
}
const char* MimgSampleOpcodeName(uint32_t opcode) {
const auto* sample = LookupSample(opcode);
const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode);
return sample != nullptr ? sample->name : nullptr;
}
@@ -24,7 +24,8 @@ constexpr OpcodeMap SOP2_OPCODE_LIST[] = {
{0x1cu, Opcode::S_XNOR_B32}, {0x1du, Opcode::S_XNOR_B64},
{0x1eu, Opcode::S_LSHL_B32}, {0x1fu, Opcode::S_LSHL_B64},
{0x20u, Opcode::S_LSHR_B32}, {0x21u, Opcode::S_LSHR_B64},
{0x22u, Opcode::S_ASHR_I32}, {0x24u, Opcode::S_BFM_B32},
{0x22u, Opcode::S_ASHR_I32}, {0x23u, Opcode::S_ASHR_I64},
{0x24u, Opcode::S_BFM_B32},
{0x25u, Opcode::S_BFM_B64}, {0x26u, Opcode::S_MUL_I32},
{0x27u, Opcode::S_BFE_U32}, {0x28u, Opcode::S_BFE_I32},
{0x29u, Opcode::S_BFE_U64}, {0x2cu, Opcode::S_ABSDIFF_I32},
@@ -73,7 +74,8 @@ constexpr OpcodeMap SOPC_OPCODE_LIST[] = {
{0x03u, Opcode::S_CMP_GE_I32}, {0x04u, Opcode::S_CMP_LT_I32}, {0x05u, Opcode::S_CMP_LE_I32},
{0x06u, Opcode::S_CMP_EQ_U32}, {0x07u, Opcode::S_CMP_LG_U32}, {0x08u, Opcode::S_CMP_GT_U32},
{0x09u, Opcode::S_CMP_GE_U32}, {0x0au, Opcode::S_CMP_LT_U32}, {0x0bu, Opcode::S_CMP_LE_U32},
{0x0cu, Opcode::S_BITCMP0_B32}, {0x0du, Opcode::S_BITCMP1_B32}, {0x12u, Opcode::S_CMP_EQ_U64},
{0x0cu, Opcode::S_BITCMP0_B32}, {0x0du, Opcode::S_BITCMP1_B32},
{0x0eu, Opcode::S_BITCMP0_B64}, {0x0fu, Opcode::S_BITCMP1_B64}, {0x12u, Opcode::S_CMP_EQ_U64},
{0x13u, Opcode::S_CMP_LG_U64},
};
@@ -137,6 +139,10 @@ void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
inst.family = Family::SOP1;
inst.opcode_id = opcode;
inst.opcode = Detail::LookupOpcode(SOP1_OPS, opcode);
if (opcode == 0x21u && sdst == 125u) {
// S_SWAPPC_B64 with NULL discards the return PC, so it is a plain jump.
inst.opcode = Opcode::S_SETPC_B64;
}
SetRawWords(inst, code, word_index, 1);
if (inst.opcode == Opcode::UNSUPPORTED) {
@@ -14,10 +14,6 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
namespace {
uint32_t FloatBits(float value) {
return std::bit_cast<uint32_t>(value);
}
bool HasLiteral(const Instruction& inst) {
return inst.src0.kind == OperandKind::LiteralConstant ||
inst.src1.kind == OperandKind::LiteralConstant ||
@@ -144,6 +140,7 @@ std::string FormatMimg(const Instruction& inst) {
}
switch (inst.opcode) {
case Opcode::IMAGE_SAMPLE:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_LZ:
@@ -241,7 +238,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
if (code >= 240u && code <= 247u) {
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
operand.kind = OperandKind::FloatInlineConstant;
operand.value = FloatBits(values[code - 240u]);
operand.value = std::bit_cast<uint32_t>(values[code - 240u]);
return;
}
if (code >= 256u && code <= 511u) {
@@ -259,7 +256,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
case 248u:
operand.kind = OperandKind::FloatInlineConstant;
operand.value = FloatBits(0.15915494309189535f);
operand.value = std::bit_cast<uint32_t>(0.15915494309189535f);
return;
case 251u: operand.kind = OperandKind::VccZ; return;
case 252u: operand.kind = OperandKind::ExecZ; return;
@@ -475,8 +472,9 @@ std::string OperandToString(const Operand& operand) {
text += ".clamp";
}
if (operand.dpp) {
text += fmt::format(".dpp(ctrl=0x{:x},fi={},bc={})", operand.dpp_ctrl,
operand.dpp_fetch_inactive ? 1u : 0u, operand.dpp_bound_ctrl ? 1u : 0u);
text += fmt::format(".{}(ctrl=0x{:x},fi={},bc={})", operand.dpp8 ? "dpp8" : "dpp",
operand.dpp_ctrl, operand.dpp_fetch_inactive ? 1u : 0u,
operand.dpp_bound_ctrl ? 1u : 0u);
}
return text;
}
@@ -580,6 +578,7 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::IMAGE_LOAD_MIP:
case Opcode::IMAGE_GET_RESINFO:
case Opcode::IMAGE_GET_LOD:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_C_LZ:
@@ -2,8 +2,8 @@
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERDECODER_H_
#include "common/common.h"
#include "common/magicEnum.h"
#include <magic_enum.hpp>
#include <span>
#include <string_view>
#include <vector>
@@ -73,6 +73,8 @@ enum class Opcode {
S_SUB_I32,
S_BITCMP0_B32,
S_BITCMP1_B32,
S_BITCMP0_B64,
S_BITCMP1_B64,
S_BITSET0_B32,
S_BITSET1_B32,
S_BITSET0_B64,
@@ -106,6 +108,7 @@ enum class Opcode {
S_LSHR_B32,
S_LSHR_B64,
S_ASHR_I32,
S_ASHR_I64,
S_MUL_I32,
S_MUL_HI_U32,
S_MUL_HI_I32,
@@ -227,6 +230,7 @@ enum class Opcode {
V_CVT_PKRTZ_F16_F32,
V_CVT_PK_U8_F32,
V_MAD_F32,
V_MAD_I16,
V_MAD_I32_I24,
V_MAD_U32_U24,
V_MAD_U64_U32,
@@ -299,6 +303,7 @@ enum class Opcode {
V_SUBREV_NC_U32,
V_ADD_NC_U16,
V_SUB_NC_U16,
V_MUL_LO_U16,
V_MAX_U16,
V_MAX_I16,
V_MIN_U16,
@@ -377,7 +382,9 @@ enum class Opcode {
V_CMP_GT_F16,
V_CMP_LG_F16,
V_CMP_GE_F16,
V_CMP_NGT_F16,
V_CMP_NEQ_F16,
V_CMP_NLT_F16,
V_CMPX_LT_F16,
V_CMPX_EQ_F16,
V_CMPX_LE_F16,
@@ -559,6 +566,7 @@ enum class Opcode {
IMAGE_ATOMIC_OR,
IMAGE_ATOMIC_XOR,
IMAGE_SAMPLE,
IMAGE_GATHER4_L,
IMAGE_GATHER4_LZ,
IMAGE_GATHER4_C,
IMAGE_GATHER4_C_LZ,
@@ -664,6 +672,7 @@ struct Operand {
bool absolute = false;
bool clamp = false;
bool dpp = false;
bool dpp8 = false;
};
struct Instruction {
@@ -83,10 +83,6 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
constexpr auto VOP2_OPS = Detail::MakeOpcodeTable<0x40>(VOP2_OPCODE_LIST);
constexpr Opcode LookupVop2Opcode(uint32_t encoding) {
return Detail::LookupOpcode(VOP2_OPS, encoding);
}
constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
{0x00u, Opcode::V_NOP},
{0x01u, Opcode::V_MOV_B32},
@@ -250,7 +246,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
{0xf5u, Opcode::V_CMPX_NE_U64, false}, {0xc9u, Opcode::V_CMP_LT_F16},
{0xcau, Opcode::V_CMP_EQ_F16}, {0xcbu, Opcode::V_CMP_LE_F16},
{0xccu, Opcode::V_CMP_GT_F16}, {0xcdu, Opcode::V_CMP_LG_F16},
{0xceu, Opcode::V_CMP_GE_F16}, {0xedu, Opcode::V_CMP_NEQ_F16},
{0xceu, Opcode::V_CMP_GE_F16}, {0xebu, Opcode::V_CMP_NGT_F16},
{0xedu, Opcode::V_CMP_NEQ_F16}, {0xeeu, Opcode::V_CMP_NLT_F16},
{0xd9u, Opcode::V_CMPX_LT_F16}, {0xdau, Opcode::V_CMPX_EQ_F16},
{0xdbu, Opcode::V_CMPX_LE_F16}, {0xdcu, Opcode::V_CMPX_GT_F16},
{0xdeu, Opcode::V_CMPX_GE_F16}, {0xfbu, Opcode::V_CMPX_NGT_F16},
@@ -298,6 +295,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
{0x300u, Opcode::V_LSHRREV_B64},
{0x303u, Opcode::V_ADD_NC_U16},
{0x304u, Opcode::V_SUB_NC_U16},
{0x305u, Opcode::V_MUL_LO_U16},
{0x307u, Opcode::V_LSHRREV_B16},
{0x308u, Opcode::V_ASHRREV_I16},
{0x309u, Opcode::V_MAX_U16},
@@ -314,6 +312,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
{0x345u, Opcode::V_XAD_U32},
{0x346u, Opcode::V_LSHL_ADD_U32},
{0x347u, Opcode::V_ADD_LSHL_U32},
{0x35eu, Opcode::V_MAD_I16},
{0x360u, Opcode::V_READLANE_B32},
{0x361u, Opcode::V_WRITELANE_B32},
{0x362u, Opcode::V_LDEXP_F32},
@@ -374,7 +373,7 @@ Opcode LookupVop3Opcode(uint32_t opcode) {
if (IsUnsupportedVop3EncodedVop2Alias(opcode - 0x100u)) {
return Opcode::UNSUPPORTED;
}
return LookupVop2Opcode(opcode - 0x100u);
return Detail::LookupOpcode(VOP2_OPS, opcode - 0x100u);
}
if (opcode >= 0x180u && opcode <= 0x1ffu) {
return Detail::LookupOpcode(VOP3_ENCODED_VOP1_OPS, opcode - 0x180u);
@@ -426,13 +425,14 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) {
}
bool IsNativeVop3I16TernaryOpcode(Opcode opcode) {
return opcode == Opcode::V_MED3_I16;
return opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MAD_I16;
}
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
switch (opcode) {
case Opcode::V_ADD_NC_U16:
case Opcode::V_SUB_NC_U16:
case Opcode::V_MUL_LO_U16:
case Opcode::V_MAX_U16:
case Opcode::V_MAX_I16:
case Opcode::V_MIN_U16:
@@ -521,7 +521,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
{Opcode::V_CVT_F32_U32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_I32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_UBYTE0, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(),
SdwaSelFull(), false},
{Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true},
{Opcode::V_CVT_F16_F32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
@@ -685,10 +686,15 @@ void DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
ReadLiteralOperands(code, word_index, inst);
}
void ApplyDppModifier(Operand& operand, uint32_t modifier) {
void ApplyDppModifier(Operand& operand, uint32_t modifier, uint32_t encoding) {
operand.dpp = true;
if (encoding == 233u) {
operand.dpp8 = true;
operand.dpp_ctrl = modifier >> 8u;
return;
}
operand.negate = ((modifier >> 20u) & 0x1u) != 0u;
operand.absolute = ((modifier >> 21u) & 0x1u) != 0u;
operand.dpp = true;
operand.dpp_ctrl = (modifier >> 8u) & 0x1ffu;
operand.dpp_fetch_inactive = ((modifier >> 18u) & 0x1u) != 0u;
operand.dpp_bound_ctrl = ((modifier >> 19u) & 0x1u) != 0u;
@@ -709,7 +715,7 @@ void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
DecodeVectorGpr(vdst, inst.dst);
}
DecodeScalarSource(src0 + 256u, pc, inst.src0);
ApplyDppModifier(inst.src0, modifier);
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
inst.src_count = 1;
if (!IsVop1FloatSourceOpcode(inst.opcode) && (inst.src0.negate || inst.src0.absolute)) {
@@ -824,7 +830,9 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) {
case Opcode::V_CMP_GT_F16:
case Opcode::V_CMP_LG_F16:
case Opcode::V_CMP_GE_F16:
case Opcode::V_CMP_NGT_F16:
case Opcode::V_CMP_NEQ_F16:
case Opcode::V_CMP_NLT_F16:
case Opcode::V_CMPX_LT_F16:
case Opcode::V_CMPX_EQ_F16:
case Opcode::V_CMPX_LE_F16:
@@ -1046,7 +1054,7 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
DecodeVectorGpr(vdst, inst.dst);
DecodeVectorGpr(vsrc1, inst.src1);
DecodeScalarSource(src0 + 256u, pc, inst.src0);
ApplyDppModifier(inst.src0, modifier);
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16;
@@ -1155,7 +1163,7 @@ void DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
DecodeVectorGpr(vsrc1, inst.src1);
DecodeScalarSource(src0 + 256u, pc, inst.src0);
inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
ApplyDppModifier(inst.src0, modifier);
ApplyDppModifier(inst.src0, modifier, code[word_index] & 0x1ffu);
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
inst.src_count = 2;
@@ -1171,6 +1179,7 @@ uint32_t NativeVop3SourceCount(Opcode opcode) {
case Opcode::V_MUL_I32_I24:
case Opcode::V_ADD_NC_U16:
case Opcode::V_SUB_NC_U16:
case Opcode::V_MUL_LO_U16:
case Opcode::V_MAX_U16:
case Opcode::V_MAX_I16:
case Opcode::V_MIN_U16:
@@ -1355,7 +1364,8 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
}
bool SupportsNativeVop3Clamp(Opcode opcode) {
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode);
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode) ||
opcode == Opcode::V_MAD_I16;
}
bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix,
@@ -1376,7 +1386,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
return opcode != Opcode::V_FMA_F16 && (clamp != 0u || omod != 0u);
}
if (IsNativeVop3I16TernaryOpcode(opcode)) {
return abs != 0u || clamp != 0u || omod != 0u || neg != 0u;
return abs != 0u || (clamp != 0u && !clamp_modifier) || omod != 0u || neg != 0u;
}
if (IsNativeVop3B16BinaryOpcode(opcode)) {
return abs != 0u || clamp != 0u || omod != 0u || neg != 0u;
@@ -1468,7 +1478,7 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
inst.pc = pc;
inst.family = Family::VOP2;
inst.opcode_id = opcode;
inst.opcode = LookupVop2Opcode(opcode);
inst.opcode = Detail::LookupOpcode(VOP2_OPS, opcode);
SetRawWords(inst, code, word_index, 1);
if (inst.opcode == Opcode::UNSUPPORTED) {
@@ -1510,6 +1520,7 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
return;
}
switch (src0) {
case 233u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
case 249u: DecodeVop1Sdwa(pc, code, word_index, opcode, vdst, inst); return;
case 250u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
default: break;
@@ -166,9 +166,9 @@ void Translator::V_CVT_PK_U8_F32(const Decoder::Instruction& inst) {
}
void Translator::V_PACK_B32_F16(const Decoder::Instruction& inst) {
const auto low = ReadF16LaneBits(inst.src0, false);
const auto low = Read16LaneBits(inst.src0, false);
const auto high =
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
ir.ShiftLeftLogical(Read16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
}
@@ -22,6 +22,18 @@ bool Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpco
return true;
}
bool Translator::V_MAD_I16(const Decoder::Instruction& inst) {
const auto lhs = ReadU16AsU32(inst.src0, true);
const auto rhs = ReadU16AsU32(inst.src1, true);
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, true));
if (inst.dst.clamp) {
result = IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {result, IR::Value(0xffff8000u)}));
result = IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {result, IR::Value(0x7fffu)}));
}
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
return true;
}
bool Translator::V_MED3_I16(const Decoder::Instruction& inst) {
const auto result = IR::U32(ir.Emit(
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
@@ -168,6 +180,21 @@ bool Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode
return true;
}
bool Translator::S_ASHR_I64(const Decoder::Instruction& inst) {
// Signed 64-bit sources sign-extend literals; generic B64 operands zero-extend them.
const auto source =
inst.src0.kind == Decoder::OperandKind::LiteralConstant
? ir.ConstructU64(
ReadU32(inst.src0),
IR::U32(IR::Value((inst.src0.value & 0x80000000u) ? 0xffffffffu : 0u)))
: ReadU64(inst.src0);
const auto result =
ir.Emit(IR::ValueOpcode::ShiftRightArithmetic64, {source, ReadU32(inst.src1)});
WriteOperand(inst.dst, result);
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
return true;
}
bool Translator::ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool negate_rhs, bool negate_result, bool update_scc) {
const auto lhs = ReadU32(inst.src0);
@@ -480,6 +507,18 @@ bool Translator::S_BITCMP_B32(const Decoder::Instruction& inst, bool expected) {
return true;
}
bool Translator::S_BITCMP_B64(const Decoder::Instruction& inst, bool expected) {
const auto value = ReadU32Pair(inst.src0);
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(63u)));
const auto word_bit = ir.BitwiseAnd(offset, IR::U32(IR::Value(31u)));
const auto word =
ir.Select(ir.ULessThan(offset, IR::U32(IR::Value(32u))), value[0], value[1]);
const auto bit =
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {word, word_bit, IR::Value(1u)}));
WriteCompareResult(inst.dst, ir.IEqual(bit, IR::U32(IR::Value(expected ? 1u : 0u))));
return true;
}
bool Translator::V_ALIGNBIT_B32(const Decoder::Instruction& inst) {
const auto hi = ReadU32(inst.src0);
const auto lo = ReadU32(inst.src1);
@@ -34,6 +34,7 @@ Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t
result.negate_hi = false;
result.absolute = false;
result.dpp_ctrl = 0;
result.dpp8 = false;
result.dpp_row_mask = 0xf;
result.dpp_bank_mask = 0xf;
result.explicit_sdwa_dst = false;
@@ -1034,6 +1035,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) {
case Decoder::Opcode::IMAGE_STORE:
case Decoder::Opcode::IMAGE_STORE_MIP: return IMAGE_STORE(inst);
case Decoder::Opcode::IMAGE_SAMPLE: return IMAGE_SAMPLE(inst);
case Decoder::Opcode::IMAGE_GATHER4_L:
case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
@@ -179,6 +179,7 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
case O::S_LSHR_B64:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical64, IR::Type::U64, false,
false, true);
case O::S_ASHR_I64: return S_ASHR_I64(inst);
case O::S_ANDN2_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseAnd32, true, false, true);
@@ -207,6 +208,8 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
case O::S_BFE_U64: return S_BFE_U64(inst);
case O::S_BITCMP0_B32: return S_BITCMP_B32(inst, false);
case O::S_BITCMP1_B32: return S_BITCMP_B32(inst, true);
case O::S_BITCMP0_B64: return S_BITCMP_B64(inst, false);
case O::S_BITCMP1_B64: return S_BITCMP_B64(inst, true);
case O::S_PACK_LL_B32_B16: return PackB16(inst, false, false);
case O::S_PACK_LH_B32_B16: return PackB16(inst, false, true);
case O::S_PACK_HH_B32_B16: return PackB16(inst, true, true);
@@ -9,6 +9,17 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
static IR::DppMoveFlags DppFlags(const Decoder::Operand& operand) {
return {
.control = operand.dpp_ctrl,
.row_mask = operand.dpp_row_mask,
.bank_mask = operand.dpp_bank_mask,
.fetch_inactive = operand.dpp_fetch_inactive,
.bound_control = operand.dpp_bound_ctrl,
.dpp8 = operand.dpp8,
};
}
const Decoder::Operand& Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
switch (index) {
case 0: return inst.src0;
@@ -30,6 +41,7 @@ Decoder::Operand Translator::DestinationOperand(const Decoder::Instruction& inst
continue;
}
destination.dpp = true;
destination.dpp8 = source.dpp8;
destination.dpp_ctrl = source.dpp_ctrl;
destination.dpp_row_mask = source.dpp_row_mask;
destination.dpp_bank_mask = source.dpp_bank_mask;
@@ -106,6 +118,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
result.dpp_fetch_inactive = false;
result.dpp_bound_ctrl = false;
result.dpp = false;
result.dpp8 = false;
return result;
}
@@ -167,14 +180,8 @@ IR::U32 Translator::ReadScalarCode(uint32_t code) {
IR::U32 Translator::ApplyBitSourceModifiers(const Decoder::Operand& operand, IR::U32 value) {
if (operand.dpp) {
const IR::DppMoveFlags flags {
.control = static_cast<uint16_t>(operand.dpp_ctrl),
.row_mask = static_cast<uint8_t>(operand.dpp_row_mask),
.bank_mask = static_cast<uint8_t>(operand.dpp_bank_mask),
.fetch_inactive = operand.dpp_fetch_inactive,
.bound_control = operand.dpp_bound_ctrl,
};
value = IR::U32(ir.Emit(IR::ValueOpcode::DppMoveU32, {value, ir.GetExec()}, flags));
value =
IR::U32(ir.Emit(IR::ValueOpcode::DppMoveU32, {value, ir.GetExec()}, DppFlags(operand)));
}
if (operand.sdwa_sel != 6u) {
uint32_t offset = 0;
@@ -301,15 +308,8 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
const auto reg = static_cast<IR::VectorReg>(operand.reg);
const auto old = ir.GetVectorReg(reg);
if (operand.dpp) {
const IR::DppMoveFlags flags {
.control = static_cast<uint16_t>(operand.dpp_ctrl),
.row_mask = static_cast<uint8_t>(operand.dpp_row_mask),
.bank_mask = static_cast<uint8_t>(operand.dpp_bank_mask),
.fetch_inactive = operand.dpp_fetch_inactive,
.bound_control = operand.dpp_bound_ctrl,
};
value = IR::U32(
ir.Emit(IR::ValueOpcode::DppUpdateU32, {value, old, ir.GetExec()}, flags));
value = IR::U32(ir.Emit(IR::ValueOpcode::DppUpdateU32, {value, old, ir.GetExec()},
DppFlags(operand)));
} else {
value = ir.Select(ir.GetExec(), value, old);
}
@@ -541,10 +541,7 @@ IR::U32 Translator::ReadU16LaneRaw(const Decoder::Operand& operand, bool high_la
IR::U32 Translator::ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane,
bool sign_extend) {
auto value = ReadU16LaneRaw(operand, high_lane);
if (high_lane ? operand.negate_hi : operand.negate) {
value = ir.BitwiseAnd(ir.ISub(IR::U32(IR::Value(0u)), value), IR::U32(IR::Value(0xffffu)));
}
auto value = Read16LaneBits(operand, high_lane);
if (sign_extend || operand.sdwa_sext) {
value = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {value, IR::Value(0u), IR::Value(16u)}));
@@ -556,12 +553,13 @@ IR::U32 Translator::ReadU16AsU32(const Decoder::Operand& operand, bool sign_exte
return ReadU16LaneAsU32(operand, false, sign_extend);
}
IR::U32 Translator::ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane) {
IR::U32 Translator::Read16LaneBits(const Decoder::Operand& operand, bool high_lane) {
auto value = ReadU16LaneRaw(operand, high_lane);
if (operand.absolute) {
value = ir.BitwiseAnd(value, IR::U32(IR::Value(0x7fffu)));
}
if (high_lane ? operand.negate_hi : operand.negate) {
// RDNA2 source NEG flips the sign bit, including packed integer operands.
value = ir.BitwiseXor(value, IR::U32(IR::Value(0x8000u)));
}
return value;
@@ -44,7 +44,7 @@ private:
IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane);
IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend);
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
IR::U32 Read16LaneBits(const Decoder::Operand& operand, bool high_lane);
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
IR::U32 ConditionBit(const Decoder::Operand& operand);
@@ -164,6 +164,7 @@ private:
bool FloatCube(const Decoder::Instruction& inst, uint32_t result_kind);
bool Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
bool Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
bool V_MAD_I16(const Decoder::Instruction& inst);
bool V_MED3_I16(const Decoder::Instruction& inst);
bool PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool arithmetic);
@@ -176,6 +177,7 @@ private:
bool negate_result);
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
bool reverse, bool mask_shift_count, bool update_scc);
bool S_ASHR_I64(const Decoder::Instruction& inst);
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool negate_rhs, bool negate_result, bool update_scc);
bool V_AND_OR_B32(const Decoder::Instruction& inst);
@@ -203,6 +205,7 @@ private:
bool V_BFE_U32(const Decoder::Instruction& inst, bool sign);
bool V_BFI_B32(const Decoder::Instruction& inst);
bool S_BITCMP_B32(const Decoder::Instruction& inst, bool expected);
bool S_BITCMP_B64(const Decoder::Instruction& inst, bool expected);
bool V_ALIGNBIT_B32(const Decoder::Instruction& inst);
bool V_ALIGNBYTE_B32(const Decoder::Instruction& inst);
bool V_LSHL_ADD_U32(const Decoder::Instruction& inst);
@@ -264,6 +264,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_CMPX_LE_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, true, true);
return true;
case O::V_CMP_NGT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, false);
return true;
case O::V_CMPX_NGT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, true);
return true;
@@ -273,6 +276,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_CMPX_NEQ_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, true, true);
return true;
case O::V_CMP_NLT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, false);
return true;
case O::V_CMPX_NLT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, true);
return true;
@@ -321,6 +327,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_ADD_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::IAdd32, false);
case O::V_SUB_NC_U16:
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
case O::V_MAD_I16: return V_MAD_I16(inst);
case O::V_MED3_I16: return V_MED3_I16(inst);
case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true);
case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true);
@@ -34,11 +34,12 @@ struct ImageOpcodeInfo {
};
struct DppMoveFlags {
uint16_t control = 0;
uint8_t row_mask = 0xf;
uint8_t bank_mask = 0xf;
uint32_t control : 24 = 0;
uint32_t row_mask : 4 = 0xf;
uint32_t bank_mask : 4 = 0xf;
bool fetch_inactive = false;
bool bound_control = false;
bool dpp8 = false;
};
static_assert(sizeof(DppMoveFlags) <= sizeof(uint64_t));
static_assert(std::is_trivially_copyable_v<DppMoveFlags>);
@@ -125,6 +125,7 @@ bool IsRuntimeUniformOp(ValueOpcode op) {
case ValueOpcode::ULessThan32:
case ValueOpcode::IEqual32:
case ValueOpcode::UGreaterThan32:
case ValueOpcode::SGreaterThanEqual32:
case ValueOpcode::INotEqual32:
case ValueOpcode::LogicalOr:
case ValueOpcode::LogicalAnd:
@@ -479,8 +480,6 @@ private:
return std::bit_cast<float>(static_cast<uint32_t>(bits));
}
static uint64_t Float32Bits(float value) { return std::bit_cast<uint32_t>(value); }
bool EvaluateWide(Value value, uint64_t& result) {
value = value.Resolve();
if (value.IsImmediate()) {
@@ -490,7 +489,7 @@ private:
case Type::U16: result = value.U16(); return true;
case Type::U32: result = value.U32(); return true;
case Type::U64: result = value.U64(); return true;
case Type::F32: result = Float32Bits(value.F32Value()); return true;
case Type::F32: result = std::bit_cast<uint32_t>(value.F32Value()); return true;
default: return false;
}
}
@@ -733,7 +732,7 @@ private:
return false;
case ValueOpcode::ConvertF32U32:
if (Arg(inst, 0, a)) {
result = Float32Bits(static_cast<float>(static_cast<uint32_t>(a)));
result = std::bit_cast<uint32_t>(static_cast<float>(static_cast<uint32_t>(a)));
return true;
}
return false;
@@ -750,13 +749,13 @@ private:
return false;
case ValueOpcode::FPMul32:
if (binary()) {
result = Float32Bits(Float32(a) * Float32(b));
result = std::bit_cast<uint32_t>(Float32(a) * Float32(b));
return true;
}
return false;
case ValueOpcode::FPTrunc32:
if (Arg(inst, 0, a)) {
result = Float32Bits(std::trunc(Float32(a)));
result = std::bit_cast<uint32_t>(std::trunc(Float32(a)));
return true;
}
return false;
@@ -932,6 +931,13 @@ private:
return true;
}
return false;
case ValueOpcode::SGreaterThanEqual32:
if (binary()) {
result = std::bit_cast<int32_t>(static_cast<uint32_t>(a)) >=
std::bit_cast<int32_t>(static_cast<uint32_t>(b));
return true;
}
return false;
case ValueOpcode::LogicalAnd:
if (binary()) {
result = (a != 0u) && (b != 0u);
+17 -27
View File
@@ -60,12 +60,12 @@ public:
: parameters(parameters_) {
builder.AddMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
void_type = Type(spv::OpTypeVoid);
uint_type = Type(spv::OpTypeInt, 32u, 0u);
int_type = Type(spv::OpTypeInt, 32u, 1u);
float_type = Type(spv::OpTypeFloat, 32u);
vec4_float_type = Type(spv::OpTypeVector, float_type, 4u);
function_type = Type(spv::OpTypeFunction, void_type);
void_type = builder.Type(spv::OpTypeVoid);
uint_type = builder.Type(spv::OpTypeInt, 32u, 0u);
int_type = builder.Type(spv::OpTypeInt, 32u, 1u);
float_type = builder.Type(spv::OpTypeFloat, 32u);
vec4_float_type = builder.Type(spv::OpTypeVector, float_type, 4u);
function_type = builder.Type(spv::OpTypeFunction, void_type);
per_vertex_type = builder.DecoratedType(
spv::OpTypeStruct,
@@ -76,12 +76,12 @@ public:
ptr_input_vec4_float = Pointer(spv::StorageClassInput, vec4_float_type);
ptr_output_vec4_float = Pointer(spv::StorageClassOutput, vec4_float_type);
if (model == spv::ExecutionModelTessellationControl) {
bool_type = Type(spv::OpTypeBool);
vec2_bool_type = Type(spv::OpTypeVector, bool_type, 2u);
vec2_float_type = Type(spv::OpTypeVector, float_type, 2u);
bool_type = builder.Type(spv::OpTypeBool);
vec2_bool_type = builder.Type(spv::OpTypeVector, bool_type, 2u);
vec2_float_type = builder.Type(spv::OpTypeVector, float_type, 2u);
ptr_output_float = Pointer(spv::StorageClassOutput, float_type);
} else {
vec3_float_type = Type(spv::OpTypeVector, float_type, 3u);
vec3_float_type = builder.Type(spv::OpTypeVector, float_type, 3u);
ptr_input_float = Pointer(spv::StorageClassInput, float_type);
}
}
@@ -161,8 +161,8 @@ public:
Store(Access(ptr_output_vec4_float, outputs[i], invocation), value);
}
Emit(spv::OpReturn);
Emit(spv::OpFunctionEnd);
builder.AddFunction(spv::OpReturn);
builder.AddFunction(spv::OpFunctionEnd);
return builder.Build();
}
@@ -184,27 +184,22 @@ public:
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
}
Emit(spv::OpReturn);
Emit(spv::OpFunctionEnd);
builder.AddFunction(spv::OpReturn);
builder.AddFunction(spv::OpFunctionEnd);
return builder.Build();
}
private:
template <typename... Args>
uint32_t Type(spv::Op opcode, Args... operands) {
return builder.Type(opcode, operands...);
}
uint32_t Constant(uint32_t type, uint32_t value) {
return builder.Constant(spv::OpConstant, type, value);
}
uint32_t Pointer(spv::StorageClass storage, uint32_t type) {
return Type(spv::OpTypePointer, storage, type);
return builder.Type(spv::OpTypePointer, storage, type);
}
uint32_t Array(uint32_t type, uint32_t size) {
return Type(spv::OpTypeArray, type, Uint(size));
return builder.Type(spv::OpTypeArray, type, Uint(size));
}
template <typename... Args>
@@ -221,11 +216,6 @@ private:
return id;
}
template <typename... Args>
void Emit(spv::Op opcode, Args... operands) {
builder.AddFunction(opcode, operands...);
}
template <typename... Args>
uint32_t Access(uint32_t pointer_type, uint32_t base, Args... indices) {
return Result(spv::OpAccessChain, pointer_type, base, indices...);
@@ -233,7 +223,7 @@ private:
uint32_t Load(uint32_t type, uint32_t pointer) { return Result(spv::OpLoad, type, pointer); }
void Store(uint32_t pointer, uint32_t value) { Emit(spv::OpStore, pointer, value); }
void Store(uint32_t pointer, uint32_t value) { builder.AddFunction(spv::OpStore, pointer, value); }
uint32_t Int(uint32_t value) { return Constant(int_type, value); }
+1 -5
View File
@@ -4,7 +4,6 @@
#include "common/common.h"
#include "common/emulatorConfig.h"
#include "common/logging/log.h"
#include "common/magicEnum.h"
#include "common/profiler.h"
#include "common/stringUtils.h"
#include "graphics/guest_gpu/gpu_defs.h"
@@ -722,6 +721,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
key.push_back(static_cast<uint32_t>(info.ps_depth_export_enable));
key.push_back(static_cast<uint32_t>(info.ps_sample_mask_export_enable));
key.push_back(static_cast<uint32_t>(info.ps_early_z));
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
key.insert(key.end(), std::begin(info.target_output_mode), std::end(info.target_output_mode));
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
uint32_t packed = 0;
@@ -1006,8 +1006,4 @@ void ShaderDbgDumpInputInfo(const ShaderComputeInputInfo& info) {
info.group_id[1] ? "true" : "false", info.group_id[2] ? "true" : "false");
}
bool ShaderAddressValid(uint64_t addr) {
return reinterpret_cast<const uint32_t*>(addr) != nullptr;
}
} // namespace Libs::Graphics
+1 -1
View File
@@ -175,6 +175,7 @@ struct ShaderPixelInputInfo {
bool ps_depth_export_enable = false;
bool ps_sample_mask_export_enable = false;
bool ps_sample_shading = false;
bool dual_source_blending = false;
bool ps_early_z = false;
bool ps_execute_on_noop = false;
ShaderStageRuntime stage;
@@ -303,7 +304,6 @@ void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data);
void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info);
void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info);
void ShaderDbgDumpInputInfo(const ShaderComputeInputInfo& info);
bool ShaderAddressValid(uint64_t addr);
} // namespace Libs::Graphics
+11 -9
View File
@@ -251,16 +251,15 @@ void FileDescriptors::CloseAll() {
void MountPoints::Mount(const std::filesystem::path& folder, const std::string& point) {
Common::LockGuard lock(m_mutex);
auto folder_str = Common::FixDirectorySlash(Common::PathToGenericString(folder));
auto point_str = Common::FixDirectorySlash(point);
Umount(point_str);
MountPair p;
p.dir = folder_str;
p.dir = folder;
p.point = point_str;
m_mount_pairs.push_back(p);
m_mount_pairs.push_back(std::move(p));
}
void MountPoints::Umount(const std::string& folder_or_point) {
@@ -270,7 +269,7 @@ void MountPoints::Umount(const std::string& folder_or_point) {
const auto it = std::find_if(
m_mount_pairs.begin(), m_mount_pairs.end(), [&folder_or_point_str](const MountPair& p) {
return Common::PathToGenericString(p.dir) == folder_or_point_str ||
return Common::FixDirectorySlash(Common::PathToGenericString(p.dir)) == folder_or_point_str ||
p.point == folder_or_point_str;
});
if (it != m_mount_pairs.end()) {
@@ -334,23 +333,26 @@ std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name)
// Match the entire guest path so a mount root works with or without a trailing slash.
const auto mounted_path = Common::FixDirectorySlash(mounted_name);
const auto it = std::find_if(
const auto it = std::find_if(
m_mount_pairs.begin(), m_mount_pairs.end(),
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
[&mounted_path](const MountPair& p) { return mounted_path.starts_with(p.point); });
if (it != m_mount_pairs.end()) {
const auto& p = *it;
auto rel_path = Common::RemoveFirst(Common::FixFilenameSlash(mounted_name), p.point.size());
while (Common::StartsWith(rel_path, '/')) {
while (rel_path.starts_with('/')) {
rel_path = Common::RemoveFirst(rel_path, 1);
}
const auto native_rel_path = Common::PathFromUtf8(rel_path);
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
if (HasWindowsForbiddenFilenameCharacter(rel_path)) {
::printf("FileSystem: Windows-incompatible guest filename: %s\n",
mounted_name.c_str());
}
return p.dir / rel_path;
return p.dir / native_rel_path;
#else
return ResolvePathIgnoringCase(p.dir / rel_path);
return ResolvePathIgnoringCase(p.dir / native_rel_path);
#endif
}
+10 -13
View File
@@ -2,7 +2,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/magicEnum.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "common/virtualMemory.h"
@@ -14,9 +13,11 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <bit>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <magic_enum.hpp>
#include <map>
#include <memory>
#include <mutex>
@@ -2324,8 +2325,8 @@ int32_t KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t le
"\t flags = 0x%08" PRIx32 "\n"
"\t name = %s\n"
"\t gpu_mode = %s\n",
in_addr, out_addr, len, Common::EnumName(mode).c_str(), static_cast<uint32_t>(flags), name,
Common::EnumName(gpu_mode).c_str());
in_addr, out_addr, len, magic_enum::enum_name(mode), static_cast<uint32_t>(flags), name,
magic_enum::enum_name(gpu_mode));
MapGpuRange(out_addr, len);
@@ -3041,8 +3042,8 @@ int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int f
"\t shared = %s\n"
"\t reason = %s\n",
in_addr, out_addr, static_cast<uint64_t>(direct_memory_start), len,
Common::EnumName(mode).c_str(), static_cast<uint32_t>(flags), alignment,
Common::EnumName(gpu_mode).c_str(), shared_backing ? "yes" : "no", shared_reason);
magic_enum::enum_name(mode), static_cast<uint32_t>(flags), alignment,
magic_enum::enum_name(gpu_mode), shared_backing ? "yes" : "no", shared_reason);
if (out_addr == 0) {
if (consumed_reservation) {
@@ -3305,7 +3306,7 @@ static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size) {
"\t reserve-fixed replace: backend unmap failed at 0x%016" PRIx64
", size=0x%016" PRIx64 ", type=%s\n",
chunk.range.start, chunk.range.size,
Common::EnumName(chunk.range.type).c_str());
magic_enum::enum_name(chunk.range.type));
if (!restore_chunks()) {
EXIT("reserve-fixed backend-unmap rollback failed\n");
}
@@ -3769,7 +3770,7 @@ int KYTY_SYSV_ABI KernelMprotect(const void* addr, size_t len, int prot) {
}
g_virtual_ranges->Protect(aligned_addr, aligned_len, prot);
LOGF("\t prot: %s -> %s\n", Common::EnumName(old_mode).c_str(), Common::EnumName(mode).c_str());
LOGF("\t prot: %s -> %s\n", magic_enum::enum_name(old_mode), magic_enum::enum_name(mode));
return OK;
}
@@ -3879,10 +3880,6 @@ static bool IsAligned(uint64_t value, uint64_t alignment) {
return alignment == 0 || (value & (alignment - 1u)) == 0;
}
static bool IsPowerOfTwo(uint64_t value) {
return value != 0 && (value & (value - 1u)) == 0;
}
static void MemoryPoolSubtractCommitted(uint64_t len) {
auto current = g_memory_pool_committed.load(std::memory_order_relaxed);
while (current != 0) {
@@ -3904,7 +3901,7 @@ int KYTY_SYSV_ABI KernelMemoryPoolExpand(int64_t search_start, int64_t search_en
if (search_start < 0 || search_end <= search_start || len == 0 ||
(len & (POOL_PAGE_SIZE - 1u)) != 0 || phys_addr_out == nullptr ||
(alignment != 0 &&
(!IsPowerOfTwo(alignment) || (alignment & (POOL_PAGE_SIZE - 1u)) != 0))) {
(!std::has_single_bit(alignment) || (alignment & (POOL_PAGE_SIZE - 1u)) != 0))) {
return KERNEL_ERROR_EINVAL;
}
if (static_cast<uint64_t>(search_end - search_start) < len) {
@@ -3953,7 +3950,7 @@ int KYTY_SYSV_ABI KernelMemoryPoolReserve(void* addr_in, size_t len, size_t alig
return KERNEL_ERROR_EINVAL;
}
if (alignment != 0 &&
(!IsPowerOfTwo(alignment) || !IsAligned(alignment, POOL_RESERVE_ALIGNMENT))) {
(!std::has_single_bit(alignment) || !IsAligned(alignment, POOL_RESERVE_ALIGNMENT))) {
return KERNEL_ERROR_EINVAL;
}
+2 -9
View File
@@ -149,7 +149,7 @@ static uint64_t KernelGetTscFrequencyNative() {
}
KernelReadTscNative();
Common::Thread::Sleep(1);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
KernelReadTscNative();
const auto host_start = Common::Timer::QueryPerformanceCounter();
@@ -993,7 +993,7 @@ static void FreeDetachedThreads(void* /*arg*/) {
auto* pthread_pool = g_pthread_context->GetPthreadPool();
while (true) {
Common::Thread::Sleep(10000);
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
pthread_pool->FreeDetachedThreads();
}
}
@@ -1202,13 +1202,6 @@ static int32_t GetDstSeconds() {
#endif
}
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
static void sec_to_timeval(KernelTimeval* ts, double sec) {
ts->tv_sec = static_cast<int64_t>(sec);
ts->tv_usec = static_cast<int64_t>((sec - static_cast<double>(ts->tv_sec)) * 1000000.0);
}
#endif
static bool GetPosixClockId(KernelClockid clock_id, clockid_t* out) {
EXIT_IF(out == nullptr);
@@ -167,6 +167,12 @@
<property name="text"><string>Enable readback</string></property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="checkBox_tessellation">
<property name="toolTip"><string>Draw tessellated geometry. Disabled by default; unsupported tessellation may cause crashes.</string></property>
<property name="text"><string>Enable tessellation support (experimental)</string></property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -275,13 +281,13 @@
<item>
<widget class="QCheckBox" name="checkBox_amd_cpu">
<property name="toolTip"><string>Patch CPU instructions that differ between AMD and the host CPU.</string></property>
<property name="text"><string>AMD CPU patch (EXPERIMENTAL)</string></property>
<property name="text"><string>AMD CPU patch (experimental)</string></property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_red_zone_protection">
<property name="toolTip"><string>Helps prevent crashes in some games on Windows</string></property>
<property name="text"><string>Windows SysV red zone crash protection (Experimental)</string></property>
<property name="text"><string>Windows SysV red zone crash protection (experimental)</string></property>
</widget>
</item>
</layout>
@@ -329,12 +335,4 @@
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>cancel_button</sender>
<signal>clicked()</signal>
<receiver>ConfigurationEditDialog</receiver>
<slot>reject()</slot>
</connection>
</connections>
</ui>
@@ -44,6 +44,28 @@
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QToolButton" name="refresh_button">
<property name="minimumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="global_settings_button">
<property name="minimumSize">
@@ -245,6 +267,14 @@
</layout>
</item>
</layout>
<action name="refresh_action">
<property name="text">
<string>Refresh game list</string>
</property>
<property name="toolTip">
<string>Rescan game folders for new games</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#000" d="M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>

After

Width:  |  Height:  |  Size: 232 B

-20
View File
@@ -1,20 +0,0 @@
#ifndef LAUNCHER_INCLUDE_COMMON_H_
#define LAUNCHER_INCLUDE_COMMON_H_
// IWYU pragma: begin_exports
#include <QArgument>
#include <QObject>
#include <cinttypes>
#include <cstdint>
#include <cstdio>
// IWYU pragma: end_exports
#define KYTY_QT_CLASS_NO_COPY(name) \
public: \
name(const name&) = delete; /* NOLINT(bugprone-macro-parentheses) */ \
name& operator=(const name&) = delete; /* NOLINT(bugprone-macro-parentheses) */ \
name(name&&) noexcept = delete; /* NOLINT(bugprone-macro-parentheses) */ \
name& operator=(name&&) noexcept = delete; /* NOLINT(bugprone-macro-parentheses) */
#endif /* LAUNCHER_INCLUDE_COMMON_H_ */
@@ -1,7 +1,6 @@
#ifndef COMPATIBILITY_DATABASE_H
#define COMPATIBILITY_DATABASE_H
#include "common.h"
#include "configuration.h"
#include <QMap>
@@ -15,7 +14,6 @@ struct CompatibilityEntry {
class CompatibilityDatabase: public QObject {
Q_OBJECT
KYTY_QT_CLASS_NO_COPY(CompatibilityDatabase);
public:
explicit CompatibilityDatabase(bool local, QObject* parent = nullptr);
+6 -4
View File
@@ -1,13 +1,13 @@
#ifndef LAUNCHER_INCLUDE_CONFIGURATION_H_
#define LAUNCHER_INCLUDE_CONFIGURATION_H_
#include "common.h"
#include "common/emulatorConfig.h"
#include <QByteArray>
#include <QChar>
#include <QMetaEnum>
#include <QMetaType>
#include <QObject>
#include <QSettings>
#include <QString>
#include <QStringList>
@@ -92,6 +92,7 @@ public:
int gpu_index = -1;
bool fullscreen_enabled = false;
bool readback_linear_images = false;
bool tessellation_enabled = false;
int vblank_frequency = 60;
int console_language = DEFAULT_CONSOLE_LANGUAGE;
bool vulkan_validation_enabled = false;
@@ -122,6 +123,7 @@ public:
gpu_index = other.gpu_index;
fullscreen_enabled = other.fullscreen_enabled;
readback_linear_images = other.readback_linear_images;
tessellation_enabled = other.tessellation_enabled;
vblank_frequency = other.vblank_frequency;
console_language = other.console_language;
vulkan_validation_enabled = other.vulkan_validation_enabled;
@@ -142,7 +144,7 @@ public:
host_input_mapping = other.host_input_mapping;
}
void CopyFrom(const Configuration& other) {
void CopyGameInfoFrom(const Configuration& other) {
name = other.name;
title_id = other.title_id;
gameVersion = other.gameVersion;
@@ -152,8 +154,6 @@ public:
custom_settings = other.custom_settings;
game_status = other.game_status;
game_comment = other.game_comment;
CopyEmulatorSettingsFrom(other);
elf = other.elf;
}
void WriteSettings(QSettings* s) const {
@@ -169,6 +169,7 @@ public:
KYTY_CFG_SET(gpu_index);
KYTY_CFG_SET(fullscreen_enabled);
KYTY_CFG_SET(readback_linear_images);
KYTY_CFG_SET(tessellation_enabled);
KYTY_CFG_SET(vblank_frequency);
KYTY_CFG_SET(console_language);
KYTY_CFG_SET(vulkan_validation_enabled);
@@ -210,6 +211,7 @@ public:
}
KYTY_CFG_GET(fullscreen_enabled);
KYTY_CFG_GET(readback_linear_images);
KYTY_CFG_GET(tessellation_enabled);
vblank_frequency = s->value("vblank_frequency", vblank_frequency).toInt();
console_language = s->value("console_language", console_language).toInt();
if (console_language < 0 || console_language > MAX_CONSOLE_LANGUAGE) {
@@ -1,8 +1,6 @@
#ifndef CONFIGURATION_EDIT_DIALOG_H
#define CONFIGURATION_EDIT_DIALOG_H
#include "common.h"
#include <QDialog>
#include <QString>
#include <QStringList>
@@ -22,7 +20,6 @@ class ConfigurationEditDialog;
class ConfigurationEditDialog: public QDialog {
Q_OBJECT
KYTY_QT_CLASS_NO_COPY(ConfigurationEditDialog);
public:
explicit ConfigurationEditDialog(Configuration& info, QWidget* parent = nullptr);
@@ -31,7 +28,6 @@ public:
static void WriteSettings(QSettings& s);
static void ReadSettings(QSettings& s);
void SetTitle(const QString& str);
void SetGameDirectories(const QStringList& dirs);
[[nodiscard]] QStringList GetGameDirectories() const;
@@ -55,8 +51,6 @@ protected:
/*slots:*/
void update_info();
void adjust_size();
void save();
void clear();
void add_game_directory();
-4
View File
@@ -1,8 +1,6 @@
#ifndef CONFIGURATION_ITEM_H
#define CONFIGURATION_ITEM_H
#include "common.h"
#include <QObject>
#include <QTreeWidgetItem>
@@ -23,8 +21,6 @@ public:
void Update();
bool operator<(const QTreeWidgetItem& other) const override;
KYTY_QT_CLASS_NO_COPY(ConfigurationItem);
Configuration& GetInfo() { return *m_info; }
[[nodiscard]] const Configuration& GetInfo() const { return *m_info; }
// void SetInfo(Configuration* info);
+6 -18
View File
@@ -1,7 +1,6 @@
#ifndef CONFIGURATION_LIST_WIDGET_H
#define CONFIGURATION_LIST_WIDGET_H
#include "common.h"
#include "configuration.h"
#include <QMap>
@@ -9,12 +8,13 @@
#include <QStringList>
#include <QWidget>
#include <memory>
class ConfigurationItem;
class CompatibilityDatabase;
class QEvent;
class QTreeWidgetItem;
class QPoint;
class MainDialog;
namespace Ui {
class ConfigurationListWidget;
@@ -22,27 +22,19 @@ class ConfigurationListWidget;
class ConfigurationListWidget: public QWidget {
Q_OBJECT
KYTY_QT_CLASS_NO_COPY(ConfigurationListWidget);
public:
explicit ConfigurationListWidget(QWidget* parent = nullptr);
~ConfigurationListWidget() override;
void SetRunEnabled(bool flag) { m_run_enabled = flag; }
[[nodiscard]] bool IsRunEnabled() const { return m_run_enabled; }
void SetRunEnabled(bool flag) { m_run_enabled = flag; }
[[nodiscard]] const ConfigurationItem* GetSelectedItem() const { return m_selected_item; }
ConfigurationItem* GetSelectedItem() { return m_selected_item; }
void SetMainDialog(MainDialog* main_dialog) { m_main_dialog = main_dialog; }
MainDialog* GetMainDialog() { return m_main_dialog; }
[[nodiscard]] const QString& GetSettingsFile() const { return m_settings_file; }
[[nodiscard]] const QStringList& GetGameDirectories() const { return m_game_dirs; }
[[nodiscard]] const QStringList& GetHostInputMapping() const {
return m_global_info.host_input_mapping;
}
[[nodiscard]] bool CanViewSelectedTrophies() const;
[[nodiscard]] const QString& GetSettingsFile() const { return m_settings_file; }
[[nodiscard]] std::unique_ptr<Configuration>
CreateConfiguration(const ConfigurationItem& item) const;
bool EnsureGameDirectory();
void ScanGameDirectory();
@@ -66,8 +58,6 @@ protected slots:
void delete_configuartion();
void edit_global_settings();
void edit_input_mapping();
void run_configuration();
void list_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
void list_itemDoubleClicked(QTreeWidgetItem* witem, int column);
void show_context_menu(const QPoint& pos);
void open_game_folder();
@@ -75,7 +65,6 @@ protected slots:
void filter_configurations(const QString& text);
private:
void ClearCustomSettings(ConfigurationItem* item);
void SelectItem(QTreeWidgetItem* witem);
void ApplyCompatibility();
void UpdateToolbarIcons();
@@ -84,7 +73,6 @@ private:
ConfigurationItem* m_selected_item = nullptr;
bool m_run_enabled = true;
Ui::ConfigurationListWidget* m_ui = nullptr;
MainDialog* m_main_dialog = nullptr;
QString m_settings_file;
QStringList m_game_dirs;
Configuration m_global_info;
-4
View File
@@ -1,8 +1,6 @@
#ifndef MAIN_DIALOG_H
#define MAIN_DIALOG_H
#include "common.h"
#include <QDialog>
#include <QString>
@@ -15,11 +13,9 @@ class QResizeEvent;
class Configuration;
class MainDialog: public QDialog {
Q_OBJECT
KYTY_QT_CLASS_NO_COPY(MainDialog);
signals:
void Start();
void Quit();
void Resize();
public:
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef MANDATORY_LINEEDIT_H
#define MANDATORY_LINEEDIT_H
#include "common.h"
#include <QLineEdit>
#include <QObject>
#include <QString>
class QPaintEvent;
-4
View File
@@ -1,8 +1,6 @@
#ifndef PATCHES_DIALOG_H
#define PATCHES_DIALOG_H
#include "common.h"
#include <QDialog>
#include <QString>
@@ -16,8 +14,6 @@ public:
explicit PatchesDialog(const Configuration& game, QWidget* parent = nullptr);
~PatchesDialog() override = default;
KYTY_QT_CLASS_NO_COPY(PatchesDialog);
[[nodiscard]] static bool IsSupportedTitleId(const QString& title_id);
[[nodiscard]] static QString PatchPlanPath(const QString& title_id);
@@ -1,8 +1,6 @@
#ifndef TROPHY_VIEWER_DIALOG_H
#define TROPHY_VIEWER_DIALOG_H
#include "common.h"
#include <QDialog>
class QTabWidget;
@@ -10,8 +8,6 @@ class QWidget;
class Configuration;
class TrophyViewerDialog: public QDialog {
KYTY_QT_CLASS_NO_COPY(TrophyViewerDialog);
public:
explicit TrophyViewerDialog(QWidget* parent = nullptr);
+1
View File
@@ -3,6 +3,7 @@
<file alias="edit-configuration.svg">icons/edit-configuration.svg</file>
<file alias="global-settings.svg">icons/global-settings.svg</file>
<file alias="input-mapping.svg">icons/input-mapping.svg</file>
<file alias="refresh.svg">icons/refresh.svg</file>
<file alias="remove-configuration.svg">icons/remove-configuration.svg</file>
</qresource>
<qresource prefix="/styles">
+5 -14
View File
@@ -108,6 +108,7 @@ ConfigurationEditDialog::ConfigurationEditDialog(Configuration& info, QWidget* p
InitGameDirectories();
connect(m_ui->ok_button, &QPushButton::clicked, this, &ConfigurationEditDialog::save);
connect(m_ui->cancel_button, &QPushButton::clicked, this, &QDialog::reject);
connect(m_ui->clear_button, &QPushButton::clicked, this, &ConfigurationEditDialog::clear);
connect(m_ui->comboBox_shader_log_direction, &QComboBox::currentTextChanged, this,
[this](const QString& text) {
@@ -213,7 +214,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
#endif
QVulkanInstance instance;
instance.setApiVersion(QVersionNumber(1, 3, 0));
#if !defined(__APPLE__)
#if !defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
instance.setFlags(QVulkanInstance::NoPortabilityDrivers);
#endif
if (instance.create()) {
@@ -229,6 +230,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
: 0);
m_ui->checkBox_fullscreen->setChecked(info.fullscreen_enabled);
m_ui->checkBox_readback->setChecked(info.readback_linear_images);
m_ui->checkBox_tessellation->setChecked(info.tessellation_enabled);
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
m_ui->comboBox_console_language->clear();
m_ui->comboBox_console_language->addItems(CONSOLE_LANGUAGE_NAMES);
@@ -306,10 +308,6 @@ void ConfigurationEditDialog::InitGameDirectories() {
update_game_directory_buttons();
}
void ConfigurationEditDialog::SetTitle(const QString& str) {
setWindowTitle(str);
}
void ConfigurationEditDialog::SetGameDirectories(const QStringList& dirs) {
m_show_game_dirs = true;
m_game_dirs_list->clear();
@@ -378,6 +376,7 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
info.gpu_index = ui.comboBox_gpu->currentIndex() - 1;
info.fullscreen_enabled = ui.checkBox_fullscreen->isChecked();
info.readback_linear_images = ui.checkBox_readback->isChecked();
info.tessellation_enabled = ui.checkBox_tessellation->isChecked();
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
info.console_language = ui.comboBox_console_language->currentIndex();
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
@@ -400,14 +399,6 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
info.profiler_enabled = ui.checkBox_profiler->isChecked();
}
void ConfigurationEditDialog::update_info() {
UpdateInfo(m_info, *m_ui);
}
void ConfigurationEditDialog::adjust_size() {
this->adjustSize();
}
void ConfigurationEditDialog::save() {
if (MandatoryLineEdit::FindEmpty(this)) {
QMessageBox::critical(this, tr("Save failed"), tr("Please fill all mandatory fields"));
@@ -427,7 +418,7 @@ void ConfigurationEditDialog::save() {
return;
}
update_info();
UpdateInfo(m_info, *m_ui);
emit accept();
}
+3 -7
View File
@@ -77,10 +77,6 @@ void SetStatus(QComboBox* combo, Configuration::GameStatus status) {
combo->setCurrentIndex(index >= 0 ? index : 0);
}
QIcon StandardIcon(QStyle::StandardPixmap icon) {
return QApplication::style()->standardIcon(icon);
}
QString GetPathText(const Configuration& info) {
return !info.game_path.isEmpty() ? info.game_path : info.basedir;
}
@@ -278,11 +274,11 @@ void ConfigurationItem::UpdateIcon() {
}
if (m_running) {
setIcon(NameColumn, StandardIcon(QStyle::SP_MediaPlay));
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_MediaPlay));
} else if (m_info->custom_settings) {
setIcon(NameColumn, StandardIcon(QStyle::SP_FileIcon));
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_FileIcon));
} else {
setIcon(NameColumn, StandardIcon(QStyle::SP_ComputerIcon));
setIcon(NameColumn, QApplication::style()->standardIcon(QStyle::SP_ComputerIcon));
}
}
+106 -61
View File
@@ -1,6 +1,5 @@
#include "configurationListWidget.h"
#include "common.h"
#include "compatibilityDatabase.h"
#include "configuration.h"
#include "configurationEditDialog.h"
@@ -14,6 +13,7 @@
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QCoreApplication>
#include <QCursor>
@@ -27,11 +27,13 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QKeySequence>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QPalette>
#include <QPointer>
#include <QPushButton>
#include <QRegularExpression>
#include <QSet>
@@ -202,6 +204,10 @@ ConfigurationListWidget::ConfigurationListWidget(QWidget* parent)
m_ui->setupUi(this);
ConfigureGameList(m_ui);
m_ui->refresh_action->setShortcuts(QKeySequence::Refresh);
m_ui->refresh_button->setDefaultAction(m_ui->refresh_action);
addAction(m_ui->refresh_action);
UpdateToolbarIcons();
m_ui->global_settings_button->setToolTip(tr("Edit global settings and game folders"));
m_ui->input_mapping_button->setToolTip(tr("Edit global input mapping"));
@@ -222,7 +228,10 @@ ConfigurationListWidget::ConfigurationListWidget(QWidget* parent)
m_ui->cfgs_list->setColumnWidth(GAME_PATH_COLUMN, 320);
m_ui->cfgs_list->setColumnWidth(GAME_STATUS_COLUMN, 150);
m_ui->cfgs_list->setColumnWidth(GAME_COMMENT_COLUMN, 240);
m_ui->cfgs_list->sortItems(GAME_NAME_COLUMN, Qt::AscendingOrder);
connect(m_ui->refresh_action, &QAction::triggered, this,
&ConfigurationListWidget::ScanGameDirectory);
connect(m_ui->global_settings_button, &QToolButton::clicked, this,
&ConfigurationListWidget::edit_global_settings);
connect(m_ui->input_mapping_button, &QToolButton::clicked, this,
@@ -232,7 +241,7 @@ ConfigurationListWidget::ConfigurationListWidget(QWidget* parent)
connect(m_ui->delete_button, &QToolButton::clicked, this,
&ConfigurationListWidget::delete_configuartion);
connect(m_ui->cfgs_list, &QTreeWidget::currentItemChanged, this,
&ConfigurationListWidget::list_currentItemChanged);
&ConfigurationListWidget::SelectItem);
connect(m_ui->cfgs_list, &QTreeWidget::itemDoubleClicked, this,
&ConfigurationListWidget::list_itemDoubleClicked);
connect(m_ui->cfgs_list, &QTreeWidget::customContextMenuRequested, this,
@@ -277,6 +286,8 @@ void ConfigurationListWidget::UpdateToolbarIcons() {
button->setIcon(QIcon(pixmap));
};
set_icon(m_ui->refresh_button, QStringLiteral(":/icons/refresh.svg"));
m_ui->refresh_action->setIcon(m_ui->refresh_button->icon());
set_icon(m_ui->global_settings_button, QStringLiteral(":/icons/global-settings.svg"));
set_icon(m_ui->input_mapping_button, QStringLiteral(":/icons/input-mapping.svg"));
set_icon(m_ui->edit_button, QStringLiteral(":/icons/edit-configuration.svg"));
@@ -399,10 +410,17 @@ void ConfigurationListWidget::ApplyCompatibility() {
}
}
static Configuration* CloneConfiguration(const Configuration& source) {
auto* ret = new Configuration;
ret->CopyFrom(source);
return ret;
std::unique_ptr<Configuration>
ConfigurationListWidget::CreateConfiguration(const ConfigurationItem& item) const {
auto info = std::make_unique<Configuration>();
const auto* custom = m_custom_infos.value(item.GetInfo().game_path);
info->CopyGameInfoFrom(item.GetInfo());
info->CopyEmulatorSettingsFrom(custom != nullptr ? *custom : m_global_info);
if (custom != nullptr && !custom->elf.isEmpty()) {
info->elf = custom->elf;
}
info->host_input_mapping = m_global_info.host_input_mapping;
return info;
}
struct GameMetadata {
@@ -517,9 +535,6 @@ static void SetGameFiles(Configuration& info, const QString& game_dir, const QSt
if (info.name.isEmpty()) {
info.name = game.dirName();
}
if (info.elf.isEmpty()) {
info.elf = QStringLiteral("eboot.bin");
}
}
static Configuration* FindCustomInfo(QMap<QString, Configuration*>* custom_infos,
@@ -565,11 +580,32 @@ bool ConfigurationListWidget::HasValidGameDirectory() const {
}
void ConfigurationListWidget::ScanGameDirectory() {
// Commit an active inline editor before destroying its row or replacing its data.
if (auto* focused = QApplication::focusWidget();
focused != nullptr && m_ui->cfgs_list->isAncestorOf(focused)) {
m_ui->cfgs_list->setFocus();
}
const auto selected_path =
m_selected_item != nullptr ? m_selected_item->GetInfo().game_path : QString();
const bool sorting_enabled = m_ui->cfgs_list->isSortingEnabled();
const int sort_column = m_ui->cfgs_list->sortColumn();
const auto sort_order = m_ui->cfgs_list->header()->sortIndicatorOrder();
const QSignalBlocker blocker(m_ui->cfgs_list);
m_ui->cfgs_list->setSortingEnabled(false);
m_selected_item = nullptr;
m_ui->cfgs_list->clear();
m_ui->cfgs_list->SetBackgroundImage({});
m_ui->edit_button->setEnabled(false);
m_ui->delete_button->setEnabled(false);
// MainDialog tracks the running item, so keep its identity even if its
// directory is no longer present. Other rows can be rebuilt from disk.
QMap<QString, ConfigurationItem*> running_items;
for (int index = m_ui->cfgs_list->topLevelItemCount() - 1; index >= 0; index--) {
auto* item = static_cast<ConfigurationItem*>(m_ui->cfgs_list->topLevelItem(index));
if (item->IsRunning()) {
running_items.insert(PathKey(item->GetInfo().game_path), item);
} else {
delete item;
}
}
const QString eboot_name = QStringLiteral("eboot.bin");
QSet<QString> found_games;
@@ -602,16 +638,9 @@ void ConfigurationListWidget::ScanGameDirectory() {
auto metadata = GetGameMetadata(
game_dir.filePath(QStringLiteral("sce_sys/param.json")), game_dir.dirName());
auto info = std::make_unique<Configuration>();
auto* custom = FindCustomInfo(&m_custom_infos, game_path, legacy_game_path);
if (custom != nullptr) {
info->CopyFrom(*custom);
info->custom_settings = true;
} else {
info->CopyEmulatorSettingsFrom(m_global_info);
info->custom_settings = false;
}
auto info = std::make_unique<Configuration>();
info->custom_settings =
FindCustomInfo(&m_custom_infos, game_path, legacy_game_path) != nullptr;
SetGameFiles(*info, game_dir.absolutePath(), game_path, metadata);
const auto* compatibility = m_compatibility->Find(info->title_id);
if (compatibility != nullptr) {
@@ -619,10 +648,20 @@ void ConfigurationListWidget::ScanGameDirectory() {
info->game_comment = compatibility->comment;
}
if (auto* item = running_items.value(game_key); item != nullptr) {
// Refresh metadata without losing the running row's identity.
const QSignalBlocker status_blocker(item->GetStatusCombo());
item->GetInfo().CopyGameInfoFrom(*info);
item->Update();
item->SetCompatibilityEditable(m_compatibility->IsLocal() &&
!item->GetInfo().title_id.trimmed().isEmpty());
continue;
}
auto* item = new ConfigurationItem(std::move(info), m_ui->cfgs_list);
item->SetCompatibilityEditable(m_compatibility->IsLocal() &&
!item->GetInfo().title_id.trimmed().isEmpty());
connect(item->GetStatusCombo(), &QComboBox::currentIndexChanged, this,
connect(item->GetStatusCombo(), &QComboBox::currentIndexChanged, item,
[this, item](int /*index*/) {
if (!m_compatibility->IsLocal()) {
return;
@@ -639,7 +678,7 @@ void ConfigurationListWidget::ScanGameDirectory() {
m_ui->cfgs_list->setCurrentItem(item);
SelectItem(item);
});
connect(item->GetCommentEdit(), &QLineEdit::editingFinished, this, [this, item]() {
connect(item->GetCommentEdit(), &QLineEdit::editingFinished, item, [this, item]() {
if (!m_compatibility->IsLocal()) {
return;
}
@@ -665,8 +704,22 @@ void ConfigurationListWidget::ScanGameDirectory() {
}
}
m_ui->cfgs_list->sortItems(GAME_NAME_COLUMN, Qt::AscendingOrder);
m_ui->cfgs_list->setSortingEnabled(sorting_enabled);
if (sorting_enabled) {
m_ui->cfgs_list->sortItems(sort_column, sort_order);
}
filter_configurations(m_ui->search_line_edit->text());
ConfigurationItem* selected_item = nullptr;
for (int index = 0; index < m_ui->cfgs_list->topLevelItemCount(); index++) {
auto* item = static_cast<ConfigurationItem*>(m_ui->cfgs_list->topLevelItem(index));
if (!item->isHidden() && item->GetInfo().game_path == selected_path) {
selected_item = item;
break;
}
}
m_ui->cfgs_list->setCurrentItem(selected_item);
SelectItem(selected_item);
}
void ConfigurationListWidget::edit_configuration() {
@@ -675,16 +728,19 @@ void ConfigurationListWidget::edit_configuration() {
return;
}
ConfigurationEditDialog dlg(item->GetInfo(), this);
dlg.SetTitle(tr("Edit game settings"));
auto info = CreateConfiguration(*item);
ConfigurationEditDialog dlg(*info, this);
dlg.setWindowTitle(tr("Edit game settings"));
if (dlg.exec() == QDialog::Accepted) {
info->custom_settings = true;
item->GetInfo().custom_settings = true;
auto game_path = item->GetInfo().game_path;
auto game_path = info->game_path;
delete m_custom_infos.take(game_path);
m_custom_infos.insert(game_path, CloneConfiguration(item->GetInfo()));
m_custom_infos.insert(game_path, info.release());
WriteSettings();
ScanGameDirectory();
item->Update();
SelectItem(item);
}
}
@@ -696,9 +752,11 @@ void ConfigurationListWidget::delete_configuartion() {
if (QMessageBox::Yes == QMessageBox::question(this, tr("Clear custom settings"),
tr("Do you want to clear custom settings?"))) {
ClearCustomSettings(item);
delete m_custom_infos.take(item->GetInfo().game_path);
item->GetInfo().custom_settings = false;
WriteSettings();
ScanGameDirectory();
item->Update();
SelectItem(item);
}
}
@@ -708,14 +766,18 @@ void ConfigurationListWidget::edit_global_settings() {
info.name = tr("Global settings");
ConfigurationEditDialog dlg(info, this);
dlg.SetTitle(tr("Global settings"));
dlg.setWindowTitle(tr("Global settings"));
dlg.SetGameDirectories(m_game_dirs);
if (dlg.exec() == QDialog::Accepted) {
m_global_info.CopyEmulatorSettingsFrom(info);
m_game_dirs = NormalizeGameDirectories(dlg.GetGameDirectories());
const auto game_dirs = NormalizeGameDirectories(dlg.GetGameDirectories());
const bool game_dirs_changed = game_dirs != m_game_dirs;
m_game_dirs = game_dirs;
WriteSettings();
ScanGameDirectory();
if (game_dirs_changed) {
ScanGameDirectory();
}
}
}
@@ -727,19 +789,6 @@ void ConfigurationListWidget::edit_input_mapping() {
}
}
void ConfigurationListWidget::ClearCustomSettings(ConfigurationItem* item) {
delete m_custom_infos.take(item->GetInfo().game_path);
}
void ConfigurationListWidget::run_configuration() {
emit Run();
}
bool ConfigurationListWidget::CanViewSelectedTrophies() const {
return m_selected_item != nullptr &&
TrophyViewerDialog::HasTrophyData(&m_selected_item->GetInfo());
}
void ConfigurationListWidget::ViewTrophies() {
auto* current = static_cast<ConfigurationItem*>(m_ui->cfgs_list->currentItem());
auto* item = current != nullptr ? current : m_selected_item;
@@ -852,11 +901,6 @@ void ConfigurationListWidget::SelectItem(QTreeWidgetItem* witem) {
emit Select();
}
void ConfigurationListWidget::list_currentItemChanged(QTreeWidgetItem* current,
QTreeWidgetItem* /*previous*/) {
SelectItem(current);
}
void ConfigurationListWidget::list_itemDoubleClicked(QTreeWidgetItem* witem, int /*column*/) {
SelectItem(witem);
if (m_run_enabled) {
@@ -877,7 +921,7 @@ void ConfigurationListWidget::show_context_menu(const QPoint& pos) {
const bool has_trophy_data =
item != nullptr && TrophyViewerDialog::HasTrophyData(&item->GetInfo());
QAction* action_run = menu.addAction(tr("Run"), this, SLOT(run_configuration()));
QAction* action_run = menu.addAction(tr("Run"), this, SIGNAL(Run()));
QAction* action_open_folder =
menu.addAction(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Open game folder"), this,
SLOT(open_game_folder()));
@@ -886,12 +930,13 @@ void ConfigurationListWidget::show_context_menu(const QPoint& pos) {
connect(action_view_trophies, &QAction::triggered, this,
&ConfigurationListWidget::ViewTrophies);
QAction* action_patches = menu.addAction(tr("Cheats (experimental)..."));
connect(action_patches, &QAction::triggered, this, [this, item]() {
if (item != nullptr) {
auto* dialog = new PatchesDialog(item->GetInfo(), this);
dialog->show();
}
});
connect(action_patches, &QAction::triggered, this,
[this, item = QPointer<ConfigurationItem>(item)]() {
if (item != nullptr) {
auto* dialog = new PatchesDialog(item->GetInfo(), this);
dialog->show();
}
});
action_patches->setVisible(item != nullptr &&
PatchesDialog::IsSupportedTitleId(item->GetInfo().title_id));
QAction* action_remove_save_data =
+4 -4
View File
@@ -90,6 +90,7 @@ QString KeyName(const QKeyEvent& event) {
}
switch (key) {
case Qt::Key_Space: return QStringLiteral("Space");
case Qt::Key_Return:
case Qt::Key_Enter: return QStringLiteral("Return");
case Qt::Key_Backspace: return QStringLiteral("Backspace");
@@ -132,8 +133,7 @@ public:
auto* layout = new QVBoxLayout(this);
m_label = new QLabel(
tr("Press a key or mouse button.\nSpace, F1, F7, and F11 are reserved; Esc cancels."),
this);
tr("Press a key or mouse button.\nF1, F7, and F11 are reserved; Esc cancels."), this);
m_label->setAlignment(Qt::AlignCenter);
layout->addWidget(m_label);
}
@@ -149,8 +149,8 @@ protected:
reject();
return;
}
if (event->key() == Qt::Key_Space || event->key() == Qt::Key_F1 ||
event->key() == Qt::Key_F7 || event->key() == Qt::Key_F11) {
if (event->key() == Qt::Key_F1 || event->key() == Qt::Key_F7 ||
event->key() == Qt::Key_F11) {
m_label->setText(tr("That key is reserved by the emulator."));
return;
}
-4
View File
@@ -2,8 +2,6 @@
#include "launcherTheme.h"
#include <QApplication>
#include <QArgument>
#include <QObject>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
@@ -11,8 +9,6 @@ int main(int argc, char* argv[]) {
MainDialog w;
QObject::connect(&a, &QApplication::aboutToQuit, &w, &MainDialog::Quit);
w.emit Start();
w.show();
+9 -21
View File
@@ -1,6 +1,5 @@
#include "mainDialog.h"
#include "common.h"
#include "configuration.h"
#include "configurationItem.h"
#include "configurationListWidget.h"
@@ -17,6 +16,7 @@
#include <QIODevice>
#include <QLabel>
#include <QMessageBox>
#include <QObject>
#include <QPointer>
#include <QProcess>
#include <QRadioButton>
@@ -27,6 +27,8 @@
#include <QVariant>
#include <QtCore>
#include <cstdint>
#include "ui_main_dialog.h"
#if defined(_WIN32)
@@ -58,20 +60,8 @@ constexpr char SETTINGS_MAIN_DIALOG[] = "MainDialog";
constexpr char SETTINGS_MAIN_LAST_GEOMETRY[] = "geometry";
constexpr char SETTINGS_CHECK_UPDATES[] = "check_updates_on_startup";
class DetachableProcess: public QProcess {
Q_OBJECT;
public:
explicit DetachableProcess(QObject* parent = nullptr): QProcess(parent) {}
void Detach() {
this->waitForStarted();
setProcessState(QProcess::NotRunning);
}
};
class MainDialogPrivate: public QObject {
Q_OBJECT
KYTY_QT_CLASS_NO_COPY(MainDialogPrivate);
public:
explicit MainDialogPrivate(QObject* parent = nullptr): QObject(parent) {}
@@ -99,7 +89,7 @@ private:
UpdateChecker* m_update_checker = nullptr;
QString m_interpreter;
/*DetachableProcess*/ QProcess m_process;
QProcess m_process;
QPointer<ConfigurationItem> m_running_item;
};
@@ -121,7 +111,6 @@ void MainDialogPrivate::Setup(MainDialog* main_dialog) {
m_main_dialog = main_dialog;
m_update_checker = new UpdateChecker(main_dialog);
m_ui->widget->SetMainDialog(main_dialog);
m_ui->check_updates_on_startup->setChecked(g_check_updates_on_startup);
m_ui->check_updates_link->setVisible(UpdateChecker::IsSupported());
m_ui->check_updates_on_startup->setVisible(UpdateChecker::IsSupported());
@@ -154,8 +143,6 @@ void MainDialogPrivate::Setup(MainDialog* main_dialog) {
Update();
});
// connect(main_dialog, &MainDialog::Quit, [=]() { m_process.Detach(); });
m_ui->label_settings_file->setText(tr("Settings file: ") + m_ui->widget->GetSettingsFile());
m_main_dialog->restoreGeometry(g_last_geometry);
@@ -239,6 +226,9 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
args << "--fullscreen";
}
args << "--readback-linear-images" << BoolArg(info.readback_linear_images);
if (info.tessellation_enabled) {
args << "--tessellation";
}
args << "--vblank-frequency" << QString::number(info.vblank_frequency);
args << "--console-language" << QString::number(info.console_language);
args << "--vulkan-validation" << BoolArg(info.vulkan_validation_enabled);
@@ -503,10 +493,8 @@ void MainDialogPrivate::Run() {
m_running_item->SetRunning(true);
Configuration info;
info.CopyFrom(m_running_item->GetInfo());
info.host_input_mapping = m_ui->widget->GetHostInputMapping();
m_main_dialog->RunInterpreter(&m_process, info);
auto info = m_ui->widget->CreateConfiguration(*m_running_item);
m_main_dialog->RunInterpreter(&m_process, *info);
Update();
}
+8 -4
View File
@@ -3,6 +3,7 @@
#include "configuration.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
@@ -18,7 +19,7 @@
PatchesDialog::PatchesDialog(const Configuration& game, QWidget* parent)
: QDialog(parent), m_title_id(game.title_id.trimmed().toUpper()) {
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(tr("Cheats (Experimental) - %1").arg(game.name));
setWindowTitle(tr("Cheats (experimental) - %1").arg(game.name));
resize(640, 480);
auto* layout = new QVBoxLayout(this);
@@ -129,8 +130,11 @@ void PatchesDialog::Save() {
document.setObject(root);
QSaveFile output(path);
if (output.open(QIODevice::WriteOnly) && output.write(document.toJson()) >= 0 &&
output.commit()) {
m_status->setText(tr("Cheat selection saved."));
if (!output.open(QIODevice::WriteOnly) || output.write(document.toJson()) < 0 ||
!output.commit()) {
qWarning() << "Could not save cheat file:" << output.errorString();
m_status->setText(tr("Could not save cheat file: %1").arg(output.errorString()));
return;
}
m_status->setText(tr("Cheat selection saved."));
}
+12 -25
View File
@@ -20,6 +20,7 @@
#include <QLabel>
#include <QMap>
#include <QMessageBox>
#include <QObject>
#include <QPixmap>
#include <QRegularExpression>
#include <QSet>
@@ -32,8 +33,9 @@
#include <QVBoxLayout>
#include <QVariant>
#include <QWidget>
#include <QtEndian>
#include <limits>
#include <utility>
namespace {
@@ -83,21 +85,6 @@ static bool CanRead(const QByteArray& data, qsizetype offset, qsizetype size) {
return offset >= 0 && size >= 0 && offset <= data.size() && size <= data.size() - offset;
}
static quint32 ReadBe32(const QByteArray& data, qsizetype offset) {
const auto* p = reinterpret_cast<const uchar*>(data.constData() + offset);
return (static_cast<quint32>(p[0]) << 24u) | (static_cast<quint32>(p[1]) << 16u) |
(static_cast<quint32>(p[2]) << 8u) | static_cast<quint32>(p[3]);
}
static quint64 ReadBe64(const QByteArray& data, qsizetype offset) {
return (static_cast<quint64>(ReadBe32(data, offset)) << 32u) |
static_cast<quint64>(ReadBe32(data, offset + 4));
}
static bool FitsQSizeType(quint64 value) {
return value <= static_cast<quint64>(std::numeric_limits<qsizetype>::max());
}
static QString ReadFixedString(const QByteArray& data, qsizetype offset, qsizetype max_size) {
if (!CanRead(data, offset, max_size)) {
return {};
@@ -125,33 +112,33 @@ static bool ReadUcp(const QString& file_name, QMap<QString, QByteArray>& files,
return false;
}
const auto magic = ReadBe32(data, 0x00);
const auto magic = qFromBigEndian<quint32>(data.constData() + 0x00);
if (magic != UCP_MAGIC) {
error = QObject::tr("%1 has an invalid trophy package magic.")
.arg(QFileInfo(file_name).fileName());
return false;
}
const auto version = ReadBe32(data, 0x04);
const auto version = qFromBigEndian<quint32>(data.constData() + 0x04);
if (version != UCP_VERSION) {
error = QObject::tr("%1 uses unsupported trophy package version %2.")
.arg(QFileInfo(file_name).fileName(), QString::number(version));
return false;
}
const auto declared_size = ReadBe64(data, 0x08);
const auto declared_size = qFromBigEndian<quint64>(data.constData() + 0x08);
if (declared_size > static_cast<quint64>(data.size())) {
error = QObject::tr("%1 is truncated.").arg(QFileInfo(file_name).fileName());
return false;
}
const auto file_count = ReadBe32(data, 0x10);
const auto toc_offset = static_cast<quint64>(ReadBe32(data, 0x14));
const auto file_count = qFromBigEndian<quint32>(data.constData() + 0x10);
const auto toc_offset = static_cast<quint64>(qFromBigEndian<quint32>(data.constData() + 0x14));
const auto data_size = static_cast<quint64>(data.size());
const quint64 table_size = UCP_TOC_SKIP + static_cast<quint64>(file_count) * UCP_ENTRY_LEN;
if (toc_offset > data_size || table_size > data_size - toc_offset ||
!FitsQSizeType(toc_offset + table_size)) {
!std::in_range<qsizetype>(toc_offset + table_size)) {
error = QObject::tr("%1 has an invalid table of contents.")
.arg(QFileInfo(file_name).fileName());
return false;
@@ -162,14 +149,14 @@ static bool ReadUcp(const QString& file_name, QMap<QString, QByteArray>& files,
static_cast<quint64>(i) * UCP_ENTRY_LEN);
UcpEntry entry;
entry.name = ReadFixedString(data, entry_offset, UCP_NAME_LEN).trimmed();
entry.offset = ReadBe64(data, entry_offset + 0x20);
entry.size = ReadBe64(data, entry_offset + 0x28);
entry.offset = qFromBigEndian<quint64>(data.constData() + entry_offset + 0x20);
entry.size = qFromBigEndian<quint64>(data.constData() + entry_offset + 0x28);
if (entry.name.isEmpty()) {
continue;
}
if (entry.offset > data_size || entry.size > data_size - entry.offset ||
!FitsQSizeType(entry.offset) || !FitsQSizeType(entry.size)) {
!std::in_range<qsizetype>(entry.offset) || !std::in_range<qsizetype>(entry.size)) {
error = QObject::tr("%1 has an invalid entry for %2.")
.arg(QFileInfo(file_name).fileName(), entry.name);
return false;
+10 -20
View File
@@ -262,35 +262,25 @@ struct CommandBuffer {
reserved_dw);
}
[[nodiscard]] KYTY_SYSV_ABI uint32_t GetAvailableSizeDW() const {
if (cursor_up == nullptr || cursor_down == nullptr || cursor_down <= cursor_up) {
return 0;
}
auto available = static_cast<uint64_t>(cursor_down - cursor_up);
[[nodiscard]] KYTY_SYSV_ABI uint64_t GetAvailableSizeDW() const {
// Interpret the signed cursor distance as an unsigned 64-bit DWORD count.
const auto distance = static_cast<int64_t>(reinterpret_cast<uintptr_t>(cursor_down) -
reinterpret_cast<uintptr_t>(cursor_up));
const auto available = static_cast<uint64_t>(distance >> 2);
if (available <= reserved_dw) {
return 0;
}
if (available - reserved_dw > UINT32_MAX) {
LOGF_COLOR(
Log::Color::Red,
"\t command buffer has suspiciously large free space: cursor_up = 0x%016" PRIx64
", cursor_down = 0x%016" PRIx64 ", reserved_dw = %" PRIu32 "\n",
reinterpret_cast<uint64_t>(cursor_up), reinterpret_cast<uint64_t>(cursor_down),
reserved_dw);
return UINT32_MAX;
}
return static_cast<uint32_t>(available - reserved_dw);
return available - reserved_dw;
}
KYTY_SYSV_ABI bool ReserveDW(uint32_t num_dw) {
uint32_t remaining = GetAvailableSizeDW();
const uint64_t remaining = GetAvailableSizeDW();
if (num_dw > remaining) {
if (callback == nullptr) {
LOGF_COLOR(
Log::Color::Red,
"\t command buffer exhausted and has no grow callback: requested = %" PRIu32
", remaining = %" PRIu32 ", reserved_dw = %" PRIu32 "\n",
", remaining = %" PRIu64 ", reserved_dw = %" PRIu32 "\n",
num_dw, remaining, reserved_dw);
DbgDump();
return false;
@@ -300,7 +290,7 @@ struct CommandBuffer {
if (!result) {
LOGF_COLOR(Log::Color::Red,
"\t command buffer grow callback failed: requested = %" PRIu32
", remaining = %" PRIu32 ", reserved_dw = %" PRIu32 "\n",
", remaining = %" PRIu64 ", reserved_dw = %" PRIu32 "\n",
num_dw, remaining, reserved_dw);
DbgDump();
return false;
@@ -308,7 +298,7 @@ struct CommandBuffer {
if (GetAvailableSizeDW() < num_dw) {
LOGF_COLOR(Log::Color::Red,
"\t command buffer grow callback did not provide enough space: "
"requested = %" PRIu32 ", remaining = %" PRIu32
"requested = %" PRIu32 ", remaining = %" PRIu64
", reserved_dw = %" PRIu32 "\n",
num_dw, GetAvailableSizeDW(), reserved_dw);
DbgDump();
+3 -2
View File
@@ -849,8 +849,9 @@ int KYTY_SYSV_ABI AjmMemoryUnregister(uint32_t context, void* ptr) {
int KYTY_SYSV_ABI AjmBatchInitialize(void* buffer, size_t size, AjmBatchInfo* info) {
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(buffer == nullptr);
EXIT_NOT_IMPLEMENTED(info == nullptr);
if (buffer == nullptr || info == nullptr) {
return AJM_ERROR_INVALID_PARAMETER;
}
info->buffer = buffer;
info->offset = 0;
+32 -22
View File
@@ -38,9 +38,9 @@ static constexpr uint32_t AJM_DEC_M4AAC_MAX_SAMPLING_FREQ_INDEX = 11;
class AjmAacDecoder final: public AjmDecoder {
public:
AjmAacDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding,
AjmAacDecoder(uint32_t max_channels, uint32_t sample_rate, AjmSampleEncoding encoding,
uint64_t flags)
: AjmDecoder(channels, sample_rate, encoding), m_flags(flags) {
: AjmDecoder(0, sample_rate, encoding), m_max_channels(max_channels), m_flags(flags) {
m_codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
if (m_codec == nullptr) {
LOGF("AJM AAC: avcodec_find_decoder failed\n");
@@ -94,6 +94,11 @@ public:
((m_flags & AJM_INSTANCE_FLAG_DEC_M4AAC_ENABLE_NONDELAY_OUTPUT) != 0 ? 0u : 2u);
m_total_decoded_samples = 0;
m_bitrate = 0;
m_channels = 0;
m_raw_configured = false;
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW) {
m_sample_rate = SamplingRateFromIndex(m_sampling_freq_index);
}
if (m_is_initialized) {
OpenDecoder();
}
@@ -189,26 +194,6 @@ private:
return;
}
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW) {
const auto sample_index =
std::min(m_sampling_freq_index, AJM_DEC_M4AAC_MAX_SAMPLING_FREQ_INDEX);
const auto channels = std::clamp<uint32_t>(m_channels, 1u, AJM_DEC_M4AAC_MAX_CHANNELS);
constexpr uint32_t audio_object_type = 2;
auto* extradata = static_cast<uint8_t*>(av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE));
if (extradata == nullptr) {
Release();
return;
}
extradata[0] = static_cast<uint8_t>((audio_object_type << 3u) | (sample_index >> 1u));
extradata[1] = static_cast<uint8_t>(((sample_index & 0x1u) << 7u) |
(std::min(channels, 7u) << 3u));
m_codec_context->extradata = extradata;
m_codec_context->extradata_size = 2;
SetFormat(channels, SamplingRateFromIndex(sample_index), m_sample_encoding);
}
if (avcodec_open2(m_codec_context, m_codec, nullptr) < 0) {
LOGF("AJM AAC: decoder initialization failed\n");
Release();
@@ -222,6 +207,10 @@ private:
}
const auto channels = static_cast<uint32_t>(std::max(frame->ch_layout.nb_channels, 1));
if (channels > m_max_channels) {
result->result = AJM_RESULT_TOO_MANY_CHANNELS;
return false;
}
const auto bpf = channels * AjmBytesPerSample(m_sample_encoding);
if (bpf == 0) {
result->result = AJM_RESULT_INVALID_PARAMETER;
@@ -306,6 +295,25 @@ private:
return false;
}
std::memcpy(packet->data, packet_data, static_cast<size_t>(packet_size));
if (m_config_number == AJM_DEC_M4AAC_CONFIG_NUMBER_RAW && !m_raw_configured) {
// Only indexed stereo starts with a channel pair; the instance limit is not
// the stream's channel count. A leading SCE remains ambiguous.
const auto channels = (packet_data[0] >> 5u) == 1u
? 2u
: std::clamp(m_max_channels, 1u, AJM_DEC_M4AAC_MAX_CHANNELS);
auto* extradata = av_packet_new_side_data(packet, AV_PKT_DATA_NEW_EXTRADATA, 2);
if (extradata == nullptr) {
av_packet_free(&packet);
result->result = AJM_RESULT_CODEC_ERROR | AJM_RESULT_FATAL;
return false;
}
constexpr uint32_t audio_object_type = 2;
extradata[0] = static_cast<uint8_t>((audio_object_type << 3u) |
(m_sampling_freq_index >> 1u));
extradata[1] = static_cast<uint8_t>(((m_sampling_freq_index & 1u) << 7u) |
(std::min(channels, 7u) << 3u));
m_raw_configured = true;
}
int rc = avcodec_send_packet(m_codec_context, packet);
av_packet_free(&packet);
@@ -353,12 +361,14 @@ private:
const AVCodec* m_codec = nullptr;
AVCodecContext* m_codec_context = nullptr;
const uint32_t m_max_channels;
uint64_t m_flags = 0;
uint32_t m_config_number = AJM_DEC_M4AAC_CONFIG_NUMBER_ADTS;
uint32_t m_sampling_freq_index = 3;
uint32_t m_skip_frames = 2;
uint32_t m_bitrate = 0;
bool m_is_initialized = false;
bool m_raw_configured = false;
};
} // namespace Libs::Audio::Ajm
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more