mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
Compare commits
337
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3cc8454b9 | ||
|
|
b8ad1d8c4b | ||
|
|
cfb110e0aa | ||
|
|
e930657bf6 | ||
|
|
0ad9149c90 | ||
|
|
25f2699523 | ||
|
|
a849f25376 | ||
|
|
fa51da4d76 | ||
|
|
6b79b7a003 | ||
|
|
102a10f0fe | ||
|
|
8d66c4ef6d | ||
|
|
8cecaf4e30 | ||
|
|
82b2711cdf | ||
|
|
35a275e97d | ||
|
|
303a0203b6 | ||
|
|
cfbaee07f0 | ||
|
|
fe45265fcd | ||
|
|
8b294eb5b8 | ||
|
|
07c9978ac6 | ||
|
|
b61b3f2a40 | ||
|
|
001dfea501 | ||
|
|
f100f785da | ||
|
|
ba1854bbdc | ||
|
|
ea6aae8577 | ||
|
|
5b7d3343cf | ||
|
|
394e638905 | ||
|
|
c2b7b0b58c | ||
|
|
1d2f59dfa4 | ||
|
|
007d7ea2eb | ||
|
|
95d3d9d3ad | ||
|
|
bfc8cd0e0b | ||
|
|
165234b159 | ||
|
|
6493674726 | ||
|
|
cbedf5707f | ||
|
|
57c5b206ca | ||
|
|
87b4417aca | ||
|
|
104530eafc | ||
|
|
6cfde37e98 | ||
|
|
c5cebc1d19 | ||
|
|
697b18b366 | ||
|
|
5b2935e36c | ||
|
|
5dc644edb4 | ||
|
|
1211958b4e | ||
|
|
218e7200bb | ||
|
|
2bf78a94e0 | ||
|
|
cc8e02380f | ||
|
|
c66d977c1d | ||
|
|
4b77686566 | ||
|
|
a6144f794b | ||
|
|
bab1c3614c | ||
|
|
de1545e19a | ||
|
|
ce629e0391 | ||
|
|
6c03b4aaa5 | ||
|
|
0627e23e92 | ||
|
|
4e6f4f99d3 | ||
|
|
f30afc184e | ||
|
|
228cf3fbf4 | ||
|
|
47957faa16 | ||
|
|
235e24d44f | ||
|
|
e0ecffed70 | ||
|
|
fa913ec1a7 | ||
|
|
49b2eedd1c | ||
|
|
63fb822f8a | ||
|
|
f38d738ee2 | ||
|
|
eda6ba37ec | ||
|
|
b594f8c834 | ||
|
|
bc45aaf604 | ||
|
|
fb94a819c7 | ||
|
|
8589731a27 | ||
|
|
c1dccd7db1 | ||
|
|
af3a3f4bf6 | ||
|
|
349fae00d9 | ||
|
|
10cae50b34 | ||
|
|
5b541375a3 | ||
|
|
ded6964953 | ||
|
|
9b2111dc01 | ||
|
|
71f52f9d90 | ||
|
|
86e5e01403 | ||
|
|
77680ce9d2 | ||
|
|
32e6a433e8 | ||
|
|
7c9148dd47 | ||
|
|
957452d6f9 | ||
|
|
e6156bef05 | ||
|
|
441367b1a1 | ||
|
|
629ae1ac05 | ||
|
|
0d9e95d96a | ||
|
|
9cf7963f44 | ||
|
|
acd7435d31 | ||
|
|
7af28c267d | ||
|
|
79120d53ec | ||
|
|
6a2987a7b2 | ||
|
|
d1372abf46 | ||
|
|
98b5d88d67 | ||
|
|
86414760e6 | ||
|
|
a1fbc41146 | ||
|
|
5702cfc935 | ||
|
|
7b5a33f878 | ||
|
|
ea092a9472 | ||
|
|
18a1e0fadc | ||
|
|
437e69ef1b | ||
|
|
dd408ffd00 | ||
|
|
bce8924dd6 | ||
|
|
fe26c1de17 | ||
|
|
01df42a5f3 | ||
|
|
968cf3c03f | ||
|
|
586cbd4a0d | ||
|
|
940e42f130 | ||
|
|
3f80151a6f | ||
|
|
7e89b97daf | ||
|
|
d63fb62367 | ||
|
|
29e9ea6344 | ||
|
|
9a74ef15c3 | ||
|
|
a9ae04c152 | ||
|
|
8e35e1233b | ||
|
|
5610976720 | ||
|
|
1133272668 | ||
|
|
bbebb6419b | ||
|
|
b67cb81359 | ||
|
|
f3c82a317a | ||
|
|
f426d60ff1 | ||
|
|
b2fdad9e47 | ||
|
|
87971a28f5 | ||
|
|
96d62b3367 | ||
|
|
7516068662 | ||
|
|
6d1ba5870a | ||
|
|
00a16f3120 | ||
|
|
d3d7bd33f8 | ||
|
|
069f928edc | ||
|
|
0f3674f531 | ||
|
|
a21ffde34e | ||
|
|
1208d22547 | ||
|
|
3a27f8a76a | ||
|
|
dff2b19c56 | ||
|
|
a58db4f023 | ||
|
|
7bcd43dbee | ||
|
|
7c5473400e | ||
|
|
23c8a43e37 | ||
|
|
2d37634ee9 | ||
|
|
e026bfc9a2 | ||
|
|
5a31de7955 | ||
|
|
20e57994cf | ||
|
|
59ef88c959 | ||
|
|
1d1f00bdef | ||
|
|
47b26e497f | ||
|
|
733842cad4 | ||
|
|
73363f1272 | ||
|
|
0cd7682012 | ||
|
|
25a44371e3 | ||
|
|
f2455c875e | ||
|
|
2e315a3c62 | ||
|
|
d2fa86579b | ||
|
|
23e7df21b1 | ||
|
|
a305a6c1c6 | ||
|
|
635fb8882b | ||
|
|
fe6f49632c | ||
|
|
c3546577b6 | ||
|
|
c913951f7b | ||
|
|
3902b42096 | ||
|
|
ae573f941a | ||
|
|
20ea54cf0c | ||
|
|
0b4e78c0b2 | ||
|
|
57844a07e2 | ||
|
|
ba18551d6e | ||
|
|
c7b0d77970 | ||
|
|
1af19eabf0 | ||
|
|
32ea0869e5 | ||
|
|
e04007a7c6 | ||
|
|
1a3a15e4cf | ||
|
|
679adbfa5e | ||
|
|
d285efe3b5 | ||
|
|
a64c8d3205 | ||
|
|
82c52c3fab | ||
|
|
dd1e90fcd7 | ||
|
|
ef0155636a | ||
|
|
9d550fbffa | ||
|
|
fcef691e0a | ||
|
|
839c172612 | ||
|
|
9f49ac8249 | ||
|
|
fde550ef66 | ||
|
|
2b3993a595 | ||
|
|
de3e86791c | ||
|
|
6f66f77b09 | ||
|
|
d853aba955 | ||
|
|
cd62693792 | ||
|
|
cb4c8f3878 | ||
|
|
4d371d5f5f | ||
|
|
72963b01e1 | ||
|
|
80a18e82b7 | ||
|
|
574e687734 | ||
|
|
c725ec50ff | ||
|
|
c6f6e306e0 | ||
|
|
b847135321 | ||
|
|
ca3dc8792a | ||
|
|
ac3156df3a | ||
|
|
6aa6e390ee | ||
|
|
e4c10de8b6 | ||
|
|
b3b3aeef73 | ||
|
|
056dcee70c | ||
|
|
13c5bbe916 | ||
|
|
bbe3bd0304 | ||
|
|
f9b279c49d | ||
|
|
b61360a0c0 | ||
|
|
27ee6f02d6 | ||
|
|
2097ebb414 | ||
|
|
587df9145f | ||
|
|
2c5060e14f | ||
|
|
75aaa963d2 | ||
|
|
34ebfe40c3 | ||
|
|
2a54310876 | ||
|
|
8bccd325ec | ||
|
|
ad935311f0 | ||
|
|
19fc0274de | ||
|
|
6cc9e5de11 | ||
|
|
bf7ca29487 | ||
|
|
3cf426662e | ||
|
|
1fd07c2c83 | ||
|
|
f880c58ccc | ||
|
|
49e59e572e | ||
|
|
9d1fc8328f | ||
|
|
c9ff09c7c2 | ||
|
|
ceb2b664e0 | ||
|
|
b5b4c06179 | ||
|
|
d70f9c0b2b | ||
|
|
e847f82fab | ||
|
|
807a594c2d | ||
|
|
ced73c56cc | ||
|
|
9c6f6fa624 | ||
|
|
533176f9ff | ||
|
|
66c1663495 | ||
|
|
30c7715d3f | ||
|
|
093911a1dc | ||
|
|
d83f1f8235 | ||
|
|
50e78d717d | ||
|
|
6ca8201982 | ||
|
|
99a87bd65b | ||
|
|
f9f8e12c35 | ||
|
|
dcaf4205cb | ||
|
|
4735359466 | ||
|
|
211242452b | ||
|
|
6ae5cb7b64 | ||
|
|
997ad60c50 | ||
|
|
44f982bca7 | ||
|
|
0456f5a06b | ||
|
|
b346f58b73 | ||
|
|
ace877ce28 | ||
|
|
cf0cbee02d | ||
|
|
98e7eeb72a | ||
|
|
803c124136 | ||
|
|
d9d17737c3 | ||
|
|
46850f9574 | ||
|
|
314d37155a | ||
|
|
ddcdb654ad | ||
|
|
cdec5c515b | ||
|
|
1eb13dfa5e | ||
|
|
dda628ca81 | ||
|
|
13ad9f510d | ||
|
|
4c769dfbf9 | ||
|
|
ccd19b3e56 | ||
|
|
81d0ea5697 | ||
|
|
43de8bfaac | ||
|
|
86b539a9ea | ||
|
|
81a5dd612f | ||
|
|
9c00c7077b | ||
|
|
49ff962f69 | ||
|
|
c83aac966e | ||
|
|
9918dc2591 | ||
|
|
3ab372ccec | ||
|
|
843c0c59be | ||
|
|
d93812b0bd | ||
|
|
1c68c94c05 | ||
|
|
c66060be10 | ||
|
|
e7b269a9f6 | ||
|
|
d5b19850fb | ||
|
|
e7fe8192bb | ||
|
|
ebe0979563 | ||
|
|
a379304362 | ||
|
|
bec7c510c2 | ||
|
|
6a187ac67e | ||
|
|
9641b51925 | ||
|
|
b96a30ed88 | ||
|
|
2ec0e9e8f3 | ||
|
|
51afce3a97 | ||
|
|
dea80f34ca | ||
|
|
994271e2f6 | ||
|
|
59902d1fd8 | ||
|
|
b92f0a5458 | ||
|
|
251f6d6121 | ||
|
|
0d8c83a5cb | ||
|
|
3624d96c78 | ||
|
|
3213934d42 | ||
|
|
246212789f | ||
|
|
c7f7fd974b | ||
|
|
5cf2f5767a | ||
|
|
e8e580cf6c | ||
|
|
74a78f37d0 | ||
|
|
8d3ebb5a55 | ||
|
|
3628d2b4c1 | ||
|
|
d96d0eeb18 | ||
|
|
bd401a1407 | ||
|
|
070730be0a | ||
|
|
94e7d0a189 | ||
|
|
c28b71f8e0 | ||
|
|
27f015ecd1 | ||
|
|
8596627ced | ||
|
|
d4cca82a22 | ||
|
|
dd9ecd5f65 | ||
|
|
81b0c1302d | ||
|
|
b9b0b87545 | ||
|
|
040b2b9653 | ||
|
|
eccd6f6cac | ||
|
|
0ce19357f6 | ||
|
|
d762d24f73 | ||
|
|
eb917463ad | ||
|
|
c2189de88f | ||
|
|
2fe5c150bc | ||
|
|
5b8e3b51b0 | ||
|
|
f27a136430 | ||
|
|
fb4d97e8a9 | ||
|
|
5ffcd28ca4 | ||
|
|
b3c8f91a84 | ||
|
|
c78a5a9fdf | ||
|
|
858c18468b | ||
|
|
cd74809086 | ||
|
|
e21c5f37d0 | ||
|
|
2d5023fda2 | ||
|
|
98200fed19 | ||
|
|
c48a8bd6ae | ||
|
|
16ea36eb4b | ||
|
|
8f6f65f4c9 | ||
|
|
b81f1fc774 | ||
|
|
c85bd45604 | ||
|
|
5c86f894be | ||
|
|
86e3c90e32 | ||
|
|
93be8c201b | ||
|
|
894bc7de3c | ||
|
|
fc91fd2b1a | ||
|
|
a06a005124 |
No files matched your search
@@ -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
|
||||
@@ -6,7 +6,12 @@ on:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
KYTY_BUILD_ORIGIN: ${{ github.repository != 'KytyPS5/KytyPS5' && 'Fork' || github.event_name == 'push' && 'Official' || 'Source' }}
|
||||
KYTY_BUILD_REPOSITORY: ${{ github.repository }}
|
||||
|
||||
@@ -78,6 +83,9 @@ jobs:
|
||||
arch: win64_msvc2022_64
|
||||
cache: true
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.11
|
||||
|
||||
- name: Verify toolchain
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -100,6 +108,8 @@ jobs:
|
||||
-DKYTY_RELEASE_TAG=${{ needs.metadata.outputs.release_tag }} ^
|
||||
-DCMAKE_C_COMPILER=clang-cl ^
|
||||
-DCMAKE_CXX_COMPILER=clang-cl ^
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache ^
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^
|
||||
-DCMAKE_PREFIX_PATH="%Qt6_DIR%"
|
||||
|
||||
- name: Build
|
||||
@@ -156,6 +166,9 @@ jobs:
|
||||
arch: clang_64
|
||||
cache: true
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.11
|
||||
|
||||
- name: Verify toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -180,6 +193,8 @@ jobs:
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_PREFIX_PATH="$QT_ROOT_DIR"
|
||||
|
||||
- name: Build
|
||||
@@ -304,6 +319,7 @@ jobs:
|
||||
libxss-dev \
|
||||
lld \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
wayland-protocols
|
||||
|
||||
- name: Install Qt 6.10.3
|
||||
@@ -315,6 +331,9 @@ jobs:
|
||||
arch: linux_gcc_64
|
||||
cache: true
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@v0.0.11
|
||||
|
||||
- name: Verify toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -339,6 +358,8 @@ jobs:
|
||||
-DKYTY_RELEASE_TAG="${{ needs.metadata.outputs.release_tag }}" \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR" 2>&1 | tee _Build/configure.log
|
||||
exit "${PIPESTATUS[0]}"
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "3rdparty/ffmpeg-core"]
|
||||
path = 3rdparty/ffmpeg-core
|
||||
url = https://github.com/shadps4-emu/ext-ffmpeg-core.git
|
||||
url = https://github.com/KytyPS5/ext-ffmpeg-core.git
|
||||
shallow = true
|
||||
[submodule "3rdparty/fmt"]
|
||||
path = 3rdparty/fmt
|
||||
|
||||
Vendored
+6
-4
@@ -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"
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -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)
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/ffmpeg-core updated: 94dde08c8a...9ac4cfd195.
Vendored
+1
-1
Submodule 3rdparty/tracy updated: 05cceee0df...30997d5ca6.
+37
-23
@@ -126,7 +126,7 @@ if (KYTY_LINKER STREQUAL LD AND NOT LINUX)
|
||||
set(KYTY_LD_OPTIONS "-Wl,--image-base=0x100000000000")
|
||||
endif()
|
||||
|
||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.2.2)
|
||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.3.0)
|
||||
|
||||
include_directories(
|
||||
${KYTY_THIRD_PARTY_DIR}/gtest/include
|
||||
@@ -317,24 +317,10 @@ endif()
|
||||
|
||||
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL2-static imgui xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9 kyty_git_version Zydis::Zydis xbyak::xbyak)
|
||||
|
||||
# Linux system libraries required by the static FFmpeg archive.
|
||||
# Linux system libraries.
|
||||
if(LINUX)
|
||||
find_package(Threads REQUIRED)
|
||||
list(APPEND kyty_emulator_link_libraries m ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
|
||||
# Optional FFmpeg dependencies.
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
list(APPEND kyty_emulator_link_libraries ZLIB::ZLIB)
|
||||
endif()
|
||||
find_library(KYTY_BZ2_LIBRARY bz2)
|
||||
if(KYTY_BZ2_LIBRARY)
|
||||
list(APPEND kyty_emulator_link_libraries ${KYTY_BZ2_LIBRARY})
|
||||
endif()
|
||||
find_library(KYTY_LZMA_LIBRARY lzma)
|
||||
if(KYTY_LZMA_LIBRARY)
|
||||
list(APPEND kyty_emulator_link_libraries ${KYTY_LZMA_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(inc_headers
|
||||
@@ -342,6 +328,7 @@ set(inc_headers
|
||||
${KYTY_THIRD_PARTY_DIR}/SDL2/include
|
||||
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Headers/include
|
||||
${KYTY_THIRD_PARTY_DIR}/fmt/include
|
||||
${KYTY_THIRD_PARTY_DIR}/xxhash
|
||||
${KYTY_THIRD_PARTY_DIR}/cpuinfo/include
|
||||
@@ -431,16 +418,15 @@ add_executable(shader_vertex_metadata_tests EXCLUDE_FROM_ALL
|
||||
)
|
||||
target_include_directories(shader_vertex_metadata_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(shader_stage_runtime_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ShaderStageRuntimeTests.cpp"
|
||||
add_executable(resource_materialization_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceMaterializationTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/shaderStageRuntime.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
)
|
||||
target_link_libraries(shader_stage_runtime_tests fmt::fmt)
|
||||
target_include_directories(shader_stage_runtime_tests PRIVATE ${inc_headers})
|
||||
target_link_libraries(resource_materialization_tests fmt::fmt)
|
||||
target_include_directories(resource_materialization_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp"
|
||||
@@ -464,6 +450,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"
|
||||
@@ -542,15 +534,20 @@ if(BUILD_TESTING)
|
||||
add_test(NAME page_manager COMMAND $<TARGET_FILE:page_manager_tests>)
|
||||
add_test(NAME bit_array COMMAND $<TARGET_FILE:bit_array_tests>)
|
||||
add_test(NAME shader_vertex_metadata COMMAND $<TARGET_FILE:shader_vertex_metadata_tests>)
|
||||
add_test(NAME shader_stage_runtime COMMAND $<TARGET_FILE:shader_stage_runtime_tests>)
|
||||
add_test(NAME resource_materialization COMMAND $<TARGET_FILE:resource_materialization_tests>)
|
||||
add_test(NAME resource_tracking COMMAND $<TARGET_FILE:resource_tracking_tests>)
|
||||
add_test(NAME event_queue_lifetime COMMAND $<TARGET_FILE:event_queue_lifetime_tests>)
|
||||
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)
|
||||
add_test(NAME graphics_pipeline_rasterization
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --polygon-mode-only)
|
||||
add_test(NAME graphics_draw_offsets
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --draw-offset-only)
|
||||
add_test(NAME virtual_memory_allocation
|
||||
COMMAND $<TARGET_FILE:virtual_memory_allocation_tests>)
|
||||
add_test(NAME guest_red_zone_patcher
|
||||
@@ -568,6 +565,8 @@ if(BUILD_TESTING)
|
||||
|
||||
add_test(NAME texture_cache_layered_image
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
||||
add_test(NAME host_image_allocation
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --host-image-allocation-only)
|
||||
add_test(NAME texture_cache_image_views
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
||||
add_test(NAME texture_cache_storage_sampled
|
||||
@@ -598,7 +597,7 @@ if(BUILD_TESTING)
|
||||
page_manager_tests
|
||||
bit_array_tests
|
||||
shader_vertex_metadata_tests
|
||||
shader_stage_runtime_tests
|
||||
resource_materialization_tests
|
||||
resource_tracking_tests
|
||||
event_queue_lifetime_tests
|
||||
kernel_file_system_tests
|
||||
@@ -609,6 +608,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)
|
||||
|
||||
@@ -90,8 +90,14 @@ starting a large change.
|
||||
|
||||
Set up the clang-format hook after cloning:
|
||||
|
||||
```powershell
|
||||
python -m pip install pre-commit
|
||||
Install `pre-commit` using the method appropriate for your platform:
|
||||
|
||||
- **Arch Linux / CachyOS:** `sudo pacman -S pre-commit`
|
||||
- **Other Linux / macOS / Windows:** `python -m pip install pre-commit`
|
||||
|
||||
Then install the Git hook:
|
||||
|
||||
```bash
|
||||
python -m pre_commit install --install-hooks
|
||||
```
|
||||
|
||||
@@ -131,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`.
|
||||
|
||||
@@ -165,7 +172,7 @@ build has no working sound and no gamepad hotplug:
|
||||
|
||||
```bash
|
||||
sudo apt-get install --no-install-recommends \
|
||||
clang lld ninja-build cmake git glslang-tools \
|
||||
clang lld ninja-build cmake git glslang-tools pkg-config \
|
||||
libgl1-mesa-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev \
|
||||
libxi-dev libxrandr-dev libxss-dev libxkbcommon-dev \
|
||||
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev libwayland-dev wayland-protocols
|
||||
@@ -186,13 +193,37 @@ cmake --install _Build/linux --prefix _Build/linux/install
|
||||
```
|
||||
|
||||
The install step copies the Qt libraries and plugins next to the binaries, so
|
||||
`_Build/linux/install` runs without a matching system Qt.
|
||||
`_Build/linux/install` runs without a matching system Qt. FFmpeg is linked statically
|
||||
from the pinned [KytyPS5 FFmpeg core](https://github.com/KytyPS5/ext-ffmpeg-core)
|
||||
release, including VP9 and WebM support. System FFmpeg packages are not required.
|
||||
|
||||
As on Windows, the MSVC compiler is not used; Clang is required. `cl.exe` is rejected at configure
|
||||
time.
|
||||
|
||||
The CMake source root is the repository root.
|
||||
|
||||
### Building on NixOS
|
||||
|
||||
A development shell provides Clang, CMake, Ninja, Qt 6, the Vulkan headers, and the SDL2 backend
|
||||
libraries. Enter it and configure exactly as on other Linux distributions; the shell exports
|
||||
`CMAKE_PREFIX_PATH` and `QT_PLUGIN_PATH`, so the `-DCMAKE_PREFIX_PATH="$Qt6_DIR"` argument is not
|
||||
needed:
|
||||
|
||||
```bash
|
||||
nix-shell # or: nix develop
|
||||
git submodule update --init --recursive
|
||||
|
||||
cmake -S . -B _Build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
|
||||
cmake --build _Build/linux --target launcher --parallel
|
||||
cmake --install _Build/linux --prefix _Build/linux/install
|
||||
```
|
||||
|
||||
The configure step downloads the FFmpeg prebuilts and the `xbyak`/`zydis` sources, so it needs
|
||||
network access; a fully sandboxed `nix build` would require vendoring those inputs. A Vulkan 1.3
|
||||
driver must be available at runtime (on NixOS, `hardware.graphics.enable = true`).
|
||||
|
||||
### Building on macOS
|
||||
|
||||
macOS builds target x86-64 and run under Rosetta 2 on Apple Silicon, so the PS5's x86-64 game
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 2.2 MiB |
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1789426278,
|
||||
"narHash": "sha256-GnotcvKtbTMnoVx6G4E7ZdUX6487Dky74dqq3pU7iRk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "efe6f071ede9d21c37462d2d6682d5e670099684",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
description = "KytyPS5 development shell";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
systems = [ "x86_64-linux" ];
|
||||
forAllSystems = nixpkgs.lib.genAttrs systems;
|
||||
in
|
||||
{
|
||||
devShells = forAllSystems (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
{
|
||||
default = import ./nix/devshell.nix { inherit pkgs; };
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{ pkgs }:
|
||||
|
||||
let
|
||||
# Build-time dependencies. Every -dev package the bundled SDL2 probes for must
|
||||
# be listed here, otherwise SDL2 quietly drops the audio/Wayland/udev backends.
|
||||
buildDeps = with pkgs; [
|
||||
qt6.qtbase # Concurrent, Network, Widgets (launcher)
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
libglvnd
|
||||
# X11 / input
|
||||
libX11
|
||||
libXcursor
|
||||
libXext
|
||||
libXfixes
|
||||
libXi
|
||||
libXrandr
|
||||
libXScrnSaver
|
||||
libxkbcommon
|
||||
# Wayland
|
||||
wayland
|
||||
wayland-protocols
|
||||
libdecor
|
||||
# audio + device/dbus
|
||||
alsa-lib
|
||||
libpulseaudio
|
||||
systemd # libudev
|
||||
dbus
|
||||
];
|
||||
|
||||
# Libraries the built binaries need at runtime.
|
||||
runtimeLibs = with pkgs; [
|
||||
stdenv.cc.cc.lib # libstdc++ / libgcc_s
|
||||
libgcc.lib
|
||||
qt6.qtbase # libQt6Concurrent/Network/Widgets
|
||||
vulkan-loader
|
||||
libglvnd
|
||||
alsa-lib
|
||||
libpulseaudio
|
||||
libX11
|
||||
libXcursor
|
||||
libXext
|
||||
libXfixes
|
||||
libXi
|
||||
libXrandr
|
||||
libXScrnSaver
|
||||
libxkbcommon
|
||||
wayland
|
||||
libdecor
|
||||
systemd
|
||||
dbus
|
||||
];
|
||||
in
|
||||
pkgs.mkShell {
|
||||
name = "kytyps5";
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
clang
|
||||
lld
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
git
|
||||
glslang # provides glslangValidator for the bundled shaders
|
||||
];
|
||||
|
||||
buildInputs = buildDeps;
|
||||
|
||||
shellHook = ''
|
||||
export CMAKE_PREFIX_PATH="${pkgs.qt6.qtbase}:''${CMAKE_PREFIX_PATH:-}"
|
||||
export QT_PLUGIN_PATH="${pkgs.qt6.qtbase}/lib/qt-6/plugins:${pkgs.qt6.qtbase}/lib/qt6/plugins:''${QT_PLUGIN_PATH:-}"
|
||||
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath runtimeLibs}:''${LD_LIBRARY_PATH:-}"
|
||||
|
||||
if [ -z "''${KYTY_SHELL_QUIET:-}" ]; then
|
||||
echo "KytyPS5 dev shell"
|
||||
echo " cmake -S . -B _Build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release \\"
|
||||
echo " -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
|
||||
echo " cmake --build _Build/linux --target launcher --parallel"
|
||||
echo " cmake --install _Build/linux --prefix _Build/linux/install"
|
||||
fi
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
let
|
||||
pkgs = import <nixpkgs> { };
|
||||
in
|
||||
import ./nix/devshell.nix { inherit pkgs; }
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+3
-18
@@ -1,6 +1,5 @@
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "kytyGitVersion.h"
|
||||
@@ -12,25 +11,10 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int PRINT_STACK_FROM = 4;
|
||||
#else
|
||||
constexpr int PRINT_STACK_FROM = 2;
|
||||
#endif
|
||||
|
||||
static std::string BuildFatalReport(const char* title, std::string_view text, const char* file,
|
||||
int line) {
|
||||
DebugStack stack;
|
||||
DebugStack::Trace(&stack);
|
||||
|
||||
std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n";
|
||||
for (int i = PRINT_STACK_FROM; i < stack.depth; i++) {
|
||||
report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM,
|
||||
static_cast<uint64_t>(stack.GetAddr(i)));
|
||||
}
|
||||
report += fmt::format("{}\n{} in {}:{}\n", title, text, file, line);
|
||||
return report;
|
||||
return fmt::format("--- Build ---\n{}\n{}\n{} in {}:{}\n", KYTY_BUILD_LABEL, title, text, file,
|
||||
line);
|
||||
}
|
||||
|
||||
static int DbgReport(const char* title, std::string_view text, const char* file, int line) {
|
||||
@@ -59,6 +43,7 @@ int DbgExitHandler(const char* file, int line, fmt::text_style style, std::strin
|
||||
}
|
||||
|
||||
void DbgExit(int status) {
|
||||
Subsystems::EmergencyShutdownActive();
|
||||
std::fflush(nullptr);
|
||||
std::_Exit(status);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,7 @@ void DbgExit(int status);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
#define EXIT_HALT() (Common::DbgExit(321), 1)
|
||||
#else
|
||||
#define EXIT_HALT() (std::_Exit(321), 1)
|
||||
#endif
|
||||
|
||||
#ifndef KYTY_FINAL
|
||||
#define EXIT_IF(x) \
|
||||
|
||||
@@ -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_ */
|
||||
@@ -1,20 +1,7 @@
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void DebugStack::Print(int from, [[maybe_unused]] bool with_name) const {
|
||||
for (int i = from; i < depth; i++) {
|
||||
printf("[%d] %016" PRIx64 "\n", i - from, static_cast<uint64_t>(GetAddr(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStack::Trace(DebugStack* stack) {
|
||||
stack->depth = DEBUG_MAX_STACK_DEPTH;
|
||||
SysStackWalk(stack->stack.data(), &stack->depth);
|
||||
}
|
||||
|
||||
std::string Debug::GetCompiler() {
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
return "clang";
|
||||
@@ -37,8 +24,4 @@ std::string Debug::GetLinker() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Debug::GetBitness() {
|
||||
return "64";
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+1
-27
@@ -2,42 +2,16 @@
|
||||
#define KYTY_COMMON_DEBUG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 20;
|
||||
#else
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 15;
|
||||
#endif
|
||||
|
||||
namespace Debug {
|
||||
std::string GetCompiler();
|
||||
std::string GetLinker();
|
||||
std::string GetBitness();
|
||||
} // namespace Debug
|
||||
|
||||
struct DebugStack {
|
||||
int depth {0};
|
||||
std::array<void*, DEBUG_MAX_STACK_DEPTH> stack {};
|
||||
|
||||
[[nodiscard]] uintptr_t GetAddr(int i) const { return reinterpret_cast<uintptr_t>(stack[i]); }
|
||||
|
||||
void Print(int from, bool with_name = true) const;
|
||||
|
||||
void CopyTo(DebugStack* s) const {
|
||||
std::memcpy(s->stack.data(), stack.data(), sizeof(void*) * depth);
|
||||
s->depth = depth;
|
||||
}
|
||||
|
||||
static void Trace(DebugStack* stack);
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_DEBUG_H_ */
|
||||
@@ -43,6 +43,10 @@ int32_t GetUserId() {
|
||||
return g_config->user_id;
|
||||
}
|
||||
|
||||
const std::string& GetAudioInputDevice() {
|
||||
return g_config->audio_input_device;
|
||||
}
|
||||
|
||||
PresentMode GetPresentMode() {
|
||||
return g_config->present_mode;
|
||||
}
|
||||
@@ -55,6 +59,14 @@ bool FullscreenEnabled() {
|
||||
return g_config->fullscreen_enabled;
|
||||
}
|
||||
|
||||
bool VrEnabled() {
|
||||
return g_config->vr_enabled;
|
||||
}
|
||||
|
||||
bool AmdCpuEnabled() {
|
||||
return g_config->amd_cpu_enabled;
|
||||
}
|
||||
|
||||
uint32_t GetVblankFrequency() {
|
||||
return std::clamp(g_config->vblank_frequency, 30u, 360u);
|
||||
}
|
||||
@@ -75,7 +87,7 @@ ShaderOptimizationType GetShaderOptimizationType() {
|
||||
return g_config->shader_optimization_type;
|
||||
}
|
||||
|
||||
ShaderLogDirection GetShaderLogDirection() {
|
||||
LogDirection GetShaderLogDirection() {
|
||||
return g_config->shader_log_direction;
|
||||
}
|
||||
|
||||
@@ -95,7 +107,7 @@ bool GraphicsDebugDumpEnabled() {
|
||||
return g_config->graphics_debug_dump_enabled;
|
||||
}
|
||||
|
||||
OutputDirection GetPrintfDirection() {
|
||||
LogDirection GetPrintfDirection() {
|
||||
return g_config->printf_direction;
|
||||
}
|
||||
|
||||
@@ -103,8 +115,8 @@ std::filesystem::path GetPrintfOutputFile() {
|
||||
return g_config->printf_output_file;
|
||||
}
|
||||
|
||||
ProfilerDirection GetProfilerDirection() {
|
||||
return g_config->profiler_direction;
|
||||
bool ProfilerEnabled() {
|
||||
return g_config->profiler_enabled;
|
||||
}
|
||||
|
||||
bool SpirvDebugPrintfEnabled() {
|
||||
@@ -123,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;
|
||||
}
|
||||
|
||||
+16
-12
@@ -21,11 +21,7 @@ struct Lifecycle {
|
||||
|
||||
enum class ShaderOptimizationType { None, Size, Performance };
|
||||
|
||||
enum class ShaderLogDirection { Silent, Console, File };
|
||||
|
||||
enum class ProfilerDirection { None, Network };
|
||||
|
||||
enum class OutputDirection { Silent, Console, File };
|
||||
enum class LogDirection { Silent, Console, File };
|
||||
|
||||
enum class PresentMode { Fifo, Mailbox, Immediate };
|
||||
|
||||
@@ -47,26 +43,30 @@ struct ConfigOptions {
|
||||
uint32_t screen_height = 720;
|
||||
std::string user_name = "Kyty";
|
||||
int32_t user_id = DEFAULT_USER_ID;
|
||||
PresentMode present_mode = PresentMode::Fifo;
|
||||
std::string audio_input_device;
|
||||
PresentMode present_mode = PresentMode::Mailbox;
|
||||
int32_t gpu_index = -1;
|
||||
bool fullscreen_enabled = false;
|
||||
bool vr_enabled = false;
|
||||
bool amd_cpu_enabled = false;
|
||||
uint32_t vblank_frequency = 60;
|
||||
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||
bool vulkan_validation_enabled = false;
|
||||
bool shader_validation_enabled = false;
|
||||
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
|
||||
ShaderLogDirection shader_log_direction = ShaderLogDirection::Silent;
|
||||
LogDirection shader_log_direction = LogDirection::Silent;
|
||||
std::filesystem::path shader_log_folder = "_Shaders";
|
||||
bool command_buffer_dump_enabled = false;
|
||||
std::filesystem::path command_buffer_dump_folder = "_Buffers";
|
||||
bool graphics_debug_dump_enabled = false;
|
||||
OutputDirection printf_direction = OutputDirection::Silent;
|
||||
LogDirection printf_direction = LogDirection::Silent;
|
||||
std::filesystem::path printf_output_file = "_kyty.txt";
|
||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||
bool profiler_enabled = false;
|
||||
bool spirv_debug_printf_enabled = false;
|
||||
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;
|
||||
@@ -80,16 +80,19 @@ uint32_t GetScreenWidth();
|
||||
uint32_t GetScreenHeight();
|
||||
const std::string& GetUserName();
|
||||
int32_t GetUserId();
|
||||
const std::string& GetAudioInputDevice();
|
||||
PresentMode GetPresentMode();
|
||||
int32_t GetGpuIndex();
|
||||
bool FullscreenEnabled();
|
||||
bool VrEnabled();
|
||||
bool AmdCpuEnabled();
|
||||
uint32_t GetVblankFrequency();
|
||||
uint32_t GetConsoleLanguage();
|
||||
bool VulkanValidationEnabled();
|
||||
|
||||
bool ShaderValidationEnabled();
|
||||
ShaderOptimizationType GetShaderOptimizationType();
|
||||
ShaderLogDirection GetShaderLogDirection();
|
||||
LogDirection GetShaderLogDirection();
|
||||
std::filesystem::path GetShaderLogFolder();
|
||||
|
||||
bool CommandBufferDumpEnabled();
|
||||
@@ -97,10 +100,10 @@ std::filesystem::path GetCommandBufferDumpFolder();
|
||||
|
||||
bool GraphicsDebugDumpEnabled();
|
||||
|
||||
OutputDirection GetPrintfDirection();
|
||||
LogDirection GetPrintfDirection();
|
||||
std::filesystem::path GetPrintfOutputFile();
|
||||
|
||||
ProfilerDirection GetProfilerDirection();
|
||||
bool ProfilerEnabled();
|
||||
|
||||
bool SpirvDebugPrintfEnabled();
|
||||
|
||||
@@ -108,6 +111,7 @@ bool GpuAssistedValidationEnabled();
|
||||
|
||||
bool RenderDocEnabled();
|
||||
bool ReadbackLinearImagesEnabled();
|
||||
bool TessellationEnabled();
|
||||
bool PlayGoHackEnabled();
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled();
|
||||
|
||||
+52
-122
@@ -69,7 +69,7 @@ File::File(): m_p(std::make_unique<FilePrivate>()) {
|
||||
}
|
||||
|
||||
File::~File() {
|
||||
EXIT_IF(m_p->f != nullptr);
|
||||
Close();
|
||||
}
|
||||
|
||||
File::File(const std::filesystem::path& name): m_p(std::make_unique<FilePrivate>()) {
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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_ */
|
||||
@@ -5,14 +5,17 @@
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
#elif defined(__APPLE__)
|
||||
#else
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <unistd.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/ucontext.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <initializer_list>
|
||||
#include <ucontext.h> // IWYU pragma: keep
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// IWYU pragma: no_include <errhandlingapi.h>
|
||||
@@ -32,6 +35,51 @@ static_assert(decltype(g_handler)::is_always_lock_free);
|
||||
static_assert(decltype(g_install_state)::is_always_lock_free);
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
|
||||
// macOS uses the same POSIX platform setting and needs a signal stack too.
|
||||
class ThreadSignalStack {
|
||||
public:
|
||||
ThreadSignalStack() {
|
||||
const auto page_size = static_cast<size_t>(::getpagesize());
|
||||
const auto stack_size =
|
||||
(std::max<size_t>(64 * 1024, MINSIGSTKSZ) + page_size - 1) & ~(page_size - 1);
|
||||
if (::posix_memalign(&m_memory, page_size, stack_size) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack_t stack {};
|
||||
stack.ss_sp = m_memory;
|
||||
stack.ss_size = stack_size;
|
||||
if (::sigaltstack(&stack, &m_previous) != 0) {
|
||||
std::free(m_memory);
|
||||
m_memory = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadSignalStack() {
|
||||
if (m_memory != nullptr && ::sigaltstack(&m_previous, nullptr) == 0) {
|
||||
std::free(m_memory);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsInitialized() const { return m_memory != nullptr; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(ThreadSignalStack)
|
||||
|
||||
private:
|
||||
void* m_memory = nullptr;
|
||||
stack_t m_previous {};
|
||||
};
|
||||
|
||||
bool InitializeThreadSignalStack() {
|
||||
// Keep fault handling off guest stacks, which GPU tracking can make read-only.
|
||||
thread_local ThreadSignalStack signal_stack;
|
||||
return signal_stack.IsInitialized();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception) noexcept {
|
||||
@@ -253,7 +301,7 @@ bool InstallHandler(Handler handler) {
|
||||
#elif defined(__APPLE__)
|
||||
struct sigaction sa {};
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
// The guest signal-dispatch path (KernelRaiseException) interrupts threads with
|
||||
// SIGUSR1; block it while a fault is being resolved so a stop-the-world request
|
||||
@@ -274,8 +322,7 @@ bool InstallHandler(Handler handler) {
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = SignalHandler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
// Fault resolution needs the normal thread stack.
|
||||
action.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
|
||||
|
||||
for (const int signal_number: {SIGSEGV, SIGBUS, SIGILL}) {
|
||||
if (::sigaction(signal_number, &action, nullptr) != 0) {
|
||||
|
||||
@@ -39,6 +39,10 @@ using Handler = bool (*)(const ExceptionInfo&);
|
||||
|
||||
bool InstallHandler(Handler handler);
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
bool InitializeThreadSignalStack();
|
||||
#endif
|
||||
|
||||
} // namespace Common::HostException
|
||||
|
||||
#endif /* KYTY_COMMON_HOST_EXCEPTION_H_ */
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -129,6 +128,14 @@ static void WriteImpl(std::string_view text, fmt::text_style style = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
void WriteToConsoleAndLog(std::string_view text) {
|
||||
WriteImpl(text);
|
||||
if (g_initialized && g_direction != Direction::Console) {
|
||||
WriteStdout(text);
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
void WriteFatal(std::string_view text) {
|
||||
if (g_direction == Direction::Silent || !g_initialized) {
|
||||
WriteStdout(text);
|
||||
@@ -152,9 +159,9 @@ void WriteFatal(fmt::text_style style, std::string_view text) {
|
||||
void Initialize() {
|
||||
g_initialized = true;
|
||||
switch (Config::GetPrintfDirection()) {
|
||||
case Config::OutputDirection::Silent: g_direction = Direction::Silent; break;
|
||||
case Config::OutputDirection::Console: g_direction = Direction::Console; break;
|
||||
case Config::OutputDirection::File: g_direction = Direction::File; break;
|
||||
case Config::LogDirection::Silent: g_direction = Direction::Silent; break;
|
||||
case Config::LogDirection::Console: g_direction = Direction::Console; break;
|
||||
case Config::LogDirection::File: g_direction = Direction::File; break;
|
||||
}
|
||||
g_output_file =
|
||||
(g_direction == Direction::File ? Config::GetPrintfOutputFile() : std::filesystem::path {});
|
||||
|
||||
@@ -25,6 +25,7 @@ Direction GetDirection();
|
||||
bool IsSilent();
|
||||
void Write(std::string_view text);
|
||||
void Write(fmt::text_style style, std::string_view text);
|
||||
void WriteToConsoleAndLog(std::string_view text);
|
||||
void WriteFatal(std::string_view text);
|
||||
void WriteFatal(fmt::text_style style, std::string_view text);
|
||||
void Flush();
|
||||
|
||||
@@ -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_ */
|
||||
@@ -30,8 +30,6 @@ struct sys_dbg_stack_info_t {
|
||||
#endif
|
||||
};
|
||||
|
||||
void SysStackWalk(void** stack, int* depth);
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack); // NOLINT(google-runtime-references)
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSDBG_H_ */
|
||||
@@ -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);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <execinfo.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
@@ -17,56 +16,6 @@
|
||||
#include <libgen.h> // POSIX basename() lives here on macOS, not in <cstring>
|
||||
#endif
|
||||
|
||||
// Avoid unwinding a guest-owned stack.
|
||||
static bool OnOwnStack() {
|
||||
const char* probe = reinterpret_cast<const char*>(&probe);
|
||||
|
||||
pthread_attr_t attr {};
|
||||
#if defined(__APPLE__)
|
||||
const auto* top = static_cast<const char*>(pthread_get_stackaddr_np(pthread_self()));
|
||||
const auto size = pthread_get_stacksize_np(pthread_self());
|
||||
(void)attr;
|
||||
return top != nullptr && size != 0 && probe < top && probe >= top - size;
|
||||
#else
|
||||
if (pthread_getattr_np(pthread_self(), &attr) != 0) {
|
||||
return false;
|
||||
}
|
||||
void* base = nullptr;
|
||||
size_t size = 0;
|
||||
const bool ok = pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0;
|
||||
pthread_attr_destroy(&attr);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
const auto* low = static_cast<const char*>(base);
|
||||
return probe >= low && probe < low + size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
if (stack == nullptr || depth == nullptr || *depth <= 0) {
|
||||
if (depth != nullptr) {
|
||||
*depth = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OnOwnStack()) {
|
||||
*depth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const int n = ::backtrace(stack, *depth);
|
||||
*depth = (n < 0 ? 0 : n);
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.commited_addr),
|
||||
static_cast<uint64_t>(stack.commited_size));
|
||||
printf("code: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.code_addr),
|
||||
static_cast<uint64_t>(stack.code_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
pid_t pid = getpid();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#else
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysVirtual.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
#include <atomic>
|
||||
@@ -28,13 +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;
|
||||
static std::map<uintptr_t, int>* g_protects = nullptr;
|
||||
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
|
||||
|
||||
void SysVirtualInit() {
|
||||
void Init() {
|
||||
pthread_mutexattr_t attr {};
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
@@ -46,43 +44,27 @@ void SysVirtualInit() {
|
||||
pthread_mutex_init(&g_virtual_mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
g_allocs = new std::map<uintptr_t, size_t>;
|
||||
g_protects = new std::map<uintptr_t, int>;
|
||||
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;
|
||||
}
|
||||
|
||||
static VirtualMemory::Mode get_protection_flag(int mode) {
|
||||
switch (mode) {
|
||||
case PROT_NONE: return VirtualMemory::Mode::NoAccess;
|
||||
case PROT_READ: return VirtualMemory::Mode::Read;
|
||||
case PROT_WRITE: return VirtualMemory::Mode::Write;
|
||||
case PROT_READ | PROT_WRITE: return VirtualMemory::Mode::ReadWrite; // NOLINT
|
||||
case PROT_EXEC: return VirtualMemory::Mode::Execute;
|
||||
case PROT_EXEC | PROT_WRITE: return VirtualMemory::Mode::ExecuteWrite; // NOLINT
|
||||
case PROT_EXEC | PROT_READ: return VirtualMemory::Mode::ExecuteRead; // NOLINT
|
||||
case PROT_EXEC | PROT_WRITE | PROT_READ:
|
||||
return VirtualMemory::Mode::ExecuteReadWrite; // NOLINT
|
||||
default: return VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep automatic mappings inside the guest and GPU-addressable low window.
|
||||
#ifdef KYTY_FIXED_NOREPLACE
|
||||
static constexpr uintptr_t LOW_ARENA_LIMIT = 0x000000FC00000000ULL; // libc mspace window ceiling
|
||||
@@ -147,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);
|
||||
@@ -161,11 +143,6 @@ uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mo
|
||||
if (ptr != MAP_FAILED) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
}
|
||||
|
||||
@@ -176,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;
|
||||
}
|
||||
@@ -246,16 +222,11 @@ 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);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return ret_addr;
|
||||
@@ -308,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);
|
||||
@@ -337,11 +308,6 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
|
||||
if (ptr != MAP_FAILED) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return true;
|
||||
@@ -350,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;
|
||||
}
|
||||
@@ -427,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);
|
||||
@@ -437,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);
|
||||
@@ -473,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;
|
||||
}
|
||||
|
||||
@@ -499,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;
|
||||
|
||||
@@ -516,21 +482,10 @@ bool SysVirtualFree(uint64_t address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (munmap(reinterpret_cast<void*>(addr), size) == 0) {
|
||||
uintptr_t page_start = addr >> 12u;
|
||||
uintptr_t page_end = (addr + size - 1) >> 12u;
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
g_protects->erase(page);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
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;
|
||||
@@ -582,46 +537,22 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
if (end < alloc_end) {
|
||||
(*g_allocs)[end] = alloc_end - end;
|
||||
}
|
||||
for (uintptr_t page = addr >> 12u; page <= (end - 1u) >> 12u; page++) {
|
||||
g_protects->erase(page);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
VirtualMemory::Mode* old_mode) {
|
||||
auto addr = static_cast<uintptr_t>(address);
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
if (old_mode != nullptr) {
|
||||
if (auto s = g_protects->find(addr >> 12u); s != g_protects->end()) {
|
||||
*old_mode = get_protection_flag(s->second);
|
||||
} else {
|
||||
*old_mode = VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
uintptr_t page_start = addr >> 12u;
|
||||
uintptr_t page_end = (addr + size - 1) >> 12u;
|
||||
if (mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
|
||||
get_protection_flag(mode)) == 0) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = get_protection_flag(mode);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
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;
|
||||
return mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
|
||||
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
|
||||
@@ -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_ */
|
||||
@@ -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.
|
||||
|
||||
@@ -1,28 +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,
|
||||
VirtualMemory::Mode* old_mode = nullptr);
|
||||
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSVIRTUAL_H_ */
|
||||
@@ -15,18 +15,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
*depth = static_cast<int>(CaptureStackBackTrace(0, static_cast<DWORD>(*depth), stack, nullptr));
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64
|
||||
", %" PRIu64 ")\n",
|
||||
static_cast<uint64_t>(stack.reserved_addr), static_cast<uint64_t>(stack.reserved_size),
|
||||
static_cast<uint64_t>(stack.guard_addr), static_cast<uint64_t>(stack.guard_size),
|
||||
static_cast<uint64_t>(stack.commited_addr), static_cast<uint64_t>(stack.commited_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
MEMORY_BASIC_INFORMATION mbi {};
|
||||
[[maybe_unused]] size_t ss = VirtualQuery(&mbi, &mbi, sizeof(mbi));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,45 +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;
|
||||
}
|
||||
|
||||
static VirtualMemory::Mode GetProtectionFlag(DWORD mode) {
|
||||
switch (mode) {
|
||||
case PAGE_NOACCESS: return VirtualMemory::Mode::NoAccess;
|
||||
case PAGE_READONLY: return VirtualMemory::Mode::Read;
|
||||
case PAGE_READWRITE: return VirtualMemory::Mode::ReadWrite;
|
||||
case PAGE_EXECUTE: return VirtualMemory::Mode::Execute;
|
||||
case PAGE_EXECUTE_READ: return VirtualMemory::Mode::ExecuteRead;
|
||||
case PAGE_EXECUTE_READWRITE: return VirtualMemory::Mode::ExecuteReadWrite;
|
||||
default: return VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
void Init() {}
|
||||
|
||||
void SysVirtualInit() {}
|
||||
|
||||
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),
|
||||
@@ -71,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;
|
||||
@@ -92,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;
|
||||
@@ -147,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)));
|
||||
@@ -173,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)));
|
||||
@@ -191,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)));
|
||||
@@ -202,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()));
|
||||
@@ -262,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));
|
||||
@@ -288,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",
|
||||
@@ -298,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()));
|
||||
@@ -307,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;
|
||||
}
|
||||
@@ -325,24 +311,20 @@ 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,
|
||||
VirtualMemory::Mode* old_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) {
|
||||
printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
if (old_mode != nullptr) {
|
||||
*old_mode = GetProtectionFlag(old_protect);
|
||||
}
|
||||
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) {
|
||||
@@ -353,6 +335,6 @@ bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common::VirtualMemory
|
||||
|
||||
#endif
|
||||
+7
-13
@@ -55,19 +55,13 @@ void SetThreadName(const char* name) {
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
switch (Config::GetProfilerDirection()) {
|
||||
case Config::ProfilerDirection::Network:
|
||||
if (!tracy::ProfilerAvailable()) {
|
||||
tracy::StartupProfiler();
|
||||
TracySetProgramName("KytyPS5");
|
||||
::printf("Tracy profiler enabled: client %d.%d.%d, protocol %u, "
|
||||
"broadcast %u, connect to 127.0.0.1:8086\n",
|
||||
tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch,
|
||||
tracy::ProtocolVersion, tracy::BroadcastVersion);
|
||||
}
|
||||
break;
|
||||
case Config::ProfilerDirection::None:
|
||||
default: break;
|
||||
if (Config::ProfilerEnabled() && !tracy::ProfilerAvailable()) {
|
||||
tracy::StartupProfiler();
|
||||
TracySetProgramName("KytyPS5");
|
||||
::printf("Tracy profiler enabled: client %d.%d.%d, protocol %u, "
|
||||
"broadcast %u, connect to 127.0.0.1:8086\n",
|
||||
tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch,
|
||||
tracy::ProtocolVersion, tracy::BroadcastVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-180
@@ -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,86 +157,18 @@ 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), "\"", "\"\"") + "\"";
|
||||
inline std::string Utf16ToUtf8(std::u16string_view utf16) {
|
||||
if (utf16.empty()) {
|
||||
return {};
|
||||
}
|
||||
return std::string(text);
|
||||
}
|
||||
|
||||
inline std::string Utf16ToUtf8(const char16_t* utf16) {
|
||||
std::u16string input(utf16);
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(input);
|
||||
}
|
||||
|
||||
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;
|
||||
return convert.to_bytes(utf16.data(), utf16.data() + utf16.size());
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+8
-51
@@ -1,7 +1,6 @@
|
||||
#include "common/threads.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -9,7 +8,6 @@
|
||||
#include <chrono> // IWYU pragma: keep
|
||||
#include <condition_variable> // IWYU pragma: keep
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
#define KYTY_WIN_CS
|
||||
@@ -198,37 +196,7 @@ struct CondVarPrivate {
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::recursive_mutex g_cond_waiters_mutex;
|
||||
static std::vector<std::pair<int, CondVarPrivate*>> g_cond_waiters;
|
||||
static wait_poll_func_t g_cond_wait_poll_callback = nullptr;
|
||||
|
||||
static void WakeCondVar(CondVarPrivate* cond_var) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeAllConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&cond_var->m_cv);
|
||||
#else
|
||||
cond_var->m_cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void RegisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
g_cond_waiters.emplace_back(Thread::GetThreadIdUnique(), cond_var);
|
||||
}
|
||||
|
||||
static void UnregisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
const auto thread_id = Thread::GetThreadIdUnique();
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
|
||||
const auto it = std::find_if(g_cond_waiters.begin(), g_cond_waiters.end(),
|
||||
[thread_id, cond_var](const auto& waiter) {
|
||||
return waiter.first == thread_id && waiter.second == cond_var;
|
||||
});
|
||||
if (it != g_cond_waiters.end()) {
|
||||
g_cond_waiters.erase(it);
|
||||
}
|
||||
}
|
||||
static wait_poll_func_t g_cond_wait_poll_callback = nullptr;
|
||||
|
||||
struct ThreadPrivate {
|
||||
ThreadPrivate(thread_func_t f, void* a): func(f), arg(a), m_thread(&Run, this) {}
|
||||
@@ -285,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);
|
||||
@@ -366,7 +330,6 @@ CondVar::~CondVar() {
|
||||
}
|
||||
|
||||
void CondVar::Wait(Mutex* mutex) {
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
@@ -407,7 +370,6 @@ void CondVar::Wait(Mutex* mutex) {
|
||||
}
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SetWaitPollCallback(wait_poll_func_t callback) {
|
||||
@@ -416,7 +378,6 @@ void CondVar::SetWaitPollCallback(wait_poll_func_t callback) {
|
||||
|
||||
bool CondVar::WaitFor(Mutex* mutex, uint32_t micros) {
|
||||
bool ok = false;
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
@@ -431,7 +392,6 @@ bool CondVar::WaitFor(Mutex* mutex, uint32_t micros) {
|
||||
std::cv_status::no_timeout);
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -446,16 +406,13 @@ void CondVar::Signal() {
|
||||
}
|
||||
|
||||
void CondVar::SignalAll() {
|
||||
WakeCondVar(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SignalThread(int thread_id) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
for (const auto& waiter: g_cond_waiters) {
|
||||
if (waiter.first == thread_id) {
|
||||
WakeCondVar(waiter.second);
|
||||
}
|
||||
}
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeAllConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&m_cond_var->m_cv);
|
||||
#else
|
||||
m_cond_var->m_cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadIdUnique() {
|
||||
|
||||
@@ -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();
|
||||
@@ -77,7 +76,6 @@ public:
|
||||
void Signal();
|
||||
void SignalAll();
|
||||
|
||||
static void SignalThread(int thread_id);
|
||||
static void SetWaitPollCallback(wait_poll_func_t callback);
|
||||
|
||||
KYTY_CLASS_NO_COPY(CondVar);
|
||||
|
||||
@@ -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(¤t_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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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, Mode* old_mode) {
|
||||
return SysVirtualProtect(address, size, mode, old_mode);
|
||||
}
|
||||
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
return SysVirtualFlushInstructionCache(address, size);
|
||||
}
|
||||
|
||||
} // namespace VirtualMemory
|
||||
|
||||
} // namespace Common
|
||||
@@ -35,7 +35,7 @@ bool ReserveFixed(uint64_t address, uint64_t size);
|
||||
bool Decommit(uint64_t address, uint64_t size);
|
||||
bool Free(uint64_t address);
|
||||
bool FreeRange(uint64_t address, uint64_t size);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode = nullptr);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode);
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size);
|
||||
|
||||
} // namespace VirtualMemory
|
||||
|
||||
@@ -196,6 +196,10 @@ void Run(const RunOptions& options) {
|
||||
ClearDebugTextureFolder();
|
||||
|
||||
PrintSystemInfo();
|
||||
std::string title_id;
|
||||
if (Loader::SystemContentParamSfoGetString("TITLE_ID", &title_id) && !title_id.empty()) {
|
||||
Log::WriteToConsoleAndLog(fmt::format("Title ID: {}\n", title_id));
|
||||
}
|
||||
|
||||
int ok = atexit(KytyClose);
|
||||
EXIT_NOT_IMPLEMENTED(ok != 0);
|
||||
|
||||
@@ -32,11 +32,12 @@ private:
|
||||
|
||||
struct BufferCursor {
|
||||
std::span<const uint32_t> commands;
|
||||
uint32_t offset_dw = 0;
|
||||
uint32_t deferred_advance_dw = 0;
|
||||
uint32_t offset_dw = 0;
|
||||
};
|
||||
|
||||
std::vector<BufferCursor> m_buffer_stack;
|
||||
std::span<const uint32_t> m_next_buffer;
|
||||
bool m_chain = false;
|
||||
bool m_suspended = false;
|
||||
bool m_made_progress = false;
|
||||
};
|
||||
@@ -73,13 +74,9 @@ public:
|
||||
void SetDrawIndirectArgsBaseAddress(uint64_t draw_indirect_args_base_addr);
|
||||
void SetDispatchIndirectArgsBaseAddress(uint64_t dispatch_indirect_args_base_addr);
|
||||
void SetNumInstances(uint32_t num_instances);
|
||||
void DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type,
|
||||
uint32_t instance_count = 0, const void* object_ids = nullptr,
|
||||
uint32_t render_target_slice_offset = 0, int32_t vertex_offset_add = 0,
|
||||
uint32_t first_instance = 0);
|
||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
|
||||
void DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset = 0);
|
||||
void DrawIndex(DrawIndexArgs args);
|
||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count);
|
||||
void DrawIndexAuto(DrawAutoArgs args);
|
||||
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
|
||||
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
|
||||
@@ -133,7 +130,7 @@ public:
|
||||
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
||||
|
||||
Pm4ProcessResult Process(Pm4Execution& execution, std::span<const uint32_t> commands);
|
||||
void ProcessIndirectBuffer(std::span<const uint32_t> commands);
|
||||
void ProcessIndirectBuffer(std::span<const uint32_t> commands, bool chain);
|
||||
|
||||
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
||||
|
||||
@@ -147,16 +144,11 @@ private:
|
||||
uint32_t cache_action, uint32_t event_index, uint32_t event_write_source,
|
||||
void* dst_gpu_addr, T value, uint32_t interrupt_selector,
|
||||
uint32_t interrupt_context_id);
|
||||
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
||||
void ProcessPm4(Pm4Execution& execution);
|
||||
void SuspendPm4();
|
||||
void SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset, uint32_t first_vertex,
|
||||
uint32_t first_instance);
|
||||
|
||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||
void CheckBuffer() const { GetScheduler().CheckActive(); }
|
||||
GpuResourceManager& GetGpuResources() const { return m_renderer.GetGpuResources(); }
|
||||
|
||||
RenderContext& m_renderer;
|
||||
HW::Context m_ctx;
|
||||
|
||||
@@ -24,7 +24,6 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::DB_DEPTH_CLEAR] = HwCtxSetDepthClear;
|
||||
g_hw_ctx_func[Pm4::PA_SC_SCREEN_SCISSOR_TL] = HwCtxSetScreenScissor;
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_INFO] = HwCtxSetStencilInfo;
|
||||
g_hw_ctx_func[Pm4::DB_SHADING_RATE_ENCODING] = HwCtxSetDepthShadingRateEncoding;
|
||||
g_hw_ctx_func[Pm4::PA_SU_HARDWARE_SCREEN_OFFSET] = HwCtxSetHardwareScreenOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_OFFSET] = HwCtxSetWindowOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_SCISSOR_TL] = HwCtxSetWindowScissor;
|
||||
@@ -140,13 +139,13 @@ constexpr auto MakeShaderDispatchTable() {
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMPRING_SIZE] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC3] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PACE_ID] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_PS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = HwShSetRegisters;
|
||||
|
||||
return g_hw_sh_func;
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ uint32_t HwCtxSetStencilClear(CommandProcessor&, uint32_t, uint32_t, const uint3
|
||||
uint32_t HwCtxSetDepthClear(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetScreenScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetStencilInfo(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthShadingRateEncoding(CommandProcessor&, uint32_t, uint32_t,
|
||||
const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetHardwareScreenOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetWindowOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -100,7 +98,7 @@ uint32_t HwShSetCsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t
|
||||
uint32_t HwShIgnoreUserAccumulator(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwShSetCsRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShIgnoreRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetPrimitiveType(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetIndexType(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetObjectId(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
|
||||
@@ -315,8 +315,17 @@ static void HwCtxSetDepthBoundsRegister(CommandProcessor& cp, uint32_t cmd_offse
|
||||
}
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreDepthMetadataRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
[[maybe_unused]] uint32_t value) {}
|
||||
static void HwCtxSetDepthMetadataRegister(CommandProcessor& cp, uint32_t cmd_offset,
|
||||
uint32_t value) {
|
||||
if (cmd_offset == Pm4::DB_RENDER_OVERRIDE) {
|
||||
HW::DepthRenderOverride control;
|
||||
control.force_z_valid = (value & 0x20000000u) != 0;
|
||||
control.force_z_dirty = (value & 0x08000000u) != 0;
|
||||
control.force_stencil_valid = (value & 0x40000000u) != 0;
|
||||
control.force_stencil_dirty = (value & 0x10000000u) != 0;
|
||||
cp.GetCtx().SetDepthRenderOverride(control);
|
||||
}
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreFovWindow([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
@@ -389,7 +398,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetDepthMetadataRegisters) {
|
||||
auto num_values = KYTY_PM4_LEN(cmd_id) - 2u;
|
||||
|
||||
for (uint32_t i = 0; i < num_values; i++) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset + i, buffer[i]);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset + i, buffer[i]);
|
||||
}
|
||||
|
||||
return num_values;
|
||||
@@ -599,17 +608,6 @@ static void HwCtxSetDepthShadingRateEncodingRegister(CommandProcessor& cp, uint3
|
||||
ctx.SetDepthRenderTarget(target);
|
||||
}
|
||||
|
||||
KYTY_HW_CTX_PARSER(HwCtxSetDepthShadingRateEncoding) {
|
||||
auto num_values = KYTY_PM4_LEN(cmd_id) - 2u;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(num_values != 1);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_SHADING_RATE_ENCODING);
|
||||
|
||||
HwCtxSetDepthShadingRateEncodingRegister(cp, buffer[0]);
|
||||
|
||||
return num_values;
|
||||
}
|
||||
|
||||
static HW::EqaaControl ParseEqaaControl(uint32_t value) {
|
||||
HW::EqaaControl r;
|
||||
|
||||
@@ -991,7 +989,7 @@ static void HwShIgnoreComputeRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
static void HwShIgnoreShaderRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
[[maybe_unused]] uint32_t value) {}
|
||||
|
||||
KYTY_HW_SH_PARSER(HwShIgnoreRegisters) {
|
||||
KYTY_HW_SH_PARSER(HwShSetRegisters) {
|
||||
const auto reg_num = (cmd_id >> 16u) & 0x3fffu;
|
||||
EXIT_NOT_IMPLEMENTED(reg_num == 0);
|
||||
|
||||
@@ -1497,10 +1495,10 @@ KYTY_CP_OP_PARSER(CpOpBranch) {
|
||||
reinterpret_cast<uint64_t>(else_buffer), else_num_dw);
|
||||
|
||||
if (take_then) {
|
||||
cp.ProcessIndirectBuffer({then_buffer, then_num_dw});
|
||||
cp.ProcessIndirectBuffer({then_buffer, then_num_dw}, true);
|
||||
} else if (mode == 2 && else_num_dw != 0) {
|
||||
EXIT_NOT_IMPLEMENTED(else_buffer == nullptr);
|
||||
cp.ProcessIndirectBuffer({else_buffer, else_num_dw});
|
||||
cp.ProcessIndirectBuffer({else_buffer, else_num_dw}, true);
|
||||
}
|
||||
|
||||
return payload_dw;
|
||||
@@ -1618,15 +1616,15 @@ KYTY_CP_OP_PARSER(CpOpDrawIndex) {
|
||||
auto* index_addr =
|
||||
reinterpret_cast<void*>(buffer[1] | (static_cast<uint64_t>(buffer[2]) << 32u));
|
||||
uint32_t max_instance_count = buffer[3];
|
||||
auto* object_ids =
|
||||
reinterpret_cast<void*>(buffer[4] | (static_cast<uint64_t>(buffer[5]) << 32u));
|
||||
uint32_t instance_count = buffer[6];
|
||||
uint32_t flags = buffer[7];
|
||||
uint32_t instance_count = buffer[6];
|
||||
uint32_t flags = buffer[7];
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(instance_count > max_instance_count);
|
||||
EXIT_NOT_IMPLEMENTED((flags & ~0xa0u) != 0);
|
||||
|
||||
cp.DrawIndex(index_count, index_addr, 0, 1, instance_count, object_ids);
|
||||
cp.DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = instance_count});
|
||||
|
||||
return 8;
|
||||
}
|
||||
@@ -1641,7 +1639,7 @@ KYTY_CP_OP_PARSER(CpOpDrawIndex) {
|
||||
EXIT_NOT_IMPLEMENTED(index_count > max_index_count);
|
||||
EXIT_NOT_IMPLEMENTED((flags & ~0x20u) != 0);
|
||||
|
||||
cp.DrawIndex(index_count, index_addr, 0, 1);
|
||||
cp.DrawIndex({.index_count = index_count, .index_addr = index_addr});
|
||||
|
||||
return 5;
|
||||
}
|
||||
@@ -1700,7 +1698,7 @@ KYTY_CP_OP_PARSER(CpOpDrawIndexOffset) {
|
||||
EXIT_NOT_IMPLEMENTED(index_count > max_index_size);
|
||||
EXIT_NOT_IMPLEMENTED((flags & ~0x20u) != 0);
|
||||
|
||||
cp.DrawIndexOffset(index_offset, index_count, 0);
|
||||
cp.DrawIndexOffset(index_offset, index_count);
|
||||
|
||||
return 4;
|
||||
}
|
||||
@@ -1715,7 +1713,7 @@ KYTY_CP_OP_PARSER(CpOpDrawIndexAuto) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED((flags & ~0x22u) != 0);
|
||||
|
||||
cp.DrawIndexAuto(index_count, 0);
|
||||
cp.DrawIndexAuto({.vertex_count = index_count});
|
||||
|
||||
return 2;
|
||||
}
|
||||
@@ -1961,7 +1959,7 @@ KYTY_CP_OP_PARSER(CpOpIndirectBuffer) {
|
||||
|
||||
GraphicsDbgDumpDcb("ci", indirect_num_dw, indirect_buffer);
|
||||
|
||||
cp.ProcessIndirectBuffer({indirect_buffer, indirect_num_dw});
|
||||
cp.ProcessIndirectBuffer({indirect_buffer, indirect_num_dw}, (control & (1u << 20u)) != 0);
|
||||
|
||||
return 3;
|
||||
}
|
||||
@@ -3014,28 +3012,28 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
HwCtxIgnoreCbDccControl(value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_COUNT_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
for (auto cmd_offset = Pm4::DB_SRESULTS_COMPARE_STATE0;
|
||||
cmd_offset <= Pm4::DB_SRESULTS_COMPARE_STATE1; cmd_offset++) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
}
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE2] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_DFSM_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RMI_L2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::CB_RMI_GL2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::TA_BC_BASE_ADDR] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreBorderColorTableAddr(cmd_offset, value);
|
||||
@@ -3637,15 +3635,21 @@ void GraphicsInitJmpTablesShIndirect() {
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
// Toolkit state restoration emits the compiler's GS-front allocation
|
||||
// metadata here. Native GS resource registers already carry that allocation.
|
||||
g_hw_sh_indirect_func[0x0ca] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
g_hw_sh_indirect_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
for (uint32_t offset = Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS;
|
||||
offset <= Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS; offset++) {
|
||||
g_hw_sh_indirect_func[offset] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
cp.GetShCtx().SetGsUserDataAddress(cmd_offset - Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS,
|
||||
value);
|
||||
};
|
||||
}
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_CHKSUM_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
@@ -3655,12 +3659,13 @@ void GraphicsInitJmpTablesShIndirect() {
|
||||
g_hw_sh_indirect_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HwShIgnoreShaderRegister(cmd_offset, value);
|
||||
};
|
||||
for (uint32_t offset = Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS;
|
||||
offset <= Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS; offset++) {
|
||||
g_hw_sh_indirect_func[offset] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
cp.GetShCtx().SetHsUserDataAddress(cmd_offset - Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS,
|
||||
value);
|
||||
};
|
||||
}
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_LO_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
auto base = cp.GetShCtx().GetVs().hs_regs.data_addr;
|
||||
base &= 0xFFFFFF00000000FFull;
|
||||
@@ -3819,6 +3824,25 @@ void GraphicsInitJmpTablesUcIndirect() {
|
||||
for (auto& func: g_hw_uc_indirect_func) {
|
||||
func = nullptr;
|
||||
}
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
g_hw_uc_indirect_func[Pm4::FSR_CONTROL_POINTS_LEFT_X + i] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.GetUcfg().SetFsrControlPoint(0, cmd_offset - Pm4::FSR_CONTROL_POINTS_LEFT_X, value);
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::FSR_CONTROL_POINTS_LEFT_Y + i] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.GetUcfg().SetFsrControlPoint(1, cmd_offset - Pm4::FSR_CONTROL_POINTS_LEFT_Y, value);
|
||||
};
|
||||
}
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
g_hw_uc_indirect_func[Pm4::FSR_ALPHA_LEFT_X + i] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.GetUcfg().SetFsrAlpha(0, cmd_offset - Pm4::FSR_ALPHA_LEFT_X, value);
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::FSR_ALPHA_LEFT_Y + i] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.GetUcfg().SetFsrAlpha(1, cmd_offset - Pm4::FSR_ALPHA_LEFT_Y, value);
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::FSR_WINDOW_LEFT + i] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.GetUcfg().SetFsrWindow(cmd_offset - Pm4::FSR_WINDOW_LEFT, value);
|
||||
};
|
||||
}
|
||||
|
||||
g_hw_uc_indirect_func[Pm4::GE_CNTL] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
HW::GeControl r;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -15,6 +15,7 @@ struct FormatInfo {
|
||||
uint32_t render_target_bytes_per_element;
|
||||
bool sampled_texture;
|
||||
bool uint_texture;
|
||||
bool sint_texture = false;
|
||||
};
|
||||
|
||||
constexpr FormatInfo kFormatInfo[] = {
|
||||
@@ -24,19 +25,19 @@ constexpr FormatInfo kFormatInfo[] = {
|
||||
{BufferFormat::k16UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k16Float, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k32UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k11_11_10UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
|
||||
@@ -45,20 +46,20 @@ constexpr FormatInfo kFormatInfo[] = {
|
||||
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32_32UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k32_32Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16SNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k16_16_16_16Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k32_32_32UInt, 12, 0, 12, true, true},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, false, false},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, true, false, true},
|
||||
{BufferFormat::k32_32_32Float, 12, 0, 12, true, false},
|
||||
{BufferFormat::k32_32_32_32UInt, 16, 0, 16, true, true},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, false, false},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, true, false, true},
|
||||
{BufferFormat::k32_32_32_32Float, 16, 0, 16, true, false},
|
||||
{BufferFormat::k8Srgb, 1, 0, 0, true, false},
|
||||
{BufferFormat::k8_8Srgb, 2, 0, 0, true, false},
|
||||
@@ -218,14 +219,15 @@ bool IsFmaskTextureFormat(BufferFormat format) {
|
||||
return format >= BufferFormat::kFmask8_S2_F1 && format <= BufferFormat::kFmask64_S16_F8;
|
||||
}
|
||||
|
||||
bool IsSampledTextureFormat(BufferFormat format) {
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->sampled_texture;
|
||||
}
|
||||
|
||||
bool IsUintTextureFormat(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->uint_texture;
|
||||
if (info == nullptr || !info->sampled_texture) {
|
||||
return TextureNumericClass::Unsupported;
|
||||
}
|
||||
if (info->sint_texture) {
|
||||
return TextureNumericClass::Sint;
|
||||
}
|
||||
return info->uint_texture ? TextureNumericClass::Uint : TextureNumericClass::Float;
|
||||
}
|
||||
|
||||
BufferFormat RemapTextureFormat(BufferFormat format) {
|
||||
|
||||
@@ -37,13 +37,14 @@ struct RenderTargetFormatEncoding {
|
||||
}
|
||||
};
|
||||
|
||||
enum class TextureNumericClass { Unsupported, Float, Uint, Sint };
|
||||
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type);
|
||||
uint32_t NumBytesPerElement(BufferFormat format);
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
||||
bool IsFmaskTextureFormat(BufferFormat format);
|
||||
bool IsSampledTextureFormat(BufferFormat format);
|
||||
bool IsUintTextureFormat(BufferFormat format);
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format);
|
||||
BufferFormat RemapTextureFormat(BufferFormat format);
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/renderer/sync.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
#include "graphics/presentation/window.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
@@ -40,6 +39,21 @@ static thread_local bool g_gpu_mutex_owned = false;
|
||||
static thread_local bool g_gpu_thread = false;
|
||||
static thread_local GuestGpu* g_gpu_state = nullptr;
|
||||
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
class GpuMutexLock final {
|
||||
public:
|
||||
explicit GpuMutexLock(Common::Mutex& mutex): m_mutex(mutex) {
|
||||
@@ -63,7 +77,7 @@ private:
|
||||
|
||||
static bool GraphicsRunDebugDumpEnabled() {
|
||||
return Config::GraphicsDebugDumpEnabled() &&
|
||||
Config::GetPrintfDirection() != Config::OutputDirection::Silent;
|
||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||
}
|
||||
|
||||
GuestGpu::GuestGpu(RenderContext& renderer): m_renderer(renderer) {
|
||||
@@ -372,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,
|
||||
@@ -413,7 +431,7 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
if (!decode_gds(dst_sel, dst_gds)) {
|
||||
EXIT("unsupported dmaData destination selector 0x%02" PRIx8 "\n", dst_sel);
|
||||
}
|
||||
auto& buffer_cache = GetGpuResources().GetBufferCache();
|
||||
auto& buffer_cache = m_renderer.GetBufferCache();
|
||||
if (src_sel == 2) {
|
||||
buffer_cache.FillBuffer(
|
||||
dst_address_or_offset, num_bytes,
|
||||
@@ -546,10 +564,6 @@ void GuestGpu::ThreadRun(void* data) {
|
||||
|
||||
bool GuestGpu::Process(Submission& submission) {
|
||||
const bool first_slice = !submission.started;
|
||||
if (first_slice && RenderDocCaptureRequested()) {
|
||||
Common::LockGuard render_lock(m_renderer.GetMutex());
|
||||
RenderDocStartCapture();
|
||||
}
|
||||
auto& cp = GetProcessor(submission.queue_id);
|
||||
|
||||
if (first_slice && submission.reset_processor) {
|
||||
@@ -593,11 +607,11 @@ bool GuestGpu::Process(Submission& submission) {
|
||||
}
|
||||
if (progressed) {
|
||||
if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
cp.BufferFlush();
|
||||
} else if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -619,16 +633,16 @@ bool GuestGpu::Process(Submission& submission) {
|
||||
Pm4ProcessResult::Complete;
|
||||
if (submission.command_execution.MadeProgress()) {
|
||||
if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
cp.BufferFlush();
|
||||
} else if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionType::FlipPreparation:
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
cp.PrepareCpuFlip(submission.flip_request_id);
|
||||
break;
|
||||
}
|
||||
@@ -662,20 +676,16 @@ Pm4ProcessResult CommandProcessor::Process(Pm4Execution& execution,
|
||||
Pm4Execution* previous_execution;
|
||||
} execution_scope(*this, execution);
|
||||
|
||||
ProcessPm4(execution, 0);
|
||||
ProcessPm4(execution);
|
||||
return execution.m_buffer_stack.empty() ? Pm4ProcessResult::Complete
|
||||
: Pm4ProcessResult::Blocked;
|
||||
}
|
||||
|
||||
void CommandProcessor::ProcessIndirectBuffer(std::span<const uint32_t> commands) {
|
||||
void CommandProcessor::ProcessIndirectBuffer(std::span<const uint32_t> commands, bool chain) {
|
||||
EXIT_IF(g_current_execution == nullptr);
|
||||
if (commands.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& execution = *g_current_execution;
|
||||
const auto stop_depth = execution.m_buffer_stack.size();
|
||||
execution.m_buffer_stack.push_back({commands});
|
||||
ProcessPm4(execution, stop_depth);
|
||||
EXIT_IF(!g_current_execution->m_next_buffer.empty());
|
||||
g_current_execution->m_next_buffer = commands;
|
||||
g_current_execution->m_chain = chain;
|
||||
}
|
||||
|
||||
void CommandProcessor::SuspendPm4() {
|
||||
@@ -683,21 +693,13 @@ void CommandProcessor::SuspendPm4() {
|
||||
g_current_execution->m_suspended = true;
|
||||
}
|
||||
|
||||
void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
while (execution.m_buffer_stack.size() > stop_depth) {
|
||||
void CommandProcessor::ProcessPm4(Pm4Execution& execution) {
|
||||
while (!execution.m_buffer_stack.empty()) {
|
||||
if (g_gpu_state != nullptr) {
|
||||
g_gpu_state->ProcessCommands();
|
||||
}
|
||||
const auto buffer_index = execution.m_buffer_stack.size() - 1;
|
||||
auto& cursor = execution.m_buffer_stack[buffer_index];
|
||||
auto& cursor = execution.m_buffer_stack.back();
|
||||
EXIT_IF(cursor.offset_dw > cursor.commands.size());
|
||||
if (cursor.deferred_advance_dw != 0) {
|
||||
EXIT_IF(cursor.deferred_advance_dw > cursor.commands.size() - cursor.offset_dw);
|
||||
cursor.offset_dw += cursor.deferred_advance_dw;
|
||||
cursor.deferred_advance_dw = 0;
|
||||
execution.m_made_progress = true;
|
||||
continue;
|
||||
}
|
||||
if (cursor.offset_dw == cursor.commands.size()) {
|
||||
execution.m_buffer_stack.pop_back();
|
||||
continue;
|
||||
@@ -773,14 +775,19 @@ void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
handler(*this, packet_header & ~1u, packet + 1, remaining_dw, total_dw) + 1;
|
||||
EXIT_IF(packet_dw > remaining_dw);
|
||||
if (execution.m_suspended) {
|
||||
if (execution.m_buffer_stack.size() > buffer_index + 1) {
|
||||
execution.m_buffer_stack[buffer_index].deferred_advance_dw = packet_dw;
|
||||
}
|
||||
return;
|
||||
}
|
||||
EXIT_IF(execution.m_buffer_stack.size() != buffer_index + 1);
|
||||
execution.m_buffer_stack[buffer_index].offset_dw += packet_dw;
|
||||
cursor.offset_dw += packet_dw;
|
||||
execution.m_made_progress = true;
|
||||
if (!execution.m_next_buffer.empty()) {
|
||||
// Chains and taken branches reuse the fetcher; only calls retain a return cursor.
|
||||
if (execution.m_chain) {
|
||||
cursor = {execution.m_next_buffer};
|
||||
} else {
|
||||
execution.m_buffer_stack.push_back({execution.m_next_buffer});
|
||||
}
|
||||
execution.m_next_buffer = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,68 +822,72 @@ void CommandProcessor::SetNumInstances(uint32_t num_instances) {
|
||||
|
||||
void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t wait_op,
|
||||
const volatile void* address, uint32_t count_in_dwords) {
|
||||
if (wait_op != 0) {
|
||||
BufferFlushAndWait();
|
||||
}
|
||||
|
||||
(void)count_in_dwords;
|
||||
uint64_t value = 0;
|
||||
|
||||
switch (op) {
|
||||
case 0x00: {
|
||||
case 0x00:
|
||||
m_predicate_skip = false;
|
||||
} break;
|
||||
case 0x03: {
|
||||
return;
|
||||
case 0x01: {
|
||||
EXIT_NOT_IMPLEMENTED(address == nullptr);
|
||||
|
||||
auto value = *reinterpret_cast<const volatile uint64_t*>(address);
|
||||
|
||||
switch (condition) {
|
||||
case 0x00: m_predicate_skip = (value != 0); break;
|
||||
case 0x01: m_predicate_skip = (value == 0); break;
|
||||
default: EXIT("unknown predication condition: 0x%08" PRIx32 "\n", condition);
|
||||
}
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 128) {
|
||||
LOGF("\t bool predication: addr=0x%016" PRIx64 ", value=0x%016" PRIx64
|
||||
", condition=%" PRIu32 ", skip=%u, wait_op=%" PRIu32 "\n",
|
||||
reinterpret_cast<uint64_t>(address), value, condition,
|
||||
m_predicate_skip ? 1u : 0u, wait_op);
|
||||
// One begin/end pair per DB; bit 63 marks each counter ready.
|
||||
constexpr uint64_t ready_bit = 1ull << 63u;
|
||||
const auto* results = reinterpret_cast<const volatile uint64_t*>(address);
|
||||
for (uint32_t db = 0; db < 16u; db++) {
|
||||
const auto begin = results[db * 2u];
|
||||
const auto end = results[db * 2u + 1u];
|
||||
if ((begin & end & ready_bit) == 0) {
|
||||
if (wait_op == 0) {
|
||||
SuspendPm4();
|
||||
} else {
|
||||
m_predicate_skip = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
value += end - begin;
|
||||
}
|
||||
} break;
|
||||
case 0x03:
|
||||
if (wait_op != 0) {
|
||||
BufferFlushAndWait();
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(address == nullptr);
|
||||
value = *reinterpret_cast<const volatile uint64_t*>(address);
|
||||
break;
|
||||
default: EXIT("unknown predication op: 0x%08" PRIx32 "\n", op);
|
||||
}
|
||||
switch (condition) {
|
||||
case 0x00: m_predicate_skip = (value != 0); break;
|
||||
case 0x01: m_predicate_skip = (value == 0); break;
|
||||
default: EXIT("unknown predication condition: 0x%08" PRIx32 "\n", condition);
|
||||
}
|
||||
if (op == 0x03) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 128) {
|
||||
LOGF("\t bool predication: addr=0x%016" PRIx64 ", value=0x%016" PRIx64
|
||||
", condition=%" PRIu32 ", skip=%u, wait_op=%" PRIu32 "\n",
|
||||
reinterpret_cast<uint64_t>(address), value, condition,
|
||||
m_predicate_skip ? 1u : 0u, wait_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags,
|
||||
uint32_t type, uint32_t instance_count, const void* object_ids,
|
||||
uint32_t render_target_slice_offset, int32_t vertex_offset_add,
|
||||
uint32_t first_instance) {
|
||||
void CommandProcessor::DrawIndex(DrawIndexArgs args) {
|
||||
CheckBuffer();
|
||||
|
||||
if (instance_count == 0) {
|
||||
instance_count = m_num_instances;
|
||||
args.index_type_and_size = m_index_type_and_size;
|
||||
if (args.instance_count == 0) {
|
||||
args.instance_count = m_num_instances;
|
||||
}
|
||||
if (object_ids != nullptr) {
|
||||
LOGF("\t draw indexed multi-instanced objectIds = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(object_ids));
|
||||
if (args.base_vertex != 0 || args.first_instance != 0) {
|
||||
LOGF("\t draw indexed offsets: base_vertex = %" PRId32 ", first_instance = %" PRIu32 "\n",
|
||||
args.base_vertex, args.first_instance);
|
||||
}
|
||||
if (render_target_slice_offset != 0) {
|
||||
LOGF("\t draw render target slice offset = %" PRIu32 "\n", render_target_slice_offset);
|
||||
}
|
||||
if (vertex_offset_add != 0 || first_instance != 0) {
|
||||
LOGF("\t draw indexed offsets: vertex_offset_add = %" PRId32 ", first_instance = %" PRIu32
|
||||
"\n",
|
||||
vertex_offset_add, first_instance);
|
||||
}
|
||||
m_renderer.GetRenderExecutor().DrawIndex(
|
||||
m_submit_id, CurrentBuffer(), m_index_type_and_size, index_count, index_addr, flags, type,
|
||||
instance_count, render_target_slice_offset, vertex_offset_add, first_instance);
|
||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), args);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count,
|
||||
uint32_t flags) {
|
||||
CheckBuffer();
|
||||
|
||||
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count) {
|
||||
uint64_t index_size = 0;
|
||||
switch (m_index_type_and_size) {
|
||||
case 0: index_size = 2; break;
|
||||
@@ -888,25 +899,10 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
|
||||
auto* index_addr = reinterpret_cast<const void*>(
|
||||
m_index_base_addr + static_cast<uint64_t>(index_offset) * index_size);
|
||||
|
||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), m_index_type_and_size,
|
||||
index_count, index_addr, flags, 1, m_num_instances);
|
||||
DrawIndex({.index_count = index_count, .index_addr = index_addr});
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
@@ -928,8 +924,11 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
}
|
||||
m_num_instances = args.instance_count;
|
||||
SubmitNonIndexedDraw(args.vertex_count_per_instance, 0, 0, args.start_vertex_location,
|
||||
args.start_instance_location);
|
||||
DrawIndexAuto({.vertex_count = args.vertex_count_per_instance,
|
||||
.instance_count = args.instance_count,
|
||||
.first_vertex = args.start_vertex_location,
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -970,28 +969,18 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
|
||||
m_num_instances = args.instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
||||
DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = args.instance_count,
|
||||
.base_vertex = static_cast<int32_t>(args.base_vertex_location),
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr,
|
||||
uint32_t stride_in_bytes, uint32_t draw_initiator,
|
||||
bool indexed) {
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
@@ -1028,8 +1017,11 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
}
|
||||
}
|
||||
m_num_instances = args->instance_count;
|
||||
SubmitNonIndexedDraw(args->vertex_count_per_instance, 0, 0, args->start_vertex_location,
|
||||
args->start_instance_location);
|
||||
DrawIndexAuto({.vertex_count = args->vertex_count_per_instance,
|
||||
.instance_count = args->instance_count,
|
||||
.first_vertex = args->start_vertex_location,
|
||||
.first_instance = args->start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1071,8 +1063,12 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
}
|
||||
|
||||
m_num_instances = args->instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
||||
DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = args->instance_count,
|
||||
.base_vertex = static_cast<int32_t>(args->base_vertex_location),
|
||||
.first_instance = args->start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,17 +1106,6 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
// local_x = std::max(cs.num_thread_x, 1u);
|
||||
// local_y = std::max(cs.num_thread_y, 1u);
|
||||
// local_z = std::max(cs.num_thread_z, 1u);
|
||||
if (cs.wave_size == 64u) {
|
||||
static std::atomic_bool logged_wave64_shader {false};
|
||||
if (!logged_wave64_shader.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("warning: executing wave64 compute shader cs=0x%016" PRIx64 "\n",
|
||||
cs.data_addr);
|
||||
std::printf("warning: executing wave64 compute shader cs=0x%016" PRIx64 "\n",
|
||||
cs.data_addr);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
m_renderer.GetRenderExecutor().DispatchDirect(m_submit_id, CurrentBuffer(), thread_group_x,
|
||||
thread_group_y, thread_group_z, mode);
|
||||
}
|
||||
@@ -1163,19 +1148,13 @@ void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
||||
DispatchDirect(args->thread_group_x, args->thread_group_y, args->thread_group_z, mode);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset) {
|
||||
SubmitNonIndexedDraw(index_count, flags, render_target_slice_offset, 0, 0);
|
||||
}
|
||||
|
||||
void CommandProcessor::SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t first_vertex, uint32_t first_instance) {
|
||||
void CommandProcessor::DrawIndexAuto(DrawAutoArgs args) {
|
||||
CheckBuffer();
|
||||
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), vertex_count, flags,
|
||||
render_target_slice_offset, m_num_instances,
|
||||
first_vertex, first_instance);
|
||||
if (args.instance_count == 0) {
|
||||
args.instance_count = m_num_instances;
|
||||
}
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), args);
|
||||
}
|
||||
|
||||
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index) {
|
||||
@@ -1318,6 +1297,7 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
break;
|
||||
case 0x14:
|
||||
case 0x28:
|
||||
case 0x2f:
|
||||
if (event_index == 0x00) {
|
||||
write64(false);
|
||||
return;
|
||||
@@ -1325,7 +1305,6 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
break;
|
||||
case 0x2b:
|
||||
case 0x2d:
|
||||
case 0x2f:
|
||||
case 0x30:
|
||||
if (event_index == 0x00 && !with_interrupt) {
|
||||
write64(false);
|
||||
|
||||
@@ -202,6 +202,13 @@ struct RenderControl {
|
||||
uint8_t copy_sample = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderOverride {
|
||||
bool force_z_valid = false;
|
||||
bool force_z_dirty = false;
|
||||
bool force_stencil_valid = false;
|
||||
bool force_stencil_dirty = false;
|
||||
};
|
||||
|
||||
struct GdsOaCounter {
|
||||
uint32_t counter = 0;
|
||||
uint32_t address = 0;
|
||||
@@ -362,16 +369,16 @@ struct AaConfig {
|
||||
struct Viewport {
|
||||
float zmin = 0.0f;
|
||||
float zmax = 0.0f;
|
||||
float xscale = 0.0f;
|
||||
float xscale = 1.0f;
|
||||
float xoffset = 0.0f;
|
||||
float yscale = 0.0f;
|
||||
float yscale = 1.0f;
|
||||
float yoffset = 0.0f;
|
||||
float zscale = 0.0f;
|
||||
float zscale = 1.0f;
|
||||
float zoffset = 0.0f;
|
||||
int viewport_scissor_left = 0;
|
||||
int viewport_scissor_top = 0;
|
||||
int viewport_scissor_right = 0;
|
||||
int viewport_scissor_bottom = 0;
|
||||
int viewport_scissor_right = 16384;
|
||||
int viewport_scissor_bottom = 16384;
|
||||
bool viewport_scissor_window_offset_enable = false;
|
||||
};
|
||||
|
||||
@@ -380,26 +387,26 @@ struct ScreenViewport {
|
||||
uint32_t transform_control = 1087;
|
||||
int screen_scissor_left = 0;
|
||||
int screen_scissor_top = 0;
|
||||
int screen_scissor_right = 0;
|
||||
int screen_scissor_bottom = 0;
|
||||
int screen_scissor_right = 16384;
|
||||
int screen_scissor_bottom = 16384;
|
||||
int window_scissor_left = 0;
|
||||
int window_scissor_top = 0;
|
||||
int window_scissor_right = 0;
|
||||
int window_scissor_bottom = 0;
|
||||
int window_scissor_right = 16384;
|
||||
int window_scissor_bottom = 16384;
|
||||
bool window_scissor_window_offset_enable = false;
|
||||
int generic_scissor_left = 0;
|
||||
int generic_scissor_top = 0;
|
||||
int generic_scissor_right = 0;
|
||||
int generic_scissor_bottom = 0;
|
||||
int generic_scissor_right = 16384;
|
||||
int generic_scissor_bottom = 16384;
|
||||
bool generic_scissor_window_offset_enable = false;
|
||||
int window_offset_x = 0;
|
||||
int window_offset_y = 0;
|
||||
uint32_t hw_offset_x = 0;
|
||||
uint32_t hw_offset_y = 0;
|
||||
float guard_band_horz_clip = 0.0f;
|
||||
float guard_band_vert_clip = 0.0f;
|
||||
float guard_band_horz_discard = 0.0f;
|
||||
float guard_band_vert_discard = 0.0f;
|
||||
float guard_band_horz_clip = 1.0f;
|
||||
float guard_band_vert_clip = 1.0f;
|
||||
float guard_band_horz_discard = 1.0f;
|
||||
float guard_band_vert_discard = 1.0f;
|
||||
uint16_t clip_rect_rule = 0xffffu;
|
||||
int clip_rect_left[4] = {};
|
||||
int clip_rect_top[4] = {};
|
||||
@@ -491,7 +498,8 @@ struct LsStageRegisters {
|
||||
};
|
||||
|
||||
struct HsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t user_data_addr = 0;
|
||||
HsShaderResource1 rsrc1;
|
||||
HsShaderResource2 rsrc2;
|
||||
};
|
||||
@@ -520,7 +528,8 @@ struct GsShaderResource2 {
|
||||
};
|
||||
|
||||
struct GsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t user_data_addr = 0;
|
||||
GsShaderResource1 rsrc1;
|
||||
GsShaderResource2 rsrc2;
|
||||
};
|
||||
@@ -808,6 +817,12 @@ public:
|
||||
void SetClipControl(const ClipControl& control) { m_clip_control = control; }
|
||||
[[nodiscard]] const RenderControl& GetRenderControl() const { return m_render_control; }
|
||||
void SetRenderControl(const RenderControl& control) { m_render_control = control; }
|
||||
[[nodiscard]] const DepthRenderOverride& GetDepthRenderOverride() const {
|
||||
return m_depth_render_override;
|
||||
}
|
||||
void SetDepthRenderOverride(const DepthRenderOverride& control) {
|
||||
m_depth_render_override = control;
|
||||
}
|
||||
[[nodiscard]] const DepthControl& GetDepthControl() const { return m_depth_control; }
|
||||
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
|
||||
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
|
||||
@@ -903,15 +918,16 @@ private:
|
||||
|
||||
uint32_t m_shader_stages = 0;
|
||||
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthRenderOverride m_depth_render_override;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
|
||||
ModeControl m_mode_control;
|
||||
PolyOffset m_poly_offset;
|
||||
@@ -920,6 +936,12 @@ private:
|
||||
ShaderRegisters m_sh_regs;
|
||||
};
|
||||
|
||||
struct FsrView {
|
||||
uint32_t control_points[2][4] {};
|
||||
uint32_t alphas[2][2] {};
|
||||
uint32_t window[2] {};
|
||||
};
|
||||
|
||||
class UserConfig {
|
||||
public:
|
||||
UserConfig() = default;
|
||||
@@ -949,8 +971,17 @@ public:
|
||||
void SetGdsOaCntl(uint32_t value) { m_gds_oa.cntl = value; }
|
||||
void SetGdsOaCounter(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].counter = value; }
|
||||
void SetGdsOaAddress(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].address = value; }
|
||||
void SetFsrControlPoint(uint32_t axis, uint32_t index, uint32_t value) {
|
||||
m_fsr_view.control_points[axis][index] = value;
|
||||
}
|
||||
void SetFsrAlpha(uint32_t axis, uint32_t index, uint32_t value) {
|
||||
m_fsr_view.alphas[axis][index] = value;
|
||||
}
|
||||
void SetFsrWindow(uint32_t index, uint32_t value) { m_fsr_view.window[index] = value; }
|
||||
[[nodiscard]] const FsrView& GetFsrView() const { return m_fsr_view; }
|
||||
|
||||
private:
|
||||
FsrView m_fsr_view;
|
||||
Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone;
|
||||
uint32_t m_index_offset = 0;
|
||||
uint32_t m_object_id = 0;
|
||||
@@ -973,9 +1004,15 @@ public:
|
||||
void SetEsShaderBase(uint64_t addr) { m_vs.es_regs.data_addr = addr; }
|
||||
void SetLsShaderBase(uint64_t addr) { m_vs.ls_regs.data_addr = addr; }
|
||||
void SetHsShaderBase(uint64_t addr) { m_vs.hs_regs.data_addr = addr; }
|
||||
void SetHsUserDataAddress(uint32_t word, uint32_t value) {
|
||||
SetUserDataAddressWord(m_vs.hs_regs.user_data_addr, word, value);
|
||||
}
|
||||
void SetHsShaderResource1(const HsShaderResource1& rsrc1) { m_vs.hs_regs.rsrc1 = rsrc1; }
|
||||
void SetHsShaderResource2(const HsShaderResource2& rsrc2) { m_vs.hs_regs.rsrc2 = rsrc2; }
|
||||
void SetGsShaderBase(uint64_t addr) { m_vs.gs_regs.data_addr = addr; }
|
||||
void SetGsUserDataAddress(uint32_t word, uint32_t value) {
|
||||
SetUserDataAddressWord(m_vs.gs_regs.user_data_addr, word, value);
|
||||
}
|
||||
void SetGsShaderResource1(const GsShaderResource1& rsrc1) { m_vs.gs_regs.rsrc1 = rsrc1; }
|
||||
void SetGsShaderResource2(const GsShaderResource2& rsrc2) { m_vs.gs_regs.rsrc2 = rsrc2; }
|
||||
|
||||
@@ -1016,6 +1053,12 @@ public:
|
||||
[[nodiscard]] const ComputeShaderInfo& GetCs() const { return m_cs; }
|
||||
|
||||
private:
|
||||
static void SetUserDataAddressWord(uint64_t& address, uint32_t word, uint32_t value) {
|
||||
const auto shift = word * 32u;
|
||||
address = (address & ~(uint64_t {0xffffffffu} << shift)) |
|
||||
(static_cast<uint64_t>(value) << shift);
|
||||
}
|
||||
|
||||
VertexShaderInfo m_vs;
|
||||
PixelShaderInfo m_ps;
|
||||
ComputeShaderInfo m_cs;
|
||||
|
||||
@@ -987,6 +987,12 @@ constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR2_MASK = 0x1;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_SHIFT = 2;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t FSR_WINDOW_LEFT = 0x2BC;
|
||||
constexpr uint32_t FSR_CONTROL_POINTS_LEFT_X = 0x2C4;
|
||||
constexpr uint32_t FSR_CONTROL_POINTS_LEFT_Y = 0x2CC;
|
||||
constexpr uint32_t FSR_ALPHA_LEFT_X = 0x2E4;
|
||||
constexpr uint32_t FSR_ALPHA_LEFT_Y = 0x2E8;
|
||||
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR = 0x380;
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR_HI = 0x381;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_FACTORS = 0x383;
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#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 {
|
||||
|
||||
static uint32_t AlignUp(uint32_t value, uint32_t alignment) {
|
||||
return (value + alignment - 1u) & ~(alignment - 1u);
|
||||
}
|
||||
|
||||
static uint32_t ShiftCeil(uint32_t value, uint32_t shift) {
|
||||
return static_cast<uint32_t>((static_cast<uint64_t>(value) + (1ull << shift) - 1ull) >> shift);
|
||||
}
|
||||
@@ -33,7 +27,7 @@ static uint32_t CalcLinearAlignedLevelPitch(uint32_t base_width, uint32_t base_h
|
||||
const uint32_t level_width = ShiftCeil(base_width, level);
|
||||
const uint32_t level_height = ShiftCeil(base_height, level);
|
||||
const uint32_t padded_width =
|
||||
AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element));
|
||||
Common::AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element));
|
||||
const uint64_t size =
|
||||
static_cast<uint64_t>(padded_width) * std::max(level_height, 1u) * bytes_per_element;
|
||||
EXIT_NOT_IMPLEMENTED(size > 0xffffffffull);
|
||||
@@ -61,7 +55,7 @@ static uint32_t SetLinearMipChainLayout(uint32_t levels, const uint32_t* mip_pit
|
||||
offset += mip_size[level];
|
||||
}
|
||||
|
||||
return AlignUp(offset, 256u);
|
||||
return Common::AlignUp(offset, 256u);
|
||||
}
|
||||
|
||||
struct Log2BlockDimensions {
|
||||
@@ -366,8 +360,8 @@ bool TileGetTiledTextureLayout(const TileSurfaceDescription& description, TileSu
|
||||
((description.width >> level) + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
mip.height = std::max(
|
||||
((description.height >> level) + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
mip.padded_width = AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width);
|
||||
mip.padded_height = AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height);
|
||||
mip.padded_width = Common::AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width);
|
||||
mip.padded_height = Common::AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height);
|
||||
mip.size = static_cast<uint64_t>(block.block_depth) * mip.padded_width * mip.padded_height *
|
||||
block.bytes_per_element;
|
||||
result.block_slice_size += mip.size;
|
||||
@@ -1121,8 +1115,8 @@ static bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& hti
|
||||
}
|
||||
// Prospero HTile stores one DWORD per depth tile. Its 32 KiB allocation blocks cover
|
||||
// 1024x512 pixels, independently of the attachment's fragment count.
|
||||
const uint64_t size = static_cast<uint64_t>(AlignUp(width, 1024u) / 1024u) *
|
||||
(AlignUp(height, 512u) / 512u) * 32768u;
|
||||
const uint64_t size = static_cast<uint64_t>(Common::AlignUp(width, 1024u) / 1024u) *
|
||||
(Common::AlignUp(height, 512u) / 512u) * 32768u;
|
||||
if (size == 0 || size > UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
@@ -1154,13 +1148,13 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
&stencil_block_height));
|
||||
const uint32_t fragments = 1u << num_fragments_log2;
|
||||
const uint64_t depth_bytes_total =
|
||||
valid_blocks ? static_cast<uint64_t>(AlignUp(width, depth_block_width)) *
|
||||
AlignUp(height, depth_block_height) * depth_bytes * fragments
|
||||
valid_blocks ? static_cast<uint64_t>(Common::AlignUp(width, depth_block_width)) *
|
||||
Common::AlignUp(height, depth_block_height) * depth_bytes * fragments
|
||||
: 0;
|
||||
const uint64_t stencil_bytes_total =
|
||||
stencil_format == Prospero::StencilFormat::k8UInt && valid_blocks
|
||||
? static_cast<uint64_t>(AlignUp(width, stencil_block_width)) *
|
||||
AlignUp(height, stencil_block_height) * fragments
|
||||
? static_cast<uint64_t>(Common::AlignUp(width, stencil_block_width)) *
|
||||
Common::AlignUp(height, stencil_block_height) * fragments
|
||||
: 0;
|
||||
TileSizeAlign calculated_htile {};
|
||||
const bool htile_valid = !htile || TileGetHtileSize(width, height, calculated_htile);
|
||||
@@ -1187,8 +1181,7 @@ uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
&block_width, &block_height)) {
|
||||
return 0;
|
||||
}
|
||||
const uint64_t pitch = (static_cast<uint64_t>(width) + block_width - 1u) &
|
||||
~static_cast<uint64_t>(block_width - 1u);
|
||||
const uint64_t pitch = Common::AlignUp(static_cast<uint64_t>(width), block_width);
|
||||
return pitch <= UINT32_MAX ? static_cast<uint32_t>(pitch) : 0;
|
||||
}
|
||||
|
||||
@@ -1209,8 +1202,7 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
pitch != TileGetRenderTargetPitch(width, bytes_per_element, num_fragments_log2)) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t padded_height = (static_cast<uint64_t>(height) + block_height - 1u) &
|
||||
~static_cast<uint64_t>(block_height - 1u);
|
||||
const uint64_t padded_height = Common::AlignUp(static_cast<uint64_t>(height), block_height);
|
||||
const uint64_t size = static_cast<uint64_t>(pitch) * padded_height * bytes_per_element *
|
||||
(1u << num_fragments_log2);
|
||||
if (size == 0 || size > UINT32_MAX) {
|
||||
@@ -1221,6 +1213,31 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||
TileSizeAlign& total_size, uint32_t num_fragments_log2) {
|
||||
total_size = {};
|
||||
if (width == 0 || height == 0 || slices == 0 || levels != 1 || num_fragments_log2 != 0 ||
|
||||
!std::has_single_bit(bytes_per_element) || bytes_per_element > 16 ||
|
||||
(tile != Prospero::TileMode::kRenderTarget && tile != Prospero::TileMode::kDepth)) {
|
||||
return false;
|
||||
}
|
||||
// Gen5 color metadata uses 4 KiB blocks, with one byte per 256 bytes of color data.
|
||||
// Metadata stays thin for volume surfaces, so every depth slice has its own block raster.
|
||||
const uint32_t coverage_bits = 20u - std::countr_zero(bytes_per_element);
|
||||
const uint32_t block_width = 1u << ((coverage_bits + 1u) / 2u);
|
||||
const uint32_t block_height = 1u << (coverage_bits / 2u);
|
||||
const uint64_t blocks_x = (static_cast<uint64_t>(width) + block_width - 1u) / block_width;
|
||||
const uint64_t blocks_y = (static_cast<uint64_t>(height) + block_height - 1u) / block_height;
|
||||
const uint64_t blocks = blocks_x * blocks_y;
|
||||
if (blocks > UINT32_MAX / 4096u / slices) {
|
||||
return false;
|
||||
}
|
||||
total_size.size = static_cast<uint32_t>(blocks * slices * 4096u);
|
||||
total_size.align = 4096;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, uint32_t levels,
|
||||
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
|
||||
@@ -1302,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1347,7 +1359,7 @@ uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
|
||||
TileTextureElementLayout element {};
|
||||
if (TileGetTextureElementLayout(format, element) && element.texel_width == 1 &&
|
||||
element.texel_height == 1) {
|
||||
pitch = AlignUp(pitch, CalcLinearBlockWidth(element.bytes));
|
||||
pitch = Common::AlignUp(pitch, CalcLinearBlockWidth(element.bytes));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1358,7 +1370,7 @@ uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
|
||||
case Prospero::TileMode::kRenderTarget: {
|
||||
TileTextureBlockLayout layout {};
|
||||
if (TileGetTextureBlockLayout(format, tile, false, layout)) {
|
||||
pitch = AlignUp(pitch, layout.block.block_width * layout.texel_width);
|
||||
pitch = Common::AlignUp(pitch, layout.block.block_width * layout.texel_width);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ struct TileMipLayout {
|
||||
uint32_t padded_height = 0;
|
||||
uint32_t tail_x = 0;
|
||||
uint32_t tail_y = 0;
|
||||
bool operator==(const TileMipLayout&) const = default;
|
||||
};
|
||||
|
||||
struct TileSurfaceLayout {
|
||||
@@ -122,6 +123,9 @@ uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, TileSizeAlign& total_size,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||
TileSizeAlign& total_size, uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, uint32_t levels,
|
||||
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct VulkanBuffer;
|
||||
struct VulkanImage;
|
||||
struct VulkanMemory;
|
||||
|
||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||
|
||||
@@ -29,10 +27,13 @@ struct GraphicContext {
|
||||
vk::Device device = nullptr;
|
||||
VmaAllocator allocator = nullptr;
|
||||
bool memory_budget_ext_enabled = false;
|
||||
bool rt_extensions_enabled = false;
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool compute_wave64_supported = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
uint32_t subgroup_size = 0;
|
||||
uint32_t min_subgroup_size = 0;
|
||||
uint32_t max_subgroup_size = 0;
|
||||
@@ -80,6 +81,10 @@ struct GraphicContext {
|
||||
return it->second.first;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsComputeWave64() const noexcept {
|
||||
return subgroup_size == 64u || compute_subgroup_size_control_enabled;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::DeviceSize StorageMinAlignment() const {
|
||||
const auto alignment = physical_device_properties.limits.minStorageBufferOffsetAlignment;
|
||||
return alignment != 0 ? alignment : 1;
|
||||
@@ -91,15 +96,8 @@ struct GraphicContext {
|
||||
[[nodiscard]] bool CanReportMemoryUsage() const noexcept { return memory_budget_ext_enabled; }
|
||||
[[nodiscard]] uint64_t GetDeviceMemoryUsage() const;
|
||||
[[nodiscard]] uint64_t GetTotalMemoryBudget() const;
|
||||
void CreateBuffer(uint64_t size, VulkanBuffer& buffer);
|
||||
[[nodiscard]] bool CreateImage(const vk::ImageCreateInfo& info, VulkanImage& image);
|
||||
void DeleteImage(VulkanImage& image);
|
||||
void MapMemory(VulkanMemory& memory, void*& data);
|
||||
void UnmapMemory(VulkanMemory& memory);
|
||||
void AppendHardwareRayTracingDeviceExtensions(
|
||||
const std::vector<vk::ExtensionProperties>& available_extensions,
|
||||
std::vector<const char*>& device_extensions);
|
||||
void LoadHardwareRayTracingFunctions() const;
|
||||
|
||||
uint32_t screen_width = 0;
|
||||
uint32_t screen_height = 0;
|
||||
@@ -114,18 +112,6 @@ private:
|
||||
m_image_format_properties;
|
||||
};
|
||||
|
||||
struct VulkanMemory {
|
||||
vk::MemoryRequirements requirements = {};
|
||||
vk::MemoryPropertyFlags property = {};
|
||||
vk::MemoryPropertyFlags preferred_property = {};
|
||||
vk::DeviceMemory memory = nullptr;
|
||||
VmaAllocation allocation = nullptr;
|
||||
VmaAllocationInfo allocation_info = {};
|
||||
vk::DeviceSize offset = 0;
|
||||
uint32_t type = 0;
|
||||
uint64_t unique_id = 0;
|
||||
};
|
||||
|
||||
struct VulkanImageState {
|
||||
vk::PipelineStageFlags2 pl_stage = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
vk::AccessFlags2 access_mask = vk::AccessFlagBits2::eNone;
|
||||
@@ -139,7 +125,6 @@ struct VulkanImage {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::ImageType image_type = vk::ImageType::e2D;
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
uint32_t guest_pitch = 0;
|
||||
uint32_t layers = 1;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t samples = 1;
|
||||
@@ -148,15 +133,10 @@ struct VulkanImage {
|
||||
vk::Image image = nullptr;
|
||||
VulkanImageState state;
|
||||
std::vector<VulkanImageState> subresource_states;
|
||||
Graphics::VulkanMemory memory;
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
struct VulkanBuffer {
|
||||
vk::Buffer buffer = nullptr;
|
||||
VulkanMemory memory;
|
||||
vk::BufferUsageFlags usage = {};
|
||||
uint64_t buffer_size = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/memoryTracker.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -8,9 +9,6 @@ static_assert(std::atomic<void*>::is_always_lock_free);
|
||||
|
||||
MemoryTracker::MemoryTracker(PageManager& page_manager): m_page_manager(page_manager) {
|
||||
m_regions = std::make_unique<std::atomic<RegionManager*>[]>(REGION_COUNT);
|
||||
for (size_t i = 0; i < REGION_COUNT; i++) {
|
||||
m_regions[i].store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryTracker::~MemoryTracker() = default;
|
||||
@@ -23,10 +21,7 @@ void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr,
|
||||
EXIT("MemoryTracker: invalid dirty-page validation range\n");
|
||||
}
|
||||
for (auto page = vaddr; page < vaddr + size; page += TRACKER_PAGE_SIZE) {
|
||||
bool found = false;
|
||||
dirty.ForEachIntersection(page, TRACKER_PAGE_SIZE,
|
||||
[&found](RangeSet::Range) { found = true; });
|
||||
if (!found) {
|
||||
if (!dirty.Intersects(page, TRACKER_PAGE_SIZE)) {
|
||||
EXIT("MemoryTracker: GPU-dirty tracker page has no dirty bytes, operation=%s "
|
||||
"addr=0x%016" PRIx64 "\n",
|
||||
operation, page);
|
||||
@@ -37,12 +32,10 @@ void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr,
|
||||
void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
const char* operation) {
|
||||
ValidateRange(vaddr, size);
|
||||
const auto begin = vaddr & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto end = (vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto begin = Common::AlignDown(vaddr, TRACKER_PAGE_SIZE);
|
||||
const auto end = Common::AlignUp(vaddr + size, TRACKER_PAGE_SIZE);
|
||||
for (auto page = begin; page < end; page += TRACKER_PAGE_SIZE) {
|
||||
bool has_dirty_bytes = false;
|
||||
dirty.ForEachIntersection(page, TRACKER_PAGE_SIZE,
|
||||
[&has_dirty_bytes](RangeSet::Range) { has_dirty_bytes = true; });
|
||||
const bool has_dirty_bytes = dirty.Intersects(page, TRACKER_PAGE_SIZE);
|
||||
if (IsRegionGpuModified(page, TRACKER_PAGE_SIZE) != has_dirty_bytes) {
|
||||
EXIT("MemoryTracker: tracker and byte ownership disagree, operation=%s "
|
||||
"addr=0x%016" PRIx64 "\n",
|
||||
@@ -113,7 +106,8 @@ void MemoryTracker::UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
});
|
||||
}
|
||||
|
||||
void MemoryTracker::UntrackMemoryImpl(uint64_t vaddr, uint64_t size) {
|
||||
void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::vector<RegionManager*> managers;
|
||||
managers.reserve((vaddr % TRACKER_REGION_SIZE + size + TRACKER_REGION_SIZE - 1) /
|
||||
TRACKER_REGION_SIZE);
|
||||
@@ -134,12 +128,6 @@ void MemoryTracker::UntrackMemoryImpl(uint64_t vaddr, uint64_t size) {
|
||||
Iterate<false>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
});
|
||||
locks.clear();
|
||||
}
|
||||
|
||||
void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
UntrackMemoryImpl(vaddr, size);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -34,7 +34,6 @@ public:
|
||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) noexcept {
|
||||
static_assert(std::is_invocable_v<Flush&>);
|
||||
CheckNotInUploadCallback();
|
||||
ValidateRange(vaddr, size);
|
||||
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const bool should_flush = [&] {
|
||||
@@ -63,43 +62,18 @@ public:
|
||||
void ValidateGpuDirtyOwnership(const RangeSet&, uint64_t, uint64_t, const char*) {}
|
||||
#endif
|
||||
|
||||
template <bool clear, typename Preflight, typename Func>
|
||||
void ForEachDownloadRange(uint64_t vaddr, uint64_t size, Preflight&& preflight, Func&& func) {
|
||||
static_assert(std::is_nothrow_invocable_v<Preflight&, uint64_t, uint64_t>);
|
||||
static_assert(std::is_nothrow_invocable_v<Func&, uint64_t, uint64_t>);
|
||||
CheckNotInUploadCallback();
|
||||
std::vector<RegionManager*> managers;
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t, uint64_t) {
|
||||
managers.push_back(manager);
|
||||
});
|
||||
std::vector<std::unique_lock<TrackingSpinLock>> locks;
|
||||
locks.reserve(managers.size());
|
||||
for (auto* manager: managers) {
|
||||
locks.emplace_back(manager->lock);
|
||||
}
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes,
|
||||
preflight);
|
||||
});
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(
|
||||
manager->GetCpuAddr() + offset, bytes, func);
|
||||
});
|
||||
if constexpr (clear) {
|
||||
Iterate<false>(vaddr, size,
|
||||
[&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, true>(
|
||||
address, bytes, [](uint64_t, uint64_t) noexcept {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <bool clear, typename Func>
|
||||
void ForEachDownloadRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||
ForEachDownloadRange<clear>(
|
||||
vaddr, size, [](uint64_t, uint64_t) noexcept {}, std::forward<Func>(func));
|
||||
static_assert(std::is_nothrow_invocable_v<Func&, uint64_t, uint64_t>);
|
||||
CheckNotInUploadCallback();
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes, func);
|
||||
if constexpr (clear) {
|
||||
manager->template ChangeState<DirtySource::Gpu, false>(address, bytes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename RangeFunc, typename UploadFunc>
|
||||
@@ -171,7 +145,6 @@ private:
|
||||
}
|
||||
|
||||
static void ValidateRange(uint64_t vaddr, uint64_t size);
|
||||
void UntrackMemoryImpl(uint64_t vaddr, uint64_t size);
|
||||
RegionManager* GetOrCreateRegion(uint64_t index);
|
||||
|
||||
std::unique_ptr<std::atomic<RegionManager*>[]> m_regions;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/virtualMemory.h"
|
||||
#include "graphics/host_gpu/regionDefinitions.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
@@ -21,10 +22,7 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#elif defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -36,38 +34,12 @@ constexpr uint64_t REGION_SIZE = TRACKER_REGION_SIZE;
|
||||
constexpr uint64_t ADDRESS_SIZE = TRACKER_ADDRESS_SIZE;
|
||||
constexpr uint64_t REGION_COUNT = ADDRESS_SIZE / REGION_SIZE;
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// The tracker reuses Win32 memory-protection tags as internal page-state values.
|
||||
// Mirror their canonical numeric values so the shared state-machine logic is identical.
|
||||
constexpr uint32_t PAGE_NOACCESS = 0x01;
|
||||
constexpr uint32_t PAGE_READONLY = 0x02;
|
||||
constexpr uint32_t PAGE_READWRITE = 0x04;
|
||||
#endif
|
||||
constexpr uint64_t REGION_PAGES = REGION_SIZE / PAGE_SIZE;
|
||||
|
||||
constexpr uint32_t NO_ACCESS_PROTECTION = PAGE_NOACCESS;
|
||||
constexpr uint32_t READ_ONLY_PROTECTION = PAGE_READONLY;
|
||||
constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
|
||||
[[noreturn]] void FailFast(const char* reason = nullptr) noexcept {
|
||||
std::fputs("PageManager fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
#if !defined(__APPLE__)
|
||||
void* frames[16] {};
|
||||
int frame_count = static_cast<int>(std::size(frames));
|
||||
SysStackWalk(frames, &frame_count);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
const auto image_base = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
const auto address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
std::fprintf(stderr, " frame[%d]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
|
||||
address, address >= image_base ? address - image_base : 0);
|
||||
}
|
||||
#else
|
||||
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
|
||||
#endif
|
||||
#endif
|
||||
std::fflush(stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(EXCEPTION_NONCONTINUABLE_EXCEPTION));
|
||||
@@ -86,15 +58,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
std::_Exit(322);
|
||||
}
|
||||
|
||||
Common::VirtualMemory::Mode ToMemoryMode(uint32_t protection) {
|
||||
switch (protection) {
|
||||
case NO_ACCESS_PROTECTION: return Common::VirtualMemory::Mode::NoAccess;
|
||||
case READ_ONLY_PROTECTION: return Common::VirtualMemory::Mode::Read;
|
||||
case READ_WRITE_PROTECTION: return Common::VirtualMemory::Mode::ReadWrite;
|
||||
default: Fatal("unmappable protection 0x%08" PRIx32, protection);
|
||||
}
|
||||
}
|
||||
|
||||
class SpinGuard final {
|
||||
public:
|
||||
explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) {
|
||||
@@ -115,15 +78,6 @@ void ValidateRange(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t PageStart(uint64_t vaddr) {
|
||||
return vaddr & ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
uint64_t PageEnd(uint64_t vaddr, uint64_t size) {
|
||||
ValidateRange(vaddr, size);
|
||||
return PageStart(vaddr + size - 1) + PAGE_SIZE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct PageManager::Impl {
|
||||
@@ -131,14 +85,14 @@ struct PageManager::Impl {
|
||||
uint8_t write_watchers : 7 = 0;
|
||||
uint8_t access_watchers : 1 = 0;
|
||||
|
||||
[[nodiscard]] uint32_t Perms() const noexcept {
|
||||
[[nodiscard]] Common::VirtualMemory::Mode Perms() const noexcept {
|
||||
if (access_watchers != 0) {
|
||||
return NO_ACCESS_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
if (write_watchers != 0) {
|
||||
return READ_ONLY_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::Read;
|
||||
}
|
||||
return READ_WRITE_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::ReadWrite;
|
||||
}
|
||||
|
||||
template <int delta, bool is_read>
|
||||
@@ -202,9 +156,6 @@ struct PageManager::Impl {
|
||||
}
|
||||
#endif
|
||||
regions = std::make_unique<std::atomic<Region*>[]>(REGION_COUNT);
|
||||
for (uint64_t i = 0; i < REGION_COUNT; i++) {
|
||||
regions[i].store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
@@ -239,11 +190,10 @@ struct PageManager::Impl {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Protect(uint64_t vaddr, uint64_t size, uint32_t protection) noexcept {
|
||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size,
|
||||
ToMemoryMode(protection))) {
|
||||
Fatal("address-space protection failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr,
|
||||
protection);
|
||||
void Protect(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) noexcept {
|
||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size, mode)) {
|
||||
Fatal("address-space protection failed at 0x%016" PRIx64 ", mode=0x%08" PRIx32, vaddr,
|
||||
static_cast<uint32_t>(mode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,11 +250,12 @@ struct PageManager::Impl {
|
||||
|
||||
template <bool track, bool is_read>
|
||||
void UpdatePageWatchers(uint64_t vaddr, uint64_t size) {
|
||||
const auto begin = PageStart(vaddr);
|
||||
const auto end = PageEnd(vaddr, size);
|
||||
ValidateRange(vaddr, size);
|
||||
const auto begin = Common::AlignDown(vaddr, PAGE_SIZE);
|
||||
const auto end = Common::AlignUp(vaddr + size, PAGE_SIZE);
|
||||
for (auto chunk_begin = begin; chunk_begin < end;) {
|
||||
const auto chunk_end = std::min(end, (chunk_begin / REGION_SIZE + 1) * REGION_SIZE);
|
||||
const auto region_base = chunk_begin / REGION_SIZE * REGION_SIZE;
|
||||
const auto chunk_end = std::min(end, Common::AlignUp(chunk_begin + 1, REGION_SIZE));
|
||||
const auto region_base = Common::AlignDown(chunk_begin, REGION_SIZE);
|
||||
auto* region = track ? GetOrCreateRegion(chunk_begin) : FindRegion(chunk_begin);
|
||||
if (region == nullptr) {
|
||||
Fatal("untracking unknown page 0x%016" PRIx64, chunk_begin);
|
||||
@@ -371,8 +322,4 @@ template void PageManager::UpdatePageWatchersForRegion<true, false>(uint64_t, Re
|
||||
template void PageManager::UpdatePageWatchersForRegion<false, true>(uint64_t, RegionBits&);
|
||||
template void PageManager::UpdatePageWatchersForRegion<false, false>(uint64_t, RegionBits&);
|
||||
|
||||
void PageManager::OnGpuMap(uint64_t, uint64_t) {}
|
||||
|
||||
void PageManager::OnGpuUnmap(uint64_t, uint64_t) {}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -24,8 +24,6 @@ public:
|
||||
void UpdatePageWatchers(uint64_t vaddr, uint64_t size);
|
||||
template <bool track, bool is_read = false>
|
||||
void UpdatePageWatchersForRegion(uint64_t base_addr, RegionBits& mask);
|
||||
void OnGpuMap(uint64_t vaddr, uint64_t size);
|
||||
void OnGpuUnmap(uint64_t vaddr, uint64_t size);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -6,17 +6,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class RangeSet final {
|
||||
public:
|
||||
struct Range {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
};
|
||||
|
||||
void Add(uint64_t address, uint64_t size) {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.lower_bound(address);
|
||||
@@ -62,12 +56,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Range> Intersections(uint64_t address, uint64_t size) const {
|
||||
std::vector<Range> result;
|
||||
ForEachIntersection(address, size, [&result](Range range) { result.push_back(range); });
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Intersects(uint64_t address, uint64_t size) const {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.lower_bound(address);
|
||||
@@ -88,7 +76,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachIntersection(uint64_t address, uint64_t size, Func&& func) const {
|
||||
void ForEachInRange(uint64_t address, uint64_t size, Func&& func) const {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.upper_bound(address);
|
||||
if (it != m_ranges.begin()) {
|
||||
@@ -98,7 +86,7 @@ public:
|
||||
const auto begin = std::max(address, it->first);
|
||||
const auto last = std::min(end, it->second);
|
||||
if (begin < last) {
|
||||
func(Range {begin, last - begin});
|
||||
func(begin, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#undef MemoryBarrier
|
||||
#elif defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#elif defined(__linux__)
|
||||
@@ -111,56 +112,43 @@ public:
|
||||
bits.UnsetRange(start, end);
|
||||
}
|
||||
if constexpr (source == DirtySource::Cpu) {
|
||||
UpdateCpuProtection<!enable>();
|
||||
UpdateProtection<!enable, false>();
|
||||
} else {
|
||||
UpdateGpuProtection<enable>();
|
||||
UpdateProtection<enable, true>();
|
||||
}
|
||||
}
|
||||
|
||||
template <DirtySource source, bool clear, typename Func>
|
||||
void ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||
const auto [start, end] = GetPageRange(vaddr, size);
|
||||
RegionBits mask(GetBits<source>(), start, end);
|
||||
auto& bits = GetBits<source>();
|
||||
RegionBits mask(bits, start, end);
|
||||
if constexpr (clear) {
|
||||
GetBits<source>().UnsetRange(start, end);
|
||||
bits.UnsetRange(start, end);
|
||||
if constexpr (source == DirtySource::Cpu) {
|
||||
UpdateProtection<true, false>();
|
||||
} else {
|
||||
UpdateProtection<false, true>();
|
||||
}
|
||||
}
|
||||
if constexpr (source == DirtySource::Cpu && clear) {
|
||||
UpdateCpuProtection<true>();
|
||||
ForEachRange(mask, std::forward<Func>(func));
|
||||
return;
|
||||
for (const auto [first, last]: mask) {
|
||||
func(m_cpu_addr + first * TRACKER_PAGE_SIZE, (last - first) * TRACKER_PAGE_SIZE);
|
||||
}
|
||||
if constexpr (source == DirtySource::Gpu && clear) {
|
||||
UpdateGpuProtection<false>();
|
||||
}
|
||||
ForEachRange(mask, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
TrackingSpinLock lock;
|
||||
|
||||
private:
|
||||
template <bool track>
|
||||
void UpdateCpuProtection() {
|
||||
auto mask = m_cpu_dirty ^ m_writable;
|
||||
m_writable = m_cpu_dirty;
|
||||
template <bool track, bool is_read>
|
||||
void UpdateProtection() {
|
||||
const auto protection = is_read ? ~m_gpu_dirty : m_cpu_dirty;
|
||||
auto& previous = is_read ? m_readable : m_writable;
|
||||
auto mask = protection ^ previous;
|
||||
if (mask.None()) {
|
||||
return;
|
||||
}
|
||||
m_page_manager.UpdatePageWatchersForRegion<track>(m_cpu_addr, mask);
|
||||
}
|
||||
|
||||
template <bool track>
|
||||
void UpdateGpuProtection() {
|
||||
auto readable = ~m_gpu_dirty;
|
||||
auto mask = readable ^ m_readable;
|
||||
m_readable = readable;
|
||||
if (mask.None()) {
|
||||
return;
|
||||
}
|
||||
if constexpr (track) {
|
||||
m_page_manager.UpdatePageWatchersForRegion<true, true>(m_cpu_addr, mask);
|
||||
} else {
|
||||
m_page_manager.UpdatePageWatchersForRegion<false, true>(m_cpu_addr, mask);
|
||||
}
|
||||
previous = protection;
|
||||
m_page_manager.UpdatePageWatchersForRegion<track, is_read>(m_cpu_addr, mask);
|
||||
}
|
||||
|
||||
template <DirtySource source>
|
||||
@@ -191,13 +179,6 @@ private:
|
||||
static_cast<size_t>((offset + size + TRACKER_PAGE_SIZE - 1) / TRACKER_PAGE_SIZE)};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachRange(const RegionBits& bits, Func&& func) const {
|
||||
for (const auto [start, end]: bits) {
|
||||
func(m_cpu_addr + start * TRACKER_PAGE_SIZE, (end - start) * TRACKER_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
PageManager& m_page_manager;
|
||||
uint64_t m_cpu_addr = 0;
|
||||
RegionBits m_cpu_dirty;
|
||||
|
||||
+197
-313
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
@@ -13,7 +14,6 @@
|
||||
#include "kernel/memory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@@ -43,13 +43,6 @@ void BufferCache::WriteDataBuffer(Buffer& buffer, uint64_t address, const void*
|
||||
}
|
||||
}
|
||||
|
||||
struct BufferCache::DownloadCopy {
|
||||
Buffer* buffer = nullptr;
|
||||
uint64_t source_offset = 0;
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
};
|
||||
|
||||
void BufferCache::Register(BufferId id) {
|
||||
ChangeRegister<true>(id);
|
||||
}
|
||||
@@ -104,8 +97,7 @@ void BufferCache::TouchBuffer(const Buffer& buffer) {
|
||||
}
|
||||
|
||||
void BufferCache::DeleteBuffer(BufferId id) {
|
||||
auto* buffer = m_slot_buffers.try_get(id);
|
||||
if (buffer == nullptr || buffer->is_deleted) {
|
||||
if (IsBufferInvalid(id)) {
|
||||
return;
|
||||
}
|
||||
Unregister(id);
|
||||
@@ -116,94 +108,84 @@ void BufferCache::DeleteBuffer(BufferId id) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<uint64_t, uint64_t> BufferCache::DownloadEnvelope(const DownloadCopy& copy) {
|
||||
if (copy.buffer == nullptr || copy.size == 0 || copy.source_offset > copy.buffer->Size() ||
|
||||
copy.size > copy.buffer->Size() - copy.source_offset) {
|
||||
EXIT("BufferCache: invalid download copy\n");
|
||||
bool BufferCache::DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t size) {
|
||||
std::vector<vk::BufferCopy> copies;
|
||||
uint64_t total_size = 0;
|
||||
const auto buffer_address = buffer.CpuAddress();
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
vaddr, size, [&](uint64_t address, uint64_t bytes) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||
"buffer download");
|
||||
m_gpu_modified_ranges.ForEachInRange(address, bytes, [&](uint64_t start, uint64_t end) {
|
||||
copies.emplace_back(start - buffer_address, total_size, end - start);
|
||||
// Keep packed ranges on separate cache lines, as in shadPS4.
|
||||
total_size += Common::AlignUp(end - start, 64);
|
||||
});
|
||||
m_gpu_modified_ranges.Subtract(address, bytes);
|
||||
});
|
||||
if (copies.empty()) {
|
||||
return false;
|
||||
}
|
||||
const auto begin = copy.source_offset & ~uint64_t {3};
|
||||
if (copy.source_offset > UINT64_MAX - copy.size ||
|
||||
copy.source_offset + copy.size > UINT64_MAX - 3) {
|
||||
EXIT("BufferCache: download copy alignment overflow\n");
|
||||
}
|
||||
const auto end = (copy.source_offset + copy.size + 3) & ~uint64_t {3};
|
||||
if (end > copy.buffer->Size()) {
|
||||
EXIT("BufferCache: aligned download copy exceeds its owner\n");
|
||||
}
|
||||
return {begin, end - begin};
|
||||
}
|
||||
|
||||
void BufferCache::DownloadBufferMemory(std::span<const DownloadCopy> copies) {
|
||||
std::vector<DownloadCopy> batch;
|
||||
batch.reserve(copies.size());
|
||||
uint64_t packed_size = 0;
|
||||
auto& download = m_download_buffer;
|
||||
const auto flush = [&] {
|
||||
const auto [mapped, base_offset] = download.Map(packed_size, DOWNLOAD_ALIGNMENT);
|
||||
EXIT_IF(mapped == nullptr);
|
||||
uint64_t cursor = 0;
|
||||
for (const auto& copy: batch) {
|
||||
const auto [source_begin, envelope_size] = DownloadEnvelope(copy);
|
||||
download.CopyFrom(m_scheduler.Current(), *copy.buffer, source_begin, base_offset + cursor,
|
||||
envelope_size, vk::AccessFlagBits::eMemoryWrite, vk::AccessFlags {},
|
||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||
vk::AccessFlagBits::eHostRead);
|
||||
cursor += AlignDownload(envelope_size);
|
||||
}
|
||||
download.Commit();
|
||||
const auto completion_tick = m_scheduler.CurrentTick();
|
||||
m_scheduler.Finish();
|
||||
m_scheduler.WaitPriorityOperations(completion_tick);
|
||||
cursor = 0;
|
||||
for (const auto& copy: batch) {
|
||||
const auto [source_begin, envelope_size] = DownloadEnvelope(copy);
|
||||
const auto offset = cursor + copy.source_offset - source_begin;
|
||||
download.Invalidate(base_offset + offset, copy.size);
|
||||
Libs::LibKernel::Memory::WriteBacking(copy.address, mapped + offset, copy.size);
|
||||
cursor += AlignDownload(envelope_size);
|
||||
}
|
||||
batch.clear();
|
||||
packed_size = 0;
|
||||
};
|
||||
for (auto copy: copies) {
|
||||
while (copy.size != 0) {
|
||||
const auto available = download.Size() - packed_size;
|
||||
const auto prefix = copy.source_offset & 3u;
|
||||
const auto bytes = std::min(copy.size, available - prefix);
|
||||
DownloadCopy part {copy.buffer, copy.source_offset, copy.address, bytes};
|
||||
const auto [source_begin, envelope_size] = DownloadEnvelope(part);
|
||||
(void)source_begin;
|
||||
packed_size += AlignDownload(envelope_size);
|
||||
batch.push_back(part);
|
||||
copy.source_offset += bytes;
|
||||
copy.address += bytes;
|
||||
copy.size -= bytes;
|
||||
if (packed_size == download.Size()) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
const auto [mapped, offset] = m_download_buffer.Map(total_size, 64);
|
||||
if (mapped == nullptr) {
|
||||
EXIT("BufferCache: download exceeds 32 MiB staging buffer capacity\n");
|
||||
}
|
||||
if (!batch.empty()) {
|
||||
flush();
|
||||
}
|
||||
for (const auto& copy: copies) {
|
||||
m_gpu_modified_ranges.Subtract(copy.address, copy.size);
|
||||
m_download_buffer.Commit();
|
||||
for (auto& copy: copies) {
|
||||
copy.dstOffset += offset;
|
||||
}
|
||||
|
||||
auto& command = m_scheduler.Current();
|
||||
command.EndRendering();
|
||||
const auto native = command.Handle();
|
||||
vk::BufferMemoryBarrier before {};
|
||||
before.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
before.dstAccessMask = vk::AccessFlagBits::eTransferRead;
|
||||
before.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
before.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
before.buffer = buffer.Handle();
|
||||
before.offset = 0;
|
||||
before.size = buffer.Size();
|
||||
native.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::PipelineStageFlagBits::eTransfer, {}, 0, nullptr, 1, &before, 0,
|
||||
nullptr);
|
||||
native.copyBuffer(buffer.Handle(), m_download_buffer.Handle(),
|
||||
static_cast<uint32_t>(copies.size()), copies.data());
|
||||
|
||||
auto after = before;
|
||||
after.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
after.dstAccessMask = vk::AccessFlagBits::eHostRead;
|
||||
after.buffer = m_download_buffer.Handle();
|
||||
after.offset = offset;
|
||||
after.size = total_size;
|
||||
native.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eAllCommands |
|
||||
vk::PipelineStageFlagBits::eHost,
|
||||
{}, 0, nullptr, 1, &after, 0, nullptr);
|
||||
m_scheduler.DeferPriorityOperation([this, mapped, offset, total_size, buffer_address,
|
||||
copies = std::move(copies)] {
|
||||
m_download_buffer.Invalidate(offset, total_size);
|
||||
for (const auto& copy: copies) {
|
||||
Libs::LibKernel::Memory::WriteBacking(buffer_address + copy.srcOffset,
|
||||
mapped + (copy.dstOffset - offset), copy.size);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
PageManager& page_manager, TextureCache& texture_cache)
|
||||
: m_graphics(graphics), m_scheduler(scheduler),
|
||||
m_fault_manager(graphics, scheduler, *this, CACHING_PAGEBITS, CACHING_NUMPAGES),
|
||||
m_gds_buffer(graphics, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize),
|
||||
m_bda_pagetable_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags,
|
||||
BDA_PAGETABLE_SIZE),
|
||||
m_memory_tracker(page_manager),
|
||||
m_staging_buffer(graphics, scheduler, MemoryUsage::Upload, 512 * MiB),
|
||||
m_stream_buffer(graphics, scheduler, MemoryUsage::Stream, 64 * MiB),
|
||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 32 * MiB),
|
||||
m_device_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 128 * MiB),
|
||||
m_texture_cache(texture_cache) {
|
||||
: m_graphics(graphics), m_scheduler(scheduler), m_fault_manager(graphics, scheduler, *this),
|
||||
m_gds_buffer(graphics, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize),
|
||||
m_bda_pagetable_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags,
|
||||
BDA_PAGETABLE_SIZE),
|
||||
m_memory_tracker(page_manager),
|
||||
m_staging_buffer(graphics, scheduler, MemoryUsage::Upload, 512 * MiB),
|
||||
m_stream_buffer(graphics, scheduler, MemoryUsage::Stream, 64 * MiB),
|
||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 32 * MiB),
|
||||
m_device_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 128 * MiB),
|
||||
m_texture_cache(texture_cache) {
|
||||
std::memset(m_gds_buffer.Mapped().data(), 0, static_cast<size_t>(m_gds_buffer.Size()));
|
||||
m_gds_buffer.Flush(0, m_gds_buffer.Size());
|
||||
SetVulkanObjectNameF(m_graphics.device, m_bda_pagetable_buffer.Handle(),
|
||||
@@ -254,59 +236,29 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
m_scheduler.Context().GetGpu().SendCommandSync(
|
||||
[this, vaddr, size, is_write] { ReadMemoryOnGpu(vaddr, size, is_write); });
|
||||
}
|
||||
|
||||
void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||
// CPU invalidation reaches this point only for a GPU-owned tracker page. Resolve the exact
|
||||
// Buffer owner on the GPU thread so the cache index remains single-thread-owned.
|
||||
if (is_write && !IsRegionRegistered(vaddr, size)) {
|
||||
return;
|
||||
}
|
||||
std::vector<DownloadCopy> copies;
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
vaddr, size,
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||
"memory invalidation");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
for (uint64_t copied = 0; copied < range.size;) {
|
||||
const auto copy_address = range.address + copied;
|
||||
const auto* owner = m_page_table.Find(copy_address >> PageTable::kPageBits);
|
||||
if (owner == nullptr || !*owner) {
|
||||
EXIT("BufferCache: invalidation readback has no buffer owner\n");
|
||||
}
|
||||
auto& buffer = m_slot_buffers[*owner];
|
||||
if (!buffer.IsInBounds(copy_address, 1)) {
|
||||
EXIT("BufferCache: invalidation readback is outside its buffer owner\n");
|
||||
}
|
||||
const auto copy_size = std::min(
|
||||
range.size - copied, buffer.CpuAddress() + buffer.Size() - copy_address);
|
||||
copies.push_back(
|
||||
{&buffer, buffer.Offset(copy_address), copy_address, copy_size});
|
||||
copied += copy_size;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (copies.empty()) {
|
||||
if (!is_write) {
|
||||
m_scheduler.Context().GetGpu().SendCommandSync([this, vaddr, size, is_write] {
|
||||
if (is_write && !IsRegionRegistered(vaddr, size)) {
|
||||
return;
|
||||
}
|
||||
// A preceding read fault can consume the last GPU-owned copy after this write invalidation
|
||||
// has already chosen to flush. Complete the CPU ownership handoff even though this callback
|
||||
// no longer has bytes to download.
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
return;
|
||||
}
|
||||
DownloadBufferMemory(copies);
|
||||
// The enumeration above covered whole dirty pages and every exact interval on them.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
||||
if (is_write) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
}
|
||||
auto& buffer = m_slot_buffers[FindBuffer(vaddr, size)];
|
||||
|
||||
// Widen nearby CPU reads so they share one GPU drain.
|
||||
constexpr uint64_t WindowSize = 512 * 1024;
|
||||
const auto buffer_begin = buffer.CpuAddress();
|
||||
const auto buffer_end = buffer_begin + buffer.Size();
|
||||
const auto window_begin = std::max(Common::AlignDown(vaddr, WindowSize), buffer_begin);
|
||||
const auto window_end = std::min(std::max(window_begin + WindowSize, vaddr + size), buffer_end);
|
||||
|
||||
if (DownloadBufferMemory(buffer, window_begin, window_end - window_begin)) {
|
||||
const auto tick = m_scheduler.CurrentTick();
|
||||
m_scheduler.Wait(tick);
|
||||
m_scheduler.WaitPriorityOperations(tick);
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(window_begin, window_end - window_begin);
|
||||
}
|
||||
if (is_write) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) {
|
||||
@@ -326,38 +278,83 @@ BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) {
|
||||
return CreateBuffer(vaddr, size);
|
||||
}
|
||||
|
||||
BufferId BufferCache::CreateBuffer(uint64_t vaddr, uint64_t size) {
|
||||
auto& command = m_scheduler.Current();
|
||||
EXIT_IF(command.IsInvalid());
|
||||
auto begin = vaddr & ~(CACHING_PAGESIZE - 1);
|
||||
auto end = (vaddr + size + CACHING_PAGESIZE - 1) & ~(CACHING_PAGESIZE - 1);
|
||||
auto first = m_buffers.lower_bound(begin);
|
||||
if (first != m_buffers.begin()) {
|
||||
const auto previous = std::prev(first);
|
||||
if (const auto& buffer = m_slot_buffers[previous->second];
|
||||
buffer.CpuAddress() + buffer.Size() > begin) {
|
||||
first = previous;
|
||||
BufferCache::OverlapResult BufferCache::ResolveOverlaps(uint64_t vaddr, uint64_t size) {
|
||||
static constexpr int StreamLeapThreshold = 16;
|
||||
static constexpr uint64_t StreamLeapSize = CACHING_PAGESIZE * 128;
|
||||
|
||||
auto begin = vaddr;
|
||||
auto end = vaddr + size;
|
||||
const auto find_first = [&](uint64_t address) {
|
||||
auto first = m_buffers.lower_bound(address);
|
||||
if (first != m_buffers.begin()) {
|
||||
const auto previous = std::prev(first);
|
||||
const auto& buffer = m_slot_buffers[previous->second];
|
||||
if (buffer.CpuAddress() + buffer.Size() > address) {
|
||||
first = previous;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
};
|
||||
auto first = find_first(begin);
|
||||
auto last = first;
|
||||
int stream_score = 0;
|
||||
bool has_stream_leap = false;
|
||||
for (; last != m_buffers.end() && last->first < end; ++last) {
|
||||
const auto& buffer = m_slot_buffers[last->second];
|
||||
const auto buffer_begin = buffer.CpuAddress();
|
||||
const auto buffer_end = buffer_begin + buffer.Size();
|
||||
const bool expands_left = buffer_begin < begin;
|
||||
const bool expands_right = buffer_end > end;
|
||||
begin = std::min(begin, buffer_begin);
|
||||
end = std::max(end, buffer_end);
|
||||
if (!has_stream_leap && (stream_score += buffer.StreamScore()) > StreamLeapThreshold) {
|
||||
has_stream_leap = true;
|
||||
// Reserve space in the incoming stream's direction of growth.
|
||||
// The old buffer extending left of the request predicts growth to the right, and vice versa.
|
||||
if (expands_left) {
|
||||
end += std::min(StreamLeapSize, PageTable::kAddressSpaceSize - end);
|
||||
}
|
||||
if (expands_right) {
|
||||
const auto minimum = CACHING_PAGESIZE * 2;
|
||||
if (begin > minimum) {
|
||||
begin -= std::min(StreamLeapSize, begin - minimum);
|
||||
}
|
||||
first = find_first(begin);
|
||||
begin = std::min(begin, first->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto last = first;
|
||||
for (; last != m_buffers.end() && last->first < end; ++last) {
|
||||
const auto& buffer = m_slot_buffers[last->second];
|
||||
begin = std::min(begin, buffer.CpuAddress());
|
||||
end = std::max(end, buffer.CpuAddress() + buffer.Size());
|
||||
return {first, last, begin, end, has_stream_leap};
|
||||
}
|
||||
|
||||
void BufferCache::JoinOverlap(BufferId new_id, BufferId overlap_id, bool accumulate_stream_score) {
|
||||
auto& new_buffer = m_slot_buffers[new_id];
|
||||
auto& overlap = m_slot_buffers[overlap_id];
|
||||
if (accumulate_stream_score) {
|
||||
new_buffer.IncreaseStreamScore(overlap.StreamScore() + 1);
|
||||
}
|
||||
new_buffer.CopyFrom(m_scheduler.Current(), overlap, 0,
|
||||
overlap.CpuAddress() - new_buffer.CpuAddress(), overlap.Size());
|
||||
DeleteBuffer(overlap_id);
|
||||
}
|
||||
|
||||
BufferId BufferCache::CreateBuffer(uint64_t vaddr, uint64_t size) {
|
||||
EXIT_IF(m_scheduler.Current().IsInvalid());
|
||||
const auto end = Common::AlignUp(vaddr + size, CACHING_PAGESIZE);
|
||||
vaddr = Common::AlignDown(vaddr, CACHING_PAGESIZE);
|
||||
size = end - vaddr;
|
||||
const auto overlap = ResolveOverlaps(vaddr, size);
|
||||
|
||||
const auto id = m_slot_buffers.insert(
|
||||
m_graphics, m_scheduler, MemoryUsage::DeviceLocal, begin,
|
||||
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, end - begin);
|
||||
auto& buffer = m_slot_buffers[id];
|
||||
m_graphics, m_scheduler, MemoryUsage::DeviceLocal, overlap.begin,
|
||||
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, overlap.end - overlap.begin);
|
||||
const auto& buffer = m_slot_buffers[id];
|
||||
SetVulkanObjectNameF(m_graphics.device, buffer.Handle(),
|
||||
"Kyty.GameBuffer[guest=0x{:016x} size=0x{:x}]", begin, end - begin);
|
||||
for (auto overlap = first; overlap != last;) {
|
||||
const auto current = overlap++;
|
||||
const auto old_id = current->second;
|
||||
const auto& old = m_slot_buffers[old_id];
|
||||
buffer.CopyFrom(command, old, 0, old.CpuAddress() - begin, old.Size());
|
||||
DeleteBuffer(old_id);
|
||||
"Kyty.GameBuffer[guest=0x{:016x} size=0x{:x}]", overlap.begin,
|
||||
overlap.end - overlap.begin);
|
||||
for (auto it = overlap.first; it != overlap.last;) {
|
||||
const auto old_id = (it++)->second;
|
||||
JoinOverlap(id, old_id, !overlap.has_stream_leap);
|
||||
}
|
||||
Register(id);
|
||||
return id;
|
||||
@@ -380,7 +377,6 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, uint64_t vaddr, uint64_t siz
|
||||
command.EndRendering();
|
||||
const auto native = command.Handle();
|
||||
vk::BufferMemoryBarrier before {};
|
||||
before.sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
before.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite |
|
||||
vk::AccessFlagBits::eTransferRead |
|
||||
vk::AccessFlagBits::eTransferWrite;
|
||||
@@ -458,54 +454,34 @@ std::pair<Buffer*, uint64_t> BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t
|
||||
}
|
||||
}
|
||||
|
||||
auto* buffer = m_slot_buffers.try_get(id);
|
||||
if (buffer == nullptr || buffer->is_deleted || !buffer->IsInBounds(vaddr, size)) {
|
||||
id = FindBuffer(vaddr, size);
|
||||
buffer = &m_slot_buffers[id];
|
||||
if (IsBufferInvalid(id) || !m_slot_buffers[id].IsInBounds(vaddr, size)) {
|
||||
id = FindBuffer(vaddr, size);
|
||||
}
|
||||
TouchBuffer(*buffer);
|
||||
(void)SynchronizeBuffer(*buffer, vaddr, size, is_written, is_texel_buffer);
|
||||
auto& buffer = m_slot_buffers[id];
|
||||
TouchBuffer(buffer);
|
||||
(void)SynchronizeBuffer(buffer, vaddr, size, is_written, is_texel_buffer);
|
||||
if (is_written) {
|
||||
m_gpu_modified_ranges.Add(vaddr, size);
|
||||
}
|
||||
return {buffer, buffer->Offset(vaddr)};
|
||||
return {&buffer, buffer.Offset(vaddr)};
|
||||
}
|
||||
|
||||
std::pair<Buffer*, uint64_t> BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: invalid image source\n");
|
||||
}
|
||||
auto find_owner = [&]() -> Buffer* {
|
||||
const auto* owner = m_page_table.Find(vaddr >> PageTable::kPageBits);
|
||||
if (owner == nullptr || !*owner) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto* owner = m_page_table.Find(vaddr >> PageTable::kPageBits);
|
||||
if (owner != nullptr && *owner) {
|
||||
auto& buffer = m_slot_buffers[*owner];
|
||||
return buffer.IsInBounds(vaddr, size) ? &buffer : nullptr;
|
||||
};
|
||||
|
||||
{
|
||||
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||
const bool has_dirty_buffer_source = m_gpu_modified_ranges.Intersects(vaddr, size);
|
||||
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
|
||||
"image source");
|
||||
|
||||
auto* owner = find_owner();
|
||||
if (has_dirty_buffer_source && owner == nullptr) {
|
||||
if (!IsRegionRegistered(vaddr, size)) {
|
||||
EXIT("BufferCache: GPU-dirty image source has no native buffer\n");
|
||||
}
|
||||
owner = &m_slot_buffers[FindBuffer(vaddr, size)];
|
||||
}
|
||||
if (owner != nullptr && !cpu_modified && (!gpu_modified || has_dirty_buffer_source)) {
|
||||
TouchBuffer(*owner);
|
||||
return {owner, owner->Offset(vaddr)};
|
||||
}
|
||||
if (has_dirty_buffer_source && owner == nullptr) {
|
||||
EXIT("BufferCache: GPU-dirty image source could not resolve its native owner\n");
|
||||
if (buffer.IsInBounds(vaddr, size)) {
|
||||
TouchBuffer(buffer);
|
||||
(void)SynchronizeBuffer(buffer, vaddr, size, false, false);
|
||||
return {&buffer, buffer.Offset(vaddr)};
|
||||
}
|
||||
}
|
||||
if (IsRegionGpuModified(vaddr, size)) {
|
||||
return ObtainBuffer(vaddr, size, false, false);
|
||||
}
|
||||
|
||||
auto [staging, stage_offset] = m_staging_buffer.Map(size, 16);
|
||||
if (staging == nullptr || (!Libs::LibKernel::Memory::TryReadBacking(vaddr, staging, size) &&
|
||||
@@ -513,52 +489,7 @@ std::pair<Buffer*, uint64_t> BufferCache::ObtainBufferForImage(uint64_t vaddr, u
|
||||
EXIT("BufferCache: failed to read mapped guest image backing\n");
|
||||
}
|
||||
m_staging_buffer.Commit();
|
||||
|
||||
const bool has_dirty_buffer_source = m_gpu_modified_ranges.Intersects(vaddr, size);
|
||||
auto* owner = find_owner();
|
||||
if (has_dirty_buffer_source && owner == nullptr) {
|
||||
EXIT("BufferCache: GPU-dirty image source lost its native owner\n");
|
||||
}
|
||||
if (owner == nullptr ||
|
||||
(m_memory_tracker.IsRegionGpuModified(vaddr, size) && !has_dirty_buffer_source)) {
|
||||
return {&m_staging_buffer, stage_offset};
|
||||
}
|
||||
|
||||
TouchBuffer(*owner);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> uploads;
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
vaddr, size, false,
|
||||
[&](uint64_t address, uint64_t upload_size) noexcept {
|
||||
uploads.emplace_back(address, upload_size);
|
||||
},
|
||||
[&]() noexcept {
|
||||
for (const auto& [address, upload_size]: uploads) {
|
||||
owner->CopyFrom(m_scheduler.Current(), m_staging_buffer,
|
||||
stage_offset + address - vaddr, owner->Offset(address), upload_size,
|
||||
vk::AccessFlagBits::eHostWrite);
|
||||
}
|
||||
});
|
||||
return {owner, owner->Offset(vaddr)};
|
||||
}
|
||||
|
||||
void BufferCache::WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data) {
|
||||
if (vaddr == 0 || data.empty() || data.size() > UINT64_MAX - vaddr) {
|
||||
EXIT("BufferCache: invalid host DMA write\n");
|
||||
}
|
||||
Libs::LibKernel::Memory::WriteBacking(vaddr, data.data(), data.size());
|
||||
|
||||
const auto end = vaddr + data.size();
|
||||
for (const auto& [address, id]: m_buffers) {
|
||||
auto& buffer = m_slot_buffers[id];
|
||||
const auto buffer_end = address + buffer.Size();
|
||||
const auto begin = std::max(vaddr, address);
|
||||
const auto range_end = std::min(end, buffer_end);
|
||||
if (begin >= range_end) {
|
||||
continue;
|
||||
}
|
||||
WriteDataBuffer(buffer, begin, data.data() + begin - vaddr, range_end - begin);
|
||||
TouchBuffer(buffer);
|
||||
}
|
||||
return {&m_staging_buffer, stage_offset};
|
||||
}
|
||||
|
||||
void BufferCache::FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds) {
|
||||
@@ -576,29 +507,15 @@ void BufferCache::FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool
|
||||
EXIT("BufferCache: invalid fill memory address\n");
|
||||
}
|
||||
(void)m_texture_cache.ClearMeta(vaddr);
|
||||
{
|
||||
const auto region = m_texture_cache.QueryRegion(vaddr, size);
|
||||
if (!HasGpuDirtyBytes(vaddr, size) && !region.gpu_image_bytes) {
|
||||
if (region.image_bytes) {
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
}
|
||||
std::array<uint32_t, 4096> values;
|
||||
values.fill(value);
|
||||
const std::span<const uint8_t> bytes {reinterpret_cast<const uint8_t*>(values.data()),
|
||||
sizeof(values)};
|
||||
for (uint64_t offset = 0; offset < size;) {
|
||||
const auto chunk = std::min<uint64_t>(size - offset, bytes.size());
|
||||
WriteHostMemory(vaddr + offset, bytes.first(chunk));
|
||||
offset += chunk;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!IsRegionGpuModified(vaddr, size)) {
|
||||
// Access the guest mapping so write faults invalidate cached buffers and images.
|
||||
auto* destination = reinterpret_cast<uint32_t*>(vaddr);
|
||||
std::fill(destination, destination + size / sizeof(uint32_t), value);
|
||||
return;
|
||||
}
|
||||
|
||||
m_texture_cache.InvalidateMemoryFromGPU(vaddr, size);
|
||||
const auto id = FindBuffer(vaddr, size);
|
||||
auto [dst, dst_offset] = ObtainBuffer(vaddr, size, true, true, id);
|
||||
EXIT_IF(dst == nullptr);
|
||||
auto [dst, dst_offset] = ObtainBuffer(vaddr, size, true, true);
|
||||
dst->Fill(dst_offset, size, value);
|
||||
}
|
||||
|
||||
@@ -615,29 +532,11 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
" size=0x%016" PRIx64 " src_gds=%d dst_gds=%d\n",
|
||||
src_vaddr, dst_vaddr, size, static_cast<int>(src_gds), static_cast<int>(dst_gds));
|
||||
}
|
||||
if (src_memory || dst_memory) {
|
||||
const auto src_region =
|
||||
src_memory ? m_texture_cache.QueryRegion(src_vaddr, size) : TextureCache::RegionInfo {};
|
||||
const auto dst_region =
|
||||
dst_memory ? m_texture_cache.QueryRegion(dst_vaddr, size) : TextureCache::RegionInfo {};
|
||||
if (src_memory && dst_memory && !HasGpuDirtyBytes(src_vaddr, size) &&
|
||||
!HasGpuDirtyBytes(dst_vaddr, size) && !src_region.gpu_image_bytes &&
|
||||
!dst_region.gpu_image_bytes) {
|
||||
if (dst_region.image_bytes) {
|
||||
m_texture_cache.InvalidateMemory(dst_vaddr, size);
|
||||
}
|
||||
std::array<uint8_t, 64 * 1024> bytes;
|
||||
for (uint64_t offset = 0; offset < size;) {
|
||||
const auto chunk = std::min<uint64_t>(size - offset, bytes.size());
|
||||
if (!Libs::LibKernel::Memory::TryReadBacking(src_vaddr + offset, bytes.data(),
|
||||
chunk)) {
|
||||
EXIT("BufferCache: host DMA source has no direct backing\n");
|
||||
}
|
||||
WriteHostMemory(dst_vaddr + offset, std::span {bytes}.first(chunk));
|
||||
offset += chunk;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (src_memory && dst_memory && !IsRegionGpuModified(dst_vaddr, size) &&
|
||||
!IsRegionGpuModified(src_vaddr, size) && !m_texture_cache.FindImageFromRange(src_vaddr, size)) {
|
||||
std::memcpy(reinterpret_cast<void*>(dst_vaddr), reinterpret_cast<const void*>(src_vaddr),
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& command = m_scheduler.Current();
|
||||
@@ -650,10 +549,6 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
: std::pair {&m_gds_buffer, src_vaddr};
|
||||
auto [dst, dst_offset] = dst_memory ? ObtainBuffer(dst_vaddr, size, true, true, dst_id)
|
||||
: std::pair {&m_gds_buffer, dst_vaddr};
|
||||
EXIT_IF(src == nullptr || dst == nullptr);
|
||||
if (src == dst && src_offset < dst_offset + size && dst_offset < src_offset + size) {
|
||||
EXIT("BufferCache: resolved Vulkan copy ranges overlap\n");
|
||||
}
|
||||
dst->CopyFrom(command, *src, src_offset, dst_offset, size);
|
||||
}
|
||||
|
||||
@@ -697,8 +592,7 @@ void BufferCache::RunGarbageCollector() {
|
||||
const size_t limit = aggressive ? 64 : 32;
|
||||
|
||||
std::vector<BufferId> dirty_buffers;
|
||||
std::vector<DownloadCopy> copies;
|
||||
size_t retire_count = 0;
|
||||
size_t retire_count = 0;
|
||||
m_lru_cache.ForEachItemBelow(tick - age, [&](BufferId id) {
|
||||
auto& buffer = m_slot_buffers[id];
|
||||
EXIT_IF(buffer.is_deleted);
|
||||
@@ -709,19 +603,7 @@ void BufferCache::RunGarbageCollector() {
|
||||
return false;
|
||||
}
|
||||
if (dirty) {
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
buffer.CpuAddress(), buffer.Size(),
|
||||
[&](uint64_t dirty_address, uint64_t dirty_size) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, dirty_address,
|
||||
dirty_size, "garbage collection");
|
||||
},
|
||||
[&](uint64_t dirty_address, uint64_t dirty_size) noexcept {
|
||||
m_gpu_modified_ranges.ForEachIntersection(
|
||||
dirty_address, dirty_size, [&](RangeSet::Range range) {
|
||||
copies.push_back({&buffer, range.address - buffer.CpuAddress(),
|
||||
range.address, range.size});
|
||||
});
|
||||
});
|
||||
EXIT_IF(!DownloadBufferMemory(buffer, buffer.CpuAddress(), buffer.Size()));
|
||||
dirty_buffers.push_back(id);
|
||||
} else {
|
||||
m_memory_tracker.UntrackMemory(buffer.CpuAddress(), buffer.Size());
|
||||
@@ -733,8 +615,10 @@ void BufferCache::RunGarbageCollector() {
|
||||
return;
|
||||
}
|
||||
|
||||
EXIT_IF(copies.empty());
|
||||
DownloadBufferMemory(copies);
|
||||
// Publish all queued downloads before releasing their tracked pages and owners.
|
||||
const auto completion_tick = m_scheduler.CurrentTick();
|
||||
m_scheduler.Wait(completion_tick);
|
||||
m_scheduler.WaitPriorityOperations(completion_tick);
|
||||
for (const auto id: dirty_buffers) {
|
||||
auto& buffer = m_slot_buffers[id];
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(buffer.CpuAddress(), buffer.Size());
|
||||
|
||||
+19
-10
@@ -74,16 +74,26 @@ public:
|
||||
private:
|
||||
friend struct BufferCacheTestAccess;
|
||||
|
||||
struct DownloadCopy;
|
||||
bool IsBufferInvalid(BufferId id) const {
|
||||
const auto* buffer = m_slot_buffers.try_get(id);
|
||||
return buffer == nullptr || buffer->is_deleted;
|
||||
}
|
||||
|
||||
using BufferMap = std::map<uint64_t, BufferId>;
|
||||
struct OverlapResult {
|
||||
BufferMap::iterator first;
|
||||
BufferMap::iterator last;
|
||||
uint64_t begin;
|
||||
uint64_t end;
|
||||
bool has_stream_leap;
|
||||
};
|
||||
|
||||
using PageTable = MultiLevelPageTable<BufferId, CACHING_PAGEBITS, 40, 16>;
|
||||
static_assert(CACHING_PAGESIZE == (uint64_t {1} << PageTable::kPageBits));
|
||||
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
|
||||
[[nodiscard]] static constexpr uint64_t AlignDownload(uint64_t size) noexcept {
|
||||
return (size + DOWNLOAD_ALIGNMENT - 1) & ~(DOWNLOAD_ALIGNMENT - 1);
|
||||
}
|
||||
[[nodiscard]] static std::pair<uint64_t, uint64_t> DownloadEnvelope(const DownloadCopy& copy);
|
||||
void WriteDataBuffer(Buffer& buffer, uint64_t address, const void* source, uint64_t size);
|
||||
void TouchBuffer(const Buffer& buffer);
|
||||
[[nodiscard]] OverlapResult ResolveOverlaps(uint64_t vaddr, uint64_t size);
|
||||
void JoinOverlap(BufferId new_id, BufferId overlap_id, bool accumulate_stream_score);
|
||||
[[nodiscard]] BufferId CreateBuffer(uint64_t vaddr, uint64_t size);
|
||||
void Register(BufferId id);
|
||||
void Unregister(BufferId id);
|
||||
@@ -95,9 +105,8 @@ private:
|
||||
[[nodiscard]] vk::Buffer UploadCopies(Buffer& buffer, std::span<vk::BufferCopy> copies,
|
||||
uint64_t total_size);
|
||||
[[nodiscard]] bool SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size);
|
||||
void DownloadBufferMemory(std::span<const DownloadCopy> copies);
|
||||
void WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data);
|
||||
void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write);
|
||||
// Queues backing publication; callers wait before clearing dirty pages or reusing their data.
|
||||
[[nodiscard]] bool DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t size);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
@@ -106,7 +115,7 @@ private:
|
||||
Buffer m_bda_pagetable_buffer;
|
||||
Common::SlotVector<Buffer> m_slot_buffers;
|
||||
Common::LeastRecentlyUsedCache<BufferId, uint64_t> m_lru_cache;
|
||||
std::map<uint64_t, BufferId> m_buffers;
|
||||
BufferMap m_buffers;
|
||||
PageTable m_page_table;
|
||||
RangeSet m_gpu_modified_ranges;
|
||||
MemoryTracker m_memory_tracker;
|
||||
|
||||
+9
-25
@@ -23,13 +23,10 @@ constexpr size_t PageFaultAreaSize = MaxPageFaults * sizeof(uint64_t);
|
||||
} // namespace
|
||||
|
||||
FaultManager::FaultManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
BufferCache& buffer_cache, uint32_t caching_pagebits,
|
||||
uint64_t caching_num_pages)
|
||||
BufferCache& buffer_cache)
|
||||
: m_graphics(graphics), m_scheduler(scheduler), m_buffer_cache(buffer_cache),
|
||||
m_caching_pagesize(uint64_t {1} << caching_pagebits),
|
||||
m_caching_num_pages(caching_num_pages), m_fault_buffer_size(caching_num_pages / 8),
|
||||
m_fault_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags,
|
||||
m_fault_buffer_size),
|
||||
BufferCache::CACHING_NUMPAGES / 8),
|
||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 0, AllFlags,
|
||||
MaxPendingFaults * PageFaultAreaSize) {
|
||||
SetVulkanObjectNameF(m_graphics.device, m_fault_buffer.Handle(), "Fault Buffer");
|
||||
@@ -39,7 +36,6 @@ FaultManager::FaultManager(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
{1, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute, nullptr},
|
||||
};
|
||||
vk::DescriptorSetLayoutCreateInfo layout_info {};
|
||||
layout_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
layout_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
layout_info.bindingCount = std::size(bindings);
|
||||
layout_info.pBindings = bindings;
|
||||
@@ -48,16 +44,9 @@ FaultManager::FaultManager(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
&m_fault_process_desc_layout),
|
||||
"create fault-buffer descriptor layout");
|
||||
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = std::size(FAULT_BUFFER_PROCESS_SPV) * sizeof(uint32_t);
|
||||
module_info.pCode = FAULT_BUFFER_PROCESS_SPV;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create fault-buffer shader module");
|
||||
const auto module = CompileSPV(FAULT_BUFFER_PROCESS_SPV, m_graphics.device);
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
pipeline_layout_info.setLayoutCount = 1;
|
||||
pipeline_layout_info.pSetLayouts = &m_fault_process_desc_layout;
|
||||
RequireVulkanSuccess(
|
||||
@@ -66,12 +55,10 @@ FaultManager::FaultManager(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
"create fault-buffer pipeline layout");
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
vk::ComputePipelineCreateInfo pipeline_info {};
|
||||
pipeline_info.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
pipeline_info.stage = stage;
|
||||
pipeline_info.layout = m_fault_process_pipeline_layout;
|
||||
const auto result = m_graphics.device.createComputePipelines(
|
||||
@@ -99,14 +86,13 @@ void FaultManager::ProcessFaultBuffer() {
|
||||
m_download_buffer.Flush(offset, PageFaultAreaSize);
|
||||
|
||||
vk::BufferMemoryBarrier2 pre_barrier {};
|
||||
pre_barrier.sType = vk::StructureType::eBufferMemoryBarrier2;
|
||||
pre_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
pre_barrier.srcAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
pre_barrier.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader;
|
||||
pre_barrier.dstAccessMask = vk::AccessFlagBits2::eShaderRead;
|
||||
pre_barrier.buffer = m_fault_buffer.Handle();
|
||||
pre_barrier.offset = 0;
|
||||
pre_barrier.size = m_fault_buffer_size;
|
||||
pre_barrier.size = m_fault_buffer.Size();
|
||||
auto post_barrier = pre_barrier;
|
||||
post_barrier.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader;
|
||||
post_barrier.srcAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
@@ -114,12 +100,11 @@ void FaultManager::ProcessFaultBuffer() {
|
||||
post_barrier.dstAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
|
||||
const vk::DescriptorBufferInfo infos[] {
|
||||
{m_fault_buffer.Handle(), 0, m_fault_buffer_size},
|
||||
{m_fault_buffer.Handle(), 0, m_fault_buffer.Size()},
|
||||
{m_download_buffer.Handle(), offset, PageFaultAreaSize},
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); ++index) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
@@ -129,7 +114,6 @@ void FaultManager::ProcessFaultBuffer() {
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::DependencyInfo dependency {};
|
||||
dependency.sType = vk::StructureType::eDependencyInfo;
|
||||
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
||||
dependency.bufferMemoryBarrierCount = 1;
|
||||
dependency.pBufferMemoryBarriers = &pre_barrier;
|
||||
@@ -137,7 +121,7 @@ void FaultManager::ProcessFaultBuffer() {
|
||||
command.bindPipeline(vk::PipelineBindPoint::eCompute, m_fault_process_pipeline);
|
||||
command.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute,
|
||||
m_fault_process_pipeline_layout, 0, writes);
|
||||
const auto num_threads = m_caching_num_pages / 32;
|
||||
const auto num_threads = BufferCache::CACHING_NUMPAGES / 32;
|
||||
const auto num_workgroups = (num_threads + 63) / 64;
|
||||
command.dispatch(static_cast<uint32_t>(num_workgroups), 1, 1);
|
||||
dependency.pBufferMemoryBarriers = &post_barrier;
|
||||
@@ -146,14 +130,14 @@ void FaultManager::ProcessFaultBuffer() {
|
||||
const auto area = m_current_area;
|
||||
m_scheduler.DeferOperation([this, mapped, offset, area] {
|
||||
m_download_buffer.Invalidate(offset, PageFaultAreaSize);
|
||||
m_fault_ranges.Clear();
|
||||
RangeSet fault_ranges;
|
||||
const auto* faults = std::bit_cast<const uint64_t*>(mapped);
|
||||
const auto count = static_cast<uint32_t>(faults[0]);
|
||||
for (uint32_t index = 1; index <= count; ++index) {
|
||||
m_fault_ranges.Add(faults[index], m_caching_pagesize);
|
||||
fault_ranges.Add(faults[index], BufferCache::CACHING_PAGESIZE);
|
||||
LOGF("Accessed non-GPU cached memory at 0x%016" PRIx64 "\n", faults[index]);
|
||||
}
|
||||
m_fault_ranges.ForEach([this](uint64_t start, uint64_t end) {
|
||||
fault_ranges.ForEach([this](uint64_t start, uint64_t end) {
|
||||
EXIT_IF(end - start > std::numeric_limits<uint32_t>::max());
|
||||
(void)m_buffer_cache.FindBuffer(start, end - start);
|
||||
});
|
||||
|
||||
+1
-7
@@ -2,7 +2,6 @@
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_CACHE_FAULTMANAGER_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "graphics/host_gpu/rangeSet.h"
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
|
||||
#include <array>
|
||||
@@ -16,8 +15,7 @@ class FaultManager {
|
||||
static constexpr size_t MaxPendingFaults = 8;
|
||||
|
||||
public:
|
||||
FaultManager(GraphicContext& graphics, CommandScheduler& scheduler, BufferCache& buffer_cache,
|
||||
uint32_t caching_pagebits, uint64_t caching_num_pages);
|
||||
FaultManager(GraphicContext& graphics, CommandScheduler& scheduler, BufferCache& buffer_cache);
|
||||
~FaultManager();
|
||||
KYTY_CLASS_NO_COPY(FaultManager);
|
||||
|
||||
@@ -28,10 +26,6 @@ private:
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
BufferCache& m_buffer_cache;
|
||||
RangeSet m_fault_ranges;
|
||||
uint64_t m_caching_pagesize;
|
||||
uint64_t m_caching_num_pages;
|
||||
uint64_t m_fault_buffer_size;
|
||||
Buffer m_fault_buffer;
|
||||
Buffer m_download_buffer;
|
||||
std::array<uint64_t, MaxPendingFaults> m_fault_areas {};
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/graphicsRun.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
namespace Libs::Graphics {
|
||||
|
||||
GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
: m_scheduler(scheduler), m_buffer_cache(graphics, scheduler, m_page_manager, m_texture_cache),
|
||||
m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache) {}
|
||||
|
||||
GpuResourceManager::~GpuResourceManager() = default;
|
||||
|
||||
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
||||
constexpr uint64_t fault_size = 8;
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
return false;
|
||||
}
|
||||
if (access == PageFaultAccess::Write) {
|
||||
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
} else {
|
||||
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (!IsMapped(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
return false;
|
||||
}
|
||||
std::shared_lock lock(m_mapped_ranges_mutex);
|
||||
return m_mapped_ranges.Contains(vaddr, size);
|
||||
}
|
||||
|
||||
void GpuResourceManager::MapMemory(uint64_t vaddr, uint64_t size) {
|
||||
{
|
||||
std::lock_guard lock(m_mapped_ranges_mutex);
|
||||
m_mapped_ranges.Add(vaddr, size);
|
||||
}
|
||||
m_page_manager.OnGpuMap(vaddr, size);
|
||||
}
|
||||
|
||||
void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported memory unmap from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
const auto unmap = [this, vaddr, size] {
|
||||
if (m_scheduler.Active()) {
|
||||
const auto tick = m_scheduler.CurrentTick();
|
||||
m_scheduler.Finish();
|
||||
m_scheduler.WaitPriorityOperations(tick);
|
||||
}
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.UnmapMemory(vaddr, size);
|
||||
m_page_manager.OnGpuUnmap(vaddr, size);
|
||||
std::lock_guard lock(m_mapped_ranges_mutex);
|
||||
m_mapped_ranges.Subtract(vaddr, size);
|
||||
};
|
||||
if (m_gpu == nullptr) {
|
||||
unmap();
|
||||
return;
|
||||
}
|
||||
m_gpu->SendCommandSync(unmap);
|
||||
}
|
||||
|
||||
void GpuResourceManager::PrepareBda() {
|
||||
std::shared_lock lock(m_mapped_ranges_mutex);
|
||||
m_mapped_ranges.ForEach([this](uint64_t start, uint64_t end) {
|
||||
m_buffer_cache.SynchronizeBuffersInRange(start, end - start);
|
||||
});
|
||||
m_fault_process_pending = true;
|
||||
}
|
||||
|
||||
void GpuResourceManager::RunGarbageCollector() {
|
||||
if (m_fault_process_pending) {
|
||||
m_fault_process_pending = false;
|
||||
m_buffer_cache.ProcessFaultBuffer();
|
||||
}
|
||||
m_texture_cache.ProcessDownloadImages();
|
||||
m_texture_cache.RunGarbageCollector();
|
||||
m_buffer_cache.RunGarbageCollector();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandScheduler;
|
||||
class GuestGpu;
|
||||
|
||||
class GpuResourceManager {
|
||||
public:
|
||||
GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler);
|
||||
~GpuResourceManager();
|
||||
KYTY_CLASS_NO_COPY(GpuResourceManager);
|
||||
|
||||
[[nodiscard]] BufferCache& GetBufferCache() { return m_buffer_cache; }
|
||||
[[nodiscard]] TextureCache& GetTextureCache() { return m_texture_cache; }
|
||||
void SetGpu(GuestGpu* gpu) noexcept { m_gpu = gpu; }
|
||||
|
||||
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
|
||||
[[nodiscard]] bool InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
|
||||
void MapMemory(uint64_t vaddr, uint64_t size);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
void PrepareBda();
|
||||
void RunGarbageCollector();
|
||||
|
||||
private:
|
||||
PageManager m_page_manager;
|
||||
CommandScheduler& m_scheduler;
|
||||
BufferCache m_buffer_cache;
|
||||
TextureCache m_texture_cache;
|
||||
mutable std::shared_mutex m_mapped_ranges_mutex;
|
||||
RangeSet m_mapped_ranges;
|
||||
GuestGpu* m_gpu = nullptr;
|
||||
bool m_fault_process_pending = false;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
@@ -256,17 +256,6 @@ private:
|
||||
size_type m_inline_size = 0;
|
||||
};
|
||||
|
||||
// Removes only the requested owner. Other owners in the same page entry remain intact.
|
||||
template <typename Container, typename Value>
|
||||
[[nodiscard]] bool EraseExact(Container& owners, const Value& owner) {
|
||||
const auto it = std::find(owners.begin(), owners.end(), owner);
|
||||
if (it == owners.end()) {
|
||||
return false;
|
||||
}
|
||||
owners.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_MULTILEVELPAGETABLE_H_
|
||||
@@ -116,9 +116,6 @@ vk::Sampler SamplerCache::GetSampler(const ShaderSamplerResource& r) {
|
||||
default: EXIT("unknown border color: %d", static_cast<int>(r.BorderColorType()));
|
||||
}
|
||||
|
||||
sampler_info.sType = vk::StructureType::eSamplerCreateInfo;
|
||||
sampler_info.pNext = nullptr;
|
||||
sampler_info.flags = {};
|
||||
sampler_info.magFilter = to_vk_filter(mag_filter);
|
||||
sampler_info.minFilter = to_vk_filter(min_filter);
|
||||
sampler_info.mipmapMode =
|
||||
|
||||
+29
-67
@@ -1,13 +1,12 @@
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
@@ -46,16 +45,11 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
const auto remainder = value % alignment;
|
||||
if (remainder == 0) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
const auto increment = alignment - remainder;
|
||||
if (value > std::numeric_limits<uint64_t>::max() - increment) {
|
||||
const auto aligned = Common::AlignUp(value, alignment);
|
||||
if (aligned < value) {
|
||||
return false;
|
||||
}
|
||||
result = value + increment;
|
||||
result = aligned;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,14 +58,13 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsage usage,
|
||||
uint64_t cpu_address, vk::BufferUsageFlags flags, uint64_t size)
|
||||
: m_graphics(&graphics), m_scheduler(&scheduler), m_usage(usage), m_cpu_address(cpu_address),
|
||||
m_size(size), m_buffer(std::make_unique<VulkanBuffer>()) {
|
||||
m_size(size) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(graphics.allocator == nullptr || size == 0);
|
||||
|
||||
vk::BufferCreateInfo buffer_info {};
|
||||
buffer_info.size = size;
|
||||
buffer_info.usage = flags;
|
||||
buffer_info.sharingMode = vk::SharingMode::eExclusive;
|
||||
|
||||
const bool with_bda = bool(flags & vk::BufferUsageFlagBits::eShaderDeviceAddress);
|
||||
const VmaAllocationCreateFlags bda_flag =
|
||||
@@ -88,93 +81,70 @@ Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsag
|
||||
VkBuffer native_buffer = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(vmaCreateBuffer(
|
||||
graphics.allocator, static_cast<const VkBufferCreateInfo*>(buffer_info), &allocation_info,
|
||||
&native_buffer, &m_buffer->memory.allocation, &allocation_result));
|
||||
&native_buffer, &m_allocation, &allocation_result));
|
||||
if (result != vk::Result::eSuccess) {
|
||||
graphics.LogMemoryBudget();
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
|
||||
m_buffer->buffer = native_buffer;
|
||||
m_buffer->usage = flags;
|
||||
m_buffer->buffer_size = size;
|
||||
m_buffer->memory.allocation_info = allocation_result;
|
||||
m_buffer->memory.memory = allocation_result.deviceMemory;
|
||||
m_buffer->memory.offset = allocation_result.offset;
|
||||
m_buffer->memory.type = allocation_result.memoryType;
|
||||
m_buffer->memory.unique_id = VulkanNextMemoryUniqueId();
|
||||
graphics.device.getBufferMemoryRequirements(m_buffer->buffer, &m_buffer->memory.requirements);
|
||||
if (static_cast<bool>(flags & vk::BufferUsageFlagBits::eShaderDeviceAddress)) {
|
||||
m_buffer = native_buffer;
|
||||
if (with_bda) {
|
||||
vk::BufferDeviceAddressInfo address_info {};
|
||||
address_info.sType = vk::StructureType::eBufferDeviceAddressInfo;
|
||||
address_info.buffer = m_buffer->buffer;
|
||||
address_info.buffer = m_buffer;
|
||||
m_device_address = graphics.device.getBufferAddress(address_info);
|
||||
EXIT_IF(m_device_address == 0);
|
||||
}
|
||||
|
||||
VkMemoryPropertyFlags properties = 0;
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_buffer->memory.allocation, &properties);
|
||||
m_buffer->memory.property = vk::MemoryPropertyFlags(properties);
|
||||
m_is_coherent = (properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_allocation, &properties);
|
||||
m_coherent = (properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||
if (allocation_result.pMappedData != nullptr) {
|
||||
m_mapped = {static_cast<uint8_t*>(allocation_result.pMappedData),
|
||||
static_cast<size_t>(size)};
|
||||
}
|
||||
VulkanTrackAllocation(m_buffer->memory);
|
||||
}
|
||||
|
||||
Buffer::~Buffer() {
|
||||
if (m_buffer->buffer != nullptr) {
|
||||
VulkanUntrackAllocation(m_buffer->memory);
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer->buffer, m_buffer->memory.allocation);
|
||||
if (m_buffer != nullptr) {
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer, m_allocation);
|
||||
}
|
||||
}
|
||||
|
||||
vk::Buffer Buffer::Handle() const noexcept {
|
||||
return m_buffer->buffer;
|
||||
}
|
||||
|
||||
vk::DeviceAddress Buffer::BufferDeviceAddress() const noexcept {
|
||||
EXIT_IF(m_device_address == 0);
|
||||
return m_device_address;
|
||||
}
|
||||
|
||||
bool Buffer::IsInBounds(uint64_t address, uint64_t size) const noexcept {
|
||||
return address >= m_cpu_address && size <= m_size && address - m_cpu_address <= m_size - size;
|
||||
}
|
||||
|
||||
void Buffer::Write(uint64_t offset, const void* source, uint64_t size) {
|
||||
EXIT_IF(source == nullptr || m_mapped.empty() || offset > m_size || size > m_size - offset);
|
||||
std::memcpy(m_mapped.data() + offset, source, static_cast<size_t>(size));
|
||||
Flush(offset, size);
|
||||
return address >= m_cpu_address && size <= Size() && address - m_cpu_address <= Size() - size;
|
||||
}
|
||||
|
||||
void Buffer::Flush(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_mapped.empty() || offset > m_size || size > m_size - offset);
|
||||
if (!m_is_coherent && size != 0) {
|
||||
EXIT_IF(m_mapped.empty() || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaFlushAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||
vmaFlushAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::Invalidate(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_usage != MemoryUsage::Download || offset > m_size || size > m_size - offset);
|
||||
if (!m_is_coherent && size != 0) {
|
||||
EXIT_IF(m_usage != MemoryUsage::Download || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaInvalidateAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||
vmaInvalidateAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
vk::BufferMemoryBarrier Buffer::Barrier(uint64_t offset, uint64_t size, vk::AccessFlags source,
|
||||
vk::AccessFlags destination) const {
|
||||
if (Handle() == nullptr || size == 0 || offset > m_size || size > m_size - offset) {
|
||||
if (Handle() == nullptr || size == 0 || offset > Size() || size > Size() - offset) {
|
||||
EXIT("Buffer: invalid DMA barrier, handle=%p offset=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" capacity=0x%016" PRIx64 "\n",
|
||||
static_cast<const void*>(Handle()), offset, size, m_size);
|
||||
static_cast<const void*>(Handle()), offset, size, Size());
|
||||
}
|
||||
vk::BufferMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barrier.srcAccessMask = source;
|
||||
barrier.dstAccessMask = destination;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
@@ -189,8 +159,8 @@ void Buffer::CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t sou
|
||||
uint64_t destination_offset, uint64_t size, vk::AccessFlags source_before,
|
||||
vk::AccessFlags destination_before, vk::AccessFlags source_after,
|
||||
vk::AccessFlags destination_after) {
|
||||
if (size == 0 || source_offset > source.m_size || size > source.m_size - source_offset ||
|
||||
destination_offset > m_size || size > m_size - destination_offset) {
|
||||
if (size == 0 || source_offset > source.Size() || size > source.Size() - source_offset ||
|
||||
destination_offset > Size() || size > Size() - destination_offset) {
|
||||
EXIT("Buffer: invalid copy range\n");
|
||||
}
|
||||
if (source.Handle() == Handle() && source_offset < destination_offset + size &&
|
||||
@@ -247,10 +217,8 @@ void Buffer::Fill(uint64_t offset, uint64_t size, uint32_t value) {
|
||||
|
||||
StreamBuffer::StreamBuffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsage usage,
|
||||
uint64_t size)
|
||||
: Buffer(graphics, scheduler, usage, 0, AllFlags, size) {
|
||||
ReserveWatches(m_current_watches, WATCHES_INITIAL_RESERVE);
|
||||
ReserveWatches(m_previous_watches, WATCHES_INITIAL_RESERVE);
|
||||
}
|
||||
: Buffer(graphics, scheduler, usage, 0, AllFlags, size),
|
||||
m_current_watches(WATCHES_INITIAL_RESERVE), m_previous_watches(WATCHES_INITIAL_RESERVE) {}
|
||||
|
||||
bool StreamBuffer::NormalizeReservation(bool coherent, uint64_t atom, uint64_t& size,
|
||||
uint64_t& alignment) {
|
||||
@@ -315,10 +283,8 @@ std::pair<uint8_t*, uint64_t> StreamBuffer::Map(uint64_t size, uint64_t alignmen
|
||||
}
|
||||
|
||||
void StreamBuffer::Commit() {
|
||||
if (!IsCoherent() && Usage() != MemoryUsage::Download && m_mapped_size != 0) {
|
||||
const auto result = vmaFlushAllocation(
|
||||
Graphics().allocator, NativeBuffer().memory.allocation, m_offset, m_mapped_size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
if (Usage() != MemoryUsage::Download && m_mapped_size != 0) {
|
||||
Flush(m_offset, m_mapped_size);
|
||||
}
|
||||
|
||||
m_offset += m_mapped_size;
|
||||
@@ -328,7 +294,7 @@ void StreamBuffer::Commit() {
|
||||
return;
|
||||
}
|
||||
if (m_current_watch_cursor + 1 >= m_current_watches.size()) {
|
||||
ReserveWatches(m_current_watches, WATCHES_RESERVE_CHUNK);
|
||||
m_current_watches.resize(m_current_watches.size() + WATCHES_RESERVE_CHUNK);
|
||||
}
|
||||
auto& watch = m_current_watches[m_current_watch_cursor++];
|
||||
watch.upper_bound = m_offset;
|
||||
@@ -344,10 +310,6 @@ uint64_t StreamBuffer::Copy(const void* source, uint64_t size, uint64_t alignmen
|
||||
return offset;
|
||||
}
|
||||
|
||||
void StreamBuffer::ReserveWatches(std::vector<Watch>& watches, size_t grow_size) {
|
||||
watches.resize(watches.size() + grow_size);
|
||||
}
|
||||
|
||||
bool StreamBuffer::WaitPendingOperations(const std::vector<Watch>& watches,
|
||||
std::optional<size_t> invalidation_mark,
|
||||
uint64_t requested_upper_bound, bool allow_wait,
|
||||
|
||||
+13
-12
@@ -6,19 +6,19 @@
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocation)
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
struct StreamBufferTestAccess;
|
||||
struct GraphicContext;
|
||||
struct VulkanBuffer;
|
||||
|
||||
enum class MemoryUsage : uint8_t {
|
||||
DeviceLocal,
|
||||
@@ -42,10 +42,10 @@ public:
|
||||
~Buffer();
|
||||
KYTY_CLASS_NO_COPY(Buffer);
|
||||
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept;
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept { return m_buffer; }
|
||||
[[nodiscard]] uint64_t Size() const noexcept { return m_size; }
|
||||
[[nodiscard]] std::span<uint8_t> Mapped() const noexcept { return m_mapped; }
|
||||
[[nodiscard]] bool IsCoherent() const noexcept { return m_is_coherent; }
|
||||
[[nodiscard]] bool IsCoherent() const noexcept { return m_coherent; }
|
||||
[[nodiscard]] MemoryUsage Usage() const noexcept { return m_usage; }
|
||||
[[nodiscard]] uint64_t CpuAddress() const noexcept { return m_cpu_address; }
|
||||
[[nodiscard]] vk::DeviceAddress BufferDeviceAddress() const noexcept;
|
||||
@@ -53,7 +53,8 @@ public:
|
||||
return address - m_cpu_address;
|
||||
}
|
||||
[[nodiscard]] bool IsInBounds(uint64_t address, uint64_t size) const noexcept;
|
||||
void Write(uint64_t offset, const void* source, uint64_t size);
|
||||
void IncreaseStreamScore(int score) noexcept { stream_score += score; }
|
||||
[[nodiscard]] int StreamScore() const noexcept { return stream_score; }
|
||||
void Flush(uint64_t offset, uint64_t size);
|
||||
void Invalidate(uint64_t offset, uint64_t size);
|
||||
void CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t source_offset,
|
||||
@@ -68,13 +69,13 @@ public:
|
||||
void Fill(uint64_t offset, uint64_t size, uint32_t value);
|
||||
|
||||
// BufferCache state lives directly on the resource.
|
||||
bool is_deleted = false;
|
||||
size_t lru_id = 0;
|
||||
bool is_deleted = false;
|
||||
int stream_score = 0;
|
||||
size_t lru_id = 0;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return *m_graphics; }
|
||||
[[nodiscard]] CommandScheduler& Scheduler() const noexcept { return *m_scheduler; }
|
||||
[[nodiscard]] VulkanBuffer& NativeBuffer() noexcept { return *m_buffer; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] vk::BufferMemoryBarrier Barrier(uint64_t offset, uint64_t size,
|
||||
@@ -85,11 +86,12 @@ private:
|
||||
CommandScheduler* m_scheduler = nullptr;
|
||||
MemoryUsage m_usage = MemoryUsage::DeviceLocal;
|
||||
uint64_t m_cpu_address = 0;
|
||||
uint64_t m_size = 0;
|
||||
vk::DeviceAddress m_device_address = 0;
|
||||
std::unique_ptr<VulkanBuffer> m_buffer;
|
||||
vk::Buffer m_buffer = nullptr;
|
||||
VmaAllocation m_allocation = nullptr;
|
||||
uint64_t m_size;
|
||||
bool m_coherent = false;
|
||||
std::span<uint8_t> m_mapped;
|
||||
bool m_is_coherent = false;
|
||||
};
|
||||
|
||||
class StreamBuffer final: public Buffer {
|
||||
@@ -110,7 +112,6 @@ private:
|
||||
uint64_t upper_bound = 0;
|
||||
};
|
||||
|
||||
void ReserveWatches(std::vector<Watch>& watches, size_t grow_size);
|
||||
[[nodiscard]] static bool NormalizeReservation(bool coherent, uint64_t atom, uint64_t& size,
|
||||
uint64_t& alignment);
|
||||
[[nodiscard]] bool WaitPendingOperations(const std::vector<Watch>& watches,
|
||||
|
||||
+493
-435
File diff suppressed because it is too large.
Load diff
+39
-33
@@ -13,6 +13,7 @@
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
@@ -37,12 +38,6 @@ public:
|
||||
BindingType type = BindingType::Texture;
|
||||
};
|
||||
|
||||
struct RegionInfo {
|
||||
bool image_pages = false;
|
||||
bool image_bytes = false;
|
||||
bool gpu_image_bytes = false;
|
||||
};
|
||||
|
||||
TextureCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
BufferCache& buffer_cache);
|
||||
~TextureCache();
|
||||
@@ -66,15 +61,11 @@ public:
|
||||
uint32_t packed_clear);
|
||||
void InvalidateMemory(uint64_t address, uint64_t size);
|
||||
void InvalidateMemoryFromGPU(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] RegionInfo QueryRegion(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionGpuModified(uint64_t address, uint64_t size);
|
||||
|
||||
[[nodiscard]] bool IsMeta(uint64_t address);
|
||||
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice,
|
||||
uint32_t* fill_value = nullptr);
|
||||
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice);
|
||||
[[nodiscard]] bool ClearMeta(uint64_t address);
|
||||
// Returns true when registered DCC absorbed the fill and the caller may skip the dispatch.
|
||||
// False may still record PendingDcc state, but the guest dispatch must execute.
|
||||
[[nodiscard]] bool TryConsumeDccFill(uint64_t address, uint64_t size, uint32_t fill_value);
|
||||
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
||||
|
||||
void UnmapMemory(uint64_t address, uint64_t size);
|
||||
@@ -83,21 +74,14 @@ public:
|
||||
|
||||
private:
|
||||
enum class TransferDirection { Upload, Download };
|
||||
struct ColorTransferPlan;
|
||||
struct DownloadPlan;
|
||||
struct TextureTransfer;
|
||||
struct ImageDownload;
|
||||
|
||||
struct MetaDataInfo {
|
||||
// A guest metadata-fill dispatch may initialize DCC before its render target is bound.
|
||||
// PendingDcc retains that exact fill until FindRenderTarget classifies the address,
|
||||
// without exposing an unconfirmed buffer address to the normal metadata heuristics.
|
||||
// Keep all surface metadata in one entry so CMask/FMask can be
|
||||
// registered beside HTile and DCC without introducing parallel tracking paths.
|
||||
enum class Type : uint8_t { PendingDcc, CMask, FMask, HTile, Dcc };
|
||||
enum class Type : uint8_t { CMask, FMask, HTile };
|
||||
|
||||
Type type = Type::PendingDcc;
|
||||
uint32_t clear_mask = 0;
|
||||
uint32_t fill_value = 0xffffffffu;
|
||||
uint64_t fill_size = 0;
|
||||
Type type;
|
||||
uint32_t clear_mask = UINT32_MAX;
|
||||
};
|
||||
|
||||
struct OverlapResult {
|
||||
@@ -109,6 +93,23 @@ private:
|
||||
using ImageIds = InlinePageOwnerList<ImageId, 16>;
|
||||
using ImagePageTable = MultiLevelPageTable<ImageIds, 20, 40, 10>;
|
||||
|
||||
// Callers have validated the nonempty 40-bit range with TryGetPageRange.
|
||||
template <typename Func>
|
||||
static void ForEachPage(uint64_t address, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, uint64_t>::type;
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
|
||||
const uint64_t page_end = (address + size - 1) >> ImagePageTable::kPageBits;
|
||||
for (uint64_t page = address >> ImagePageTable::kPageBits; page <= page_end; ++page) {
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(page)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ImageId InsertImage(const ImageInfo& info);
|
||||
[[nodiscard]] ImageId GetNullImage(const ImageDesc& desc);
|
||||
void RegisterImage(ImageId id);
|
||||
@@ -137,16 +138,21 @@ private:
|
||||
[[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding,
|
||||
ImageId cached);
|
||||
[[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source);
|
||||
void RefreshImage(ImageId id, const ImageDesc& desc);
|
||||
void InitializeImage(ImageId id, const ImageDesc& desc);
|
||||
[[nodiscard]] ColorTransferPlan BuildColorTransfer(const Image& image, BindingType binding,
|
||||
TransferDirection direction) const;
|
||||
[[nodiscard]] DownloadPlan BuildDownload(const Image& image) const;
|
||||
void UploadImage(Image& image, const ImageDesc& desc, Buffer& source, uint64_t source_offset);
|
||||
void DownloadImageData(Image& image, Buffer& destination, uint64_t destination_offset,
|
||||
uint64_t destination_size, DownloadPlan plan);
|
||||
void RefreshImage(ImageId id);
|
||||
void MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
||||
uint32_t metadata_base_layer);
|
||||
void InitializeImage(ImageId id);
|
||||
[[nodiscard]] TextureTransfer
|
||||
BuildTextureTransfer(const Image& image, BindingType binding, TransferDirection direction) const;
|
||||
[[nodiscard]] ImageDownload BuildDownload(const Image& image) const;
|
||||
void UploadImage(Image& image, Buffer& source, uint64_t source_offset);
|
||||
void DownloadImage(Image& image, Buffer& destination, uint64_t destination_offset,
|
||||
uint64_t destination_size, ImageDownload transfer);
|
||||
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
||||
void CommitGpuWrite(Image& image);
|
||||
// Caller holds m_lock. Volume layer ranges select depth slices.
|
||||
void ClearImage(CommandBuffer& command, ImageId id, vk::Format format,
|
||||
const vk::ImageSubresourceRange& range, const vk::ClearValue& clear);
|
||||
void PrepareImageCopy(Image& image);
|
||||
void RefreshCopySource(ImageId id);
|
||||
[[nodiscard]] bool CopyD16(Image& destination, Image& source);
|
||||
@@ -156,7 +162,7 @@ private:
|
||||
void ValidateImageDesc(const ImageDesc& desc) const;
|
||||
|
||||
void InvalidateCpuAliases(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] bool TryDownloadImage(ImageId id);
|
||||
[[nodiscard]] bool DownloadImageMemory(ImageId id);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
@@ -21,47 +22,40 @@ namespace Libs::Graphics {
|
||||
|
||||
static std::atomic<uint32_t> g_render_color_log_count = 0;
|
||||
|
||||
static void ResolveDccClearInfo(RenderColorInfo& info, vk::Format format, bool has_dcc,
|
||||
uint32_t packed_clear) {
|
||||
// Register-backed DCC clears use the target's packed clear value. Decode one-word guest
|
||||
// formats here; unsupported encodings remain tracked without unsafe materialization.
|
||||
info.metadata_clear_supported =
|
||||
has_dcc && DecodePackedColorClear(format, packed_clear, info.color_clear_value);
|
||||
switch (format) {
|
||||
case vk::Format::eR8G8B8A8Unorm:
|
||||
case vk::Format::eR8G8B8A8Srgb:
|
||||
case vk::Format::eB8G8R8A8Unorm:
|
||||
case vk::Format::eB8G8R8A8Srgb:
|
||||
case vk::Format::eA2B10G10R10UnormPack32:
|
||||
case vk::Format::eA2R10G10B10UnormPack32:
|
||||
info.metadata_fixed_clear_supported = has_dcc;
|
||||
break;
|
||||
default: info.metadata_fixed_clear_supported = false; break;
|
||||
static bool DccAlphaOnMsb(const HW::ColorInfo& info) {
|
||||
switch (info.format) {
|
||||
case Prospero::ChannelLayout::k10_10_10_2:
|
||||
case Prospero::ChannelLayout::k10_10_10_2Float:
|
||||
case Prospero::ChannelLayout::k5_5_5_1: return true;
|
||||
case Prospero::ChannelLayout::k2_10_10_10:
|
||||
case Prospero::ChannelLayout::k1_5_5_5: return false;
|
||||
default: break;
|
||||
}
|
||||
if (!info.metadata_clear_supported) {
|
||||
info.color_clear_value = {};
|
||||
const auto components =
|
||||
Prospero::ResolveRenderTargetFormat(info.format, info.channel_type).components;
|
||||
if (components == 1) {
|
||||
return info.channel_order != Prospero::ChannelOrder::kStandard;
|
||||
}
|
||||
return components == 3 || info.channel_order == Prospero::ChannelOrder::kStandard ||
|
||||
info.channel_order == Prospero::ChannelOrder::kAlt;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderColorInfo& r,
|
||||
void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& r,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t render_target_slot, bool ignore_target_mask,
|
||||
uint32_t rt_slot, bool ignore_target_mask,
|
||||
bool exact_format) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
|
||||
const auto rt_slot = (render_target_slot == UINT32_MAX ? render_target_first_bound_slot(buffer)
|
||||
: render_target_slot);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
auto mask = render_target_mask_slot(hw.GetRenderTargetMask(), rt_slot);
|
||||
if (ignore_target_mask && rt.base.addr != 0 && mask == 0) {
|
||||
mask = 0x0f;
|
||||
}
|
||||
|
||||
r.target_slot = rt_slot;
|
||||
r.export_mapping = {};
|
||||
r = {};
|
||||
r.target_slot = rt_slot;
|
||||
|
||||
if (rt.base.addr == 0 || mask == 0) {
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
@@ -76,23 +70,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
}
|
||||
}
|
||||
|
||||
// No color output
|
||||
r.type = RenderColorType::NoColorOutput;
|
||||
r.desc = {};
|
||||
r.base_addr = 0;
|
||||
r.image_id = {};
|
||||
r.image_view = nullptr;
|
||||
r.format = vk::Format::eUndefined;
|
||||
r.extent = {};
|
||||
r.base_mip_level = 0;
|
||||
r.base_array_layer = 0;
|
||||
r.buffer_size = 0;
|
||||
r.samples = 1;
|
||||
r.export_mapping = {};
|
||||
r.color_clear_enable = false;
|
||||
r.metadata_clear_supported = false;
|
||||
r.metadata_fixed_clear_supported = false;
|
||||
r.color_clear_value = {};
|
||||
return;
|
||||
}
|
||||
const auto samples = render_sample_count(rt.attrib.num_fragments);
|
||||
@@ -100,50 +77,11 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
EXIT("unsupported render-target sample configuration: samples=%u fragments=%u\n",
|
||||
rt.attrib.num_samples, rt.attrib.num_fragments);
|
||||
}
|
||||
const auto view = ResolveTargetViewInfo(
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset);
|
||||
switch (view.type) {
|
||||
case TargetViewType::Image2D:
|
||||
case TargetViewType::Image2DArray: break;
|
||||
case TargetViewType::Unsupported:
|
||||
EXIT("invalid render-target view: base=%u last=%u draw_offset=%u\n",
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index,
|
||||
render_target_slice_offset);
|
||||
}
|
||||
r.base_array_layer = view.base_layer;
|
||||
const uint32_t levels = rt.attrib2.num_mip_levels + 1u;
|
||||
if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) {
|
||||
EXIT("unsupported render-target mip range: current=%u levels=%u\n",
|
||||
rt.view.current_mip_level, levels);
|
||||
}
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
static std::atomic_uint log_count = 0;
|
||||
const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed);
|
||||
if (log_id < 128) {
|
||||
LOGF("RenderColorTarget: inspect slot=%" PRIu32 " base=0x%010" PRIx64
|
||||
" mask=0x%01" PRIx32 " attrib2_width=%" PRIu32 " attrib2_height=%" PRIu32
|
||||
" attrib3_tile=0x%08" PRIx32 " attrib3_dim=0x%08" PRIx32 " fmt=0x%08" PRIx32
|
||||
" nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 "\n",
|
||||
rt_slot, rt.base.addr, mask, rt.attrib2.width, rt.attrib2.height,
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
}
|
||||
}
|
||||
|
||||
// Color-control state selects the color-buffer operation and logical blend operation.
|
||||
// The normal copy operation is a regular color write, not an attachment clear.
|
||||
// Nonlinear clear values are still stored as normalized components.
|
||||
// Fast color clears are metadata driven and must be handled explicitly when
|
||||
// that metadata path is implemented; render-pass load must preserve contents.
|
||||
r.color_clear_enable = false;
|
||||
r.color_clear_value = {};
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t pitch = 0;
|
||||
uint64_t size = 0;
|
||||
bool tile = false;
|
||||
static constexpr std::array image_types {Prospero::ImageType::kColor1D,
|
||||
Prospero::ImageType::kColor2D,
|
||||
Prospero::ImageType::kColor3D};
|
||||
@@ -165,7 +103,48 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
if (volume && samples != 1) {
|
||||
EXIT("multisampled 3D render targets are unsupported\n");
|
||||
}
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
// For volumes, CB_COLOR_VIEW bounds exported slices; ATTRIB3 defines storage depth.
|
||||
// The host attachment contains only the selected slices that exist in this mip.
|
||||
const uint32_t last_layer = volume
|
||||
? std::min(rt.view.last_array_slice_index,
|
||||
std::max(depth >> rt.view.current_mip_level, 1u) - 1u)
|
||||
: rt.view.last_array_slice_index;
|
||||
const auto view = ResolveTargetViewInfo(
|
||||
rt.view.base_array_slice_index, last_layer, render_target_slice_offset);
|
||||
switch (view.type) {
|
||||
case TargetViewType::Image2D:
|
||||
case TargetViewType::Image2DArray: break;
|
||||
case TargetViewType::Unsupported:
|
||||
EXIT("invalid render-target view: base=%u last=%u draw_offset=%u\n",
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index,
|
||||
render_target_slice_offset);
|
||||
}
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
static std::atomic_uint log_count = 0;
|
||||
const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed);
|
||||
if (log_id < 128) {
|
||||
LOGF("RenderColorTarget: inspect slot=%" PRIu32 " base=0x%010" PRIx64
|
||||
" mask=0x%01" PRIx32 " attrib2_width=%" PRIu32 " attrib2_height=%" PRIu32
|
||||
" attrib3_tile=0x%08" PRIx32 " attrib3_dim=0x%08" PRIx32 " fmt=0x%08" PRIx32
|
||||
" nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 "\n",
|
||||
rt_slot, rt.base.addr, mask, rt.attrib2.width, rt.attrib2.height,
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
}
|
||||
}
|
||||
|
||||
// Color-control state selects the color-buffer operation and logical blend operation.
|
||||
// The normal copy operation is a regular color write, not an attachment clear.
|
||||
// Nonlinear clear values are still stored as normalized components.
|
||||
// Fast color clears are metadata driven and must be handled explicitly when
|
||||
// that metadata path is implemented; render-pass load must preserve contents.
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t pitch = 0;
|
||||
uint64_t size = 0;
|
||||
bool tile = false;
|
||||
const bool standard4 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard4KB;
|
||||
const bool standard64 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard64KB;
|
||||
const bool depth_tile = rt.attrib3.tile_mode == Prospero::TileMode::kDepth;
|
||||
@@ -224,19 +203,14 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
" layer=%u/%u\n",
|
||||
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
|
||||
}
|
||||
if (tile) {
|
||||
if (volume) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else if (texture_tile) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
// PPSA28068: the linear EULA target and its sampled view must share the padded pitch.
|
||||
if (!tile || volume || texture_tile) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = width;
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
|
||||
TileSizeOffset mip_sizes[16] {};
|
||||
@@ -305,12 +279,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
|
||||
const vk::Extent2D view_extent = {std::max(width >> rt.view.current_mip_level, 1u),
|
||||
std::max(height >> rt.view.current_mip_level, 1u)};
|
||||
const uint32_t view_depth = std::max(depth >> rt.view.current_mip_level, 1u);
|
||||
if (volume &&
|
||||
(view.base_layer >= view_depth || view.layer_count > view_depth - view.base_layer)) {
|
||||
EXIT("3D render-target view exceeds mip depth: base=%u count=%u depth=%u mip=%u\n",
|
||||
view.base_layer, view.layer_count, view_depth, rt.view.current_mip_level);
|
||||
}
|
||||
|
||||
auto decision_log_id = g_render_color_log_count.fetch_add(1);
|
||||
if (decision_log_id < 128) {
|
||||
@@ -337,10 +305,14 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
desc.info.tile_mode = rt.attrib3.tile_mode;
|
||||
const bool has_dcc = rt.info.dcc_compression_enable && rt.dcc_addr.addr != 0;
|
||||
if (has_dcc) {
|
||||
// DCC uses a separate metadata allocation. Carry its address through ImageInfo so
|
||||
// TextureCache can associate metadata fills observed before target registration.
|
||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||
desc.info.metadata.range.address = rt.dcc_addr.addr;
|
||||
TileSizeAlign metadata_size {};
|
||||
(void)TileGetDccSize(width, height, volume ? depth : view.image_layers, bytes_per_element,
|
||||
levels, rt.attrib3.tile_mode, metadata_size, rt.attrib.num_fragments);
|
||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||
desc.info.metadata.range = {rt.dcc_addr.addr, metadata_size.size};
|
||||
desc.info.metadata.dcc_clear_word = rt.clear_word0.word0;
|
||||
desc.info.metadata.dcc_clear_register_valid = true;
|
||||
desc.info.metadata.dcc_alpha_msb = DccAlphaOnMsb(rt.info);
|
||||
}
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
if (volume) {
|
||||
@@ -382,18 +354,10 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
auto& texture_cache = m_context.GetTextureCache();
|
||||
r.desc = std::move(desc);
|
||||
r.guest_mip_level = rt.view.current_mip_level;
|
||||
r.guest_array_layer = view.base_layer;
|
||||
r.image_id = texture_cache.FindImage(r.desc, exact_format);
|
||||
r.type = RenderColorType::RenderTexture;
|
||||
r.base_addr = rt.base.addr;
|
||||
r.image_view = nullptr;
|
||||
r.format = r.desc.view_info.format;
|
||||
r.extent = view_extent;
|
||||
r.base_mip_level = rt.view.current_mip_level;
|
||||
r.buffer_size = backing_size;
|
||||
r.samples = samples;
|
||||
r.export_mapping = target_format.export_mapping;
|
||||
r.color_clear_enable = false;
|
||||
ResolveDccClearInfo(r, target_format.format, has_dcc, rt.clear_word0.word0);
|
||||
BindRenderTarget(r.image_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,33 +6,24 @@
|
||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
enum class RenderColorType {
|
||||
NoColorOutput,
|
||||
RenderTexture,
|
||||
};
|
||||
|
||||
struct RenderColorInfo {
|
||||
RenderColorType type = RenderColorType::NoColorOutput;
|
||||
// Discovery keeps guest image information but can remap the view into a larger cache image.
|
||||
TextureCache::ImageDesc desc;
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::Extent2D extent = {};
|
||||
uint32_t base_mip_level = 0;
|
||||
uint32_t base_array_layer = 0;
|
||||
uint64_t base_addr = 0;
|
||||
uint64_t buffer_size = 0;
|
||||
uint32_t target_slot = 0;
|
||||
uint32_t samples = 1;
|
||||
uint32_t guest_mip_level = 0;
|
||||
uint32_t guest_array_layer = 0;
|
||||
Prospero::ColorComponentMapping export_mapping;
|
||||
bool color_clear_enable = false;
|
||||
bool metadata_clear_supported = false;
|
||||
bool metadata_fixed_clear_supported = false;
|
||||
vk::ClearColorValue color_clear_value {};
|
||||
|
||||
[[nodiscard]] vk::Extent2D Extent() const {
|
||||
return {std::max(desc.info.extent.width >> guest_mip_level, 1u),
|
||||
std::max(desc.info.extent.height >> guest_mip_level, 1u)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -19,11 +19,11 @@ void ReportVulkanFatal(const char* what, vk::Result result, uint64_t tick, uint3
|
||||
uint32_t arg3, uint64_t arg4) {
|
||||
LOGF("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, VulkanToString(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
what, vk::to_string(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::printf("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, VulkanToString(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
what, vk::to_string(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
@@ -34,7 +34,6 @@ CommandScheduler::CommandPool::CommandPool(GraphicContext& graphics, MasterSemap
|
||||
: m_graphics(graphics), m_master(master) {
|
||||
EXIT_IF(graphics.queue_family == static_cast<uint32_t>(-1));
|
||||
vk::CommandPoolCreateInfo create {};
|
||||
create.sType = vk::StructureType::eCommandPoolCreateInfo;
|
||||
create.queueFamilyIndex = graphics.queue_family;
|
||||
create.flags = vk::CommandPoolCreateFlagBits::eTransient |
|
||||
vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||
@@ -52,7 +51,6 @@ size_t CommandScheduler::CommandPool::Grow() {
|
||||
m_buffers.resize(first + GrowStep);
|
||||
|
||||
vk::CommandBufferAllocateInfo allocate {};
|
||||
allocate.sType = vk::StructureType::eCommandBufferAllocateInfo;
|
||||
allocate.commandPool = m_pool;
|
||||
allocate.level = vk::CommandBufferLevel::ePrimary;
|
||||
allocate.commandBufferCount = static_cast<uint32_t>(GrowStep);
|
||||
@@ -87,10 +85,7 @@ vk::CommandBuffer CommandScheduler::CommandPool::Commit() {
|
||||
m_ticks[*found] = m_master.CurrentTick();
|
||||
}
|
||||
|
||||
m_hint = *found + 1;
|
||||
if (m_hint == m_ticks.size()) {
|
||||
m_hint = 0;
|
||||
}
|
||||
m_hint = (*found + 1) % m_ticks.size();
|
||||
return m_buffers[*found];
|
||||
}
|
||||
|
||||
@@ -153,14 +148,10 @@ void CommandScheduler::Begin(HW::Context& registers, HW::UserConfig& user_config
|
||||
std::lock_guard lock(m_operation_mutex);
|
||||
EXIT_IF(m_operation_state != OperationState::Open);
|
||||
}
|
||||
m_registers = ®isters;
|
||||
m_user_config = &user_config;
|
||||
m_shaders = &shaders;
|
||||
m_command.Bind(registers, user_config, shaders);
|
||||
|
||||
if (m_command.IsInvalid()) {
|
||||
BeginNext();
|
||||
} else {
|
||||
BindCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,13 +208,7 @@ void CommandScheduler::Wait(uint64_t tick) {
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations() {
|
||||
PopPendingOperations(true);
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations(bool refresh_gpu_tick) {
|
||||
if (refresh_gpu_tick) {
|
||||
m_master.Refresh();
|
||||
}
|
||||
m_master.Refresh();
|
||||
for (;;) {
|
||||
PendingOperation operation;
|
||||
{
|
||||
@@ -351,11 +336,6 @@ CommandBuffer& CommandScheduler::Current() {
|
||||
return m_command;
|
||||
}
|
||||
|
||||
void CommandScheduler::BindCurrent() {
|
||||
EXIT_IF(m_registers == nullptr || m_user_config == nullptr || m_shaders == nullptr);
|
||||
m_command.Bind(*m_registers, *m_user_config, *m_shaders);
|
||||
}
|
||||
|
||||
CommandBuffer& CommandScheduler::BeginCommand() {
|
||||
EXIT_IF(!m_command.IsInvalid());
|
||||
m_command.m_buffer = m_command_pool.Commit();
|
||||
@@ -381,14 +361,12 @@ uint64_t CommandScheduler::Submit(SubmitInfo submit) {
|
||||
submit.AddSignal(m_master.Handle(), tick);
|
||||
|
||||
vk::TimelineSemaphoreSubmitInfo timeline_info {};
|
||||
timeline_info.sType = vk::StructureType::eTimelineSemaphoreSubmitInfo;
|
||||
timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores;
|
||||
timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data();
|
||||
timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores;
|
||||
timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data();
|
||||
|
||||
vk::SubmitInfo submit_info {};
|
||||
submit_info.sType = vk::StructureType::eSubmitInfo;
|
||||
submit_info.pNext = &timeline_info;
|
||||
submit_info.waitSemaphoreCount = submit.num_wait_semaphores;
|
||||
submit_info.pWaitSemaphores = submit.wait_semaphores.data();
|
||||
@@ -414,8 +392,7 @@ uint64_t CommandScheduler::Submit(SubmitInfo submit) {
|
||||
}
|
||||
|
||||
void CommandScheduler::BeginNext() {
|
||||
EXIT_IF(!m_command.IsInvalid());
|
||||
BindCurrent();
|
||||
CheckActive();
|
||||
BeginCommand();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
void DeferPriorityOperation(Common::UniqueFunction<void>&& operation);
|
||||
[[nodiscard]] static bool InDeferredOperation() noexcept;
|
||||
|
||||
[[nodiscard]] bool Active() const noexcept { return m_registers != nullptr; }
|
||||
[[nodiscard]] bool Active() const noexcept { return m_command.m_registers != nullptr; }
|
||||
void CheckActive() const;
|
||||
CommandBuffer& Current();
|
||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||
@@ -80,9 +80,7 @@ private:
|
||||
uint64_t tick = 0;
|
||||
};
|
||||
|
||||
void BindCurrent();
|
||||
void BeginNext();
|
||||
void PopPendingOperations(bool refresh_gpu_tick);
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
|
||||
@@ -99,9 +97,6 @@ private:
|
||||
bool m_priority_active = false;
|
||||
uint64_t m_priority_active_tick = 0;
|
||||
OperationState m_operation_state = OperationState::Open;
|
||||
HW::Context* m_registers = nullptr;
|
||||
HW::UserConfig* m_user_config = nullptr;
|
||||
HW::Shader* m_shaders = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -34,10 +33,7 @@ void CommandBuffer::Begin() {
|
||||
auto buffer = Handle();
|
||||
|
||||
vk::CommandBufferBeginInfo begin_info {};
|
||||
begin_info.sType = vk::StructureType::eCommandBufferBeginInfo;
|
||||
begin_info.pNext = nullptr;
|
||||
begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
begin_info.pInheritanceInfo = nullptr;
|
||||
begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
|
||||
auto result = buffer.begin(&begin_info);
|
||||
|
||||
@@ -65,17 +61,16 @@ void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0,
|
||||
}
|
||||
|
||||
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 ||
|
||||
state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
if (m_rendering && m_render_state == state) {
|
||||
return;
|
||||
}
|
||||
EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 ||
|
||||
state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
EndRendering();
|
||||
|
||||
std::array<vk::RenderingAttachmentInfo, RENDER_COLOR_ATTACHMENTS_MAX> colors {};
|
||||
for (uint32_t i = 0; i < state.num_color_attachments; i++) {
|
||||
const auto& attachment = state.color_attachments[i];
|
||||
colors[i].sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
colors[i].imageView = attachment.image_view;
|
||||
colors[i].imageLayout = attachment.image_layout;
|
||||
colors[i].loadOp =
|
||||
@@ -86,7 +81,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
|
||||
const auto& depth_stencil = state.depth_stencil_attachment;
|
||||
vk::RenderingAttachmentInfo depth {};
|
||||
depth.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
depth.imageView = depth_stencil.image_view;
|
||||
depth.imageLayout = depth_stencil.image_layout;
|
||||
depth.loadOp =
|
||||
@@ -95,7 +89,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
depth.clearValue.depthStencil.depth = std::bit_cast<float>(depth_stencil.clear_value[0]);
|
||||
|
||||
vk::RenderingAttachmentInfo stencil {};
|
||||
stencil.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
stencil.imageView = depth_stencil.image_view;
|
||||
stencil.imageLayout = depth_stencil.image_layout;
|
||||
stencil.loadOp =
|
||||
@@ -104,7 +97,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
stencil.clearValue.depthStencil.stencil = depth_stencil.clear_value[1];
|
||||
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = {state.width, state.height};
|
||||
rendering.layerCount = state.num_layers;
|
||||
rendering.colorAttachmentCount = state.num_color_attachments;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
@@ -17,8 +16,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static std::atomic<uint32_t> g_scissor_default_log_count = 0;
|
||||
|
||||
uint32_t render_target_mask_slot(uint32_t mask, uint32_t slot) {
|
||||
return (mask >> (slot * 4u)) & 0x0fu;
|
||||
}
|
||||
@@ -59,7 +56,7 @@ uint32_t render_target_first_bound_slot(const CommandBuffer& buffer) {
|
||||
|
||||
bool graphics_debug_dump_enabled() {
|
||||
return Config::GraphicsDebugDumpEnabled() &&
|
||||
Config::GetPrintfDirection() != Config::OutputDirection::Silent;
|
||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||
}
|
||||
|
||||
void uc_print(const char* func, const HW::UserConfig& uc) {
|
||||
@@ -91,91 +88,75 @@ void uc_check(const HW::UserConfig& uc) {
|
||||
EXIT_NOT_IMPLEMENTED(user_en.vgpr3 != false);
|
||||
}
|
||||
|
||||
void sh_print(const char* func, const HW::Shader& /*uc*/) {
|
||||
LOGF("%s\n", func);
|
||||
}
|
||||
std::string rt_print(const char* func, const HW::RenderTarget& rt) {
|
||||
std::string dst;
|
||||
dst.reserve(4096);
|
||||
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt) {
|
||||
std::vector<std::string> dst;
|
||||
dst.reserve(40);
|
||||
dst += fmt::format("{}\n", func);
|
||||
|
||||
dst.push_back(fmt::format("{}\n", func));
|
||||
dst += fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr);
|
||||
dst += fmt::format("\t view.base_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.base_array_slice_index);
|
||||
dst += fmt::format("\t view.last_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.last_array_slice_index);
|
||||
dst += fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level);
|
||||
dst += fmt::format("\t info.fmask_compression_enable = {}\n",
|
||||
rt.info.fmask_compression_enable ? "true" : "false");
|
||||
|
||||
dst.push_back(fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr));
|
||||
dst.push_back(fmt::format("\t view.base_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.base_array_slice_index));
|
||||
dst.push_back(fmt::format("\t view.last_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.last_array_slice_index));
|
||||
dst.push_back(
|
||||
fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level));
|
||||
dst.push_back(fmt::format("\t info.fmask_compression_enable = {}\n",
|
||||
rt.info.fmask_compression_enable ? "true" : "false"));
|
||||
dst += fmt::format("\t info.fmask_data_compression_disable = {}\n",
|
||||
rt.info.fmask_data_compression_disable ? "true" : "false");
|
||||
dst += fmt::format("\t info.fmask_one_frag_mode = {}\n",
|
||||
rt.info.fmask_one_frag_mode ? "true" : "false");
|
||||
|
||||
// dst.push_back(fmt::format("\t info.fmask_compression_mode = 0x{:08x}\n", //
|
||||
// rt.info.fmask_compression_mode));
|
||||
dst.push_back(fmt::format("\t info.fmask_data_compression_disable = {}\n",
|
||||
rt.info.fmask_data_compression_disable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.fmask_one_frag_mode = {}\n",
|
||||
rt.info.fmask_one_frag_mode ? "true" : "false"));
|
||||
|
||||
dst.push_back(fmt::format("\t info.cmask_fast_clear_enable = {}\n",
|
||||
rt.info.cmask_fast_clear_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.dcc_compression_enable = {}\n",
|
||||
rt.info.dcc_compression_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.format)));
|
||||
dst.push_back(fmt::format("\t info.channel_type = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_type)));
|
||||
dst.push_back(fmt::format("\t info.channel_order = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_order)));
|
||||
dst.push_back(fmt::format("\t info.blend_bypa = {}\n",
|
||||
rt.info.blend_bypass ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.blend_clamp = {}\n",
|
||||
rt.info.blend_clamp ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.round_mode = {}\n",
|
||||
rt.info.round_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false"));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments));
|
||||
dst.push_back(fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels));
|
||||
dst.push_back(fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth));
|
||||
dst.push_back(fmt::format("\t attrib3.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode)));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension));
|
||||
dst.push_back(fmt::format("\t attrib3.metadata_pipe_aligned = {}\n",
|
||||
rt.attrib3.metadata_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n",
|
||||
rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_uncompressed_block_size));
|
||||
dst.push_back(fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_compressed_block_size));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform));
|
||||
dst.push_back(fmt::format("\t dcc.overwrite_combiner_disable = {}\n",
|
||||
rt.dcc.overwrite_combiner_disable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_block_size = 0x{:02x}\n",
|
||||
static_cast<uint8_t>(rt.dcc.independent_block_size)));
|
||||
dst.push_back(fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n",
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.dcc_clear_key_enable = {}\n",
|
||||
rt.dcc.dcc_clear_key_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr));
|
||||
dst.push_back(fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr));
|
||||
dst += fmt::format("\t info.cmask_fast_clear_enable = {}\n",
|
||||
rt.info.cmask_fast_clear_enable ? "true" : "false");
|
||||
dst += fmt::format("\t info.dcc_compression_enable = {}\n",
|
||||
rt.info.dcc_compression_enable ? "true" : "false");
|
||||
dst += fmt::format("\t info.format = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.format));
|
||||
dst += fmt::format("\t info.channel_type = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_type));
|
||||
dst += fmt::format("\t info.channel_order = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
dst += fmt::format("\t info.blend_bypa = {}\n",
|
||||
rt.info.blend_bypass ? "true" : "false");
|
||||
dst += fmt::format("\t info.blend_clamp = {}\n",
|
||||
rt.info.blend_clamp ? "true" : "false");
|
||||
dst += fmt::format("\t info.round_mode = {}\n",
|
||||
rt.info.round_mode ? "true" : "false");
|
||||
dst += fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false");
|
||||
dst += fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples);
|
||||
dst += fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments);
|
||||
dst += fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width);
|
||||
dst += fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height);
|
||||
dst += fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels);
|
||||
dst += fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth);
|
||||
dst += fmt::format("\t attrib3.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
dst += fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension);
|
||||
dst += fmt::format("\t attrib3.metadata_pipe_aligned = {}\n",
|
||||
rt.attrib3.metadata_pipe_aligned ? "true" : "false");
|
||||
dst += fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n",
|
||||
rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_uncompressed_block_size);
|
||||
dst += fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_compressed_block_size);
|
||||
dst += fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform);
|
||||
dst += fmt::format("\t dcc.overwrite_combiner_disable = {}\n",
|
||||
rt.dcc.overwrite_combiner_disable ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.independent_block_size = 0x{:02x}\n",
|
||||
static_cast<uint8_t>(rt.dcc.independent_block_size));
|
||||
dst += fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n",
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.dcc_clear_key_enable = {}\n",
|
||||
rt.dcc.dcc_clear_key_enable ? "true" : "false");
|
||||
dst += fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr);
|
||||
dst += fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr);
|
||||
dst += fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0);
|
||||
dst += fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1);
|
||||
dst += fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr);
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -387,81 +368,6 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
|
||||
z.size.valid ? "true" : "false");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
const HW::RenderControl& rc) {
|
||||
const bool depth_active = dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color;
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rc.copy_depth_to_color || rc.copy_stencil_to_color);
|
||||
EXIT_NOT_IMPLEMENTED(!z.z_info.HasValidTextureCompatibility());
|
||||
EXIT_NOT_IMPLEMENTED(!z.stencil_info.HasValidTextureCompatibility());
|
||||
if (z.z_info.format == Prospero::DepthFormat::kInvalid) {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kInvalid);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.htile_acceleration != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kZ16 &&
|
||||
z.z_info.format != Prospero::DepthFormat::kZ32F);
|
||||
if (z.z_info.num_samples != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("DepthTarget: using native num_samples=0x%08" PRIx32 "\n",
|
||||
z.z_info.num_samples);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
}
|
||||
|
||||
if (z.stencil_info.format == Prospero::StencilFormat::kInvalid) {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != Prospero::StencilFormat::k8UInt);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
}
|
||||
|
||||
if (z.z_info.format != Prospero::DepthFormat::kInvalid ||
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid) {
|
||||
EXIT_NOT_IMPLEMENTED(z.shading_rate_encoding != 0);
|
||||
|
||||
if (z.depth_view.current_mip_level != 0x00000000) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
||||
LOGF("DepthTarget: temporary: ignoring PS5 current mip level=0x%08" PRIx32 "\n",
|
||||
z.depth_view.current_mip_level);
|
||||
}
|
||||
}
|
||||
if (z.depth_view.depth_write_disable || z.depth_view.stencil_write_disable) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
||||
LOGF("DepthTarget: honoring write disable depth=%s, stencil=%s\n",
|
||||
z.depth_view.depth_write_disable ? "true" : "false",
|
||||
z.depth_view.stencil_write_disable ? "true" : "false");
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(!z.depth_view.depth_write_disable &&
|
||||
z.z_read_base_addr != z.z_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(!z.depth_view.stencil_write_disable &&
|
||||
z.stencil_read_base_addr != z.stencil_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(!z.depth_view.depth_write_disable && z.z_write_base_addr == 0);
|
||||
// EXIT_NOT_IMPLEMENTED(z.htile_data_base_addr == 0);
|
||||
EXIT_NOT_IMPLEMENTED(!z.size.valid);
|
||||
}
|
||||
}
|
||||
|
||||
static void ClipPrint(const char* func, const HW::ClipControl& c) {
|
||||
LOGF("%s\n", func);
|
||||
|
||||
@@ -546,16 +452,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.cull_front != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.cull_back != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.face != false);
|
||||
if (c.poly_mode != 0) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("\t temporary: PA_SU_SC_MODE_CNTL.POLY_MODE is not fully implemented; continuing "
|
||||
"with filled polygons\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.polymode_front_ptype != 0 && c.polymode_front_ptype != 2);
|
||||
EXIT_NOT_IMPLEMENTED(c.polymode_back_ptype != 0 && c.polymode_back_ptype != 2);
|
||||
if (c.vtx_window_offset_enable) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -565,7 +461,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.provoking_vtx_last != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.persp_corr_dis != false);
|
||||
}
|
||||
|
||||
@@ -592,16 +487,7 @@ static void BcPrint(const char* func, const HW::BlendControl& c, const HW::Blend
|
||||
color.red, color.green, color.blue, color.alpha, cc.mode, cc.op);
|
||||
}
|
||||
|
||||
static void BcCheck(const HW::BlendControl& /*c*/, const HW::BlendColor& color,
|
||||
const HW::ColorControl& cc) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_srcblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_comb_fcn != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_destblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_srcblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_comb_fcn != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_destblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.separate_alpha_blend != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.enable != false);
|
||||
static void BcCheck(const HW::BlendColor& color, const HW::ColorControl& cc) {
|
||||
if (color.red != 0.0f || color.green != 0.0f || color.blue != 0.0f || color.alpha != 0.0f) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -664,29 +550,6 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci
|
||||
sm.stencil_opval_bf);
|
||||
}
|
||||
|
||||
static void DCheck(const HW::DepthControl& c, const HW::StencilControl& s,
|
||||
const HW::StencilMask& /*sm*/) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.z_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.z_write_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.zfunc != 0);
|
||||
// Back-face stencil state is handled separately when enabled.
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_fail != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zpass != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zfail != 0);
|
||||
// Back-face stencil ops may legitimately differ from the front-face ops.
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_testval != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_mask != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_writemask != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_opval != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_testval_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_mask_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_writemask_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_opval_bf != 0);
|
||||
}
|
||||
|
||||
static void EqaaPrint(const char* func, const HW::EqaaControl& c) {
|
||||
LOGF("%s\n", func);
|
||||
|
||||
@@ -845,15 +708,6 @@ static void VpCheck(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc
|
||||
// EXIT_NOT_IMPLEMENTED(smc.vport_scissor_enable);
|
||||
EXIT_NOT_IMPLEMENTED(smc.line_stipple_enable);
|
||||
|
||||
if (vp.viewports[0].zmin > 0.000000 || vp.viewports[0].zmax != 1.000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("\t warning: non-default viewport depth clamp zmin = %f, zmax = %f; using "
|
||||
"viewport scale/offset for Vulkan depth range\n",
|
||||
vp.viewports[0].zmin, vp.viewports[0].zmax);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].xscale != 960.000000);
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].xoffset != 960.000000);
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].yscale != -540.000000);
|
||||
@@ -873,7 +727,7 @@ static void VpCheck(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc
|
||||
// EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_horz_clip - 33.133327f) > 0.001f);
|
||||
// EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_vert_clip - 59.629623f) > 0.001f);
|
||||
|
||||
if (vp.guard_band_horz_discard != 0.0f || vp.guard_band_vert_discard != 0.0f) {
|
||||
if (vp.guard_band_horz_discard != 1.0f || vp.guard_band_vert_discard != 1.0f) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 16) {
|
||||
LOGF("\t warning: unsupported PS5 guard band discard = %f, %f, continuing\n",
|
||||
@@ -893,10 +747,6 @@ static bool ScissorRectValid(const ScissorRect& r) {
|
||||
return r.right > r.left && r.bottom > r.top;
|
||||
}
|
||||
|
||||
static bool ScissorRectSet(const ScissorRect& r) {
|
||||
return r.left != 0 || r.top != 0 || r.right != 0 || r.bottom != 0;
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectOffset(ScissorRect r, int x, int y) {
|
||||
r.left += x;
|
||||
r.right += x;
|
||||
@@ -906,8 +756,8 @@ static ScissorRect ScissorRectOffset(ScissorRect r, int x, int y) {
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectIntersect(const ScissorRect& a, const ScissorRect& b) {
|
||||
return {a.left > b.left ? a.left : b.left, a.top > b.top ? a.top : b.top,
|
||||
a.right < b.right ? a.right : b.right, a.bottom < b.bottom ? a.bottom : b.bottom};
|
||||
return {std::max(a.left, b.left), std::max(a.top, b.top),
|
||||
std::min(a.right, b.right), std::min(a.bottom, b.bottom)};
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectClamp(ScissorRect r, uint32_t width, uint32_t height) {
|
||||
@@ -954,49 +804,26 @@ static bool ScissorClipRuleToIntersectionMask(uint16_t rule, uint8_t* mask) {
|
||||
}
|
||||
|
||||
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
|
||||
vk::Extent2D extent) {
|
||||
ScissorRect screen {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right,
|
||||
vp.screen_scissor_bottom};
|
||||
ScissorRect final = screen;
|
||||
|
||||
if (!ScissorRectSet(screen)) {
|
||||
final = {0, 0, static_cast<int>(extent.width), static_cast<int>(extent.height)};
|
||||
|
||||
auto log_id = g_scissor_default_log_count.fetch_add(1);
|
||||
if (log_id < 32) {
|
||||
LOGF("temporary: default unset screen scissor to framebuffer extent %ux%u\n",
|
||||
extent.width, extent.height);
|
||||
vk::Extent2D extent, uint32_t viewport_index) {
|
||||
EXIT_IF(viewport_index >= std::size(vp.viewports));
|
||||
ScissorRect final {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right,
|
||||
vp.screen_scissor_bottom};
|
||||
const auto intersect = [&](ScissorRect rect, bool window_offset) {
|
||||
if (window_offset) {
|
||||
rect = ScissorRectOffset(rect, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
}
|
||||
final = ScissorRectIntersect(final, rect);
|
||||
};
|
||||
intersect({vp.window_scissor_left, vp.window_scissor_top, vp.window_scissor_right,
|
||||
vp.window_scissor_bottom}, vp.window_scissor_window_offset_enable);
|
||||
intersect({vp.generic_scissor_left, vp.generic_scissor_top, vp.generic_scissor_right,
|
||||
vp.generic_scissor_bottom}, vp.generic_scissor_window_offset_enable);
|
||||
|
||||
ScissorRect window {vp.window_scissor_left, vp.window_scissor_top, vp.window_scissor_right,
|
||||
vp.window_scissor_bottom};
|
||||
if (ScissorRectSet(window)) {
|
||||
if (vp.window_scissor_window_offset_enable) {
|
||||
window = ScissorRectOffset(window, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, window);
|
||||
}
|
||||
|
||||
ScissorRect generic {vp.generic_scissor_left, vp.generic_scissor_top, vp.generic_scissor_right,
|
||||
vp.generic_scissor_bottom};
|
||||
if (ScissorRectSet(generic)) {
|
||||
if (vp.generic_scissor_window_offset_enable) {
|
||||
generic = ScissorRectOffset(generic, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, generic);
|
||||
}
|
||||
|
||||
const auto& viewport = vp.viewports[0];
|
||||
ScissorRect viewport_scissor {viewport.viewport_scissor_left, viewport.viewport_scissor_top,
|
||||
viewport.viewport_scissor_right,
|
||||
viewport.viewport_scissor_bottom};
|
||||
if (smc.vport_scissor_enable && ScissorRectSet(viewport_scissor)) {
|
||||
if (viewport.viewport_scissor_window_offset_enable) {
|
||||
viewport_scissor =
|
||||
ScissorRectOffset(viewport_scissor, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, viewport_scissor);
|
||||
const auto& viewport = vp.viewports[viewport_index];
|
||||
if (smc.vport_scissor_enable) {
|
||||
intersect({viewport.viewport_scissor_left, viewport.viewport_scissor_top,
|
||||
viewport.viewport_scissor_right, viewport.viewport_scissor_bottom},
|
||||
viewport.viewport_scissor_window_offset_enable);
|
||||
}
|
||||
|
||||
if (vp.clip_rect_rule == 0) {
|
||||
@@ -1009,12 +836,8 @@ ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeC
|
||||
continue;
|
||||
}
|
||||
|
||||
ScissorRect clip {vp.clip_rect_left[i], vp.clip_rect_top[i], vp.clip_rect_right[i],
|
||||
vp.clip_rect_bottom[i]};
|
||||
if (vp.clip_rect_window_offset_enable[i]) {
|
||||
clip = ScissorRectOffset(clip, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, clip);
|
||||
intersect({vp.clip_rect_left[i], vp.clip_rect_top[i], vp.clip_rect_right[i],
|
||||
vp.clip_rect_bottom[i]}, vp.clip_rect_window_offset_enable[i]);
|
||||
}
|
||||
} else {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
@@ -1032,15 +855,10 @@ void hw_check(const CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto rt_slot = render_target_first_bound_slot(buffer);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
const auto& bc = hw.GetBlendControl(rt_slot);
|
||||
const auto& bclr = hw.GetBlendColor();
|
||||
const auto& vp = hw.GetScreenViewport();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& c = hw.GetClipControl();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& d = hw.GetDepthControl();
|
||||
const auto& s = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const auto& mc = hw.GetModeControl();
|
||||
const auto& eqaa = hw.GetEqaaControl();
|
||||
const auto& cc = hw.GetColorControl();
|
||||
@@ -1061,18 +879,15 @@ void hw_check(const CommandBuffer& buffer) {
|
||||
RtCheck(rt);
|
||||
log_phase("vp");
|
||||
VpCheck(vp, smc);
|
||||
log_phase("z");
|
||||
ZCheck(z, d, rc);
|
||||
log_phase("clip");
|
||||
ClipCheck(c);
|
||||
log_phase("rc");
|
||||
RcCheck(rc);
|
||||
log_phase("depth");
|
||||
DCheck(d, s, sm);
|
||||
log_phase("mode");
|
||||
McCheck(mc);
|
||||
log_phase("blend");
|
||||
BcCheck(bc, bclr, cc);
|
||||
BcCheck(bclr, cc);
|
||||
log_phase("eqaa");
|
||||
EqaaCheck(eqaa, ac);
|
||||
log_phase("aa");
|
||||
@@ -1131,7 +946,7 @@ void hw_print(const CommandBuffer& buffer) {
|
||||
hw.GetRenderTargetMask(), hw.GetDepthClearValue(), hw.GetStencilClearValue(),
|
||||
hw.GetLineWidth(), hw.GetPrimitiveResetIndex());
|
||||
|
||||
LOGF("%s", Common::Concat(rt_print("RenderTraget:", rt), "").c_str());
|
||||
LOGF("%s", rt_print("RenderTraget:", rt).c_str());
|
||||
|
||||
ZPrint("DepthRenderTraget:", z);
|
||||
VpPrint("ScreenViewport:", vp, smc);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -13,7 +12,6 @@ class CommandBuffer;
|
||||
|
||||
namespace HW {
|
||||
class Context;
|
||||
class Shader;
|
||||
class UserConfig;
|
||||
struct RenderTarget;
|
||||
struct ScanModeControl;
|
||||
@@ -32,14 +30,13 @@ uint32_t render_target_first_bound_slot(const CommandBuffer& buf
|
||||
bool graphics_debug_dump_enabled();
|
||||
void uc_print(const char* func, const HW::UserConfig& uc);
|
||||
void uc_check(const HW::UserConfig& uc);
|
||||
void sh_print(const char* func, const HW::Shader& uc);
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt);
|
||||
std::string rt_print(const char* func, const HW::RenderTarget& rt);
|
||||
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode);
|
||||
void hw_print(const CommandBuffer& buffer);
|
||||
void hw_check(const CommandBuffer& buffer);
|
||||
void LogDrawPhase(const char* draw_name, const char* phase);
|
||||
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
|
||||
vk::Extent2D extent);
|
||||
vk::Extent2D extent, uint32_t viewport_index);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
@@ -44,8 +46,12 @@ static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t
|
||||
switch (static_cast<Prospero::StencilOp>(value)) {
|
||||
case Prospero::StencilOp::kKeep: return vk::StencilOp::eKeep;
|
||||
case Prospero::StencilOp::kZero: return vk::StencilOp::eZero;
|
||||
case Prospero::StencilOp::kReplaceTest:
|
||||
case Prospero::StencilOp::kReplaceOp: return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kReplaceTest: return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kReplaceOp:
|
||||
if ((op_value & write_mask) == 0) {
|
||||
return vk::StencilOp::eZero;
|
||||
}
|
||||
return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kAddClamp: return vk::StencilOp::eIncrementAndClamp;
|
||||
case Prospero::StencilOp::kSubClamp: return vk::StencilOp::eDecrementAndClamp;
|
||||
case Prospero::StencilOp::kInvert: return vk::StencilOp::eInvert;
|
||||
@@ -65,9 +71,38 @@ static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
constexpr auto replace_op = static_cast<uint8_t>(Prospero::StencilOp::kReplaceOp);
|
||||
return fail == replace_op || pass == replace_op || depth_fail == replace_op;
|
||||
static vk::StencilOpState ConvertStencilState(
|
||||
uint8_t compare, const std::array<uint8_t, 3>& operations, uint8_t op_value,
|
||||
const vk::StencilOpState& state) {
|
||||
const auto test_value = state.reference;
|
||||
auto reference = test_value;
|
||||
auto required_bits = state.compareMask;
|
||||
if (compare == static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
compare == static_cast<uint8_t>(vk::CompareOp::eNever)) {
|
||||
required_bits = 0;
|
||||
}
|
||||
std::array<vk::StencilOp, 3> converted {};
|
||||
for (size_t i = 0; i < operations.size(); i++) {
|
||||
converted[i] = ConvertStencilOp(operations[i], state.writeMask, op_value);
|
||||
if (converted[i] != vk::StencilOp::eReplace) {
|
||||
continue;
|
||||
}
|
||||
auto replacement = test_value;
|
||||
if (static_cast<Prospero::StencilOp>(operations[i]) == Prospero::StencilOp::kReplaceOp) {
|
||||
replacement = op_value;
|
||||
}
|
||||
if (((reference ^ replacement) & required_bits & state.writeMask) != 0) {
|
||||
DepthFatal("unsupported stencil replacement: compare=%u, compare mask=0x%02" PRIx32
|
||||
", write mask=0x%02" PRIx32 ", operation value=0x%02" PRIx8
|
||||
", test value=0x%02" PRIx32,
|
||||
compare, state.compareMask, state.writeMask, op_value, test_value);
|
||||
}
|
||||
// Vulkan shares one reference between comparison and every replacement on this face.
|
||||
reference = (reference & ~state.writeMask) | (replacement & state.writeMask);
|
||||
required_bits |= state.writeMask;
|
||||
}
|
||||
return {converted[0], converted[1], converted[2], static_cast<vk::CompareOp>(compare),
|
||||
state.compareMask, state.writeMask, reference};
|
||||
}
|
||||
|
||||
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const CommandBuffer& buffer,
|
||||
@@ -96,54 +131,20 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
return vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
(void)submit_id;
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active = dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
|
||||
const HW::DepthRenderTarget& z,
|
||||
bool write_buffer = false) {
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const auto depth_address = write_buffer ? z.z_write_base_addr : z.z_read_base_addr;
|
||||
const auto stencil_address =
|
||||
write_buffer ? z.stencil_write_base_addr : z.stencil_read_base_addr;
|
||||
if (!z.z_info.HasValidTextureCompatibility() ||
|
||||
!z.stencil_info.HasValidTextureCompatibility()) {
|
||||
DepthFatal("invalid PS5 depth texture-compatibility encoding");
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const bool has_htile = z.z_info.htile_acceleration;
|
||||
const bool unsupported_shading_rate_encoding =
|
||||
z.shading_rate_encoding > 1 || (z.shading_rate_encoding != 0 && !has_htile);
|
||||
const auto samples = render_sample_count(z.z_info.num_samples);
|
||||
if (samples == 0) {
|
||||
DepthFatal("unsupported depth fragment count: %u", z.z_info.num_samples);
|
||||
@@ -158,23 +159,16 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max);
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || 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 || z.shading_rate_encoding != 0 ||
|
||||
z.z_read_base_addr == 0 ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(z.z_read_base_addr & 0xffffu) != 0 ||
|
||||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
|
||||
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
|
||||
depth_address == 0 || (depth_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
if (has_stencil) {
|
||||
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
|
||||
z.stencil_read_base_addr == 0 ||
|
||||
(!z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr) ||
|
||||
(z.stencil_read_base_addr & 0xffffu) != 0) {
|
||||
stencil_address == 0 || (stencil_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported stencil attachment state");
|
||||
}
|
||||
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
|
||||
@@ -201,10 +195,10 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("unsupported depth/stencil format pair");
|
||||
}
|
||||
const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil);
|
||||
r.format = ResolveHostDepthAttachmentFormat(buffer, *policy, has_stencil, samples);
|
||||
if (r.format == vk::Format::eUndefined) {
|
||||
const auto format = ResolveHostDepthAttachmentFormat(buffer, *policy, has_stencil, samples);
|
||||
if (format == vk::Format::eUndefined) {
|
||||
DepthFatal("no host depth/stencil format supports required usage for %s",
|
||||
VulkanToString(ideal_format).c_str());
|
||||
vk::to_string(ideal_format).c_str());
|
||||
}
|
||||
const auto guest_format = policy->guest_format;
|
||||
const uint32_t bytes = policy->bytes_per_element;
|
||||
@@ -227,27 +221,98 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const auto depth_backing_size = depth_size.size * view.image_layers;
|
||||
const auto stencil_backing_size = stencil_size.size * view.image_layers;
|
||||
const auto htile_backing_size = htile_size.size * view.image_layers;
|
||||
if (!GuestRange {z.z_read_base_addr, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {z.stencil_read_base_addr, stencil_backing_size}.Valid()) ||
|
||||
if (!GuestRange {depth_address, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {stencil_address, stencil_backing_size}.Valid()) ||
|
||||
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
|
||||
DepthFatal("layered depth backing range is invalid");
|
||||
}
|
||||
r.htile = has_htile;
|
||||
r.width = width;
|
||||
r.height = height;
|
||||
r.samples = samples;
|
||||
r.depth_buffer_size = depth_backing_size;
|
||||
r.depth_buffer_vaddr = z.z_read_base_addr;
|
||||
r.stencil_buffer_size = has_stencil ? stencil_backing_size : 0;
|
||||
r.stencil_buffer_vaddr = has_stencil ? z.stencil_read_base_addr : 0;
|
||||
r.htile_buffer_size = has_htile ? htile_backing_size : 0;
|
||||
r.htile_buffer_vaddr = has_htile ? z.htile_data_base_addr : 0;
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.type = TextureCache::BindingType::DepthTarget;
|
||||
desc.info.data = {depth_address, depth_backing_size};
|
||||
desc.info.stencil =
|
||||
has_stencil ? GuestRange {stencil_address, stencil_backing_size} : GuestRange {};
|
||||
desc.info.pixel_format = format;
|
||||
desc.info.guest_format = guest_format;
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {width, height, 1};
|
||||
desc.info.resources = {1, view.image_layers};
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
|
||||
desc.info.metadata.range =
|
||||
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
desc.info.metadata.stencil_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
||||
desc.view_info.format = format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
|
||||
desc.view_info.base_level = 0;
|
||||
desc.view_info.level_count = 1;
|
||||
desc.view_info.base_layer = view.base_layer;
|
||||
desc.view_info.layer_count = view.layer_count;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
return desc;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(has_stencil && !z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
r.desc = MakeDepthTargetDesc(buffer, z);
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_meta_clear_enable = false;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
r.depth_clear_value = hw.GetDepthClearValue();
|
||||
r.depth_test_enable = dc.z_enable;
|
||||
r.depth_write_enable = dc.z_write_enable && !z.depth_view.depth_write_disable;
|
||||
r.depth_write_enable = r.depth_test_enable && dc.z_write_enable &&
|
||||
!z.depth_view.depth_write_disable && !r.depth_clear_enable;
|
||||
r.depth_compare_op = static_cast<vk::CompareOp>(dc.zfunc);
|
||||
|
||||
r.depth_bounds_test_enable = dc.depth_bounds_enable;
|
||||
@@ -265,75 +330,91 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const uint8_t back_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask_bf;
|
||||
if (dc.stencilfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(dc.backface_enable &&
|
||||
dc.stencilfunc_bf > static_cast<uint8_t>(vk::CompareOp::eAlways)) ||
|
||||
(front_write_mask != 0 &&
|
||||
UsesStencilOpValue(sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail) &&
|
||||
sm.stencil_opval != sm.stencil_testval) ||
|
||||
(dc.backface_enable && back_write_mask != 0 &&
|
||||
UsesStencilOpValue(sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf) &&
|
||||
sm.stencil_opval_bf != sm.stencil_testval_bf)) {
|
||||
DepthFatal("unsupported stencil compare or replacement state");
|
||||
dc.stencilfunc_bf > static_cast<uint8_t>(vk::CompareOp::eAlways))) {
|
||||
DepthFatal("unsupported stencil compare state");
|
||||
}
|
||||
r.stencil_static_front = {
|
||||
ConvertStencilOp(sc.stencil_fail, front_write_mask, sm.stencil_opval),
|
||||
ConvertStencilOp(sc.stencil_zpass, front_write_mask, sm.stencil_opval),
|
||||
ConvertStencilOp(sc.stencil_zfail, front_write_mask, sm.stencil_opval),
|
||||
static_cast<vk::CompareOp>(dc.stencilfunc)};
|
||||
r.stencil_dynamic_front = {sm.stencil_mask, front_write_mask, sm.stencil_testval};
|
||||
r.stencil_front = ConvertStencilState(
|
||||
dc.stencilfunc, {sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail},
|
||||
sm.stencil_opval, {.compareMask = sm.stencil_mask, .writeMask = front_write_mask,
|
||||
.reference = sm.stencil_testval});
|
||||
if (dc.backface_enable) {
|
||||
r.stencil_static_back = {
|
||||
ConvertStencilOp(sc.stencil_fail_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
ConvertStencilOp(sc.stencil_zpass_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
ConvertStencilOp(sc.stencil_zfail_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
static_cast<vk::CompareOp>(dc.stencilfunc_bf)};
|
||||
r.stencil_dynamic_back = {sm.stencil_mask_bf, back_write_mask, sm.stencil_testval_bf};
|
||||
r.stencil_back = ConvertStencilState(
|
||||
dc.stencilfunc_bf, {sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf},
|
||||
sm.stencil_opval_bf, {.compareMask = sm.stencil_mask_bf, .writeMask = back_write_mask,
|
||||
.reference = sm.stencil_testval_bf});
|
||||
} else {
|
||||
r.stencil_static_back = r.stencil_static_front;
|
||||
r.stencil_dynamic_back = r.stencil_dynamic_front;
|
||||
r.stencil_back = r.stencil_front;
|
||||
}
|
||||
}
|
||||
r.vaddr_num = has_stencil ? 2 : 1;
|
||||
r.vaddr[0] = r.depth_buffer_vaddr;
|
||||
r.size[0] = r.depth_buffer_size;
|
||||
if (has_stencil) {
|
||||
r.vaddr[1] = r.stencil_buffer_vaddr;
|
||||
r.size[1] = r.stencil_buffer_size;
|
||||
}
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.type = TextureCache::BindingType::DepthTarget;
|
||||
desc.info.data = {r.depth_buffer_vaddr, r.depth_buffer_size};
|
||||
desc.info.stencil = {r.stencil_buffer_vaddr, r.stencil_buffer_size};
|
||||
desc.info.pixel_format = r.format;
|
||||
desc.info.guest_format = guest_format;
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {width, height, 1};
|
||||
desc.info.resources = {1, view.image_layers};
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, r.depth_buffer_size, pitch, height};
|
||||
desc.info.metadata.range = {r.htile_buffer_vaddr, r.htile_buffer_size};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
desc.info.metadata.stencil_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
||||
desc.view_info.format = r.format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(r.format);
|
||||
desc.view_info.base_level = 0;
|
||||
desc.view_info.level_count = 1;
|
||||
desc.view_info.base_layer = view.base_layer;
|
||||
desc.view_info.layer_count = view.layer_count;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
r.desc = std::move(desc);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
r.image_view = nullptr;
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
BindRenderTarget(r.image_id);
|
||||
}
|
||||
|
||||
bool RenderExecutor::DepthStencilCopy(CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& override = hw.GetDepthRenderOverride();
|
||||
if (hw.GetColorControl().mode != 0) {
|
||||
return false;
|
||||
}
|
||||
const bool depth_copy = override.force_z_dirty && override.force_z_valid &&
|
||||
z.z_info.format != Prospero::DepthFormat::kInvalid &&
|
||||
z.z_read_base_addr != 0 && z.z_write_base_addr != 0 &&
|
||||
z.z_read_base_addr != z.z_write_base_addr;
|
||||
const bool stencil_copy = override.force_stencil_dirty && override.force_stencil_valid &&
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
|
||||
z.stencil_read_base_addr != 0 && z.stencil_write_base_addr != 0 &&
|
||||
z.stencil_read_base_addr != z.stencil_write_base_addr;
|
||||
if (!depth_copy && !stencil_copy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read_desc = MakeDepthTargetDesc(buffer, z);
|
||||
auto write_desc = MakeDepthTargetDesc(buffer, z, true);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
const auto read_id = cache.FindImage(read_desc);
|
||||
BindRenderTarget(read_id);
|
||||
const auto write_id = cache.FindImage(write_desc);
|
||||
BindRenderTarget(write_id);
|
||||
cache.UpdateImage(read_id);
|
||||
cache.UpdateImage(write_id);
|
||||
cache.MarkGpuWritten(write_id);
|
||||
auto& source = cache.GetImage(read_id);
|
||||
auto& destination = cache.GetImage(write_id);
|
||||
EXIT_IF(read_id == write_id || source.backing.format != destination.backing.format);
|
||||
|
||||
auto& scheduler = m_context.GetCommandScheduler();
|
||||
scheduler.EndRendering();
|
||||
const auto command = scheduler.Current().Handle();
|
||||
const ImageSubresourceRange range {read_desc.view_info.base_level, 1,
|
||||
read_desc.view_info.base_layer,
|
||||
read_desc.view_info.layer_count};
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
range, command);
|
||||
destination.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||
range, command);
|
||||
std::array<vk::ImageCopy, 2> regions {};
|
||||
uint32_t count = 0;
|
||||
for (const auto aspect: {vk::ImageAspectFlagBits::eDepth, vk::ImageAspectFlagBits::eStencil}) {
|
||||
if ((aspect == vk::ImageAspectFlagBits::eDepth && !depth_copy) ||
|
||||
(aspect == vk::ImageAspectFlagBits::eStencil && !stencil_copy)) {
|
||||
continue;
|
||||
}
|
||||
auto& region = regions[count++];
|
||||
region.srcSubresource = {aspect, range.base_level, range.base_layer,
|
||||
range.layer_count};
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.extent = write_desc.info.extent;
|
||||
}
|
||||
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
count, regions.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
const auto format = desc.view_info.format;
|
||||
if (format == vk::Format::eUndefined) {
|
||||
return {};
|
||||
}
|
||||
@@ -341,21 +422,20 @@ vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
const auto available = ImageViewOps::DepthAspectMask(format);
|
||||
vk::ImageAspectFlags writes {};
|
||||
if ((available & vk::ImageAspectFlagBits::eDepth) &&
|
||||
(depth_load_clear_enable || (depth_test_enable && depth_write_enable))) {
|
||||
(depth_load_clear_enable || depth_write_enable)) {
|
||||
writes |= vk::ImageAspectFlagBits::eDepth;
|
||||
}
|
||||
if (!(available & vk::ImageAspectFlagBits::eStencil)) {
|
||||
return writes;
|
||||
}
|
||||
|
||||
const auto face_writes = [&](const PipelineStencilStaticState& state,
|
||||
const PipelineStencilDynamicState& dynamic) {
|
||||
if (dynamic.writeMask == 0) {
|
||||
const auto face_writes = [&](const vk::StencilOpState& state) {
|
||||
if (state.writeMask == 0) {
|
||||
return false;
|
||||
}
|
||||
bool can_pass = state.compareOp != vk::CompareOp::eNever;
|
||||
bool can_fail = state.compareOp != vk::CompareOp::eAlways;
|
||||
if (dynamic.compareMask == 0) {
|
||||
if (state.compareMask == 0) {
|
||||
switch (state.compareOp) {
|
||||
case vk::CompareOp::eEqual:
|
||||
case vk::CompareOp::eLessOrEqual:
|
||||
@@ -381,8 +461,7 @@ vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
(can_pass && depth_fail && state.depthFailOp != vk::StencilOp::eKeep);
|
||||
};
|
||||
if (stencil_clear_enable ||
|
||||
(stencil_test_enable && (face_writes(stencil_static_front, stencil_dynamic_front) ||
|
||||
face_writes(stencil_static_back, stencil_dynamic_back)))) {
|
||||
(stencil_test_enable && (face_writes(stencil_front) || face_writes(stencil_back)))) {
|
||||
writes |= vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
return writes;
|
||||
|
||||
@@ -19,26 +19,13 @@ inline constexpr bool depth_htile_stencil_acceleration_compatible(bool has_stenc
|
||||
}
|
||||
|
||||
struct RenderDepthInfo {
|
||||
// Discovery keeps guest image information but can remap the view into a larger cache image.
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t samples = 1;
|
||||
bool htile = false;
|
||||
uint64_t depth_buffer_size = 0;
|
||||
uint64_t depth_buffer_vaddr = 0;
|
||||
uint64_t depth_tile_swizzle = 0;
|
||||
uint64_t stencil_buffer_size = 0;
|
||||
uint64_t stencil_buffer_vaddr = 0;
|
||||
uint64_t stencil_tile_swizzle = 0;
|
||||
uint64_t htile_buffer_size = 0;
|
||||
uint64_t htile_buffer_vaddr = 0;
|
||||
uint64_t htile_tile_swizzle = 0;
|
||||
bool depth_clear_enable = false;
|
||||
bool depth_load_clear_enable = false;
|
||||
bool depth_meta_clear_enable = false;
|
||||
float depth_clear_value = 0.0f;
|
||||
bool depth_test_enable = false;
|
||||
// Effective draw writes; discovery applies test, target-write and clear controls.
|
||||
bool depth_write_enable = false;
|
||||
vk::CompareOp depth_compare_op = vk::CompareOp::eNever;
|
||||
bool depth_bounds_test_enable = false;
|
||||
@@ -47,29 +34,20 @@ struct RenderDepthInfo {
|
||||
bool stencil_clear_enable = false;
|
||||
uint8_t stencil_clear_value = 0;
|
||||
bool stencil_test_enable = false;
|
||||
PipelineStencilStaticState stencil_static_front;
|
||||
PipelineStencilStaticState stencil_static_back;
|
||||
PipelineStencilDynamicState stencil_dynamic_front;
|
||||
PipelineStencilDynamicState stencil_dynamic_back;
|
||||
vk::StencilOpState stencil_front;
|
||||
vk::StencilOpState stencil_back;
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
uint64_t vaddr[3] = {};
|
||||
uint64_t size[3] = {};
|
||||
int vaddr_num = 0;
|
||||
|
||||
[[nodiscard]] vk::ImageAspectFlags AttachmentWriteAspects() const;
|
||||
};
|
||||
|
||||
inline bool depth_attachment_read_only(const RenderDepthInfo& depth) {
|
||||
return !depth.AttachmentWriteAspects();
|
||||
}
|
||||
|
||||
inline vk::ImageLayout depth_attachment_layout(const RenderDepthInfo& depth) {
|
||||
const auto available = ImageViewOps::DepthAspectMask(depth.format);
|
||||
const auto available = ImageViewOps::DepthAspectMask(depth.desc.view_info.format);
|
||||
const auto writes = depth.AttachmentWriteAspects();
|
||||
const bool has_depth = static_cast<bool>(available & vk::ImageAspectFlagBits::eDepth);
|
||||
const bool has_stencil = static_cast<bool>(available & vk::ImageAspectFlagBits::eStencil);
|
||||
const bool depth_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eDepth);
|
||||
// LoadOp clears are separate from guest depth writes.
|
||||
const bool depth_write = depth.depth_write_enable;
|
||||
const bool stencil_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eStencil);
|
||||
if (!has_stencil) {
|
||||
return depth_write ? vk::ImageLayout::eDepthAttachmentOptimal
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -23,7 +22,6 @@ BlitHelper::BlitHelper(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
texture_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_info {};
|
||||
descriptor_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
descriptor_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
descriptor_info.bindingCount = 1;
|
||||
descriptor_info.pBindings = &texture_binding;
|
||||
@@ -32,16 +30,14 @@ BlitHelper::BlitHelper(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
"create BlitHelper descriptor layout");
|
||||
|
||||
vk::PipelineLayoutCreateInfo layout_info {};
|
||||
layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
layout_info.setLayoutCount = 1;
|
||||
layout_info.pSetLayouts = &m_descriptor_layout;
|
||||
RequireVulkanSuccess(
|
||||
m_graphics.device.createPipelineLayout(&layout_info, nullptr, &m_pipeline_layout),
|
||||
"create BlitHelper pipeline layout");
|
||||
|
||||
m_vertex_shader = CreateShader(GPU_BLIT_FS_TRIANGLE_SPV, std::size(GPU_BLIT_FS_TRIANGLE_SPV));
|
||||
m_fragment_shader =
|
||||
CreateShader(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV, std::size(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV));
|
||||
m_vertex_shader = CompileSPV(GPU_BLIT_FS_TRIANGLE_SPV, m_graphics.device);
|
||||
m_fragment_shader = CompileSPV(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV, m_graphics.device);
|
||||
}
|
||||
|
||||
BlitHelper::~BlitHelper() {
|
||||
@@ -62,18 +58,6 @@ BlitHelper::~BlitHelper() {
|
||||
}
|
||||
}
|
||||
|
||||
vk::ShaderModule BlitHelper::CreateShader(const uint32_t* code, size_t words) const {
|
||||
EXIT_IF(code == nullptr || words == 0);
|
||||
vk::ShaderModuleCreateInfo create {};
|
||||
create.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
create.codeSize = words * sizeof(uint32_t);
|
||||
create.pCode = code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&create, nullptr, &module),
|
||||
"create BlitHelper shader module");
|
||||
return module;
|
||||
}
|
||||
|
||||
vk::Pipeline BlitHelper::GetPipeline(PipelineKey key) {
|
||||
const auto cached = std::ranges::find(m_pipelines, key, &Pipeline::key);
|
||||
if (cached != m_pipelines.end()) {
|
||||
@@ -84,51 +68,37 @@ vk::Pipeline BlitHelper::GetPipeline(PipelineKey key) {
|
||||
EXIT_IF(samples == vk::SampleCountFlagBits {} || key.format == vk::Format::eUndefined);
|
||||
|
||||
std::array<vk::PipelineShaderStageCreateInfo, 2> stages {};
|
||||
stages[0].sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stages[0].stage = vk::ShaderStageFlagBits::eVertex;
|
||||
stages[0].module = m_vertex_shader;
|
||||
stages[0].pName = "main";
|
||||
stages[1].sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stages[1].stage = vk::ShaderStageFlagBits::eFragment;
|
||||
stages[1].module = m_fragment_shader;
|
||||
stages[1].pName = "main";
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertex_input {};
|
||||
vertex_input.sType = vk::StructureType::ePipelineVertexInputStateCreateInfo;
|
||||
vk::PipelineInputAssemblyStateCreateInfo input_assembly {};
|
||||
input_assembly.sType = vk::StructureType::ePipelineInputAssemblyStateCreateInfo;
|
||||
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
|
||||
vk::PipelineViewportStateCreateInfo viewport {};
|
||||
viewport.sType = vk::StructureType::ePipelineViewportStateCreateInfo;
|
||||
viewport.viewportCount = 1;
|
||||
viewport.scissorCount = 1;
|
||||
vk::PipelineRasterizationStateCreateInfo rasterization {};
|
||||
rasterization.sType = vk::StructureType::ePipelineRasterizationStateCreateInfo;
|
||||
rasterization.polygonMode = vk::PolygonMode::eFill;
|
||||
rasterization.cullMode = vk::CullModeFlagBits::eNone;
|
||||
rasterization.lineWidth = 1.0f;
|
||||
rasterization.lineWidth = 1.0f;
|
||||
vk::PipelineMultisampleStateCreateInfo multisample {};
|
||||
multisample.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
|
||||
multisample.rasterizationSamples = samples;
|
||||
vk::PipelineDepthStencilStateCreateInfo depth {};
|
||||
depth.sType = vk::StructureType::ePipelineDepthStencilStateCreateInfo;
|
||||
depth.depthTestEnable = VK_TRUE;
|
||||
depth.depthWriteEnable = VK_TRUE;
|
||||
depth.depthCompareOp = vk::CompareOp::eAlways;
|
||||
vk::PipelineColorBlendStateCreateInfo color_blend {};
|
||||
color_blend.sType = vk::StructureType::ePipelineColorBlendStateCreateInfo;
|
||||
const std::array dynamic_states {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
|
||||
vk::PipelineDynamicStateCreateInfo dynamic {};
|
||||
dynamic.sType = vk::StructureType::ePipelineDynamicStateCreateInfo;
|
||||
dynamic.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
|
||||
dynamic.pDynamicStates = dynamic_states.data();
|
||||
|
||||
vk::PipelineRenderingCreateInfo rendering {};
|
||||
rendering.sType = vk::StructureType::ePipelineRenderingCreateInfo;
|
||||
rendering.depthAttachmentFormat = key.format;
|
||||
|
||||
vk::GraphicsPipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eGraphicsPipelineCreateInfo;
|
||||
create.pNext = &rendering;
|
||||
create.stageCount = static_cast<uint32_t>(stages.size());
|
||||
create.pStages = stages.data();
|
||||
@@ -186,7 +156,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
command);
|
||||
|
||||
vk::RenderingAttachmentInfo depth_attachment {};
|
||||
depth_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
depth_attachment.imageView = destination_view;
|
||||
depth_attachment.imageLayout = ColorToMsDepthLayout;
|
||||
depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
|
||||
@@ -194,7 +163,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
depth_attachment.clearValue.depthStencil = {0.0f, 0};
|
||||
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = {destination_info.extent.width, destination_info.extent.height};
|
||||
rendering.layerCount = 1;
|
||||
rendering.pDepthAttachment = &depth_attachment;
|
||||
@@ -204,7 +172,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
descriptor_image.imageView = source_view;
|
||||
descriptor_image.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
vk::WriteDescriptorSet descriptor_write {};
|
||||
descriptor_write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
descriptor_write.dstBinding = 0;
|
||||
descriptor_write.descriptorCount = 1;
|
||||
descriptor_write.descriptorType = vk::DescriptorType::eSampledImage;
|
||||
|
||||
@@ -37,8 +37,7 @@ private:
|
||||
vk::Pipeline handle = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] vk::ShaderModule CreateShader(const uint32_t* code, size_t words) const;
|
||||
[[nodiscard]] vk::Pipeline GetPipeline(PipelineKey key);
|
||||
[[nodiscard]] vk::Pipeline GetPipeline(PipelineKey key);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
|
||||
@@ -26,12 +26,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const ImageInfo& info) {
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const GraphicContext& graphics,
|
||||
const ImageInfo& info) {
|
||||
vk::ImageCreateFlags flags {};
|
||||
if (DepthAspectTransferFormat(info.pixel_format) == vk::Format::eUndefined) {
|
||||
flags |= vk::ImageCreateFlagBits::eMutableFormat;
|
||||
flags |= vk::ImageCreateFlagBits::eExtendedUsage;
|
||||
if (Prospero::BlockCompressedBytesPerBlock(info.guest_format) != 0) {
|
||||
if (info.IsBlock() && graphics.supports_block_texel_view) {
|
||||
flags |= vk::ImageCreateFlagBits::eBlockTexelViewCompatible;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +48,10 @@ namespace {
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageUsageFlags ImageUsageFlags(GraphicContext& graphics, const ImageInfo& info) {
|
||||
if (info.IsBlock()) {
|
||||
return vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eSampled;
|
||||
}
|
||||
const auto properties = graphics.GetFormatProperties(info.pixel_format);
|
||||
auto usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eSampledImage)) {
|
||||
@@ -54,6 +59,9 @@ namespace {
|
||||
}
|
||||
if (DepthAspectTransferFormat(info.pixel_format) != vk::Format::eUndefined) {
|
||||
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
if (graphics.attachment_feedback_loop_enabled && (usage & vk::ImageUsageFlagBits::eSampled)) {
|
||||
usage |= vk::ImageUsageFlagBits::eAttachmentFeedbackLoopEXT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
|
||||
@@ -202,9 +210,7 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
|
||||
if (barriers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (m_scheduler != nullptr) {
|
||||
m_scheduler->EndRendering();
|
||||
}
|
||||
m_scheduler.EndRendering();
|
||||
vk::DependencyInfo dependency {};
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(barriers.size());
|
||||
dependency.pImageMemoryBarriers = barriers.data();
|
||||
@@ -213,8 +219,8 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
|
||||
|
||||
void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
|
||||
uint64_t size) {
|
||||
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler.EndRendering();
|
||||
vk::BufferMemoryBarrier2 buffer_barrier {};
|
||||
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
buffer_barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite;
|
||||
@@ -234,7 +240,7 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
|
||||
dependency.pBufferMemoryBarriers = &buffer_barrier;
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
|
||||
dependency.pImageMemoryBarriers = image_barriers.data();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
command.pipelineBarrier2(dependency);
|
||||
command.copyBufferToImage(buffer, backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
static_cast<uint32_t>(copies.size()), copies.data());
|
||||
@@ -252,8 +258,8 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
|
||||
|
||||
void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
|
||||
uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler.EndRendering();
|
||||
vk::BufferMemoryBarrier2 buffer_barrier {};
|
||||
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
buffer_barrier.srcAccessMask =
|
||||
@@ -274,7 +280,7 @@ void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buf
|
||||
dependency.pBufferMemoryBarriers = &buffer_barrier;
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
|
||||
dependency.pImageMemoryBarriers = image_barriers.data();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
command.pipelineBarrier2(dependency);
|
||||
command.copyImageToBuffer(backing.image, vk::ImageLayout::eTransferSrcOptimal, buffer,
|
||||
static_cast<uint32_t>(copies.size()), copies.data());
|
||||
@@ -311,8 +317,8 @@ std::pair<uint32_t, uint32_t> Image::SanitizeCopyLayers(const Image& source,
|
||||
}
|
||||
|
||||
void Image::CopyImage(Image& source) {
|
||||
EXIT_IF(m_scheduler == nullptr || source.backing.samples != backing.samples);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(source.backing.samples != backing.samples);
|
||||
m_scheduler.EndRendering();
|
||||
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
|
||||
const uint32_t base_depth = backing.image_type == vk::ImageType::e3D
|
||||
? backing.extent.depth
|
||||
@@ -351,7 +357,7 @@ void Image::CopyImage(Image& source) {
|
||||
if (copies.empty()) {
|
||||
return;
|
||||
}
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
@@ -364,8 +370,7 @@ void Image::CopyImage(Image& source) {
|
||||
|
||||
void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
|
||||
const ImageSubresourceRange& destination_range) {
|
||||
EXIT_IF(m_scheduler == nullptr || backing.samples != 1 ||
|
||||
source.backing.image_type != vk::ImageType::e2D ||
|
||||
EXIT_IF(backing.samples != 1 || source.backing.image_type != vk::ImageType::e2D ||
|
||||
backing.image_type != vk::ImageType::e2D || source_range.level_count != 1 ||
|
||||
destination_range.level_count != 1 ||
|
||||
source_range.base_level >= source.backing.mip_levels ||
|
||||
@@ -393,8 +398,8 @@ void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
|
||||
resolved_destination_range.layer_count = layers;
|
||||
const vk::Extent3D resolve_extent {info.extent.width, info.extent.height, 1};
|
||||
|
||||
m_scheduler->EndRendering();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
resolved_source_range, command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||
@@ -430,9 +435,8 @@ uint32_t Image::CopyRows(uint64_t row_size, uint32_t rows, uint64_t capacity) no
|
||||
}
|
||||
|
||||
void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
EXIT_IF(m_scheduler == nullptr || buffer.Handle() == nullptr || source.backing.samples != 1 ||
|
||||
backing.samples != 1);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(buffer.Handle() == nullptr || source.backing.samples != 1 || backing.samples != 1);
|
||||
m_scheduler.EndRendering();
|
||||
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
|
||||
const auto source_aspect =
|
||||
FullAspectMask(source.backing.format) & ~vk::ImageAspectFlagBits::eStencil;
|
||||
@@ -462,7 +466,7 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
||||
dependency.bufferMemoryBarrierCount = 1;
|
||||
dependency.pBufferMemoryBarriers = &barrier;
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
@@ -522,9 +526,9 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
}
|
||||
|
||||
void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
EXIT_IF(m_scheduler == nullptr || source.backing.samples != backing.samples ||
|
||||
mip >= backing.mip_levels || layer >= backing.layers);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(source.backing.samples != backing.samples || mip >= backing.mip_levels ||
|
||||
layer >= backing.layers);
|
||||
m_scheduler.EndRendering();
|
||||
const auto width = std::max(backing.extent.width >> mip, 1u);
|
||||
const auto height = std::max(backing.extent.height >> mip, 1u);
|
||||
const auto depth = std::max(backing.extent.depth >> mip, 1u);
|
||||
@@ -544,7 +548,7 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
copy.dstSubresource = {aspect, mip, layer, destination_layers};
|
||||
copy.extent = {width, height, depth};
|
||||
}
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
@@ -648,7 +652,7 @@ Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
} // namespace ImageOps
|
||||
|
||||
Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageInfo& image_info)
|
||||
: info(image_info), m_graphics(&graphics), m_scheduler(&scheduler) {
|
||||
: info(image_info), m_graphics(graphics), m_scheduler(scheduler) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
ImageOps::Validate(info);
|
||||
m_cpu_dirty =
|
||||
@@ -657,29 +661,17 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
return;
|
||||
}
|
||||
|
||||
backing.format = info.pixel_format;
|
||||
backing.image_type = HostImageType(info.type);
|
||||
backing.extent = info.extent;
|
||||
backing.guest_pitch = info.pitch;
|
||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
backing.mip_levels = info.resources.levels;
|
||||
backing.samples = info.samples;
|
||||
backing.flags = ImageCreateFlags(info);
|
||||
backing.usage = ImageUsageFlags(graphics, info);
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageCreateInfo;
|
||||
create.flags = backing.flags;
|
||||
create.imageType = backing.image_type;
|
||||
create.extent = backing.extent;
|
||||
create.mipLevels = backing.mip_levels;
|
||||
create.arrayLayers = backing.layers;
|
||||
create.format = backing.format;
|
||||
create.flags = ImageCreateFlags(graphics, info);
|
||||
create.imageType = HostImageType(info.type);
|
||||
create.extent = info.extent;
|
||||
create.mipLevels = info.resources.levels;
|
||||
create.arrayLayers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
create.format = info.pixel_format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = backing.state.layout;
|
||||
create.usage = backing.usage;
|
||||
create.sharingMode = vk::SharingMode::eExclusive;
|
||||
create.samples = vulkan_sample_count(backing.samples);
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = ImageUsageFlags(graphics, info);
|
||||
create.samples = vulkan_sample_count(info.samples);
|
||||
|
||||
vk::ImageFormatProperties properties {};
|
||||
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
||||
@@ -690,10 +682,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
"flags=0x%x samples=%u\n",
|
||||
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
||||
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), backing.samples);
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), info.samples);
|
||||
}
|
||||
|
||||
backing.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
if (!graphics.CreateImage(create, backing)) {
|
||||
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
||||
create.extent.width, create.extent.height, create.extent.depth,
|
||||
@@ -702,11 +693,12 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
}
|
||||
|
||||
uint64_t Image::HashGuestEdges() const {
|
||||
constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1;
|
||||
std::array<uint8_t, TRACKER_PAGE_SIZE * 2> bytes {};
|
||||
const auto range = info.data;
|
||||
const uint64_t head_end = std::min(range.End(), (range.address + page_mask) & ~page_mask);
|
||||
const uint64_t tail_begin = std::max(range.address, range.End() & ~page_mask);
|
||||
const uint64_t head_end =
|
||||
std::min(range.End(), Common::AlignUp(range.address, TRACKER_PAGE_SIZE));
|
||||
const uint64_t tail_begin =
|
||||
std::max(range.address, Common::AlignDown(range.End(), TRACKER_PAGE_SIZE));
|
||||
const uint64_t head_size = head_end - range.address;
|
||||
const uint64_t tail_address = tail_begin < head_end ? head_end : tail_begin;
|
||||
const uint64_t tail_size = range.End() - tail_address;
|
||||
@@ -721,16 +713,13 @@ uint64_t Image::HashGuestEdges() const {
|
||||
|
||||
Image::~Image() {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
if (m_graphics == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (const auto& cached: views) {
|
||||
if (cached.view != nullptr) {
|
||||
m_graphics->device.destroyImageView(cached.view, nullptr);
|
||||
m_graphics.device.destroyImageView(cached.view, nullptr);
|
||||
}
|
||||
}
|
||||
if (backing.image != nullptr) {
|
||||
m_graphics->DeleteImage(backing);
|
||||
m_graphics.DeleteImage(backing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/slotVector.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
@@ -35,11 +36,13 @@ struct ImageUsage {
|
||||
};
|
||||
|
||||
struct ImageBinding {
|
||||
bool is_bound = false;
|
||||
bool is_target = false;
|
||||
bool needs_rebind = false;
|
||||
bool force_general = false;
|
||||
bool shader_write = false;
|
||||
vk::ImageLayout attachment_layout = vk::ImageLayout::eUndefined;
|
||||
vk::AccessFlags2 attachment_access;
|
||||
bool is_bound = false;
|
||||
bool is_target = false;
|
||||
bool needs_rebind = false;
|
||||
bool force_general = false;
|
||||
bool shader_write = false;
|
||||
};
|
||||
|
||||
class Image final {
|
||||
@@ -49,7 +52,6 @@ public:
|
||||
KYTY_CLASS_NO_COPY(Image);
|
||||
|
||||
[[nodiscard]] vk::ImageView FindView(const ImageViewInfo& view_info);
|
||||
void AssociateDepth(ImageId image_id) { depth_id = image_id; }
|
||||
using Barriers = std::vector<vk::ImageMemoryBarrier2>;
|
||||
[[nodiscard]] Barriers GetBarriers(vk::ImageLayout destination_layout,
|
||||
vk::AccessFlags2 destination_access,
|
||||
@@ -127,15 +129,12 @@ public:
|
||||
return pages ? ImagePageRangesOverlap(info.data.address, info.data.size, address, size)
|
||||
: ImageRangeOverlaps(info.data.address, info.data.size, address, size);
|
||||
}
|
||||
[[nodiscard]] bool GpuOverlaps(uint64_t address, uint64_t size) const noexcept {
|
||||
return IsGpuModified() && Overlaps(address, size);
|
||||
}
|
||||
[[nodiscard]] bool SafeToDownload() const noexcept {
|
||||
return IsGpuModified() && !IsBufferModified() && !IsCpuDirty();
|
||||
}
|
||||
[[nodiscard]] bool IsTracked() const noexcept { return track_addr != 0 && track_addr_end != 0; }
|
||||
[[nodiscard]] uint64_t AccountedSize() const noexcept {
|
||||
return backing.image == nullptr ? 0 : (info.data.size + 1023) & ~uint64_t {1023};
|
||||
return backing.image == nullptr ? 0 : Common::AlignUp(info.data.size, 1024);
|
||||
}
|
||||
[[nodiscard]] uint64_t HashGuestEdges() const;
|
||||
|
||||
@@ -161,8 +160,8 @@ private:
|
||||
[[nodiscard]] static std::pair<uint32_t, uint32_t>
|
||||
SanitizeCopyLayers(const Image& source, const Image& destination, uint32_t depth);
|
||||
|
||||
GraphicContext* m_graphics = nullptr;
|
||||
CommandScheduler* m_scheduler = nullptr;
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
uint64_t m_maybe_cpu_hash = 0;
|
||||
bool m_cpu_dirty = false;
|
||||
bool m_maybe_cpu_dirty = false;
|
||||
|
||||
@@ -23,8 +23,11 @@ struct ImageMetadataInfo {
|
||||
GuestRange range;
|
||||
ImageMetadataKind kind = ImageMetadataKind::None;
|
||||
uint32_t control = 0;
|
||||
uint32_t dcc_clear_word = 0;
|
||||
VideoOutCompression compression = VideoOutCompression::Uncompressed;
|
||||
bool stencil_compressed = false;
|
||||
bool dcc_clear_register_valid = false;
|
||||
bool dcc_alpha_msb = true;
|
||||
};
|
||||
|
||||
struct ImageSubresources {
|
||||
@@ -44,6 +47,7 @@ struct ImageSubresourceRange {
|
||||
struct ImageMipInfo {
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = 0;
|
||||
// Padded dimensions in storage elements (compressed blocks for BC formats).
|
||||
uint32_t pitch = 0;
|
||||
uint32_t height = 0;
|
||||
auto operator<=>(const ImageMipInfo&) const = default;
|
||||
@@ -78,9 +82,6 @@ struct ImageInfo {
|
||||
[[nodiscard]] constexpr bool IsVolume() const noexcept {
|
||||
return type == Prospero::ImageType::kColor3D;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsLayered() const noexcept {
|
||||
return !IsVolume() && resources.layers > 1;
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t TransferLayers() const noexcept {
|
||||
return IsVolume() ? extent.depth : resources.layers;
|
||||
}
|
||||
@@ -93,9 +94,24 @@ struct ImageInfo {
|
||||
bytes_per_block == other.bytes_per_block;
|
||||
}
|
||||
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || resources.levels != 1 ||
|
||||
container.resources.layers == 0 ||
|
||||
container.resources.levels > container.mip_layout.size()) {
|
||||
if (container.resources.levels > container.mip_layout.size()) {
|
||||
return -1;
|
||||
}
|
||||
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
||||
if (SliceOf(container, static_cast<int32_t>(level)) >= 0) {
|
||||
return static_cast<int32_t>(level);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || type != container.type ||
|
||||
IsVolume() || resources.levels != 1 || resources.layers == 0 ||
|
||||
container.resources.layers == 0 || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
!data.Valid() || !container.data.Valid() || data.address < container.data.address ||
|
||||
data.End() > container.data.End()) {
|
||||
return -1;
|
||||
}
|
||||
if (HasStencil() != container.HasStencil() ||
|
||||
@@ -104,75 +120,36 @@ struct ImageInfo {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t mip = -1;
|
||||
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
||||
container.data.address > UINT64_MAX - layout.offset) {
|
||||
continue;
|
||||
}
|
||||
const auto mip_base = container.data.address + layout.offset;
|
||||
const auto slice_size = layout.size / container.resources.layers;
|
||||
if (slice_size == 0 || mip_base > UINT64_MAX - layout.size) {
|
||||
continue;
|
||||
}
|
||||
const auto mip_end = mip_base + layout.size;
|
||||
if (data.address >= mip_base && data.address < mip_end &&
|
||||
(data.address - mip_base) % slice_size == 0) {
|
||||
mip = static_cast<int32_t>(level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mip < 0) {
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u) ||
|
||||
extent.depth != container.extent.depth || mip_layout[0].pitch != layout.pitch ||
|
||||
mip_layout[0].height != layout.height || layout.size == 0 ||
|
||||
layout.size % container.resources.layers != 0 ||
|
||||
container.data.size % container.resources.layers != 0 ||
|
||||
data.size % resources.layers != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
||||
// PS5 block slices contain the entire reversed mip chain.
|
||||
const auto slice_stride = container.data.size / container.resources.layers;
|
||||
const auto child_stride = data.size / resources.layers;
|
||||
if (child_stride != layout.size / container.resources.layers ||
|
||||
layout.offset >= slice_stride || child_stride > slice_stride - layout.offset ||
|
||||
(resources.layers > 1 && child_stride != slice_stride)) {
|
||||
return -1;
|
||||
}
|
||||
const auto mip_depth = std::max(container.extent.depth >> level, 1u);
|
||||
if (container.type == Prospero::ImageType::kColor3D &&
|
||||
type == Prospero::ImageType::kColor2D) {
|
||||
if (resources.layers != mip_depth) {
|
||||
return -1;
|
||||
}
|
||||
} else if (type != container.type) {
|
||||
const auto address_delta = data.address - container.data.address;
|
||||
if (address_delta < layout.offset || (address_delta - layout.offset) % slice_stride != 0) {
|
||||
return -1;
|
||||
}
|
||||
return mip;
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || type != container.type || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
container.resources.layers == 0 || data.size == 0) {
|
||||
const auto layer = (address_delta - layout.offset) / slice_stride;
|
||||
if (layer > INT32_MAX || layer >= container.resources.layers ||
|
||||
resources.layers > container.resources.layers - layer) {
|
||||
return -1;
|
||||
}
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
||||
return -1;
|
||||
}
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
||||
container.data.address > UINT64_MAX - layout.offset) {
|
||||
return -1;
|
||||
}
|
||||
const auto slice_size = layout.size / container.resources.layers;
|
||||
if (slice_size == 0 || data.size % slice_size != 0) {
|
||||
return -1;
|
||||
}
|
||||
const auto mip_base = container.data.address + layout.offset;
|
||||
if (data.address < mip_base) {
|
||||
return -1;
|
||||
}
|
||||
const auto address_delta = data.address - mip_base;
|
||||
if (address_delta % data.size != 0 || address_delta / data.size > INT32_MAX) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int32_t>(address_delta / data.size);
|
||||
return static_cast<int32_t>(layer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -182,6 +159,7 @@ struct ImageViewInfo {
|
||||
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor;
|
||||
uint32_t base_level = 0;
|
||||
uint32_t level_count = 1;
|
||||
uint32_t min_lod = 0; // U4.8 clamp relative to base_level.
|
||||
uint32_t base_layer = 0;
|
||||
uint32_t layer_count = 1;
|
||||
vk::ComponentMapping mapping = {};
|
||||
@@ -190,9 +168,10 @@ struct ImageViewInfo {
|
||||
[[nodiscard]] bool operator==(const ImageViewInfo& rhs) const noexcept {
|
||||
return format == rhs.format && type == rhs.type && aspect == rhs.aspect &&
|
||||
base_level == rhs.base_level && level_count == rhs.level_count &&
|
||||
base_layer == rhs.base_layer && layer_count == rhs.layer_count &&
|
||||
mapping.r == rhs.mapping.r && mapping.g == rhs.mapping.g &&
|
||||
mapping.b == rhs.mapping.b && mapping.a == rhs.mapping.a && usage == rhs.usage;
|
||||
min_lod == rhs.min_lod && base_layer == rhs.base_layer &&
|
||||
layer_count == rhs.layer_count && mapping.r == rhs.mapping.r &&
|
||||
mapping.g == rhs.mapping.g && mapping.b == rhs.mapping.b &&
|
||||
mapping.a == rhs.mapping.a && usage == rhs.usage;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -338,14 +317,6 @@ ClassifyVideoOutCompression(bool compressed, uint64_t metadata_address, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool
|
||||
CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_target,
|
||||
bool gpu_modified, bool guest_modified) noexcept {
|
||||
return compression != VideoOutCompression::Uncompressed &&
|
||||
compression != VideoOutCompression::Unsupported && !guest_modified &&
|
||||
(render_target || gpu_modified);
|
||||
}
|
||||
|
||||
struct VideoOutPixelFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
|
||||
@@ -396,32 +367,6 @@ inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool
|
||||
IsSupportedDisplayRenderTargetTileMode(Prospero::TileMode tile_mode) noexcept {
|
||||
return tile_mode == Prospero::TileMode::kRenderTarget;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) {
|
||||
if (info.tile_mode != Prospero::TileMode::kStandard64KB || info.data.address == 0 ||
|
||||
(info.data.address & 0xffffu) != 0 || info.extent.width == 0 || info.extent.height == 0 ||
|
||||
info.bytes_per_block != 4 || info.resources.levels != 1 || info.resources.layers != 1 ||
|
||||
info.samples != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto expected_pitch =
|
||||
(static_cast<uint64_t>(info.extent.width) + 127u) & ~uint64_t {127u};
|
||||
const auto padded_height =
|
||||
(static_cast<uint64_t>(info.extent.height) + 127u) & ~uint64_t {127u};
|
||||
return expected_pitch <= UINT32_MAX && info.pitch == expected_pitch &&
|
||||
expected_pitch <= UINT64_MAX / padded_height / info.bytes_per_block &&
|
||||
info.data.size == expected_pitch * padded_height * info.bytes_per_block;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept {
|
||||
return info.tile_mode == Prospero::TileMode::kRenderTarget ||
|
||||
IsSupportedStandard64RenderTarget(info);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool DecodePackedColorClear(vk::Format format, uint32_t packed,
|
||||
vk::ClearColorValue& clear) {
|
||||
vk::ClearColorValue next {};
|
||||
|
||||
@@ -21,10 +21,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCompatibleViewFormat(vk::Format image_format, vk::Format view_format) {
|
||||
return ImageViewOps::FormatsCompatible(image_format, view_format);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsStencilViewFormat(vk::Format format) {
|
||||
switch (format) {
|
||||
case vk::Format::eS8Uint:
|
||||
@@ -326,8 +322,14 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
normalized.aspect = vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
normalized.usage = is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
|
||||
for (const auto& cached: views) {
|
||||
if (cached.info == normalized) {
|
||||
return cached.view;
|
||||
}
|
||||
}
|
||||
|
||||
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
|
||||
IsCompatibleViewFormat(image.format, normalized.format);
|
||||
ImageViewOps::FormatsCompatible(image.format, normalized.format);
|
||||
const bool slice_view =
|
||||
image.image_type == vk::ImageType::e3D && (normalized.type == vk::ImageViewType::e2D ||
|
||||
normalized.type == vk::ImageViewType::e2DArray);
|
||||
@@ -355,20 +357,18 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
image.layers);
|
||||
}
|
||||
|
||||
for (const auto& cached: views) {
|
||||
if (cached.info == normalized) {
|
||||
return cached.view;
|
||||
}
|
||||
}
|
||||
|
||||
vk::ImageViewUsageCreateInfo usage {};
|
||||
usage.sType = vk::StructureType::eImageViewUsageCreateInfo;
|
||||
usage.usage = image.usage;
|
||||
if (!is_storage) {
|
||||
usage.usage &= ~vk::ImageUsageFlagBits::eStorage;
|
||||
}
|
||||
vk::ImageViewMinLodCreateInfoEXT min_lod {};
|
||||
if (normalized.min_lod != 0) {
|
||||
min_lod.minLod = static_cast<float>(normalized.base_level) +
|
||||
static_cast<float>(normalized.min_lod) / 256.0f;
|
||||
usage.pNext = &min_lod;
|
||||
}
|
||||
vk::ImageViewCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageViewCreateInfo;
|
||||
create.pNext = &usage;
|
||||
create.image = image.image;
|
||||
create.viewType = normalized.type;
|
||||
@@ -381,7 +381,7 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
create.subresourceRange.layerCount = normalized.layer_count;
|
||||
|
||||
vk::ImageView view = nullptr;
|
||||
const auto result = m_graphics->device.createImageView(&create, nullptr, &view);
|
||||
const auto result = m_graphics.device.createImageView(&create, nullptr, &view);
|
||||
if (result != vk::Result::eSuccess || view == nullptr) {
|
||||
EXIT("failed to create image view: result=%d image_format=%d view_format=%d type=%d "
|
||||
"aspect=0x%x mip=%u+%u layer=%u+%u usage=0x%x\n",
|
||||
|
||||
@@ -92,29 +92,18 @@ SelectSampledColorView(vk::Format image_format, vk::Format view_format, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint32_t
|
||||
SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t swizzle) noexcept {
|
||||
if (IsSupportedSampledDepthView(image_format, view_format, swizzle)) {
|
||||
return swizzle;
|
||||
}
|
||||
EXIT("unsupported sampled depth image view: image_format=%d view_format=%d swizzle=0x%03x\n",
|
||||
static_cast<int>(image_format), static_cast<int>(view_format), swizzle);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
if (resource.kind != ShaderRecompiler::IR::ResourceKind::Image &&
|
||||
resource.kind != ShaderRecompiler::IR::ResourceKind::ImageUint) {
|
||||
if (resource.resource_class != ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
return false;
|
||||
}
|
||||
if (resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint && resource.depth_compare) {
|
||||
if (resource.numeric_class == Prospero::TextureNumericClass::Unsupported) {
|
||||
return false;
|
||||
}
|
||||
return (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
if (resource.numeric_class != Prospero::TextureNumericClass::Float && resource.depth_compare) {
|
||||
return false;
|
||||
}
|
||||
return resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
!resource.written && !resource.atomic;
|
||||
}
|
||||
|
||||
@@ -128,8 +117,9 @@ inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_fo
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
return (resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
||||
return resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Storage &&
|
||||
(resource.numeric_class == Prospero::TextureNumericClass::Float ||
|
||||
resource.numeric_class == Prospero::TextureNumericClass::Uint) &&
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
@@ -141,19 +131,19 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
|
||||
resource.mip_count != 0u)) &&
|
||||
resource.written &&
|
||||
(!resource.atomic ||
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.read)) &&
|
||||
(resource.numeric_class == Prospero::TextureNumericClass::Uint && resource.read)) &&
|
||||
!resource.depth_compare;
|
||||
}
|
||||
|
||||
inline void
|
||||
ValidateStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
if (!IsSupportedStorageImageResource(resource)) {
|
||||
EXIT("unsupported storage color image resource: kind=%u dimension=%u mip=%u "
|
||||
EXIT("unsupported storage color image resource: class=%u numeric=%u dimension=%u mip=%u "
|
||||
"read=%d written=%d atomic=%d depth_compare=%d\n",
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
resource.atomic, resource.depth_compare);
|
||||
static_cast<uint32_t>(resource.resource_class),
|
||||
static_cast<uint32_t>(resource.numeric_class),
|
||||
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
|
||||
resource.read, resource.written, resource.atomic, resource.depth_compare);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
@@ -27,13 +28,13 @@ constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = {
|
||||
Prospero::ColorMappingArgb},
|
||||
};
|
||||
|
||||
struct RenderTargetHostFormatInfo {
|
||||
struct HostFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
if (order == Prospero::ChannelOrder::kAlt) {
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
||||
@@ -47,17 +48,77 @@ RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
const auto format = VulkanFormat(guest_format);
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm:
|
||||
return {vk::Format::eA1R5G5B5UnormPack16, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm: return {format, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k1_5_5_5UNorm:
|
||||
return {vk::Format::eR5G5B5A1UnormPack16, Prospero::ColorMappingAbgr};
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm:
|
||||
return {vk::Format::eR4G4B4A4UnormPack16, Prospero::ColorMappingAbgr};
|
||||
default: return {VulkanFormat(guest_format), {}};
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm: return {format, Prospero::ColorMappingAbgr};
|
||||
default: return {format, {}};
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
size_t GetTextureRegionCount(uint32_t depth, uint32_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint32_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
stride = std::max(stride, mips[mip_level].offset + mips[mip_level].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
stride = std::max(stride, total_size / depth);
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t element_columns =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t element_rows =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(element_columns) * element_rows * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint32_t height,
|
||||
uint32_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto mip_pitch = base_pitch;
|
||||
auto mip_height = height;
|
||||
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, mip_pitch, mip_height);
|
||||
mips[mip_level] = {offset, size};
|
||||
|
||||
offset += size;
|
||||
if (mip_pitch > 1) {
|
||||
mip_pitch /= 2;
|
||||
}
|
||||
if (mip_height > 1) {
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool FitsBufferRange(uint64_t offset, uint64_t size, uint64_t capacity) {
|
||||
return offset <= capacity && size <= capacity - offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
@@ -65,135 +126,61 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host = ResolveRenderTargetHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto host_format = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host_format.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host.format, bytes, host.host_to_storage.Then(order_mapping)};
|
||||
return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage) {
|
||||
constexpr std::array host_components {vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG,
|
||||
vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA};
|
||||
std::array<vk::ComponentSwizzle, 4> storage_components {};
|
||||
for (uint32_t host_component = 0; host_component < host_components.size(); ++host_component) {
|
||||
storage_components[host_to_storage.Map(host_component)] = host_components[host_component];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
|
||||
switch (static_cast<Prospero::CompSwizzle>(s)) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return vk::ComponentSwizzle::eR;
|
||||
case Prospero::CompSwizzle::kGreen: return vk::ComponentSwizzle::eG;
|
||||
case Prospero::CompSwizzle::kBlue: return vk::ComponentSwizzle::eB;
|
||||
case Prospero::CompSwizzle::kAlpha: return vk::ComponentSwizzle::eA;
|
||||
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
|
||||
}
|
||||
return vk::ComponentSwizzle::eIdentity;
|
||||
}
|
||||
|
||||
static uint32_t TextureGetDstSel(uint32_t swizzle, uint32_t channel) {
|
||||
return (swizzle >> (channel * 3u)) & 0x7u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
||||
vk::ComponentMapping components {};
|
||||
components.r = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 0)));
|
||||
components.g = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 1)));
|
||||
components.b = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 2)));
|
||||
components.a = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 3)));
|
||||
return components;
|
||||
const auto resolve = [&](uint32_t component) {
|
||||
const auto selector = static_cast<Prospero::CompSwizzle>(GetDstSel(swizzle, component));
|
||||
switch (selector) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return storage_components[0];
|
||||
case Prospero::CompSwizzle::kGreen: return storage_components[1];
|
||||
case Prospero::CompSwizzle::kBlue: return storage_components[2];
|
||||
case Prospero::CompSwizzle::kAlpha: return storage_components[3];
|
||||
default: EXIT("unknown swizzle: %u\n", static_cast<uint32_t>(selector));
|
||||
}
|
||||
};
|
||||
return {resolve(0), resolve(1), resolve(2), resolve(3)};
|
||||
}
|
||||
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto vk_format = VulkanFormat(backing_format);
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto host_format = ResolveHostFormat(backing_format, Prospero::ChannelOrder::kStandard);
|
||||
const auto conversion_format =
|
||||
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||
if (vk_format != vk::Format::eUndefined) {
|
||||
return SurfaceFormatInfo(vk_format, conversion_format);
|
||||
if (host_format.format != vk::Format::eUndefined) {
|
||||
return {host_format.format, conversion_format, host_format.host_to_storage};
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
return SurfaceFormatInfo(vk::Format::eUndefined, Prospero::BufferFormat::kInvalid);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
stride = std::max(stride, mips[i].offset + mips[i].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
const auto guest_stride = total_size / depth;
|
||||
if (guest_stride >= stride) {
|
||||
stride = guest_stride;
|
||||
}
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t elements_w =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t elements_h =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(elements_w) * elements_h * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint64_t height,
|
||||
uint64_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto pitch = base_pitch;
|
||||
auto h = static_cast<uint32_t>(height);
|
||||
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, pitch, h);
|
||||
mips[i].size = size;
|
||||
mips[i].offset = offset;
|
||||
|
||||
offset += size;
|
||||
if (pitch > 1) {
|
||||
pitch /= 2;
|
||||
}
|
||||
if (h > 1) {
|
||||
h /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner) {
|
||||
TextureUploadLayout layout {};
|
||||
layout.surface.description = {
|
||||
format,
|
||||
tile,
|
||||
tile_mode,
|
||||
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
|
||||
width,
|
||||
height,
|
||||
@@ -202,7 +189,6 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
volume_texture ? 1u : depth,
|
||||
};
|
||||
const auto& description = layout.surface.description;
|
||||
const auto tile_mode = description.tile_mode;
|
||||
TileTextureElementLayout element {};
|
||||
|
||||
if (format == Prospero::BufferFormat::kInvalid) {
|
||||
@@ -212,34 +198,19 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
levels);
|
||||
}
|
||||
|
||||
switch (tile_mode) {
|
||||
case Prospero::TileMode::kLinear:
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
if (!allow_depth_tile || !TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile_mode != Prospero::TileMode::kLinear) {
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
} else {
|
||||
if ((tile_mode == Prospero::TileMode::kDepth && !allow_depth_tile) ||
|
||||
!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format), static_cast<uint32_t>(tile_mode),
|
||||
upload_size, width, height, levels);
|
||||
}
|
||||
element = {layout.surface.texture.block.bytes_per_element,
|
||||
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
|
||||
}
|
||||
@@ -272,40 +243,33 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const bool linear_texture = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
uint32_t mip_width = description.width;
|
||||
uint32_t mip_height = description.height;
|
||||
uint32_t mip_pitch = volume_texture && description.tile_mode != Prospero::TileMode::kLinear
|
||||
? description.width
|
||||
: layout.pitch;
|
||||
uint32_t mip_pitch = volume_texture && !linear_texture ? description.width : layout.pitch;
|
||||
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
regions.reserve(GetTextureRegionCount(depth, description.levels, volume_texture));
|
||||
for (uint32_t i = 0; i < description.levels; i++) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[i].size == 0);
|
||||
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, i, volume_texture);
|
||||
|
||||
for (uint32_t z = 0; z < mip_depth; z++) {
|
||||
const auto slice_offset = z * layout.slice_stride;
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = layout.mips[i].offset + slice_offset;
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, volume_texture ? 0u : z,
|
||||
1};
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
const bool linear = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
if (linear) {
|
||||
region.bufferRowLength = layout.mips[i].row_length;
|
||||
region.bufferImageHeight = layout.mips[i].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align = [](uint32_t value, uint32_t block) {
|
||||
return ((value + block - 1u) / block) * block;
|
||||
};
|
||||
const auto pitch = align(mip_pitch, texel_width);
|
||||
region.bufferRowLength = pitch > align(mip_width, texel_width) ? pitch : 0;
|
||||
}
|
||||
for (uint32_t mip_level = 0; mip_level < description.levels; ++mip_level) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[mip_level].size == 0);
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
vk::BufferImageCopy region {};
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, mip_level, 0, 1};
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
if (linear_texture) {
|
||||
region.bufferRowLength = layout.mips[mip_level].row_length;
|
||||
region.bufferImageHeight = layout.mips[mip_level].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto aligned_pitch = Common::AlignUp(mip_pitch, texel_width);
|
||||
region.bufferRowLength =
|
||||
aligned_pitch > Common::AlignUp(mip_width, texel_width) ? aligned_pitch : 0;
|
||||
}
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; ++slice_index) {
|
||||
region.bufferOffset = layout.mips[mip_level].offset + slice_index * layout.slice_stride;
|
||||
region.imageSubresource.baseArrayLayer = volume_texture ? 0u : slice_index;
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(slice_index) : 0;
|
||||
regions.push_back(region);
|
||||
}
|
||||
|
||||
@@ -323,17 +287,9 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
return regions;
|
||||
}
|
||||
|
||||
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t& size) {
|
||||
if (offset > capacity || length > capacity - offset) {
|
||||
return false;
|
||||
}
|
||||
size = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& out_infos) {
|
||||
std::vector<GpuTileInfo>& out_tile_infos) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
@@ -351,111 +307,69 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GpuTileInfo> infos;
|
||||
infos.reserve(regions.size());
|
||||
if (volume_texture) {
|
||||
size_t region_base = 0;
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
const uint32_t mip_depth = GetTextureLevelDepth(depth, level, true);
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const uint64_t linear_stride = layout.slice_stride;
|
||||
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
||||
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
||||
const auto& region = regions[region_base + z];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset =
|
||||
static_cast<uint64_t>(z / block.block_depth) * surface.block_slice_size +
|
||||
surface.mips[level].offset;
|
||||
const uint64_t linear_span =
|
||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||
layout.mips[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, surface.mips[level].size, tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.linear_slice_stride = linear_stride;
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.depth = copy_depth;
|
||||
info.surface_z =
|
||||
std::vector<GpuTileInfo> tile_infos;
|
||||
tile_infos.reserve(regions.size());
|
||||
const auto slices_per_copy = volume_texture ? block.block_depth : 1u;
|
||||
size_t region_base = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto& mip = surface.mips[mip_level];
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; slice_index += slices_per_copy) {
|
||||
const auto& region = regions[region_base + slice_index];
|
||||
GpuTileInfo tile_info {};
|
||||
tile_info.family = block.family;
|
||||
tile_info.bytes_per_element = block.bytes_per_element;
|
||||
tile_info.linear_offset = region.bufferOffset;
|
||||
tile_info.linear_size = layout.mips[mip_level].size;
|
||||
tile_info.tiled_size = mip.size;
|
||||
if (volume_texture) {
|
||||
tile_info.depth = std::min(slices_per_copy, mip_depth - slice_index);
|
||||
tile_info.linear_slice_stride = layout.slice_stride;
|
||||
tile_info.linear_size += (tile_info.depth - 1u) * layout.slice_stride;
|
||||
tile_info.tiled_offset =
|
||||
mip.offset +
|
||||
static_cast<uint64_t>(slice_index / slices_per_copy) * surface.block_slice_size;
|
||||
tile_info.surface_z =
|
||||
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail_x = tail ? surface.mips[level].tail_x : 0;
|
||||
info.tail_y = tail ? surface.mips[level].tail_y : 0;
|
||||
info.tail = tail;
|
||||
info.tiled_width = surface.mips[level].padded_width;
|
||||
info.tiled_height = surface.mips[level].padded_height;
|
||||
infos.push_back(info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
} else {
|
||||
size_t region_index = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
const auto& level_size = layout.mips[level];
|
||||
const auto& mip = surface.mips[level];
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const auto level_depth = GetTextureLevelDepth(depth, level, false);
|
||||
for (uint32_t z = 0; z < level_depth; z++) {
|
||||
const auto& region = regions[region_index++];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
} else {
|
||||
const auto source_stride = layout.source_slice_stride != 0
|
||||
? layout.source_slice_stride
|
||||
: surface.block_slice_size;
|
||||
if (z > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
if (slice_index > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
return false;
|
||||
}
|
||||
info.tiled_offset = mip.offset + static_cast<uint64_t>(z) * source_stride;
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, mip.size, tiled_size, info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail = tail;
|
||||
info.tail_x = tail ? mip.tail_x : 0;
|
||||
info.tail_y = tail ? mip.tail_y : 0;
|
||||
info.tiled_width = mip.padded_width;
|
||||
info.tiled_height = mip.padded_height;
|
||||
infos.push_back(info);
|
||||
tile_info.tiled_offset =
|
||||
mip.offset + static_cast<uint64_t>(slice_index) * source_stride;
|
||||
tile_info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
}
|
||||
if (!FitsBufferRange(tile_info.linear_offset, tile_info.linear_size, UINT64_MAX) ||
|
||||
!FitsBufferRange(tile_info.tiled_offset, tile_info.tiled_size, tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
const auto row_length =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto image_height = region.bufferImageHeight != 0 ? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
tile_info.width = std::max(
|
||||
(region.imageExtent.width + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.height =
|
||||
std::max((image_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
tile_info.pitch =
|
||||
std::max((row_length + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.tail = mip_level >= surface.first_tail_level;
|
||||
tile_info.tail_x = tile_info.tail ? mip.tail_x : 0;
|
||||
tile_info.tail_y = tile_info.tail ? mip.tail_y : 0;
|
||||
tile_info.tiled_width = mip.padded_width;
|
||||
tile_info.tiled_height = mip.padded_height;
|
||||
tile_infos.push_back(tile_info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
|
||||
if (infos.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_infos = std::move(infos);
|
||||
out_tile_infos = std::move(tile_infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
@@ -20,12 +18,9 @@ struct RenderTargetFormatInfo {
|
||||
};
|
||||
|
||||
struct SurfaceFormatInfo {
|
||||
explicit constexpr SurfaceFormatInfo(vk::Format format,
|
||||
Prospero::BufferFormat conversion_format)
|
||||
: vk_format(format), conversion_format(conversion_format) {}
|
||||
|
||||
vk::Format vk_format;
|
||||
Prospero::BufferFormat conversion_format;
|
||||
vk::Format vk_format;
|
||||
Prospero::BufferFormat conversion_format;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
struct TextureUploadMipLayout {
|
||||
@@ -43,20 +38,21 @@ struct TextureUploadLayout {
|
||||
TextureUploadMipLayout mips[16] = {};
|
||||
};
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage);
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner);
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& infos);
|
||||
std::vector<GpuTileInfo>& out_tile_infos);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "gpu_tiler_shaders/gpu_tiler_demote_d16_spv.h"
|
||||
#include "gpu_tiler_shaders/gpu_tiler_depth_spv.h"
|
||||
@@ -40,7 +41,6 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
nullptr};
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_info {};
|
||||
descriptor_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
descriptor_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
descriptor_info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
descriptor_info.pBindings = bindings.data();
|
||||
@@ -50,7 +50,6 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
|
||||
const vk::PushConstantRange push_range {vk::ShaderStageFlagBits::eCompute, 0, sizeof(Push)};
|
||||
vk::PipelineLayoutCreateInfo layout_info {};
|
||||
layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
layout_info.setLayoutCount = 1;
|
||||
layout_info.pSetLayouts = &m_descriptor_layout;
|
||||
layout_info.pushConstantRangeCount = 1;
|
||||
@@ -92,11 +91,9 @@ TileManager::~TileManager() {
|
||||
TileManager::Scratch TileManager::AllocateScratch(uint64_t size) {
|
||||
EXIT_IF(size == 0);
|
||||
vk::BufferCreateInfo create {};
|
||||
create.sType = vk::StructureType::eBufferCreateInfo;
|
||||
create.size = size;
|
||||
create.usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferSrc |
|
||||
vk::BufferUsageFlagBits::eTransferDst;
|
||||
create.sharingMode = vk::SharingMode::eExclusive;
|
||||
|
||||
VmaAllocationCreateInfo allocate {};
|
||||
allocate.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
@@ -225,7 +222,7 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca
|
||||
|
||||
const uint64_t uniform_alignment =
|
||||
std::max<uint64_t>(limits.minUniformBufferOffsetAlignment, 1);
|
||||
const uint64_t stride = (sizeof(Push) + uniform_alignment - 1) & ~(uniform_alignment - 1);
|
||||
const uint64_t stride = Common::AlignUp<uint64_t>(sizeof(Push), uniform_alignment);
|
||||
EXIT_NOT_IMPLEMENTED(dispatches.size() > UINT64_MAX / stride);
|
||||
const uint64_t bytes = dispatches.size() * stride;
|
||||
auto [mapped, offset] = m_stream_buffer.Map(bytes, uniform_alignment);
|
||||
@@ -263,21 +260,14 @@ vk::Pipeline TileManager::GetPipeline(uint32_t slot) {
|
||||
const uint32_t values[] {1u << element_index, direction_index};
|
||||
const vk::SpecializationMapEntry entries[] {{0, 0, 4}, {1, 4, 4}};
|
||||
const vk::SpecializationInfo specialization {2, entries, sizeof(values), values};
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = shaders[family_index].words * sizeof(uint32_t);
|
||||
module_info.pCode = shaders[family_index].code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create TileManager shader module");
|
||||
const auto module =
|
||||
CompileSPV({shaders[family_index].code, shaders[family_index].words}, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
stage.pSpecializationInfo = &specialization;
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -287,19 +277,19 @@ vk::Pipeline TileManager::GetPipeline(uint32_t slot) {
|
||||
return m_pipelines[slot];
|
||||
}
|
||||
|
||||
void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
void TileManager::Record(vk::Buffer source, uint64_t source_offset,
|
||||
uint64_t source_capacity, vk::Buffer target, uint64_t target_offset,
|
||||
uint64_t target_capacity, std::span<Dispatch> dispatches,
|
||||
bool clear_target) {
|
||||
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
|
||||
const uint64_t descriptor_alignment =
|
||||
std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
|
||||
const uint64_t source_descriptor_offset = source_offset & ~(descriptor_alignment - 1);
|
||||
const uint64_t target_descriptor_offset = target_offset & ~(descriptor_alignment - 1);
|
||||
const uint64_t source_descriptor_offset = Common::AlignDown(source_offset, descriptor_alignment);
|
||||
const uint64_t target_descriptor_offset = Common::AlignDown(target_offset, descriptor_alignment);
|
||||
const uint64_t source_base = source_offset - source_descriptor_offset;
|
||||
const uint64_t target_base = target_offset - target_descriptor_offset;
|
||||
const uint64_t source_range = (source_base + source_capacity + 3u) & ~uint64_t {3};
|
||||
const uint64_t target_range = (target_base + target_capacity + 3u) & ~uint64_t {3};
|
||||
const uint64_t source_range = Common::AlignUp(source_base + source_capacity, 4);
|
||||
const uint64_t target_range = Common::AlignUp(target_base + target_capacity, 4);
|
||||
EXIT_NOT_IMPLEMENTED(source_range > limits.maxStorageBufferRange ||
|
||||
target_range > limits.maxStorageBufferRange || target_offset % 4 != 0 ||
|
||||
target_capacity % 4 != 0);
|
||||
@@ -307,7 +297,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::BufferMemoryBarrier barriers[3] {};
|
||||
barriers[0].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[0].srcAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eHostWrite;
|
||||
barriers[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
@@ -316,7 +305,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
barriers[0].offset = source_offset;
|
||||
barriers[0].size = source_capacity;
|
||||
barriers[1] = barriers[0];
|
||||
barriers[1].srcAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eHostWrite;
|
||||
barriers[1].dstAccessMask =
|
||||
clear_target ? vk::AccessFlagBits::eTransferWrite
|
||||
: vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
@@ -352,7 +340,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
const vk::DescriptorBufferInfo infos[] {source_info, target_info, params_info};
|
||||
std::array<vk::WriteDescriptorSet, 3> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = index == 2 ? vk::DescriptorType::eUniformBuffer
|
||||
@@ -382,9 +369,9 @@ TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset,
|
||||
const uint64_t source_base = tiled_offset & (descriptor_alignment - 1);
|
||||
std::vector<Dispatch> dispatches;
|
||||
Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches);
|
||||
auto scratch = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3});
|
||||
auto scratch = AllocateScratch(Common::AlignUp(linear_capacity, 4));
|
||||
DeferDestroy(scratch);
|
||||
Record(false, tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches,
|
||||
Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches,
|
||||
true);
|
||||
return {scratch.buffer, 0, linear_capacity};
|
||||
}
|
||||
@@ -399,7 +386,7 @@ void TileManager::Tile(vk::Buffer linear, uint64_t linear_offset, uint64_t linea
|
||||
const uint64_t target_base = tiled_offset & (descriptor_alignment - 1);
|
||||
std::vector<Dispatch> dispatches;
|
||||
Prepare(true, tiled_capacity, linear_capacity, infos, source_base, target_base, dispatches);
|
||||
Record(true, linear, linear_offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
Record(linear, linear_offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
dispatches, false);
|
||||
}
|
||||
|
||||
@@ -416,19 +403,19 @@ void TileManager::TileImage(Image& image, std::span<const vk::BufferImageCopy> r
|
||||
// Reserve all stream parameters before creating a scheduler-lived scratch dependency:
|
||||
// StreamBuffer::Map is allowed to submit the current tick when it wraps.
|
||||
Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches);
|
||||
auto linear = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3});
|
||||
auto linear = AllocateScratch(Common::AlignUp(linear_capacity, 4));
|
||||
DeferDestroy(linear);
|
||||
image.Download(regions, linear.buffer, 0, linear.size);
|
||||
Result source {linear.buffer, 0, linear.size};
|
||||
if (transform == ColorTransform::SwapBgra16) {
|
||||
source = SwapBgra16(source);
|
||||
}
|
||||
Record(true, source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
Record(source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
dispatches, false);
|
||||
}
|
||||
|
||||
TileManager::Result TileManager::GetScratchBuffer(uint64_t size) {
|
||||
auto scratch = AllocateScratch((size + 3u) & ~uint64_t {3});
|
||||
auto scratch = AllocateScratch(Common::AlignUp(size, 4));
|
||||
DeferDestroy(scratch);
|
||||
return {scratch.buffer, 0, scratch.size};
|
||||
}
|
||||
@@ -436,11 +423,11 @@ TileManager::Result TileManager::GetScratchBuffer(uint64_t size) {
|
||||
TileManager::StorageBinding TileManager::BindStorage(Result buffer, uint64_t size) const {
|
||||
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
|
||||
const auto alignment = std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
|
||||
const auto descriptor_offset = buffer.offset - buffer.offset % alignment;
|
||||
const auto descriptor_offset = Common::AlignDown(buffer.offset, alignment);
|
||||
const auto base = buffer.offset - descriptor_offset;
|
||||
EXIT_IF(buffer.buffer == nullptr || size == 0 || buffer.size < size || base > UINT32_MAX ||
|
||||
size > UINT64_MAX - base || base + size > UINT64_MAX - 3);
|
||||
const auto range = (base + size + 3) & ~uint64_t {3};
|
||||
const auto range = Common::AlignUp(base + size, 4);
|
||||
EXIT_IF(range > limits.maxStorageBufferRange || range > UINT32_MAX);
|
||||
return {{buffer.buffer, descriptor_offset, range}, static_cast<uint32_t>(base)};
|
||||
}
|
||||
@@ -481,21 +468,13 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
code = GPU_TILER_DEMOTE_D16_SPV;
|
||||
words = std::size(GPU_TILER_DEMOTE_D16_SPV);
|
||||
}
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = words * sizeof(uint32_t);
|
||||
module_info.pCode = code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create D16 conversion shader module");
|
||||
const auto module = CompileSPV({code, words}, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
stage.pSpecializationInfo = &specialization;
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -525,14 +504,13 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
const auto target_required = required(layout.height, layout.layers, layout.target_row_stride,
|
||||
layout.target_slice_stride, target_active);
|
||||
EXIT_IF(source_required > UINT64_MAX - 3 || target_required > UINT64_MAX - 3);
|
||||
const auto source_barrier_size = (source_required + 3) & ~uint64_t {3};
|
||||
const auto target_barrier_size = (target_required + 3) & ~uint64_t {3};
|
||||
const auto source_barrier_size = Common::AlignUp(source_required, 4);
|
||||
const auto target_barrier_size = Common::AlignUp(target_required, 4);
|
||||
EXIT_IF(source.size < source_barrier_size || target.size < target_barrier_size);
|
||||
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::BufferMemoryBarrier barriers[2] {};
|
||||
barriers[0].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].buffer = source.buffer;
|
||||
@@ -542,7 +520,6 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
vk::AccessFlagBits::eTransferWrite |
|
||||
vk::AccessFlagBits::eShaderWrite;
|
||||
barriers[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||
barriers[1].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[1].buffer = target.buffer;
|
||||
@@ -596,7 +573,6 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
@@ -626,20 +602,12 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
|
||||
void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
if (m_swap_bgra16 == nullptr) {
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = std::size(GPU_TILER_SWAP_BGRA16_SPV) * sizeof(uint32_t);
|
||||
module_info.pCode = GPU_TILER_SWAP_BGRA16_SPV;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create BGRA16 swap shader module");
|
||||
const auto module = CompileSPV(GPU_TILER_SWAP_BGRA16_SPV, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -658,7 +626,6 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
@@ -666,7 +633,6 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
}
|
||||
vk::BufferMemoryBarrier barriers[2] {};
|
||||
for (uint32_t index = 0; index < 2; index++) {
|
||||
barriers[index].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[index].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[index].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[index].buffer = infos[index].buffer;
|
||||
|
||||
Loaded 100 of 326 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user