mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2f077c37 | ||
|
|
498d63b620 | ||
|
|
566a45017e | ||
|
|
7a40dad90f | ||
|
|
a9e843b38e | ||
|
|
9a76719544 | ||
|
|
472c8213db | ||
|
|
a4f9c022ff | ||
|
|
97514884b1 | ||
|
|
4980efa9b7 | ||
|
|
34cf130e4d | ||
|
|
21aa7b66ce | ||
|
|
11b95bed20 | ||
|
|
ca7c8f0074 | ||
|
|
d7063d0b81 | ||
|
|
091e6b31dc | ||
|
|
4cd6132758 | ||
|
|
9c263a9886 | ||
|
|
c11fc96019 | ||
|
|
4f2b5eb749 | ||
|
|
d7013dc2b3 | ||
|
|
0bbe301120 | ||
|
|
37aa6f88b0 | ||
|
|
e3890febaa | ||
|
|
a4da2a9f00 | ||
|
|
3005a210c6 | ||
|
|
027433fcdc | ||
|
|
7ff9e4907f | ||
|
|
87428d7a4b | ||
|
|
822ffba313 | ||
|
|
3c850b65c8 | ||
|
|
698c9f802f | ||
|
|
1d5f3c8f66 | ||
|
|
ec78afb966 | ||
|
|
3a7656336e | ||
|
|
90bf3c9236 | ||
|
|
d4f8338937 | ||
|
|
5125cd09c7 | ||
|
|
67cfec9063 | ||
|
|
81e52df7bf | ||
|
|
4b26d03dba | ||
|
|
216c580d13 | ||
|
|
7907a50817 | ||
|
|
769edf470b | ||
|
|
68fffdfb66 | ||
|
|
26781e6fea | ||
|
|
d5c95bc3ee | ||
|
|
ace549ab21 | ||
|
|
0cbae39e41 | ||
|
|
e9f5da27f8 | ||
|
|
3052dac82e | ||
|
|
2bb0483473 | ||
|
|
389fd1e5be | ||
|
|
a76f3ac2fe | ||
|
|
2fc4855f29 | ||
|
|
869329ee68 | ||
|
|
b2963aa6e0 | ||
|
|
9859b3e1d6 | ||
|
|
cd19fa283f | ||
|
|
b6af7c73ec | ||
|
|
ac64d202af | ||
|
|
df44ad3fac | ||
|
|
0a2f4814ac | ||
|
|
e6b4707a5d | ||
|
|
9a3468e3a0 | ||
|
|
73da43a7be | ||
|
|
aa88d94755 | ||
|
|
51562cd8d2 | ||
|
|
822ca3c10b | ||
|
|
2b6fd699e7 | ||
|
|
b9aa0dc3b3 | ||
|
|
f380d69b9d | ||
|
|
f06d7e6084 | ||
|
|
00a5dd32c0 | ||
|
|
a9ddd44d73 | ||
|
|
2f9fcc63c3 | ||
|
|
0bbd0bdd99 | ||
|
|
6396dc9a85 | ||
|
|
a3a00464bc | ||
|
|
1da99a0671 | ||
|
|
9304921132 | ||
|
|
c13a1d752b | ||
|
|
51d0e02543 | ||
|
|
e8752aca7e |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
name: Game Emulation Bug Report
|
||||
description: Report KytyPS5 game boot, crash, freeze, rendering, audio, input, or gameplay behavior problems.
|
||||
title: "[GAME BUG]: "
|
||||
name: Game Emulation Status Report
|
||||
description: Submit a KytyPS5 game compatibility result, including boot, rendering, audio, input, or gameplay behavior.
|
||||
title: "[GAME STATUS]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -10,6 +10,7 @@ body:
|
||||
Before submitting, please:
|
||||
- Search existing issues first.
|
||||
- Re-test with a clean game dump.
|
||||
- Use the latest KytyPS5 build available to you.
|
||||
- Attach the complete log file instead of edited snippets.
|
||||
|
||||
- type: markdown
|
||||
@@ -44,6 +45,28 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: compatibility-status
|
||||
attributes:
|
||||
label: Compatibility status
|
||||
description: Select the furthest point the game consistently reaches on this build.
|
||||
options:
|
||||
- "Doesn't boot"
|
||||
- Logo
|
||||
- Main menu
|
||||
- In game
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Status guide**
|
||||
- **Doesn't boot:** The game does not show its first logo or startup screen.
|
||||
- **Logo:** The game shows a logo or startup screen but does not reach the main menu.
|
||||
- **Main menu:** The game reaches its menus but does not enter gameplay.
|
||||
- **In game:** The game reaches controllable gameplay, even if bugs prevent completion.
|
||||
|
||||
- type: input
|
||||
id: regression-builds
|
||||
attributes:
|
||||
@@ -54,22 +77,22 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Problem details
|
||||
## Test result
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Describe the failure or incorrect behavior as directly as possible.
|
||||
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue."
|
||||
label: Result details
|
||||
description: Describe what worked, what failed, and any visible or audible problems.
|
||||
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue. Audio plays correctly until the freeze."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: List the shortest path that triggers the problem from a fresh KytyPS5 start.
|
||||
label: Steps to reproduce the result
|
||||
description: List the shortest path to the reported status or failure from a fresh KytyPS5 start.
|
||||
placeholder: |
|
||||
1. Open KytyPS5.
|
||||
2. Boot the game.
|
||||
@@ -81,7 +104,7 @@ body:
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Explain what should happen at the same point in the game.
|
||||
description: Explain what should happen at the same point in the game. If there is no known problem, write "No known issue".
|
||||
placeholder: "Example: The save should load and gameplay should continue normally."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
+98
-30
@@ -6,9 +6,27 @@ on:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
KYTY_BUILD_ORIGIN: ${{ github.repository != 'KytyPS5/KytyPS5' && 'Fork' || github.event_name == 'push' && 'Official' || 'Source' }}
|
||||
KYTY_BUILD_REPOSITORY: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
metadata:
|
||||
name: Prepare build metadata
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_tag: ${{ steps.release_tag.outputs.value }}
|
||||
|
||||
steps:
|
||||
- name: Generate release tag
|
||||
id: release_tag
|
||||
shell: bash
|
||||
run: |
|
||||
echo "value=KytyPS5-$(date -u +'%Y-%m-%d')-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
windows:
|
||||
name: Build KytyPS5 (Windows)
|
||||
needs: metadata
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
@@ -76,6 +94,10 @@ jobs:
|
||||
cmake -S . -B _Build/windows ^
|
||||
-G Ninja ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON ^
|
||||
-DKYTY_BUILD_ORIGIN=%KYTY_BUILD_ORIGIN% ^
|
||||
-DKYTY_BUILD_REPOSITORY=%KYTY_BUILD_REPOSITORY% ^
|
||||
-DKYTY_RELEASE_TAG=${{ needs.metadata.outputs.release_tag }} ^
|
||||
-DCMAKE_C_COMPILER=clang-cl ^
|
||||
-DCMAKE_CXX_COMPILER=clang-cl ^
|
||||
-DCMAKE_PREFIX_PATH="%Qt6_DIR%"
|
||||
@@ -83,12 +105,12 @@ jobs:
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
cmake --build _Build/windows --target launcher audio_out2_port_tests virtual_memory_allocation_tests --parallel
|
||||
cmake --build _Build/windows --target launcher audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests --parallel
|
||||
|
||||
- name: Test
|
||||
shell: cmd
|
||||
run: |
|
||||
ctest --test-dir _Build/windows --output-on-failure -R "^(audio_out2_port|virtual_memory_allocation)$"
|
||||
ctest --test-dir _Build/windows --output-on-failure -R "^(audio_out2_port|ime_dialog|virtual_memory_allocation)$"
|
||||
|
||||
- name: Install
|
||||
shell: cmd
|
||||
@@ -104,6 +126,7 @@ jobs:
|
||||
|
||||
macos:
|
||||
name: Build KytyPS5 (macOS)
|
||||
needs: metadata
|
||||
runs-on: macos-15
|
||||
|
||||
steps:
|
||||
@@ -150,6 +173,10 @@ jobs:
|
||||
cmake -S . -B _Build/macos \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
|
||||
-DKYTY_BUILD_ORIGIN="$KYTY_BUILD_ORIGIN" \
|
||||
-DKYTY_BUILD_REPOSITORY="$KYTY_BUILD_REPOSITORY" \
|
||||
-DKYTY_RELEASE_TAG="${{ needs.metadata.outputs.release_tag }}" \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
@@ -159,14 +186,14 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build _Build/macos \
|
||||
--target launcher audio_out2_port_tests virtual_memory_allocation_tests \
|
||||
--target launcher audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests \
|
||||
--parallel
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
ctest --test-dir _Build/macos --output-on-failure \
|
||||
-R '^(audio_out2_port|virtual_memory_allocation)$'
|
||||
-R '^(audio_out2_port|ime_dialog|virtual_memory_allocation)$'
|
||||
|
||||
- name: Install
|
||||
shell: bash
|
||||
@@ -213,6 +240,7 @@ jobs:
|
||||
|
||||
linux:
|
||||
name: Build KytyPS5 (Linux)
|
||||
needs: metadata
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
@@ -273,6 +301,10 @@ jobs:
|
||||
cmake -S . -B _Build/linux \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
|
||||
-DKYTY_BUILD_ORIGIN="$KYTY_BUILD_ORIGIN" \
|
||||
-DKYTY_BUILD_REPOSITORY="$KYTY_BUILD_REPOSITORY" \
|
||||
-DKYTY_RELEASE_TAG="${{ needs.metadata.outputs.release_tag }}" \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR" 2>&1 | tee _Build/configure.log
|
||||
@@ -297,14 +329,14 @@ jobs:
|
||||
run: |
|
||||
cmake --build _Build/linux \
|
||||
--target launcher page_manager_tests memory_tracker_tests \
|
||||
audio_out2_port_tests virtual_memory_allocation_tests \
|
||||
audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests \
|
||||
--parallel
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
ctest --test-dir _Build/linux --output-on-failure \
|
||||
-R '^(audio_out2_port|page_manager|memory_tracker|virtual_memory_allocation)$'
|
||||
-R '^(audio_out2_port|ime_dialog|page_manager|memory_tracker|virtual_memory_allocation)$'
|
||||
|
||||
- name: Install
|
||||
shell: bash
|
||||
@@ -341,46 +373,77 @@ jobs:
|
||||
|
||||
release:
|
||||
name: Release KytyPS5
|
||||
if: github.event_name == 'push' && github.repository == 'KytyPS5/KytyPS5'
|
||||
needs: [windows, macos, linux]
|
||||
if: >-
|
||||
always() &&
|
||||
github.event_name == 'push' &&
|
||||
github.repository == 'KytyPS5/KytyPS5' &&
|
||||
(needs.windows.result == 'success' ||
|
||||
needs.macos.result == 'success' ||
|
||||
needs.linux.result == 'success')
|
||||
needs: [metadata, windows, macos, linux]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
RELEASE_NAME: ${{ needs.metadata.outputs.release_tag }}
|
||||
|
||||
steps:
|
||||
- name: Download builds
|
||||
- name: Download Windows build
|
||||
if: needs.windows.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
name: KytyPS5-Windows-x64
|
||||
path: artifacts/KytyPS5-Windows-x64
|
||||
|
||||
- name: Set release name
|
||||
shell: bash
|
||||
run: |
|
||||
echo "RELEASE_NAME=KytyPS5-$(date -u +'%Y-%m-%d')-${GITHUB_SHA::7}" >> "$GITHUB_ENV"
|
||||
- name: Download macOS build
|
||||
if: needs.macos.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: KytyPS5-macOS-x86_64
|
||||
path: artifacts/KytyPS5-macOS-x86_64
|
||||
|
||||
- name: Package builds
|
||||
- name: Download Linux build
|
||||
if: needs.linux.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: KytyPS5-Linux-x86_64
|
||||
path: artifacts/KytyPS5-Linux-x86_64
|
||||
|
||||
- name: Package Windows build
|
||||
if: needs.windows.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
windows_dir="artifacts/KytyPS5-Windows-x64"
|
||||
macos_dir="artifacts/KytyPS5-macOS-x86_64"
|
||||
linux_dir="artifacts/KytyPS5-Linux-x86_64"
|
||||
test -d "$windows_dir"
|
||||
test -d "$macos_dir"
|
||||
test -d "$linux_dir"
|
||||
chmod a+x \
|
||||
"$macos_dir/launcher" \
|
||||
"$macos_dir/kyty_emulator" \
|
||||
"$macos_dir/libMoltenVK.dylib" \
|
||||
"$linux_dir/launcher" \
|
||||
"$linux_dir/kyty_emulator"
|
||||
(
|
||||
cd "$windows_dir"
|
||||
zip -r "$GITHUB_WORKSPACE/$RELEASE_NAME-Windows-x64.zip" .
|
||||
)
|
||||
|
||||
- name: Package macOS build
|
||||
if: needs.macos.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
macos_dir="artifacts/KytyPS5-macOS-x86_64"
|
||||
test -d "$macos_dir"
|
||||
chmod a+x \
|
||||
"$macos_dir/launcher" \
|
||||
"$macos_dir/kyty_emulator" \
|
||||
"$macos_dir/libMoltenVK.dylib"
|
||||
(
|
||||
cd "$macos_dir"
|
||||
zip -r "$GITHUB_WORKSPACE/$RELEASE_NAME-macOS-x86_64.zip" .
|
||||
)
|
||||
|
||||
- name: Package Linux build
|
||||
if: needs.linux.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
linux_dir="artifacts/KytyPS5-Linux-x86_64"
|
||||
test -d "$linux_dir"
|
||||
chmod a+x \
|
||||
"$linux_dir/launcher" \
|
||||
"$linux_dir/kyty_emulator"
|
||||
tar -C "$linux_dir" -czf \
|
||||
"$GITHUB_WORKSPACE/$RELEASE_NAME-Linux-x86_64.tar.gz" .
|
||||
|
||||
@@ -389,11 +452,16 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
assets=(
|
||||
"$RELEASE_NAME-Windows-x64.zip"
|
||||
"$RELEASE_NAME-macOS-x86_64.zip"
|
||||
"$RELEASE_NAME-Linux-x86_64.tar.gz"
|
||||
)
|
||||
assets=()
|
||||
for artifact in \
|
||||
"$RELEASE_NAME-Windows-x64.zip" \
|
||||
"$RELEASE_NAME-macOS-x86_64.zip" \
|
||||
"$RELEASE_NAME-Linux-x86_64.tar.gz"; do
|
||||
if [[ -f "$artifact" ]]; then
|
||||
assets+=("$artifact")
|
||||
fi
|
||||
done
|
||||
test "${#assets[@]}" -gt 0
|
||||
if gh release view "$RELEASE_NAME" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
|
||||
gh release upload "$RELEASE_NAME" "${assets[@]}" \
|
||||
--clobber \
|
||||
|
||||
@@ -39,3 +39,6 @@
|
||||
[submodule "3rdparty/nlohmann_json"]
|
||||
path = 3rdparty/nlohmann_json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "3rdparty/imgui"]
|
||||
path = 3rdparty/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
Vendored
+45
@@ -2,6 +2,32 @@ set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(cpuinfo)
|
||||
|
||||
# The Windows guest red-zone patcher needs a decoder/encoder and a small
|
||||
# trampoline assembler. Pin the same revisions used by shadPS4's implementation.
|
||||
include(FetchContent)
|
||||
|
||||
set(XBYAK_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(
|
||||
xbyak
|
||||
GIT_REPOSITORY https://github.com/herumi/xbyak.git
|
||||
GIT_TAG 44a72f369268f7d552650891b296693e91db86bb
|
||||
GIT_SHALLOW FALSE
|
||||
)
|
||||
|
||||
set(ZYDIS_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(ZYDIS_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(ZYDIS_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(ZYDIS_BUILD_DOXYGEN OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(
|
||||
zydis
|
||||
GIT_REPOSITORY https://github.com/zyantific/zydis.git
|
||||
GIT_TAG 120e0e705f8e3b507dc49377ac2879979f0d545c
|
||||
GIT_SUBMODULES_RECURSE TRUE
|
||||
GIT_SHALLOW FALSE
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(xbyak zydis)
|
||||
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(SDL2)
|
||||
|
||||
@@ -11,6 +37,25 @@ target_include_directories(Vulkan-Headers INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Vulkan-Headers/include"
|
||||
)
|
||||
|
||||
add_library(imgui STATIC
|
||||
imgui/imgui.cpp
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_tables.cpp
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/backends/imgui_impl_vulkan.cpp
|
||||
)
|
||||
target_include_directories(imgui PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/imgui"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends"
|
||||
)
|
||||
target_compile_definitions(imgui PRIVATE IMGUI_IMPL_VULKAN_NO_PROTOTYPES)
|
||||
target_link_libraries(imgui PUBLIC Vulkan::Headers)
|
||||
if(MSVC)
|
||||
target_compile_options(imgui PRIVATE /W0)
|
||||
else()
|
||||
target_compile_options(imgui PRIVATE -w)
|
||||
endif()
|
||||
|
||||
set(SPIRV_SKIP_TESTS ON CACHE BOOL "" FORCE)
|
||||
set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "" FORCE)
|
||||
set(SKIP_SPIRV_TOOLS_INSTALL ON CACHE BOOL "" FORCE)
|
||||
|
||||
+1
Submodule 3rdparty/imgui added at f1cc2ae15e.
Vendored
+1
-1
Submodule 3rdparty/tracy updated: 00c079cec9...05cceee0df.
+64
-19
@@ -4,6 +4,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.0)
|
||||
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "Minimum policy version for bundled dependencies")
|
||||
endif()
|
||||
|
||||
get_property(KYTY_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT KYTY_GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
|
||||
endif()
|
||||
|
||||
project(Kyty)
|
||||
|
||||
set(KYTY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
@@ -28,6 +34,29 @@ include(CTest)
|
||||
|
||||
option(KYTY_ENABLE_CLANG_TIDY "Run clang-tidy checks during builds" OFF)
|
||||
|
||||
set(KYTY_BUILD_ORIGIN "Source" CACHE STRING "Build provenance shown in diagnostics")
|
||||
set_property(CACHE KYTY_BUILD_ORIGIN PROPERTY STRINGS Source Official Fork)
|
||||
if(NOT KYTY_BUILD_ORIGIN STREQUAL "Source" AND NOT KYTY_BUILD_ORIGIN STREQUAL "Official" AND
|
||||
NOT KYTY_BUILD_ORIGIN STREQUAL "Fork")
|
||||
message(FATAL_ERROR "KYTY_BUILD_ORIGIN must be Source, Official, or Fork")
|
||||
endif()
|
||||
|
||||
set(KYTY_RELEASE_TAG "" CACHE STRING "Release tag embedded in official builds")
|
||||
set(KYTY_BUILD_REPOSITORY "" CACHE STRING "GitHub repository embedded in fork builds")
|
||||
set(KYTY_OFFICIAL_BUILD OFF)
|
||||
set(KYTY_FORK_BUILD OFF)
|
||||
if(KYTY_BUILD_ORIGIN STREQUAL "Official")
|
||||
if(KYTY_RELEASE_TAG STREQUAL "")
|
||||
message(FATAL_ERROR "Official builds require KYTY_RELEASE_TAG")
|
||||
endif()
|
||||
set(KYTY_OFFICIAL_BUILD ON)
|
||||
elseif(KYTY_BUILD_ORIGIN STREQUAL "Fork")
|
||||
if(KYTY_BUILD_REPOSITORY STREQUAL "")
|
||||
message(FATAL_ERROR "Fork builds require KYTY_BUILD_REPOSITORY")
|
||||
endif()
|
||||
set(KYTY_FORK_BUILD ON)
|
||||
endif()
|
||||
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
include(TestBigEndian)
|
||||
@@ -117,16 +146,20 @@ configure_file(
|
||||
|
||||
find_package(Git)
|
||||
|
||||
add_custom_target( KytyGitVersion
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D INPUT_FILE=${KYTY_SOURCE_DIR}/kytyGitVersion.h.in
|
||||
-D OUTPUT_FILE=${PROJECT_BINARY_DIR}/kytyGitVersion.h
|
||||
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-D GIT_WORKING_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-P ${KYTY_SOURCE_DIR}/generate_version.cmake
|
||||
add_custom_target(KytyGitVersion
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D INPUT_FILE=${KYTY_SOURCE_DIR}/kytyGitVersion.h.in
|
||||
-D OUTPUT_FILE=${PROJECT_BINARY_DIR}/kytyGitVersion.h
|
||||
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-D GIT_WORKING_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-P ${KYTY_SOURCE_DIR}/generate_version.cmake
|
||||
BYPRODUCTS ${PROJECT_BINARY_DIR}/kytyGitVersion.h
|
||||
COMMENT "Generate kytyGitVersion.h"
|
||||
)
|
||||
|
||||
add_library(kyty_git_version INTERFACE)
|
||||
add_dependencies(kyty_git_version KytyGitVersion)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
||||
list(APPEND KYTY_IWYU
|
||||
kyty_emulator
|
||||
@@ -259,7 +292,7 @@ if (KYTY_CLANG_CL)
|
||||
set_source_files_properties(${kyty_emulator_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack -Wno-deprecated-declarations -D_TIMESPEC_DEFINED")
|
||||
endif()
|
||||
|
||||
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL2-static xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9)
|
||||
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.
|
||||
if(LINUX)
|
||||
@@ -345,6 +378,7 @@ add_executable(page_manager_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/PageManagerTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||
)
|
||||
target_link_libraries(page_manager_tests common)
|
||||
target_include_directories(page_manager_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
||||
@@ -352,6 +386,13 @@ add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
||||
)
|
||||
target_include_directories(bit_array_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(ime_dialog_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ImeDialogTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/imeCommon.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/imeDialog.cpp"
|
||||
)
|
||||
target_include_directories(ime_dialog_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(memory_tracker_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/MemoryTrackerTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||
@@ -394,13 +435,6 @@ add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
target_link_libraries(resource_tracking_tests fmt::fmt)
|
||||
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(resource_mutex_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceMutexTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/cache/resourceMutex.cpp"
|
||||
)
|
||||
target_link_libraries(resource_mutex_tests common)
|
||||
target_include_directories(resource_mutex_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/AudioOut2PortTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/libAudio2.cpp"
|
||||
@@ -417,6 +451,13 @@ add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
|
||||
target_link_libraries(event_queue_lifetime_tests common fmt::fmt)
|
||||
target_include_directories(event_queue_lifetime_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(sync_on_address_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/SyncOnAddressTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/kernel/syncOnAddress.cpp"
|
||||
)
|
||||
target_link_libraries(sync_on_address_tests common)
|
||||
target_include_directories(sync_on_address_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(image_page_table_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ImagePageTableTests.cpp"
|
||||
)
|
||||
@@ -459,6 +500,7 @@ if(NOT KYTY_CLANG_CL)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test(NAME ime_dialog COMMAND $<TARGET_FILE:ime_dialog_tests>)
|
||||
add_test(NAME shader_cfg COMMAND $<TARGET_FILE:shader_cfg_tests>)
|
||||
add_test(NAME scalar_provenance COMMAND $<TARGET_FILE:scalar_provenance_tests>)
|
||||
add_test(NAME image_page_table COMMAND $<TARGET_FILE:image_page_table_tests>)
|
||||
@@ -468,18 +510,22 @@ if(BUILD_TESTING)
|
||||
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_tracking COMMAND $<TARGET_FILE:resource_tracking_tests>)
|
||||
add_test(NAME resource_mutex COMMAND $<TARGET_FILE:resource_mutex_tests>)
|
||||
add_test(NAME event_queue_lifetime COMMAND $<TARGET_FILE:event_queue_lifetime_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 shader_recompiler_compute COMMAND $<TARGET_FILE:shader_recompiler_compute_tests>)
|
||||
add_test(NAME virtual_memory_allocation
|
||||
COMMAND $<TARGET_FILE:virtual_memory_allocation_tests>)
|
||||
add_test(NAME guest_red_zone_patcher
|
||||
COMMAND $<TARGET_FILE:virtual_memory_allocation_tests> --red-zone-patcher-only)
|
||||
add_test(NAME command_scheduler_timeline
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --scheduler-only)
|
||||
add_test(NAME stream_buffer_ring
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --stream-buffer-only)
|
||||
add_test(NAME gpu_command_lane
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --gpu-command-lane-only)
|
||||
add_test(NAME pm4_context_state
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --context-state-only)
|
||||
add_test(NAME gpu_tiler
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --gpu-tiler-only)
|
||||
|
||||
@@ -505,6 +551,7 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
|
||||
add_custom_target(kyty_tests DEPENDS
|
||||
ime_dialog_tests
|
||||
shader_cfg_tests
|
||||
scalar_provenance_tests
|
||||
image_page_table_tests
|
||||
@@ -514,8 +561,8 @@ if(BUILD_TESTING)
|
||||
shader_vertex_metadata_tests
|
||||
shader_stage_runtime_tests
|
||||
resource_tracking_tests
|
||||
resource_mutex_tests
|
||||
event_queue_lifetime_tests
|
||||
sync_on_address_tests
|
||||
shader_recompiler_compute_tests
|
||||
virtual_memory_allocation_tests
|
||||
)
|
||||
@@ -563,8 +610,6 @@ elseif(WIN32 OR LINUX)
|
||||
set_target_properties(kyty_emulator PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS} -Wl,-Map=${KYTY_EMULATOR_MAP_LINK_PATH}")
|
||||
endif()
|
||||
|
||||
add_dependencies(kyty_emulator KytyGitVersion)
|
||||
|
||||
if(APPLE)
|
||||
# The emulator writes x86-64 trampolines/PLT stubs into memory and executes them, and
|
||||
# runs guest code under Rosetta. That requires the JIT / unsigned-executable-memory
|
||||
|
||||
@@ -31,6 +31,9 @@ macOS support is experimental. The emulator is built for x86-64 and runs on Appl
|
||||
Rosetta 2, with Vulkan provided by MoltenVK. A small number of titles have been verified in-game
|
||||
on Apple Silicon hardware; see [Building on macOS](#building-on-macos).
|
||||
|
||||
Community game test results are available in the
|
||||
[KytyPS5 Compatibility List](https://kytyps5.github.io/).
|
||||
|
||||
## Bugs and Issues
|
||||
|
||||
The project is in an early stage, so please be mindful when opening new issues. Expect crashes,
|
||||
@@ -52,7 +55,7 @@ graphical glitches, low compatibility, and poor performance.
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Neptunia ReVerse</strong><br>
|
||||
<img src="docs/screenshots/ps5-04.png" width="300" alt="Minecraft Legends running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-04.png" width="300" alt="Neptunia ReVerse running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
<strong>SILENT HILL: The Short Message</strong><br>
|
||||
@@ -62,11 +65,11 @@ graphical glitches, low compatibility, and poor performance.
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Hellboy</strong><br>
|
||||
<img src="docs/screenshots/ps5-02.png" width="300" alt="Disgaea 6 running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-02.png" width="300" alt="Hellboy running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
<strong>Paleo Pines</strong><br>
|
||||
<img src="docs/screenshots/ps5-06.png" width="300" alt="Dreaming Sarah running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-06.png" width="300" alt="Paleo Pines running in KytyPS5">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#define KYTY_VERSION "@KYTY_VERSION@"
|
||||
#define KYTY_BUILD_REPOSITORY "@KYTY_BUILD_REPOSITORY@"
|
||||
#define KYTY_RELEASE_TAG "@KYTY_RELEASE_TAG@"
|
||||
#cmakedefine KYTY_OFFICIAL_BUILD
|
||||
#cmakedefine KYTY_FORK_BUILD
|
||||
#define KYTY_ENDIAN @KYTY_ENDIAN@
|
||||
#define KYTY_PLATFORM @KYTY_PLATFORM@
|
||||
#define KYTY_COMPILER KYTY_COMPILER_@KYTY_COMPILER@
|
||||
|
||||
@@ -24,6 +24,7 @@ target_link_libraries(common
|
||||
Tracy::TracyClient
|
||||
PRIVATE
|
||||
cpuinfo
|
||||
kyty_git_version
|
||||
spdlog::spdlog
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "kytyGitVersion.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@@ -23,7 +24,7 @@ static std::string BuildFatalReport(const char* title, std::string_view text, co
|
||||
DebugStack stack;
|
||||
DebugStack::Trace(&stack);
|
||||
|
||||
std::string report = "--- Stack Trace ---\n";
|
||||
std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n";
|
||||
for (int i = PRINT_STACK_FROM; i < stack.depth; i++) {
|
||||
report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM,
|
||||
static_cast<uint64_t>(stack.GetAddr(i)));
|
||||
@@ -34,7 +35,7 @@ static std::string BuildFatalReport(const char* title, std::string_view text, co
|
||||
|
||||
static int DbgReport(const char* title, std::string_view text, const char* file, int line) {
|
||||
Log::WriteFatal(BuildFatalReport(title, text, file, line));
|
||||
SubsystemsListSingleton::Instance()->ShutdownAll();
|
||||
Subsystems::EmergencyShutdownActive();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "common/commonSubsystem.h"
|
||||
|
||||
#include "common/byteBuffer.h" // IWYU pragma: associated
|
||||
#include "common/common.h" // IWYU pragma: associated
|
||||
#include "common/debug.h" // IWYU pragma: associated
|
||||
#include "common/hash.h" // IWYU pragma: associated
|
||||
#include "common/magicEnum.h" // IWYU pragma: associated
|
||||
#include "common/singleton.h" // IWYU pragma: associated
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Common) {
|
||||
VirtualMemory::Init();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Common) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Common) {}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef KYTY_COMMON_COMMON_SUBSYSTEM_H_
|
||||
#define KYTY_COMMON_COMMON_SUBSYSTEM_H_
|
||||
|
||||
#include "common/subsystems.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Common);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_COMMON_SUBSYSTEM_H_ */
|
||||
@@ -9,15 +9,15 @@ namespace Config {
|
||||
|
||||
static std::unique_ptr<ConfigOptions> g_config;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Config) {
|
||||
void Initialize() {
|
||||
EXIT_IF(g_config != nullptr);
|
||||
|
||||
g_config = std::make_unique<ConfigOptions>();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Config) {}
|
||||
void Shutdown() {
|
||||
g_config.reset();
|
||||
}
|
||||
|
||||
void Load(const ConfigOptions& cfg) {
|
||||
EXIT_IF(g_config == nullptr);
|
||||
@@ -33,6 +33,10 @@ uint32_t GetScreenHeight() {
|
||||
return g_config->screen_height;
|
||||
}
|
||||
|
||||
bool FullscreenEnabled() {
|
||||
return g_config->fullscreen_enabled;
|
||||
}
|
||||
|
||||
uint32_t GetVblankFrequency() {
|
||||
return std::clamp(g_config->vblank_frequency, 30u, 360u);
|
||||
}
|
||||
@@ -97,4 +101,14 @@ bool ReadbackLinearImagesEnabled() {
|
||||
return g_config->readback_linear_images;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled() {
|
||||
return g_config->red_zone_protection_enabled;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Keymap& GetKeymap() {
|
||||
return g_config->keymap;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
@@ -2,13 +2,21 @@
|
||||
#define KYTY_COMMON_EMULATOR_CONFIG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Config {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Config);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Config";
|
||||
static constexpr auto initialize = Config::Initialize;
|
||||
static constexpr auto shutdown = Config::Shutdown;
|
||||
};
|
||||
|
||||
enum class ShaderOptimizationType { None, Size, Performance };
|
||||
|
||||
@@ -18,12 +26,15 @@ enum class ProfilerDirection { None, Network };
|
||||
|
||||
enum class OutputDirection { Silent, Console, File };
|
||||
|
||||
using Keymap = std::vector<std::string>;
|
||||
|
||||
constexpr uint32_t DEFAULT_CONSOLE_LANGUAGE = 1;
|
||||
constexpr uint32_t MAX_CONSOLE_LANGUAGE = 29;
|
||||
|
||||
struct ConfigOptions {
|
||||
uint32_t screen_width = 1280;
|
||||
uint32_t screen_height = 720;
|
||||
bool fullscreen_enabled = false;
|
||||
uint32_t vblank_frequency = 60;
|
||||
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||
bool vulkan_validation_enabled = false;
|
||||
@@ -40,12 +51,17 @@ struct ConfigOptions {
|
||||
bool spirv_debug_printf_enabled = false;
|
||||
bool renderdoc_enabled = false;
|
||||
bool readback_linear_images = false;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool red_zone_protection_enabled = false;
|
||||
#endif
|
||||
Keymap keymap;
|
||||
};
|
||||
|
||||
void Load(const ConfigOptions& cfg);
|
||||
|
||||
uint32_t GetScreenWidth();
|
||||
uint32_t GetScreenHeight();
|
||||
bool FullscreenEnabled();
|
||||
uint32_t GetVblankFrequency();
|
||||
uint32_t GetConsoleLanguage();
|
||||
bool VulkanValidationEnabled();
|
||||
@@ -69,6 +85,11 @@ bool SpirvDebugPrintfEnabled();
|
||||
|
||||
bool RenderDocEnabled();
|
||||
bool ReadbackLinearImagesEnabled();
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled();
|
||||
#endif
|
||||
|
||||
const Keymap& GetKeymap();
|
||||
|
||||
} // namespace Config
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ void WriteFatal(fmt::text_style style, std::string_view text) {
|
||||
Flush();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Log) {
|
||||
void Initialize() {
|
||||
g_initialized = true;
|
||||
switch (Config::GetPrintfDirection()) {
|
||||
case Config::OutputDirection::Silent: g_direction = Direction::Silent; break;
|
||||
@@ -161,13 +161,7 @@ KYTY_SUBSYSTEM_INIT(Log) {
|
||||
SetupLogger();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Log) {
|
||||
Flush();
|
||||
std::lock_guard lock(g_logger_mutex);
|
||||
g_logger.reset();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Log) {
|
||||
void Shutdown() {
|
||||
Flush();
|
||||
std::lock_guard lock(g_logger_mutex);
|
||||
g_logger.reset();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_LOGGING_LOG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/printf.h>
|
||||
@@ -10,7 +9,15 @@
|
||||
|
||||
namespace Log {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Log);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Log";
|
||||
static constexpr auto initialize = Log::Initialize;
|
||||
static constexpr auto shutdown = Log::Shutdown;
|
||||
static constexpr auto emergency_shutdown = Log::Shutdown;
|
||||
};
|
||||
|
||||
enum class Direction { Silent, Console, File };
|
||||
|
||||
|
||||
@@ -15,56 +15,8 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
struct FrameS {
|
||||
struct FrameS* next;
|
||||
void* ret_addr;
|
||||
};
|
||||
|
||||
int WalkStack(int z_stack_depth, void** z_stack_trace) {
|
||||
CONTEXT context;
|
||||
// KNONVOLATILE_CONTEXT_POINTERS NvContext;
|
||||
PRUNTIME_FUNCTION runtime_function = nullptr;
|
||||
PVOID handler_data = nullptr;
|
||||
ULONG64 establisher_frame = 0;
|
||||
ULONG64 image_base = 0;
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
int frame = 0;
|
||||
|
||||
while (true) {
|
||||
if (frame >= z_stack_depth) {
|
||||
break;
|
||||
}
|
||||
|
||||
z_stack_trace[frame] = reinterpret_cast<void*>(context.Rip);
|
||||
|
||||
frame++;
|
||||
|
||||
runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, nullptr);
|
||||
|
||||
if (runtime_function == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// RtlZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
|
||||
RtlVirtualUnwind(0, image_base, context.Rip, runtime_function, &context, &handler_data,
|
||||
&establisher_frame, nullptr /*&NvContext*/);
|
||||
|
||||
if (context.Rip == 0u) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
int n = WalkStack(*depth, stack);
|
||||
*depth = n;
|
||||
// unwind_info_t info = {stack, 0, *depth};
|
||||
// _Unwind_Backtrace(&trace_fcn, &info);
|
||||
// *depth = info.depth;
|
||||
*depth = static_cast<int>(CaptureStackBackTrace(0, static_cast<DWORD>(*depth), stack, nullptr));
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
|
||||
+5
-14
@@ -1,7 +1,6 @@
|
||||
#include "common/profiler.h"
|
||||
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <common/TracyProtocol.hpp>
|
||||
@@ -55,13 +54,7 @@ void SetThreadName(const char* name) {
|
||||
}
|
||||
}
|
||||
|
||||
void Close() {
|
||||
if (tracy::ProfilerAvailable()) {
|
||||
tracy::ShutdownProfiler();
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Profiler) {
|
||||
void Initialize() {
|
||||
switch (Config::GetProfilerDirection()) {
|
||||
case Config::ProfilerDirection::Network:
|
||||
if (!tracy::ProfilerAvailable()) {
|
||||
@@ -78,12 +71,10 @@ KYTY_SUBSYSTEM_INIT(Profiler) {
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Profiler) {
|
||||
Close();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Profiler) {
|
||||
Close();
|
||||
void Shutdown() {
|
||||
if (tracy::ProfilerAvailable()) {
|
||||
tracy::ShutdownProfiler();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Profiler
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_PROFILER_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
@@ -42,7 +41,15 @@ private:
|
||||
void EndBlock();
|
||||
void SetThreadName(const char* name);
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Profiler);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Profiler";
|
||||
static constexpr auto initialize = Profiler::Initialize;
|
||||
static constexpr auto shutdown = Profiler::Shutdown;
|
||||
static constexpr auto emergency_shutdown = Profiler::Shutdown;
|
||||
};
|
||||
|
||||
} // namespace Profiler
|
||||
|
||||
|
||||
+25
-214
@@ -1,232 +1,43 @@
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class SubsystemPrivate {
|
||||
public:
|
||||
SubsystemPrivate() = default;
|
||||
virtual ~SubsystemPrivate() = default;
|
||||
static Subsystems* g_active_subsystems = nullptr;
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemPrivate);
|
||||
Subsystems::Subsystems(bool print): m_print(print) {
|
||||
m_active.reserve(16);
|
||||
g_active_subsystems = this;
|
||||
}
|
||||
|
||||
bool failed {false};
|
||||
std::string fail_msg;
|
||||
};
|
||||
|
||||
class SubsystemsListPrivate {
|
||||
public:
|
||||
explicit SubsystemsListPrivate(SubsystemsList& p): parent(p) {}
|
||||
|
||||
virtual ~SubsystemsListPrivate() = default;
|
||||
|
||||
void SetArgs(int argc, char** argv) {
|
||||
this->m_argc = argc;
|
||||
this->m_argv = argv;
|
||||
Subsystems::~Subsystems() {
|
||||
Destroy();
|
||||
if (g_active_subsystems == this) {
|
||||
g_active_subsystems = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
|
||||
EXIT_IF(!s);
|
||||
|
||||
const char* name = s->Id();
|
||||
|
||||
EXIT_IF(FindByName(name) != nullptr);
|
||||
|
||||
auto nl = std::make_unique<SubsListStruct>();
|
||||
|
||||
nl->s = s;
|
||||
nl->name = name;
|
||||
nl->deps = nullptr;
|
||||
nl->next = std::move(list);
|
||||
nl->prev_init = nullptr;
|
||||
|
||||
auto* node = nl.get();
|
||||
list = std::move(nl);
|
||||
|
||||
for (auto* dep: deps) {
|
||||
const char* str = dep->Id();
|
||||
|
||||
auto l = std::make_unique<DepsListStruct>();
|
||||
l->dep_name = str;
|
||||
l->next = std::move(node->deps);
|
||||
|
||||
node->deps = std::move(l);
|
||||
void Subsystems::Destroy() {
|
||||
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
|
||||
if (it->shutdown != nullptr) {
|
||||
it->shutdown();
|
||||
}
|
||||
|
||||
node->initialized = false;
|
||||
}
|
||||
m_active.clear();
|
||||
}
|
||||
|
||||
bool InitAll(bool print_msg) {
|
||||
while (SubsListStruct* n = FindNextToInitialize()) {
|
||||
n->s->Init(&parent);
|
||||
|
||||
if (n->s->m_p->failed) {
|
||||
fail_msg = n->s->m_p->fail_msg.c_str();
|
||||
fail_name = n->name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (print_msg) {
|
||||
printf("Initialized: %s\n", n->name);
|
||||
}
|
||||
|
||||
n->initialized = true;
|
||||
|
||||
SubsListStruct* last = last_init;
|
||||
last_init = n;
|
||||
n->prev_init = last;
|
||||
void Subsystems::EmergencyShutdown() {
|
||||
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
|
||||
if (it->emergency_shutdown != nullptr) {
|
||||
it->emergency_shutdown();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
m_active.clear();
|
||||
}
|
||||
|
||||
void DestroyAll(bool print_msg) {
|
||||
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
|
||||
n->s->Destroy(&parent);
|
||||
n->initialized = false;
|
||||
|
||||
if (print_msg) {
|
||||
printf("Destroyed: %s\n", n->name);
|
||||
}
|
||||
}
|
||||
|
||||
last_init = nullptr;
|
||||
void Subsystems::EmergencyShutdownActive() {
|
||||
if (g_active_subsystems != nullptr) {
|
||||
g_active_subsystems->EmergencyShutdown();
|
||||
}
|
||||
|
||||
void ShutdownAll() {
|
||||
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
|
||||
n->s->UnexpectedShutdown(&parent);
|
||||
n->initialized = false;
|
||||
}
|
||||
|
||||
last_init = nullptr;
|
||||
}
|
||||
|
||||
struct DepsListStruct {
|
||||
const char* dep_name;
|
||||
std::unique_ptr<DepsListStruct> next;
|
||||
};
|
||||
|
||||
struct SubsListStruct {
|
||||
Subsystem* s;
|
||||
const char* name;
|
||||
std::unique_ptr<DepsListStruct> deps;
|
||||
std::unique_ptr<SubsListStruct> next;
|
||||
SubsListStruct* prev_init;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
[[nodiscard]] SubsListStruct* FindByName(const char* name) const {
|
||||
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
|
||||
if (std::strcmp(n->name, name) == 0) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] SubsListStruct* FindNextToInitialize() const {
|
||||
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
|
||||
if (n->initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DepsListStruct* d = n->deps.get();
|
||||
for (; d != nullptr; d = d->next.get()) {
|
||||
SubsListStruct* s = FindByName(d->dep_name);
|
||||
if ((s == nullptr) || !s->initialized) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (d == nullptr) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemsListPrivate);
|
||||
|
||||
std::unique_ptr<SubsListStruct> list;
|
||||
SubsListStruct* last_init = nullptr;
|
||||
int m_argc = 0;
|
||||
char** m_argv = nullptr;
|
||||
const char* fail_msg = nullptr;
|
||||
const char* fail_name = nullptr;
|
||||
SubsystemsList& parent;
|
||||
};
|
||||
|
||||
SubsystemsList::SubsystemsList(): m_p(std::make_unique<SubsystemsListPrivate>(*this)) {}
|
||||
|
||||
SubsystemsList::~SubsystemsList() = default;
|
||||
|
||||
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
|
||||
m_p->Add(s, deps);
|
||||
}
|
||||
|
||||
bool SubsystemsList::InitAll(bool print_msg) {
|
||||
return m_p->InitAll(print_msg);
|
||||
}
|
||||
|
||||
void SubsystemsList::DestroyAll(bool print_msg) {
|
||||
m_p->DestroyAll(print_msg);
|
||||
}
|
||||
|
||||
int* SubsystemsList::GetArgc() {
|
||||
return &m_p->m_argc;
|
||||
}
|
||||
|
||||
char** SubsystemsList::GetArgv() {
|
||||
return m_p->m_argv;
|
||||
}
|
||||
|
||||
Subsystem::Subsystem(): m_p(std::make_unique<SubsystemPrivate>()) {}
|
||||
|
||||
Subsystem::~Subsystem() = default;
|
||||
|
||||
void Subsystem::Fail(const char* format, ...) {
|
||||
va_list args {};
|
||||
va_start(args, format);
|
||||
|
||||
va_list args_copy {};
|
||||
va_copy(args_copy, args);
|
||||
int len = std::vsnprintf(nullptr, 0, format, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
if (len > 0) {
|
||||
std::string msg(static_cast<size_t>(len) + 1, '\0');
|
||||
std::vsnprintf(msg.data(), msg.size(), format, args);
|
||||
m_p->fail_msg = msg.c_str();
|
||||
m_p->failed = true;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
const char* SubsystemsList::GetFailName() const {
|
||||
return m_p->fail_name;
|
||||
}
|
||||
|
||||
const char* SubsystemsList::GetFailMsg() const {
|
||||
return m_p->fail_msg;
|
||||
}
|
||||
|
||||
void SubsystemsList::SetArgs(int argc, char* argv[]) {
|
||||
m_p->SetArgs(argc, argv);
|
||||
}
|
||||
|
||||
void SubsystemsList::ShutdownAll() {
|
||||
m_p->ShutdownAll();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+41
-70
@@ -2,90 +2,61 @@
|
||||
#define KYTY_COMMON_SUBSYSTEMS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class Subsystem;
|
||||
class SubsystemsListPrivate;
|
||||
class SubsystemPrivate;
|
||||
|
||||
class SubsystemsList {
|
||||
class Subsystems {
|
||||
public:
|
||||
SubsystemsList();
|
||||
virtual ~SubsystemsList();
|
||||
void SetArgs(int argc, char* argv[]);
|
||||
using Callback = void (*)();
|
||||
|
||||
// void Add(Subsystem* s, const char* name, ...);
|
||||
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps);
|
||||
explicit Subsystems(bool print = false);
|
||||
~Subsystems();
|
||||
|
||||
bool InitAll(bool print_msg = false);
|
||||
void DestroyAll(bool print_msg = false);
|
||||
template <typename Lifecycle>
|
||||
void Initialize() {
|
||||
Lifecycle::initialize();
|
||||
m_active.push_back({ShutdownCallback<Lifecycle>(), EmergencyCallback<Lifecycle>()});
|
||||
if (m_print) {
|
||||
std::printf("Initialized: %s\n", Lifecycle::name);
|
||||
}
|
||||
}
|
||||
|
||||
int* GetArgc();
|
||||
char** GetArgv();
|
||||
void Destroy();
|
||||
void EmergencyShutdown();
|
||||
|
||||
[[nodiscard]] const char* GetFailName() const;
|
||||
[[nodiscard]] const char* GetFailMsg() const;
|
||||
static void EmergencyShutdownActive();
|
||||
|
||||
void ShutdownAll();
|
||||
|
||||
static SubsystemsList* Instance() { return Common::Singleton<SubsystemsList>::Instance(); }
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemsList);
|
||||
KYTY_CLASS_NO_COPY(Subsystems);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SubsystemsListPrivate> m_p;
|
||||
template <typename Lifecycle>
|
||||
static consteval Callback ShutdownCallback() {
|
||||
if constexpr (requires { Lifecycle::shutdown; }) {
|
||||
return Lifecycle::shutdown;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Lifecycle>
|
||||
static consteval Callback EmergencyCallback() {
|
||||
if constexpr (requires { Lifecycle::emergency_shutdown; }) {
|
||||
return Lifecycle::emergency_shutdown;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
Callback shutdown;
|
||||
Callback emergency_shutdown;
|
||||
};
|
||||
|
||||
std::vector<Entry> m_active;
|
||||
bool m_print;
|
||||
};
|
||||
|
||||
using SubsystemsListSingleton = Common::Singleton<SubsystemsList>;
|
||||
|
||||
class Subsystem {
|
||||
public:
|
||||
Subsystem();
|
||||
virtual ~Subsystem();
|
||||
|
||||
virtual const char* Id() = 0;
|
||||
virtual void Init(SubsystemsList* parent) = 0;
|
||||
virtual void Destroy(SubsystemsList* parent) = 0;
|
||||
virtual void UnexpectedShutdown(SubsystemsList* parent) = 0;
|
||||
|
||||
friend class SubsystemsListPrivate;
|
||||
|
||||
KYTY_CLASS_NO_COPY(Subsystem);
|
||||
|
||||
protected:
|
||||
void Fail(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SubsystemPrivate> m_p;
|
||||
};
|
||||
|
||||
#define KYTY_SUBSYSTEM_DEFINE(s) \
|
||||
class s##Subsystem: public Common::Subsystem { \
|
||||
public: \
|
||||
static Subsystem* Instance() { return Common::Singleton<s##Subsystem>::Instance(); } \
|
||||
const char* Id() { return #s; } \
|
||||
void Init(Common::SubsystemsList* parent); \
|
||||
void Destroy(Common::SubsystemsList* parent); \
|
||||
void UnexpectedShutdown(Common::SubsystemsList* parent); \
|
||||
}; \
|
||||
typedef Common::Singleton<s##Subsystem> s##SubsystemSingleton;
|
||||
|
||||
#define KYTY_SUBSYSTEM_INIT(s) \
|
||||
void s##Subsystem::Init([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
#define KYTY_SUBSYSTEM_DESTROY(s) \
|
||||
void s##Subsystem::Destroy([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
#define KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(s) \
|
||||
void s##Subsystem::UnexpectedShutdown([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
|
||||
// #define KYTY_SUBSYSTEM_ADD(list, s, ...) list.push_back(s##SubsystemSingleton::Instance(), #s,
|
||||
// __VA_ARGS__); #define KYTY_SUBSYSTEM_ADD2(list, ...) list.Add2(s##SubsystemSingleton::Instance(),
|
||||
// __VA_ARGS__);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_SUBSYSTEMS_H_ */
|
||||
@@ -28,7 +28,8 @@
|
||||
#ifdef KYTY_WIN_CS
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
// IWYU pragma: no_include <winbase.h>
|
||||
constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
|
||||
constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
|
||||
constexpr uint64_t KYTY_SLEEP_SPIN_LIMIT_100NS = 500; // 50 us
|
||||
|
||||
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
|
||||
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
|
||||
@@ -39,7 +40,10 @@ static void SleepHighResolution100ns(uint64_t units_100ns) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (units_100ns <= 10000) {
|
||||
// Keep spinning only where a kernel transition is
|
||||
// likely to cost more than the requested delay; ordinary millisecond sleeps use the
|
||||
// per-thread high-resolution waitable timer below.
|
||||
if (units_100ns <= KYTY_SLEEP_SPIN_LIMIT_100NS) {
|
||||
LARGE_INTEGER frequency {};
|
||||
LARGE_INTEGER start {};
|
||||
if (QueryPerformanceFrequency(&frequency) != 0 && QueryPerformanceCounter(&start) != 0 &&
|
||||
@@ -248,15 +252,11 @@ static thread_id_t g_main_thread;
|
||||
static int g_main_thread_int;
|
||||
static std::atomic<int> g_thread_counter = 0;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Threads) {
|
||||
void InitializeThreads() {
|
||||
g_main_thread = std::this_thread::get_id();
|
||||
g_main_thread_int = Thread::GetThreadIdUnique();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Threads) {}
|
||||
|
||||
Thread::Thread(thread_func_t func, void* arg)
|
||||
: m_thread(std::make_unique<ThreadPrivate>(func, arg)) {
|
||||
while (!m_thread->started) {
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
#define KYTY_COMMON_THREADS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Threads);
|
||||
void InitializeThreads();
|
||||
|
||||
using thread_func_t = void (*)(void*);
|
||||
using wait_poll_func_t = void (*)();
|
||||
|
||||
+18
-36
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/commonSubsystem.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -43,7 +42,7 @@ static void KytyClose() {
|
||||
|
||||
LOGF("done!\n");
|
||||
|
||||
Common::SubsystemsListSingleton::Instance()->ShutdownAll();
|
||||
Common::Subsystems::EmergencyShutdownActive();
|
||||
}
|
||||
|
||||
static void MountOrCreateDir(const std::filesystem::path& dir, const std::string& point) {
|
||||
@@ -105,30 +104,13 @@ static void ClearDebugTextureFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json) {
|
||||
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json,
|
||||
Common::Subsystems& subsystems) {
|
||||
EXIT_IF(!Common::Thread::IsMainThread());
|
||||
|
||||
auto* slist = Common::SubsystemsList::Instance();
|
||||
|
||||
auto* audio = Libs::Audio::AudioSubsystem::Instance();
|
||||
auto* config = Config::ConfigSubsystem::Instance();
|
||||
auto* controller = Libs::Controller::ControllerSubsystem::Instance();
|
||||
auto* core = Common::CommonSubsystem::Instance();
|
||||
auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance();
|
||||
auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance();
|
||||
auto* log = Log::LogSubsystem::Instance();
|
||||
auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance();
|
||||
auto* network = Libs::Network::NetworkSubsystem::Instance();
|
||||
auto* profiler = Profiler::ProfilerSubsystem::Instance();
|
||||
auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance();
|
||||
auto* timer = Loader::Timer::TimerSubsystem::Instance();
|
||||
|
||||
slist->Add(config, {core});
|
||||
slist->InitAll(true);
|
||||
|
||||
subsystems.Initialize<Config::Lifecycle>();
|
||||
Config::Load(cfg);
|
||||
slist->Add(log, {core, config});
|
||||
slist->InitAll(true);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
|
||||
if (Common::File::IsFileExisting(param_json)) {
|
||||
Loader::SystemContentLoadParamSfo(param_json);
|
||||
@@ -138,17 +120,16 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path&
|
||||
}
|
||||
}
|
||||
|
||||
slist->Add(audio, {core, log, pthread, memory});
|
||||
slist->Add(controller, {core, log, config});
|
||||
slist->Add(file_system, {core, log, pthread});
|
||||
slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller});
|
||||
slist->Add(memory, {core, log});
|
||||
slist->Add(network, {core, log, pthread});
|
||||
slist->Add(profiler, {core, config});
|
||||
slist->Add(pthread, {core, log, timer});
|
||||
slist->Add(timer, {core, log});
|
||||
|
||||
slist->InitAll(true);
|
||||
// Initialization order is explicit; destruction is automatic and reversed.
|
||||
subsystems.Initialize<Loader::Timer::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::PthreadLifecycle>();
|
||||
subsystems.Initialize<Profiler::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Network::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::FileSystem::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Controller::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Audio::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Graphics::Lifecycle>();
|
||||
}
|
||||
|
||||
static void LoadElf(const std::filesystem::path& elf, bool dbg_print_reloc = false,
|
||||
@@ -189,8 +170,9 @@ void Run(const RunOptions& options) {
|
||||
EXIT("ELF is required\n");
|
||||
}
|
||||
|
||||
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
|
||||
Init(options.config, param_json);
|
||||
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
|
||||
Common::Subsystems subsystems(true);
|
||||
Init(options.config, param_json, subsystems);
|
||||
|
||||
ClearDebugTextureFolder();
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
set(KYTY_GIT_VERSION "unknown")
|
||||
set(KYTY_GIT_HASH "unknown")
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always --dirty
|
||||
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
|
||||
OUTPUT_VARIABLE KYTY_GIT_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
@@ -11,5 +12,27 @@ if(GIT_EXECUTABLE)
|
||||
if(NOT GIT_RESULT EQUAL 0)
|
||||
set(KYTY_GIT_VERSION "unknown")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --short=7 HEAD
|
||||
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
|
||||
OUTPUT_VARIABLE KYTY_GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE GIT_HASH_RESULT
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT GIT_HASH_RESULT EQUAL 0)
|
||||
set(KYTY_GIT_HASH "unknown")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
|
||||
RESULT_VARIABLE GIT_DIRTY_RESULT
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT GIT_DIRTY_RESULT EQUAL 0)
|
||||
string(APPEND KYTY_GIT_HASH "-dirty")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${INPUT_FILE}" "${OUTPUT_FILE}")
|
||||
@@ -15,6 +15,13 @@ bool TestWaitRegMemValue(uint64_t value, uint64_t ref, uint64_t mask, uint32_t f
|
||||
|
||||
enum class Pm4ProcessResult { Complete, Blocked };
|
||||
|
||||
enum class ContextStateOperation : uint32_t {
|
||||
Clear = 0,
|
||||
Push = 1,
|
||||
Pop = 2,
|
||||
PushClear = 3,
|
||||
};
|
||||
|
||||
class Pm4Execution {
|
||||
public:
|
||||
[[nodiscard]] bool MadeProgress() const noexcept { return m_made_progress; }
|
||||
@@ -49,24 +56,12 @@ public:
|
||||
KYTY_CLASS_NO_COPY(CommandProcessor);
|
||||
|
||||
void Reset();
|
||||
void ApplyContextStateOperation(ContextStateOperation operation);
|
||||
|
||||
void BufferInit();
|
||||
void BufferFlush();
|
||||
void BufferFlushAndWait();
|
||||
void BufferWait();
|
||||
void BeginReadbackTransaction() {
|
||||
if (m_readback_active) {
|
||||
EXIT("nested command-processor readback transaction\n");
|
||||
}
|
||||
m_readback_active = true;
|
||||
}
|
||||
void EndReadbackTransaction() {
|
||||
if (!m_readback_active) {
|
||||
EXIT("command-processor readback transaction is not active\n");
|
||||
}
|
||||
m_readback_active = false;
|
||||
}
|
||||
|
||||
void BufferInit();
|
||||
void BufferFlush();
|
||||
void BufferFlushAndWait();
|
||||
void BufferWait();
|
||||
HW::Context& GetCtx() { return m_ctx; }
|
||||
HW::UserConfig& GetUcfg() { return m_ucfg; }
|
||||
HW::Shader& GetShCtx() { return m_sh_ctx; }
|
||||
@@ -162,6 +157,8 @@ private:
|
||||
|
||||
RenderContext& m_renderer;
|
||||
HW::Context m_ctx;
|
||||
HW::Context m_saved_ctx;
|
||||
bool m_context_state_pushed = false;
|
||||
HW::UserConfig m_ucfg;
|
||||
HW::Shader m_sh_ctx;
|
||||
HW::UserSgprType m_user_data_marker = HW::UserSgprType::Unknown;
|
||||
@@ -173,10 +170,9 @@ private:
|
||||
// Persistent draw state: indirect draws update it for subsequent draws.
|
||||
uint32_t m_num_instances = 1;
|
||||
|
||||
uint32_t m_de_count = 0;
|
||||
uint32_t m_ce_count = 0;
|
||||
bool m_ce_complete = false;
|
||||
bool m_readback_active = false;
|
||||
uint32_t m_de_count = 0;
|
||||
uint32_t m_ce_count = 0;
|
||||
bool m_ce_complete = false;
|
||||
|
||||
uint32_t m_const_ram[0x3000] = {0};
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ constexpr auto MakeCustomOpcodeDispatchTable() {
|
||||
g_cp_op_custom_func[Pm4::R_WAIT_MEM_64] = CpOpWaitRegMem64;
|
||||
g_cp_op_custom_func[Pm4::R_FLIP] = CpOpFlip;
|
||||
g_cp_op_custom_func[Pm4::R_RELEASE_MEM] = CpOpReleaseMem;
|
||||
g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState;
|
||||
|
||||
return g_cp_op_custom_func;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ uint32_t HwUcSetGeIndexOffset(CommandProcessor&, uint32_t, uint32_t, const uint3
|
||||
uint32_t HwUcSetGdsOaRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
|
||||
uint32_t CpOpNop(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpContextState(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpSetBase(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpClearState(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpIndexBase(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#define KYTY_HW_CTX_PARSER_ARGS \
|
||||
@@ -101,6 +103,128 @@ void LogUnknownReleaseMemGcr(uint32_t gcr_cntl) {
|
||||
|
||||
} // namespace
|
||||
|
||||
static HW::BlendControl DecodeBlendControl(uint32_t value) {
|
||||
HW::BlendControl r {};
|
||||
|
||||
r.color_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_SRCBLEND);
|
||||
r.color_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_COMB_FCN);
|
||||
r.color_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_DESTBLEND);
|
||||
r.alpha_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_SRCBLEND);
|
||||
r.alpha_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_COMB_FCN);
|
||||
r.alpha_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_DESTBLEND);
|
||||
r.separate_alpha_blend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, SEPARATE_ALPHA_BLEND) != 0;
|
||||
r.enable = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ENABLE) != 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::ClipControl DecodeClipControl(uint32_t value) {
|
||||
HW::ClipControl r {};
|
||||
|
||||
r.user_clip_planes = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_ENA);
|
||||
r.user_clip_plane_mode = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_MODE);
|
||||
r.dx_clip_space = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
|
||||
r.vertex_kill_any = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
|
||||
r.min_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
|
||||
r.max_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
|
||||
r.user_clip_plane_negate_y = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
|
||||
r.clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
|
||||
r.user_clip_plane_cull_only = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
|
||||
r.cull_on_clipping_error_disable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
|
||||
r.linear_attribute_clip_enable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
|
||||
r.force_viewport_index_from_vs_enable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::ColorControl DecodeColorControl(uint32_t value) {
|
||||
HW::ColorControl r {};
|
||||
r.mode = KYTY_PM4_GET(value, CB_COLOR_CONTROL, MODE);
|
||||
r.op = KYTY_PM4_GET(value, CB_COLOR_CONTROL, ROP3);
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::DepthControl DecodeDepthControl(uint32_t value) {
|
||||
HW::DepthControl r {};
|
||||
|
||||
r.stencil_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
|
||||
r.z_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_ENABLE) != 0;
|
||||
r.z_write_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
|
||||
r.depth_bounds_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
|
||||
r.zfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ZFUNC);
|
||||
r.backface_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
|
||||
r.stencilfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC);
|
||||
r.stencilfunc_bf = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC_BF);
|
||||
r.color_writes_on_depth_fail_enable =
|
||||
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
|
||||
r.color_writes_on_depth_pass_disable =
|
||||
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::ModeControl DecodeModeControl(uint32_t value) {
|
||||
HW::ModeControl r {};
|
||||
|
||||
r.cull_front = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
|
||||
r.cull_back = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
|
||||
r.face = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, FACE) != 0;
|
||||
r.poly_mode = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_MODE);
|
||||
r.polymode_front_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
|
||||
r.polymode_back_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
|
||||
r.poly_offset_front_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
|
||||
r.poly_offset_back_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
|
||||
r.vtx_window_offset_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
|
||||
r.provoking_vtx_last = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
|
||||
r.persp_corr_dis = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::RenderControl DecodeRenderControl(uint32_t value) {
|
||||
HW::RenderControl r {};
|
||||
|
||||
r.depth_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
|
||||
r.stencil_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
|
||||
r.resummarize_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
|
||||
r.stencil_compress_disable =
|
||||
KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
|
||||
r.depth_compress_disable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
|
||||
r.copy_centroid = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_CENTROID) != 0;
|
||||
r.copy_sample = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_SAMPLE);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::ScanModeControl DecodeScanModeControl(uint32_t value) {
|
||||
HW::ScanModeControl r {};
|
||||
|
||||
r.msaa_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
|
||||
r.vport_scissor_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
|
||||
r.line_stipple_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::StencilControl DecodeStencilControl(uint32_t value) {
|
||||
HW::StencilControl r {};
|
||||
|
||||
r.stencil_fail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL);
|
||||
r.stencil_zpass = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS);
|
||||
r.stencil_zfail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL);
|
||||
r.stencil_fail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL_BF);
|
||||
r.stencil_zpass_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS_BF);
|
||||
r.stencil_zfail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL_BF);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static HW::AaConfig ParseAaConfig(uint32_t value) {
|
||||
HW::AaConfig r;
|
||||
|
||||
@@ -181,6 +305,10 @@ static void HwCtxIgnoreAaMaskRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
|
||||
static void HwCtxIgnoreAlphaToMaskRegister([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
static void HwCtxIgnoreDisabledUserClipPlane(CommandProcessor& cp, uint32_t value) {
|
||||
EXIT_NOT_IMPLEMENTED(value != 0 || cp.GetCtx().GetClipControl().user_clip_planes != 0);
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreDrawPayloadControl([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
static void HwCtxIgnoreObjprimIdControl([[maybe_unused]] uint32_t value) {}
|
||||
@@ -423,26 +551,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetBlendControl) {
|
||||
|
||||
uint32_t param = (cmd_offset - Pm4::CB_BLEND0_CONTROL) / 1;
|
||||
|
||||
HW::BlendControl r;
|
||||
|
||||
r.color_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK;
|
||||
r.color_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_COLOR_COMB_FCN_MASK;
|
||||
r.color_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_COLOR_DESTBLEND_MASK;
|
||||
r.alpha_srcblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_ALPHA_SRCBLEND_MASK;
|
||||
r.alpha_comb_fcn = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_ALPHA_COMB_FCN_MASK;
|
||||
r.alpha_destblend = (buffer[0] >> Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_ALPHA_DESTBLEND_MASK;
|
||||
r.separate_alpha_blend = ((buffer[0] >> Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_MASK) != 0;
|
||||
r.enable = ((buffer[0] >> Pm4::CB_BLEND0_CONTROL_ENABLE_SHIFT) &
|
||||
Pm4::CB_BLEND0_CONTROL_ENABLE_MASK) != 0;
|
||||
|
||||
cp.GetCtx().SetBlendControl(param, r);
|
||||
cp.GetCtx().SetBlendControl(param, DecodeBlendControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -451,38 +560,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetClipControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_CL_CLIP_CNTL);
|
||||
|
||||
HW::ClipControl r;
|
||||
|
||||
// r.user_clip_planes = buffer[0] & 0x3fu;
|
||||
// r.user_clip_plane_mode = (buffer[0] >> 14u) & 0x3u;
|
||||
// r.clip_space = (buffer[0] >> 19u) & 0x1u;
|
||||
// r.vertex_kill_mode = (buffer[0] >> 21u) & 0x1u;
|
||||
// r.min_z_clip_enable = (buffer[0] >> 26u) & 0x1u;
|
||||
// r.max_z_clip_enable = (buffer[0] >> 27u) & 0x1u;
|
||||
// r.user_clip_plane_negate_y = (buffer[0] & 0x00002000u) != 0;
|
||||
// r.clip_enable = (buffer[0] & 0x00010000u) != 0;
|
||||
// r.user_clip_plane_cull_only = (buffer[0] & 0x00020000u) != 0;
|
||||
// r.cull_on_clipping_error_disable = (buffer[0] & 0x00100000u) != 0;
|
||||
// r.linear_attribute_clip_enable = (buffer[0] & 0x01000000u) != 0;
|
||||
// r.force_viewport_index_from_vs_enable = (buffer[0] & 0x02000000u) != 0;
|
||||
|
||||
r.user_clip_planes = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, UCP_ENA);
|
||||
r.user_clip_plane_mode = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, PS_UCP_MODE);
|
||||
r.dx_clip_space = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
|
||||
r.vertex_kill_any = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
|
||||
r.min_z_clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
|
||||
r.max_z_clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
|
||||
r.user_clip_plane_negate_y = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
|
||||
r.clip_disable = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
|
||||
r.user_clip_plane_cull_only = KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
|
||||
r.cull_on_clipping_error_disable =
|
||||
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
|
||||
r.linear_attribute_clip_enable =
|
||||
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
|
||||
r.force_viewport_index_from_vs_enable =
|
||||
KYTY_PM4_GET(buffer[0], PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
|
||||
|
||||
cp.GetCtx().SetClipControl(r);
|
||||
cp.GetCtx().SetClipControl(DecodeClipControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -491,12 +569,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetColorControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::CB_COLOR_CONTROL);
|
||||
|
||||
HW::ColorControl r;
|
||||
|
||||
r.mode = KYTY_PM4_GET(buffer[0], CB_COLOR_CONTROL, MODE);
|
||||
r.op = KYTY_PM4_GET(buffer[0], CB_COLOR_CONTROL, ROP3);
|
||||
|
||||
cp.GetCtx().SetColorControl(r);
|
||||
cp.GetCtx().SetColorControl(DecodeColorControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -520,9 +593,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetColorInfo) {
|
||||
// r.format = (buffer[4] >> 2u) & 0x1fu;
|
||||
// r.channel_type = (buffer[4] >> 8u) & 0x7u;
|
||||
// r.channel_order = (buffer[4] >> 11u) & 0x3u;
|
||||
r.format = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FORMAT);
|
||||
r.channel_type = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, NUMBER_TYPE);
|
||||
r.channel_order = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMP_SWAP);
|
||||
r.format =
|
||||
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FORMAT));
|
||||
r.channel_type =
|
||||
static_cast<Prospero::ChannelType>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, NUMBER_TYPE));
|
||||
r.channel_order =
|
||||
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMP_SWAP));
|
||||
r.cmask_fast_clear_enable = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, FAST_CLEAR) != 0;
|
||||
r.fmask_compression_enable = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, COMPRESSION) != 0;
|
||||
r.blend_clamp = KYTY_PM4_GET(buffer[0], CB_COLOR0_INFO, BLEND_CLAMP) != 0;
|
||||
@@ -554,22 +630,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetDepthControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_DEPTH_CONTROL);
|
||||
|
||||
HW::DepthControl r;
|
||||
|
||||
r.stencil_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
|
||||
r.z_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_ENABLE) != 0;
|
||||
r.z_write_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
|
||||
r.depth_bounds_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
|
||||
r.zfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, ZFUNC);
|
||||
r.backface_enable = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
|
||||
r.stencilfunc = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC);
|
||||
r.stencilfunc_bf = KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, STENCILFUNC_BF);
|
||||
r.color_writes_on_depth_fail_enable =
|
||||
KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
|
||||
r.color_writes_on_depth_pass_disable =
|
||||
KYTY_PM4_GET(buffer[0], DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
|
||||
|
||||
cp.GetCtx().SetDepthControl(r);
|
||||
cp.GetCtx().SetDepthControl(DecodeDepthControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -873,45 +934,49 @@ KYTY_HW_CTX_PARSER(HwCtxSetModeControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_SU_SC_MODE_CNTL);
|
||||
|
||||
HW::ModeControl r;
|
||||
|
||||
r.cull_front = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
|
||||
r.cull_back = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
|
||||
r.face = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, FACE) != 0;
|
||||
r.poly_mode = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_MODE);
|
||||
r.polymode_front_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
|
||||
r.polymode_back_ptype = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
|
||||
r.poly_offset_front_enable =
|
||||
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
|
||||
r.poly_offset_back_enable =
|
||||
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
|
||||
r.vtx_window_offset_enable =
|
||||
KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
|
||||
r.provoking_vtx_last = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
|
||||
r.persp_corr_dis = KYTY_PM4_GET(buffer[0], PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
|
||||
|
||||
cp.GetCtx().SetModeControl(r);
|
||||
cp.GetCtx().SetModeControl(DecodeModeControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void HwCtxIgnorePolyOffsetRegister(uint32_t cmd_offset, uint32_t value) {
|
||||
static std::atomic<uint32_t> log_count = 0;
|
||||
static void HwCtxSetPolyOffsetRegister(CommandProcessor& cp, uint32_t cmd_offset, uint32_t value) {
|
||||
HW::PolyOffset offset = cp.GetCtx().GetPolyOffset();
|
||||
|
||||
auto count = log_count.fetch_add(1);
|
||||
if (count < 8) {
|
||||
LOGF_COLOR(Log::Color::Red,
|
||||
"\t temporary: ignoring polygon offset context register 0x%03" PRIx32
|
||||
" = 0x%08" PRIx32 " (depth bias not implemented)\n",
|
||||
cmd_offset, value);
|
||||
switch (cmd_offset) {
|
||||
case Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL: {
|
||||
const auto neg_bits =
|
||||
KYTY_PM4_GET(value, PA_SU_POLY_OFFSET_DB_FMT_CNTL, NEG_NUM_DB_BITS);
|
||||
offset.neg_num_db_bits = std::bit_cast<int8_t>(static_cast<uint8_t>(neg_bits));
|
||||
offset.db_is_float_fmt =
|
||||
KYTY_PM4_GET(value, PA_SU_POLY_OFFSET_DB_FMT_CNTL, DB_IS_FLOAT_FMT) != 0;
|
||||
break;
|
||||
}
|
||||
case Pm4::PA_SU_POLY_OFFSET_CLAMP: offset.clamp = std::bit_cast<float>(value); break;
|
||||
case Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE:
|
||||
offset.front_scale = std::bit_cast<float>(value);
|
||||
break;
|
||||
case Pm4::PA_SU_POLY_OFFSET_FRONT_OFFSET:
|
||||
offset.front_offset = std::bit_cast<float>(value);
|
||||
break;
|
||||
case Pm4::PA_SU_POLY_OFFSET_BACK_SCALE:
|
||||
offset.back_scale = std::bit_cast<float>(value);
|
||||
break;
|
||||
case Pm4::PA_SU_POLY_OFFSET_BACK_OFFSET:
|
||||
offset.back_offset = std::bit_cast<float>(value);
|
||||
break;
|
||||
default:
|
||||
EXIT("unknown polygon offset context register 0x%03" PRIx32 " = 0x%08" PRIx32 "\n",
|
||||
cmd_offset, value);
|
||||
}
|
||||
|
||||
cp.GetCtx().SetPolyOffset(offset);
|
||||
}
|
||||
|
||||
KYTY_HW_CTX_PARSER(HwCtxSetPolyOffsetRegisters) {
|
||||
auto num_values = KYTY_PM4_LEN(cmd_id) - 2u;
|
||||
|
||||
for (uint32_t i = 0; i < num_values; i++) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset + i, buffer[i]);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset + i, buffer[i]);
|
||||
}
|
||||
|
||||
return num_values;
|
||||
@@ -935,19 +1000,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_RENDER_CONTROL);
|
||||
|
||||
HW::RenderControl r;
|
||||
|
||||
r.depth_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
|
||||
r.stencil_clear_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
|
||||
r.resummarize_enable = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
|
||||
r.stencil_compress_disable =
|
||||
KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
|
||||
r.depth_compress_disable =
|
||||
KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
|
||||
r.copy_centroid = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_CENTROID) != 0;
|
||||
r.copy_sample = KYTY_PM4_GET(buffer[0], DB_RENDER_CONTROL, COPY_SAMPLE);
|
||||
|
||||
cp.GetCtx().SetRenderControl(r);
|
||||
cp.GetCtx().SetRenderControl(DecodeRenderControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1015,9 +1068,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderTarget) {
|
||||
// info.format = (buffer[4] >> 2u) & 0x1fu;
|
||||
// info.channel_type = (buffer[4] >> 8u) & 0x7u;
|
||||
// info.channel_order = (buffer[4] >> 11u) & 0x3u;
|
||||
info.format = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FORMAT);
|
||||
info.channel_type = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, NUMBER_TYPE);
|
||||
info.channel_order = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMP_SWAP);
|
||||
info.format =
|
||||
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FORMAT));
|
||||
info.channel_type =
|
||||
static_cast<Prospero::ChannelType>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, NUMBER_TYPE));
|
||||
info.channel_order =
|
||||
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMP_SWAP));
|
||||
info.cmask_fast_clear_enable = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, FAST_CLEAR) != 0;
|
||||
info.fmask_compression_enable = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, COMPRESSION) != 0;
|
||||
info.blend_clamp = KYTY_PM4_GET(buffer[4], CB_COLOR0_INFO, BLEND_CLAMP) != 0;
|
||||
@@ -1039,10 +1095,12 @@ KYTY_HW_CTX_PARSER(HwCtxSetRenderTarget) {
|
||||
// attrib.num_fragments = (buffer[5] >> 15u) & 0x3u;
|
||||
attrib.force_dest_alpha_to_one =
|
||||
KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FORCE_DST_ALPHA_1) != 0;
|
||||
attrib.tile_mode = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, TILE_MODE_INDEX);
|
||||
attrib.fmask_tile_mode = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX);
|
||||
attrib.num_samples = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_SAMPLES);
|
||||
attrib.num_fragments = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
|
||||
attrib.tile_mode =
|
||||
static_cast<Prospero::TileMode>(KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, TILE_MODE_INDEX));
|
||||
attrib.fmask_tile_mode = static_cast<Prospero::TileMode>(
|
||||
KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX));
|
||||
attrib.num_samples = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_SAMPLES);
|
||||
attrib.num_fragments = KYTY_PM4_GET(buffer[5], CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
|
||||
|
||||
// dcc.max_uncompressed_block_size = (buffer[6] >> 2u) & 0x3u;
|
||||
// dcc.max_compressed_block_size = (buffer[6] >> 5u) & 0x3u;
|
||||
@@ -1135,13 +1193,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetScanModeControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::PA_SC_MODE_CNTL_0);
|
||||
|
||||
HW::ScanModeControl r;
|
||||
|
||||
r.msaa_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
|
||||
r.vport_scissor_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
|
||||
r.line_stipple_enable = KYTY_PM4_GET(buffer[0], PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
|
||||
|
||||
cp.GetCtx().SetScanModeControl(r);
|
||||
cp.GetCtx().SetScanModeControl(DecodeScanModeControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1181,16 +1233,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetStencilControl) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0016900);
|
||||
EXIT_NOT_IMPLEMENTED(cmd_offset != Pm4::DB_STENCIL_CONTROL);
|
||||
|
||||
HW::StencilControl r;
|
||||
|
||||
r.stencil_fail = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILFAIL);
|
||||
r.stencil_zpass = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZPASS);
|
||||
r.stencil_zfail = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZFAIL);
|
||||
r.stencil_fail_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILFAIL_BF);
|
||||
r.stencil_zpass_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZPASS_BF);
|
||||
r.stencil_zfail_bf = KYTY_PM4_GET(buffer[0], DB_STENCIL_CONTROL, STENCILZFAIL_BF);
|
||||
|
||||
cp.GetCtx().SetStencilControl(r);
|
||||
cp.GetCtx().SetStencilControl(DecodeStencilControl(buffer[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1312,26 +1355,36 @@ static void HwShSetCsRegister(CommandProcessor& cp, uint32_t cmd_offset, uint32_
|
||||
(value >> Pm4::COMPUTE_PGM_RSRC1_VGPRS_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_VGPRS_MASK;
|
||||
cs_regs.sgprs =
|
||||
(value >> Pm4::COMPUTE_PGM_RSRC1_SGPRS_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_SGPRS_MASK;
|
||||
cs_regs.bulky =
|
||||
(value >> Pm4::COMPUTE_PGM_RSRC1_BULKY_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_BULKY_MASK;
|
||||
cs_regs.wave_size = (((value >> Pm4::COMPUTE_PGM_RSRC1_W32_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_W32_EN_MASK) != 0u)
|
||||
? 32u
|
||||
: 64u;
|
||||
cs_regs.float_mode = (value >> Pm4::COMPUTE_PGM_RSRC1_FLOAT_MODE_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_FLOAT_MODE_MASK;
|
||||
cs_regs.dx10_clamp = ((value >> Pm4::COMPUTE_PGM_RSRC1_DX10_CLAMP_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_DX10_CLAMP_MASK) != 0u;
|
||||
cs_regs.ieee_mode = ((value >> Pm4::COMPUTE_PGM_RSRC1_IEEE_MODE_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_IEEE_MODE_MASK) != 0u;
|
||||
cs_regs.fp16_overflow = ((value >> Pm4::COMPUTE_PGM_RSRC1_FP16_OVFL_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_FP16_OVFL_MASK) != 0u;
|
||||
cs_regs.bulky = ((value >> Pm4::COMPUTE_PGM_RSRC1_BULKY_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_BULKY_MASK) != 0u;
|
||||
cs_regs.threadgroup_configuration = ((value >> Pm4::COMPUTE_PGM_RSRC1_WGP_MODE_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_WGP_MODE_MASK) != 0u;
|
||||
cs_regs.wave_size = (((value >> Pm4::COMPUTE_PGM_RSRC1_W32_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC1_W32_EN_MASK) != 0u)
|
||||
? 32u
|
||||
: 64u;
|
||||
break;
|
||||
case Pm4::COMPUTE_PGM_RSRC2:
|
||||
cs_regs.scratch_en = (value >> Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_MASK;
|
||||
cs_regs.scratch_en = ((value >> Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_SCRATCH_EN_MASK) != 0u;
|
||||
cs_regs.user_sgpr = (value >> Pm4::COMPUTE_PGM_RSRC2_USER_SGPR_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_USER_SGPR_MASK;
|
||||
cs_regs.tgid_x_en = (value >> Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_MASK;
|
||||
cs_regs.tgid_y_en = (value >> Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_MASK;
|
||||
cs_regs.tgid_z_en = (value >> Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_MASK;
|
||||
cs_regs.tg_size_en = (value >> Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_MASK;
|
||||
cs_regs.tgid_x_en = ((value >> Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_X_EN_MASK) != 0u;
|
||||
cs_regs.tgid_y_en = ((value >> Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_Y_EN_MASK) != 0u;
|
||||
cs_regs.tgid_z_en = ((value >> Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TGID_Z_EN_MASK) != 0u;
|
||||
cs_regs.tg_size_en = ((value >> Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TG_SIZE_EN_MASK) != 0u;
|
||||
cs_regs.tidig_comp_cnt = (value >> Pm4::COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_SHIFT) &
|
||||
Pm4::COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_MASK;
|
||||
cs_regs.lds_size = (value >> Pm4::COMPUTE_PGM_RSRC2_LDS_SIZE_SHIFT) &
|
||||
@@ -1531,7 +1584,7 @@ KYTY_HW_UC_PARSER(HwUcSetPrimitiveType) {
|
||||
|
||||
uint32_t prim_type = KYTY_PM4_GET(buffer[0], VGT_PRIMITIVE_TYPE, PRIM_TYPE);
|
||||
|
||||
cp.GetUcfg().SetPrimitiveType(prim_type);
|
||||
cp.GetUcfg().SetPrimitiveType(static_cast<Prospero::PrimitiveType>(prim_type));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -2102,11 +2155,24 @@ KYTY_CP_OP_PARSER(CpOpClearState) {
|
||||
EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0001200);
|
||||
EXIT_NOT_IMPLEMENTED((buffer[0] & ~0xfu) != 0);
|
||||
|
||||
cp.Reset();
|
||||
cp.ApplyContextStateOperation(ContextStateOperation::Clear);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
KYTY_CP_OP_PARSER(CpOpContextState) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
const auto packet_size_dw = KYTY_PM4_LEN(cmd_id);
|
||||
EXIT_NOT_IMPLEMENTED(KYTY_PM4_R(cmd_id) != Pm4::R_CONTEXT_STATE);
|
||||
EXIT_NOT_IMPLEMENTED(packet_size_dw != 3u && packet_size_dw != 5u);
|
||||
EXIT_NOT_IMPLEMENTED(buffer[0] > static_cast<uint32_t>(ContextStateOperation::PushClear));
|
||||
|
||||
cp.ApplyContextStateOperation(static_cast<ContextStateOperation>(buffer[0]));
|
||||
|
||||
return packet_size_dw - 1u;
|
||||
}
|
||||
|
||||
KYTY_CP_OP_PARSER(CpOpDumpConstRam) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
@@ -2636,14 +2702,21 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
const bool gl2_writeback = ((gcr_cntl & GcrGl2Writeback) != 0);
|
||||
|
||||
auto trigger_interrupt = [&]() {
|
||||
bool queued = false;
|
||||
switch (interrupt_selector) {
|
||||
case 0x00:
|
||||
case 0x03: break;
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x04: cp.TriggerEopEventAtEndOfPipe(interrupt_context_id); break;
|
||||
case 0x04:
|
||||
cp.TriggerEopEventAtEndOfPipe(interrupt_context_id);
|
||||
queued = true;
|
||||
break;
|
||||
default: EXIT("unknown release_mem interrupt selector\n");
|
||||
}
|
||||
if (queued) {
|
||||
cp.BufferFlush();
|
||||
}
|
||||
};
|
||||
|
||||
if (data_sel == 0 || interrupt_selector == 4) {
|
||||
@@ -2651,10 +2724,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
cp.EmitGlobalBarrier();
|
||||
}
|
||||
|
||||
if (gl2_writeback) {
|
||||
cp.SynchronizeGpu();
|
||||
}
|
||||
|
||||
trigger_interrupt();
|
||||
|
||||
return 7;
|
||||
@@ -2665,10 +2734,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
cp.EmitGlobalBarrier();
|
||||
}
|
||||
|
||||
if (gl2_writeback) {
|
||||
cp.SynchronizeGpu();
|
||||
}
|
||||
|
||||
trigger_interrupt();
|
||||
|
||||
return 7;
|
||||
@@ -2682,17 +2747,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
cache_policy = 0;
|
||||
|
||||
if (data_sel == 1) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
const auto dst_addr = reinterpret_cast<uint64_t>(dst_gpu_addr);
|
||||
if ((dst_addr & 0xffffffffff000000ull) == 0x0000001021000000ull || log_count.load() < 64) {
|
||||
if (log_count.fetch_add(1) < 256) {
|
||||
LOGF(
|
||||
"\t runtime release_mem32: dst=0x%016" PRIx64 ", value=0x%08" PRIx32
|
||||
", action=0x%02" PRIx32 ", gcr=0x%04" PRIx32 ", cache_action=0x%08" PRIx32 "\n",
|
||||
dst_addr, static_cast<uint32_t>(value), eop_event_type, gcr_cntl, cache_action);
|
||||
}
|
||||
}
|
||||
|
||||
eop_event_type = 0x2f;
|
||||
event_index = 6;
|
||||
auto event_source = 2u;
|
||||
@@ -2707,7 +2761,6 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
|
||||
if (data_sel == 5) {
|
||||
if (gl2_writeback) {
|
||||
cp.SynchronizeGpu();
|
||||
cache_action = 0;
|
||||
}
|
||||
|
||||
@@ -2718,7 +2771,9 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) {
|
||||
cp.WriteAtEndOfPipe32(cache_policy, event_write_dest, eop_event_type, cache_action,
|
||||
event_index, event_source, dst_gpu_addr, static_cast<uint32_t>(value),
|
||||
interrupt_selector, interrupt_context_id);
|
||||
cp.BufferFlush();
|
||||
if (interrupt_selector == 0x01) {
|
||||
cp.BufferFlush();
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
@@ -3067,9 +3122,12 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
uint32_t slot = (cmd_offset - Pm4::CB_COLOR0_INFO) / 15;
|
||||
HW::ColorInfo info;
|
||||
info.format = KYTY_PM4_GET(value, CB_COLOR0_INFO, FORMAT);
|
||||
info.channel_type = KYTY_PM4_GET(value, CB_COLOR0_INFO, NUMBER_TYPE);
|
||||
info.channel_order = KYTY_PM4_GET(value, CB_COLOR0_INFO, COMP_SWAP);
|
||||
info.format =
|
||||
static_cast<Prospero::ChannelLayout>(KYTY_PM4_GET(value, CB_COLOR0_INFO, FORMAT));
|
||||
info.channel_type = static_cast<Prospero::ChannelType>(
|
||||
KYTY_PM4_GET(value, CB_COLOR0_INFO, NUMBER_TYPE));
|
||||
info.channel_order =
|
||||
static_cast<Prospero::ChannelOrder>(KYTY_PM4_GET(value, CB_COLOR0_INFO, COMP_SWAP));
|
||||
info.cmask_fast_clear_enable = KYTY_PM4_GET(value, CB_COLOR0_INFO, FAST_CLEAR) != 0;
|
||||
info.fmask_compression_enable = KYTY_PM4_GET(value, CB_COLOR0_INFO, COMPRESSION) != 0;
|
||||
info.blend_clamp = KYTY_PM4_GET(value, CB_COLOR0_INFO, BLEND_CLAMP) != 0;
|
||||
@@ -3094,10 +3152,12 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
HW::ColorAttrib attrib;
|
||||
attrib.force_dest_alpha_to_one =
|
||||
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FORCE_DST_ALPHA_1) != 0;
|
||||
attrib.tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, TILE_MODE_INDEX);
|
||||
attrib.fmask_tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX);
|
||||
attrib.num_samples = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_SAMPLES);
|
||||
attrib.num_fragments = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
|
||||
attrib.tile_mode = static_cast<Prospero::TileMode>(
|
||||
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, TILE_MODE_INDEX));
|
||||
attrib.fmask_tile_mode = static_cast<Prospero::TileMode>(
|
||||
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, FMASK_TILE_MODE_INDEX));
|
||||
attrib.num_samples = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_SAMPLES);
|
||||
attrib.num_fragments = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB, NUM_FRAGMENTS);
|
||||
cp.GetCtx().SetColorAttrib(slot, attrib);
|
||||
};
|
||||
}
|
||||
@@ -3251,8 +3311,9 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
uint32_t slot = (cmd_offset - Pm4::CB_COLOR0_ATTRIB3);
|
||||
HW::ColorAttrib3 attrib3;
|
||||
attrib3.depth = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, MIP0_DEPTH);
|
||||
attrib3.tile_mode = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, COLOR_SW_MODE);
|
||||
attrib3.depth = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, MIP0_DEPTH);
|
||||
attrib3.tile_mode = static_cast<Prospero::TileMode>(
|
||||
KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, COLOR_SW_MODE));
|
||||
attrib3.dimension = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, RESOURCE_TYPE);
|
||||
attrib3.cmask_pipe_aligned = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, CMASK_PIPE_ALIGNED);
|
||||
attrib3.dcc_pipe_aligned = KYTY_PM4_GET(value, CB_COLOR0_ATTRIB3, DCC_PIPE_ALIGNED);
|
||||
@@ -3308,6 +3369,12 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
};
|
||||
}
|
||||
|
||||
for (auto cmd_offset = Pm4::PA_CL_UCP_0_X; cmd_offset <= Pm4::PA_CL_UCP_5_W; cmd_offset++) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDisabledUserClipPlane(cp, value);
|
||||
};
|
||||
}
|
||||
|
||||
for (auto cmd_offset = Pm4::PA_SC_VPORT_SCISSOR_0_TL;
|
||||
cmd_offset <= Pm4::PA_SC_VPORT_SCISSOR_15_TL; cmd_offset += 2) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -3646,58 +3713,31 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
for (uint32_t slot = 0; slot < 8; slot++) {
|
||||
g_hw_ctx_indirect_func[Pm4::CB_BLEND0_CONTROL + slot] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
uint32_t param = cmd_offset - Pm4::CB_BLEND0_CONTROL;
|
||||
|
||||
HW::BlendControl r;
|
||||
r.color_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_SRCBLEND);
|
||||
r.color_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_COMB_FCN);
|
||||
r.color_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, COLOR_DESTBLEND);
|
||||
r.alpha_srcblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_SRCBLEND);
|
||||
r.alpha_comb_fcn = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_COMB_FCN);
|
||||
r.alpha_destblend = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ALPHA_DESTBLEND);
|
||||
r.separate_alpha_blend =
|
||||
KYTY_PM4_GET(value, CB_BLEND0_CONTROL, SEPARATE_ALPHA_BLEND) != 0;
|
||||
r.enable = KYTY_PM4_GET(value, CB_BLEND0_CONTROL, ENABLE) != 0;
|
||||
|
||||
cp.GetCtx().SetBlendControl(param, r);
|
||||
cp.GetCtx().SetBlendControl(param, DecodeBlendControl(value));
|
||||
};
|
||||
}
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_SC_MODE_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::ModeControl r;
|
||||
r.cull_front = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_FRONT) != 0;
|
||||
r.cull_back = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, CULL_BACK) != 0;
|
||||
r.face = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, FACE) != 0;
|
||||
r.poly_mode = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_MODE);
|
||||
r.polymode_front_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_FRONT_PTYPE);
|
||||
r.polymode_back_ptype = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLYMODE_BACK_PTYPE);
|
||||
r.poly_offset_front_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_FRONT_ENABLE) != 0;
|
||||
r.poly_offset_back_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, POLY_OFFSET_BACK_ENABLE) != 0;
|
||||
r.vtx_window_offset_enable =
|
||||
KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, VTX_WINDOW_OFFSET_ENABLE) != 0;
|
||||
r.provoking_vtx_last = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PROVOKING_VTX_LAST) != 0;
|
||||
r.persp_corr_dis = KYTY_PM4_GET(value, PA_SU_SC_MODE_CNTL, PERSP_CORR_DIS) != 0;
|
||||
cp.GetCtx().SetModeControl(r);
|
||||
cp.GetCtx().SetModeControl(DecodeModeControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_DB_FMT_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_CLAMP] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_FRONT_SCALE] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_FRONT_OFFSET] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_BACK_SCALE] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_POLY_OFFSET_BACK_OFFSET] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnorePolyOffsetRegister(cmd_offset, value);
|
||||
HwCtxSetPolyOffsetRegister(cp, cmd_offset, value);
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_Z_INFO] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -3846,28 +3886,11 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_STENCIL_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::StencilControl r;
|
||||
r.stencil_fail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL);
|
||||
r.stencil_zpass = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS);
|
||||
r.stencil_zfail = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL);
|
||||
r.stencil_fail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILFAIL_BF);
|
||||
r.stencil_zpass_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZPASS_BF);
|
||||
r.stencil_zfail_bf = KYTY_PM4_GET(value, DB_STENCIL_CONTROL, STENCILZFAIL_BF);
|
||||
cp.GetCtx().SetStencilControl(r);
|
||||
cp.GetCtx().SetStencilControl(DecodeStencilControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RENDER_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::RenderControl r;
|
||||
r.depth_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_CLEAR_ENABLE) != 0;
|
||||
r.stencil_clear_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_CLEAR_ENABLE) != 0;
|
||||
r.resummarize_enable = KYTY_PM4_GET(value, DB_RENDER_CONTROL, RESUMMARIZE_ENABLE) != 0;
|
||||
r.stencil_compress_disable =
|
||||
KYTY_PM4_GET(value, DB_RENDER_CONTROL, STENCIL_COMPRESS_DISABLE) != 0;
|
||||
r.depth_compress_disable =
|
||||
KYTY_PM4_GET(value, DB_RENDER_CONTROL, DEPTH_COMPRESS_DISABLE) != 0;
|
||||
r.copy_centroid = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_CENTROID) != 0;
|
||||
r.copy_sample = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_SAMPLE);
|
||||
cp.GetCtx().SetRenderControl(r);
|
||||
cp.GetCtx().SetRenderControl(DecodeRenderControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_STENCILREFMASK] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -3889,23 +3912,7 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::PA_CL_CLIP_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::ClipControl r;
|
||||
r.user_clip_planes = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_ENA);
|
||||
r.user_clip_plane_mode = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_MODE);
|
||||
r.dx_clip_space = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_CLIP_SPACE_DEF) != 0;
|
||||
r.vertex_kill_any = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTX_KILL_OR) != 0;
|
||||
r.min_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_NEAR_DISABLE) != 0;
|
||||
r.max_z_clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, ZCLIP_FAR_DISABLE) != 0;
|
||||
r.user_clip_plane_negate_y = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, PS_UCP_Y_SCALE_NEG) != 0;
|
||||
r.clip_disable = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, CLIP_DISABLE) != 0;
|
||||
r.user_clip_plane_cull_only = KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, UCP_CULL_ONLY_ENA) != 0;
|
||||
r.cull_on_clipping_error_disable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DIS_CLIP_ERR_DETECT) != 0;
|
||||
r.linear_attribute_clip_enable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, DX_LINEAR_ATTR_CLIP_ENA) != 0;
|
||||
r.force_viewport_index_from_vs_enable =
|
||||
KYTY_PM4_GET(value, PA_CL_CLIP_CNTL, VTE_VPORT_PROVOKE_DISABLE) != 0;
|
||||
cp.GetCtx().SetClipControl(r);
|
||||
cp.GetCtx().SetClipControl(DecodeClipControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SU_LINE_CNTL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -3933,11 +3940,7 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SC_MODE_CNTL_0] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::ScanModeControl r;
|
||||
r.msaa_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, MSAA_ENABLE) != 0;
|
||||
r.vport_scissor_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, VPORT_SCISSOR_ENABLE) != 0;
|
||||
r.line_stipple_enable = KYTY_PM4_GET(value, PA_SC_MODE_CNTL_0, LINE_STIPPLE_ENABLE) != 0;
|
||||
cp.GetCtx().SetScanModeControl(r);
|
||||
cp.GetCtx().SetScanModeControl(DecodeScanModeControl(value));
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::PA_SC_MODE_CNTL_1] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreScanModeControl1(value);
|
||||
@@ -3996,27 +3999,11 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
HwCtxIgnoreAlphaToMaskRegister(value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::CB_COLOR_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::ColorControl r;
|
||||
r.mode = KYTY_PM4_GET(value, CB_COLOR_CONTROL, MODE);
|
||||
r.op = KYTY_PM4_GET(value, CB_COLOR_CONTROL, ROP3);
|
||||
cp.GetCtx().SetColorControl(r);
|
||||
cp.GetCtx().SetColorControl(DecodeColorControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_DEPTH_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HW::DepthControl r;
|
||||
r.stencil_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCIL_ENABLE) != 0;
|
||||
r.z_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_ENABLE) != 0;
|
||||
r.z_write_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, Z_WRITE_ENABLE) != 0;
|
||||
r.depth_bounds_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DEPTH_BOUNDS_ENABLE) != 0;
|
||||
r.zfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ZFUNC);
|
||||
r.backface_enable = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, BACKFACE_ENABLE) != 0;
|
||||
r.stencilfunc = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC);
|
||||
r.stencilfunc_bf = KYTY_PM4_GET(value, DB_DEPTH_CONTROL, STENCILFUNC_BF);
|
||||
r.color_writes_on_depth_fail_enable =
|
||||
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, ENABLE_COLOR_WRITES_ON_DEPTH_FAIL) != 0;
|
||||
r.color_writes_on_depth_pass_disable =
|
||||
KYTY_PM4_GET(value, DB_DEPTH_CONTROL, DISABLE_COLOR_WRITES_ON_DEPTH_PASS) != 0;
|
||||
cp.GetCtx().SetDepthControl(r);
|
||||
cp.GetCtx().SetDepthControl(DecodeDepthControl(value));
|
||||
};
|
||||
|
||||
g_hw_ctx_indirect_func[Pm4::DB_EQAA] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -4220,29 +4207,28 @@ void GraphicsInitJmpTablesShIndirect() {
|
||||
};
|
||||
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC1_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HW::VsShaderResource1 r1;
|
||||
r1.vgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, VGPRS);
|
||||
r1.sgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, SGPRS);
|
||||
r1.priority = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, PRIORITY);
|
||||
r1.float_mode = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, FLOAT_MODE);
|
||||
r1.dx10_clamp = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, DX10_CLAMP) != 0;
|
||||
r1.ieee_mode = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, IEEE_MODE) != 0;
|
||||
r1.vgpr_component_count = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, VGPR_COMP_CNT);
|
||||
r1.cu_group_enable = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, CU_GROUP_ENABLE) != 0;
|
||||
HW::HsShaderResource1 r1;
|
||||
r1.vgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, VGPRS);
|
||||
r1.priority = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, PRIORITY);
|
||||
r1.float_mode = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, FLOAT_MODE);
|
||||
r1.dx10_clamp = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, DX10_CLAMP) != 0;
|
||||
r1.debug_mode = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, DEBUG_MODE) != 0;
|
||||
r1.ieee_mode = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, IEEE_MODE) != 0;
|
||||
r1.require_forward_progress =
|
||||
KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, FWD_PROGRESS) != 0;
|
||||
r1.fp16_overflow = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_VS, FP16_OVFL) != 0;
|
||||
KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, FWD_PROGRESS) != 0;
|
||||
r1.threadgroup_configuration = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, WGP_MODE) != 0;
|
||||
r1.ls_vgpr_component_count = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, LS_VGPR_COMP_CNT);
|
||||
r1.fp16_overflow = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_HS, FP16_OVFL) != 0;
|
||||
cp.GetShCtx().SetHsShaderResource1(r1);
|
||||
};
|
||||
|
||||
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC2_HS] = [](KYTY_HW_SH_INDIRECT_ARGS) {
|
||||
HW::VsShaderResource2 r2;
|
||||
r2.scratch_en = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, SCRATCH_EN) != 0;
|
||||
r2.user_sgpr = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, USER_SGPR) +
|
||||
(KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, USER_SGPR_MSB) << 5u);
|
||||
r2.offchip_lds = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, OC_LDS_EN) != 0;
|
||||
r2.streamout_enabled = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, SO_EN) != 0;
|
||||
r2.shared_vgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_VS, SHARED_VGPR_CNT);
|
||||
HW::HsShaderResource2 r2;
|
||||
r2.scratch_en = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_HS, SCRATCH_EN) != 0;
|
||||
r2.user_sgpr = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_HS, USER_SGPR) +
|
||||
(KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_HS, USER_SGPR_MSB) << 5u);
|
||||
r2.lds_size = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_HS, LDS_SIZE);
|
||||
r2.shared_vgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_HS, SHARED_VGPR_CNT);
|
||||
cp.GetShCtx().SetHsShaderResource2(r2);
|
||||
};
|
||||
|
||||
@@ -4330,7 +4316,7 @@ void GraphicsInitJmpTablesShIndirect() {
|
||||
r1.cu_group_enable = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, CU_GROUP_ENABLE) != 0;
|
||||
r1.require_forward_progress =
|
||||
KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, FWD_PROGRESS) != 0;
|
||||
r1.lds_configuration = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, WGP_MODE) != 0;
|
||||
r1.threadgroup_configuration = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, WGP_MODE) != 0;
|
||||
r1.gs_vgpr_component_count = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, GS_VGPR_COMP_CNT);
|
||||
r1.fp16_overflow = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC1_GS, FP16_OVFL) != 0;
|
||||
cp.GetShCtx().SetGsShaderResource1(r1);
|
||||
@@ -4390,7 +4376,7 @@ void GraphicsInitJmpTablesShIndirect() {
|
||||
r2.wave_cnt_en = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_PS, WAVE_CNT_EN);
|
||||
r2.extra_lds_size = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_PS, EXTRA_LDS_SIZE);
|
||||
r2.raster_ordered_shading =
|
||||
KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_PS, LOAD_INTRAWAVE_COLLISION);
|
||||
KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_PS, RASTER_ORDERED_SHADING);
|
||||
r2.shared_vgprs = KYTY_PM4_GET(value, SPI_SHADER_PGM_RSRC2_PS, SHARED_VGPR_CNT);
|
||||
cp.GetShCtx().SetPsShaderResource2(r2);
|
||||
};
|
||||
@@ -4407,6 +4393,11 @@ void GraphicsInitJmpTablesUcIndirect() {
|
||||
r.vertex_group_size = KYTY_PM4_GET(value, GE_CNTL, VERT_GRP_SIZE);
|
||||
cp.GetUcfg().SetGeControl(r);
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::GE_PC_ALLOC] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
(void)cp;
|
||||
(void)cmd_offset;
|
||||
(void)value;
|
||||
};
|
||||
|
||||
g_hw_uc_indirect_func[Pm4::GE_USER_VGPR_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
HW::GeUserVgprEn r;
|
||||
@@ -4418,7 +4409,7 @@ void GraphicsInitJmpTablesUcIndirect() {
|
||||
|
||||
g_hw_uc_indirect_func[Pm4::VGT_PRIMITIVE_TYPE] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
uint32_t prim_type = KYTY_PM4_GET(value, VGT_PRIMITIVE_TYPE, PRIM_TYPE);
|
||||
cp.GetUcfg().SetPrimitiveType(prim_type);
|
||||
cp.GetUcfg().SetPrimitiveType(static_cast<Prospero::PrimitiveType>(prim_type));
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::VGT_INDEX_TYPE] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
cp.SetIndexType(value & 0x3u);
|
||||
|
||||
@@ -3,15 +3,8 @@
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
|
||||
template <typename T>
|
||||
constexpr auto GpuEnumValue(T value) {
|
||||
return static_cast<std::underlying_type_t<T>>(value);
|
||||
}
|
||||
|
||||
enum class PrimitiveType : uint32_t {
|
||||
kNone = 0,
|
||||
kPointList = 1,
|
||||
@@ -60,24 +53,34 @@ enum class GsOutputPrimitiveType : uint32_t {
|
||||
};
|
||||
|
||||
enum class ChannelLayout : uint32_t {
|
||||
kInvalid = 0,
|
||||
k8 = 1,
|
||||
k16 = 2,
|
||||
k8_8 = 3,
|
||||
k32 = 4,
|
||||
k16_16 = 5,
|
||||
k11_11_10 = 6,
|
||||
k10_10_10_2 = 9,
|
||||
k8_8_8_8 = 10,
|
||||
k32_32 = 11,
|
||||
k16_16_16_16 = 12,
|
||||
k32_32_32_32 = 14,
|
||||
k5_6_5 = 16,
|
||||
k5_5_5_1 = 17,
|
||||
k4_4_4_4 = 19,
|
||||
kBc1 = 35,
|
||||
kBc2 = 36,
|
||||
kBc3 = 37,
|
||||
kInvalid = 0,
|
||||
k8 = 1,
|
||||
k16 = 2,
|
||||
k8_8 = 3,
|
||||
k32 = 4,
|
||||
k16_16 = 5,
|
||||
k11_11_10 = 6,
|
||||
k10_11_11 = 7,
|
||||
k2_10_10_10 = 8,
|
||||
k10_10_10_2 = 9,
|
||||
k8_8_8_8 = 10,
|
||||
k32_32 = 11,
|
||||
k16_16_16_16 = 12,
|
||||
k32_32_32 = 13,
|
||||
k32_32_32_32 = 14,
|
||||
k5_6_5 = 16,
|
||||
k5_5_5_1 = 17,
|
||||
k1_5_5_5 = 18,
|
||||
k4_4_4_4 = 19,
|
||||
k10_10_10_2Float = 31,
|
||||
k9_9_9_5 = 34,
|
||||
kBc1 = 35,
|
||||
kBc2 = 36,
|
||||
kBc3 = 37,
|
||||
kBc4 = 38,
|
||||
kBc5 = 39,
|
||||
kBc6 = 40,
|
||||
kBc7 = 41,
|
||||
};
|
||||
|
||||
enum class ChannelType : uint32_t {
|
||||
@@ -105,32 +108,52 @@ enum class ChannelOrder : uint32_t {
|
||||
kAltReversed = 3,
|
||||
};
|
||||
|
||||
// Maps a shader's logical RGBA export to the host color-attachment components. Two bits per
|
||||
// component keep this cheap to carry in shader and pipeline keys.
|
||||
// Selects the logical shader component written to each physical color-attachment component.
|
||||
// Two bits per component keep this cheap to carry in shader and pipeline keys.
|
||||
struct ColorComponentMapping {
|
||||
uint8_t packed = 0xe4u; // RGBA: 0, 1, 2, 3
|
||||
static constexpr uint8_t kIdentity = 0xe4u;
|
||||
|
||||
uint8_t packed = kIdentity;
|
||||
|
||||
[[nodiscard]] constexpr uint32_t Map(uint32_t component) const {
|
||||
return component < 4u ? (packed >> (component * 2u)) & 0x3u : component;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t mask) const {
|
||||
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t logical_mask) const {
|
||||
uint32_t mapped = 0;
|
||||
for (uint32_t component = 0; component < 4u; component++) {
|
||||
mapped |= ((mask >> component) & 1u) << Map(component);
|
||||
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
|
||||
mapped |= ((logical_mask >> Map(physical_component)) & 1u) << physical_component;
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsIdentity() const { return packed == 0xe4u; }
|
||||
// This mapping selects an intermediate component; next selects the final logical component.
|
||||
[[nodiscard]] constexpr ColorComponentMapping Then(ColorComponentMapping next) const {
|
||||
ColorComponentMapping result {0u};
|
||||
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
|
||||
result.packed |= static_cast<uint8_t>(next.Map(Map(physical_component))
|
||||
<< (physical_component * 2u));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsIdentity() const { return packed == kIdentity; }
|
||||
|
||||
bool operator==(const ColorComponentMapping&) const = default;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ColorComponentMapping) == sizeof(uint8_t));
|
||||
|
||||
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingRgba {ColorComponentMapping::kIdentity};
|
||||
inline constexpr ColorComponentMapping ColorMappingGr {0xe1u}; // 1, 0, 2, 3
|
||||
inline constexpr ColorComponentMapping ColorMappingRabg {0x6cu}; // 0, 3, 2, 1
|
||||
inline constexpr ColorComponentMapping ColorMappingRgab {0xb4u}; // 0, 1, 3, 2
|
||||
inline constexpr ColorComponentMapping ColorMappingBgra {0xc6u}; // 2, 1, 0, 3
|
||||
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingAgba {0x27u}; // 3, 1, 2, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingArbg {0x63u}; // 3, 0, 2, 1
|
||||
inline constexpr ColorComponentMapping ColorMappingAgbr {0x87u}; // 3, 1, 0, 2
|
||||
inline constexpr ColorComponentMapping ColorMappingArgb {0x93u}; // 3, 0, 1, 2
|
||||
|
||||
enum class DepthFormat : uint32_t {
|
||||
kInvalid = 0,
|
||||
@@ -378,9 +401,11 @@ enum class BufferFormat : uint32_t {
|
||||
k8Srgb = 128,
|
||||
k8_8Srgb = 129,
|
||||
k8_8_8_8Srgb = 130,
|
||||
k10_10_10_2Float = 131,
|
||||
k9_9_9_5Float = 132,
|
||||
k5_6_5UNorm = 133,
|
||||
k5_5_5_1UNorm = 134,
|
||||
k1_5_5_5UNorm = 135,
|
||||
k4_4_4_4UNorm = 136,
|
||||
kFmask8_S4_F4 = 161,
|
||||
kBc1UNorm = 169,
|
||||
|
||||
@@ -3,129 +3,216 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
namespace {
|
||||
|
||||
struct FormatInfo {
|
||||
uint32_t format;
|
||||
uint32_t bytes_per_element;
|
||||
uint32_t block_compressed_bytes_per_block;
|
||||
uint32_t render_target_bytes_per_element;
|
||||
bool supported_sampled_texture_format;
|
||||
bool unsigned_integer_texture_format;
|
||||
BufferFormat format;
|
||||
uint32_t bytes_per_element;
|
||||
uint32_t block_compressed_bytes_per_block;
|
||||
uint32_t render_target_bytes_per_element;
|
||||
bool supported_sampled_texture_format;
|
||||
bool unsigned_integer_texture_format;
|
||||
};
|
||||
|
||||
constexpr FormatInfo kFormatInfo[] = {
|
||||
{GpuEnumValue(BufferFormat::k8UNorm), 1, 0, 1, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8SNorm), 0, 0, 1, false, false},
|
||||
{GpuEnumValue(BufferFormat::k8UInt), 1, 0, 1, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16SNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16UInt), 2, 0, 2, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16SInt), 2, 0, 2, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16Float), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8SNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8UInt), 2, 0, 2, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8SInt), 2, 0, 2, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16SNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16_16SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k11_11_10Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k10_10_10_2UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k10_10_10_2UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8SNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32UInt), 8, 0, 8, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32SInt), 8, 0, 8, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32Float), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16UNorm), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16SNorm), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16UInt), 8, 0, 8, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16SInt), 8, 0, 8, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16Float), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32UInt), 12, 0, 12, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32SInt), 12, 0, 12, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32Float), 12, 0, 12, true, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32UInt), 16, 0, 16, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32SInt), 16, 0, 16, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32Float), 16, 0, 16, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8Srgb), 1, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8Srgb), 2, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8Srgb), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k9_9_9_5Float), 4, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k5_6_5UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k5_5_5_1UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k4_4_4_4UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::kFmask8_S4_F4), 1, 0, 1, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc1UNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc1Srgb), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc2UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc2Srgb), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc3UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc3Srgb), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc4UNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc4SNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc5UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc5SNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc6UFloat), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc6SFloat), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc7UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc7Srgb), 0, 16, 0, true, false},
|
||||
{BufferFormat::k8UNorm, 1, 0, 1, true, false},
|
||||
{BufferFormat::k8SNorm, 0, 0, 1, false, false},
|
||||
{BufferFormat::k8UInt, 1, 0, 1, true, true},
|
||||
{BufferFormat::k16UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k16Float, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, false, false},
|
||||
{BufferFormat::k32UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k32Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, false, false},
|
||||
{BufferFormat::k32_32UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k32_32Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16SNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, false, false},
|
||||
{BufferFormat::k16_16_16_16Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k32_32_32UInt, 12, 0, 12, true, true},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, false, false},
|
||||
{BufferFormat::k32_32_32Float, 12, 0, 12, true, false},
|
||||
{BufferFormat::k32_32_32_32UInt, 16, 0, 16, true, true},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, false, false},
|
||||
{BufferFormat::k32_32_32_32Float, 16, 0, 16, true, false},
|
||||
{BufferFormat::k8Srgb, 1, 0, 0, true, false},
|
||||
{BufferFormat::k8_8Srgb, 2, 0, 0, true, false},
|
||||
{BufferFormat::k8_8_8_8Srgb, 4, 0, 4, true, false},
|
||||
{BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false},
|
||||
{BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false},
|
||||
{BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask8_S4_F4, 1, 0, 1, true, false},
|
||||
{BufferFormat::kBc1UNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc1Srgb, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc2UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc2Srgb, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc3UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc3Srgb, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc4UNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc4SNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc5UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc5SNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc6UFloat, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc6SFloat, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc7UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc7Srgb, 0, 16, 0, true, false},
|
||||
};
|
||||
|
||||
constexpr uint8_t RenderTargetTypeBit(ChannelType type) {
|
||||
return static_cast<uint8_t>(1u << static_cast<uint32_t>(type));
|
||||
}
|
||||
|
||||
constexpr auto kUNormType = RenderTargetTypeBit(ChannelType::kUNorm);
|
||||
constexpr auto kIntegerTypes =
|
||||
RenderTargetTypeBit(ChannelType::kUInt) | RenderTargetTypeBit(ChannelType::kSInt);
|
||||
constexpr auto kNormalizedIntegerTypes =
|
||||
kUNormType | RenderTargetTypeBit(ChannelType::kSNorm) | kIntegerTypes;
|
||||
|
||||
struct RenderTargetLayoutInfo {
|
||||
ChannelLayout layout;
|
||||
BufferFormat first_linear_format;
|
||||
uint8_t linear_type_mask;
|
||||
uint8_t components;
|
||||
BufferFormat extra_format = BufferFormat::kInvalid;
|
||||
ChannelType extra_type = ChannelType::kUNorm;
|
||||
ChannelOrderSupport order_support = ChannelOrderSupport::kAll;
|
||||
};
|
||||
|
||||
constexpr RenderTargetLayoutInfo kRenderTargetLayouts[] = {
|
||||
{ChannelLayout::k8, BufferFormat::k8UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k8Srgb,
|
||||
ChannelType::kSrgb},
|
||||
{ChannelLayout::k16, BufferFormat::k16UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k16Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k8_8, BufferFormat::k8_8UNorm, kNormalizedIntegerTypes, 2,
|
||||
BufferFormat::k8_8Srgb, ChannelType::kSrgb},
|
||||
{ChannelLayout::k32, BufferFormat::k32UInt, kIntegerTypes, 1, BufferFormat::k32Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k16_16, BufferFormat::k16_16UNorm, kNormalizedIntegerTypes, 2,
|
||||
BufferFormat::k16_16Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k11_11_10, BufferFormat::k11_11_10UNorm, kNormalizedIntegerTypes, 3,
|
||||
BufferFormat::k11_11_10Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k10_11_11, BufferFormat::k10_11_11UNorm, kNormalizedIntegerTypes, 3,
|
||||
BufferFormat::k10_11_11Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k2_10_10_10, BufferFormat::k2_10_10_10UNorm, kNormalizedIntegerTypes, 4},
|
||||
{ChannelLayout::k10_10_10_2, BufferFormat::k10_10_10_2UNorm, kNormalizedIntegerTypes, 4},
|
||||
{ChannelLayout::k8_8_8_8, BufferFormat::k8_8_8_8UNorm, kNormalizedIntegerTypes, 4,
|
||||
BufferFormat::k8_8_8_8Srgb, ChannelType::kSrgb},
|
||||
{ChannelLayout::k32_32, BufferFormat::k32_32UInt, kIntegerTypes, 2, BufferFormat::k32_32Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k16_16_16_16, BufferFormat::k16_16_16_16UNorm, kNormalizedIntegerTypes, 4,
|
||||
BufferFormat::k16_16_16_16Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k32_32_32_32, BufferFormat::k32_32_32_32UInt, kIntegerTypes, 4,
|
||||
BufferFormat::k32_32_32_32Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k5_6_5, BufferFormat::k5_6_5UNorm, kUNormType, 3},
|
||||
{ChannelLayout::k5_5_5_1, BufferFormat::k5_5_5_1UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k1_5_5_5, BufferFormat::k1_5_5_5UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k4_4_4_4, BufferFormat::k4_4_4_4UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k10_10_10_2Float, BufferFormat::kInvalid, 0, 4, BufferFormat::k10_10_10_2Float,
|
||||
ChannelType::kFloat, ChannelOrderSupport::kStandardOnly},
|
||||
};
|
||||
|
||||
const RenderTargetLayoutInfo* FindRenderTargetLayout(ChannelLayout layout) {
|
||||
for (const auto& info: kRenderTargetLayouts) {
|
||||
if (info.layout == layout) {
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr auto MakeFormatInfoLookup() {
|
||||
constexpr uint32_t kMaxFormat = GpuEnumValue(BufferFormat::kBc7Srgb);
|
||||
constexpr size_t kMaxFormat = static_cast<size_t>(BufferFormat::kBc7Srgb);
|
||||
std::array<const FormatInfo*, kMaxFormat + 1> lookup {};
|
||||
for (const auto& info: kFormatInfo) {
|
||||
lookup[info.format] = &info;
|
||||
lookup[static_cast<size_t>(info.format)] = &info;
|
||||
}
|
||||
return lookup;
|
||||
}
|
||||
|
||||
constexpr auto kFormatInfoLookup = MakeFormatInfoLookup();
|
||||
|
||||
const FormatInfo* FindFormatInfo(uint32_t format) {
|
||||
return format < kFormatInfoLookup.size() ? kFormatInfoLookup[format] : nullptr;
|
||||
const FormatInfo* FindFormatInfo(BufferFormat format) {
|
||||
const auto index = static_cast<size_t>(format);
|
||||
return index < kFormatInfoLookup.size() ? kFormatInfoLookup[index] : nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
uint32_t NumBytesPerElement(uint32_t format) {
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type) {
|
||||
const auto* layout_info = FindRenderTargetLayout(layout);
|
||||
const auto raw_type = static_cast<uint32_t>(type);
|
||||
if (layout_info == nullptr || raw_type >= 8u) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto format = BufferFormat::kInvalid;
|
||||
if (layout_info->extra_format != BufferFormat::kInvalid && type == layout_info->extra_type) {
|
||||
format = layout_info->extra_format;
|
||||
} else if ((layout_info->linear_type_mask & (1u << raw_type)) != 0) {
|
||||
const auto first_type =
|
||||
std::countr_zero(static_cast<uint32_t>(layout_info->linear_type_mask));
|
||||
format = static_cast<BufferFormat>(static_cast<uint32_t>(layout_info->first_linear_format) +
|
||||
raw_type - first_type);
|
||||
}
|
||||
if (format == BufferFormat::kInvalid) {
|
||||
return {};
|
||||
}
|
||||
return {format, layout_info->components, layout_info->order_support};
|
||||
}
|
||||
|
||||
uint32_t NumBytesPerElement(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->bytes_per_element : 0;
|
||||
}
|
||||
|
||||
uint32_t BlockCompressedBytesPerBlock(uint32_t format) {
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->block_compressed_bytes_per_block : 0;
|
||||
}
|
||||
|
||||
uint32_t RenderTargetBytesPerElement(uint32_t format) {
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->render_target_bytes_per_element : 0;
|
||||
}
|
||||
|
||||
bool IsSupportedTextureFormat(uint32_t format) {
|
||||
bool IsSupportedTextureFormat(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->supported_sampled_texture_format;
|
||||
}
|
||||
|
||||
bool IsUintTextureFormat(uint32_t format) {
|
||||
bool IsUintTextureFormat(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->unsigned_integer_texture_format;
|
||||
}
|
||||
|
||||
bool IsFmaskTextureFormat(uint32_t format) {
|
||||
return format == GpuEnumValue(BufferFormat::kFmask8_S4_F4);
|
||||
bool IsFmaskTextureFormat(BufferFormat format) {
|
||||
return format == BufferFormat::kFmask8_S4_F4;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
@@ -1,16 +1,49 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
|
||||
uint32_t NumBytesPerElement(uint32_t format);
|
||||
uint32_t BlockCompressedBytesPerBlock(uint32_t format);
|
||||
uint32_t RenderTargetBytesPerElement(uint32_t format);
|
||||
bool IsSupportedTextureFormat(uint32_t format);
|
||||
bool IsUintTextureFormat(uint32_t format);
|
||||
bool IsFmaskTextureFormat(uint32_t format);
|
||||
enum class ChannelOrderSupport : uint8_t {
|
||||
kNone,
|
||||
kStandardOnly,
|
||||
kAll,
|
||||
};
|
||||
|
||||
struct RenderTargetFormatEncoding {
|
||||
BufferFormat buffer_format = BufferFormat::kInvalid;
|
||||
uint8_t components = 0;
|
||||
ChannelOrderSupport order_support = ChannelOrderSupport::kNone;
|
||||
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
return buffer_format != BufferFormat::kInvalid && components >= 1u && components <= 4u &&
|
||||
order_support != ChannelOrderSupport::kNone;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool SupportsOrder(ChannelOrder order) const {
|
||||
switch (order_support) {
|
||||
case ChannelOrderSupport::kStandardOnly: return order == ChannelOrder::kStandard;
|
||||
case ChannelOrderSupport::kAll:
|
||||
switch (order) {
|
||||
case ChannelOrder::kStandard:
|
||||
case ChannelOrder::kAlt:
|
||||
case ChannelOrder::kReversed:
|
||||
case ChannelOrder::kAltReversed: return true;
|
||||
}
|
||||
return false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type);
|
||||
uint32_t NumBytesPerElement(BufferFormat format);
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
||||
bool IsSupportedTextureFormat(BufferFormat format);
|
||||
bool IsUintTextureFormat(BufferFormat format);
|
||||
bool IsFmaskTextureFormat(BufferFormat format);
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ static thread_local CommandProcessor* g_current_processor = nullptr;
|
||||
static thread_local Pm4Execution* g_current_execution = nullptr;
|
||||
static thread_local bool g_gpu_mutex_owned = false;
|
||||
static thread_local bool g_gpu_thread = false;
|
||||
static thread_local GpuState* g_gpu_state = nullptr;
|
||||
|
||||
class GpuMutexLock final {
|
||||
public:
|
||||
@@ -98,11 +99,10 @@ public:
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
void Done();
|
||||
void Shutdown();
|
||||
[[nodiscard]] bool IsStopping();
|
||||
void SendCommand(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSyncWithProcessor(Common::UniqueFunction<void, CommandProcessor&>&& command);
|
||||
int GetFrameNum();
|
||||
[[nodiscard]] bool IsStopping();
|
||||
void SendCommand(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
int GetFrameNum();
|
||||
[[nodiscard]] static bool IsGpuThread() noexcept { return g_gpu_thread; }
|
||||
|
||||
private:
|
||||
@@ -124,9 +124,9 @@ private:
|
||||
uint64_t flip_request_id = 0;
|
||||
};
|
||||
|
||||
void WaitLocked();
|
||||
void Enqueue(Submission submission);
|
||||
void WaitForIdle();
|
||||
void ProcessCommands();
|
||||
bool Process(Submission& submission);
|
||||
static void ThreadRun(void* data);
|
||||
CommandProcessor& GetProcessor(uint32_t queue_id);
|
||||
@@ -139,6 +139,7 @@ private:
|
||||
Common::CondVar m_idle;
|
||||
std::array<std::deque<Submission>, QueueCount> m_queues;
|
||||
std::deque<Common::UniqueFunction<void>> m_commands;
|
||||
std::atomic_uint32_t m_pending_commands {0};
|
||||
uint32_t m_next_queue = 0;
|
||||
uint32_t m_submission_count = 0;
|
||||
bool m_processing = false;
|
||||
@@ -153,6 +154,8 @@ private:
|
||||
uint64_t m_submit_id = 0;
|
||||
std::atomic_int m_done_num = 0;
|
||||
std::jthread m_thread;
|
||||
|
||||
friend class CommandProcessor;
|
||||
};
|
||||
|
||||
static bool GraphicsRunDebugDumpEnabled() {
|
||||
@@ -195,9 +198,25 @@ void GpuState::SendCommand(Common::UniqueFunction<void>&& command) {
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
EXIT_IF(!m_accepting);
|
||||
m_commands.push_back(std::move(command));
|
||||
m_pending_commands.fetch_add(1, std::memory_order_release);
|
||||
m_work_available.Signal();
|
||||
}
|
||||
|
||||
void GpuState::ProcessCommands() {
|
||||
EXIT_IF(!IsGpuThread());
|
||||
while (m_pending_commands.load(std::memory_order_acquire) != 0) {
|
||||
Common::UniqueFunction<void> command;
|
||||
{
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
EXIT_IF(m_commands.empty());
|
||||
command = std::move(m_commands.front());
|
||||
m_commands.pop_front();
|
||||
EXIT_IF(m_pending_commands.fetch_sub(1, std::memory_order_acq_rel) == 0);
|
||||
}
|
||||
command();
|
||||
}
|
||||
}
|
||||
|
||||
void GpuState::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
EXIT_IF(!command);
|
||||
if (IsGpuThread()) {
|
||||
@@ -212,17 +231,6 @@ void GpuState::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
done.acquire();
|
||||
}
|
||||
|
||||
void GpuState::SendCommandSyncWithProcessor(
|
||||
Common::UniqueFunction<void, CommandProcessor&>&& command) {
|
||||
EXIT_IF(!command);
|
||||
SendCommandSync([this, operation = std::move(command)]() mutable {
|
||||
EXIT_IF(g_current_processor != nullptr);
|
||||
g_current_processor = m_gfx_cp.get();
|
||||
operation(*m_gfx_cp);
|
||||
g_current_processor = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void GpuState::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
uint32_t num_const_dw, bool trigger_agc_interrupt_on_done) {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
@@ -267,10 +275,8 @@ void GpuState::SubmitFlipPreparation(uint64_t request_id) {
|
||||
|
||||
void GpuState::Done() {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
if (IsGpuThread()) {
|
||||
m_gfx_cp->BufferWait();
|
||||
} else {
|
||||
WaitLocked();
|
||||
if (!IsGpuThread()) {
|
||||
WaitForIdle();
|
||||
}
|
||||
m_graphics_done = true;
|
||||
m_done_num++;
|
||||
@@ -280,11 +286,6 @@ int GpuState::GetFrameNum() {
|
||||
return m_done_num;
|
||||
}
|
||||
|
||||
void GpuState::WaitLocked() {
|
||||
WaitForIdle();
|
||||
SendCommandSync([this] { m_gfx_cp->BufferWait(); });
|
||||
}
|
||||
|
||||
CommandProcessor& GpuState::GetProcessor(uint32_t queue_id) {
|
||||
EXIT_IF(queue_id >= QueueCount);
|
||||
if (queue_id == 0) {
|
||||
@@ -298,11 +299,11 @@ CommandProcessor& GpuState::GetProcessor(uint32_t queue_id) {
|
||||
}
|
||||
|
||||
void CommandProcessor::Reset() {
|
||||
BufferWait();
|
||||
|
||||
m_sh_ctx.Reset();
|
||||
m_ucfg.Reset();
|
||||
m_ctx.Reset();
|
||||
m_saved_ctx.Reset();
|
||||
m_context_state_pushed = false;
|
||||
m_index_type_and_size = 0;
|
||||
m_index_buffer_size = 0;
|
||||
m_user_data_marker = HW::UserSgprType::Unknown;
|
||||
@@ -312,6 +313,30 @@ void CommandProcessor::Reset() {
|
||||
std::memset(m_const_ram, 0, sizeof(m_const_ram));
|
||||
}
|
||||
|
||||
void CommandProcessor::ApplyContextStateOperation(ContextStateOperation operation) {
|
||||
switch (operation) {
|
||||
case ContextStateOperation::Clear: m_ctx.Reset(); break;
|
||||
case ContextStateOperation::Push:
|
||||
EXIT_IF(m_context_state_pushed);
|
||||
m_saved_ctx = m_ctx;
|
||||
m_context_state_pushed = true;
|
||||
break;
|
||||
case ContextStateOperation::Pop:
|
||||
EXIT_IF(!m_context_state_pushed);
|
||||
m_ctx = m_saved_ctx;
|
||||
m_saved_ctx = {};
|
||||
m_context_state_pushed = false;
|
||||
break;
|
||||
case ContextStateOperation::PushClear:
|
||||
EXIT_IF(m_context_state_pushed);
|
||||
m_saved_ctx = m_ctx;
|
||||
m_context_state_pushed = true;
|
||||
m_ctx.Reset();
|
||||
break;
|
||||
default: EXIT("unknown context state operation: %u\n", static_cast<uint32_t>(operation));
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::BufferInit() {
|
||||
GetScheduler().Begin(m_ctx, m_ucfg, m_sh_ctx);
|
||||
}
|
||||
@@ -350,7 +375,6 @@ void CommandProcessor::WaitDeDiff(uint32_t diff) {
|
||||
}
|
||||
|
||||
void CommandProcessor::IncrementDe() {
|
||||
BufferWait();
|
||||
m_de_count++;
|
||||
}
|
||||
|
||||
@@ -449,7 +473,6 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
if (num_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED((num_bytes & 3u) != 0);
|
||||
EXIT_NOT_IMPLEMENTED(dst_cache_policy > 3);
|
||||
EXIT_NOT_IMPLEMENTED(src_cache_policy > 3);
|
||||
EXIT_NOT_IMPLEMENTED(wait_for_previous > 1);
|
||||
@@ -509,6 +532,7 @@ void GpuState::ThreadRun(void* data) {
|
||||
EXIT_IF(gpu == nullptr);
|
||||
KYTY_PROFILER_THREAD("Thread_Gpu");
|
||||
g_gpu_thread = true;
|
||||
g_gpu_state = gpu;
|
||||
|
||||
for (;;) {
|
||||
Submission submission;
|
||||
@@ -529,6 +553,7 @@ void GpuState::ThreadRun(void* data) {
|
||||
} else if (!gpu->m_commands.empty()) {
|
||||
command = std::move(gpu->m_commands.front());
|
||||
gpu->m_commands.pop_front();
|
||||
EXIT_IF(gpu->m_pending_commands.fetch_sub(1, std::memory_order_acq_rel) == 0);
|
||||
gpu->m_processing = true;
|
||||
} else {
|
||||
int selected_queue = -1;
|
||||
@@ -560,6 +585,7 @@ void GpuState::ThreadRun(void* data) {
|
||||
}
|
||||
if (should_stop) {
|
||||
gpu->m_gfx_cp->BufferWait();
|
||||
g_gpu_state = nullptr;
|
||||
g_gpu_thread = false;
|
||||
return;
|
||||
}
|
||||
@@ -741,6 +767,9 @@ void CommandProcessor::SuspendPm4() {
|
||||
|
||||
void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
while (execution.m_buffer_stack.size() > stop_depth) {
|
||||
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];
|
||||
if (cursor.deferred_advance_dw != 0) {
|
||||
@@ -1135,9 +1164,9 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y,
|
||||
uint32_t thread_group_z, uint32_t mode) {
|
||||
uint32_t frame_num = 0;
|
||||
uint32_t local_x = 1;
|
||||
uint32_t local_y = 1;
|
||||
uint32_t local_z = 1;
|
||||
// uint32_t local_x = 1;
|
||||
// uint32_t local_y = 1;
|
||||
// uint32_t local_z = 1;
|
||||
|
||||
{
|
||||
CheckBuffer();
|
||||
@@ -1161,9 +1190,9 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
}
|
||||
|
||||
const auto& cs = m_sh_ctx.GetCs().cs_regs;
|
||||
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);
|
||||
// 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)) {
|
||||
@@ -1179,27 +1208,27 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
thread_group_y, thread_group_z, mode);
|
||||
}
|
||||
|
||||
constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||
/*constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||
auto group_count = [](uint32_t threads, uint32_t group_size) {
|
||||
return (threads == 0
|
||||
? 0u
|
||||
: (threads + std::max(group_size, 1u) - 1u) / std::max(group_size, 1u));
|
||||
return (threads == 0
|
||||
? 0u
|
||||
: (threads + std::max(group_size, 1u) - 1u) / std::max(group_size, 1u));
|
||||
};
|
||||
|
||||
auto groups_x = thread_group_x;
|
||||
auto groups_y = thread_group_y;
|
||||
auto groups_z = thread_group_z;
|
||||
if ((mode & DispatchInitiatorUseThreadDimensions) != 0) {
|
||||
groups_x = group_count(thread_group_x, local_x);
|
||||
groups_y = group_count(thread_group_y, local_y);
|
||||
groups_z = group_count(thread_group_z, local_z);
|
||||
groups_x = group_count(thread_group_x, local_x);
|
||||
groups_y = group_count(thread_group_y, local_y);
|
||||
groups_z = group_count(thread_group_z, local_z);
|
||||
}
|
||||
|
||||
const uint64_t invocations =
|
||||
static_cast<uint64_t>(groups_x) * groups_y * groups_z * local_x * local_y * local_z;
|
||||
if (invocations != 0) {
|
||||
BufferFlushAndWait();
|
||||
}
|
||||
BufferFlushAndWait();
|
||||
}*/
|
||||
}
|
||||
|
||||
void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
||||
@@ -1311,6 +1340,9 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
value >> 16u);
|
||||
Sync::WriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst, value & 0xffffu,
|
||||
value >> 16u);
|
||||
if (with_interrupt) {
|
||||
m_renderer.TriggerEopEvent(interrupt_context_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (eop_event_type == 0x04 && cache_action == 0x00 && event_index == 0x05) {
|
||||
@@ -1352,9 +1384,14 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
case 0x00:
|
||||
switch (eop_event_type) {
|
||||
case 0x04:
|
||||
if (event_index == 0x05) {
|
||||
write64(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x14:
|
||||
case 0x28:
|
||||
if ((eop_event_type == 0x04 && event_index == 0x05) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) {
|
||||
if (event_index == 0x00) {
|
||||
write64(false);
|
||||
return;
|
||||
}
|
||||
@@ -1513,7 +1550,6 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
|
||||
event_index);
|
||||
}
|
||||
EmitGlobalBarrier();
|
||||
SynchronizeGpu();
|
||||
break;
|
||||
// DbDataWritebackInvalidate, DbMetadataWritebackInvalidate, CbMetadataWritebackInvalidate.
|
||||
case 0x0000002a:
|
||||
@@ -1650,10 +1686,6 @@ void Gpu::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
m_state->SendCommandSync(std::move(command));
|
||||
}
|
||||
|
||||
void Gpu::SendCommandSyncWithProcessor(Common::UniqueFunction<void, CommandProcessor&>&& command) {
|
||||
m_state->SendCommandSyncWithProcessor(std::move(command));
|
||||
}
|
||||
|
||||
void Gpu::Submit(uint32_t* draw_commands, uint32_t draw_size_dw, uint32_t* constant_commands,
|
||||
uint32_t constant_size_dw, bool trigger_agc_interrupt_on_done) {
|
||||
EXIT_IF(draw_commands == nullptr || draw_size_dw == 0);
|
||||
@@ -1679,12 +1711,8 @@ int Gpu::GetFrameNum() const {
|
||||
return m_state->GetFrameNum();
|
||||
}
|
||||
|
||||
bool Gpu::IsCommandProcessorThread() noexcept {
|
||||
return g_current_processor != nullptr;
|
||||
}
|
||||
|
||||
CommandProcessor* Gpu::CurrentCommandProcessor() noexcept {
|
||||
return g_current_processor;
|
||||
bool Gpu::IsGpuThread() noexcept {
|
||||
return GpuState::IsGpuThread();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandProcessor;
|
||||
class GpuState;
|
||||
class RenderContext;
|
||||
|
||||
@@ -23,7 +22,6 @@ public:
|
||||
[[nodiscard]] bool IsStopping();
|
||||
void SendCommand(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSyncWithProcessor(Common::UniqueFunction<void, CommandProcessor&>&& command);
|
||||
|
||||
void Submit(uint32_t* draw_commands, uint32_t draw_size_dw, uint32_t* constant_commands,
|
||||
uint32_t constant_size_dw, bool trigger_agc_interrupt_on_done = false);
|
||||
@@ -33,8 +31,7 @@ public:
|
||||
void Done();
|
||||
[[nodiscard]] int GetFrameNum() const;
|
||||
|
||||
[[nodiscard]] static bool IsCommandProcessorThread() noexcept;
|
||||
[[nodiscard]] static CommandProcessor* CurrentCommandProcessor() noexcept;
|
||||
[[nodiscard]] static bool IsGpuThread() noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GpuState> m_state;
|
||||
|
||||
@@ -29,27 +29,27 @@ struct ColorView {
|
||||
struct ColorInfo {
|
||||
bool fmask_compression_enable = false;
|
||||
// uint32_t fmask_compression_mode = 0;
|
||||
bool fmask_data_compression_disable = false;
|
||||
bool fmask_one_frag_mode = false;
|
||||
bool cmask_fast_clear_enable = false;
|
||||
bool dcc_compression_enable = false;
|
||||
bool blend_clamp = false;
|
||||
bool blend_bypass = false;
|
||||
bool round_mode = false;
|
||||
uint32_t cmask_is_linear = 0;
|
||||
uint32_t cmask_addr_type = 0;
|
||||
bool alt_tile_mode = false;
|
||||
uint32_t format = 0;
|
||||
uint32_t channel_type = 0;
|
||||
uint32_t channel_order = 0;
|
||||
bool fmask_data_compression_disable = false;
|
||||
bool fmask_one_frag_mode = false;
|
||||
bool cmask_fast_clear_enable = false;
|
||||
bool dcc_compression_enable = false;
|
||||
bool blend_clamp = false;
|
||||
bool blend_bypass = false;
|
||||
bool round_mode = false;
|
||||
uint32_t cmask_is_linear = 0;
|
||||
uint32_t cmask_addr_type = 0;
|
||||
bool alt_tile_mode = false;
|
||||
Prospero::ChannelLayout format = Prospero::ChannelLayout::kInvalid;
|
||||
Prospero::ChannelType channel_type = Prospero::ChannelType::kUNorm;
|
||||
Prospero::ChannelOrder channel_order = Prospero::ChannelOrder::kStandard;
|
||||
};
|
||||
|
||||
struct ColorAttrib {
|
||||
bool force_dest_alpha_to_one = false;
|
||||
uint32_t tile_mode = 0;
|
||||
uint32_t fmask_tile_mode = 0;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
bool force_dest_alpha_to_one = false;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
Prospero::TileMode fmask_tile_mode = Prospero::TileMode::kLinear;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
};
|
||||
|
||||
struct ColorAttrib2 {
|
||||
@@ -59,11 +59,11 @@ struct ColorAttrib2 {
|
||||
};
|
||||
|
||||
struct ColorAttrib3 {
|
||||
uint32_t depth = 0;
|
||||
uint32_t tile_mode = 0;
|
||||
uint32_t dimension = 0;
|
||||
bool cmask_pipe_aligned = false;
|
||||
bool dcc_pipe_aligned = false;
|
||||
uint32_t depth = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
uint32_t dimension = 0;
|
||||
bool cmask_pipe_aligned = false;
|
||||
bool dcc_pipe_aligned = false;
|
||||
};
|
||||
|
||||
struct ColorDccControl {
|
||||
@@ -132,7 +132,7 @@ struct RenderTarget {
|
||||
};
|
||||
|
||||
struct DepthZInfo {
|
||||
uint32_t format = 0;
|
||||
Prospero::DepthFormat format = Prospero::DepthFormat::kInvalid;
|
||||
uint32_t num_samples = 0;
|
||||
Prospero::TextureCompatiblePlaneCompression texture_compatibility =
|
||||
Prospero::TextureCompatiblePlaneCompression::kDisable;
|
||||
@@ -144,16 +144,16 @@ struct DepthZInfo {
|
||||
|
||||
[[nodiscard]] static DepthZInfo Decode(uint32_t value) {
|
||||
DepthZInfo info;
|
||||
info.format = value & 0x3u;
|
||||
info.format = static_cast<Prospero::DepthFormat>(value & 0x3u);
|
||||
info.num_samples = (value >> 2u) & 0x3u;
|
||||
info.texture_compatibility = static_cast<Prospero::TextureCompatiblePlaneCompression>(
|
||||
value & Prospero::GpuEnumValue(Prospero::TextureCompatiblePlaneCompression::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::TextureCompatiblePlaneCompression::kBitMask));
|
||||
info.partially_resident = (value & 0x00001000u) != 0;
|
||||
info.max_mip_level = static_cast<uint8_t>((value >> 16u) & 0x0fu);
|
||||
info.expclear_enabled = (value & 0x08000000u) != 0;
|
||||
info.htile_acceleration = (value & 0x20000000u) != 0;
|
||||
info.z_compare_base = static_cast<Prospero::ZCompareBase>(
|
||||
value & Prospero::GpuEnumValue(Prospero::ZCompareBase::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::ZCompareBase::kBitMask));
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ struct DepthZInfo {
|
||||
};
|
||||
|
||||
struct DepthStencilInfo {
|
||||
uint32_t format = 0;
|
||||
Prospero::StencilFormat format = Prospero::StencilFormat::kInvalid;
|
||||
Prospero::TextureCompatibleStencil texture_compatibility =
|
||||
Prospero::TextureCompatibleStencil::kDisable;
|
||||
bool expclear_enabled = false;
|
||||
@@ -176,9 +176,9 @@ struct DepthStencilInfo {
|
||||
|
||||
[[nodiscard]] static DepthStencilInfo Decode(uint32_t value) {
|
||||
DepthStencilInfo info;
|
||||
info.format = value & 0x1u;
|
||||
info.format = static_cast<Prospero::StencilFormat>(value & 0x1u);
|
||||
info.texture_compatibility = static_cast<Prospero::TextureCompatibleStencil>(
|
||||
value & Prospero::GpuEnumValue(Prospero::TextureCompatibleStencil::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::TextureCompatibleStencil::kBitMask));
|
||||
info.partially_resident = (value & 0x00001000u) != 0;
|
||||
info.expclear_enabled = (value & 0x08000000u) != 0;
|
||||
info.htile_stencil_disabled = (value & 0x20000000u) != 0;
|
||||
@@ -348,6 +348,16 @@ struct ModeControl {
|
||||
bool persp_corr_dis = false;
|
||||
};
|
||||
|
||||
struct PolyOffset {
|
||||
int8_t neg_num_db_bits = -23;
|
||||
bool db_is_float_fmt = true;
|
||||
float clamp = 0.0f;
|
||||
float front_scale = 0.0f;
|
||||
float front_offset = 0.0f;
|
||||
float back_scale = 0.0f;
|
||||
float back_offset = 0.0f;
|
||||
};
|
||||
|
||||
struct BlendControl {
|
||||
uint8_t color_srcblend = 1;
|
||||
uint8_t color_comb_fcn = 0;
|
||||
@@ -482,6 +492,26 @@ struct VsShaderResource2 {
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct HsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool debug_mode = false;
|
||||
bool ieee_mode = false;
|
||||
bool require_forward_progress = false;
|
||||
bool threadgroup_configuration = false;
|
||||
uint8_t ls_vgpr_component_count = 0;
|
||||
bool fp16_overflow = false;
|
||||
};
|
||||
|
||||
struct HsShaderResource2 {
|
||||
bool scratch_en = false;
|
||||
uint8_t user_sgpr = 0;
|
||||
uint16_t lds_size = 0;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct PsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
@@ -500,7 +530,7 @@ struct PsShaderResource2 {
|
||||
uint8_t user_sgpr = 0;
|
||||
bool wave_cnt_en = false;
|
||||
uint8_t extra_lds_size = 0;
|
||||
bool raster_ordered_shading = false;
|
||||
uint8_t raster_ordered_shading = 0;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
@@ -513,22 +543,27 @@ struct PsStageRegisters {
|
||||
|
||||
struct CsStageRegisters {
|
||||
|
||||
uint64_t data_addr = 0;
|
||||
uint32_t num_thread_x = 0;
|
||||
uint32_t num_thread_y = 0;
|
||||
uint32_t num_thread_z = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t bulky = 0;
|
||||
uint8_t wave_size = 64;
|
||||
uint8_t scratch_en = 0;
|
||||
uint8_t user_sgpr = 0;
|
||||
uint8_t tgid_x_en = 0;
|
||||
uint8_t tgid_y_en = 0;
|
||||
uint8_t tgid_z_en = 0;
|
||||
uint8_t tg_size_en = 0;
|
||||
uint8_t tidig_comp_cnt = 0;
|
||||
uint8_t lds_size = 0;
|
||||
uint64_t data_addr = 0;
|
||||
uint32_t num_thread_x = 0;
|
||||
uint32_t num_thread_y = 0;
|
||||
uint32_t num_thread_z = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool ieee_mode = false;
|
||||
bool fp16_overflow = false;
|
||||
bool bulky = false;
|
||||
bool threadgroup_configuration = false;
|
||||
uint8_t wave_size = 64;
|
||||
bool scratch_en = false;
|
||||
uint8_t user_sgpr = 0;
|
||||
bool tgid_x_en = false;
|
||||
bool tgid_y_en = false;
|
||||
bool tgid_z_en = false;
|
||||
bool tg_size_en = false;
|
||||
uint8_t tidig_comp_cnt = 0;
|
||||
uint16_t lds_size = 0;
|
||||
};
|
||||
|
||||
struct EsStageRegisters {
|
||||
@@ -543,24 +578,24 @@ struct LsStageRegisters {
|
||||
|
||||
struct HsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
VsShaderResource1 rsrc1;
|
||||
VsShaderResource2 rsrc2;
|
||||
HsShaderResource1 rsrc1;
|
||||
HsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct GsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool debug_mode = false;
|
||||
bool ieee_mode = false;
|
||||
bool cu_group_enable = false;
|
||||
bool require_forward_progress = false;
|
||||
bool lds_configuration = false;
|
||||
uint8_t gs_vgpr_component_count = 0;
|
||||
bool fp16_overflow = false;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool debug_mode = false;
|
||||
bool ieee_mode = false;
|
||||
bool cu_group_enable = false;
|
||||
bool require_forward_progress = false;
|
||||
bool threadgroup_configuration = false;
|
||||
uint8_t gs_vgpr_component_count = 0;
|
||||
bool fp16_overflow = false;
|
||||
};
|
||||
|
||||
struct GsShaderResource2 {
|
||||
@@ -880,6 +915,8 @@ public:
|
||||
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
|
||||
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
|
||||
void SetModeControl(const ModeControl& control) { m_mode_control = control; }
|
||||
[[nodiscard]] const PolyOffset& GetPolyOffset() const { return m_poly_offset; }
|
||||
void SetPolyOffset(const PolyOffset& offset) { m_poly_offset = offset; }
|
||||
[[nodiscard]] const EqaaControl& GetEqaaControl() const { return m_eqaa_control; }
|
||||
void SetEqaaControl(const EqaaControl& control) { m_eqaa_control = control; }
|
||||
[[nodiscard]] const StencilControl& GetStencilControl() const { return m_stencil_control; }
|
||||
@@ -977,6 +1014,7 @@ private:
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
|
||||
ModeControl m_mode_control;
|
||||
PolyOffset m_poly_offset;
|
||||
EqaaControl m_eqaa_control;
|
||||
|
||||
ShaderRegisters m_sh_regs;
|
||||
@@ -991,8 +1029,8 @@ public:
|
||||
|
||||
void Reset() { *this = UserConfig(); }
|
||||
|
||||
void SetPrimitiveType(uint32_t prim_type) { m_prim_type = prim_type; }
|
||||
[[nodiscard]] uint32_t GetPrimType() const { return m_prim_type; }
|
||||
void SetPrimitiveType(Prospero::PrimitiveType prim_type) { m_prim_type = prim_type; }
|
||||
[[nodiscard]] Prospero::PrimitiveType GetPrimType() const { return m_prim_type; }
|
||||
void SetIndexOffset(uint32_t index_offset) { m_index_offset = index_offset; }
|
||||
[[nodiscard]] uint32_t GetIndexOffset() const { return m_index_offset; }
|
||||
void SetObjectId(uint32_t object_id) { m_object_id = object_id; }
|
||||
@@ -1011,9 +1049,9 @@ public:
|
||||
void SetGdsOaAddress(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].address = value; }
|
||||
|
||||
private:
|
||||
uint32_t m_prim_type = 0;
|
||||
uint32_t m_index_offset = 0;
|
||||
uint32_t m_object_id = 0;
|
||||
Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone;
|
||||
uint32_t m_index_offset = 0;
|
||||
uint32_t m_object_id = 0;
|
||||
|
||||
GeControl m_ge_cntl;
|
||||
GeUserVgprEn m_ge_user_vgpr_en;
|
||||
@@ -1034,8 +1072,8 @@ public:
|
||||
void SetLsShaderResource1(const VsShaderResource1& rsrc1) { m_vs.ls_regs.rsrc1 = rsrc1; }
|
||||
void SetLsShaderResource2(const VsShaderResource2& rsrc2) { m_vs.ls_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderBase(uint64_t addr) { m_vs.hs_regs.data_addr = addr; }
|
||||
void SetHsShaderResource1(const VsShaderResource1& rsrc1) { m_vs.hs_regs.rsrc1 = rsrc1; }
|
||||
void SetHsShaderResource2(const VsShaderResource2& rsrc2) { m_vs.hs_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderResource1(const HsShaderResource1& rsrc1) { m_vs.hs_regs.rsrc1 = rsrc1; }
|
||||
void SetHsShaderResource2(const HsShaderResource2& rsrc2) { m_vs.hs_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderChksum(uint32_t value) { m_vs.hs_regs.chksum = value; }
|
||||
void SetGsShaderBase(uint64_t addr) { m_vs.gs_regs.data_addr = addr; }
|
||||
void SetGsShaderResource1(const GsShaderResource1& rsrc1) { m_vs.gs_regs.rsrc1 = rsrc1; }
|
||||
|
||||
@@ -30,6 +30,7 @@ constexpr auto MakeRegisterNames() {
|
||||
names[R_FLIP] = "R_FLIP";
|
||||
names[R_RELEASE_MEM] = "R_RELEASE_MEM";
|
||||
names[R_DMA_DATA] = "R_DMA_DATA";
|
||||
names[R_CONTEXT_STATE] = "R_CONTEXT_STATE";
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
+134
-87
@@ -87,6 +87,7 @@ constexpr uint32_t R_WAIT_MEM_64 = 0x16;
|
||||
constexpr uint32_t R_FLIP = 0x17;
|
||||
constexpr uint32_t R_RELEASE_MEM = 0x18;
|
||||
constexpr uint32_t R_DMA_DATA = 0x19;
|
||||
constexpr uint32_t R_CONTEXT_STATE = 0x1A;
|
||||
|
||||
constexpr uint32_t R_NUM = 0x3F + 1;
|
||||
|
||||
@@ -371,6 +372,7 @@ constexpr uint32_t PA_CL_UCP_0_X = 0x16F;
|
||||
constexpr uint32_t PA_CL_UCP_0_Y = 0x170;
|
||||
constexpr uint32_t PA_CL_UCP_0_Z = 0x171;
|
||||
constexpr uint32_t PA_CL_UCP_0_W = 0x172;
|
||||
constexpr uint32_t PA_CL_UCP_5_W = 0x186;
|
||||
constexpr uint32_t SPI_PS_INPUT_CNTL_0 = 0x191;
|
||||
constexpr uint32_t SPI_PS_INPUT_CNTL_31 = 0x1B0;
|
||||
constexpr uint32_t SPI_VS_OUT_CONFIG = 0x1B1;
|
||||
@@ -577,24 +579,28 @@ constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_SHIFT = 16;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0;
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1;
|
||||
constexpr uint32_t VGT_GS_MAX_VERT_OUT = 0x2CE;
|
||||
constexpr uint32_t GE_NGG_SUBGRP_CNTL = 0x2D3;
|
||||
constexpr uint32_t VGT_TESS_DISTRIBUTION = 0x2D4;
|
||||
constexpr uint32_t VGT_SHADER_STAGES_EN = 0x2D5;
|
||||
constexpr uint32_t VGT_LS_HS_CONFIG = 0x2D6;
|
||||
constexpr uint32_t VGT_TF_PARAM = 0x2DB;
|
||||
constexpr uint32_t DB_ALPHA_TO_MASK = 0x2DC;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL = 0x2DE;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_CLAMP = 0x2DF;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_SCALE = 0x2E0;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_OFFSET = 0x2E1;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_BACK_SCALE = 0x2E2;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_BACK_OFFSET = 0x2E3;
|
||||
constexpr uint32_t VGT_GS_INSTANCE_CNT = 0x2E4;
|
||||
constexpr uint32_t PA_SC_CENTROID_PRIORITY_0 = 0x2F5;
|
||||
constexpr uint32_t PA_SC_CENTROID_PRIORITY_1 = 0x2F6;
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0;
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1;
|
||||
constexpr uint32_t VGT_GS_MAX_VERT_OUT = 0x2CE;
|
||||
constexpr uint32_t GE_NGG_SUBGRP_CNTL = 0x2D3;
|
||||
constexpr uint32_t VGT_TESS_DISTRIBUTION = 0x2D4;
|
||||
constexpr uint32_t VGT_SHADER_STAGES_EN = 0x2D5;
|
||||
constexpr uint32_t VGT_LS_HS_CONFIG = 0x2D6;
|
||||
constexpr uint32_t VGT_TF_PARAM = 0x2DB;
|
||||
constexpr uint32_t DB_ALPHA_TO_MASK = 0x2DC;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL = 0x2DE;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_NEG_NUM_DB_BITS_SHIFT = 0;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_NEG_NUM_DB_BITS_MASK = 0xFF;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_DB_IS_FLOAT_FMT_SHIFT = 8;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_DB_FMT_CNTL_DB_IS_FLOAT_FMT_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_CLAMP = 0x2DF;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_SCALE = 0x2E0;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_FRONT_OFFSET = 0x2E1;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_BACK_SCALE = 0x2E2;
|
||||
constexpr uint32_t PA_SU_POLY_OFFSET_BACK_OFFSET = 0x2E3;
|
||||
constexpr uint32_t VGT_GS_INSTANCE_CNT = 0x2E4;
|
||||
constexpr uint32_t PA_SC_CENTROID_PRIORITY_0 = 0x2F5;
|
||||
constexpr uint32_t PA_SC_CENTROID_PRIORITY_1 = 0x2F6;
|
||||
|
||||
constexpr uint32_t PA_SC_AA_CONFIG = 0x2F8;
|
||||
constexpr uint32_t PA_SC_AA_CONFIG_MSAA_NUM_SAMPLES_SHIFT = 0;
|
||||
@@ -778,21 +784,21 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_FWD_PROGRESS_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_FP16_OVFL_SHIFT = 29;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_FP16_OVFL_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS = 0xB;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_SHIFT = 1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MASK = 0x1F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_SHIFT = 7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_EXTRA_LDS_SIZE_SHIFT = 8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_EXTRA_LDS_SIZE_MASK = 0xFF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_LOAD_INTRAWAVE_COLLISION_SHIFT = 26;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_LOAD_INTRAWAVE_COLLISION_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MSB_SHIFT = 27;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS = 0xB;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_SHIFT = 1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MASK = 0x1F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_SHIFT = 7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_EXTRA_LDS_SIZE_SHIFT = 8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_EXTRA_LDS_SIZE_MASK = 0xFF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_RASTER_ORDERED_SHADING_SHIFT = 25;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_RASTER_ORDERED_SHADING_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MSB_SHIFT = 27;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_0 = 0xC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_15 = 0x1B;
|
||||
@@ -885,61 +891,101 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_GS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_GS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_GS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_0 = 0x8C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_15 = 0x9B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_31 = 0xAB;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_ESGS = 0xB0;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_0 = 0xB2;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_3 = 0xB5;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES_GS = 0xBC;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_ES = 0xC8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_ES = 0xC9;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_ES = 0xCA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES = 0xCB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_0 = 0xCC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_15 = 0xDB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_31 = 0xEB;
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_HS = 0x101;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_HS = 0x102;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_HS = 0x103;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_HS = 0x107;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_HS = 0x108;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_HS = 0x109;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS = 0x10A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS = 0x10B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_0 = 0x10C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_15 = 0x11B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_31 = 0x12B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_LSHS = 0x130;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_0 = 0x132;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_3 = 0x135;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_LS = 0x148;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_LS = 0x149;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_LS = 0x14A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_LS = 0x14B;
|
||||
constexpr uint32_t COMPUTE_START_X = 0x204;
|
||||
constexpr uint32_t COMPUTE_START_Y = 0x205;
|
||||
constexpr uint32_t COMPUTE_START_Z = 0x206;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_X = 0x207;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Y = 0x208;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Z = 0x209;
|
||||
constexpr uint32_t COMPUTE_PGM_LO = 0x20C;
|
||||
constexpr uint32_t COMPUTE_PGM_HI = 0x20D;
|
||||
constexpr uint32_t COMPUTE_TBA_LO = 0x20E;
|
||||
constexpr uint32_t COMPUTE_TBA_HI = 0x20F;
|
||||
constexpr uint32_t COMPUTE_TMA_LO = 0x210;
|
||||
constexpr uint32_t COMPUTE_TMA_HI = 0x211;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_0 = 0x8C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_15 = 0x9B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_31 = 0xAB;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_ESGS = 0xB0;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_0 = 0xB2;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_3 = 0xB5;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES_GS = 0xBC;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_ES = 0xC8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_ES = 0xC9;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_ES = 0xCA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES = 0xCB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_0 = 0xCC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_15 = 0xDB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_31 = 0xEB;
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_HS = 0x101;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_HS = 0x102;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_HS = 0x103;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_HS = 0x107;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_HS = 0x108;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_HS = 0x109;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS = 0x10A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS = 0x10B;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FLOAT_MODE_SHIFT = 12;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FLOAT_MODE_MASK = 0xFF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_DX10_CLAMP_SHIFT = 21;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_DX10_CLAMP_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_DEBUG_MODE_SHIFT = 22;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_DEBUG_MODE_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_IEEE_MODE_SHIFT = 23;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_IEEE_MODE_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FWD_PROGRESS_SHIFT = 25;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FWD_PROGRESS_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_WGP_MODE_SHIFT = 26;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_WGP_MODE_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_LS_VGPR_COMP_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_LS_VGPR_COMP_CNT_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FP16_OVFL_SHIFT = 30;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS_FP16_OVFL_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SCRATCH_EN_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SCRATCH_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_USER_SGPR_SHIFT = 1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_USER_SGPR_MASK = 0x1F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_LDS_SIZE_SHIFT = 18;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_LDS_SIZE_MASK = 0x1FF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_USER_SGPR_MSB_SHIFT = 27;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_0 = 0x10C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_15 = 0x11B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_31 = 0x12B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_LSHS = 0x130;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_0 = 0x132;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_3 = 0x135;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_LS = 0x148;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_LS = 0x149;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_LS = 0x14A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_LS = 0x14B;
|
||||
constexpr uint32_t COMPUTE_START_X = 0x204;
|
||||
constexpr uint32_t COMPUTE_START_Y = 0x205;
|
||||
constexpr uint32_t COMPUTE_START_Z = 0x206;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_X = 0x207;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Y = 0x208;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Z = 0x209;
|
||||
constexpr uint32_t COMPUTE_PGM_LO = 0x20C;
|
||||
constexpr uint32_t COMPUTE_PGM_HI = 0x20D;
|
||||
constexpr uint32_t COMPUTE_TBA_LO = 0x20E;
|
||||
constexpr uint32_t COMPUTE_TBA_HI = 0x20F;
|
||||
constexpr uint32_t COMPUTE_TMA_LO = 0x210;
|
||||
constexpr uint32_t COMPUTE_TMA_HI = 0x211;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1 = 0x212;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_SHIFT = 24;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_SHIFT = 30;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1 = 0x212;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_SHIFT = 12;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_MASK = 0xFF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_SHIFT = 21;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_SHIFT = 23;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_SHIFT = 24;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_SHIFT = 26;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_SHIFT = 29;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_SHIFT = 30;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2 = 0x213;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT = 0;
|
||||
@@ -1002,6 +1048,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C;
|
||||
constexpr uint32_t GE_USER_VGPR2 = 0x25D;
|
||||
constexpr uint32_t GE_USER_VGPR3 = 0x25E;
|
||||
constexpr uint32_t GE_STEREO_CNTL = 0x25F;
|
||||
constexpr uint32_t GE_PC_ALLOC = 0x260;
|
||||
|
||||
constexpr uint32_t GE_USER_VGPR_EN = 0x262;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0;
|
||||
|
||||
+333
-629
File diff suppressed because it is too large.
Load diff
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -39,7 +40,7 @@ enum class TileBlockFamily : uint32_t {
|
||||
};
|
||||
|
||||
struct TileBlockLayout {
|
||||
TileBlockFamily family = TileBlockFamily::Standard256B;
|
||||
TileBlockFamily family = TileBlockFamily::Count;
|
||||
uint32_t bytes_per_element = 0;
|
||||
uint32_t block_size = 0;
|
||||
uint32_t block_width = 0;
|
||||
@@ -47,42 +48,73 @@ struct TileBlockLayout {
|
||||
uint32_t block_depth = 0;
|
||||
};
|
||||
|
||||
struct TileVolumeLayout {
|
||||
TileBlockFamily family = TileBlockFamily::Count;
|
||||
uint32_t bytes_per_element = 0;
|
||||
uint32_t texel_width = 1;
|
||||
uint32_t texel_height = 1;
|
||||
uint32_t first_tail_level = 0;
|
||||
uint32_t block_depth = 1;
|
||||
uint64_t block_slice_size = 0;
|
||||
uint64_t total_size = 0;
|
||||
uint64_t level_offsets[16] = {};
|
||||
uint64_t level_sizes[16] = {};
|
||||
uint32_t tail_x[16] = {};
|
||||
uint32_t tail_y[16] = {};
|
||||
uint32_t level_widths[16] = {};
|
||||
uint32_t level_heights[16] = {};
|
||||
struct TileTextureElementLayout {
|
||||
uint32_t bytes = 0;
|
||||
uint32_t texel_width = 1;
|
||||
uint32_t texel_height = 1;
|
||||
};
|
||||
|
||||
struct TileTextureBlockLayout {
|
||||
TileBlockLayout block;
|
||||
uint32_t texel_width = 1;
|
||||
uint32_t texel_height = 1;
|
||||
};
|
||||
|
||||
enum class TileSurfaceDimension : uint32_t {
|
||||
Dim2D,
|
||||
Dim3D,
|
||||
};
|
||||
|
||||
struct TileSurfaceDescription {
|
||||
Prospero::BufferFormat format = Prospero::BufferFormat::kInvalid;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
TileSurfaceDimension dimension = TileSurfaceDimension::Dim2D;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 1;
|
||||
uint32_t levels = 1;
|
||||
uint32_t layers = 1;
|
||||
};
|
||||
|
||||
// Width/height fields are measured in addressable elements, not texture texels.
|
||||
struct TileMipLayout {
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t padded_width = 0;
|
||||
uint32_t padded_height = 0;
|
||||
uint32_t tail_x = 0;
|
||||
uint32_t tail_y = 0;
|
||||
};
|
||||
|
||||
struct TileSurfaceLayout {
|
||||
TileSurfaceDescription description;
|
||||
TileTextureBlockLayout texture;
|
||||
uint32_t first_tail_level = 0;
|
||||
uint64_t block_slice_size = 0;
|
||||
uint64_t total_size = 0;
|
||||
TileMipLayout mips[16] = {};
|
||||
};
|
||||
|
||||
bool TileGetBlockLayout(TileBlockFamily family, uint32_t bytes_per_element,
|
||||
TileBlockLayout& layout);
|
||||
bool TileGetTextureElementLayout(Prospero::BufferFormat format, TileTextureElementLayout& layout);
|
||||
bool TileGetTextureBlockLayout(Prospero::BufferFormat format, Prospero::TileMode tile, bool volume,
|
||||
TileTextureBlockLayout& layout);
|
||||
bool TileGetTiledTextureLayout(const TileSurfaceDescription& description,
|
||||
TileSurfaceLayout& layout);
|
||||
bool TileGetBlockOffset(const TileBlockLayout& layout, uint32_t x, uint32_t y, uint32_t z,
|
||||
uint32_t& byte_offset);
|
||||
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
|
||||
uint32_t& byte_offset);
|
||||
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
|
||||
uint32_t block_z, uint32_t& byte_offset);
|
||||
bool TileIsStandard256BTextureSupported(uint32_t format);
|
||||
bool TileIsStandard4KBTextureSupported(uint32_t format);
|
||||
bool TileIsStandard64KBTextureSupported(uint32_t format);
|
||||
bool TileGetTextureVolumeLayout(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t levels, uint32_t tile, TileVolumeLayout& layout);
|
||||
|
||||
bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& htile_size);
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
|
||||
uint32_t stencil_format, bool htile, TileSizeAlign& stencil_size,
|
||||
TileSizeAlign& htile_size, TileSizeAlign& depth_size,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
Prospero::DepthFormat z_format, Prospero::StencilFormat stencil_format,
|
||||
bool htile, TileSizeAlign& stencil_size, TileSizeAlign& htile_size,
|
||||
TileSizeAlign& depth_size, uint32_t num_fragments_log2 = 0);
|
||||
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
@@ -94,13 +126,14 @@ bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t
|
||||
uint32_t bytes_per_element, uint32_t levels,
|
||||
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
|
||||
TilePaddedSize* padded_size);
|
||||
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t levels, uint32_t tile, TileSizeAlign* total_size,
|
||||
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
|
||||
TileSizeOffset* level_sizes, TilePaddedSize* padded_size);
|
||||
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t pitch, uint32_t levels, uint32_t tile, bool volume_texture,
|
||||
TileSizeAlign& total_size);
|
||||
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t levels, uint32_t tile);
|
||||
void TileGetTextureTotalSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t levels, Prospero::TileMode tile,
|
||||
bool volume_texture, TileSizeAlign& total_size);
|
||||
uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
|
||||
Prospero::TileMode tile);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ RegionManager* MemoryTracker::GetOrCreateRegion(uint64_t index) {
|
||||
|
||||
bool MemoryTracker::IsRegionCpuModified(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
return Iterate<true>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
return manager->IsModified<DirtySource::Cpu>(offset, bytes);
|
||||
@@ -88,7 +87,6 @@ bool MemoryTracker::IsRegionCpuModified(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
bool MemoryTracker::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
return Iterate<false>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
return manager->IsModified<DirtySource::Gpu>(offset, bytes);
|
||||
@@ -97,7 +95,6 @@ bool MemoryTracker::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
void MemoryTracker::MarkRegionAsCpuModified(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
Iterate<true>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
@@ -106,7 +103,6 @@ void MemoryTracker::MarkRegionAsCpuModified(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
void MemoryTracker::MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
Iterate<true>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
manager->ChangeState<DirtySource::Gpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
@@ -115,14 +111,13 @@ void MemoryTracker::MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
void MemoryTracker::UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
Iterate<false>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
manager->ChangeState<DirtySource::Gpu, false>(manager->GetCpuAddr() + offset, bytes);
|
||||
});
|
||||
}
|
||||
|
||||
void MemoryTracker::UntrackMemoryLocked(uint64_t vaddr, uint64_t size) {
|
||||
void MemoryTracker::UntrackMemoryImpl(uint64_t vaddr, uint64_t size) {
|
||||
std::vector<RegionManager*> managers;
|
||||
managers.reserve((vaddr % TRACKER_REGION_SIZE + size + TRACKER_REGION_SIZE - 1) /
|
||||
TRACKER_REGION_SIZE);
|
||||
@@ -148,8 +143,7 @@ void MemoryTracker::UntrackMemoryLocked(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::lock_guard access(m_access_mutex);
|
||||
UntrackMemoryLocked(vaddr, size);
|
||||
UntrackMemoryImpl(vaddr, size);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -29,45 +29,29 @@ public:
|
||||
void MarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
||||
void UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size);
|
||||
void UntrackMemory(uint64_t vaddr, uint64_t size);
|
||||
// Removes protection from a range and flushes GPU-owned data when required.
|
||||
template <typename Flush>
|
||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) {
|
||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) noexcept {
|
||||
static_assert(std::is_invocable_v<Flush&>);
|
||||
CheckNotInUploadCallback();
|
||||
ValidateRange(vaddr, size);
|
||||
|
||||
const auto update_cpu_state = [this, vaddr, size] {
|
||||
std::lock_guard access(m_access_mutex);
|
||||
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 bool should_flush = [&] {
|
||||
// Perform both the GPU modification check and CPU state change with the lock in
|
||||
// case the GPU thread is racing to mark the page modified. If a flush is needed,
|
||||
// on_flush performs the CPU state change.
|
||||
std::scoped_lock lock(manager->lock);
|
||||
if (manager->IsModified<DirtySource::Gpu>(offset, bytes)) {
|
||||
return true;
|
||||
}
|
||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
return false;
|
||||
}();
|
||||
if (should_flush) {
|
||||
on_flush();
|
||||
}
|
||||
const bool gpu_modified = Iterate<false>(
|
||||
vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
return manager->IsModified<DirtySource::Gpu>(offset, bytes);
|
||||
});
|
||||
if (gpu_modified) {
|
||||
return true;
|
||||
}
|
||||
Iterate<false>(vaddr, size,
|
||||
[](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->ChangeState<DirtySource::Cpu, true>(
|
||||
manager->GetCpuAddr() + offset, bytes);
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!update_cpu_state()) {
|
||||
return;
|
||||
}
|
||||
std::forward<Flush>(on_flush)();
|
||||
if (update_cpu_state()) {
|
||||
EXIT("memory invalidation retained GPU-owned pages\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
#if KYTY_BUILD == KYTY_BUILD_DEBUG
|
||||
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
@@ -84,7 +68,6 @@ public:
|
||||
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::lock_guard access(m_access_mutex);
|
||||
std::vector<RegionManager*> managers;
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t, uint64_t) {
|
||||
managers.push_back(manager);
|
||||
@@ -125,7 +108,6 @@ public:
|
||||
static_assert(std::is_nothrow_invocable_v<RangeFunc&, uint64_t, uint64_t>);
|
||||
static_assert(std::is_nothrow_invocable_v<UploadFunc&>);
|
||||
CheckNotInUploadCallback();
|
||||
std::unique_lock access(m_access_mutex);
|
||||
Iterate<true>(vaddr, size, [](RegionManager*, uint64_t, uint64_t) {});
|
||||
const auto* previous_upload_owner = std::exchange(s_upload_owner, this);
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
@@ -189,13 +171,12 @@ private:
|
||||
}
|
||||
|
||||
static void ValidateRange(uint64_t vaddr, uint64_t size);
|
||||
void UntrackMemoryLocked(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;
|
||||
std::vector<std::unique_ptr<RegionManager>> m_region_storage;
|
||||
std::mutex m_region_mutex;
|
||||
std::mutex m_access_mutex;
|
||||
PageManager& m_page_manager;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
#include "graphics/host_gpu/regionDefinitions.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
@@ -52,20 +53,20 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
std::fputs("PageManager fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
#if !defined(__APPLE__)
|
||||
void* frames[16] {};
|
||||
int frame_count = static_cast<int>(std::size(frames));
|
||||
SysStackWalk(frames, &frame_count);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void* frames[16] {};
|
||||
const auto frame_count =
|
||||
CaptureStackBackTrace(0, static_cast<DWORD>(std::size(frames)), frames, nullptr);
|
||||
const auto image_base = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
for (uint16_t i = 0; i < frame_count; i++) {
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
const auto address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
std::fprintf(stderr, " frame[%u]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
|
||||
std::fprintf(stderr, " frame[%d]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
|
||||
address, address >= image_base ? address - image_base : 0);
|
||||
}
|
||||
#elif !defined(__APPLE__)
|
||||
void* frames[16] {};
|
||||
const int frame_count = ::backtrace(frames, static_cast<int>(std::size(frames)));
|
||||
#else
|
||||
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
|
||||
#endif
|
||||
#endif
|
||||
std::fflush(stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
+159
-191
@@ -3,15 +3,17 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/guest_gpu/graphicsRun.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -20,30 +22,9 @@ namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
thread_local const void* g_cache_lock_owner = nullptr;
|
||||
|
||||
constexpr uint64_t MiB = 1024 * 1024;
|
||||
constexpr uint64_t GdsBufferSize = 64 * 1024;
|
||||
|
||||
class FaultSafeCacheLock final {
|
||||
public:
|
||||
FaultSafeCacheLock(const void* owner, Common::Mutex& mutex): m_mutex(mutex) {
|
||||
if (g_cache_lock_owner != nullptr) {
|
||||
EXIT("BufferCache: recursive cache lock acquisition\n");
|
||||
}
|
||||
g_cache_lock_owner = owner;
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
~FaultSafeCacheLock() {
|
||||
m_mutex.Unlock();
|
||||
g_cache_lock_owner = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::Mutex& m_mutex;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
uint64_t BufferCache::AlignDown(uint64_t value) noexcept {
|
||||
@@ -57,15 +38,6 @@ uint64_t BufferCache::AlignUp(uint64_t value) {
|
||||
return (value + CACHING_PAGE_SIZE - 1) & ~(CACHING_PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
bool BufferCache::PageOverlaps(uint64_t left, uint64_t left_size, uint64_t right,
|
||||
uint64_t right_size) noexcept {
|
||||
const auto left_begin = left & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto left_end = (left + left_size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto right_begin = right & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto right_end = (right + right_size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
|
||||
return left_begin < right_end && right_begin < left_end;
|
||||
}
|
||||
|
||||
void BufferCache::Upload(CommandBuffer& command, Buffer& destination, uint64_t destination_offset,
|
||||
const void* source, uint64_t size) {
|
||||
auto* bytes = static_cast<const uint8_t*>(source);
|
||||
@@ -198,27 +170,23 @@ void BufferCache::QueueGarbageDownload(std::span<const DownloadCopy> copies, Ret
|
||||
m_scheduler.DeferOperation(
|
||||
[this, downloads = std::move(downloads), retire = std::move(retire)]() mutable {
|
||||
PublishDownloads(downloads);
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& range: downloads) {
|
||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||
}
|
||||
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
|
||||
// interval on those pages was downloaded and removed. Clearing the original
|
||||
// query therefore cannot orphan a dirty sibling on an edge page.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
|
||||
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
|
||||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
|
||||
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
|
||||
}
|
||||
m_memory_tracker.UntrackMemory(retire.address, retire.size);
|
||||
for (const auto& range: downloads) {
|
||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||
}
|
||||
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
|
||||
// interval on those pages was downloaded and removed. Clearing the original
|
||||
// query therefore cannot orphan a dirty sibling on an edge page.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
|
||||
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
|
||||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
|
||||
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
|
||||
}
|
||||
m_memory_tracker.UntrackMemory(retire.address, retire.size);
|
||||
});
|
||||
}
|
||||
|
||||
BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
PageManager& page_manager, TextureCache& texture_cache,
|
||||
ResourceMutex& resource_mutex)
|
||||
PageManager& page_manager, TextureCache& texture_cache)
|
||||
: m_graphics(graphics), m_scheduler(scheduler),
|
||||
m_gds_buffer(graphics, scheduler, MemoryUsage::Stream, 0, AllFlags, GdsBufferSize),
|
||||
m_memory_tracker(page_manager),
|
||||
@@ -226,7 +194,7 @@ BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
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_resource_mutex(resource_mutex) {
|
||||
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());
|
||||
if (!m_graphics.CanReportMemoryUsage()) {
|
||||
@@ -284,58 +252,77 @@ void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
EXIT("BufferCache: invalid memory-invalidation range\n");
|
||||
}
|
||||
if (!HasPageOverlap(vaddr, size)) {
|
||||
return;
|
||||
}
|
||||
m_memory_tracker.InvalidateRegion(vaddr, size,
|
||||
[this, vaddr, size] { ReadMemory(vaddr, size); });
|
||||
[this, vaddr, size] { ReadMemory(vaddr, size, true); });
|
||||
}
|
||||
|
||||
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
|
||||
std::vector<DownloadCopy> copies;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
vaddr, size,
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||
"memory invalidation");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
for (uint64_t copied = 0; copied < range.size;) {
|
||||
const auto copy_address = range.address + copied;
|
||||
auto owner = m_buffers.upper_bound(copy_address);
|
||||
if (owner == m_buffers.begin()) {
|
||||
EXIT("BufferCache: invalidation readback has no buffer owner\n");
|
||||
}
|
||||
auto& cached = *std::prev(owner)->second;
|
||||
if (!cached.buffer->IsInBounds(copy_address, 1)) {
|
||||
EXIT(
|
||||
"BufferCache: invalidation readback is outside its buffer owner\n");
|
||||
}
|
||||
const auto copy_size = std::min(range.size - copied,
|
||||
cached.vaddr + cached.size - copy_address);
|
||||
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
|
||||
copy_address, copy_size});
|
||||
copied += copy_size;
|
||||
}
|
||||
}
|
||||
});
|
||||
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||
if (Gpu::IsGpuThread()) {
|
||||
ReadMemoryOnGpu(vaddr, size, is_write);
|
||||
return;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported buffer readback from an asynchronous GPU completion, "
|
||||
"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;
|
||||
auto owner = m_buffers.upper_bound(copy_address);
|
||||
if (owner == m_buffers.begin()) {
|
||||
EXIT("BufferCache: invalidation readback has no buffer owner\n");
|
||||
}
|
||||
auto& cached = *std::prev(owner)->second;
|
||||
if (!cached.buffer->IsInBounds(copy_address, 1)) {
|
||||
EXIT("BufferCache: invalidation readback is outside its buffer owner\n");
|
||||
}
|
||||
const auto copy_size =
|
||||
std::min(range.size - copied, cached.vaddr + cached.size - copy_address);
|
||||
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
|
||||
copy_address, copy_size});
|
||||
copied += copy_size;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (copies.empty()) {
|
||||
if (!is_write) {
|
||||
return;
|
||||
}
|
||||
// A preceding read fault can consume the last GPU-owned copy after this write invalidation
|
||||
// has already chosen to flush. Complete the CPU ownership handoff even though this callback
|
||||
// no longer has bytes to download.
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
return;
|
||||
}
|
||||
auto downloads = RecordDownloads(copies);
|
||||
m_scheduler.FinishCurrent();
|
||||
PublishDownloads(downloads);
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& range: downloads) {
|
||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||
}
|
||||
// The enumeration above covered whole dirty pages and every exact interval on them.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
||||
for (const auto& range: downloads) {
|
||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||
}
|
||||
// The enumeration above covered whole dirty pages and every exact interval on them.
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
||||
if (is_write) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,43 +333,40 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||
std::vector<DownloadCopy> copies;
|
||||
std::vector<std::pair<uint64_t, uint64_t>> modified_buffers;
|
||||
std::vector<std::pair<uint64_t, uint64_t>> retired_buffers;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& [begin, cached]: m_buffers) {
|
||||
if (vaddr < begin + cached->size && begin < vaddr + size) {
|
||||
retired_buffers.emplace_back(begin, cached->size);
|
||||
}
|
||||
for (const auto& [begin, cached]: m_buffers) {
|
||||
if (vaddr < begin + cached->size && begin < vaddr + size) {
|
||||
retired_buffers.emplace_back(begin, cached->size);
|
||||
}
|
||||
for (const auto& [begin, cached]: m_buffers) {
|
||||
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
|
||||
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
|
||||
continue;
|
||||
}
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
|
||||
if (dirty.empty()) {
|
||||
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
|
||||
}
|
||||
modified_buffers.emplace_back(begin, cached->size);
|
||||
}
|
||||
for (const auto& [begin, cached]: m_buffers) {
|
||||
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
|
||||
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& [begin, bytes]: modified_buffers) {
|
||||
auto owner = m_buffers.find(begin);
|
||||
if (owner == m_buffers.end() || owner->second->size != bytes) {
|
||||
EXIT("BufferCache: unmap owner changed during collection\n");
|
||||
}
|
||||
auto& cached = *owner->second;
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
begin, cached.size,
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||
"unmap");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
copies.push_back(
|
||||
{cached.buffer, range.address - begin, range.address, range.size});
|
||||
}
|
||||
});
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
|
||||
if (dirty.empty()) {
|
||||
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
|
||||
}
|
||||
modified_buffers.emplace_back(begin, cached->size);
|
||||
}
|
||||
for (const auto& [begin, bytes]: modified_buffers) {
|
||||
auto owner = m_buffers.find(begin);
|
||||
if (owner == m_buffers.end() || owner->second->size != bytes) {
|
||||
EXIT("BufferCache: unmap owner changed during collection\n");
|
||||
}
|
||||
auto& cached = *owner->second;
|
||||
m_memory_tracker.ForEachDownloadRange<false>(
|
||||
begin, cached.size,
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||
"unmap");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
copies.push_back(
|
||||
{cached.buffer, range.address - begin, range.address, range.size});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!copies.empty()) {
|
||||
auto downloads = RecordDownloads(copies);
|
||||
@@ -393,29 +377,26 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||
// command stream before removing such backing.
|
||||
m_scheduler.FinishCurrent();
|
||||
}
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& [begin, bytes]: modified_buffers) {
|
||||
m_gpu_modified_ranges.Subtract(begin, bytes);
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
|
||||
}
|
||||
for (const auto& [begin, bytes]: retired_buffers) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
|
||||
}
|
||||
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
|
||||
EXIT("BufferCache: unmap retained dirty byte ranges\n");
|
||||
}
|
||||
m_memory_tracker.UntrackMemory(vaddr, size);
|
||||
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
|
||||
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
|
||||
if (it->second->size > m_total_used_memory) {
|
||||
EXIT("BufferCache: allocation accounting underflow\n");
|
||||
}
|
||||
m_total_used_memory -= it->second->size;
|
||||
it = m_buffers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
for (const auto& [begin, bytes]: modified_buffers) {
|
||||
m_gpu_modified_ranges.Subtract(begin, bytes);
|
||||
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
|
||||
}
|
||||
for (const auto& [begin, bytes]: retired_buffers) {
|
||||
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
|
||||
}
|
||||
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
|
||||
EXIT("BufferCache: unmap retained dirty byte ranges\n");
|
||||
}
|
||||
m_memory_tracker.UntrackMemory(vaddr, size);
|
||||
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
|
||||
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
|
||||
if (it->second->size > m_total_used_memory) {
|
||||
EXIT("BufferCache: allocation accounting underflow\n");
|
||||
}
|
||||
m_total_used_memory -= it->second->size;
|
||||
it = m_buffers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -497,7 +478,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
||||
if (command.IsInvalid() || command.IsExecute()) {
|
||||
EXIT("BufferCache: buffer request requires a recording command buffer\n");
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
|
||||
if (is_read && !is_written && size <= CACHING_PAGE_SIZE &&
|
||||
!m_memory_tracker.IsRegionGpuModified(vaddr, size) &&
|
||||
@@ -524,7 +504,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
||||
(void)m_texture_cache.InvalidateMemoryFromGPU(vaddr, size, true);
|
||||
}
|
||||
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
auto& cached = GetOrCreateBuffer(command, vaddr, size);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> uploads;
|
||||
m_memory_tracker.ForEachUploadRange(
|
||||
@@ -539,26 +518,23 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
||||
if (is_written) {
|
||||
m_gpu_modified_ranges.Add(vaddr, size);
|
||||
}
|
||||
auto owner = cached.buffer;
|
||||
uint64_t offset = cached.buffer->Offset(vaddr);
|
||||
if (is_formatted && is_read && !is_written) {
|
||||
(void)SynchronizeBufferFromImage(*cached.buffer, vaddr, size);
|
||||
(void)SynchronizeBufferFromImage(*owner, vaddr, size);
|
||||
}
|
||||
return {cached.buffer, cached.buffer->Handle(), cached.buffer->Offset(vaddr)};
|
||||
return {owner, owner->Handle(), offset};
|
||||
}
|
||||
|
||||
std::shared_ptr<Buffer> BufferCache::ObtainNullBuffer() {
|
||||
std::shared_ptr<Buffer> buffer;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
if (m_null_buffer != nullptr) {
|
||||
return m_null_buffer;
|
||||
}
|
||||
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal,
|
||||
0, AllFlags, 16);
|
||||
buffer = m_null_buffer;
|
||||
if (m_null_buffer != nullptr) {
|
||||
return m_null_buffer;
|
||||
}
|
||||
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal, 0,
|
||||
AllFlags, 16);
|
||||
const std::array<uint8_t, 16> zeros {};
|
||||
Upload(m_scheduler.Current(), *buffer, 0, zeros.data(), zeros.size());
|
||||
return buffer;
|
||||
Upload(m_scheduler.Current(), *m_null_buffer, 0, zeros.data(), zeros.size());
|
||||
return m_null_buffer;
|
||||
}
|
||||
|
||||
ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
||||
@@ -576,11 +552,10 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
|
||||
};
|
||||
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||
const bool has_dirty_buffer_source = !dirty.empty();
|
||||
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||
const bool has_dirty_buffer_source = !dirty.empty();
|
||||
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
|
||||
"image source");
|
||||
|
||||
@@ -651,10 +626,9 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
|
||||
}
|
||||
m_staging_buffer.Commit();
|
||||
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||
const bool has_dirty_buffer_source = !dirty.empty();
|
||||
auto owner = find_owner();
|
||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||
const bool has_dirty_buffer_source = !dirty.empty();
|
||||
auto owner = find_owner();
|
||||
if (has_dirty_buffer_source && owner == m_buffers.end()) {
|
||||
EXIT("BufferCache: GPU-dirty image source lost its native owner\n");
|
||||
}
|
||||
@@ -687,8 +661,7 @@ void BufferCache::WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data)
|
||||
}
|
||||
Libs::LibKernel::Memory::WriteBacking(vaddr, data.data(), data.size());
|
||||
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
const auto end = vaddr + data.size();
|
||||
const auto end = vaddr + data.size();
|
||||
for (auto& [address, cached]: m_buffers) {
|
||||
const auto cached_end = address + cached->size;
|
||||
const auto begin = std::max(vaddr, address);
|
||||
@@ -718,8 +691,7 @@ void BufferCache::FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool
|
||||
}
|
||||
(void)m_texture_cache.ClearMeta(vaddr);
|
||||
{
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
const auto region = m_texture_cache.QueryRegion(vaddr, size);
|
||||
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);
|
||||
@@ -750,16 +722,15 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
const bool dst_memory = !dst_gds;
|
||||
const bool src_memory = !src_gds;
|
||||
if ((dst_memory && dst_vaddr == 0) || (src_memory && src_vaddr == 0) || size == 0 ||
|
||||
((dst_vaddr | src_vaddr | size) & 3u) != 0 || size > UINT64_MAX - dst_vaddr ||
|
||||
size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
|
||||
((dst_gds || src_gds) && ((dst_vaddr | src_vaddr | size) & 3u) != 0) ||
|
||||
size > UINT64_MAX - dst_vaddr || size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
|
||||
(dst_gds == src_gds && src_vaddr < dst_vaddr + size && dst_vaddr < src_vaddr + size) ||
|
||||
(dst_gds && (dst_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - dst_vaddr)) ||
|
||||
(src_gds && (src_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - src_vaddr))) {
|
||||
EXIT("BufferCache: invalid or overlapping copy range\n");
|
||||
}
|
||||
if (src_memory || dst_memory) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
const auto src_region =
|
||||
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 {};
|
||||
@@ -810,18 +781,19 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
destination.CopyFrom(command, source, src.offset, dst.offset, size);
|
||||
}
|
||||
|
||||
bool BufferCache::HasPageOverlap(uint64_t vaddr, uint64_t size) {
|
||||
bool BufferCache::IsRegionRegistered(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
EXIT("BufferCache: invalid page-overlap query\n");
|
||||
EXIT("BufferCache: invalid registered-region query\n");
|
||||
}
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& [address, cached]: m_buffers) {
|
||||
if (PageOverlaps(vaddr, size, address, cached->size)) {
|
||||
return true;
|
||||
}
|
||||
// Cached buffers are ordered and non-overlapping. The last buffer beginning before the query
|
||||
// end is therefore the only possible intersection.
|
||||
const auto candidate = m_buffers.lower_bound(vaddr + size);
|
||||
if (candidate == m_buffers.begin()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
const auto& [address, cached] = *std::prev(candidate);
|
||||
return address + cached->size > vaddr;
|
||||
}
|
||||
|
||||
bool BufferCache::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
@@ -829,7 +801,6 @@ bool BufferCache::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
|
||||
bool BufferCache::HasGpuDirtyBytes(uint64_t vaddr, uint64_t size) {
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
return !m_gpu_modified_ranges.Intersections(vaddr, size).empty();
|
||||
}
|
||||
|
||||
@@ -838,8 +809,7 @@ bool BufferCache::IsRegionCpuModified(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
|
||||
void BufferCache::RunGarbageCollector() {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
const auto tick = m_gc_tick++;
|
||||
const auto tick = m_gc_tick++;
|
||||
if (m_graphics.CanReportMemoryUsage()) {
|
||||
m_total_used_memory = m_graphics.GetDeviceMemoryUsage();
|
||||
}
|
||||
@@ -854,7 +824,6 @@ void BufferCache::RunGarbageCollector() {
|
||||
std::vector<RetiredBuffer> retires;
|
||||
std::vector<std::pair<RetiredBuffer, std::vector<DownloadCopy>>> dirty_retires;
|
||||
{
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
std::vector<uint64_t> candidates;
|
||||
for (const auto& [address, owner]: m_buffers) {
|
||||
const auto& cached = *owner;
|
||||
@@ -902,7 +871,6 @@ void BufferCache::RunGarbageCollector() {
|
||||
QueueGarbageDownload(copies, std::move(retire));
|
||||
}
|
||||
|
||||
FaultSafeCacheLock lock(this, m_mutex);
|
||||
for (const auto& retire: retires) {
|
||||
auto found = m_buffers.find(retire.address);
|
||||
if (found == m_buffers.end() || found->second->size != retire.size ||
|
||||
|
||||
+5
-9
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/host_gpu/memoryTracker.h"
|
||||
#include "graphics/host_gpu/rangeSet.h"
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
@@ -20,7 +19,6 @@ struct GraphicContext;
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
class TextureCache;
|
||||
class ResourceMutex;
|
||||
|
||||
struct BufferBinding {
|
||||
std::shared_ptr<void> owner;
|
||||
@@ -41,12 +39,12 @@ public:
|
||||
}
|
||||
|
||||
BufferCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
TextureCache& texture_cache, ResourceMutex& resource_mutex);
|
||||
TextureCache& texture_cache);
|
||||
~BufferCache();
|
||||
KYTY_CLASS_NO_COPY(BufferCache);
|
||||
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size, bool is_write = false);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
||||
bool is_written = false, bool is_read = true,
|
||||
@@ -61,7 +59,8 @@ public:
|
||||
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds = false);
|
||||
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds = false,
|
||||
bool src_gds = false);
|
||||
[[nodiscard]] bool HasPageOverlap(uint64_t vaddr, uint64_t size);
|
||||
// Cache-index and exact dirty-range queries require GPU-thread serialization.
|
||||
[[nodiscard]] bool IsRegionRegistered(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool HasGpuDirtyBytes(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionGpuModified(uint64_t vaddr, uint64_t size);
|
||||
@@ -84,8 +83,6 @@ private:
|
||||
[[nodiscard]] static constexpr uint64_t AlignDownload(uint64_t size) noexcept {
|
||||
return (size + DOWNLOAD_ALIGNMENT - 1) & ~(DOWNLOAD_ALIGNMENT - 1);
|
||||
}
|
||||
[[nodiscard]] static bool PageOverlaps(uint64_t left, uint64_t left_size, uint64_t right,
|
||||
uint64_t right_size) noexcept;
|
||||
[[nodiscard]] static std::pair<uint64_t, uint64_t> DownloadEnvelope(const DownloadCopy& copy);
|
||||
[[nodiscard]] static bool ResolveOverlap(CacheRange& merged, CacheRange candidate) noexcept;
|
||||
void Upload(CommandBuffer& command, Buffer& destination, uint64_t destination_offset,
|
||||
@@ -97,11 +94,11 @@ private:
|
||||
void PublishDownloads(std::span<const DownloadRange> downloads);
|
||||
void QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire);
|
||||
void WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data);
|
||||
void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
Buffer m_gds_buffer;
|
||||
Common::Mutex m_mutex;
|
||||
std::shared_ptr<Buffer> m_null_buffer;
|
||||
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
||||
RangeSet m_gpu_modified_ranges;
|
||||
@@ -111,7 +108,6 @@ private:
|
||||
StreamBuffer m_download_buffer;
|
||||
StreamBuffer m_device_buffer;
|
||||
TextureCache& m_texture_cache;
|
||||
ResourceMutex& m_resource_mutex;
|
||||
uint64_t m_total_used_memory = 0;
|
||||
uint64_t m_trigger_gc_memory = 1ull * 1024 * 1024 * 1024;
|
||||
uint64_t m_critical_gc_memory = 2ull * 1024 * 1024 * 1024;
|
||||
|
||||
+10
-65
@@ -1,15 +1,13 @@
|
||||
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/command_processor/commandProcessor.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_resource_mutex),
|
||||
m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache, m_resource_mutex) {}
|
||||
: 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;
|
||||
|
||||
@@ -18,69 +16,21 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
return false;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported guest-memory fault from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " access=%u\n",
|
||||
fault_vaddr, static_cast<uint32_t>(access));
|
||||
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);
|
||||
}
|
||||
bool handled = false;
|
||||
const auto resolve = [this, access, fault_vaddr, &handled](CommandProcessor& cp) {
|
||||
cp.BeginReadbackTransaction();
|
||||
{
|
||||
ResourceMutex::FaultScope fault(m_resource_mutex);
|
||||
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);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
cp.EndReadbackTransaction();
|
||||
};
|
||||
if (auto* cp = Gpu::CurrentCommandProcessor(); cp != nullptr) {
|
||||
resolve(*cp);
|
||||
return handled;
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported page fault from a pre-owned resource transaction, addr=0x%016" PRIx64
|
||||
" access=%u\n",
|
||||
fault_vaddr, static_cast<uint32_t>(access));
|
||||
}
|
||||
EXIT_IF(m_gpu == nullptr);
|
||||
m_gpu->SendCommandSyncWithProcessor(resolve);
|
||||
return handled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (!IsMapped(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported memory invalidation from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
const auto resolve = [this, vaddr, size](CommandProcessor& cp) {
|
||||
cp.BeginReadbackTransaction();
|
||||
{
|
||||
ResourceMutex::FaultScope fault(m_resource_mutex);
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
}
|
||||
cp.EndReadbackTransaction();
|
||||
};
|
||||
if (auto* cp = Gpu::CurrentCommandProcessor(); cp != nullptr) {
|
||||
resolve(*cp);
|
||||
return true;
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported memory invalidation from a pre-owned resource transaction, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
EXIT_IF(m_gpu == nullptr);
|
||||
m_gpu->SendCommandSyncWithProcessor(resolve);
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,11 +57,6 @@ void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
if (m_resource_mutex.IsOwnedByCurrentThread()) {
|
||||
EXIT("unsupported memory unmap from a pre-owned resource transaction, "
|
||||
"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();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -35,7 +34,6 @@ public:
|
||||
|
||||
private:
|
||||
PageManager m_page_manager;
|
||||
ResourceMutex m_resource_mutex;
|
||||
CommandScheduler& m_scheduler;
|
||||
BufferCache m_buffer_cache;
|
||||
TextureCache m_texture_cache;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
ResourceMutex::FaultScope::FaultScope(ResourceMutex& mutex): m_mutex(mutex) {
|
||||
m_resource_preowned = m_mutex.BeginFault();
|
||||
}
|
||||
|
||||
ResourceMutex::FaultScope::~FaultScope() {
|
||||
m_mutex.EndFault(m_resource_preowned);
|
||||
}
|
||||
|
||||
ResourceMutex::~ResourceMutex() {
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != std::thread::id {} || m_fault_owner != std::thread::id {}) {
|
||||
EXIT("ResourceMutex destroyed with an active owner or fault transaction\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceMutex::lock() {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner == current) {
|
||||
EXIT("recursive resource transaction\n");
|
||||
}
|
||||
if (m_fault_owner == current) {
|
||||
EXIT("resource transaction re-entered from a page-fault callback\n");
|
||||
}
|
||||
}
|
||||
m_resource.lock();
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != std::thread::id {}) {
|
||||
EXIT("resource mutex acquired with a stale owner\n");
|
||||
}
|
||||
m_resource_owner = current;
|
||||
}
|
||||
|
||||
void ResourceMutex::unlock() {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != current) {
|
||||
EXIT("resource transaction released without ownership\n");
|
||||
}
|
||||
m_resource_owner = {};
|
||||
}
|
||||
m_resource.unlock();
|
||||
}
|
||||
|
||||
bool ResourceMutex::IsOwnedByCurrentThread() {
|
||||
std::lock_guard state(m_state);
|
||||
return m_resource_owner == std::this_thread::get_id();
|
||||
}
|
||||
|
||||
bool ResourceMutex::BeginFault() {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_fault_owner == current) {
|
||||
EXIT("nested resource page-fault transaction\n");
|
||||
}
|
||||
if (m_resource_owner == current) {
|
||||
m_fault_owner = current;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
lock();
|
||||
std::lock_guard state(m_state);
|
||||
if (m_resource_owner != current || m_fault_owner != std::thread::id {}) {
|
||||
EXIT("resource fault transaction acquired inconsistent ownership\n");
|
||||
}
|
||||
m_fault_owner = current;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceMutex::EndFault(bool resource_preowned) {
|
||||
const auto current = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard state(m_state);
|
||||
if (m_fault_owner != current || m_resource_owner != current) {
|
||||
EXIT("resource page-fault transaction ended with inconsistent ownership\n");
|
||||
}
|
||||
m_fault_owner = {};
|
||||
}
|
||||
if (!resource_preowned) {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -1,48 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_RESOURCEMUTEX_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_RESOURCEMUTEX_H_
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
// Owner-tracked shared buffer/image transaction. External faults pause GPU submissions first;
|
||||
// command-processor faults drain pending guest processors before entering this transaction.
|
||||
class ResourceMutex final {
|
||||
public:
|
||||
class FaultScope final {
|
||||
public:
|
||||
explicit FaultScope(ResourceMutex& mutex);
|
||||
~FaultScope();
|
||||
KYTY_CLASS_NO_COPY(FaultScope);
|
||||
|
||||
private:
|
||||
ResourceMutex& m_mutex;
|
||||
bool m_resource_preowned = false;
|
||||
};
|
||||
|
||||
ResourceMutex() = default;
|
||||
~ResourceMutex();
|
||||
KYTY_CLASS_NO_COPY(ResourceMutex);
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
[[nodiscard]] bool IsOwnedByCurrentThread();
|
||||
|
||||
private:
|
||||
friend class FaultScope;
|
||||
|
||||
[[nodiscard]] bool BeginFault();
|
||||
void EndFault(bool resource_preowned);
|
||||
|
||||
std::mutex m_resource;
|
||||
std::mutex m_state;
|
||||
std::thread::id m_resource_owner;
|
||||
std::thread::id m_fault_owner;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_RESOURCEMUTEX_H_
|
||||
+56
-77
@@ -8,7 +8,6 @@
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
@@ -56,13 +55,12 @@ private:
|
||||
} // namespace
|
||||
|
||||
TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
PageManager& page_manager, BufferCache& buffer_cache,
|
||||
ResourceMutex& resource_mutex)
|
||||
PageManager& page_manager, BufferCache& buffer_cache)
|
||||
: m_graphics(graphics), m_scheduler(scheduler), m_page_manager(page_manager),
|
||||
m_blit_helper(graphics, scheduler),
|
||||
m_tiler(std::make_unique<TileManager>(graphics, scheduler,
|
||||
buffer_cache.GetUtilityBuffer(MemoryUsage::Stream))),
|
||||
m_buffer_cache(buffer_cache), m_resource_mutex(resource_mutex),
|
||||
m_buffer_cache(buffer_cache),
|
||||
m_readback_linear_images(Config::ReadbackLinearImagesEnabled()) {
|
||||
if (m_graphics.CanReportMemoryUsage()) {
|
||||
constexpr int64_t GiB = 1024ll * 1024 * 1024;
|
||||
@@ -434,9 +432,8 @@ void TextureCache::UntrackImageTail(ImageId id) {
|
||||
}
|
||||
|
||||
void TextureCache::TrackImageDownload(ImageId id) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
TrackImageDownloadLocked(id, image);
|
||||
}
|
||||
|
||||
@@ -519,7 +516,7 @@ ImageId TextureCache::GetNullImage(const ImageDesc& desc) {
|
||||
info.pitch = 1;
|
||||
info.bytes_per_block = std::max(desc.info.bytes_per_block, 1u);
|
||||
info.samples = 1;
|
||||
info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
info.tile_mode = Prospero::TileMode::kLinear;
|
||||
info.mip_layout[0] = {0, info.bytes_per_block, 1, 1};
|
||||
const auto id = InsertImage(info);
|
||||
m_null_images.emplace(format, id);
|
||||
@@ -825,7 +822,8 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
|
||||
requested.data.address, requested.resources.levels, requested.resources.layers,
|
||||
cached.info.resources.levels, cached.info.resources.layers, requested.data.size,
|
||||
cached.info.data.size, static_cast<uint32_t>(requested.type),
|
||||
static_cast<uint32_t>(cached.info.type), requested.tile_mode, cached.info.tile_mode);
|
||||
static_cast<uint32_t>(cached.info.type), static_cast<uint32_t>(requested.tile_mode),
|
||||
static_cast<uint32_t>(cached.info.tile_mode));
|
||||
}
|
||||
|
||||
if (requested.data.address > cached.info.data.address) {
|
||||
@@ -908,10 +906,9 @@ TextureCache::ColorTransferPlan
|
||||
TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
|
||||
TransferDirection direction) const {
|
||||
const auto& info = image.info;
|
||||
uint32_t format = info.guest_format;
|
||||
auto format = info.guest_format;
|
||||
uint32_t layers = info.TransferLayers();
|
||||
bool volume = info.IsVolume();
|
||||
bool layered = info.IsLayered();
|
||||
bool allow_depth_tile = direction == TransferDirection::Upload;
|
||||
const char* owner =
|
||||
direction == TransferDirection::Upload ? "TextureCache" : "TextureCache readback";
|
||||
@@ -940,7 +937,6 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
|
||||
format = info.guest_format;
|
||||
layers = info.resources.layers;
|
||||
volume = false;
|
||||
layered = layers > 1;
|
||||
allow_depth_tile = false;
|
||||
plan.swap_bgra16 = info.bgra16;
|
||||
owner = "VideoOut";
|
||||
@@ -958,19 +954,13 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
|
||||
plan.swap_bgra16 = info.bgra16;
|
||||
}
|
||||
|
||||
plan.layout = TextureCalcUploadLayout(format, info.extent.width, info.extent.height,
|
||||
info.resources.levels, layers, info.pitch, info.tile_mode,
|
||||
info.data.size, allow_depth_tile, volume, owner);
|
||||
plan.regions = TextureBuildImageCopies(plan.layout, info.extent.width, info.extent.height,
|
||||
layers, info.resources.levels, layered, volume);
|
||||
plan.tiled = static_cast<Prospero::TileMode>(plan.layout.tile) != Prospero::TileMode::kLinear;
|
||||
plan.layout = TextureCalcUploadLayout(format, info.extent.width, info.extent.height,
|
||||
info.resources.levels, layers, info.tile_mode,
|
||||
info.data.size, allow_depth_tile, volume, owner);
|
||||
plan.regions = TextureBuildImageCopies(plan.layout);
|
||||
plan.tiled = plan.layout.surface.description.tile_mode != Prospero::TileMode::kLinear;
|
||||
if (plan.tiled) {
|
||||
// CMASK/FMASK decoding is intentionally deferred.
|
||||
if (plan.layout.tile_family == TileBlockFamily::Depth64KB &&
|
||||
Prospero::IsFmaskTextureFormat(format)) {
|
||||
return plan;
|
||||
}
|
||||
if (!TextureBuildGpuTileInfos(info.data.size, plan.regions, plan.layout, format, layers,
|
||||
if (!TextureBuildGpuTileInfos(info.data.size, plan.regions, plan.layout,
|
||||
info.resources.levels, plan.tiles)) {
|
||||
return plan;
|
||||
}
|
||||
@@ -1017,9 +1007,10 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
|
||||
" size=0x%016" PRIx64 " format=%u tile=%u family=%u extent=%ux%ux%u "
|
||||
"pitch=%u levels=%u layers=%u samples=%u\n",
|
||||
static_cast<uint32_t>(desc.type), info.data.address, info.data.size,
|
||||
info.guest_format, info.tile_mode, static_cast<uint32_t>(plan.layout.tile_family),
|
||||
info.extent.width, info.extent.height, info.extent.depth, info.pitch,
|
||||
info.resources.levels, info.resources.layers, info.samples);
|
||||
static_cast<uint32_t>(info.guest_format), static_cast<uint32_t>(info.tile_mode),
|
||||
static_cast<uint32_t>(plan.layout.surface.texture.block.family), info.extent.width,
|
||||
info.extent.height, info.extent.depth, info.pitch, info.resources.levels,
|
||||
info.resources.layers, info.samples);
|
||||
}
|
||||
TileManager::Result linear {source.Handle(), source_offset, info.data.size};
|
||||
if (plan.tiled) {
|
||||
@@ -1054,7 +1045,7 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
|
||||
copy.bufferImageHeight = info.extent.height;
|
||||
copy.imageSubresource = {vk::ImageAspectFlagBits::eDepth, 0, layer, 1};
|
||||
copy.imageExtent = {info.extent.width, info.extent.height, 1};
|
||||
if (static_cast<Prospero::TileMode>(info.tile_mode) != Prospero::TileMode::kLinear) {
|
||||
if (info.tile_mode != Prospero::TileMode::kLinear) {
|
||||
tiles.push_back({block.family, block.bytes_per_element, offset, full_slice_size, offset,
|
||||
full_slice_size, 0, info.extent.width, info.extent.height, 1,
|
||||
info.pitch});
|
||||
@@ -1152,8 +1143,7 @@ void TextureCache::RefreshImage(ImageId id, const ImageDesc& desc) {
|
||||
}
|
||||
|
||||
void TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
CacheLock lock(*this, m_lock);
|
||||
AssociateStencilLocked(depth_id, stencil);
|
||||
}
|
||||
|
||||
@@ -1196,11 +1186,9 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
}
|
||||
|
||||
ImageId result {};
|
||||
bool inserted_new = false;
|
||||
{
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
const auto candidates =
|
||||
CacheLock lock(*this, m_lock);
|
||||
const auto candidates =
|
||||
FindImagesInRegion(desc.info.data.address, desc.info.data.size, false);
|
||||
|
||||
for (const auto id: candidates) {
|
||||
@@ -1243,19 +1231,12 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
}
|
||||
if (!result) {
|
||||
result = InsertImage(desc.info);
|
||||
inserted_new = true;
|
||||
auto& inserted = ResolveImage(result);
|
||||
if (m_buffer_cache.HasGpuDirtyBytes(inserted.info.data.address,
|
||||
inserted.info.data.size)) {
|
||||
inserted.MarkBufferModified();
|
||||
}
|
||||
}
|
||||
if (inserted_new) {
|
||||
InitializeImage(result, desc);
|
||||
} else {
|
||||
RefreshImage(result, desc);
|
||||
}
|
||||
|
||||
auto& image = ResolveImage(result);
|
||||
if (desc.type == BindingType::VideoOut &&
|
||||
desc.info.metadata.compression != VideoOutCompression::Uncompressed) {
|
||||
@@ -1280,6 +1261,13 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureCache::UpdateImage(ImageId id) {
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
TouchImage(image);
|
||||
RefreshImage(id, ImageDesc {.info = image.info, .type = UploadBinding(image)});
|
||||
}
|
||||
|
||||
ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool ensure_valid) {
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
return {};
|
||||
@@ -1324,22 +1312,24 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e
|
||||
}
|
||||
|
||||
vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
TouchImage(image);
|
||||
if (!image.info.data.Empty()) {
|
||||
if (!image.registered || image.depth_id || image.binding.needs_rebind) {
|
||||
EXIT("TextureCache: texture requires rediscovery before final acquisition\n");
|
||||
}
|
||||
}
|
||||
if (desc.type == BindingType::Storage) {
|
||||
image.MarkGpuModified();
|
||||
}
|
||||
if (!image.info.data.Empty()) {
|
||||
RefreshImage(id, desc);
|
||||
}
|
||||
switch (desc.type) {
|
||||
case BindingType::Texture: break;
|
||||
case BindingType::Storage:
|
||||
if (image.info.data.Empty()) {
|
||||
image.MarkGpuModified();
|
||||
} else {
|
||||
if (!image.info.data.Empty()) {
|
||||
if (!image.registered || image.depth_id) {
|
||||
EXIT("TextureCache: cannot acquire an unavailable storage image\n");
|
||||
}
|
||||
@@ -1358,16 +1348,16 @@ vk::ImageView TextureCache::FindRenderTarget(ImageId id, const ImageDesc& desc)
|
||||
if (desc.type != BindingType::RenderTarget) {
|
||||
EXIT("TextureCache: invalid color-target binding\n");
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
if (!image.registered || image.depth_id || image.binding.needs_rebind) {
|
||||
EXIT("TextureCache: color target requires rediscovery before final acquisition\n");
|
||||
}
|
||||
TouchImage(image);
|
||||
image.MarkGpuModified();
|
||||
image.usage.render_target = true;
|
||||
RefreshImage(id, desc);
|
||||
CommitGpuWrite(image);
|
||||
image.usage.render_target = true;
|
||||
TrackImageDownloadLocked(id, image);
|
||||
const auto view = image.FindView(desc.view_info);
|
||||
RetainImage(m_scheduler.Current(), id);
|
||||
@@ -1378,13 +1368,14 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
if (desc.type != BindingType::DepthTarget) {
|
||||
EXIT("TextureCache: invalid depth-target binding\n");
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
if (!image.registered || image.depth_id || image.binding.needs_rebind) {
|
||||
EXIT("TextureCache: depth target requires rediscovery before final acquisition\n");
|
||||
}
|
||||
TouchImage(image);
|
||||
image.MarkGpuModified();
|
||||
image.usage.depth_target = true;
|
||||
RefreshImage(id, desc);
|
||||
if (desc.info.HasMetadata()) {
|
||||
image.info.metadata = desc.info.metadata;
|
||||
@@ -1392,7 +1383,6 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
MetaDataInfo {.clear_mask = image.info.htile_clear_mask});
|
||||
}
|
||||
CommitGpuWrite(image);
|
||||
image.usage.depth_target = true;
|
||||
if (desc.info.HasStencil()) {
|
||||
AssociateStencilLocked(id, desc.info.stencil);
|
||||
}
|
||||
@@ -1402,9 +1392,8 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
}
|
||||
|
||||
void TextureCache::MarkGpuWritten(ImageId id) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
if (!image.registered || image.depth_id) {
|
||||
EXIT("TextureCache: cannot mark an unavailable image GPU-written\n");
|
||||
}
|
||||
@@ -1428,7 +1417,6 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
||||
if (command.IsInvalid() || !GuestRange {address, size}.Valid()) {
|
||||
EXIT("TextureCache: invalid image clear\n");
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
ImageId selected {};
|
||||
vk::ImageAspectFlags aspect {};
|
||||
@@ -1713,14 +1701,9 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin
|
||||
return false;
|
||||
}
|
||||
if (color.tiled) {
|
||||
const auto binding = m_texture_cache.UploadBinding(image);
|
||||
const auto format =
|
||||
binding == TextureCache::BindingType::RenderTarget
|
||||
? ImageOps::RenderTargetTransferFormat(image.info.bytes_per_block)
|
||||
: image.info.guest_format;
|
||||
color.tiles.clear();
|
||||
if (!TextureBuildGpuTileInfos(copy_size, color.regions, color.layout, format,
|
||||
image.info.TransferLayers(), levels, color.tiles)) {
|
||||
if (!TextureBuildGpuTileInfos(copy_size, color.regions, color.layout, levels,
|
||||
color.tiles)) {
|
||||
return false;
|
||||
}
|
||||
color.linear_size = GetLinearSize(color.tiles);
|
||||
@@ -1891,9 +1874,8 @@ bool TextureCache::IsMetaCleared(uint64_t address, uint32_t slice) {
|
||||
}
|
||||
|
||||
bool TextureCache::ClearMeta(uint64_t address) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
const auto found = m_surface_metas.find(address);
|
||||
CacheLock lock(*this, m_lock);
|
||||
const auto found = m_surface_metas.find(address);
|
||||
if (found == m_surface_metas.end()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1919,9 +1901,8 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
EXIT("TextureCache: invalid unmap range\n");
|
||||
}
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto images = FindImagesInRegion(address, size, false);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto images = FindImagesInRegion(address, size, false);
|
||||
if (!images.empty()) {
|
||||
m_scheduler.Finish();
|
||||
}
|
||||
@@ -1938,9 +1919,8 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
|
||||
}
|
||||
|
||||
void TextureCache::RunGarbageCollector() {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
const uint64_t tick = m_gc_tick++;
|
||||
CacheLock lock(*this, m_lock);
|
||||
const uint64_t tick = m_gc_tick++;
|
||||
if (m_graphics.CanReportMemoryUsage()) {
|
||||
m_total_used_memory = m_graphics.GetDeviceMemoryUsage();
|
||||
}
|
||||
@@ -2000,8 +1980,7 @@ void TextureCache::RunGarbageCollector() {
|
||||
}
|
||||
|
||||
void TextureCache::ProcessDownloadImages() {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
CacheLock lock(*this, m_lock);
|
||||
for (const auto id: m_download_images) {
|
||||
const auto owner = ResolveOwner(id);
|
||||
if (owner != nullptr && owner->registered && owner->IsGpuModified()) {
|
||||
|
||||
+2
-3
@@ -26,7 +26,6 @@ class Buffer;
|
||||
class BufferCache;
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
class ResourceMutex;
|
||||
class RenderExecutor;
|
||||
class StreamBuffer;
|
||||
class TileManager;
|
||||
@@ -49,11 +48,12 @@ public:
|
||||
};
|
||||
|
||||
TextureCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
BufferCache& buffer_cache, ResourceMutex& resource_mutex);
|
||||
BufferCache& buffer_cache);
|
||||
~TextureCache();
|
||||
KYTY_CLASS_NO_COPY(TextureCache);
|
||||
|
||||
[[nodiscard]] ImageId FindImage(ImageDesc& desc, bool exact_format = false);
|
||||
void UpdateImage(ImageId id);
|
||||
[[nodiscard]] ImageId FindImageFromRange(uint64_t address, uint64_t size,
|
||||
bool ensure_valid = true);
|
||||
[[nodiscard]] vk::ImageView FindTexture(ImageId id, const ImageDesc& desc);
|
||||
@@ -168,7 +168,6 @@ private:
|
||||
BlitHelper m_blit_helper;
|
||||
std::unique_ptr<TileManager> m_tiler;
|
||||
BufferCache& m_buffer_cache;
|
||||
ResourceMutex& m_resource_mutex;
|
||||
std::vector<Slot> m_slots;
|
||||
std::vector<uint32_t> m_free_slots;
|
||||
ImagePageTable m_image_page_table;
|
||||
|
||||
@@ -101,8 +101,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
" attrib3_tile=0x%08" PRIx32 " attrib3_dim=0x%08" PRIx32 " fmt=0x%08" PRIx32
|
||||
" nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 "\n",
|
||||
rt_slot, rt.base.addr, mask, rt.attrib2.width, rt.attrib2.height,
|
||||
rt.attrib3.tile_mode, rt.attrib3.dimension, rt.info.format, rt.info.channel_type,
|
||||
rt.info.channel_order);
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,17 +130,16 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
if (volume && samples != 1) {
|
||||
EXIT("multisampled 3D render targets are unsupported\n");
|
||||
}
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
const bool standard64 =
|
||||
rt.attrib3.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
const bool standard64 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard64KB;
|
||||
|
||||
switch (rt.attrib3.tile_mode) {
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget):
|
||||
case Prospero::TileMode::kLinear:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
case Prospero::TileMode::kRenderTarget:
|
||||
tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode);
|
||||
break;
|
||||
default: EXIT("unknown tile mode: %u\n", rt.attrib3.tile_mode);
|
||||
default: EXIT("unknown tile mode: %u\n", static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
if (!tile && levels > 1) {
|
||||
EXIT("linear mipmapped render targets are unsupported\n");
|
||||
@@ -178,10 +178,10 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u;
|
||||
} else if (tile) {
|
||||
if (volume) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, levels, rt.attrib3.tile_mode);
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else if (standard64) {
|
||||
pitch = TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float),
|
||||
width, levels, rt.attrib3.tile_mode);
|
||||
pitch =
|
||||
TileGetTexturePitch(Prospero::BufferFormat::k32Float, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
@@ -192,15 +192,22 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
pitch = width;
|
||||
}
|
||||
|
||||
TileSizeOffset mip_sizes[16] {};
|
||||
TilePaddedSize mip_padded[16] {};
|
||||
TileVolumeLayout volume_layout {};
|
||||
uint64_t backing_size = 0;
|
||||
TileSizeOffset mip_sizes[16] {};
|
||||
TilePaddedSize mip_padded[16] {};
|
||||
TileSurfaceLayout volume_layout {};
|
||||
uint64_t backing_size = 0;
|
||||
if (volume) {
|
||||
if (!tile || !TileGetTextureVolumeLayout(transfer_format, width, height, depth, levels,
|
||||
rt.attrib3.tile_mode, volume_layout)) {
|
||||
const TileSurfaceDescription description {transfer_format,
|
||||
rt.attrib3.tile_mode,
|
||||
TileSurfaceDimension::Dim3D,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
levels,
|
||||
1};
|
||||
if (!tile || !TileGetTiledTextureLayout(description, volume_layout)) {
|
||||
EXIT("unsupported 3D render-target layout: %ux%ux%u levels=%u tile=%u\n", width, height,
|
||||
depth, levels, rt.attrib3.tile_mode);
|
||||
depth, levels, static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
size = volume_layout.block_slice_size;
|
||||
backing_size = volume_layout.total_size;
|
||||
@@ -208,9 +215,8 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
TileSizeAlign layout {};
|
||||
bool valid_layout = false;
|
||||
if (standard64) {
|
||||
TileGetTextureSize(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), width,
|
||||
height, pitch, levels, rt.attrib3.tile_mode, &layout, mip_sizes,
|
||||
mip_padded);
|
||||
TileGetTextureSize(Prospero::BufferFormat::k32Float, width, height, levels,
|
||||
rt.attrib3.tile_mode, &layout, mip_sizes, mip_padded);
|
||||
valid_layout = layout.size != 0 && layout.align == 65536;
|
||||
} else {
|
||||
valid_layout =
|
||||
@@ -271,8 +277,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
" extent=%ux%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u"
|
||||
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 " samples=%u tile=%s\n",
|
||||
rt_slot, rt.base.addr, backing_size, width, height, depth, rt.view.current_mip_level,
|
||||
view_extent.width, view_extent.height, levels, pitch, rt.info.format,
|
||||
rt.info.channel_type, rt.info.channel_order, samples, tile ? "tiled" : "linear");
|
||||
view_extent.width, view_extent.height, levels, pitch,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order), samples, tile ? "tiled" : "linear");
|
||||
}
|
||||
|
||||
TextureCache::ImageDesc desc {};
|
||||
@@ -289,11 +296,12 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
desc.info.tile_mode = rt.attrib3.tile_mode;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
if (volume) {
|
||||
const auto& mip = volume_layout.mips[level];
|
||||
desc.info.mip_layout[level] = {
|
||||
volume_layout.level_offsets[level],
|
||||
volume_layout.level_sizes[level],
|
||||
volume_layout.level_widths[level],
|
||||
volume_layout.level_heights[level],
|
||||
mip.offset,
|
||||
mip.size,
|
||||
mip.padded_width,
|
||||
mip.padded_height,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,10 @@ bool CommandScheduler::InDeferredOperation() noexcept {
|
||||
|
||||
CommandScheduler::CommandScheduler(RenderContext& context, GraphicContext& graphics)
|
||||
: m_master(graphics), m_context(context), m_graphics(graphics),
|
||||
m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) {}
|
||||
m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) {
|
||||
m_buffers.reserve(CommandBufferGrowStep);
|
||||
m_buffer_ticks.reserve(CommandBufferGrowStep);
|
||||
}
|
||||
|
||||
CommandScheduler::~CommandScheduler() {
|
||||
Shutdown();
|
||||
@@ -154,10 +157,7 @@ void CommandScheduler::Begin(HW::Context& registers, HW::UserConfig& user_config
|
||||
m_shaders = &shaders;
|
||||
|
||||
if (!Active()) {
|
||||
for (auto& buffer: m_buffers) {
|
||||
buffer = std::make_unique<RenderCommandBuffer>(*this);
|
||||
}
|
||||
m_current = 0;
|
||||
m_current = static_cast<int>(GrowCommandBuffers());
|
||||
}
|
||||
|
||||
BindCurrent();
|
||||
@@ -235,7 +235,13 @@ void CommandScheduler::Wait(uint64_t tick) {
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations() {
|
||||
m_master.Refresh();
|
||||
PopPendingOperations(true);
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations(bool refresh_gpu_tick) {
|
||||
if (refresh_gpu_tick) {
|
||||
m_master.Refresh();
|
||||
}
|
||||
for (;;) {
|
||||
Common::UniqueFunction<void> callback;
|
||||
{
|
||||
@@ -362,7 +368,7 @@ uint64_t CommandScheduler::NextSubmitSequence() noexcept {
|
||||
}
|
||||
|
||||
void CommandScheduler::CheckActive() const {
|
||||
EXIT_IF(!Active() || m_current >= BufferCount);
|
||||
EXIT_IF(!Active() || static_cast<size_t>(m_current) >= m_buffers.size());
|
||||
}
|
||||
|
||||
RenderCommandBuffer& CommandScheduler::Current() const {
|
||||
@@ -384,15 +390,52 @@ CommandBuffer& CommandScheduler::SubmitCurrent(SubmitInfo& submit) {
|
||||
const auto signal_tick = m_master.NextTick();
|
||||
submit.AddSignal(m_master.Handle(), signal_tick);
|
||||
submitted.Execute(submit);
|
||||
m_recording = false;
|
||||
m_buffer_ticks[static_cast<size_t>(m_current)] = signal_tick;
|
||||
m_recording = false;
|
||||
return submitted;
|
||||
}
|
||||
|
||||
int CommandScheduler::FindReusableBuffer(uint64_t gpu_tick) const {
|
||||
EXIT_IF(m_buffer_ticks.size() != m_buffers.size());
|
||||
const auto buffer_count = m_buffers.size();
|
||||
for (size_t offset = 1; offset <= buffer_count; ++offset) {
|
||||
const auto candidate = (static_cast<size_t>(m_current) + offset) % buffer_count;
|
||||
if (gpu_tick >= m_buffer_ticks[candidate]) {
|
||||
return static_cast<int>(candidate);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t CommandScheduler::GrowCommandBuffers() {
|
||||
const auto first = m_buffers.size();
|
||||
const auto end = first + CommandBufferGrowStep;
|
||||
m_buffers.reserve(end);
|
||||
m_buffer_ticks.reserve(end);
|
||||
for (size_t i = first; i < end; ++i) {
|
||||
m_buffers.emplace_back(std::make_unique<RenderCommandBuffer>(*this));
|
||||
m_buffer_ticks.push_back(0);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
void CommandScheduler::BeginNext() {
|
||||
EXIT_IF(m_recording);
|
||||
m_current = (m_current + 1) % BufferCount;
|
||||
|
||||
auto candidate = FindReusableBuffer(m_master.KnownGpuTick());
|
||||
bool refreshed = false;
|
||||
if (candidate < 0) {
|
||||
m_master.Refresh();
|
||||
refreshed = true;
|
||||
candidate = FindReusableBuffer(m_master.KnownGpuTick());
|
||||
}
|
||||
if (candidate < 0) {
|
||||
candidate = static_cast<int>(GrowCommandBuffers());
|
||||
}
|
||||
|
||||
m_current = candidate;
|
||||
Current().WaitForFenceAndReset();
|
||||
PopPendingOperations();
|
||||
PopPendingOperations(!refreshed);
|
||||
BindCurrent();
|
||||
Current().Begin();
|
||||
m_recording = true;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <queue>
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -31,8 +32,6 @@ struct CommandSlot {
|
||||
|
||||
class CommandScheduler {
|
||||
public:
|
||||
static constexpr int BufferCount = 8;
|
||||
|
||||
CommandScheduler(RenderContext& context, GraphicContext& graphics);
|
||||
~CommandScheduler();
|
||||
KYTY_CLASS_NO_COPY(CommandScheduler);
|
||||
@@ -65,6 +64,8 @@ public:
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||
|
||||
private:
|
||||
static constexpr size_t CommandBufferGrowStep = 4;
|
||||
|
||||
class CommandPool {
|
||||
public:
|
||||
CommandPool() = default;
|
||||
@@ -94,30 +95,34 @@ private:
|
||||
void BindCurrent() const;
|
||||
CommandBuffer& SubmitCurrent(SubmitInfo& submit);
|
||||
void BeginNext();
|
||||
void PopPendingOperations(bool refresh_gpu_tick);
|
||||
[[nodiscard]] int FindReusableBuffer(uint64_t gpu_tick) const;
|
||||
[[nodiscard]] size_t GrowCommandBuffers();
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
[[nodiscard]] CommandSlot* AllocateCommandBuffer();
|
||||
[[nodiscard]] uint64_t NextSubmitSequence() noexcept;
|
||||
|
||||
MasterSemaphore m_master;
|
||||
RenderContext& m_context;
|
||||
GraphicContext& m_graphics;
|
||||
CommandPool m_command_pool;
|
||||
std::array<std::unique_ptr<RenderCommandBuffer>, BufferCount> m_buffers;
|
||||
std::queue<PendingOperation> m_pending_operations;
|
||||
std::queue<PendingOperation> m_priority_operations;
|
||||
std::mutex m_operation_mutex;
|
||||
std::condition_variable m_operation_available;
|
||||
std::jthread m_priority_thread;
|
||||
bool m_priority_active = false;
|
||||
uint64_t m_priority_active_tick = 0;
|
||||
OperationState m_operation_state = OperationState::Open;
|
||||
int m_current = -1;
|
||||
bool m_recording = false;
|
||||
HW::Context* m_registers = nullptr;
|
||||
HW::UserConfig* m_user_config = nullptr;
|
||||
HW::Shader* m_shaders = nullptr;
|
||||
std::atomic<uint64_t> m_submit_sequence = 0;
|
||||
MasterSemaphore m_master;
|
||||
RenderContext& m_context;
|
||||
GraphicContext& m_graphics;
|
||||
CommandPool m_command_pool;
|
||||
std::vector<std::unique_ptr<RenderCommandBuffer>> m_buffers;
|
||||
std::vector<uint64_t> m_buffer_ticks;
|
||||
std::queue<PendingOperation> m_pending_operations;
|
||||
std::queue<PendingOperation> m_priority_operations;
|
||||
std::mutex m_operation_mutex;
|
||||
std::condition_variable m_operation_available;
|
||||
std::jthread m_priority_thread;
|
||||
bool m_priority_active = false;
|
||||
uint64_t m_priority_active_tick = 0;
|
||||
OperationState m_operation_state = OperationState::Open;
|
||||
int m_current = -1;
|
||||
bool m_recording = false;
|
||||
HW::Context* m_registers = nullptr;
|
||||
HW::UserConfig* m_user_config = nullptr;
|
||||
HW::Shader* m_shaders = nullptr;
|
||||
std::atomic<uint64_t> m_submit_sequence = 0;
|
||||
|
||||
friend class CommandBuffer;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <fmt/format.h>
|
||||
@@ -75,8 +76,8 @@ void uc_print(const char* func, const HW::UserConfig& uc) {
|
||||
"\t en_user_vgpr1 = %s\n"
|
||||
"\t en_user_vgpr2 = %s\n"
|
||||
"\t en_user_vgpr3 = %s\n",
|
||||
uc.GetPrimType(), uc.GetIndexOffset(), uc.GetObjectId(), ge_cntl.primitive_group_size,
|
||||
ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false",
|
||||
static_cast<uint32_t>(uc.GetPrimType()), uc.GetIndexOffset(), uc.GetObjectId(),
|
||||
ge_cntl.primitive_group_size, ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false",
|
||||
user_en.vgpr2 ? "true" : "false", user_en.vgpr3 ? "true" : "false");
|
||||
}
|
||||
|
||||
@@ -140,11 +141,12 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
fmt::format("\t info.cmask_addr_type = 0x{:08x}\n", rt.info.cmask_addr_type));
|
||||
dst.push_back(fmt::format("\t info.alt_tile_mode = {}\n",
|
||||
rt.info.alt_tile_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n", rt.info.format));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.channel_type = 0x{:08x}\n", rt.info.channel_type));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.channel_order = 0x{:08x}\n", rt.info.channel_order));
|
||||
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.format)));
|
||||
dst.push_back(fmt::format("\t info.channel_type = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_type)));
|
||||
dst.push_back(fmt::format("\t info.channel_order = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_order)));
|
||||
dst.push_back(fmt::format("\t info.blend_bypa = {}\n",
|
||||
rt.info.blend_bypass ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.blend_clamp = {}\n",
|
||||
@@ -153,10 +155,10 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
rt.info.round_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false"));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.tile_mode = 0x{:08x}\n", rt.attrib.tile_mode));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n", rt.attrib.fmask_tile_mode));
|
||||
dst.push_back(fmt::format("\t attrib.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib.tile_mode)));
|
||||
dst.push_back(fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib.fmask_tile_mode)));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples));
|
||||
dst.push_back(
|
||||
@@ -167,8 +169,8 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels));
|
||||
dst.push_back(fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.tile_mode = 0x{:08x}\n", rt.attrib3.tile_mode));
|
||||
dst.push_back(fmt::format("\t attrib3.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode)));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension));
|
||||
dst.push_back(fmt::format("\t attrib3.cmask_pipe_aligned = {}\n",
|
||||
@@ -211,22 +213,22 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
return dst;
|
||||
}
|
||||
|
||||
static bool RenderIsColorTileMode(uint32_t tile_mode) {
|
||||
static bool RenderIsColorTileMode(Prospero::TileMode tile_mode) {
|
||||
// AGC CxRenderTarget::TileMode shifted by CB_COLOR*_ATTRIB3.COLOR_SW_MODE.
|
||||
switch (tile_mode) {
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kPrt):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kDepth):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget): return true;
|
||||
case Prospero::TileMode::kLinear:
|
||||
case Prospero::TileMode::kStandard256B:
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
case Prospero::TileMode::kPrt:
|
||||
case Prospero::TileMode::kDepth:
|
||||
case Prospero::TileMode::kRenderTarget: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderIsColorTileModeLinear(uint32_t tile_mode) {
|
||||
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode) {
|
||||
return tile_mode == Prospero::TileMode::kLinear;
|
||||
}
|
||||
|
||||
static bool RenderIsColorDimension(uint32_t dimension) {
|
||||
@@ -360,7 +362,8 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
}
|
||||
}
|
||||
if (!RenderIsColorTileMode(rt.attrib3.tile_mode)) {
|
||||
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n", rt.attrib3.tile_mode);
|
||||
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
if (!RenderIsColorDimension(rt.attrib3.dimension)) {
|
||||
EXIT("unknown PS5 render-target dimension: 0x%08" PRIx32 "\n", rt.attrib3.dimension);
|
||||
@@ -455,13 +458,14 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
|
||||
"\t height = 0x%08" PRIx32 "\n"
|
||||
"\t size.x_max = 0x%04" PRIx16 "\n"
|
||||
"\t size.y_max = 0x%04" PRIx16 "\n",
|
||||
z.z_info.format, z.z_info.num_samples,
|
||||
Prospero::GpuEnumValue(z.z_info.texture_compatibility),
|
||||
static_cast<uint32_t>(z.z_info.format), z.z_info.num_samples,
|
||||
static_cast<uint32_t>(z.z_info.texture_compatibility),
|
||||
z.z_info.htile_acceleration ? "true" : "false",
|
||||
z.z_info.expclear_enabled ? "true" : "false",
|
||||
Prospero::GpuEnumValue(z.z_info.z_compare_base),
|
||||
static_cast<uint32_t>(z.z_info.z_compare_base),
|
||||
z.z_info.partially_resident ? "true" : "false", z.z_info.max_mip_level,
|
||||
z.stencil_info.format, Prospero::GpuEnumValue(z.stencil_info.texture_compatibility),
|
||||
static_cast<uint32_t>(z.stencil_info.format),
|
||||
static_cast<uint32_t>(z.stencil_info.texture_compatibility),
|
||||
z.stencil_info.htile_stencil_disabled ? "true" : "false",
|
||||
z.stencil_info.expclear_enabled ? "true" : "false",
|
||||
z.stencil_info.partially_resident ? "true" : "false", z.depth_info.addr5_swizzle_mask,
|
||||
@@ -490,15 +494,16 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!z.z_info.HasValidTextureCompatibility());
|
||||
EXIT_NOT_IMPLEMENTED(!z.stencil_info.HasValidTextureCompatibility());
|
||||
if (z.z_info.format == 0) {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0);
|
||||
if (z.z_info.format == Prospero::DepthFormat::kInvalid) {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kInvalid);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.htile_acceleration != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0x00000001 && z.z_info.format != 0x00000003);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != Prospero::DepthFormat::kZ16 &&
|
||||
z.z_info.format != Prospero::DepthFormat::kZ32F);
|
||||
if (z.z_info.num_samples != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -512,16 +517,17 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
}
|
||||
|
||||
if (z.stencil_info.format == 0) {
|
||||
if (z.stencil_info.format == Prospero::StencilFormat::kInvalid) {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0x00000001);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != Prospero::StencilFormat::k8UInt);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
}
|
||||
|
||||
if (z.z_info.format != 0 || z.stencil_info.format != 0) {
|
||||
if (z.z_info.format != Prospero::DepthFormat::kInvalid ||
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.addr5_swizzle_mask != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.array_mode != 0x00000000);
|
||||
@@ -556,7 +562,8 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
EXIT_NOT_IMPLEMENTED(z.z_read_base_addr != z.z_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_read_base_addr != z.stencil_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_write_base_addr == 0);
|
||||
if (z.stencil_info.format != 0 && z.stencil_write_base_addr == 0) {
|
||||
if (z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
|
||||
z.stencil_write_base_addr == 0) {
|
||||
LOGF("\t warning: stencil format is set without a stencil base address, continuing "
|
||||
"without stencil attachment\n");
|
||||
}
|
||||
@@ -686,14 +693,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
}
|
||||
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.poly_offset_front_enable || c.poly_offset_back_enable) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("\t temporary: PA_SU_SC_MODE_CNTL.POLY_OFFSET_*_ENABLE is not implemented; "
|
||||
"continuing without depth bias\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (c.vtx_window_offset_enable) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -1067,18 +1066,24 @@ static ScissorRect ScissorRectClamp(ScissorRect r, uint32_t width, uint32_t heig
|
||||
return r;
|
||||
}
|
||||
|
||||
static constexpr std::array<uint16_t, 16> MakeScissorIntersectionRules() {
|
||||
std::array<uint16_t, 16> rules {};
|
||||
for (uint32_t candidate = 0; candidate < rules.size(); candidate++) {
|
||||
for (uint32_t combination = 0; combination < 16; combination++) {
|
||||
if ((combination & candidate) == candidate) {
|
||||
rules[candidate] |= static_cast<uint16_t>(1u << combination);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
static bool ScissorClipRuleToIntersectionMask(uint16_t rule, uint8_t* mask) {
|
||||
EXIT_IF(mask == nullptr);
|
||||
|
||||
for (uint32_t candidate = 0; candidate < 16; candidate++) {
|
||||
uint16_t expected = 0;
|
||||
for (uint32_t combination = 0; combination < 16; combination++) {
|
||||
if ((combination & candidate) == candidate) {
|
||||
expected |= static_cast<uint16_t>(1u << combination);
|
||||
}
|
||||
}
|
||||
|
||||
if (expected == rule) {
|
||||
static constexpr auto rules = MakeScissorIntersectionRules();
|
||||
for (uint32_t candidate = 0; candidate < rules.size(); candidate++) {
|
||||
if (rules[candidate] == rule) {
|
||||
*mask = static_cast<uint8_t>(candidate);
|
||||
return true;
|
||||
}
|
||||
@@ -1219,8 +1224,9 @@ void hw_check(const RenderCommandBuffer& buffer) {
|
||||
const auto& mrt = hw.GetRenderTarget(i);
|
||||
LOGF("\t RT%u addr=0x%010" PRIx64 " mask=0x%x fmt=0x%08" PRIx32
|
||||
" width=%u height=%u tile=%u\n",
|
||||
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu, mrt.info.format,
|
||||
mrt.attrib2.width + 1, mrt.attrib2.height + 1, mrt.attrib3.tile_mode);
|
||||
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu,
|
||||
static_cast<uint32_t>(mrt.info.format), mrt.attrib2.width + 1,
|
||||
mrt.attrib2.height + 1, static_cast<uint32_t>(mrt.attrib3.tile_mode));
|
||||
}
|
||||
}
|
||||
if (rc.depth_clear_enable && hw.GetDepthClearValue() != 0.0f &&
|
||||
|
||||
@@ -34,7 +34,7 @@ void uc_print(const char* func, const HW::UserConfig& uc);
|
||||
void uc_check(const HW::UserConfig& uc);
|
||||
void sh_print(const char* func, const HW::Shader& uc);
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt);
|
||||
bool RenderIsColorTileModeLinear(uint32_t tile_mode);
|
||||
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode);
|
||||
void hw_print(const RenderCommandBuffer& buffer);
|
||||
void hw_check(const RenderCommandBuffer& buffer);
|
||||
void LogDrawPhase(const char* draw_name, const char* phase);
|
||||
|
||||
@@ -38,7 +38,10 @@ namespace Libs::Graphics {
|
||||
EXIT("unsupported render state; details were printed above\n");
|
||||
}
|
||||
|
||||
static vk::StencilOp ConvertStencilOp(uint8_t value) {
|
||||
static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t op_value) {
|
||||
if (write_mask == 0) {
|
||||
return vk::StencilOp::eKeep;
|
||||
}
|
||||
switch (static_cast<Prospero::StencilOp>(value)) {
|
||||
case Prospero::StencilOp::kKeep: return vk::StencilOp::eKeep;
|
||||
case Prospero::StencilOp::kZero: return vk::StencilOp::eZero;
|
||||
@@ -49,14 +52,23 @@ static vk::StencilOp ConvertStencilOp(uint8_t value) {
|
||||
case Prospero::StencilOp::kInvert: return vk::StencilOp::eInvert;
|
||||
case Prospero::StencilOp::kAddWrap: return vk::StencilOp::eIncrementAndWrap;
|
||||
case Prospero::StencilOp::kSubWrap: return vk::StencilOp::eDecrementAndWrap;
|
||||
default: DepthFatal("unsupported stencil operation");
|
||||
case Prospero::StencilOp::kXor:
|
||||
if ((write_mask & op_value) == 0) {
|
||||
return vk::StencilOp::eKeep;
|
||||
}
|
||||
if ((write_mask & ~op_value) != 0) {
|
||||
DepthFatal("unsupported stencil XOR operands: write mask=0x%02" PRIx8
|
||||
", operation value=0x%02" PRIx8,
|
||||
write_mask, op_value);
|
||||
}
|
||||
return vk::StencilOp::eInvert;
|
||||
default: DepthFatal("unsupported stencil operation: 0x%02" PRIx8, value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
return fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
|
||||
pass == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
|
||||
depth_fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp);
|
||||
constexpr auto replace_op = static_cast<uint8_t>(Prospero::StencilOp::kReplaceOp);
|
||||
return fail == replace_op || pass == replace_op || depth_fail == replace_op;
|
||||
}
|
||||
|
||||
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const RenderCommandBuffer& buffer,
|
||||
@@ -90,15 +102,14 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
(void)submit_id;
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil =
|
||||
z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid);
|
||||
const bool depth_active =
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active =
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
|
||||
const bool stencil_active = has_stencil && (dc.stencil_enable || rc.stencil_clear_enable);
|
||||
if (!depth_active && !stencil_active) {
|
||||
@@ -109,9 +120,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("invalid PS5 depth texture-compatibility encoding");
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kInvalid) &&
|
||||
z.stencil_info.format == Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid) &&
|
||||
z.z_info.num_samples == 0 &&
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
@@ -173,8 +183,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
if (has_stencil) {
|
||||
if (z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt) ||
|
||||
!htile_stencil_compat || z.stencil_read_base_addr == 0 ||
|
||||
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
|
||||
z.stencil_read_base_addr == 0 ||
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr ||
|
||||
(z.stencil_read_base_addr & 0xffffu) != 0 || z.depth_view.stencil_write_disable) {
|
||||
DepthFatal("unsupported stencil attachment state");
|
||||
@@ -192,8 +202,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
if (z.depth_view.slice_max >= 32) {
|
||||
DepthFatal("HTile clear tracking supports at most 32 slices");
|
||||
}
|
||||
} else if (z.htile_data_base_addr != 0) {
|
||||
DepthFatal("HTile address without an enabled tile surface");
|
||||
}
|
||||
const bool size_xy_valid = z.size.valid;
|
||||
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
|
||||
@@ -218,7 +226,7 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("no host depth/stencil format supports required usage for %s",
|
||||
VulkanToString(ideal_format).c_str());
|
||||
}
|
||||
const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format);
|
||||
const auto guest_format = policy->guest_format;
|
||||
const uint32_t bytes = policy->bytes_per_element;
|
||||
const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
|
||||
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
|
||||
@@ -283,6 +291,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
r.stencil_clear_value = hw.GetStencilClearValue();
|
||||
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
||||
if (r.stencil_test_enable) {
|
||||
const uint8_t front_write_mask = rc.stencil_clear_enable ? 0 : sm.stencil_writemask;
|
||||
const uint8_t back_write_mask = rc.stencil_clear_enable ? 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)) ||
|
||||
@@ -294,19 +304,18 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("unsupported stencil compare or replacement state");
|
||||
}
|
||||
r.stencil_static_front = {
|
||||
ConvertStencilOp(sc.stencil_fail), ConvertStencilOp(sc.stencil_zpass),
|
||||
ConvertStencilOp(sc.stencil_zfail), static_cast<vk::CompareOp>(dc.stencilfunc)};
|
||||
r.stencil_dynamic_front = {sm.stencil_mask,
|
||||
rc.stencil_clear_enable ? 0u : sm.stencil_writemask,
|
||||
sm.stencil_testval};
|
||||
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};
|
||||
if (dc.backface_enable) {
|
||||
r.stencil_static_back = {ConvertStencilOp(sc.stencil_fail_bf),
|
||||
ConvertStencilOp(sc.stencil_zpass_bf),
|
||||
ConvertStencilOp(sc.stencil_zfail_bf),
|
||||
static_cast<vk::CompareOp>(dc.stencilfunc_bf)};
|
||||
r.stencil_dynamic_back = {sm.stencil_mask_bf,
|
||||
rc.stencil_clear_enable ? 0u : sm.stencil_writemask_bf,
|
||||
sm.stencil_testval_bf};
|
||||
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};
|
||||
} else {
|
||||
r.stencil_static_back = r.stencil_static_front;
|
||||
r.stencil_dynamic_back = r.stencil_dynamic_front;
|
||||
@@ -331,7 +340,7 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, r.depth_buffer_size, pitch, height};
|
||||
desc.info.metadata.range = {r.htile_buffer_vaddr, r.htile_buffer_size};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
|
||||
@@ -646,13 +646,13 @@ void Validate(const ImageInfo& info) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
switch (bytes_per_element) {
|
||||
case 1: return Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm);
|
||||
case 2: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm);
|
||||
case 4: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
case 8: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float);
|
||||
case 16: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float);
|
||||
case 1: return Prospero::BufferFormat::k8UNorm;
|
||||
case 2: return Prospero::BufferFormat::k16UNorm;
|
||||
case 4: return Prospero::BufferFormat::k32Float;
|
||||
case 8: return Prospero::BufferFormat::k16_16_16_16Float;
|
||||
case 16: return Prospero::BufferFormat::k32_32_32_32Float;
|
||||
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
|
||||
}
|
||||
}
|
||||
@@ -663,7 +663,8 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
: info(image_info), m_graphics(&graphics), m_scheduler(&scheduler) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
ImageOps::Validate(info);
|
||||
m_cpu_dirty = !info.data.Empty();
|
||||
m_cpu_dirty =
|
||||
!info.data.Empty() && info.metadata.compression == VideoOutCompression::Uncompressed;
|
||||
if (info.pixel_format == vk::Format::eUndefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,8 +188,8 @@ private:
|
||||
|
||||
namespace ImageOps {
|
||||
|
||||
void Validate(const ImageInfo& info);
|
||||
[[nodiscard]] uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element);
|
||||
void Validate(const ImageInfo& info);
|
||||
[[nodiscard]] Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element);
|
||||
|
||||
} // namespace ImageOps
|
||||
|
||||
|
||||
@@ -67,14 +67,14 @@ struct ImageInfo {
|
||||
ImageMetadataInfo metadata;
|
||||
uint32_t htile_clear_mask = UINT32_MAX;
|
||||
vk::Format pixel_format = vk::Format::eUndefined;
|
||||
uint32_t guest_format = 0;
|
||||
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
|
||||
Prospero::ImageType type = Prospero::ImageType::kColor2D;
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
ImageSubresources resources;
|
||||
uint32_t pitch = 0;
|
||||
uint32_t bytes_per_block = 0;
|
||||
uint32_t samples = 1;
|
||||
uint32_t tile_mode = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
bool bgra16 = false;
|
||||
std::array<ImageMipInfo, 16> mip_layout {};
|
||||
|
||||
@@ -86,9 +86,7 @@ struct ImageInfo {
|
||||
[[nodiscard]] bool IsBlock() const noexcept {
|
||||
return Prospero::BlockCompressedBytesPerBlock(guest_format) != 0;
|
||||
}
|
||||
[[nodiscard]] bool IsTiled() const noexcept {
|
||||
return tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
}
|
||||
[[nodiscard]] bool IsTiled() const noexcept { return tile_mode != Prospero::TileMode::kLinear; }
|
||||
[[nodiscard]] constexpr bool IsVolume() const noexcept {
|
||||
return type == Prospero::ImageType::kColor3D;
|
||||
}
|
||||
@@ -235,9 +233,9 @@ inline constexpr std::array<DepthFormatPolicy, 2> DEPTH_FORMAT_POLICIES {{
|
||||
}};
|
||||
|
||||
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
||||
FindDepthFormatPolicy(uint32_t depth_format) noexcept {
|
||||
FindDepthFormatPolicy(Prospero::DepthFormat depth_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (Prospero::GpuEnumValue(policy.depth_format) == depth_format) {
|
||||
if (policy.depth_format == depth_format) {
|
||||
return &policy;
|
||||
}
|
||||
}
|
||||
@@ -245,9 +243,9 @@ FindDepthFormatPolicy(uint32_t depth_format) noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
||||
FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
|
||||
FindGuestDepthFormatPolicy(Prospero::BufferFormat guest_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (Prospero::GpuEnumValue(policy.guest_format) == guest_format) {
|
||||
if (policy.guest_format == guest_format) {
|
||||
return &policy;
|
||||
}
|
||||
}
|
||||
@@ -269,10 +267,11 @@ FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
|
||||
return has_stencil ? policy.stencil_attachment_formats.front() : policy.depth_attachment_format;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr vk::Format DepthAttachmentFormat(uint32_t depth_format,
|
||||
uint32_t stencil_format) noexcept {
|
||||
[[nodiscard]] inline constexpr vk::Format
|
||||
DepthAttachmentFormat(Prospero::DepthFormat depth_format,
|
||||
Prospero::StencilFormat stencil_format) noexcept {
|
||||
bool has_stencil = false;
|
||||
switch (static_cast<Prospero::StencilFormat>(stencil_format)) {
|
||||
switch (stencil_format) {
|
||||
case Prospero::StencilFormat::kInvalid: break;
|
||||
case Prospero::StencilFormat::k8UInt: has_stencil = true; break;
|
||||
default: return vk::Format::eUndefined;
|
||||
@@ -318,9 +317,9 @@ inline bool ImageInfo::IsDepth() const noexcept {
|
||||
return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
||||
uint32_t guest_format,
|
||||
vk::Format view_format) noexcept {
|
||||
[[nodiscard]] inline constexpr bool
|
||||
IsSupportedSampledDepthFormat(vk::Format image_format, Prospero::BufferFormat guest_format,
|
||||
vk::Format view_format) noexcept {
|
||||
const auto* policy = FindGuestDepthFormatPolicy(guest_format);
|
||||
return policy != nullptr && view_format == policy->sampled_view_format &&
|
||||
(image_format == policy->depth_attachment_format ||
|
||||
@@ -330,8 +329,7 @@ inline bool ImageInfo::IsDepth() const noexcept {
|
||||
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
||||
vk::Format view_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (IsSupportedSampledDepthFormat(image_format, Prospero::GpuEnumValue(policy.guest_format),
|
||||
view_format)) {
|
||||
if (IsSupportedSampledDepthFormat(image_format, policy.guest_format, view_format)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -383,10 +381,10 @@ CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_t
|
||||
}
|
||||
|
||||
struct VideoOutPixelFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
uint32_t guest_format = 0;
|
||||
uint32_t bytes_per_element = 0;
|
||||
bool bgra16 = false;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
|
||||
uint32_t bytes_per_element = 0;
|
||||
bool bgra16 = false;
|
||||
};
|
||||
|
||||
struct VideoOutFormatPolicy {
|
||||
@@ -396,23 +394,17 @@ struct VideoOutFormatPolicy {
|
||||
|
||||
inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {{
|
||||
{0x8000000022000000ull,
|
||||
{vk::Format::eR8G8B8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
||||
false}},
|
||||
{vk::Format::eR8G8B8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
|
||||
{0x8000000000000000ull,
|
||||
{vk::Format::eB8G8R8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
||||
false}},
|
||||
{vk::Format::eB8G8R8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
|
||||
{0x8100000022000000ull,
|
||||
{vk::Format::eA2B10G10R10UnormPack32,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
||||
{vk::Format::eA2B10G10R10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
|
||||
{0x8100000000000000ull,
|
||||
{vk::Format::eA2R10G10B10UnormPack32,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
||||
{vk::Format::eA2R10G10B10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
|
||||
{0xc001000622000000ull,
|
||||
{vk::Format::eR16G16B16A16Sfloat,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, false}},
|
||||
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, false}},
|
||||
{0xc001000600000000ull,
|
||||
{vk::Format::eR16G16B16A16Sfloat,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, true}},
|
||||
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, true}},
|
||||
}};
|
||||
|
||||
[[nodiscard]] inline bool DecodeVideoOutPixelFormat(uint64_t pixel_format,
|
||||
@@ -439,15 +431,15 @@ inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool
|
||||
IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
||||
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
IsSupportedDisplayRenderTargetTileMode(Prospero::TileMode tile_mode) noexcept {
|
||||
return tile_mode == Prospero::TileMode::kRenderTarget;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) {
|
||||
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
|
||||
info.data.address == 0 || (info.data.address & 0xffffu) != 0 || info.extent.width == 0 ||
|
||||
info.extent.height == 0 || info.bytes_per_block != 4 || info.resources.levels != 1 ||
|
||||
info.resources.layers != 1 || info.samples != 1) {
|
||||
if (info.tile_mode != Prospero::TileMode::kStandard64KB || info.data.address == 0 ||
|
||||
(info.data.address & 0xffffu) != 0 || info.extent.width == 0 || info.extent.height == 0 ||
|
||||
info.bytes_per_block != 4 || info.resources.levels != 1 || info.resources.layers != 1 ||
|
||||
info.samples != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto expected_pitch =
|
||||
@@ -460,7 +452,7 @@ IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept {
|
||||
return info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
||||
return info.tile_mode == Prospero::TileMode::kRenderTarget ||
|
||||
IsSupportedStandard64RenderTarget(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,164 +14,71 @@
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
|
||||
struct RenderTargetFormatMapping {
|
||||
Prospero::ChannelLayout layout;
|
||||
Prospero::ChannelType type;
|
||||
Prospero::ChannelOrder order;
|
||||
RenderTargetFormatInfo info;
|
||||
// Rows are channel order; columns are the number of physical components minus one. Unused
|
||||
// selectors complete each entry to a permutation so logical write masks can be inverted.
|
||||
constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = {
|
||||
{Prospero::ColorMappingRgba, Prospero::ColorMappingRgba, Prospero::ColorMappingRgba,
|
||||
Prospero::ColorMappingRgba},
|
||||
{Prospero::ColorMappingGr, Prospero::ColorMappingRabg, Prospero::ColorMappingRgab,
|
||||
Prospero::ColorMappingBgra},
|
||||
{Prospero::ColorMappingBgra, Prospero::ColorMappingGr, Prospero::ColorMappingBgra,
|
||||
Prospero::ColorMappingAbgr},
|
||||
{Prospero::ColorMappingAgba, Prospero::ColorMappingArbg, Prospero::ColorMappingAgbr,
|
||||
Prospero::ColorMappingArgb},
|
||||
};
|
||||
|
||||
constexpr RenderTargetFormatMapping kRenderTargetFormats[] = {
|
||||
{Prospero::ChannelLayout::k8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8Unorm, 2}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSrgb,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Srgb, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSrgb,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Srgb, 4}},
|
||||
{Prospero::ChannelLayout::k5_5_5_1,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR5G5B5A1UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k4_4_4_4,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kReversed,
|
||||
{vk::Format::eB4G4R4A4UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k10_10_10_2,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eA2B10G10R10UnormPack32, 4}},
|
||||
{Prospero::ChannelLayout::k10_10_10_2,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eA2R10G10B10UnormPack32, 4}},
|
||||
{Prospero::ChannelLayout::k11_11_10,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eB10G11R11UfloatPack32, 4}},
|
||||
{Prospero::ChannelLayout::k5_6_5,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eB5G6R5UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Unorm, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Uint, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Sfloat, 2}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Uint, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Sfloat, 4}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Unorm, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Uint, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingBgra}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kReversed,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingAbgr}},
|
||||
{Prospero::ChannelLayout::k32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32Sfloat, 4}},
|
||||
{Prospero::ChannelLayout::k32_32,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32Uint, 8}},
|
||||
{Prospero::ChannelLayout::k32_32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32Sfloat, 8}},
|
||||
{Prospero::ChannelLayout::k32_32_32_32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32B32A32Sfloat, 16}},
|
||||
struct RenderTargetHostFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
if (order == Prospero::ChannelOrder::kAlt) {
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
||||
return {vk::Format::eB8G8R8A8Unorm, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k8_8_8_8SNorm:
|
||||
return {vk::Format::eB8G8R8A8Snorm, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k8_8_8_8Srgb:
|
||||
return {vk::Format::eB8G8R8A8Srgb, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k10_10_10_2UNorm:
|
||||
return {vk::Format::eA2R10G10B10UnormPack32, Prospero::ColorMappingBgra};
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm:
|
||||
return {vk::Format::eA1R5G5B5UnormPack16, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k1_5_5_5UNorm:
|
||||
return {vk::Format::eR5G5B5A1UnormPack16, Prospero::ColorMappingAbgr};
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm:
|
||||
return {vk::Format::eR4G4B4A4UnormPack16, Prospero::ColorMappingAbgr};
|
||||
default: return {VulkanFormat(guest_format), {}};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO: cleanup!
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t raw_layout, uint32_t raw_type,
|
||||
uint32_t raw_order) {
|
||||
const auto layout = static_cast<Prospero::ChannelLayout>(raw_layout);
|
||||
const auto type = static_cast<Prospero::ChannelType>(raw_type);
|
||||
const auto order = static_cast<Prospero::ChannelOrder>(raw_order);
|
||||
|
||||
if (layout == Prospero::ChannelLayout::k8 && type == Prospero::ChannelType::kUNorm &&
|
||||
raw_order <= Prospero::GpuEnumValue(Prospero::ChannelOrder::kAltReversed)) {
|
||||
return {vk::Format::eR8Unorm, 1};
|
||||
}
|
||||
for (const auto& mapping: kRenderTargetFormats) {
|
||||
if (mapping.layout == layout && mapping.type == type && mapping.order == order) {
|
||||
return mapping.info;
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host = ResolveRenderTargetHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host.format, bytes, host.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", raw_layout,
|
||||
raw_type, raw_order);
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static uint64_t GetLevelSrcOffset(const TileSizeOffset& level_size) {
|
||||
return (level_size.src_size != 0 ? level_size.src_offset : level_size.offset);
|
||||
}
|
||||
|
||||
static uint64_t GetLevelSrcSize(const TileSizeOffset& level_size) {
|
||||
return (level_size.src_size != 0 ? level_size.src_size : level_size.size);
|
||||
}
|
||||
|
||||
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
@@ -184,20 +91,6 @@ static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t TextureUploadSliceSourceOffset(const TextureUploadLayout& layout, uint32_t level,
|
||||
uint32_t slice) {
|
||||
if (level >= 16 || layout.level_sizes[level].size == 0) {
|
||||
EXIT("invalid texture upload slice source, level=%u slice=%u\n", level, slice);
|
||||
}
|
||||
const auto level_offset = GetLevelSrcOffset(layout.level_sizes[level]);
|
||||
const auto slice_stride =
|
||||
layout.source_slice_stride != 0 ? layout.source_slice_stride : layout.slice_stride;
|
||||
if (slice_stride != 0 && slice > (UINT64_MAX - level_offset) / slice_stride) {
|
||||
EXIT("texture upload slice source offset overflow, level=%u slice=%u\n", level, slice);
|
||||
}
|
||||
return level_offset + static_cast<uint64_t>(slice) * slice_stride;
|
||||
}
|
||||
|
||||
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
|
||||
switch (static_cast<Prospero::CompSwizzle>(s)) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
@@ -226,23 +119,22 @@ vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
||||
return components;
|
||||
}
|
||||
|
||||
vk::Format TextureGetFormat(uint32_t fmt) {
|
||||
const auto vk_format = VulkanFormat(fmt);
|
||||
vk::Format TextureGetFormat(Prospero::BufferFormat format) {
|
||||
const auto vk_format = VulkanFormat(format);
|
||||
if (vk_format != vk::Format::eUndefined) {
|
||||
return vk_format;
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", fmt);
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
return vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TileSizeOffset* level_sizes, uint64_t levels,
|
||||
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, static_cast<uint64_t>(level_sizes[i].offset) + level_sizes[i].size);
|
||||
stride = std::max(stride, mips[i].offset + mips[i].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
@@ -255,39 +147,26 @@ uint64_t CalcTextureSliceStride(const TileSizeOffset* level_sizes, uint64_t leve
|
||||
return stride;
|
||||
}
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(uint32_t fmt, uint32_t pitch, uint32_t height) {
|
||||
if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(fmt);
|
||||
bytes_per_element != 0) {
|
||||
return static_cast<uint64_t>(pitch) * height * bytes_per_element;
|
||||
}
|
||||
|
||||
if (const uint32_t bytes_per_block = Prospero::BlockCompressedBytesPerBlock(fmt);
|
||||
bytes_per_block != 0) {
|
||||
const uint32_t blocks_w = std::max((pitch + 3u) / 4u, 1u);
|
||||
const uint32_t blocks_h = std::max((height + 3u) / 4u, 1u);
|
||||
return static_cast<uint64_t>(blocks_w) * blocks_h * bytes_per_block;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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(TileSizeOffset* level_sizes, uint32_t fmt, uint64_t height,
|
||||
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(fmt, pitch, h);
|
||||
EXIT_NOT_IMPLEMENTED(size > 0xffffffffull);
|
||||
EXIT_NOT_IMPLEMENTED(offset > 0xffffffffull);
|
||||
|
||||
level_sizes[i].size = static_cast<uint32_t>(size);
|
||||
level_sizes[i].offset = static_cast<uint32_t>(offset);
|
||||
level_sizes[i].src_size = 0;
|
||||
level_sizes[i].src_offset = 0;
|
||||
level_sizes[i].x = 0;
|
||||
level_sizes[i].y = 0;
|
||||
const auto size = CalcLinearUploadLevelSize(element, pitch, h);
|
||||
mips[i].size = size;
|
||||
mips[i].offset = offset;
|
||||
|
||||
offset += size;
|
||||
if (pitch > 1) {
|
||||
@@ -303,141 +182,126 @@ uint64_t SetLinearUploadLevels(TileSizeOffset* level_sizes, uint32_t fmt, uint64
|
||||
|
||||
} // namespace
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64_t height,
|
||||
uint64_t levels, uint32_t depth, uint64_t pitch,
|
||||
uint64_t tile, uint64_t upload_size,
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner) {
|
||||
TextureUploadLayout layout {};
|
||||
layout.tile = static_cast<uint32_t>(tile);
|
||||
layout.pitch = static_cast<uint32_t>(pitch);
|
||||
layout.texel_block = Prospero::BlockCompressedBytesPerBlock(fmt) != 0 ? 4u : 1u;
|
||||
layout.volume_texture = volume_texture;
|
||||
layout.surface.description = {
|
||||
format,
|
||||
tile,
|
||||
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
|
||||
width,
|
||||
height,
|
||||
volume_texture ? depth : 1u,
|
||||
levels,
|
||||
volume_texture ? 1u : depth,
|
||||
};
|
||||
const auto& description = layout.surface.description;
|
||||
const auto tile_mode = description.tile_mode;
|
||||
TileTextureElementLayout element {};
|
||||
|
||||
if (fmt != 0) {
|
||||
if (layout.tile != 0) {
|
||||
const auto tile_mode = static_cast<Prospero::TileMode>(layout.tile);
|
||||
switch (tile_mode) {
|
||||
case Prospero::TileMode::kStandard256B:
|
||||
if (TileIsStandard256BTextureSupported(fmt)) {
|
||||
layout.tile_family = TileBlockFamily::Standard256B;
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
if (TileIsStandard4KBTextureSupported(fmt)) {
|
||||
layout.tile_family = TileBlockFamily::Standard4KB;
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
if (TileIsStandard64KBTextureSupported(fmt)) {
|
||||
layout.tile_family = TileBlockFamily::Standard64KB;
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kPrt:
|
||||
if (TileIsStandard64KBTextureSupported(fmt)) {
|
||||
layout.tile_family = TileBlockFamily::Prt64KB;
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kRenderTarget:
|
||||
if (Prospero::RenderTargetBytesPerElement(fmt) != 0) {
|
||||
layout.tile_family = TileBlockFamily::RenderTarget64KB;
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
if (allow_depth_tile && Prospero::RenderTargetBytesPerElement(fmt) != 0) {
|
||||
layout.tile_family = TileBlockFamily::Depth64KB;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (layout.tile_family == TileBlockFamily::Count) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%" PRIu64 "x%" PRIu64 " pitch=%" PRIu64
|
||||
" levels=%" PRIu64 "\n",
|
||||
owner, static_cast<uint32_t>(fmt), layout.tile, upload_size, width, height,
|
||||
pitch, levels);
|
||||
}
|
||||
}
|
||||
|
||||
layout.pitch = TileGetTexturePitch(fmt, width, levels, layout.tile);
|
||||
|
||||
TileGetTextureSize(fmt, width, height, layout.pitch, levels, layout.tile, nullptr,
|
||||
layout.level_sizes, layout.padded_sizes);
|
||||
|
||||
if (static_cast<Prospero::TileMode>(layout.tile) != Prospero::TileMode::kLinear) {
|
||||
if (layout.volume_texture) {
|
||||
layout.slice_stride = SetLinearUploadLevels(layout.level_sizes, fmt, height, levels,
|
||||
static_cast<uint32_t>(width));
|
||||
} else {
|
||||
TileSizeOffset tiled_levels[16] {};
|
||||
std::copy_n(layout.level_sizes, levels, tiled_levels);
|
||||
layout.source_slice_stride =
|
||||
CalcTextureSliceStride(tiled_levels, levels, upload_size, depth);
|
||||
SetLinearUploadLevels(layout.level_sizes, fmt, height, levels, layout.pitch);
|
||||
for (uint32_t i = 0; i < levels; ++i) {
|
||||
if (tiled_levels[i].src_size > tiled_levels[i].size) {
|
||||
layout.first_tail_level = std::min(layout.first_tail_level, i);
|
||||
}
|
||||
layout.level_sizes[i].src_offset = GetLevelSrcOffset(tiled_levels[i]);
|
||||
layout.level_sizes[i].src_size = GetLevelSrcSize(tiled_levels[i]);
|
||||
layout.level_sizes[i].x = tiled_levels[i].x;
|
||||
layout.level_sizes[i].y = tiled_levels[i].y;
|
||||
}
|
||||
}
|
||||
} else if (layout.volume_texture) {
|
||||
layout.slice_stride =
|
||||
CalcTextureSliceStride(layout.level_sizes, levels, upload_size, depth);
|
||||
}
|
||||
} else {
|
||||
if (format == Prospero::BufferFormat::kInvalid) {
|
||||
EXIT("%s: legacy texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64
|
||||
" extent=%" PRIu64 "x%" PRIu64 " pitch=%" PRIu64 " levels=%" PRIu64 "\n",
|
||||
owner, layout.tile, upload_size, width, height, pitch, levels);
|
||||
" extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
|
||||
if (!layout.volume_texture) {
|
||||
layout.slice_stride =
|
||||
CalcTextureSliceStride(layout.level_sizes, levels, upload_size, depth);
|
||||
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) {
|
||||
element = {layout.surface.texture.block.bytes_per_element,
|
||||
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
|
||||
}
|
||||
layout.pitch = TileGetTexturePitch(format, width, description.tile_mode);
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
TileSizeOffset level_sizes[16] {};
|
||||
TilePaddedSize padded_sizes[16] {};
|
||||
TileGetTextureSize(format, width, height, levels, description.tile_mode, nullptr,
|
||||
level_sizes, padded_sizes);
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
layout.mips[level] = {level_sizes[level].offset, level_sizes[level].size,
|
||||
padded_sizes[level].width, padded_sizes[level].height};
|
||||
}
|
||||
} else if (volume_texture) {
|
||||
layout.slice_stride = SetLinearUploadLevels(layout.mips, element, height, levels, width);
|
||||
} else {
|
||||
layout.source_slice_stride = layout.surface.block_slice_size;
|
||||
if (depth > 1 && upload_size != 0 && upload_size % depth == 0) {
|
||||
layout.source_slice_stride = std::max(layout.source_slice_stride, upload_size / depth);
|
||||
}
|
||||
SetLinearUploadLevels(layout.mips, element, height, levels, layout.pitch);
|
||||
}
|
||||
|
||||
if (!volume_texture || tile_mode == Prospero::TileMode::kLinear) {
|
||||
layout.slice_stride = CalcTextureSliceStride(layout.mips, levels, upload_size, depth);
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint64_t levels,
|
||||
bool array_texture, bool volume_texture) {
|
||||
uint32_t mip_width = width;
|
||||
uint32_t mip_height = height;
|
||||
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(layout.tile) !=
|
||||
Prospero::TileMode::kLinear
|
||||
? width
|
||||
: layout.pitch;
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
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;
|
||||
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
regions.reserve(GetTextureRegionCount(depth, levels, volume_texture));
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.level_sizes[i].size == 0);
|
||||
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.level_sizes[i].offset + slice_offset;
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, array_texture ? z : 0,
|
||||
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 =
|
||||
static_cast<Prospero::TileMode>(layout.tile) == Prospero::TileMode::kLinear;
|
||||
const bool linear = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
if (linear) {
|
||||
region.bufferRowLength = layout.padded_sizes[i].width;
|
||||
region.bufferImageHeight = layout.padded_sizes[i].height;
|
||||
region.bufferRowLength = layout.mips[i].row_length;
|
||||
region.bufferImageHeight = layout.mips[i].image_height;
|
||||
} else {
|
||||
const auto align = [](uint32_t value, uint32_t block) {
|
||||
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, layout.texel_block);
|
||||
region.bufferRowLength = pitch > align(mip_width, layout.texel_block) ? pitch : 0;
|
||||
const auto pitch = align(mip_pitch, texel_width);
|
||||
region.bufferRowLength = pitch > align(mip_width, texel_width) ? pitch : 0;
|
||||
}
|
||||
regions.push_back(region);
|
||||
}
|
||||
@@ -456,24 +320,6 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
return regions;
|
||||
}
|
||||
|
||||
struct GpuTileElementLayout {
|
||||
uint32_t bytes = 0;
|
||||
uint32_t wide = 1;
|
||||
uint32_t tall = 1;
|
||||
};
|
||||
|
||||
static bool GetGpuTileElementLayout(uint32_t fmt, GpuTileElementLayout& out) {
|
||||
if (const auto bytes = Prospero::NumBytesPerElement(fmt); bytes != 0) {
|
||||
out = {bytes, 1, 1};
|
||||
return true;
|
||||
}
|
||||
if (const auto bytes = Prospero::BlockCompressedBytesPerBlock(fmt); bytes != 0) {
|
||||
out = {bytes, 4, 4};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t& size) {
|
||||
if (offset > capacity || length > capacity - offset) {
|
||||
return false;
|
||||
@@ -483,42 +329,32 @@ static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity,
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& out_infos) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
if (tiled_size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
||||
regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
|
||||
Prospero::IsFmaskTextureFormat(fmt)) {
|
||||
regions.size() != GetTextureRegionCount(depth, levels, volume_texture) ||
|
||||
Prospero::IsFmaskTextureFormat(description.format)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GpuTileElementLayout element {};
|
||||
if (layout.tile_family == TileBlockFamily::RenderTarget64KB ||
|
||||
layout.tile_family == TileBlockFamily::Depth64KB) {
|
||||
element.bytes = Prospero::RenderTargetBytesPerElement(fmt);
|
||||
} else if (!GetGpuTileElementLayout(fmt, element)) {
|
||||
return false;
|
||||
}
|
||||
if (element.bytes == 0) {
|
||||
const auto& surface = layout.surface;
|
||||
const auto& texture = surface.texture;
|
||||
const auto& block = texture.block;
|
||||
if (surface.description.levels < levels || block.bytes_per_element == 0 ||
|
||||
block.family == TileBlockFamily::Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GpuTileInfo> infos;
|
||||
infos.reserve(regions.size());
|
||||
if (layout.volume_texture) {
|
||||
TileVolumeLayout volume {};
|
||||
if (!TileGetTextureVolumeLayout(fmt, regions[0].imageExtent.width,
|
||||
regions[0].imageExtent.height, depth,
|
||||
static_cast<uint32_t>(levels), layout.tile, volume)) {
|
||||
return false;
|
||||
}
|
||||
element = {volume.bytes_per_element, volume.texel_width, volume.texel_height};
|
||||
TileBlockLayout block {};
|
||||
if (!TileGetBlockLayout(volume.family, element.bytes, block)) return false;
|
||||
|
||||
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 >= volume.first_tail_level;
|
||||
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);
|
||||
@@ -533,50 +369,43 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset =
|
||||
static_cast<uint64_t>(z / block.block_depth) * volume.block_slice_size +
|
||||
volume.level_offsets[level];
|
||||
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.level_sizes[level].size;
|
||||
layout.mips[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], tiled_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 + element.wide - 1u) / element.wide, 1u);
|
||||
info.height = std::max((logical_height + element.tall - 1u) / element.tall, 1u);
|
||||
info.depth = copy_depth;
|
||||
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 =
|
||||
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||
info.pitch = std::max((pitch + element.wide - 1u) / element.wide, 1u);
|
||||
info.tail_x = tail ? volume.tail_x[level] : 0;
|
||||
info.tail_y = tail ? volume.tail_y[level] : 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 = volume.level_widths[level];
|
||||
info.tiled_height = volume.level_heights[level];
|
||||
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 {
|
||||
const auto base_family = layout.tile_family;
|
||||
if (base_family == TileBlockFamily::Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t region_index = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
const auto& level_size = layout.level_sizes[level];
|
||||
const bool tail = level >= layout.first_tail_level;
|
||||
const auto family = base_family;
|
||||
TileBlockLayout block {};
|
||||
if (!TileGetBlockLayout(family, element.bytes, block)) {
|
||||
return false;
|
||||
}
|
||||
const auto level_depth = GetTextureLevelDepth(depth, level, layout.volume_texture);
|
||||
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 =
|
||||
@@ -585,39 +414,36 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
? 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 = TextureUploadSliceSourceOffset(layout, level, z);
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), tiled_size,
|
||||
info.tiled_size)) {
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
info.width =
|
||||
std::max((region.imageExtent.width + element.wide - 1u) / element.wide, 1u);
|
||||
info.height = std::max((logical_height + element.tall - 1u) / element.tall, 1u);
|
||||
info.surface_z = base_family == TileBlockFamily::RenderTarget64KB ||
|
||||
base_family == TileBlockFamily::Depth64KB
|
||||
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 + element.wide - 1u) / element.wide, 1u);
|
||||
info.tail = tail;
|
||||
info.tail_x = tail ? level_size.x : 0;
|
||||
info.tail_y = tail ? level_size.y : 0;
|
||||
info.tiled_width =
|
||||
layout.padded_sizes[level].width != 0
|
||||
? std::max((layout.padded_sizes[level].width + element.wide - 1u) /
|
||||
element.wide,
|
||||
1u)
|
||||
: info.pitch;
|
||||
info.tiled_height =
|
||||
layout.padded_sizes[level].height != 0
|
||||
? std::max((layout.padded_sizes[level].height + element.tall - 1u) /
|
||||
element.tall,
|
||||
1u)
|
||||
: info.height;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,34 +19,35 @@ struct RenderTargetFormatInfo {
|
||||
Prospero::ColorComponentMapping export_mapping;
|
||||
};
|
||||
|
||||
struct TextureUploadMipLayout {
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = 0;
|
||||
uint32_t row_length = 0;
|
||||
uint32_t image_height = 0;
|
||||
};
|
||||
|
||||
struct TextureUploadLayout {
|
||||
uint32_t tile = 0;
|
||||
uint32_t pitch = 0;
|
||||
uint32_t texel_block = 1;
|
||||
uint64_t slice_stride = 0;
|
||||
uint64_t source_slice_stride = 0;
|
||||
uint32_t first_tail_level = 16;
|
||||
TileBlockFamily tile_family = TileBlockFamily::Count;
|
||||
bool volume_texture = false;
|
||||
TileSizeOffset level_sizes[16] = {};
|
||||
TilePaddedSize padded_sizes[16] = {};
|
||||
uint32_t pitch = 0;
|
||||
uint64_t slice_stride = 0;
|
||||
uint64_t source_slice_stride = 0;
|
||||
TileSurfaceLayout surface;
|
||||
TextureUploadMipLayout mips[16] = {};
|
||||
};
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
||||
vk::Format TextureGetFormat(uint32_t fmt);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t layout, uint32_t type, uint32_t order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64_t height,
|
||||
uint64_t levels, uint32_t depth, uint64_t pitch,
|
||||
uint64_t tile, uint64_t upload_size,
|
||||
vk::Format TextureGetFormat(Prospero::BufferFormat format);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner);
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint64_t levels,
|
||||
bool array_texture, bool volume_texture);
|
||||
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 fmt, uint32_t depth,
|
||||
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& infos);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ static void CopyNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& so
|
||||
}
|
||||
|
||||
static Prospero::ImageType TextureType(const ShaderTextureResource& descriptor) {
|
||||
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
|
||||
const auto type = descriptor.Type();
|
||||
return type == Prospero::ImageType::kCube ? Prospero::ImageType::kColor2DArray : type;
|
||||
}
|
||||
|
||||
@@ -227,19 +227,19 @@ bool IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& r
|
||||
return image.usage.video_out && image.info.resources.layers == 1 &&
|
||||
IsSupportedSampledColorResource(resource) &&
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
descriptor.Depth() == 0 && descriptor.BaseArray5() == 0;
|
||||
descriptor.Type() == Prospero::ImageType::kColor2D && descriptor.Depth() == 0 &&
|
||||
descriptor.BaseArray5() == 0;
|
||||
}
|
||||
|
||||
bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, const Image& image) {
|
||||
const auto width = static_cast<uint32_t>(descriptor.Width5()) + 1u;
|
||||
const auto height = static_cast<uint32_t>(descriptor.Height5()) + 1u;
|
||||
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
|
||||
const auto type = descriptor.Type();
|
||||
const bool multisampled = IsMultisampledTexture(type);
|
||||
const auto samples = multisampled ? 1u << descriptor.LastLevel() : 1u;
|
||||
const auto pitch =
|
||||
multisampled ? TileGetDepthPitch(width, image.info.bytes_per_block, descriptor.LastLevel())
|
||||
: TileGetTexturePitch(descriptor.Format(), width, 1, descriptor.TileMode());
|
||||
: TileGetTexturePitch(descriptor.Format(), width, descriptor.TileMode());
|
||||
const bool supported_2d = type == Prospero::ImageType::kColor2D &&
|
||||
image.info.resources.layers == 1 && descriptor.Depth() == 0 &&
|
||||
descriptor.BaseArray5() == 0;
|
||||
@@ -269,9 +269,8 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c
|
||||
(supported_2d || supported_array || supported_cube || supported_msaa_2d ||
|
||||
supported_msaa_array) &&
|
||||
levels_ok && descriptor.MinLod() == 0 &&
|
||||
descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
|
||||
descriptor.BCSwizzle() == 0 && (!descriptor.MsaaDepth() || multisampled) &&
|
||||
pitch >= width && pitch == image.info.pitch;
|
||||
descriptor.TileMode() == Prospero::TileMode::kDepth && descriptor.BCSwizzle() == 0 &&
|
||||
(!descriptor.MsaaDepth() || multisampled) && pitch >= width && pitch == image.info.pitch;
|
||||
}
|
||||
|
||||
bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, const Image& image) {
|
||||
@@ -283,13 +282,13 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
|
||||
(static_cast<uint32_t>(descriptor.TileMode()) << 20u) |
|
||||
(static_cast<uint32_t>(descriptor.Type()) << 28u);
|
||||
const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u);
|
||||
const uint32_t field5_expected =
|
||||
0x00700000u | (static_cast<uint32_t>(descriptor.MaxMip()) << 4u);
|
||||
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
|
||||
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
|
||||
descriptor.fields[3] == field3_expected &&
|
||||
descriptor.fields[4] == field4_expected &&
|
||||
descriptor.fields[5] == field5_expected;
|
||||
const uint32_t field5_expected = (static_cast<uint32_t>(descriptor.PerfMod5()) << 20u) |
|
||||
(static_cast<uint32_t>(descriptor.MaxMip()) << 4u);
|
||||
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
|
||||
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
|
||||
descriptor.fields[3] == field3_expected &&
|
||||
descriptor.fields[4] == field4_expected &&
|
||||
descriptor.fields[5] == field5_expected;
|
||||
if (!common || (descriptor.fields[6] == 0 && descriptor.fields[7] != 0)) {
|
||||
return false;
|
||||
}
|
||||
@@ -300,8 +299,8 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
|
||||
const uint32_t expected_control = htile_control | (descriptor.MsaaDepth() ? (1u << 10u) : 0u);
|
||||
const auto metadata_addr = descriptor.MetaAddr() << 8u;
|
||||
return (descriptor.fields[6] & 0x00ffffffu) == expected_control && metadata_addr != 0 &&
|
||||
descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
|
||||
image.info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
|
||||
descriptor.TileMode() == Prospero::TileMode::kDepth &&
|
||||
image.info.tile_mode == Prospero::TileMode::kDepth &&
|
||||
image.info.metadata.kind == ImageMetadataKind::Htile &&
|
||||
image.info.metadata.range.Valid() && image.info.metadata.range.address == metadata_addr;
|
||||
}
|
||||
@@ -321,7 +320,7 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
||||
return;
|
||||
}
|
||||
const auto descriptor_pitch =
|
||||
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u, 1,
|
||||
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
descriptor.TileMode());
|
||||
EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d "
|
||||
@@ -332,23 +331,21 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
||||
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind),
|
||||
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
|
||||
resource.read, resource.written, resource.atomic, resource.depth_compare,
|
||||
descriptor.Format(), descriptor.DstSelXYZW(),
|
||||
static_cast<uint32_t>(descriptor.Format()), descriptor.DstSelXYZW(),
|
||||
image == nullptr ? static_cast<int>(vk::Format::eUndefined)
|
||||
: static_cast<int>(image->info.pixel_format),
|
||||
static_cast<int>(view_format), image == nullptr ? 0u : image->info.resources.layers,
|
||||
descriptor.Type(), descriptor.BaseArray5(), descriptor.Depth(), descriptor_pitch,
|
||||
image == nullptr ? 0u : image->info.pitch, descriptor.Base40(), size, descriptor.fields[0],
|
||||
descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[4],
|
||||
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
static_cast<uint32_t>(descriptor.Type()), descriptor.BaseArray5(), descriptor.Depth(),
|
||||
descriptor_pitch, image == nullptr ? 0u : image->info.pitch, descriptor.Base40(), size,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderTextureResource& descriptor) {
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool is_color_1d =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1D);
|
||||
const bool is_color_1d_array =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor1DArray);
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool is_color_1d = descriptor.Type() == Prospero::ImageType::kColor1D;
|
||||
const bool is_color_1d_array = descriptor.Type() == Prospero::ImageType::kColor1DArray;
|
||||
const bool valid_1d_slice =
|
||||
(is_color_1d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
|
||||
(is_color_1d_array && descriptor.BaseArray5() <= descriptor.Depth());
|
||||
@@ -358,36 +355,51 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray &&
|
||||
is_color_1d_array && descriptor.Height5() == 0 &&
|
||||
descriptor.BaseArray5() <= descriptor.Depth();
|
||||
const bool is_color_2d =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
|
||||
const bool is_color_2d_array =
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray);
|
||||
const bool is_color_2d = descriptor.Type() == Prospero::ImageType::kColor2D;
|
||||
const bool is_color_2d_array = descriptor.Type() == Prospero::ImageType::kColor2DArray;
|
||||
const bool valid_2d_slice =
|
||||
(is_color_2d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
|
||||
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
|
||||
const bool is_2d =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && valid_2d_slice;
|
||||
const bool is_cube = resource.cube && descriptor.Type() == Prospero::ImageType::kCube &&
|
||||
descriptor.Width5() == descriptor.Height5() &&
|
||||
descriptor.BaseArray5() <= descriptor.Depth() &&
|
||||
(descriptor.Depth() - descriptor.BaseArray5() + 1u) % 6u == 0;
|
||||
const bool is_2d_array =
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
|
||||
is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth();
|
||||
((!resource.cube && is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth()) ||
|
||||
is_cube);
|
||||
const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D &&
|
||||
descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor3D) &&
|
||||
descriptor.Type() == Prospero::ImageType::kColor3D &&
|
||||
descriptor.BaseArray5() == 0;
|
||||
TileBlockLayout depth_block {};
|
||||
const auto depth_bpe = Prospero::RenderTargetBytesPerElement(descriptor.Format());
|
||||
const bool supported_depth_tile =
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && !resource.read &&
|
||||
!Prospero::IsFmaskTextureFormat(descriptor.Format()) && (is_2d || is_2d_array) &&
|
||||
TileGetBlockLayout(TileBlockFamily::Depth64KB, depth_bpe, depth_block);
|
||||
const bool supported_standard_tile =
|
||||
(tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB) &&
|
||||
TileIsStandard4KBTextureSupported(descriptor.Format())) ||
|
||||
(tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) &&
|
||||
TileIsStandard64KBTextureSupported(descriptor.Format()));
|
||||
const bool supported_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kLinear) ||
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
||||
supported_depth_tile || supported_standard_tile;
|
||||
const auto swizzle = descriptor.DstSelXYZW();
|
||||
TileTextureBlockLayout tile_layout {};
|
||||
bool supported_tile = false;
|
||||
switch (tile) {
|
||||
case Prospero::TileMode::kLinear: supported_tile = true; break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
supported_tile =
|
||||
!resource.read && !Prospero::IsFmaskTextureFormat(descriptor.Format()) &&
|
||||
(is_2d || is_2d_array) &&
|
||||
TileGetTextureBlockLayout(descriptor.Format(), tile, false, tile_layout);
|
||||
break;
|
||||
case Prospero::TileMode::kStandard256B:
|
||||
supported_tile =
|
||||
(is_2d || is_2d_array) &&
|
||||
TileGetTextureBlockLayout(descriptor.Format(), tile, false, tile_layout);
|
||||
break;
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
supported_tile =
|
||||
TileGetTextureBlockLayout(descriptor.Format(), tile, is_3d, tile_layout);
|
||||
break;
|
||||
case Prospero::TileMode::kRenderTarget:
|
||||
supported_tile =
|
||||
TileGetTextureBlockLayout(descriptor.Format(), tile, false, tile_layout);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
const auto swizzle = descriptor.DstSelXYZW();
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(swizzle) &&
|
||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||
@@ -423,14 +435,12 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const bool encoding_ok = IsSupportedStorageTextureEncoding(descriptor);
|
||||
const bool uint_resource =
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
const bool raw_sint_storage =
|
||||
format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) && uint_resource &&
|
||||
resource.written && !resource.read && !resource.atomic;
|
||||
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
|
||||
resource.written && !resource.read && !resource.atomic;
|
||||
const bool format_ok =
|
||||
raw_sint_storage ||
|
||||
(Prospero::IsSupportedTextureFormat(format) &&
|
||||
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
||||
(!resource.atomic || format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt)));
|
||||
raw_sint_storage || (Prospero::IsSupportedTextureFormat(format) &&
|
||||
uint_resource == Prospero::IsUintTextureFormat(format) &&
|
||||
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
|
||||
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
|
||||
return;
|
||||
}
|
||||
@@ -449,24 +459,23 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
IsValidImageSwizzle(descriptor.DstSelXYZW()), descriptor.Base40(), size,
|
||||
static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Height5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Depth()) + 1u, descriptor.Type(), format,
|
||||
descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
static_cast<uint32_t>(descriptor.Depth()) + 1u, static_cast<uint32_t>(descriptor.Type()),
|
||||
static_cast<uint32_t>(format), static_cast<uint32_t>(descriptor.TileMode()),
|
||||
descriptor.DstSelXYZW(), resource.read, resource.written, descriptor.fields[0],
|
||||
descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[4],
|
||||
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
struct NullImageSpec {
|
||||
vk::Format format;
|
||||
uint32_t guest_format;
|
||||
vk::Format format;
|
||||
Prospero::BufferFormat guest_format;
|
||||
};
|
||||
|
||||
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
return uint_image ? NullImageSpec {vk::Format::eR32Uint,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt)}
|
||||
: NullImageSpec {vk::Format::eR32Sfloat,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float)};
|
||||
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
|
||||
: NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
|
||||
}
|
||||
|
||||
static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
@@ -492,19 +501,21 @@ static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::Image
|
||||
}
|
||||
|
||||
static void PopulateTextureMipLayout(ImageInfo& info) {
|
||||
if (info.IsVolume() && info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear)) {
|
||||
TileVolumeLayout volume {};
|
||||
if (!TileGetTextureVolumeLayout(info.guest_format, info.extent.width, info.extent.height,
|
||||
info.extent.depth, info.resources.levels, info.tile_mode,
|
||||
volume)) {
|
||||
if (info.IsVolume() && info.tile_mode != Prospero::TileMode::kLinear) {
|
||||
TileSurfaceLayout surface {};
|
||||
const TileSurfaceDescription description {
|
||||
info.guest_format, info.tile_mode, TileSurfaceDimension::Dim3D, info.extent.width,
|
||||
info.extent.height, info.extent.depth, info.resources.levels, 1};
|
||||
if (!TileGetTiledTextureLayout(description, surface)) {
|
||||
EXIT("unsupported normalized volume texture layout\n");
|
||||
}
|
||||
for (uint32_t level = 0; level < info.resources.levels; level++) {
|
||||
const auto& mip = surface.mips[level];
|
||||
info.mip_layout[level] = {
|
||||
volume.level_offsets[level],
|
||||
volume.level_sizes[level],
|
||||
volume.level_widths[level],
|
||||
volume.level_heights[level],
|
||||
mip.offset,
|
||||
mip.size,
|
||||
mip.padded_width,
|
||||
mip.padded_height,
|
||||
};
|
||||
}
|
||||
return;
|
||||
@@ -512,7 +523,7 @@ static void PopulateTextureMipLayout(ImageInfo& info) {
|
||||
|
||||
TileSizeOffset levels[16] {};
|
||||
TilePaddedSize padded[16] {};
|
||||
TileGetTextureSize(info.guest_format, info.extent.width, info.extent.height, info.pitch,
|
||||
TileGetTextureSize(info.guest_format, info.extent.width, info.extent.height,
|
||||
info.resources.levels, info.tile_mode, nullptr, levels, padded);
|
||||
for (uint32_t level = 0; level < info.resources.levels; level++) {
|
||||
const auto offset =
|
||||
@@ -617,10 +628,9 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
const bool multisampled = IsMultisampledTexture(type);
|
||||
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
|
||||
const auto tile = descriptor.TileMode();
|
||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
const bool msaa_tile =
|
||||
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||
const bool depth_tile = tile == Prospero::TileMode::kDepth;
|
||||
const bool msaa_tile = depth_tile || tile == Prospero::TileMode::kRenderTarget;
|
||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
|
||||
(multisampled &&
|
||||
(base_level != 0 || last_level == 0 || last_level > 3 ||
|
||||
@@ -630,7 +640,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
|
||||
base_level, last_level, levels, descriptor.MaxMip(), descriptor.Type(), tile,
|
||||
base_level, last_level, levels, descriptor.MaxMip(),
|
||||
static_cast<uint32_t>(descriptor.Type()), static_cast<uint32_t>(tile),
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
@@ -650,7 +661,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) &&
|
||||
!sampled_numeric_class) {
|
||||
EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n",
|
||||
static_cast<uint32_t>(resource.kind), format, address);
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(format), address);
|
||||
}
|
||||
|
||||
const bool volume = type == Prospero::ImageType::kColor3D;
|
||||
@@ -670,9 +681,9 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
}
|
||||
size.size *= image_layers;
|
||||
} else {
|
||||
pitch = TileGetTexturePitch(format, width, levels, tile);
|
||||
TileGetTextureTotalSize(format, width, height, volume ? depth : image_layers, pitch, levels,
|
||||
tile, volume, size);
|
||||
pitch = TileGetTexturePitch(format, width, tile);
|
||||
TileGetTextureTotalSize(format, width, height, volume ? depth : image_layers, levels, tile,
|
||||
volume, size);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(size.size == 0 || size.align == 0 ||
|
||||
(address & (static_cast<uint64_t>(size.align) - 1u)) != 0);
|
||||
@@ -680,15 +691,14 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
||||
ValidateStorageTexture(resource, descriptor, size.size);
|
||||
}
|
||||
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
|
||||
? vk::Format::eR32Uint
|
||||
: SrgbStorageViewFormat(pixel_format);
|
||||
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
|
||||
? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
const auto pixel_format = TextureGetFormat(format);
|
||||
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
|
||||
? vk::Format::eR32Uint
|
||||
: SrgbStorageViewFormat(pixel_format);
|
||||
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
|
||||
? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.data = {address, size.size};
|
||||
desc.info.pixel_format = pixel_format;
|
||||
@@ -919,22 +929,21 @@ RenderExecutor::PrepareGraphicsBindings(CommandBuffer& buffer, const ShaderStage
|
||||
return bindings;
|
||||
}
|
||||
|
||||
static void RetainBindings(CommandBuffer& buffer,
|
||||
const DescriptorCache::NativeDescriptors& resources) {
|
||||
static void RetainBindings(CommandBuffer& buffer, DescriptorCache::NativeDescriptors& resources) {
|
||||
if (resources.flattened_srt.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(resources.flattened_srt.owner);
|
||||
buffer.RetainResourceUntilFence(std::move(resources.flattened_srt.owner));
|
||||
}
|
||||
if (resources.user_data.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(resources.user_data.owner);
|
||||
buffer.RetainResourceUntilFence(std::move(resources.user_data.owner));
|
||||
}
|
||||
for (const auto& view: resources.buffers) {
|
||||
for (auto& view: resources.buffers) {
|
||||
if (view.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(view.owner);
|
||||
buffer.RetainResourceUntilFence(std::move(view.owner));
|
||||
}
|
||||
}
|
||||
for (const auto& view: resources.addresses) {
|
||||
for (auto& view: resources.addresses) {
|
||||
if (view.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(view.owner);
|
||||
buffer.RetainResourceUntilFence(std::move(view.owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,22 @@ VulkanMemoryBarrier MakeShaderWriteDependency() {
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eMemoryBarrier;
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eMemoryBarrier;
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
return barrier;
|
||||
}
|
||||
|
||||
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer) {
|
||||
EXIT_IF(buffer == nullptr);
|
||||
|
||||
@@ -84,6 +100,21 @@ bool HasShaderBufferWrites(const ShaderStageRuntime& runtime) {
|
||||
return !CollectShaderBufferWrites(*runtime.program, *runtime.resources).empty();
|
||||
}
|
||||
|
||||
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !source_stages);
|
||||
const auto barrier = MakeShaderAccessDependency();
|
||||
vk_buffer.pipelineBarrier(source_stages, vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void ShaderWriteHazardBarrier(vk::CommandBuffer vk_buffer,
|
||||
vk::PipelineStageFlags destination_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !destination_stages);
|
||||
const auto barrier = MakeShaderWriteHazardDependency();
|
||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, destination_stages,
|
||||
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void ShaderWriteBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !source_stages);
|
||||
const auto barrier = MakeShaderWriteDependency();
|
||||
|
||||
@@ -18,12 +18,17 @@ struct ShaderBufferWriteRange {
|
||||
};
|
||||
|
||||
vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages);
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteDependency();
|
||||
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer);
|
||||
std::vector<ShaderBufferWriteRange>
|
||||
CollectShaderBufferWrites(const ShaderRecompiler::IR::Program& program,
|
||||
const ShaderRecompiler::IR::ResourceSnapshot& resources);
|
||||
bool HasShaderBufferWrites(const ShaderStageRuntime& runtime);
|
||||
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
|
||||
void ShaderWriteHazardBarrier(vk::CommandBuffer vk_buffer,
|
||||
vk::PipelineStageFlags destination_stages);
|
||||
void ShaderWriteBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
// IDK: maybe we can remove it?
|
||||
constexpr uint8_t kTemporaryVertexAttribFormat113 =
|
||||
static_cast<uint8_t>(Prospero::VertexAttribFormat::k16_16SInt);
|
||||
constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u;
|
||||
|
||||
static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_components) {
|
||||
@@ -88,8 +90,9 @@ static bool NarrowInputFormat(vk::Format& format, uint32_t& size, uint32_t used_
|
||||
|
||||
static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format, uint32_t& size,
|
||||
uint32_t used_components) {
|
||||
auto fmt = res.Format();
|
||||
if (fmt == Prospero::GpuEnumValue(Prospero::VertexAttribFormat::k16_16SInt)) {
|
||||
const auto fmt = res.Format();
|
||||
const auto raw_format = res.RawFormat();
|
||||
if (raw_format == kTemporaryVertexAttribFormat113) {
|
||||
static bool logged_113 = false;
|
||||
if (!logged_113) {
|
||||
LOGF("InputFormat: temporary: accepting invalid PS5 buffer format 113 as "
|
||||
@@ -99,12 +102,12 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
||||
format = vk::Format::eR32G32B32A32Sfloat;
|
||||
size = 4;
|
||||
if (NarrowInputFormat(format, size, used_components)) {
|
||||
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", fmt,
|
||||
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
|
||||
VulkanToString(format).c_str(), used_components);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (fmt == kTemporaryPs5BufferFormat121) {
|
||||
if (raw_format == kTemporaryPs5BufferFormat121) {
|
||||
static bool logged_121 = false;
|
||||
if (!logged_121) {
|
||||
LOGF("InputFormat: accepting PS5 buffer format 121 as vk::Format::eR16G16Sfloat\n");
|
||||
@@ -113,13 +116,13 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
||||
format = vk::Format::eR16G16Sfloat;
|
||||
size = 2;
|
||||
if (NarrowInputFormat(format, size, used_components)) {
|
||||
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", fmt,
|
||||
LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format,
|
||||
VulkanToString(format).c_str(), used_components);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (static_cast<Prospero::BufferFormat>(fmt)) {
|
||||
switch (fmt) {
|
||||
case Prospero::BufferFormat::k32_32_32_32Float:
|
||||
format = vk::Format::eR32G32B32A32Sfloat;
|
||||
size = 4;
|
||||
@@ -329,7 +332,7 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
||||
size = 1;
|
||||
break;
|
||||
default:
|
||||
EXIT("unknown format: fmt = %u\n", fmt);
|
||||
EXIT("unknown format: fmt = %u\n", raw_format);
|
||||
format = vk::Format::eUndefined;
|
||||
size = 4;
|
||||
break;
|
||||
@@ -947,15 +950,23 @@ void CreatePipelineInternal(
|
||||
vk::DynamicState::eViewport,
|
||||
vk::DynamicState::eScissor,
|
||||
vk::DynamicState::eLineWidth,
|
||||
vk::DynamicState::eDepthBiasEnable,
|
||||
vk::DynamicState::eDepthBias,
|
||||
vk::DynamicState::eStencilCompareMask,
|
||||
vk::DynamicState::eStencilReference,
|
||||
vk::DynamicState::eStencilWriteMask,
|
||||
#if !defined(__APPLE__)
|
||||
// Keep last so depth-only pipelines can omit this dynamic state.
|
||||
vk::DynamicState::eColorWriteEnableEXT, // unsupported by MoltenVK; static mask instead
|
||||
#endif
|
||||
};
|
||||
const auto dynamic_states_count =
|
||||
auto dynamic_states_count =
|
||||
static_cast<uint32_t>(sizeof(dynamic_states) / sizeof(dynamic_states[0]));
|
||||
#if !defined(__APPLE__)
|
||||
if (static_params.color_count == 0) {
|
||||
dynamic_states_count--;
|
||||
}
|
||||
#endif
|
||||
|
||||
vk::PipelineDynamicStateCreateInfo dynamic_state {};
|
||||
dynamic_state.sType = vk::StructureType::ePipelineDynamicStateCreateInfo;
|
||||
|
||||
@@ -93,7 +93,7 @@ bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t grou
|
||||
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(raw);
|
||||
if (!resource.formatted || !resource.written || resource.read || resource.atomic ||
|
||||
resource.scalar || resource.max_byte_extent != 16 || descriptor.Stride() != 16 ||
|
||||
descriptor.Format() != Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32UInt) ||
|
||||
descriptor.Format() != Prospero::BufferFormat::k32_32_32_32UInt ||
|
||||
descriptor.SwizzleEnabled() || descriptor.IndexStride() != 0 || descriptor.AddTid() ||
|
||||
resource.packed_stride != descriptor.PackedStride() || raw.dword_count != 4 ||
|
||||
program.user_data_base != 0 || resources.user_data.size() != 8) {
|
||||
@@ -246,7 +246,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
LOGF(" CS buffer[%u]: source=%u usage=%s addr=0x%012" PRIx64
|
||||
" stride=%u records=%u format=%u\n",
|
||||
i, buffer.source, buffer.written ? "read-write" : "read-only", r.Base48(),
|
||||
r.Stride(), r.NumRecords(), r.Format());
|
||||
r.Stride(), r.NumRecords(), r.RawFormat());
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto& image = program.info.images[i];
|
||||
@@ -258,13 +258,13 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::ImageUint)
|
||||
? "true"
|
||||
: "false",
|
||||
r.Base40(), static_cast<uint32_t>(r.Type()), r.Format(),
|
||||
r.Base40(), static_cast<uint32_t>(r.Type()), static_cast<uint32_t>(r.Format()),
|
||||
static_cast<uint32_t>(r.Width5()) + 1u, static_cast<uint32_t>(r.Height5()) + 1u,
|
||||
static_cast<uint32_t>(r.Depth()) + 1u,
|
||||
std::max<uint32_t>(static_cast<uint32_t>(r.LastLevel()),
|
||||
static_cast<uint32_t>(r.MaxMip())) +
|
||||
1u,
|
||||
r.TileMode());
|
||||
static_cast<uint32_t>(r.TileMode()));
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.samplers.size(); i++) {
|
||||
const auto r = DecodeNativeDescriptor<ShaderSamplerResource>(resources.samplers[i]);
|
||||
@@ -325,9 +325,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
|
||||
auto vk_buffer = buffer.Handle();
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline.pipeline_layout, bindings);
|
||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
|
||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||
has_storage_writes =
|
||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
||||
@@ -338,8 +335,15 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
}) ||
|
||||
has_storage_writes;
|
||||
if (has_storage_writes) {
|
||||
ShaderWriteBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
// A host fence used to serialize every dispatch. Preserve its read-before-write ordering
|
||||
// while allowing the queue to execute asynchronously.
|
||||
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
}
|
||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
|
||||
// The removed host fence also ordered read-only dispatches before later writers.
|
||||
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
ResetBindings();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,32 @@ int32_t ResolveVertexOffset(uint32_t index_offset, const ShaderVertexInputInfo&
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::atomic<uint32_t> g_draw_state_log_count = 0;
|
||||
static std::atomic<uint32_t> g_draw_input_log_count = 0;
|
||||
static std::atomic<uint32_t> g_mrt_state_log_count = 0;
|
||||
static std::atomic<uint32_t> g_shader_stage_log_count = 0;
|
||||
static std::atomic<uint32_t> g_draw_state_log_count = 0;
|
||||
static std::atomic<uint32_t> g_draw_input_log_count = 0;
|
||||
static std::atomic<uint32_t> g_mrt_state_log_count = 0;
|
||||
static std::atomic<uint32_t> g_shader_stage_log_count = 0;
|
||||
|
||||
static std::atomic<uint32_t> g_framebuffer_skip_log_count = 0;
|
||||
|
||||
static float ConvertPolygonOffsetConstantFactor(float guest_factor, const HW::PolyOffset& offset,
|
||||
vk::Format host_depth_format) {
|
||||
if (offset.db_is_float_fmt) {
|
||||
return guest_factor;
|
||||
}
|
||||
|
||||
int host_depth_bits = 0;
|
||||
switch (host_depth_format) {
|
||||
case vk::Format::eD16Unorm:
|
||||
case vk::Format::eD16UnormS8Uint: host_depth_bits = 16; break;
|
||||
case vk::Format::eD24UnormS8Uint: host_depth_bits = 24; break;
|
||||
default:
|
||||
// A fixed-point guest bias cannot be represented exactly by a floating-point host
|
||||
// attachment without VK_EXT_depth_bias_control.
|
||||
return guest_factor;
|
||||
}
|
||||
return std::ldexp(guest_factor, host_depth_bits + offset.neg_num_db_bits);
|
||||
}
|
||||
|
||||
static const char* RenderColorTypeName(RenderColorType type) {
|
||||
switch (type) {
|
||||
case RenderColorType::NoColorOutput: return "NoColorOutput";
|
||||
@@ -103,7 +123,7 @@ static void LogFramebufferSkip(const char* draw_name, const RenderColorInfo& col
|
||||
log_id, draw_name, RenderColorTypeName(color.type), color.base_addr, color.buffer_size,
|
||||
color.image_id ? "yes" : "no", VulkanToString(depth.format).c_str(),
|
||||
depth.image_id ? "yes" : "no", depth.vaddr_num, ctx.GetRenderTargetMask(),
|
||||
ucfg.GetPrimType(), index_count, flags);
|
||||
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags);
|
||||
}
|
||||
|
||||
static void LogMrtState(const char* draw_name, const RenderCommandBuffer& buffer,
|
||||
@@ -159,10 +179,12 @@ static void LogMrtState(const char* draw_name, const RenderCommandBuffer& buffer
|
||||
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32
|
||||
" width=%u height=%u tile=%u"
|
||||
" blend=%s src=%u dst=%u alpha_src=%u alpha_dst=%u\n",
|
||||
log_id, i, rt.base.addr, ctm, csm, ps_input_info.target_output_mode[i], rt.info.format,
|
||||
rt.info.channel_type, rt.info.channel_order, rt.attrib2.width + 1,
|
||||
rt.attrib2.height + 1, rt.attrib3.tile_mode, bc.enable ? "true" : "false",
|
||||
bc.color_srcblend, bc.color_destblend, bc.alpha_srcblend, bc.alpha_destblend);
|
||||
log_id, i, rt.base.addr, ctm, csm, ps_input_info.target_output_mode[i],
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order), rt.attrib2.width + 1,
|
||||
rt.attrib2.height + 1, static_cast<uint32_t>(rt.attrib3.tile_mode),
|
||||
bc.enable ? "true" : "false", bc.color_srcblend, bc.color_destblend, bc.alpha_srcblend,
|
||||
bc.alpha_destblend);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +227,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
||||
"scissor=(%d,%d)-(%d,%d)\n",
|
||||
log_id, buffer.GetContext().GetGpu().GetFrameNum(), draw_name,
|
||||
RenderColorTypeName(color.type), color.base_addr, extent.width, extent.height,
|
||||
ucfg.GetPrimType(), index_count, flags, ctx.GetRenderTargetMask(),
|
||||
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags, ctx.GetRenderTargetMask(),
|
||||
color.color_clear_enable ? "true" : "false", color.color_clear_value.float32[0],
|
||||
color.color_clear_value.float32[1], color.color_clear_value.float32[2],
|
||||
color.color_clear_value.float32[3], cc.mode, cc.op, bc.enable ? "true" : "false",
|
||||
@@ -269,8 +291,7 @@ static void LogDrawInputState(const RenderCommandBuffer& buffer, const RenderCol
|
||||
const auto& rd = vs_input_info.resources_dst[res_index];
|
||||
const auto offset = b.attr_offsets[ai];
|
||||
if (offset + 4u <= b.stride &&
|
||||
r.Format() ==
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm)) {
|
||||
r.Format() == Prospero::BufferFormat::k8_8_8_8UNorm) {
|
||||
uint32_t packed = 0;
|
||||
std::memcpy(&packed, rec_bytes + offset, sizeof(packed));
|
||||
const auto r8 = (packed >> 0u) & 0xffu;
|
||||
@@ -335,6 +356,22 @@ static PipelineDynamicParameters BuildGraphicsDynamicParams(const RenderCommandB
|
||||
ret.stencil_front = depth.stencil_dynamic_front;
|
||||
ret.stencil_back = depth.stencil_dynamic_back;
|
||||
|
||||
const auto& mode = ctx.GetModeControl();
|
||||
const auto& poly_offset = ctx.GetPolyOffset();
|
||||
const bool use_front = mode.poly_offset_front_enable && !mode.cull_front;
|
||||
const bool use_back = mode.poly_offset_back_enable && !mode.cull_back;
|
||||
ret.depth_bias_enable = use_front || use_back;
|
||||
if (ret.depth_bias_enable) {
|
||||
// Vulkan has one bias for both faces. Prefer a visible front face when both are enabled.
|
||||
const float guest_constant_factor =
|
||||
use_front ? poly_offset.front_offset : poly_offset.back_offset;
|
||||
ret.depth_bias_constant_factor =
|
||||
ConvertPolygonOffsetConstantFactor(guest_constant_factor, poly_offset, depth.format);
|
||||
ret.depth_bias_clamp = poly_offset.clamp;
|
||||
ret.depth_bias_slope_factor =
|
||||
(use_front ? poly_offset.front_scale : poly_offset.back_scale) / 16.0f;
|
||||
}
|
||||
|
||||
// CB_COLOR_CONTROL.operation controls special CB operations, not the normal color component
|
||||
// write mask. Use CB_TARGET_MASK here so scanout passes with mode=Disable do not go black.
|
||||
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
||||
@@ -379,6 +416,13 @@ static void SetDynamicParams(const RenderCommandBuffer& buffer, vk::CommandBuffe
|
||||
}
|
||||
vk_buffer.setLineWidth(line_width);
|
||||
|
||||
vk_buffer.setDepthBiasEnable(dynamic_params.depth_bias_enable ? VK_TRUE : VK_FALSE);
|
||||
if (dynamic_params.depth_bias_enable) {
|
||||
vk_buffer.setDepthBias(dynamic_params.depth_bias_constant_factor,
|
||||
dynamic_params.depth_bias_clamp,
|
||||
dynamic_params.depth_bias_slope_factor);
|
||||
}
|
||||
|
||||
if (dynamic_params.stencil_test_enable) {
|
||||
vk_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFront,
|
||||
dynamic_params.stencil_front.compareMask);
|
||||
@@ -664,12 +708,10 @@ struct DrawIndexBufferSource {
|
||||
|
||||
struct PreparedIndexBuffer {
|
||||
std::shared_ptr<void> owner;
|
||||
vk::Buffer buffer = nullptr;
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
vk::DeviceSize offset = 0;
|
||||
vk::IndexType type = vk::IndexType::eUint16;
|
||||
bool streamed = false;
|
||||
vk::Buffer buffer = nullptr;
|
||||
uint64_t size = 0;
|
||||
vk::DeviceSize offset = 0;
|
||||
vk::IndexType type = vk::IndexType::eUint16;
|
||||
};
|
||||
|
||||
static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer) {
|
||||
@@ -686,11 +728,24 @@ struct VertexBufferRange {
|
||||
[[nodiscard]] uint64_t RequestedSize() const { return requested_end - base_address; }
|
||||
};
|
||||
|
||||
static std::vector<BufferBinding> AcquireVertexBuffers(RenderCommandBuffer& buffer,
|
||||
const ShaderVertexInputInfo& vs_input_info) {
|
||||
struct PreparedVertexBuffers {
|
||||
static constexpr uint32_t MaxBuffers = ShaderVertexInputInfo::RES_MAX;
|
||||
|
||||
std::array<vk::Buffer, MaxBuffers> buffers {};
|
||||
std::array<vk::DeviceSize, MaxBuffers> offsets {};
|
||||
std::array<std::shared_ptr<void>, MaxBuffers> owners {};
|
||||
uint32_t count = 0;
|
||||
uint32_t owner_count = 0;
|
||||
};
|
||||
|
||||
static PreparedVertexBuffers AcquireVertexBuffers(RenderCommandBuffer& buffer,
|
||||
const ShaderVertexInputInfo& vs_input_info) {
|
||||
EXIT_IF(vs_input_info.buffers_num < 0 ||
|
||||
vs_input_info.buffers_num > ShaderVertexInputInfo::RES_MAX);
|
||||
|
||||
// Collect the non-empty guest vertex ranges.
|
||||
std::vector<VertexBufferRange> ranges;
|
||||
ranges.reserve(vs_input_info.buffers_num);
|
||||
std::array<VertexBufferRange, ShaderVertexInputInfo::RES_MAX> ranges {};
|
||||
uint32_t range_count = 0;
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(vertex);
|
||||
@@ -701,27 +756,31 @@ static std::vector<BufferBinding> AcquireVertexBuffers(RenderCommandBuffer&
|
||||
EXIT("invalid vertex buffer range: addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vertex.addr, size);
|
||||
}
|
||||
ranges.push_back({vertex.addr, vertex.addr + size});
|
||||
ranges[range_count++] = {vertex.addr, vertex.addr + size};
|
||||
}
|
||||
|
||||
std::ranges::sort(ranges, [](const VertexBufferRange& left, const VertexBufferRange& right) {
|
||||
return left.base_address < right.base_address;
|
||||
});
|
||||
std::sort(ranges.begin(), ranges.begin() + range_count,
|
||||
[](const VertexBufferRange& left, const VertexBufferRange& right) {
|
||||
return left.base_address < right.base_address;
|
||||
});
|
||||
|
||||
// Merge overlapping or touching ranges before acquiring host buffers.
|
||||
std::vector<VertexBufferRange> merged_ranges;
|
||||
merged_ranges.reserve(ranges.size());
|
||||
for (const auto& range: ranges) {
|
||||
if (!merged_ranges.empty() && merged_ranges.back().requested_end >= range.base_address) {
|
||||
merged_ranges.back().requested_end =
|
||||
std::max(merged_ranges.back().requested_end, range.requested_end);
|
||||
std::array<VertexBufferRange, ShaderVertexInputInfo::RES_MAX> merged_ranges {};
|
||||
uint32_t merged_count = 0;
|
||||
for (uint32_t i = 0; i < range_count; i++) {
|
||||
const auto& range = ranges[i];
|
||||
if (merged_count != 0 &&
|
||||
merged_ranges[merged_count - 1].requested_end >= range.base_address) {
|
||||
merged_ranges[merged_count - 1].requested_end =
|
||||
std::max(merged_ranges[merged_count - 1].requested_end, range.requested_end);
|
||||
continue;
|
||||
}
|
||||
merged_ranges.push_back(range);
|
||||
merged_ranges[merged_count++] = {range.base_address, range.requested_end};
|
||||
}
|
||||
|
||||
auto& cache = buffer.GetContext().GetBufferCache();
|
||||
for (auto& range: merged_ranges) {
|
||||
for (uint32_t i = 0; i < merged_count; i++) {
|
||||
auto& range = merged_ranges[i];
|
||||
// PPSA20298
|
||||
const auto size =
|
||||
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
|
||||
@@ -730,30 +789,47 @@ static std::vector<BufferBinding> AcquireVertexBuffers(RenderCommandBuffer&
|
||||
}
|
||||
|
||||
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
|
||||
std::vector<BufferBinding> bindings;
|
||||
bindings.reserve(vs_input_info.buffers_num);
|
||||
PreparedVertexBuffers prepared;
|
||||
prepared.count = static_cast<uint32_t>(vs_input_info.buffers_num);
|
||||
std::shared_ptr<Buffer> null_owner;
|
||||
vk::Buffer null_buffer = nullptr;
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
const auto size = VertexBufferDescriptorSize(vertex);
|
||||
if (size == 0) {
|
||||
auto owner = cache.ObtainNullBuffer();
|
||||
bindings.push_back({owner, owner->Handle(), 0});
|
||||
if (null_owner == nullptr) {
|
||||
null_owner = cache.ObtainNullBuffer();
|
||||
null_buffer = null_owner->Handle();
|
||||
}
|
||||
prepared.buffers[i] = null_buffer;
|
||||
prepared.offsets[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto range = std::ranges::find_if(merged_ranges, [&](const VertexBufferRange& value) {
|
||||
return vertex.addr >= value.base_address && vertex.addr < value.acquired_end;
|
||||
});
|
||||
if (range == merged_ranges.end()) {
|
||||
const auto range = std::find_if(merged_ranges.begin(), merged_ranges.begin() + merged_count,
|
||||
[&](const VertexBufferRange& value) {
|
||||
return vertex.addr >= value.base_address &&
|
||||
vertex.addr < value.acquired_end;
|
||||
});
|
||||
if (range == merged_ranges.begin() + merged_count) {
|
||||
EXIT("vertex buffer address is outside the acquired range: addr=0x%016" PRIx64 "\n",
|
||||
vertex.addr);
|
||||
}
|
||||
|
||||
auto binding = range->binding;
|
||||
binding.offset += vertex.addr - range->base_address;
|
||||
bindings.push_back(std::move(binding));
|
||||
prepared.buffers[i] = range->binding.buffer;
|
||||
prepared.offsets[i] = range->binding.offset + vertex.addr - range->base_address;
|
||||
}
|
||||
return bindings;
|
||||
|
||||
if (null_owner != nullptr) {
|
||||
prepared.owners[prepared.owner_count++] = std::move(null_owner);
|
||||
}
|
||||
for (uint32_t i = 0; i < merged_count; i++) {
|
||||
if (merged_ranges[i].binding.owner != nullptr) {
|
||||
EXIT_IF(prepared.owner_count >= PreparedVertexBuffers::MaxBuffers);
|
||||
prepared.owners[prepared.owner_count++] = std::move(merged_ranges[i].binding.owner);
|
||||
}
|
||||
}
|
||||
return prepared;
|
||||
}
|
||||
|
||||
static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
|
||||
@@ -769,7 +845,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
|
||||
topology = vk::PrimitiveTopology::ePointList;
|
||||
|
||||
switch (static_cast<Prospero::PrimitiveType>(ucfg.GetPrimType())) {
|
||||
switch (ucfg.GetPrimType()) {
|
||||
case Prospero::PrimitiveType::kNone: return false;
|
||||
case Prospero::PrimitiveType::kPointList:
|
||||
topology = vk::PrimitiveTopology::ePointList;
|
||||
@@ -792,14 +868,14 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
break;
|
||||
case Prospero::PrimitiveType::kRectListLegacy:
|
||||
if (!auto_draw) {
|
||||
EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
|
||||
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
topology = vk::PrimitiveTopology::eTriangleStrip;
|
||||
break;
|
||||
case Prospero::PrimitiveType::kQuadListLegacy:
|
||||
topology = vk::PrimitiveTopology::eTriangleFan;
|
||||
break;
|
||||
default: EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
|
||||
default: EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -885,10 +961,9 @@ static void RefreshShaders(RenderCommandBuffer& buffer, const DrawCallInfo& draw
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<BufferBinding> PrepareVertexBuffers(uint64_t submit_id,
|
||||
RenderCommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
const ShaderVertexInputInfo& vs_input_info) {
|
||||
static PreparedVertexBuffers PrepareVertexBuffers(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
const ShaderVertexInputInfo& vs_input_info) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
(void)submit_id;
|
||||
|
||||
@@ -896,12 +971,6 @@ static std::vector<BufferBinding> PrepareVertexBuffers(uint64_t
|
||||
return AcquireVertexBuffers(buffer, vs_input_info);
|
||||
}
|
||||
|
||||
static void RebindVertexBuffers(RenderCommandBuffer& buffer,
|
||||
const ShaderVertexInputInfo& vs_input_info,
|
||||
std::vector<BufferBinding>& bindings) {
|
||||
bindings = AcquireVertexBuffers(buffer, vs_input_info);
|
||||
}
|
||||
|
||||
static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffer,
|
||||
const DrawIndexBufferSource& source) {
|
||||
PreparedIndexBuffer prepared;
|
||||
@@ -909,16 +978,14 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
|
||||
return prepared;
|
||||
}
|
||||
EXIT_IF(source.size == 0);
|
||||
prepared.address = source.address;
|
||||
prepared.size = source.size;
|
||||
prepared.type = source.type;
|
||||
prepared.size = source.size;
|
||||
prepared.type = source.type;
|
||||
if (source.host_data != nullptr) {
|
||||
auto binding =
|
||||
buffer.GetContext().GetBufferCache().UploadTransient(source.host_data, source.size, 16);
|
||||
prepared.owner = std::move(binding.owner);
|
||||
prepared.buffer = binding.buffer;
|
||||
prepared.offset = binding.offset;
|
||||
prepared.streamed = true;
|
||||
prepared.owner = std::move(binding.owner);
|
||||
prepared.buffer = binding.buffer;
|
||||
prepared.offset = binding.offset;
|
||||
} else {
|
||||
auto binding =
|
||||
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, source.address, source.size);
|
||||
@@ -929,36 +996,29 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
|
||||
return prepared;
|
||||
}
|
||||
|
||||
static void RebindIndexBuffer(RenderCommandBuffer& buffer, PreparedIndexBuffer& prepared) {
|
||||
if (prepared.size == 0 || prepared.streamed) {
|
||||
return;
|
||||
}
|
||||
auto binding =
|
||||
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, prepared.address, prepared.size);
|
||||
prepared.owner = std::move(binding.owner);
|
||||
prepared.buffer = binding.buffer;
|
||||
prepared.offset = binding.offset;
|
||||
}
|
||||
|
||||
static void CommitVertexBuffers(RenderCommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
||||
std::vector<BufferBinding>& bindings) {
|
||||
for (uint32_t slot = 0; slot < bindings.size(); slot++) {
|
||||
auto& binding = bindings[slot];
|
||||
if (binding.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(binding.owner);
|
||||
PreparedVertexBuffers& prepared) {
|
||||
for (uint32_t i = 0; i < prepared.owner_count; i++) {
|
||||
if (prepared.owners[i] != nullptr) {
|
||||
buffer.RetainResourceUntilFence(std::move(prepared.owners[i]));
|
||||
}
|
||||
EXIT_IF(binding.buffer == nullptr);
|
||||
vk_buffer.bindVertexBuffers(slot, 1, &binding.buffer, &binding.offset);
|
||||
}
|
||||
for (uint32_t i = 0; i < prepared.count; i++) {
|
||||
EXIT_IF(prepared.buffers[i] == nullptr);
|
||||
}
|
||||
if (prepared.count != 0) {
|
||||
vk_buffer.bindVertexBuffers(0, prepared.count, prepared.buffers.data(),
|
||||
prepared.offsets.data());
|
||||
}
|
||||
}
|
||||
|
||||
static void CommitIndexBuffer(RenderCommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
||||
const PreparedIndexBuffer& prepared) {
|
||||
PreparedIndexBuffer& prepared) {
|
||||
if (prepared.size == 0) {
|
||||
return;
|
||||
}
|
||||
if (prepared.owner != nullptr) {
|
||||
buffer.RetainResourceUntilFence(prepared.owner);
|
||||
buffer.RetainResourceUntilFence(std::move(prepared.owner));
|
||||
}
|
||||
EXIT_IF(prepared.buffer == nullptr);
|
||||
vk_buffer.bindIndexBuffer(prepared.buffer, prepared.offset, prepared.type);
|
||||
@@ -992,7 +1052,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
const DrawEmitInfo& emit) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
|
||||
switch (static_cast<Prospero::PrimitiveType>(ucfg.GetPrimType())) {
|
||||
switch (ucfg.GetPrimType()) {
|
||||
case Prospero::PrimitiveType::kPointList:
|
||||
case Prospero::PrimitiveType::kLineList:
|
||||
case Prospero::PrimitiveType::kLineStrip:
|
||||
@@ -1010,7 +1070,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
break;
|
||||
case Prospero::PrimitiveType::kRectListLegacy:
|
||||
if (emit.indexed) {
|
||||
EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
|
||||
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
// Sarah
|
||||
EXIT_NOT_IMPLEMENTED(!(draw.index_count == 3 && vs_input_info.buffers_num == 0));
|
||||
@@ -1028,7 +1088,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: EXIT("unknown primitive type: %u\n", ucfg.GetPrimType());
|
||||
default: EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1046,8 +1106,6 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer
|
||||
state.ps_input_info.stage, state.ps_active);
|
||||
auto vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
|
||||
auto index_binding = PrepareIndexBuffer(buffer, index_source);
|
||||
RebindVertexBuffers(buffer, state.vs_input_info, vertex_bindings);
|
||||
RebindIndexBuffer(buffer, index_binding);
|
||||
state.rendering =
|
||||
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info);
|
||||
|
||||
@@ -1330,9 +1388,8 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
||||
}
|
||||
|
||||
LogDrawStateIfNeeded(buffer, draw, state, false,
|
||||
ucfg.GetPrimType() ==
|
||||
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
||||
0, nullptr);
|
||||
ucfg.GetPrimType() == Prospero::PrimitiveType::kRectListLegacy, 0,
|
||||
nullptr);
|
||||
|
||||
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
||||
static_cast<int32_t>(first_vertex);
|
||||
|
||||
@@ -15,12 +15,16 @@ struct ShaderVertexInputInfo;
|
||||
|
||||
struct PipelineDynamicParameters {
|
||||
bool stencil_test_enable = false;
|
||||
bool depth_bias_enable = false;
|
||||
|
||||
float viewport_scale[3] = {};
|
||||
float viewport_offset[3] = {};
|
||||
int scissor_ltrb[4] = {0};
|
||||
|
||||
float line_width = 1.0f;
|
||||
float depth_bias_constant_factor = 0.0f;
|
||||
float depth_bias_clamp = 0.0f;
|
||||
float depth_bias_slope_factor = 0.0f;
|
||||
uint32_t color_write_count = 1;
|
||||
bool color_write_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {true, true};
|
||||
|
||||
@@ -33,10 +37,6 @@ struct PipelineDynamicParameters {
|
||||
static_assert(std::is_trivially_copyable_v<PipelineDynamicParameters>);
|
||||
static_assert(std::is_standard_layout_v<PipelineDynamicParameters>);
|
||||
static_assert(alignof(PipelineDynamicParameters) == 1);
|
||||
static_assert(sizeof(PipelineDynamicParameters) ==
|
||||
sizeof(bool) + sizeof(float[3]) + sizeof(float[3]) + sizeof(int[4]) + sizeof(float) +
|
||||
sizeof(uint32_t) + sizeof(bool[RENDER_COLOR_ATTACHMENTS_MAX]) +
|
||||
sizeof(PipelineStencilDynamicState) * 2);
|
||||
|
||||
[[nodiscard]] int32_t ResolveVertexOffset(uint32_t index_offset,
|
||||
const ShaderVertexInputInfo& vs_input_info);
|
||||
|
||||
@@ -83,11 +83,11 @@ constexpr FormatMapping kFormatMappings[] = {
|
||||
};
|
||||
|
||||
constexpr auto MakeFormatLookup() {
|
||||
constexpr auto kMaxFormat = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc7Srgb);
|
||||
constexpr auto kMaxFormat = static_cast<size_t>(Prospero::BufferFormat::kBc7Srgb);
|
||||
std::array<vk::Format, kMaxFormat + 1> lookup {};
|
||||
lookup.fill(vk::Format::eUndefined);
|
||||
for (const auto& mapping: kFormatMappings) {
|
||||
lookup[Prospero::GpuEnumValue(mapping.guest)] = mapping.host;
|
||||
lookup[static_cast<size_t>(mapping.guest)] = mapping.host;
|
||||
}
|
||||
return lookup;
|
||||
}
|
||||
@@ -112,9 +112,9 @@ std::string VulkanToString(vk::QueueFlags value) {
|
||||
return vk::to_string(value);
|
||||
}
|
||||
|
||||
vk::Format VulkanFormat(uint32_t guest_format) {
|
||||
return guest_format < kFormatLookup.size() ? kFormatLookup[guest_format]
|
||||
: vk::Format::eUndefined;
|
||||
vk::Format VulkanFormat(Prospero::BufferFormat guest_format) {
|
||||
const auto index = static_cast<size_t>(guest_format);
|
||||
return index < kFormatLookup.size() ? kFormatLookup[index] : vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
void RequireVulkanSuccess(vk::Result result, const char* operation) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
||||
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -29,7 +31,7 @@ std::string VulkanToString(vk::Result value);
|
||||
std::string VulkanToString(vk::Format value);
|
||||
std::string VulkanToString(vk::ImageLayout value);
|
||||
std::string VulkanToString(vk::QueueFlags value);
|
||||
vk::Format VulkanFormat(uint32_t guest_format);
|
||||
vk::Format VulkanFormat(Prospero::BufferFormat guest_format);
|
||||
void RequireVulkanSuccess(vk::Result result, const char* operation);
|
||||
|
||||
template <typename T, typename Enumerator>
|
||||
|
||||
@@ -0,0 +1,958 @@
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "libs/controller.h"
|
||||
#include "libs/ime.h"
|
||||
#include "libs/imeDialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace CoreIme = Libs::Ime;
|
||||
namespace DialogIme = Libs::Dialog::ImeDialog;
|
||||
|
||||
namespace Ime {
|
||||
|
||||
using Type = ImeCommon::Type;
|
||||
using EnterLabel = ImeCommon::EnterLabel;
|
||||
using Alignment = ImeCommon::Alignment;
|
||||
using ExternalAction = ImeCommon::ExternalAction;
|
||||
using ExternalInput = ImeCommon::ExternalInput;
|
||||
using HostSnapshot = ImeCommon::HostSnapshot;
|
||||
|
||||
constexpr uint32_t OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE;
|
||||
constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = ImeCommon::OPTION_NO_AUTO_CAPITALIZE;
|
||||
constexpr uint32_t OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD;
|
||||
constexpr uint32_t OPTION_FIXED_POSITION = ImeCommon::OPTION_FIXED_POSITION;
|
||||
constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = ImeCommon::OPTION_DISABLE_POSITION_ADJUST;
|
||||
constexpr uint32_t OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K;
|
||||
constexpr uint32_t DISABLE_DEVICE_CONTROLLER = ImeCommon::DISABLE_DEVICE_CONTROLLER;
|
||||
constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = ImeCommon::DISABLE_DEVICE_EXT_KEYBOARD;
|
||||
constexpr uint64_t CORE_GENERATION_BIT = uint64_t {1} << 63;
|
||||
|
||||
uint64_t PackCoreGeneration(uint64_t generation) {
|
||||
return generation | CORE_GENERATION_BIT;
|
||||
}
|
||||
|
||||
bool IsCoreGeneration(uint64_t generation) {
|
||||
return (generation & CORE_GENERATION_BIT) != 0;
|
||||
}
|
||||
|
||||
uint64_t UnpackGeneration(uint64_t generation) {
|
||||
return generation & ~CORE_GENERATION_BIT;
|
||||
}
|
||||
|
||||
bool GetHostSnapshot(HostSnapshot* snapshot) {
|
||||
if (snapshot == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (CoreIme::GetHostSnapshot(snapshot)) {
|
||||
snapshot->generation = PackCoreGeneration(snapshot->generation);
|
||||
return true;
|
||||
}
|
||||
return DialogIme::GetHostSnapshot(snapshot);
|
||||
}
|
||||
|
||||
bool HostInsertText(uint64_t generation, std::u16string_view text) {
|
||||
return IsCoreGeneration(generation)
|
||||
? CoreIme::HostInsertText(UnpackGeneration(generation), text)
|
||||
: DialogIme::HostInsertText(generation, text);
|
||||
}
|
||||
|
||||
bool HostBackspace(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostBackspace(UnpackGeneration(generation))
|
||||
: DialogIme::HostBackspace(generation);
|
||||
}
|
||||
|
||||
bool HostAccept(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostAccept(UnpackGeneration(generation))
|
||||
: DialogIme::HostAccept(generation);
|
||||
}
|
||||
|
||||
bool HostCancel(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostCancel(UnpackGeneration(generation))
|
||||
: DialogIme::HostCancel(generation);
|
||||
}
|
||||
|
||||
bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
|
||||
return IsCoreGeneration(generation)
|
||||
? CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(input))
|
||||
: DialogIme::HostQueueExternalInput(generation, std::move(input));
|
||||
}
|
||||
|
||||
} // namespace Ime
|
||||
|
||||
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
|
||||
|
||||
enum class InputKind : uint8_t {
|
||||
Button,
|
||||
Axis,
|
||||
MousePosition,
|
||||
MouseButton,
|
||||
MouseWheel,
|
||||
ResetController
|
||||
};
|
||||
|
||||
struct InputEvent {
|
||||
InputKind kind;
|
||||
uint64_t generation;
|
||||
int id;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct VisibilityUpdate {
|
||||
uint64_t generation;
|
||||
bool visible;
|
||||
bool capture_controller;
|
||||
bool capture_keyboard;
|
||||
bool multiline;
|
||||
};
|
||||
|
||||
std::atomic<Uint32> g_visibility_event {static_cast<Uint32>(-1)};
|
||||
std::mutex g_visibility_mutex;
|
||||
std::mutex g_input_mutex;
|
||||
std::deque<InputEvent> g_input_events;
|
||||
std::deque<VisibilityUpdate> g_visibility_updates;
|
||||
size_t g_missing_visibility_wakeups = 0;
|
||||
bool g_input_reset_requested = false;
|
||||
uint16_t g_last_external_keycode = 0;
|
||||
uint32_t g_last_external_status = 0;
|
||||
uint64_t g_input_generation = 0;
|
||||
bool g_input_active = false;
|
||||
bool g_input_controller = false;
|
||||
bool g_input_keyboard = false;
|
||||
bool g_input_multiline = false;
|
||||
bool g_input_lifecycle_active = false;
|
||||
bool g_active = false;
|
||||
bool g_controller_captured = false;
|
||||
uint64_t g_generation = 0;
|
||||
|
||||
void ClearInputEvents() {
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
g_input_events.clear();
|
||||
g_input_reset_requested = false;
|
||||
}
|
||||
|
||||
void QueueInput(InputEvent event) {
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
const bool replaceable =
|
||||
event.kind == InputKind::Axis || event.kind == InputKind::MousePosition;
|
||||
if (replaceable && !g_input_events.empty()) {
|
||||
auto& last = g_input_events.back();
|
||||
if (last.generation == event.generation && last.kind == event.kind && last.id == event.id) {
|
||||
last = event;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (g_input_events.size() == INPUT_QUEUE_CAPACITY) {
|
||||
g_input_events.clear();
|
||||
g_input_reset_requested = true;
|
||||
}
|
||||
g_input_events.push_back(event);
|
||||
}
|
||||
|
||||
void RetryVisibilityWakeup() {
|
||||
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
||||
if (type == static_cast<Uint32>(-1)) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
if (g_missing_visibility_wakeups == 0) {
|
||||
return;
|
||||
}
|
||||
SDL_Event event {};
|
||||
event.type = type;
|
||||
if (SDL_PushEvent(&event) > 0) {
|
||||
g_missing_visibility_wakeups--;
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshVisibility() {
|
||||
std::scoped_lock visibility_lock(g_visibility_mutex);
|
||||
if (!g_input_lifecycle_active) {
|
||||
return;
|
||||
}
|
||||
Ime::HostSnapshot snapshot;
|
||||
const bool visible = Ime::GetHostSnapshot(&snapshot);
|
||||
const uint64_t generation = visible ? snapshot.generation : g_generation;
|
||||
if (visible == g_active && (!visible || generation == g_generation)) {
|
||||
return;
|
||||
}
|
||||
g_generation = generation;
|
||||
bool capture_controller = false;
|
||||
bool capture_keyboard = false;
|
||||
bool multiline = false;
|
||||
if (visible) {
|
||||
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
||||
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
||||
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
|
||||
}
|
||||
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
|
||||
if (capture_controller || was_controller) {
|
||||
Controller::ControllerResetInputState();
|
||||
}
|
||||
g_active = visible;
|
||||
|
||||
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
||||
if (type != static_cast<Uint32>(-1)) {
|
||||
SDL_Event event {};
|
||||
event.type = type;
|
||||
std::scoped_lock input_lock(g_input_mutex);
|
||||
g_visibility_updates.push_back(
|
||||
{generation, visible, capture_controller, capture_keyboard, multiline});
|
||||
if (g_missing_visibility_wakeups != 0 || SDL_PushEvent(&event) <= 0) {
|
||||
g_missing_visibility_wakeups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnCoreVisibilityChanged(bool, uint64_t) {
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
void OnDialogVisibilityChanged(bool, uint64_t) {
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
|
||||
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
|
||||
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
|
||||
if ((modifiers & KMOD_LSHIFT) != 0) status |= 0x00000200;
|
||||
if ((modifiers & KMOD_LALT) != 0) status |= 0x00000400;
|
||||
if ((modifiers & KMOD_LGUI) != 0) status |= 0x00000800;
|
||||
if ((modifiers & KMOD_RCTRL) != 0) status |= 0x00001000;
|
||||
if ((modifiers & KMOD_RSHIFT) != 0) status |= 0x00002000;
|
||||
if ((modifiers & KMOD_RALT) != 0) status |= 0x00004000;
|
||||
if ((modifiers & KMOD_RGUI) != 0) status |= 0x00008000;
|
||||
if ((modifiers & KMOD_NUM) != 0) status |= 0x00010000;
|
||||
if ((modifiers & KMOD_CAPS) != 0) status |= 0x00020000;
|
||||
return status;
|
||||
}
|
||||
|
||||
Ime::ExternalInput MakeExternalInput(Ime::ExternalAction action, uint16_t keycode,
|
||||
uint32_t status) {
|
||||
Ime::ExternalInput input {};
|
||||
input.key.keycode = keycode;
|
||||
input.key.status = status;
|
||||
input.key.type = 4;
|
||||
input.action = action;
|
||||
return input;
|
||||
}
|
||||
|
||||
std::u16string Utf8ToUtf16(std::string_view text) {
|
||||
std::u16string result;
|
||||
result.reserve(text.size());
|
||||
for (size_t i = 0; i < text.size();) {
|
||||
const auto first = static_cast<uint8_t>(text[i++]);
|
||||
uint32_t codepoint = 0;
|
||||
uint32_t remaining = 0;
|
||||
if (first < 0x80) {
|
||||
codepoint = first;
|
||||
} else if ((first & 0xe0) == 0xc0) {
|
||||
codepoint = first & 0x1f;
|
||||
remaining = 1;
|
||||
} else if ((first & 0xf0) == 0xe0) {
|
||||
codepoint = first & 0x0f;
|
||||
remaining = 2;
|
||||
} else if ((first & 0xf8) == 0xf0) {
|
||||
codepoint = first & 0x07;
|
||||
remaining = 3;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (i + remaining > text.size()) {
|
||||
break;
|
||||
}
|
||||
bool valid = true;
|
||||
for (uint32_t j = 0; j < remaining; j++) {
|
||||
const auto next = static_cast<uint8_t>(text[i++]);
|
||||
if ((next & 0xc0) != 0x80) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
codepoint = (codepoint << 6) | (next & 0x3f);
|
||||
}
|
||||
if (!valid || codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)) {
|
||||
continue;
|
||||
}
|
||||
if (codepoint <= 0xffff) {
|
||||
result.push_back(static_cast<char16_t>(codepoint));
|
||||
} else {
|
||||
codepoint -= 0x10000;
|
||||
result.push_back(static_cast<char16_t>(0xd800 + (codepoint >> 10)));
|
||||
result.push_back(static_cast<char16_t>(0xdc00 + (codepoint & 0x3ff)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string VisibleText(const Ime::HostSnapshot& snapshot, size_t max_units) {
|
||||
const size_t cursor = std::min<size_t>(snapshot.cursor, snapshot.text.size());
|
||||
const size_t payload_units = max_units > 4 ? max_units - 4 : 1;
|
||||
size_t begin = cursor > payload_units / 2 ? cursor - payload_units / 2 : 0;
|
||||
size_t end = std::min(snapshot.text.size(), begin + payload_units);
|
||||
if (end == snapshot.text.size() && end - begin < payload_units) {
|
||||
begin = end > payload_units ? end - payload_units : 0;
|
||||
}
|
||||
if (begin > 0 && snapshot.text[begin] >= 0xdc00 && snapshot.text[begin] <= 0xdfff) {
|
||||
begin--;
|
||||
}
|
||||
if (end < snapshot.text.size() && end > begin && snapshot.text[end - 1] >= 0xd800 &&
|
||||
snapshot.text[end - 1] <= 0xdbff) {
|
||||
end--;
|
||||
}
|
||||
|
||||
std::u16string visible;
|
||||
if (begin != 0) {
|
||||
visible.push_back(u'\u2026');
|
||||
}
|
||||
const size_t caret = visible.size() + cursor - begin;
|
||||
if ((snapshot.option & Ime::OPTION_PASSWORD) != 0) {
|
||||
visible.append(end - begin, u'*');
|
||||
} else {
|
||||
visible.append(snapshot.text, begin, end - begin);
|
||||
std::replace(visible.begin(), visible.end(), u'\n', u'\u21b5');
|
||||
std::replace(visible.begin(), visible.end(), u'\r', u'\u21b5');
|
||||
}
|
||||
visible.insert(std::min(caret, visible.size()), 1, u'|');
|
||||
if (end != snapshot.text.size()) {
|
||||
visible.push_back(u'\u2026');
|
||||
}
|
||||
return Common::Utf16ToUtf8(visible.c_str());
|
||||
}
|
||||
|
||||
const char* EnterLabel(Ime::EnterLabel label) {
|
||||
switch (label) {
|
||||
case Ime::EnterLabel::Send: return "Send";
|
||||
case Ime::EnterLabel::Search: return "Search";
|
||||
case Ime::EnterLabel::Go: return "Go";
|
||||
default: return "Done";
|
||||
}
|
||||
}
|
||||
|
||||
float AlignmentPivot(Ime::Alignment alignment) {
|
||||
switch (alignment) {
|
||||
case Ime::Alignment::Start: return 0.0f;
|
||||
case Ime::Alignment::End: return 1.0f;
|
||||
default: return 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiKey ControllerButtonToKey(int button) {
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A: return ImGuiKey_GamepadFaceDown;
|
||||
case SDL_CONTROLLER_BUTTON_B: return ImGuiKey_GamepadFaceRight;
|
||||
case SDL_CONTROLLER_BUTTON_X: return ImGuiKey_GamepadFaceLeft;
|
||||
case SDL_CONTROLLER_BUTTON_Y: return ImGuiKey_GamepadFaceUp;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
|
||||
default: return ImGuiKey_None;
|
||||
}
|
||||
}
|
||||
|
||||
PFN_vkVoidFunction LoadVulkanFunction(const char* name, void* user_data) {
|
||||
auto& graphics = *static_cast<GraphicContext*>(user_data);
|
||||
return graphics.instance.getProcAddr(name);
|
||||
}
|
||||
|
||||
void CheckVulkanResult(VkResult result) {
|
||||
EXIT_IF(result != VK_SUCCESS);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeImeInput() {
|
||||
const Uint32 type = SDL_RegisterEvents(1);
|
||||
EXIT_IF(type == static_cast<Uint32>(-1));
|
||||
{
|
||||
std::scoped_lock lock(g_visibility_mutex);
|
||||
g_input_lifecycle_active = true;
|
||||
g_visibility_event.store(type, std::memory_order_release);
|
||||
}
|
||||
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
|
||||
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
void ShutdownImeInput() {
|
||||
CoreIme::SetVisibilityCallback(nullptr);
|
||||
DialogIme::SetVisibilityCallback(nullptr);
|
||||
{
|
||||
std::scoped_lock lock(g_visibility_mutex);
|
||||
g_input_lifecycle_active = false;
|
||||
g_active = false;
|
||||
if (std::exchange(g_controller_captured, false)) {
|
||||
Controller::ControllerResetInputState();
|
||||
}
|
||||
g_visibility_event.store(static_cast<Uint32>(-1), std::memory_order_release);
|
||||
}
|
||||
ClearInputEvents();
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
g_visibility_updates.clear();
|
||||
g_missing_visibility_wakeups = 0;
|
||||
}
|
||||
g_input_active = false;
|
||||
g_input_controller = false;
|
||||
g_input_keyboard = false;
|
||||
g_input_multiline = false;
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
}
|
||||
|
||||
ImeVisualState GetImeVisualState() noexcept {
|
||||
const auto core = CoreIme::GetVisualState();
|
||||
const auto dialog = DialogIme::GetVisualState();
|
||||
return {core.active || dialog.active, core.revision + dialog.revision};
|
||||
}
|
||||
|
||||
bool ProcessImeInput(const SDL_Event& event) {
|
||||
RetryVisibilityWakeup();
|
||||
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
|
||||
VisibilityUpdate update {};
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
if (g_visibility_updates.empty()) {
|
||||
return true;
|
||||
}
|
||||
update = g_visibility_updates.front();
|
||||
g_visibility_updates.pop_front();
|
||||
}
|
||||
ClearInputEvents();
|
||||
g_last_external_keycode = 0;
|
||||
g_last_external_status = 0;
|
||||
g_input_generation = update.generation;
|
||||
g_input_active = update.visible;
|
||||
g_input_controller = update.capture_controller;
|
||||
g_input_keyboard = update.capture_keyboard;
|
||||
g_input_multiline = update.multiline;
|
||||
if (g_input_keyboard) {
|
||||
SDL_StartTextInput();
|
||||
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
if (g_input_active && g_input_controller) {
|
||||
QueueInput({InputKind::ResetController, g_input_generation, 0, 0.0f, 0.0f});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!g_input_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t generation = g_input_generation;
|
||||
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
|
||||
event.type == SDL_CONTROLLERBUTTONUP ||
|
||||
event.type == SDL_CONTROLLERAXISMOTION;
|
||||
if (controller_event && !g_input_controller) {
|
||||
return false;
|
||||
}
|
||||
const bool keyboard_event = event.type == SDL_TEXTINPUT || event.type == SDL_TEXTEDITING ||
|
||||
event.type == SDL_KEYDOWN || event.type == SDL_KEYUP;
|
||||
if (keyboard_event && !g_input_keyboard) {
|
||||
return false;
|
||||
}
|
||||
switch (event.type) {
|
||||
case SDL_TEXTINPUT: {
|
||||
const auto text = Utf8ToUtf16(event.text.text);
|
||||
if (!text.empty()) {
|
||||
auto input = MakeExternalInput(Ime::ExternalAction::Text, g_last_external_keycode,
|
||||
g_last_external_status | 0x00000002);
|
||||
input.key.character = text.front();
|
||||
input.text = text;
|
||||
Ime::HostQueueExternalInput(generation, std::move(input));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_TEXTEDITING: return true;
|
||||
case SDL_KEYDOWN: {
|
||||
g_last_external_keycode = static_cast<uint16_t>(event.key.keysym.scancode);
|
||||
g_last_external_status =
|
||||
ExternalKeyStatus(static_cast<SDL_Keymod>(event.key.keysym.mod), false);
|
||||
auto action = Ime::ExternalAction::Text;
|
||||
bool queue = true;
|
||||
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
||||
action = Ime::ExternalAction::Backspace;
|
||||
} else if (event.key.keysym.sym == SDLK_LEFT) {
|
||||
action = Ime::ExternalAction::MoveLeft;
|
||||
} else if (event.key.keysym.sym == SDLK_RIGHT) {
|
||||
action = Ime::ExternalAction::MoveRight;
|
||||
} else if (event.key.keysym.sym == SDLK_ESCAPE) {
|
||||
action = Ime::ExternalAction::Cancel;
|
||||
} else if (event.key.keysym.sym == SDLK_RETURN ||
|
||||
event.key.keysym.sym == SDLK_KP_ENTER) {
|
||||
action =
|
||||
g_input_multiline ? Ime::ExternalAction::Newline : Ime::ExternalAction::Accept;
|
||||
} else if (event.key.keysym.sym == SDLK_TAB) {
|
||||
action = Ime::ExternalAction::None;
|
||||
} else {
|
||||
queue = false;
|
||||
}
|
||||
if (queue) {
|
||||
Ime::HostQueueExternalInput(
|
||||
generation,
|
||||
MakeExternalInput(action, g_last_external_keycode, g_last_external_status));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_KEYUP: return true;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
QueueInput({InputKind::Button, generation, event.cbutton.button,
|
||||
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||
return true;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
QueueInput({InputKind::Axis, generation, event.caxis.axis,
|
||||
static_cast<float>(event.caxis.value), 0.0f});
|
||||
return true;
|
||||
case SDL_MOUSEMOTION:
|
||||
QueueInput({InputKind::MousePosition, generation, 0, static_cast<float>(event.motion.x),
|
||||
static_cast<float>(event.motion.y)});
|
||||
return true;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
QueueInput({InputKind::MouseButton, generation, event.button.button,
|
||||
event.type == SDL_MOUSEBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||
return true;
|
||||
case SDL_MOUSEWHEEL:
|
||||
QueueInput({InputKind::MouseWheel, generation, 0, static_cast<float>(event.wheel.x),
|
||||
static_cast<float>(event.wheel.y)});
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct ImeOverlay::Impl {
|
||||
explicit Impl(GraphicContext& context): graphics(context) {}
|
||||
|
||||
~Impl() {
|
||||
ReleaseVulkan();
|
||||
if (imgui_context != nullptr) {
|
||||
ImGui::DestroyContext(imgui_context);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureContext() {
|
||||
if (imgui_context != nullptr) {
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
return;
|
||||
}
|
||||
IMGUI_CHECKVERSION();
|
||||
imgui_context = ImGui::CreateContext();
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
auto& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.LogFilename = nullptr;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
io.BackendPlatformName = "Kyty IME input";
|
||||
ImGui::StyleColorsDark();
|
||||
auto& style = ImGui::GetStyle();
|
||||
style.WindowRounding = 10.0f;
|
||||
style.FrameRounding = 6.0f;
|
||||
style.ItemSpacing = {8.0f, 8.0f};
|
||||
}
|
||||
|
||||
void EnsureVulkan(vk::Format format, uint32_t image_count) {
|
||||
EnsureContext();
|
||||
if (vulkan_initialized) {
|
||||
return;
|
||||
}
|
||||
EXIT_IF(image_count < 2);
|
||||
EXIT_IF(!ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, LoadVulkanFunction, &graphics));
|
||||
|
||||
const VkFormat color_format = static_cast<VkFormat>(format);
|
||||
ImGui_ImplVulkan_InitInfo info {};
|
||||
info.ApiVersion = VK_API_VERSION_1_3;
|
||||
info.Instance = static_cast<VkInstance>(graphics.instance);
|
||||
info.PhysicalDevice = static_cast<VkPhysicalDevice>(graphics.physical_device);
|
||||
info.Device = static_cast<VkDevice>(graphics.device);
|
||||
info.QueueFamily = graphics.queue_family;
|
||||
info.Queue = static_cast<VkQueue>(graphics.queue);
|
||||
info.DescriptorPoolSize = IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE;
|
||||
info.MinImageCount = image_count;
|
||||
info.ImageCount = image_count;
|
||||
info.UseDynamicRendering = true;
|
||||
info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount = 1;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.pColorAttachmentFormats = &color_format;
|
||||
info.CheckVkResultFn = CheckVulkanResult;
|
||||
EXIT_IF(!ImGui_ImplVulkan_Init(&info));
|
||||
vulkan_initialized = true;
|
||||
}
|
||||
|
||||
void DrainInput(uint64_t generation) {
|
||||
std::deque<InputEvent> events;
|
||||
bool reset = false;
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
events.swap(g_input_events);
|
||||
reset = g_input_reset_requested;
|
||||
g_input_reset_requested = false;
|
||||
}
|
||||
auto& io = ImGui::GetIO();
|
||||
if (reset) {
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
right_stick = {};
|
||||
}
|
||||
for (const auto& event: events) {
|
||||
if (event.generation != generation) {
|
||||
continue;
|
||||
}
|
||||
switch (event.kind) {
|
||||
case InputKind::Button: {
|
||||
const bool down = event.x != 0.0f;
|
||||
if (down && event.id == SDL_CONTROLLER_BUTTON_B) {
|
||||
Ime::HostCancel(generation);
|
||||
} else if (down && event.id == SDL_CONTROLLER_BUTTON_Y) {
|
||||
Ime::HostBackspace(generation);
|
||||
}
|
||||
const ImGuiKey key = ControllerButtonToKey(event.id);
|
||||
if (key != ImGuiKey_None) {
|
||||
io.AddKeyEvent(key, down);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputKind::Axis: {
|
||||
const float value = event.x / (event.x < 0.0f ? 32768.0f : 32767.0f);
|
||||
if (event.id == SDL_CONTROLLER_AXIS_LEFTX) {
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft, value < -0.25f,
|
||||
std::max(-value, 0.0f));
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, value > 0.25f,
|
||||
std::max(value, 0.0f));
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_LEFTY) {
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp, value < -0.25f,
|
||||
std::max(-value, 0.0f));
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, value > 0.25f,
|
||||
std::max(value, 0.0f));
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTX) {
|
||||
right_stick.x = std::abs(value) > 0.2f ? value : 0.0f;
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTY) {
|
||||
right_stick.y = std::abs(value) > 0.2f ? value : 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputKind::MousePosition: io.AddMousePosEvent(event.x, event.y); break;
|
||||
case InputKind::MouseButton: {
|
||||
int button = -1;
|
||||
if (event.id == SDL_BUTTON_LEFT) button = 0;
|
||||
if (event.id == SDL_BUTTON_RIGHT) button = 1;
|
||||
if (event.id == SDL_BUTTON_MIDDLE) button = 2;
|
||||
if (button >= 0) io.AddMouseButtonEvent(button, event.x != 0.0f);
|
||||
break;
|
||||
}
|
||||
case InputKind::MouseWheel: io.AddMouseWheelEvent(event.x, event.y); break;
|
||||
case InputKind::ResetController:
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
right_stick = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyButton(std::string_view label, char16_t value, uint64_t generation, float width,
|
||||
bool default_focus) {
|
||||
const bool pressed = ImGui::Button(label.data(), {width, button_height});
|
||||
if (default_focus) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
if (pressed) {
|
||||
Ime::HostInsertText(generation, std::u16string_view(&value, 1));
|
||||
}
|
||||
}
|
||||
|
||||
void DrawKeyRows(const Ime::HostSnapshot& snapshot, float width) {
|
||||
static constexpr std::array<std::string_view, 4> lower = {"1234567890", "qwertyuiop",
|
||||
"asdfghjkl", "zxcvbnm"};
|
||||
static constexpr std::array<std::string_view, 4> upper = {"1234567890", "QWERTYUIOP",
|
||||
"ASDFGHJKL", "ZXCVBNM"};
|
||||
static constexpr std::array<std::string_view, 3> symbols = {"1234567890", "!@#$%^&*()",
|
||||
"-_=+/:?."};
|
||||
static constexpr std::array<std::string_view, 4> number = {"123", "456", "789", "-0."};
|
||||
|
||||
std::span<const std::string_view> rows;
|
||||
if (snapshot.type == Ime::Type::Number) {
|
||||
rows = number;
|
||||
} else if (symbol_mode) {
|
||||
rows = symbols;
|
||||
} else {
|
||||
rows = shift ? std::span<const std::string_view>(upper)
|
||||
: std::span<const std::string_view>(lower);
|
||||
}
|
||||
bool first = focus_pending;
|
||||
size_t row_index = 0;
|
||||
for (const auto row: rows) {
|
||||
const float key_width =
|
||||
std::min(56.0f * ui_scale, (width - 8.0f * (row.size() - 1)) / row.size());
|
||||
const float row_width = key_width * row.size() + 8.0f * (row.size() - 1);
|
||||
ImGui::SetCursorPosX((width - row_width) * 0.5f);
|
||||
ImGui::PushID(static_cast<int>(row_index++));
|
||||
for (size_t i = 0; i < row.size(); i++) {
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
const char label[2] = {row[i], '\0'};
|
||||
KeyButton(label, static_cast<char16_t>(row[i]), snapshot.generation, key_width,
|
||||
first);
|
||||
first = false;
|
||||
ImGui::PopID();
|
||||
if (i + 1 < row.size()) ImGui::SameLine();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
focus_pending = false;
|
||||
}
|
||||
|
||||
void DrawDialog(const Ime::HostSnapshot& snapshot, vk::Extent2D extent) {
|
||||
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
|
||||
const bool over_2k = (snapshot.option & Ime::OPTION_USE_OVER_2K) != 0;
|
||||
const float reference_width = over_2k ? 3840.0f : 1920.0f;
|
||||
const float reference_height = over_2k ? 2160.0f : 1080.0f;
|
||||
const float scale_x = display.x / reference_width;
|
||||
const float scale_y = display.y / reference_height;
|
||||
const float width = static_cast<float>(snapshot.panel_width) * scale_x;
|
||||
const float height = static_cast<float>(snapshot.panel_height) * scale_y;
|
||||
ui_scale = std::min(scale_x, scale_y) * (over_2k ? 2.0f : 1.0f);
|
||||
button_height = std::max(28.0f, 42.0f * ui_scale);
|
||||
|
||||
const ImVec2 pivot {AlignmentPivot(snapshot.horizontal_alignment),
|
||||
AlignmentPivot(snapshot.vertical_alignment)};
|
||||
if ((snapshot.option & Ime::OPTION_FIXED_POSITION) == 0) {
|
||||
const float movement = 600.0f * ImGui::GetIO().DeltaTime;
|
||||
panel_offset.x += right_stick.x * movement;
|
||||
panel_offset.y += right_stick.y * movement;
|
||||
}
|
||||
const ImVec2 base_position {snapshot.posx * scale_x - pivot.x * width,
|
||||
snapshot.posy * scale_y - pivot.y * height};
|
||||
ImVec2 position {base_position.x + panel_offset.x, base_position.y + panel_offset.y};
|
||||
if ((snapshot.option & Ime::OPTION_DISABLE_POSITION_ADJ) == 0) {
|
||||
position.x = std::clamp(position.x, 0.0f, std::max(display.x - width, 0.0f));
|
||||
position.y = std::clamp(position.y, 0.0f, std::max(display.y - height, 0.0f));
|
||||
panel_offset = {position.x - base_position.x, position.y - base_position.y};
|
||||
}
|
||||
ImGui::SetNextWindowPos(position, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize({width, height}, ImGuiCond_Always);
|
||||
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
ImGui::Begin("##Ime", nullptr, flags);
|
||||
|
||||
const std::u16string title = snapshot.title.empty() ? u"Enter text" : snapshot.title;
|
||||
if (!snapshot.key_panel_visible) {
|
||||
std::string compact = Common::Utf16ToUtf8(title.c_str()) + ": ";
|
||||
const float glyph_width = std::max(ImGui::CalcTextSize("M").x, 1.0f);
|
||||
const float text_width =
|
||||
std::max(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(compact.c_str()).x,
|
||||
glyph_width);
|
||||
const size_t visible_units =
|
||||
std::max<size_t>(6, static_cast<size_t>(text_width / glyph_width));
|
||||
compact += snapshot.text.empty() && !snapshot.placeholder.empty()
|
||||
? Common::Utf16ToUtf8(snapshot.placeholder.c_str())
|
||||
: VisibleText(snapshot, visible_units);
|
||||
ImGui::TextUnformatted(compact.c_str());
|
||||
const float compact_height = std::min(button_height, 28.0f * ui_scale);
|
||||
if (ImGui::Button("Cancel", {90.0f * ui_scale, compact_height})) {
|
||||
Ime::HostCancel(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(EnterLabel(snapshot.enter_label),
|
||||
{90.0f * ui_scale, compact_height})) {
|
||||
Ime::HostAccept(snapshot.generation);
|
||||
}
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted(Common::Utf16ToUtf8(title.c_str()).c_str());
|
||||
ImGui::Separator();
|
||||
const float text_height = std::max(
|
||||
32.0f, ((snapshot.option & Ime::OPTION_MULTILINE) != 0 ? 80.0f : 48.0f) * ui_scale);
|
||||
ImGui::BeginChild("##ImeText", {0.0f, text_height}, true);
|
||||
if (snapshot.text.empty() && !snapshot.placeholder.empty()) {
|
||||
ImGui::TextDisabled("%s", Common::Utf16ToUtf8(snapshot.placeholder.c_str()).c_str());
|
||||
} else {
|
||||
const float glyph_width = std::max(ImGui::CalcTextSize("M").x, 1.0f);
|
||||
const size_t columns = std::max<size_t>(
|
||||
8, static_cast<size_t>(ImGui::GetContentRegionAvail().x / glyph_width));
|
||||
const size_t lines =
|
||||
std::max<size_t>(1, static_cast<size_t>(ImGui::GetContentRegionAvail().y /
|
||||
ImGui::GetTextLineHeightWithSpacing()));
|
||||
const auto text = VisibleText(snapshot, columns * lines);
|
||||
ImGui::TextWrapped("%s", text.c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::TextDisabled("%zu / %u", snapshot.text.size(), snapshot.max_text_length);
|
||||
ImGui::Separator();
|
||||
|
||||
const float content_width = ImGui::GetContentRegionAvail().x;
|
||||
DrawKeyRows(snapshot, content_width);
|
||||
if (snapshot.type != Ime::Type::Number) {
|
||||
if (ImGui::Button(shift ? "Lower" : "Shift", {84.0f * ui_scale, button_height})) {
|
||||
shift = !shift;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(symbol_mode ? "ABC" : "Symbols", {84.0f * ui_scale, button_height})) {
|
||||
symbol_mode = !symbol_mode;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Space", {120.0f * ui_scale, button_height})) {
|
||||
Ime::HostInsertText(snapshot.generation, u" ");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const float action_width = snapshot.type == Ime::Type::Number
|
||||
? std::max((content_width - 16.0f) / 3.0f, 48.0f)
|
||||
: 100.0f * ui_scale;
|
||||
if (ImGui::Button("Backspace", {action_width, button_height})) {
|
||||
Ime::HostBackspace(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if ((snapshot.option & Ime::OPTION_MULTILINE) != 0) {
|
||||
if (ImGui::Button("Newline", {action_width, button_height})) {
|
||||
Ime::HostInsertText(snapshot.generation, u"\n");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button("Cancel", {action_width, button_height})) {
|
||||
Ime::HostCancel(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(EnterLabel(snapshot.enter_label), {action_width, button_height})) {
|
||||
Ime::HostAccept(snapshot.generation);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool PrepareFrame(vk::Extent2D frame_extent, vk::Format format, uint32_t image_count) {
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (!Ime::GetHostSnapshot(&snapshot)) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t prepared_generation = snapshot.generation;
|
||||
EnsureVulkan(format, image_count);
|
||||
if (generation != snapshot.generation) {
|
||||
generation = snapshot.generation;
|
||||
focus_pending = true;
|
||||
shift = (snapshot.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
|
||||
symbol_mode = false;
|
||||
panel_offset = {};
|
||||
right_stick = {};
|
||||
auto& io = ImGui::GetIO();
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
}
|
||||
DrainInput(snapshot.generation);
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.DisplaySize = {static_cast<float>(frame_extent.width),
|
||||
static_cast<float>(frame_extent.height)};
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
io.DeltaTime = last_frame == std::chrono::steady_clock::time_point {}
|
||||
? 1.0f / 60.0f
|
||||
: std::clamp(std::chrono::duration<float>(now - last_frame).count(),
|
||||
1.0f / 1000.0f, 0.1f);
|
||||
last_frame = now;
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
if (!Ime::GetHostSnapshot(&snapshot) || snapshot.generation != prepared_generation) {
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
DrawDialog(snapshot, frame_extent);
|
||||
ImGui::Render();
|
||||
extent = frame_extent;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
vk::RenderingAttachmentInfo color {};
|
||||
color.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
color.imageView = target;
|
||||
color.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||
color.loadOp = vk::AttachmentLoadOp::eLoad;
|
||||
color.storeOp = vk::AttachmentStoreOp::eStore;
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = extent;
|
||||
rendering.layerCount = 1;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &color;
|
||||
command.beginRendering(rendering);
|
||||
{
|
||||
Common::LockGuard queue_lock(graphics.queue_mutex);
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(),
|
||||
static_cast<VkCommandBuffer>(command));
|
||||
}
|
||||
command.endRendering();
|
||||
}
|
||||
|
||||
void ReleaseVulkan() {
|
||||
if (!vulkan_initialized) {
|
||||
return;
|
||||
}
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
vulkan_initialized = false;
|
||||
}
|
||||
|
||||
GraphicContext& graphics;
|
||||
ImGuiContext* imgui_context = nullptr;
|
||||
bool vulkan_initialized = false;
|
||||
bool shift = false;
|
||||
bool symbol_mode = false;
|
||||
bool focus_pending = true;
|
||||
float ui_scale = 1.0f;
|
||||
float button_height = 42.0f;
|
||||
ImVec2 panel_offset {};
|
||||
ImVec2 right_stick {};
|
||||
uint64_t generation = 0;
|
||||
vk::Extent2D extent {};
|
||||
std::chrono::steady_clock::time_point last_frame;
|
||||
};
|
||||
|
||||
ImeOverlay::ImeOverlay(GraphicContext& graphics): m_impl(std::make_unique<Impl>(graphics)) {}
|
||||
|
||||
ImeOverlay::~ImeOverlay() = default;
|
||||
|
||||
bool ImeOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
||||
return m_impl->PrepareFrame(extent, format, image_count);
|
||||
}
|
||||
|
||||
void ImeOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
m_impl->Record(command, target);
|
||||
}
|
||||
|
||||
void ImeOverlay::ReleaseVulkan() {
|
||||
m_impl->ReleaseVulkan();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
|
||||
struct ImeVisualState {
|
||||
bool active;
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
void InitializeImeInput();
|
||||
void ShutdownImeInput();
|
||||
bool ProcessImeInput(const SDL_Event& event);
|
||||
ImeVisualState GetImeVisualState() noexcept;
|
||||
|
||||
class ImeOverlay final {
|
||||
public:
|
||||
explicit ImeOverlay(GraphicContext& graphics);
|
||||
~ImeOverlay();
|
||||
KYTY_CLASS_NO_COPY(ImeOverlay);
|
||||
|
||||
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target);
|
||||
void ReleaseVulkan();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
CommandBuffer* producer = nullptr);
|
||||
[[nodiscard]] Frame* PrepareLastFrame();
|
||||
[[nodiscard]] bool IsGuestPaused() const noexcept;
|
||||
[[nodiscard]] bool NeedsImeRefresh() const noexcept;
|
||||
[[nodiscard]] RenderContext& Renderer() const noexcept;
|
||||
void Present(Frame& frame, bool reuse = false);
|
||||
void Discard(Frame& frame);
|
||||
|
||||
@@ -128,8 +128,7 @@ static bool BindRenderDocApi(HMODULE module) {
|
||||
g_api = static_cast<RenderDocApi*>(api);
|
||||
|
||||
g_api->SetCaptureFilePathTemplate("_RenderDoc/kyty");
|
||||
static RenderDocInputButton capture_keys[] = {eRENDERDOC_Key_F1};
|
||||
g_api->SetCaptureKeys(capture_keys, 1);
|
||||
g_api->SetCaptureKeys(nullptr, 0);
|
||||
g_api->UnloadCrashHandler();
|
||||
|
||||
char module_path[MAX_PATH] = {};
|
||||
@@ -170,8 +169,7 @@ static bool BindRenderDocApi(HMODULE module) {
|
||||
g_api = static_cast<RenderDocApi*>(api);
|
||||
|
||||
g_api->SetCaptureFilePathTemplate("_RenderDoc/kyty");
|
||||
static RenderDocInputButton capture_keys[] = {eRENDERDOC_Key_F1};
|
||||
g_api->SetCaptureKeys(capture_keys, 1);
|
||||
g_api->SetCaptureKeys(nullptr, 0);
|
||||
g_api->UnloadCrashHandler();
|
||||
|
||||
Dl_info info {};
|
||||
|
||||
@@ -534,13 +534,12 @@ Graphics::ImageInfo BufferAttributeGroup::ImageInfo(const VideoOutBuffer& buffer
|
||||
if (!Graphics::DecodeVideoOutPixelFormat(attribute.pixel_format, pixel_format)) {
|
||||
EXIT("unsupported video-out pixel format: 0x%016" PRIx64 "\n", attribute.pixel_format);
|
||||
}
|
||||
const auto tile_mode =
|
||||
Graphics::Prospero::GpuEnumValue(Graphics::Prospero::TileMode::kRenderTarget);
|
||||
const auto tile_mode = Graphics::Prospero::TileMode::kRenderTarget;
|
||||
const auto pitch =
|
||||
Graphics::TileGetTexturePitch(pixel_format.guest_format, attribute.width, 1, tile_mode);
|
||||
Graphics::TileGetTexturePitch(pixel_format.guest_format, attribute.width, tile_mode);
|
||||
Graphics::TileSizeAlign total {};
|
||||
Graphics::TileGetTextureTotalSize(pixel_format.guest_format, attribute.width, attribute.height,
|
||||
1, pitch, 1, tile_mode, false, total);
|
||||
1, 1, tile_mode, false, total);
|
||||
if (total.size == 0 || total.align != 65536 ||
|
||||
(buffer.data_address & (total.align - 1u)) != 0) {
|
||||
EXIT("invalid video-out surface footprint or alignment\n");
|
||||
@@ -788,14 +787,10 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
||||
while (!token.stop_requested()) {
|
||||
const auto sleep_begin = Common::Timer::QueryPerformanceCounter();
|
||||
if (total_wait > 0) {
|
||||
const auto sleep_end = sleep_begin + static_cast<uint64_t>(total_wait);
|
||||
auto now = sleep_begin;
|
||||
while (!token.stop_requested() && now < sleep_end) {
|
||||
const auto remaining_us = (sleep_end - now) * 1000000u / frequency;
|
||||
Common::Thread::SleepMicro(
|
||||
static_cast<uint32_t>(std::clamp<uint64_t>(remaining_us, 1, 1000)));
|
||||
now = Common::Timer::QueryPerformanceCounter();
|
||||
}
|
||||
const auto remaining_us =
|
||||
(static_cast<uint64_t>(total_wait) * 1000000u + frequency - 1) / frequency;
|
||||
Common::Thread::SleepMicro(static_cast<uint32_t>(
|
||||
std::clamp<uint64_t>(remaining_us, 1, std::numeric_limits<uint32_t>::max())));
|
||||
}
|
||||
if (token.stop_requested()) {
|
||||
break;
|
||||
@@ -817,7 +812,24 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
||||
}
|
||||
|
||||
VblankBegin();
|
||||
const bool presented = m_flip_queue.Flip(0);
|
||||
bool presented = m_flip_queue.Flip(0);
|
||||
if (!presented && m_presenter.NeedsImeRefresh()) {
|
||||
if (auto* frame = m_presenter.PrepareLastFrame(); frame != nullptr) {
|
||||
m_presenter.Present(*frame, true);
|
||||
presented = true;
|
||||
} else {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
{
|
||||
Common::LockGuard lock(m_mutex);
|
||||
width = m_video_out_ctx[0].width;
|
||||
height = m_video_out_ctx[0].height;
|
||||
}
|
||||
auto& blank = m_presenter.PrepareBlankFrame(width, height, true);
|
||||
m_presenter.Present(blank);
|
||||
presented = true;
|
||||
}
|
||||
}
|
||||
if (!presented && total_wait < 0) {
|
||||
bool any_open = false;
|
||||
uint32_t width = 0;
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
#include "graphics/presentation/window/hostInput.h"
|
||||
|
||||
#include "SDL_keyboard.h"
|
||||
#include "SDL_keycode.h"
|
||||
#include "SDL_mouse.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "libs/controller.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ControlInfo {
|
||||
std::string_view name;
|
||||
uint32_t button = 0;
|
||||
Controller::Axis axis = Controller::Axis::AxisMax;
|
||||
bool positive = false;
|
||||
};
|
||||
|
||||
static constexpr std::array CONTROL_INFO = {
|
||||
ControlInfo {"L3", Controller::PAD_BUTTON_L3},
|
||||
ControlInfo {"R3", Controller::PAD_BUTTON_R3},
|
||||
ControlInfo {"Options", Controller::PAD_BUTTON_OPTIONS},
|
||||
ControlInfo {"Up", Controller::PAD_BUTTON_UP},
|
||||
ControlInfo {"Right", Controller::PAD_BUTTON_RIGHT},
|
||||
ControlInfo {"Down", Controller::PAD_BUTTON_DOWN},
|
||||
ControlInfo {"Left", Controller::PAD_BUTTON_LEFT},
|
||||
ControlInfo {"L2", Controller::PAD_BUTTON_L2},
|
||||
ControlInfo {"R2", Controller::PAD_BUTTON_R2},
|
||||
ControlInfo {"L1", Controller::PAD_BUTTON_L1},
|
||||
ControlInfo {"R1", Controller::PAD_BUTTON_R1},
|
||||
ControlInfo {"Triangle", Controller::PAD_BUTTON_TRIANGLE},
|
||||
ControlInfo {"Circle", Controller::PAD_BUTTON_CIRCLE},
|
||||
ControlInfo {"Cross", Controller::PAD_BUTTON_CROSS},
|
||||
ControlInfo {"Square", Controller::PAD_BUTTON_SQUARE},
|
||||
ControlInfo {"TouchPad", Controller::PAD_BUTTON_TOUCH_PAD},
|
||||
ControlInfo {"LeftStickLeft", 0, Controller::Axis::LeftX, false},
|
||||
ControlInfo {"LeftStickRight", 0, Controller::Axis::LeftX, true},
|
||||
ControlInfo {"LeftStickUp", 0, Controller::Axis::LeftY, false},
|
||||
ControlInfo {"LeftStickDown", 0, Controller::Axis::LeftY, true},
|
||||
ControlInfo {"RightStickLeft", 0, Controller::Axis::RightX, false},
|
||||
ControlInfo {"RightStickRight", 0, Controller::Axis::RightX, true},
|
||||
ControlInfo {"RightStickUp", 0, Controller::Axis::RightY, false},
|
||||
ControlInfo {"RightStickDown", 0, Controller::Axis::RightY, true},
|
||||
};
|
||||
|
||||
constexpr std::size_t INVALID_CONTROL = CONTROL_INFO.size();
|
||||
|
||||
struct Binding {
|
||||
SDL_Keycode key = SDLK_UNKNOWN;
|
||||
uint8_t mouse_button = 0;
|
||||
std::size_t control = INVALID_CONTROL;
|
||||
};
|
||||
|
||||
std::size_t ControlFromName(std::string_view name) {
|
||||
const auto info = std::find_if(CONTROL_INFO.begin(), CONTROL_INFO.end(),
|
||||
[name](const auto& item) { return item.name == name; });
|
||||
return static_cast<std::size_t>(std::distance(CONTROL_INFO.begin(), info));
|
||||
}
|
||||
|
||||
const ControlInfo& Info(std::size_t control) {
|
||||
EXIT_IF(control >= CONTROL_INFO.size());
|
||||
return CONTROL_INFO[control];
|
||||
}
|
||||
|
||||
SDL_Keycode NormalizeKey(SDL_Keycode key) {
|
||||
switch (key) {
|
||||
case SDLK_RSHIFT: return SDLK_LSHIFT;
|
||||
case SDLK_RCTRL: return SDLK_LCTRL;
|
||||
case SDLK_RALT: return SDLK_LALT;
|
||||
case SDLK_RGUI: return SDLK_LGUI;
|
||||
default: return key;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MouseButtonFromName(std::string_view name) {
|
||||
static constexpr std::array buttons = {
|
||||
std::pair {std::string_view("Mouse:Left"), uint8_t {SDL_BUTTON_LEFT}},
|
||||
std::pair {std::string_view("Mouse:Middle"), uint8_t {SDL_BUTTON_MIDDLE}},
|
||||
std::pair {std::string_view("Mouse:Right"), uint8_t {SDL_BUTTON_RIGHT}},
|
||||
std::pair {std::string_view("Mouse:X1"), uint8_t {SDL_BUTTON_X1}},
|
||||
std::pair {std::string_view("Mouse:X2"), uint8_t {SDL_BUTTON_X2}},
|
||||
};
|
||||
|
||||
const auto button = std::find_if(buttons.begin(), buttons.end(),
|
||||
[name](const auto& item) { return item.first == name; });
|
||||
return button != buttons.end() ? button->second : 0;
|
||||
}
|
||||
|
||||
bool Conflicts(const Binding& first, const Binding& second) {
|
||||
return first.control == second.control ||
|
||||
(first.key != SDLK_UNKNOWN && first.key == second.key) ||
|
||||
(first.mouse_button != 0 && first.mouse_button == second.mouse_button);
|
||||
}
|
||||
|
||||
class InputMap {
|
||||
public:
|
||||
InputMap(): m_custom(!Config::GetKeymap().empty()) {
|
||||
for (const auto& value: Config::GetKeymap()) {
|
||||
const std::string_view entry = value;
|
||||
const auto split = entry.find('=');
|
||||
|
||||
Binding binding;
|
||||
if (split != std::string_view::npos) {
|
||||
binding.control = ControlFromName(entry.substr(0, split));
|
||||
const auto host_input = entry.substr(split + 1);
|
||||
binding.mouse_button = MouseButtonFromName(host_input);
|
||||
if (binding.mouse_button == 0) {
|
||||
binding.key = NormalizeKey(SDL_GetKeyFromName(std::string(host_input).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
const bool reserved =
|
||||
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1 ||
|
||||
binding.key == SDLK_F11;
|
||||
if (binding.control == INVALID_CONTROL || reserved ||
|
||||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
|
||||
EXIT("Invalid input mapping: %s\n", value.c_str());
|
||||
}
|
||||
Add(binding);
|
||||
}
|
||||
}
|
||||
[[nodiscard]] bool Custom() const { return m_custom; }
|
||||
|
||||
[[nodiscard]] std::size_t FindKey(int key_code) const {
|
||||
key_code = NormalizeKey(static_cast<SDL_Keycode>(key_code));
|
||||
const auto binding =
|
||||
std::find_if(m_bindings.begin(), m_bindings.begin() + m_size,
|
||||
[key_code](const auto& item) { return item.key == key_code; });
|
||||
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t FindMouseButton(uint8_t mouse_button) const {
|
||||
const auto binding = std::find_if(
|
||||
m_bindings.begin(), m_bindings.begin() + m_size,
|
||||
[mouse_button](const auto& item) { return item.mouse_button == mouse_button; });
|
||||
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
|
||||
}
|
||||
|
||||
private:
|
||||
void Add(const Binding& binding) {
|
||||
for (std::size_t index = 0; index < m_size;) {
|
||||
if (Conflicts(m_bindings[index], binding)) {
|
||||
m_bindings[index] = m_bindings[--m_size];
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
EXIT_IF(m_size >= m_bindings.size());
|
||||
m_bindings[m_size++] = binding;
|
||||
}
|
||||
|
||||
std::array<Binding, CONTROL_INFO.size()> m_bindings {};
|
||||
std::size_t m_size = 0;
|
||||
bool m_custom;
|
||||
};
|
||||
|
||||
const InputMap& GetInputMap() {
|
||||
static const InputMap map;
|
||||
return map;
|
||||
}
|
||||
|
||||
void SetButton(uint32_t button, bool down) {
|
||||
if (button == Controller::PAD_BUTTON_L2) {
|
||||
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID,
|
||||
Controller::Axis::TriggerLeft, down ? 255 : 0);
|
||||
} else if (button == Controller::PAD_BUTTON_R2) {
|
||||
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID,
|
||||
Controller::Axis::TriggerRight, down ? 255 : 0);
|
||||
} else if (button != 0) {
|
||||
Controller::ControllerButton(Controller::HOST_INPUT_CONTROLLER_ID, button, down);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DefaultKeyboardButton(int key_code) {
|
||||
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
|
||||
case SDLK_UP: return Controller::PAD_BUTTON_UP;
|
||||
case SDLK_LEFT: return Controller::PAD_BUTTON_LEFT;
|
||||
case SDLK_DOWN: return Controller::PAD_BUTTON_DOWN;
|
||||
case SDLK_RIGHT: return Controller::PAD_BUTTON_RIGHT;
|
||||
case SDLK_j: return Controller::PAD_BUTTON_CROSS;
|
||||
case SDLK_i: return Controller::PAD_BUTTON_TRIANGLE;
|
||||
case SDLK_k: return Controller::PAD_BUTTON_SQUARE;
|
||||
case SDLK_l: return Controller::PAD_BUTTON_CIRCLE;
|
||||
case SDLK_q: return Controller::PAD_BUTTON_L1;
|
||||
case SDLK_e: return Controller::PAD_BUTTON_R1;
|
||||
case SDLK_LSHIFT: return Controller::PAD_BUTTON_L3;
|
||||
case SDLK_LCTRL: return Controller::PAD_BUTTON_R3;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2: return Controller::PAD_BUTTON_OPTIONS;
|
||||
case SDLK_BACKSPACE:
|
||||
case SDLK_TAB: return Controller::PAD_BUTTON_TOUCH_PAD;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct StickKeys {
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool up = false;
|
||||
bool down = false;
|
||||
};
|
||||
|
||||
void SetStickAxis(Controller::Axis axis, bool negative, bool positive) {
|
||||
const int value = negative == positive ? 128 : negative ? 0 : 255;
|
||||
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID, axis, value);
|
||||
}
|
||||
|
||||
void SetControl(std::size_t control, bool down) {
|
||||
if (control == INVALID_CONTROL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& info = Info(control);
|
||||
if (info.button != 0) {
|
||||
SetButton(info.button, down);
|
||||
return;
|
||||
}
|
||||
|
||||
struct AxisKeys {
|
||||
bool negative = false;
|
||||
bool positive = false;
|
||||
};
|
||||
static std::array<AxisKeys, 4> axes;
|
||||
|
||||
const auto axis = static_cast<std::size_t>(info.axis);
|
||||
EXIT_IF(axis >= axes.size());
|
||||
if (info.positive) {
|
||||
axes[axis].positive = down;
|
||||
} else {
|
||||
axes[axis].negative = down;
|
||||
}
|
||||
SetStickAxis(info.axis, axes[axis].negative, axes[axis].positive);
|
||||
}
|
||||
|
||||
void DefaultKeyboardInput(int key_code, bool down) {
|
||||
static StickKeys left;
|
||||
static StickKeys right;
|
||||
|
||||
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
|
||||
case SDLK_a:
|
||||
left.left = down;
|
||||
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
||||
return;
|
||||
case SDLK_d:
|
||||
left.right = down;
|
||||
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
||||
return;
|
||||
case SDLK_w:
|
||||
left.up = down;
|
||||
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
||||
return;
|
||||
case SDLK_s:
|
||||
left.down = down;
|
||||
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
||||
return;
|
||||
case SDLK_f:
|
||||
right.left = down;
|
||||
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
||||
return;
|
||||
case SDLK_h:
|
||||
right.right = down;
|
||||
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
||||
return;
|
||||
case SDLK_t:
|
||||
right.up = down;
|
||||
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
||||
return;
|
||||
case SDLK_g:
|
||||
right.down = down;
|
||||
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
||||
return;
|
||||
default: SetButton(DefaultKeyboardButton(key_code), down); return;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void HostInputInit() {
|
||||
GetInputMap();
|
||||
}
|
||||
|
||||
void HostInputKey(int key_code, bool down) {
|
||||
const auto& map = GetInputMap();
|
||||
if (map.Custom()) {
|
||||
SetControl(map.FindKey(key_code), down);
|
||||
} else {
|
||||
DefaultKeyboardInput(key_code, down);
|
||||
}
|
||||
}
|
||||
|
||||
void HostInputMouseButton(uint8_t mouse_button, bool down) {
|
||||
const auto& map = GetInputMap();
|
||||
if (map.Custom() && mouse_button != 0) {
|
||||
SetControl(map.FindMouseButton(mouse_button), down);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef KYTY_GRAPHICS_PRESENTATION_WINDOW_HOST_INPUT_H_
|
||||
#define KYTY_GRAPHICS_PRESENTATION_WINDOW_HOST_INPUT_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
void HostInputInit();
|
||||
void HostInputKey(int key_code, bool down);
|
||||
void HostInputMouseButton(uint8_t mouse_button, bool down);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif /* KYTY_GRAPHICS_PRESENTATION_WINDOW_HOST_INPUT_H_ */
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "common/systemInfo.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/timer.h"
|
||||
@@ -31,6 +30,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
@@ -329,8 +329,9 @@ public:
|
||||
void Create();
|
||||
void Recreate(bool surface_lost = false);
|
||||
[[nodiscard]] Status AcquireNextImage();
|
||||
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source);
|
||||
void Submit(CommandBuffer& command);
|
||||
[[nodiscard]] bool PrepareImeOverlay();
|
||||
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source, bool draw_ime_overlay);
|
||||
void Submit(CommandBuffer& command);
|
||||
[[nodiscard]] Status Present();
|
||||
|
||||
[[nodiscard]] uint32_t ImageCount() const noexcept {
|
||||
@@ -342,16 +343,17 @@ private:
|
||||
void Destroy();
|
||||
void RefreshSurfaceSize();
|
||||
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
std::unique_ptr<ImeOverlay> m_ime_overlay;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
};
|
||||
|
||||
struct Presenter::Impl {
|
||||
@@ -383,17 +385,20 @@ struct Presenter::Impl {
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eTransferSrc;
|
||||
desc.type = TextureCache::BindingType::VideoOut;
|
||||
|
||||
auto& cache = renderer.GetTextureCache();
|
||||
auto& image = cache.GetImage(cache.FindImage(desc));
|
||||
auto& cache = renderer.GetTextureCache();
|
||||
const auto image_id = cache.FindImage(desc);
|
||||
auto& image = cache.GetImage(image_id);
|
||||
image.usage.video_out = true;
|
||||
cache.UpdateImage(image_id);
|
||||
return image;
|
||||
}
|
||||
|
||||
RenderContext& renderer;
|
||||
WindowContext& window;
|
||||
Swapchain swapchain;
|
||||
CommandScheduler present_scheduler;
|
||||
FramePool frames;
|
||||
RenderContext& renderer;
|
||||
WindowContext& window;
|
||||
Swapchain swapchain;
|
||||
CommandScheduler present_scheduler;
|
||||
FramePool frames;
|
||||
std::atomic<uint64_t> presented_ime_revision {0};
|
||||
};
|
||||
|
||||
void Swapchain::Create() {
|
||||
@@ -522,6 +527,9 @@ void Swapchain::Destroy() {
|
||||
Common::LockGuard queue_lock(graphics.queue_mutex);
|
||||
RequireVulkanSuccess(graphics.queue.waitIdle(), "wait for swapchain queue");
|
||||
}
|
||||
if (m_ime_overlay != nullptr) {
|
||||
m_ime_overlay->ReleaseVulkan();
|
||||
}
|
||||
|
||||
for (const auto semaphore: m_image_acquired) {
|
||||
if (semaphore != nullptr) {
|
||||
@@ -606,7 +614,15 @@ Swapchain::Status Swapchain::AcquireNextImage() {
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source) {
|
||||
bool Swapchain::PrepareImeOverlay() {
|
||||
if (m_ime_overlay == nullptr) {
|
||||
m_ime_overlay = std::make_unique<ImeOverlay>(m_window.graphic_ctx);
|
||||
}
|
||||
return m_ime_overlay->PrepareFrame(m_extent, m_format, ImageCount());
|
||||
}
|
||||
|
||||
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source,
|
||||
bool draw_ime_overlay) {
|
||||
if (source.state.layout != vk::ImageLayout::eTransferSrcOptimal) {
|
||||
EXIT("invalid prepared presentation image, vk_image=%p layout=%d\n",
|
||||
static_cast<void*>(source.image), static_cast<int>(source.state.layout));
|
||||
@@ -652,11 +668,14 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
||||
vk::Filter::eLinear);
|
||||
|
||||
vk::ImageMemoryBarrier to_present {};
|
||||
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
to_present.newLayout = vk::ImageLayout::ePresentSrcKHR;
|
||||
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
to_present.dstAccessMask = draw_ime_overlay ? vk::AccessFlagBits::eColorAttachmentRead |
|
||||
vk::AccessFlagBits::eColorAttachmentWrite
|
||||
: vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
to_present.newLayout = draw_ime_overlay ? vk::ImageLayout::eColorAttachmentOptimal
|
||||
: vk::ImageLayout::ePresentSrcKHR;
|
||||
to_present.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
to_present.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
to_present.image = m_images[m_image_index];
|
||||
@@ -665,9 +684,22 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
||||
to_present.subresourceRange.levelCount = 1;
|
||||
to_present.subresourceRange.baseArrayLayer = 0;
|
||||
to_present.subresourceRange.layerCount = 1;
|
||||
vk_command.pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &to_present);
|
||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
draw_ime_overlay ? vk::PipelineStageFlagBits::eColorAttachmentOutput
|
||||
: vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
|
||||
&to_present);
|
||||
if (draw_ime_overlay) {
|
||||
m_ime_overlay->Record(vk_command, m_image_views[m_image_index]);
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
||||
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||
to_present.newLayout = vk::ImageLayout::ePresentSrcKHR;
|
||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
|
||||
&to_present);
|
||||
}
|
||||
command.End();
|
||||
}
|
||||
|
||||
@@ -772,6 +804,12 @@ bool Presenter::IsGuestPaused() const noexcept {
|
||||
return m_impl->window.loop.paused.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool Presenter::NeedsImeRefresh() const noexcept {
|
||||
const auto visual = GetImeVisualState();
|
||||
return visual.active ||
|
||||
visual.revision != m_impl->presented_ime_revision.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
RenderContext& Presenter::Renderer() const noexcept {
|
||||
return m_impl->renderer;
|
||||
}
|
||||
@@ -789,19 +827,22 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
[&window] {
|
||||
window.UpdateIcon();
|
||||
SDL_ShowWindow(window.window);
|
||||
SDL_RaiseWindow(window.window);
|
||||
},
|
||||
true);
|
||||
#else
|
||||
window.UpdateIcon();
|
||||
|
||||
SDL_ShowWindow(window.window);
|
||||
SDL_RaiseWindow(window.window);
|
||||
#endif
|
||||
|
||||
window.window_hidden = false;
|
||||
m_impl->RecoverSwapchain(Swapchain::Status::Recreate);
|
||||
}
|
||||
|
||||
auto& swapchain = m_impl->swapchain;
|
||||
const auto ime_visual = GetImeVisualState();
|
||||
auto& swapchain = m_impl->swapchain;
|
||||
for (uint32_t attempt = 0; attempt < 2; attempt++) {
|
||||
auto status = swapchain.AcquireNextImage();
|
||||
if (status != Swapchain::Status::Success) {
|
||||
@@ -814,8 +855,9 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
{
|
||||
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
||||
frame.present_commands->WaitForFenceAndReset();
|
||||
auto& command = *frame.present_commands;
|
||||
swapchain.RecordPresentCommands(command, frame.image);
|
||||
auto& command = *frame.present_commands;
|
||||
const bool draw_ime_overlay = ime_visual.active && swapchain.PrepareImeOverlay();
|
||||
swapchain.RecordPresentCommands(command, frame.image, draw_ime_overlay);
|
||||
swapchain.Submit(command);
|
||||
}
|
||||
status = swapchain.Present();
|
||||
@@ -825,6 +867,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
}
|
||||
|
||||
RenderDocOnPresent();
|
||||
m_impl->presented_ime_revision.store(ime_visual.revision, std::memory_order_release);
|
||||
window.UpdateTitle();
|
||||
m_impl->frames.Release(&frame, true);
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "common/systemInfo.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/timer.h"
|
||||
@@ -31,6 +30,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
@@ -259,6 +259,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
||||
LOGF("sampleRateShading is not supported\n");
|
||||
skip_device = true;
|
||||
}
|
||||
if (device_features2.features.depthBiasClamp != VK_TRUE) {
|
||||
LOGF("depthBiasClamp is not supported\n");
|
||||
skip_device = true;
|
||||
}
|
||||
|
||||
if (device_features2.features.fragmentStoresAndAtomics != VK_TRUE) {
|
||||
LOGF("fragmentStoresAndAtomics is not supported\n");
|
||||
@@ -548,6 +552,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
EXIT_NOT_IMPLEMENTED(required_features13.synchronization2 == VK_TRUE &&
|
||||
supported_features13.synchronization2 != VK_TRUE);
|
||||
EXIT_NOT_IMPLEMENTED(supported_features2.features.sampleRateShading != VK_TRUE);
|
||||
EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE);
|
||||
features12.timelineSemaphore = VK_TRUE;
|
||||
|
||||
vk::PhysicalDeviceFeatures device_features {};
|
||||
@@ -563,6 +568,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
device_features.independentBlend = VK_TRUE;
|
||||
device_features.tessellationShader = VK_TRUE;
|
||||
device_features.sampleRateShading = VK_TRUE;
|
||||
device_features.depthBiasClamp = VK_TRUE;
|
||||
graphics.sample_rate_shading_enabled = true;
|
||||
device_features.vertexPipelineStoresAndAtomics =
|
||||
supported_features2.features.vertexPipelineStoresAndAtomics;
|
||||
@@ -600,10 +606,6 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
create_info.flags = {};
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
create_info.enabledLayerCount =
|
||||
(r.enable_validation_layers ? static_cast<uint32_t>(r.required_layers.size()) : 0);
|
||||
create_info.ppEnabledLayerNames =
|
||||
(r.enable_validation_layers ? r.required_layers.data() : nullptr);
|
||||
create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
|
||||
create_info.ppEnabledExtensionNames = device_extensions.data();
|
||||
create_info.pEnabledFeatures = &device_features;
|
||||
@@ -1008,6 +1010,7 @@ void WindowContext::RecreateSurface() {
|
||||
}
|
||||
|
||||
WindowContext::~WindowContext() {
|
||||
ShutdownImeInput();
|
||||
presenter.reset();
|
||||
LibKernel::Memory::InstallGpuResources(nullptr);
|
||||
render_context.reset();
|
||||
|
||||
@@ -31,8 +31,11 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/window/hostInput.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
#include "kytyGitVersion.h"
|
||||
#include "libs/controller.h"
|
||||
#include "loader/systemContent.h"
|
||||
|
||||
@@ -58,8 +61,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
constexpr int KEYBOARD_CONTROLLER_ID = -1000;
|
||||
|
||||
struct EventKeyboard {
|
||||
bool down;
|
||||
bool up;
|
||||
@@ -72,26 +73,6 @@ struct EventKeyboard {
|
||||
double timestamp_seconds;
|
||||
};
|
||||
|
||||
static uint32_t KeyboardKeyToPadButton(int key_code) {
|
||||
switch (key_code) {
|
||||
case SDLK_w: return Controller::PAD_BUTTON_UP;
|
||||
case SDLK_a: return Controller::PAD_BUTTON_LEFT;
|
||||
case SDLK_s: return Controller::PAD_BUTTON_DOWN;
|
||||
case SDLK_d: return Controller::PAD_BUTTON_RIGHT;
|
||||
case SDLK_j: return Controller::PAD_BUTTON_CROSS;
|
||||
case SDLK_i: return Controller::PAD_BUTTON_TRIANGLE;
|
||||
case SDLK_k: return Controller::PAD_BUTTON_SQUARE;
|
||||
case SDLK_l: return Controller::PAD_BUTTON_CIRCLE;
|
||||
case SDLK_q: return Controller::PAD_BUTTON_L1;
|
||||
case SDLK_e: return Controller::PAD_BUTTON_R1;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_RETURN2: return Controller::PAD_BUTTON_OPTIONS;
|
||||
case SDLK_BACKSPACE:
|
||||
case SDLK_TAB: return Controller::PAD_BUTTON_TOUCH_PAD;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ControllerButtonToPadButton(int button) {
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A: return Controller::PAD_BUTTON_CROSS;
|
||||
@@ -238,7 +219,24 @@ static void GameEventTerminate(WindowLoopState& game) {
|
||||
game.need_exit = true;
|
||||
}
|
||||
|
||||
static void ToggleDesktopFullscreen() {
|
||||
if (g_window == nullptr || g_window->window == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto flags = static_cast<uint32_t>(SDL_GetWindowFlags(g_window->window));
|
||||
const bool fullscreen =
|
||||
(flags & static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0u;
|
||||
const auto mode =
|
||||
fullscreen ? 0u : static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
if (SDL_SetWindowFullscreen(g_window->window, mode) != 0) {
|
||||
LOGF("Toggle fullscreen failed: %s\n", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
|
||||
static SDL_Keycode fullscreen_key = SDLK_UNKNOWN;
|
||||
|
||||
#ifdef KYTY_DBG_INPUT
|
||||
LOGF("Key: time = %.04f, %s%s, %s%s, %s, scan = %d, key = %d, mod = %04" PRIx16 "\n",
|
||||
key.timestamp_seconds, (key.down ? "down" : ""), (key.up ? "up" : ""),
|
||||
@@ -256,20 +254,32 @@ static void GameEventKeyboard(WindowLoopState& game, const EventKeyboard& key) {
|
||||
RenderDocRequestCapture();
|
||||
}
|
||||
break;
|
||||
case SDLK_F11:
|
||||
if (!key.repeat) {
|
||||
ToggleDesktopFullscreen();
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
case SDLK_KP_ENTER:
|
||||
if (!key.repeat && (key.mod & KMOD_ALT) != 0) {
|
||||
ToggleDesktopFullscreen();
|
||||
fullscreen_key = key.key_code;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto button = KeyboardKeyToPadButton(key.key_code);
|
||||
if (button != 0 && (key.down || key.up) && !key.repeat) {
|
||||
static bool keyboard_connected = false;
|
||||
if (!keyboard_connected) {
|
||||
Controller::ControllerConnect(KEYBOARD_CONTROLLER_ID);
|
||||
keyboard_connected = true;
|
||||
}
|
||||
Controller::ControllerButton(KEYBOARD_CONTROLLER_ID, button, key.down);
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool fullscreen_key_event =
|
||||
fullscreen_key != SDLK_UNKNOWN && key.key_code == fullscreen_key;
|
||||
if (fullscreen_key_event && key.up) {
|
||||
fullscreen_key = SDLK_UNKNOWN;
|
||||
}
|
||||
if ((key.down || key.up) && !key.repeat && !fullscreen_key_event) {
|
||||
HostInputKey(key.key_code, key.down);
|
||||
}
|
||||
}
|
||||
|
||||
static void GameEventMouse([[maybe_unused]] const EventMouse& mb) {
|
||||
@@ -291,6 +301,23 @@ static void GameEventMouse([[maybe_unused]] const EventMouse& mb) {
|
||||
mb.x, mb.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mb.down || mb.up) {
|
||||
uint8_t mouse_button = 0;
|
||||
if (mb.left) {
|
||||
mouse_button = SDL_BUTTON_LEFT;
|
||||
} else if (mb.middle) {
|
||||
mouse_button = SDL_BUTTON_MIDDLE;
|
||||
} else if (mb.right) {
|
||||
mouse_button = SDL_BUTTON_RIGHT;
|
||||
} else if (mb.x1) {
|
||||
mouse_button = SDL_BUTTON_X1;
|
||||
} else if (mb.x2) {
|
||||
mouse_button = SDL_BUTTON_X2;
|
||||
}
|
||||
|
||||
HostInputMouseButton(mouse_button, mb.down);
|
||||
}
|
||||
}
|
||||
|
||||
static void GameEventFinger([[maybe_unused]] const EventFinger& f) {
|
||||
@@ -484,6 +511,9 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
auto& game = loop;
|
||||
auto* event = &game.event;
|
||||
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
|
||||
if (ProcessImeInput(*event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_QUIT: GameEventQuit(game); break;
|
||||
@@ -756,26 +786,18 @@ void WindowContext::Run() {
|
||||
#if defined(__APPLE__)
|
||||
DrainMainThreadTasks();
|
||||
#endif
|
||||
if (SDL_PollEvent(&loop.event) != 0) {
|
||||
ProcessEvent(timer.GetTimeS());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loop.paused.load(std::memory_order_acquire)) {
|
||||
if (!timer.IsPaused()) {
|
||||
timer.Pause();
|
||||
}
|
||||
if (SDL_WaitEvent(&loop.event) == 0) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
ProcessEvent(timer.GetTimeS());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timer.IsPaused()) {
|
||||
} else if (timer.IsPaused()) {
|
||||
timer.Resume();
|
||||
}
|
||||
Common::Thread::SleepMicro(1000);
|
||||
|
||||
if (SDL_WaitEvent(&loop.event) == 0) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
ProcessEvent(timer.GetTimeS());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,10 +816,15 @@ static void WindowCreate(WindowContext& context) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
HostInputInit();
|
||||
InitializeImeInput();
|
||||
|
||||
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
||||
|
||||
uint32_t window_flags = KYTY_SDL_WINDOW_FLAGS;
|
||||
if (Config::FullscreenEnabled()) {
|
||||
window_flags |= static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
// macOS 26 window chrome (CoreUI asset decode, SwiftUI titlebar) has been observed
|
||||
// throwing NSExceptions under Rosetta during the first CATransaction commit. A
|
||||
@@ -937,6 +964,14 @@ void WindowContext::UpdateTitle() {
|
||||
static uint64_t fps_frames = 0;
|
||||
static double current_fps = 0.0;
|
||||
|
||||
#if KYTY_BUILD == KYTY_BUILD_DEBUG
|
||||
static constexpr auto build_type = "Debug";
|
||||
#elif KYTY_BUILD == KYTY_BUILD_RELEASE
|
||||
static constexpr auto build_type = "Release";
|
||||
#else
|
||||
static constexpr auto build_type = "Unknown";
|
||||
#endif
|
||||
|
||||
const auto now = Common::Timer::QueryPerformanceCounter();
|
||||
const auto frequency = Common::Timer::QueryPerformanceFrequency();
|
||||
frame_num++;
|
||||
@@ -948,11 +983,11 @@ void WindowContext::UpdateTitle() {
|
||||
fps_frames = 0;
|
||||
}
|
||||
|
||||
auto fps =
|
||||
fmt::format("{}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", (has_title ? title : ""),
|
||||
(has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""),
|
||||
(has_app_ver ? " " : ""), device_name, processor_name, frame_num, current_fps);
|
||||
auto fps = fmt::format(
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type,
|
||||
(has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
||||
device_name, processor_name, frame_num, current_fps);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing.
|
||||
|
||||
@@ -84,6 +84,8 @@ bool InstructionMaySplitSpirvBlock(const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicAndU32:
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
case IR::Opcode::FlatLoadUbyte:
|
||||
case IR::Opcode::FlatLoadSbyte:
|
||||
case IR::Opcode::FlatLoadUshort:
|
||||
|
||||
@@ -57,6 +57,8 @@ constexpr MemoryOpcodeInfo MUBUF_OPS[] = {
|
||||
{0x39u, Opcode::BufferAtomicAnd, 1, 32},
|
||||
{0x3au, Opcode::BufferAtomicOr, 1, 32},
|
||||
{0x3bu, Opcode::BufferAtomicXor, 1, 32},
|
||||
{0x3fu, Opcode::BufferAtomicFMin, 1, 32},
|
||||
{0x40u, Opcode::BufferAtomicFMax, 1, 32},
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo MTBUF_OPS[] = {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
|
||||
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
};
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr const Entry* FindOpcode(const Entry (&table)[N], uint32_t encoding) {
|
||||
for (const auto& entry: table) {
|
||||
if (entry.encoding == encoding) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr Opcode LookupOpcode(const Entry (&table)[N], uint32_t encoding) {
|
||||
const auto* entry = FindOpcode(table, encoding);
|
||||
return entry != nullptr ? entry->decoded : Opcode::Unsupported;
|
||||
}
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr bool HasUniqueEncodings(const Entry (&table)[N]) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
for (size_t j = i + 1; j < N; j++) {
|
||||
if (table[i].encoding == table[j].encoding) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail
|
||||
|
||||
#endif // EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
@@ -1,14 +1,11 @@
|
||||
#include "graphics/shader/recompiler/decompiler/ScalarAluOps.h"
|
||||
|
||||
#include <iterator>
|
||||
#include "graphics/shader/recompiler/decompiler/OpcodeTable.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t opcode = 0;
|
||||
Opcode ir = Opcode::Unknown;
|
||||
};
|
||||
using Detail::OpcodeMap;
|
||||
|
||||
constexpr OpcodeMap SOP2_OPS[] = {
|
||||
{0x00u, Opcode::SAddU32}, {0x01u, Opcode::SSubU32}, {0x02u, Opcode::SAddI32},
|
||||
@@ -58,7 +55,8 @@ constexpr OpcodeMap SOPC_OPS[] = {
|
||||
{0x03u, Opcode::SCmpGeI32}, {0x04u, Opcode::SCmpLtI32}, {0x05u, Opcode::SCmpLeI32},
|
||||
{0x06u, Opcode::SCmpEqU32}, {0x07u, Opcode::SCmpLgU32}, {0x08u, Opcode::SCmpGtU32},
|
||||
{0x09u, Opcode::SCmpGeU32}, {0x0au, Opcode::SCmpLtU32}, {0x0bu, Opcode::SCmpLeU32},
|
||||
{0x0cu, Opcode::SBitcmp0B32}, {0x0du, Opcode::SBitcmp1B32}, {0x13u, Opcode::SCmpLgU64},
|
||||
{0x0cu, Opcode::SBitcmp0B32}, {0x0du, Opcode::SBitcmp1B32}, {0x12u, Opcode::SCmpEqU64},
|
||||
{0x13u, Opcode::SCmpLgU64},
|
||||
};
|
||||
|
||||
constexpr OpcodeMap SOPK_OPS[] = {
|
||||
@@ -78,15 +76,11 @@ constexpr OpcodeMap SOPP_OPS[] = {
|
||||
{0x0au, Opcode::SBarrier}, {0x0cu, Opcode::SWaitcnt}, {0x0eu, Opcode::SSleep},
|
||||
{0x10u, Opcode::SSendmsg}, {0x16u, Opcode::STtraceData}, {0x20u, Opcode::SInstPrefetch},
|
||||
};
|
||||
|
||||
Opcode Lookup(const OpcodeMap* ops, uint32_t count, uint32_t opcode) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (ops[i].opcode == opcode) {
|
||||
return ops[i].ir;
|
||||
}
|
||||
}
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
static_assert(Detail::HasUniqueEncodings(SOP1_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOP2_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPK_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPC_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPP_OPS));
|
||||
|
||||
bool DecodeBinarySources(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst, uint32_t ssrc0, uint32_t ssrc1, std::string* error) {
|
||||
@@ -111,7 +105,7 @@ bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOP1_OPS, static_cast<uint32_t>(std::size(SOP1_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOP1_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -153,7 +147,7 @@ bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOP2_OPS, static_cast<uint32_t>(std::size(SOP2_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOP2_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -180,7 +174,7 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPK;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPK_OPS, static_cast<uint32_t>(std::size(SOPK_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPK_OPS, opcode);
|
||||
inst.src0.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src0.signed_val = imm;
|
||||
inst.src0.value = static_cast<uint32_t>(imm);
|
||||
@@ -237,7 +231,7 @@ bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPC_OPS, static_cast<uint32_t>(std::size(SOPC_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPC_OPS, opcode);
|
||||
inst.dst.kind = OperandKind::Scc;
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
@@ -261,7 +255,7 @@ bool DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPP;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPP_OPS, static_cast<uint32_t>(std::size(SOPP_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPP_OPS, opcode);
|
||||
inst.src0.kind = OperandKind::LiteralConstant;
|
||||
inst.src0.value = simm;
|
||||
inst.src0.signed_val = static_cast<int16_t>(simm);
|
||||
|
||||
@@ -77,6 +77,16 @@ std::string FormatBinary(const Instruction& inst) {
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string FormatSources(const Instruction& inst) {
|
||||
std::string text = fmt::format("0x{:08x}: {}", inst.pc, OpcodeToString(inst.opcode));
|
||||
const Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2, &inst.src3};
|
||||
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
|
||||
text += i == 0 ? " " : ", ";
|
||||
text += OperandToString(*sources[i]);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string FormatMemory(const Instruction& inst) {
|
||||
std::string text = fmt::format("0x{:08x}: {} {}", inst.pc, OpcodeToString(inst.opcode).c_str(),
|
||||
OperandToString(inst.dst).c_str());
|
||||
@@ -511,6 +521,7 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::SCmpGeU32: return "s_cmp_ge_u32";
|
||||
case Opcode::SCmpLtU32: return "s_cmp_lt_u32";
|
||||
case Opcode::SCmpLeU32: return "s_cmp_le_u32";
|
||||
case Opcode::SCmpEqU64: return "s_cmp_eq_u64";
|
||||
case Opcode::SCmpLgU64: return "s_cmp_lg_u64";
|
||||
case Opcode::VNop: return "v_nop";
|
||||
case Opcode::VMovB32: return "v_mov_b32";
|
||||
@@ -766,6 +777,7 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::VCmpNeU32: return "v_cmp_ne_u32";
|
||||
case Opcode::VCmpGeU32: return "v_cmp_ge_u32";
|
||||
case Opcode::VCmpTU32: return "v_cmp_t_u32";
|
||||
case Opcode::VCmpEqI64: return "v_cmp_eq_i64";
|
||||
case Opcode::VCmpNeU64: return "v_cmp_ne_u64";
|
||||
case Opcode::VCmpxLtU32: return "v_cmpx_lt_u32";
|
||||
case Opcode::VCmpxEqU32: return "v_cmpx_eq_u32";
|
||||
@@ -823,6 +835,8 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::BufferAtomicAnd: return "buffer_atomic_and";
|
||||
case Opcode::BufferAtomicOr: return "buffer_atomic_or";
|
||||
case Opcode::BufferAtomicXor: return "buffer_atomic_xor";
|
||||
case Opcode::BufferAtomicFMin: return "buffer_atomic_fmin";
|
||||
case Opcode::BufferAtomicFMax: return "buffer_atomic_fmax";
|
||||
case Opcode::FlatLoadUbyte: return "flat_load_ubyte";
|
||||
case Opcode::FlatLoadSbyte: return "flat_load_sbyte";
|
||||
case Opcode::FlatLoadUshort: return "flat_load_ushort";
|
||||
@@ -983,6 +997,9 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
inst.pc, FamilyToString(inst.family).c_str(), inst.opcode_id,
|
||||
RawWordsToString(inst).c_str(), inst.unsupported_reason.c_str());
|
||||
}
|
||||
if (inst.family == Family::SOPC) {
|
||||
return WithUnsupportedReason(inst, FormatSources(inst));
|
||||
}
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Opcode::SMovB32:
|
||||
@@ -1110,6 +1127,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BufferAtomicAnd:
|
||||
case Opcode::BufferAtomicOr:
|
||||
case Opcode::BufferAtomicXor:
|
||||
case Opcode::BufferAtomicFMin:
|
||||
case Opcode::BufferAtomicFMax:
|
||||
case Opcode::BufferLoadSbyte:
|
||||
case Opcode::BufferLoadSshort:
|
||||
case Opcode::FlatLoadUbyte:
|
||||
@@ -1171,25 +1190,6 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DsWriteB128:
|
||||
case Opcode::DsWriteAddtidB32:
|
||||
case Opcode::DsReadAddtidB32: return WithUnsupportedReason(inst, FormatMemory(inst));
|
||||
case Opcode::SCmpEqU32:
|
||||
case Opcode::SCmpEqI32:
|
||||
case Opcode::SCmpLgU32:
|
||||
case Opcode::SCmpLgI32:
|
||||
case Opcode::SCmpGtU32:
|
||||
case Opcode::SCmpGtI32:
|
||||
case Opcode::SCmpGeU32:
|
||||
case Opcode::SCmpGeI32:
|
||||
case Opcode::SCmpLtU32:
|
||||
case Opcode::SCmpLtI32:
|
||||
case Opcode::SCmpLeU32:
|
||||
case Opcode::SCmpLeI32:
|
||||
case Opcode::SBitcmp0B32:
|
||||
case Opcode::SBitcmp1B32:
|
||||
case Opcode::SCmpLgU64:
|
||||
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc,
|
||||
OpcodeToString(inst.opcode).c_str(),
|
||||
OperandToString(inst.src0).c_str(),
|
||||
OperandToString(inst.src1).c_str()));
|
||||
default: return WithUnsupportedReason(inst, FormatBinary(inst));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ enum class Opcode {
|
||||
SCmpGeU32,
|
||||
SCmpLtU32,
|
||||
SCmpLeU32,
|
||||
SCmpEqU64,
|
||||
SCmpLgU64,
|
||||
|
||||
VNop,
|
||||
@@ -374,6 +375,7 @@ enum class Opcode {
|
||||
VCmpNeU32,
|
||||
VCmpGeU32,
|
||||
VCmpTU32,
|
||||
VCmpEqI64,
|
||||
VCmpNeU64,
|
||||
VCmpxLtU32,
|
||||
VCmpxEqU32,
|
||||
@@ -432,6 +434,8 @@ enum class Opcode {
|
||||
BufferAtomicAnd,
|
||||
BufferAtomicOr,
|
||||
BufferAtomicXor,
|
||||
BufferAtomicFMin,
|
||||
BufferAtomicFMax,
|
||||
FlatLoadUbyte,
|
||||
FlatLoadSbyte,
|
||||
FlatLoadUshort,
|
||||
|
||||
@@ -1,43 +1,91 @@
|
||||
#include "graphics/shader/recompiler/decompiler/VectorAluOps.h"
|
||||
|
||||
#include "graphics/shader/recompiler/decompiler/OpcodeTable.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <iterator>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t opcode = 0;
|
||||
Opcode ir = Opcode::Unknown;
|
||||
using Detail::OpcodeMap;
|
||||
|
||||
// These profiles describe the selectors that the current lowering implements. They are not
|
||||
// architectural SDWA legality classes.
|
||||
enum class Vop2SdwaProfile {
|
||||
None,
|
||||
Cndmask,
|
||||
Float32,
|
||||
Float16,
|
||||
IntegerFullDestination,
|
||||
IntegerPartialDestination,
|
||||
ReverseLogicalRight,
|
||||
ReverseLogicalLeft,
|
||||
Bitwise,
|
||||
Count,
|
||||
};
|
||||
|
||||
constexpr OpcodeMap VOP2_OPS[] = {
|
||||
{0x01u, Opcode::VCndmaskB32}, {0x02u, Opcode::VDot2cF32F16},
|
||||
{0x03u, Opcode::VAddF32}, {0x04u, Opcode::VSubF32},
|
||||
{0x05u, Opcode::VSubrevF32}, {0x08u, Opcode::VMulF32},
|
||||
{0x09u, Opcode::VMulI32I24}, {0x0bu, Opcode::VMulU32U24},
|
||||
{0x0fu, Opcode::VMinF32}, {0x10u, Opcode::VMaxF32},
|
||||
{0x11u, Opcode::VMinI32}, {0x12u, Opcode::VMaxI32},
|
||||
{0x13u, Opcode::VMinU32}, {0x14u, Opcode::VMaxU32},
|
||||
{0x15u, Opcode::VLshrB32}, {0x16u, Opcode::VLshrrevB32},
|
||||
{0x17u, Opcode::VAshrI32}, {0x18u, Opcode::VAshrrevI32},
|
||||
{0x19u, Opcode::VLshlB32}, {0x1au, Opcode::VLshlrevB32},
|
||||
{0x1bu, Opcode::VAndB32}, {0x1cu, Opcode::VOrB32},
|
||||
{0x1du, Opcode::VXorB32}, {0x1eu, Opcode::VXnorB32},
|
||||
{0x1fu, Opcode::VMacF32}, {0x20u, Opcode::VMadmkF32},
|
||||
{0x21u, Opcode::VMadakF32}, {0x22u, Opcode::VBcntU32B32},
|
||||
{0x23u, Opcode::VMbcntLoU32B32}, {0x24u, Opcode::VMbcntHiU32B32},
|
||||
{0x25u, Opcode::VAddNcU32}, {0x28u, Opcode::VAddcU32},
|
||||
{0x26u, Opcode::VSubNcU32}, {0x27u, Opcode::VSubrevNcU32},
|
||||
{0x2bu, Opcode::VMacF32}, {0x2cu, Opcode::VMadmkF32},
|
||||
{0x2du, Opcode::VMadakF32}, {0x2fu, Opcode::VCvtPkrtzF16F32},
|
||||
{0x32u, Opcode::VAddF16}, {0x33u, Opcode::VSubF16},
|
||||
{0x34u, Opcode::VSubrevF16}, {0x35u, Opcode::VMulF16},
|
||||
{0x36u, Opcode::VFmacF16}, {0x37u, Opcode::VFmamkF16},
|
||||
{0x38u, Opcode::VFmaakF16}, {0x39u, Opcode::VMaxF16},
|
||||
{0x3au, Opcode::VMinF16}, {0x3cu, Opcode::VPkFmacF16},
|
||||
struct Vop2OpcodeInfo {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
Vop2SdwaProfile sdwa_profile = Vop2SdwaProfile::None;
|
||||
};
|
||||
|
||||
constexpr Vop2OpcodeInfo VOP2_OPS[] = {
|
||||
{0x01u, Opcode::VCndmaskB32, Vop2SdwaProfile::Cndmask},
|
||||
{0x02u, Opcode::VDot2cF32F16},
|
||||
{0x03u, Opcode::VAddF32, Vop2SdwaProfile::Float32},
|
||||
{0x04u, Opcode::VSubF32, Vop2SdwaProfile::Float32},
|
||||
{0x05u, Opcode::VSubrevF32},
|
||||
{0x08u, Opcode::VMulF32, Vop2SdwaProfile::Float32},
|
||||
{0x09u, Opcode::VMulI32I24, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x0bu, Opcode::VMulU32U24, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x0fu, Opcode::VMinF32},
|
||||
{0x10u, Opcode::VMaxF32},
|
||||
{0x11u, Opcode::VMinI32},
|
||||
{0x12u, Opcode::VMaxI32},
|
||||
{0x13u, Opcode::VMinU32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x14u, Opcode::VMaxU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x15u, Opcode::VLshrB32},
|
||||
{0x16u, Opcode::VLshrrevB32, Vop2SdwaProfile::ReverseLogicalRight},
|
||||
{0x17u, Opcode::VAshrI32},
|
||||
{0x18u, Opcode::VAshrrevI32},
|
||||
{0x19u, Opcode::VLshlB32},
|
||||
{0x1au, Opcode::VLshlrevB32, Vop2SdwaProfile::ReverseLogicalLeft},
|
||||
{0x1bu, Opcode::VAndB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1cu, Opcode::VOrB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1du, Opcode::VXorB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1eu, Opcode::VXnorB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1fu, Opcode::VMacF32},
|
||||
{0x20u, Opcode::VMadmkF32},
|
||||
{0x21u, Opcode::VMadakF32},
|
||||
{0x22u, Opcode::VBcntU32B32},
|
||||
{0x23u, Opcode::VMbcntLoU32B32},
|
||||
{0x24u, Opcode::VMbcntHiU32B32},
|
||||
{0x25u, Opcode::VAddNcU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x28u, Opcode::VAddcU32},
|
||||
{0x26u, Opcode::VSubNcU32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x27u, Opcode::VSubrevNcU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x2bu, Opcode::VMacF32},
|
||||
{0x2cu, Opcode::VMadmkF32},
|
||||
{0x2du, Opcode::VMadakF32},
|
||||
{0x2fu, Opcode::VCvtPkrtzF16F32},
|
||||
{0x32u, Opcode::VAddF16, Vop2SdwaProfile::Float16},
|
||||
{0x33u, Opcode::VSubF16, Vop2SdwaProfile::Float16},
|
||||
{0x34u, Opcode::VSubrevF16, Vop2SdwaProfile::Float16},
|
||||
{0x35u, Opcode::VMulF16, Vop2SdwaProfile::Float16},
|
||||
{0x36u, Opcode::VFmacF16},
|
||||
{0x37u, Opcode::VFmamkF16},
|
||||
{0x38u, Opcode::VFmaakF16},
|
||||
{0x39u, Opcode::VMaxF16, Vop2SdwaProfile::Float16},
|
||||
{0x3au, Opcode::VMinF16, Vop2SdwaProfile::Float16},
|
||||
{0x3cu, Opcode::VPkFmacF16},
|
||||
};
|
||||
static_assert(Detail::HasUniqueEncodings(VOP2_OPS));
|
||||
|
||||
constexpr Opcode LookupVop2Opcode(uint32_t encoding) {
|
||||
return Detail::LookupOpcode(VOP2_OPS, encoding);
|
||||
}
|
||||
|
||||
constexpr OpcodeMap VOP1_OPS[] = {
|
||||
{0x00u, Opcode::VNop},
|
||||
{0x01u, Opcode::VMovB32},
|
||||
@@ -135,37 +183,59 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPS[] = {
|
||||
{0x5eu, Opcode::VRndneF16},
|
||||
};
|
||||
|
||||
constexpr OpcodeMap VOPC_OPS[] = {
|
||||
{0x00u, Opcode::VCmpFF32}, {0x01u, Opcode::VCmpLtF32}, {0x02u, Opcode::VCmpEqF32},
|
||||
{0x03u, Opcode::VCmpLeF32}, {0x04u, Opcode::VCmpGtF32}, {0x05u, Opcode::VCmpLgF32},
|
||||
{0x06u, Opcode::VCmpGeF32}, {0x07u, Opcode::VCmpOF32}, {0x08u, Opcode::VCmpUF32},
|
||||
{0x09u, Opcode::VCmpNgeF32}, {0x0au, Opcode::VCmpNlgF32}, {0x0bu, Opcode::VCmpNgtF32},
|
||||
{0x0cu, Opcode::VCmpNleF32}, {0x0du, Opcode::VCmpNeqF32}, {0x0eu, Opcode::VCmpNltF32},
|
||||
{0x0fu, Opcode::VCmpTruF32}, {0x11u, Opcode::VCmpxLtF32}, {0x12u, Opcode::VCmpxEqF32},
|
||||
{0x13u, Opcode::VCmpxLeF32}, {0x14u, Opcode::VCmpxGtF32}, {0x15u, Opcode::VCmpxLgF32},
|
||||
{0x16u, Opcode::VCmpxGeF32}, {0x19u, Opcode::VCmpxNgeF32}, {0x1au, Opcode::VCmpxNlgF32},
|
||||
{0x1bu, Opcode::VCmpxNgtF32}, {0x1cu, Opcode::VCmpxNleF32}, {0x1du, Opcode::VCmpxNeqF32},
|
||||
{0x1eu, Opcode::VCmpxNltF32}, {0x80u, Opcode::VCmpFI32}, {0x81u, Opcode::VCmpLtI32},
|
||||
{0x82u, Opcode::VCmpEqI32}, {0x83u, Opcode::VCmpLeI32}, {0x84u, Opcode::VCmpGtI32},
|
||||
{0x85u, Opcode::VCmpNeI32}, {0x86u, Opcode::VCmpGeI32}, {0x87u, Opcode::VCmpTI32},
|
||||
{0x88u, Opcode::VCmpClassF32}, {0x89u, Opcode::VCmpLtI16}, {0x8au, Opcode::VCmpEqI16},
|
||||
{0x8bu, Opcode::VCmpLeI16}, {0x8cu, Opcode::VCmpGtI16}, {0x8du, Opcode::VCmpNeI16},
|
||||
{0x8eu, Opcode::VCmpGeI16}, {0x91u, Opcode::VCmpxLtI32}, {0x92u, Opcode::VCmpxEqI32},
|
||||
{0x93u, Opcode::VCmpxLeI32}, {0x94u, Opcode::VCmpxGtI32}, {0x95u, Opcode::VCmpxNeI32},
|
||||
{0x96u, Opcode::VCmpxGeI32}, {0xa9u, Opcode::VCmpLtU16}, {0xaau, Opcode::VCmpEqU16},
|
||||
{0xabu, Opcode::VCmpLeU16}, {0xacu, Opcode::VCmpGtU16}, {0xadu, Opcode::VCmpNeU16},
|
||||
{0xaeu, Opcode::VCmpGeU16}, {0xc0u, Opcode::VCmpFU32}, {0xc1u, Opcode::VCmpLtU32},
|
||||
{0xc2u, Opcode::VCmpEqU32}, {0xc3u, Opcode::VCmpLeU32}, {0xc4u, Opcode::VCmpGtU32},
|
||||
{0xc5u, Opcode::VCmpNeU32}, {0xc6u, Opcode::VCmpGeU32}, {0xc7u, Opcode::VCmpTU32},
|
||||
{0xd1u, Opcode::VCmpxLtU32}, {0xd2u, Opcode::VCmpxEqU32}, {0xd3u, Opcode::VCmpxLeU32},
|
||||
{0xd4u, Opcode::VCmpxGtU32}, {0xd5u, Opcode::VCmpxNeU32}, {0xd6u, Opcode::VCmpxGeU32},
|
||||
{0xe5u, Opcode::VCmpNeU64}, {0xc9u, Opcode::VCmpLtF16}, {0xcau, Opcode::VCmpEqF16},
|
||||
{0xcbu, Opcode::VCmpLeF16}, {0xccu, Opcode::VCmpGtF16}, {0xcdu, Opcode::VCmpLgF16},
|
||||
{0xceu, Opcode::VCmpGeF16}, {0xedu, Opcode::VCmpNeqF16}, {0xd9u, Opcode::VCmpxLtF16},
|
||||
{0xdau, Opcode::VCmpxEqF16}, {0xdbu, Opcode::VCmpxLeF16}, {0xdcu, Opcode::VCmpxGtF16},
|
||||
{0xdeu, Opcode::VCmpxGeF16}, {0xfdu, Opcode::VCmpxNeqF16}, {0xfeu, Opcode::VCmpxNltF16},
|
||||
struct VopcOpcodeInfo {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
bool supports_dpp = true;
|
||||
};
|
||||
|
||||
constexpr VopcOpcodeInfo VOPC_OPS[] = {
|
||||
{0x00u, Opcode::VCmpFF32}, {0x01u, Opcode::VCmpLtF32},
|
||||
{0x02u, Opcode::VCmpEqF32}, {0x03u, Opcode::VCmpLeF32},
|
||||
{0x04u, Opcode::VCmpGtF32}, {0x05u, Opcode::VCmpLgF32},
|
||||
{0x06u, Opcode::VCmpGeF32}, {0x07u, Opcode::VCmpOF32},
|
||||
{0x08u, Opcode::VCmpUF32}, {0x09u, Opcode::VCmpNgeF32},
|
||||
{0x0au, Opcode::VCmpNlgF32}, {0x0bu, Opcode::VCmpNgtF32},
|
||||
{0x0cu, Opcode::VCmpNleF32}, {0x0du, Opcode::VCmpNeqF32},
|
||||
{0x0eu, Opcode::VCmpNltF32}, {0x0fu, Opcode::VCmpTruF32},
|
||||
{0x11u, Opcode::VCmpxLtF32}, {0x12u, Opcode::VCmpxEqF32},
|
||||
{0x13u, Opcode::VCmpxLeF32}, {0x14u, Opcode::VCmpxGtF32},
|
||||
{0x15u, Opcode::VCmpxLgF32}, {0x16u, Opcode::VCmpxGeF32},
|
||||
{0x19u, Opcode::VCmpxNgeF32}, {0x1au, Opcode::VCmpxNlgF32},
|
||||
{0x1bu, Opcode::VCmpxNgtF32}, {0x1cu, Opcode::VCmpxNleF32},
|
||||
{0x1du, Opcode::VCmpxNeqF32}, {0x1eu, Opcode::VCmpxNltF32},
|
||||
{0x80u, Opcode::VCmpFI32}, {0x81u, Opcode::VCmpLtI32},
|
||||
{0x82u, Opcode::VCmpEqI32}, {0x83u, Opcode::VCmpLeI32},
|
||||
{0x84u, Opcode::VCmpGtI32}, {0x85u, Opcode::VCmpNeI32},
|
||||
{0x86u, Opcode::VCmpGeI32}, {0x87u, Opcode::VCmpTI32},
|
||||
{0x88u, Opcode::VCmpClassF32}, {0x89u, Opcode::VCmpLtI16},
|
||||
{0x8au, Opcode::VCmpEqI16}, {0x8bu, Opcode::VCmpLeI16},
|
||||
{0x8cu, Opcode::VCmpGtI16}, {0x8du, Opcode::VCmpNeI16},
|
||||
{0x8eu, Opcode::VCmpGeI16}, {0x91u, Opcode::VCmpxLtI32},
|
||||
{0x92u, Opcode::VCmpxEqI32}, {0x93u, Opcode::VCmpxLeI32},
|
||||
{0x94u, Opcode::VCmpxGtI32}, {0x95u, Opcode::VCmpxNeI32},
|
||||
{0x96u, Opcode::VCmpxGeI32}, {0xa9u, Opcode::VCmpLtU16},
|
||||
{0xaau, Opcode::VCmpEqU16}, {0xabu, Opcode::VCmpLeU16},
|
||||
{0xacu, Opcode::VCmpGtU16}, {0xadu, Opcode::VCmpNeU16},
|
||||
{0xaeu, Opcode::VCmpGeU16}, {0xc0u, Opcode::VCmpFU32},
|
||||
{0xc1u, Opcode::VCmpLtU32}, {0xc2u, Opcode::VCmpEqU32},
|
||||
{0xc3u, Opcode::VCmpLeU32}, {0xc4u, Opcode::VCmpGtU32},
|
||||
{0xc5u, Opcode::VCmpNeU32}, {0xc6u, Opcode::VCmpGeU32},
|
||||
{0xc7u, Opcode::VCmpTU32}, {0xa2u, Opcode::VCmpEqI64, false},
|
||||
{0xd1u, Opcode::VCmpxLtU32}, {0xd2u, Opcode::VCmpxEqU32},
|
||||
{0xd3u, Opcode::VCmpxLeU32}, {0xd4u, Opcode::VCmpxGtU32},
|
||||
{0xd5u, Opcode::VCmpxNeU32}, {0xd6u, Opcode::VCmpxGeU32},
|
||||
{0xe5u, Opcode::VCmpNeU64, false}, {0xc9u, Opcode::VCmpLtF16},
|
||||
{0xcau, Opcode::VCmpEqF16}, {0xcbu, Opcode::VCmpLeF16},
|
||||
{0xccu, Opcode::VCmpGtF16}, {0xcdu, Opcode::VCmpLgF16},
|
||||
{0xceu, Opcode::VCmpGeF16}, {0xedu, Opcode::VCmpNeqF16},
|
||||
{0xd9u, Opcode::VCmpxLtF16}, {0xdau, Opcode::VCmpxEqF16},
|
||||
{0xdbu, Opcode::VCmpxLeF16}, {0xdcu, Opcode::VCmpxGtF16},
|
||||
{0xdeu, Opcode::VCmpxGeF16}, {0xfdu, Opcode::VCmpxNeqF16},
|
||||
{0xfeu, Opcode::VCmpxNltF16},
|
||||
};
|
||||
static_assert(Detail::HasUniqueEncodings(VOPC_OPS));
|
||||
|
||||
constexpr OpcodeMap VOP3_OPS[] = {
|
||||
{0x141u, Opcode::VMadF32}, {0x142u, Opcode::VMadI32I24},
|
||||
{0x143u, Opcode::VMadU32U24}, {0x176u, Opcode::VMadU64U32},
|
||||
@@ -214,16 +284,6 @@ constexpr OpcodeMap VOP3P_OPS[] = {
|
||||
{0x22u, Opcode::VMadMixhiF16},
|
||||
};
|
||||
|
||||
Opcode Lookup(const OpcodeMap* ops, uint32_t count, uint32_t opcode) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const auto& op = ops[i];
|
||||
if (op.opcode == opcode) {
|
||||
return op.ir;
|
||||
}
|
||||
}
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
|
||||
bool IsVop2LiteralMadOpcode(uint32_t opcode) {
|
||||
return opcode == 0x20u || opcode == 0x21u || opcode == 0x2cu || opcode == 0x2du;
|
||||
}
|
||||
@@ -234,24 +294,23 @@ bool IsUnsupportedVop3EncodedVop2Alias(uint32_t opcode) {
|
||||
}
|
||||
|
||||
Opcode LookupVop3Opcode(uint32_t opcode) {
|
||||
const auto direct = Lookup(VOP3_OPS, static_cast<uint32_t>(std::size(VOP3_OPS)), opcode);
|
||||
const auto direct = Detail::LookupOpcode(VOP3_OPS, opcode);
|
||||
if (direct != Opcode::Unsupported) {
|
||||
return direct;
|
||||
}
|
||||
if (opcode <= 0xffu) {
|
||||
return Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
||||
return Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
}
|
||||
if (opcode >= 0x100u && opcode <= 0x13fu) {
|
||||
if (IsUnsupportedVop3EncodedVop2Alias(opcode - 0x100u)) {
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
return Lookup(VOP2_OPS, static_cast<uint32_t>(std::size(VOP2_OPS)), opcode - 0x100u);
|
||||
return LookupVop2Opcode(opcode - 0x100u);
|
||||
}
|
||||
if (opcode >= 0x180u && opcode <= 0x1ffu) {
|
||||
return Lookup(VOP3_ENCODED_VOP1_OPS,
|
||||
static_cast<uint32_t>(std::size(VOP3_ENCODED_VOP1_OPS)), opcode - 0x180u);
|
||||
return Detail::LookupOpcode(VOP3_ENCODED_VOP1_OPS, opcode - 0x180u);
|
||||
}
|
||||
return Lookup(VOP3_OPS, static_cast<uint32_t>(std::size(VOP3_OPS)), opcode);
|
||||
return Detail::LookupOpcode(VOP3_OPS, opcode);
|
||||
}
|
||||
|
||||
bool IsVop3EncodedVopc(uint32_t opcode) {
|
||||
@@ -791,7 +850,6 @@ Vop2SdwaFields DecodeVop2SdwaFields(uint32_t modifier) {
|
||||
}
|
||||
|
||||
struct Vop2SdwaRule {
|
||||
Opcode opcode = Opcode::Unknown;
|
||||
uint32_t dst_selectors = SdwaSelFull();
|
||||
uint32_t src0_selectors = SdwaSelFull();
|
||||
uint32_t src1_selectors = SdwaSelFull();
|
||||
@@ -804,46 +862,27 @@ constexpr uint32_t SdwaSelAll() {
|
||||
}
|
||||
|
||||
constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = {
|
||||
{Opcode::VCndmaskB32, SdwaSelWords(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, false},
|
||||
{Opcode::VAddF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VSubF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VMulF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VAddF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VSubF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VSubrevF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMulF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMaxF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMinF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMulI32I24, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMulU32U24, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMinU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMaxU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VLshrrevB32, SdwaSelFull(), SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), false,
|
||||
false},
|
||||
{Opcode::VLshlrevB32, SdwaSelFull(), SdwaSelFull(), SdwaSelAll(), false, false},
|
||||
{Opcode::VAndB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VOrB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VXorB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VXnorB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VAddNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VSubNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VSubrevNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
true, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
true, true},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), false, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelAll(), false, false},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
};
|
||||
static_assert(sizeof(VOP2_SDWA_RULES) / sizeof(VOP2_SDWA_RULES[0]) ==
|
||||
static_cast<size_t>(Vop2SdwaProfile::Count));
|
||||
|
||||
const Vop2SdwaRule* FindVop2SdwaRule(Opcode opcode) {
|
||||
for (const auto& rule: VOP2_SDWA_RULES) {
|
||||
if (rule.opcode == opcode) {
|
||||
return &rule;
|
||||
}
|
||||
const Vop2SdwaRule* FindVop2SdwaRule(uint32_t encoding) {
|
||||
const auto* info = Detail::FindOpcode(VOP2_OPS, encoding);
|
||||
if (info == nullptr || info->sdwa_profile == Vop2SdwaProfile::None) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
return &VOP2_SDWA_RULES[static_cast<size_t>(info->sdwa_profile)];
|
||||
}
|
||||
|
||||
bool IsVop2SdwaDestinationSupported(const Vop2SdwaRule& rule, const Vop2SdwaFields& fields) {
|
||||
@@ -881,7 +920,7 @@ bool ValidateVop2Sdwa(Instruction& inst, uint32_t opcode, const Vop2SdwaFields&
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto* rule = FindVop2SdwaRule(inst.opcode);
|
||||
const auto* rule = FindVop2SdwaRule(opcode);
|
||||
if (rule == nullptr) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
"VOP2 SDWA modifier is not supported for opcode");
|
||||
@@ -1135,6 +1174,11 @@ bool DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
SetUnsupported(inst, Family::VOPC, opcode, "VOPC opcode is not implemented");
|
||||
return true;
|
||||
}
|
||||
const auto* info = Detail::FindOpcode(VOPC_OPS, opcode);
|
||||
if (info == nullptr || !info->supports_dpp) {
|
||||
SetUnsupported(inst, Family::VOPC, opcode, "VOPC DPP modifier is not supported for opcode");
|
||||
return true;
|
||||
}
|
||||
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error) ||
|
||||
!DecodeVectorGpr(vsrc1, inst.src1, error)) {
|
||||
return false;
|
||||
@@ -1427,7 +1471,7 @@ bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP2_OPS, static_cast<uint32_t>(std::size(VOP2_OPS)), opcode);
|
||||
inst.opcode = LookupVop2Opcode(opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
switch (opcode) {
|
||||
@@ -1468,7 +1512,7 @@ bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP1_OPS, static_cast<uint32_t>(std::size(VOP1_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOP1_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -1509,7 +1553,7 @@ bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
@@ -1723,7 +1767,7 @@ bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3P;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP3P_OPS, static_cast<uint32_t>(std::size(VOP3P_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOP3P_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
|
||||
@@ -131,7 +131,9 @@ bool IsAtomic(IR::Opcode op) {
|
||||
case IR::Opcode::AtomicUMaxU32:
|
||||
case IR::Opcode::AtomicAndU32:
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32: return true;
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -141,6 +143,9 @@ uint32_t BufferAddressOperandCount(const IR::Instruction& inst) {
|
||||
}
|
||||
|
||||
bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error) {
|
||||
if (static_cast<size_t>(inst.op) >= static_cast<size_t>(IR::Opcode::Count)) {
|
||||
return Fail(error, "IR instruction opcode is out of range");
|
||||
}
|
||||
if (inst.src_count > std::size(inst.src)) {
|
||||
return Fail(error, "instruction source count exceeds fixed IR storage");
|
||||
}
|
||||
@@ -200,6 +205,12 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
|
||||
inst.src_count != 2)) {
|
||||
return Fail(error, "storage image instruction has an invalid resource class");
|
||||
}
|
||||
const auto float_atomic =
|
||||
inst.op == IR::Opcode::AtomicFMinF32 || inst.op == IR::Opcode::AtomicFMaxF32;
|
||||
if (float_atomic &&
|
||||
(kind != IR::ResourceKind::Buffer || inst.src_count != buffer_address_count + 1u)) {
|
||||
return Fail(error, "floating-point atomic instruction must target a buffer");
|
||||
}
|
||||
if (IsAtomic(inst.op) &&
|
||||
!((kind == IR::ResourceKind::Buffer && inst.src_count == buffer_address_count + 1u) ||
|
||||
((kind == IR::ResourceKind::Lds || kind == IR::ResourceKind::Gds) &&
|
||||
@@ -389,7 +400,7 @@ bool ProgramRequiresExactSubgroupSize(const IR::Program& program) {
|
||||
if (Emitter::InstructionHasDppSource(inst)) {
|
||||
return true;
|
||||
}
|
||||
if (Emitter::IsCompareOpcode(inst.op) && !Emitter::IsSccOperand(inst.dst)) {
|
||||
if (IR::IsCompareOpcode(inst.op) && !Emitter::IsSccOperand(inst.dst)) {
|
||||
return true;
|
||||
}
|
||||
if ((IsExecDestination(inst.dst) || IsExecDestination(inst.dst2)) &&
|
||||
@@ -488,6 +499,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateBlockLabels(state, program);
|
||||
AllocateDispatcherState(state, program);
|
||||
EmitFunction(state, program);
|
||||
if (state.unsupported_ir_instruction) {
|
||||
char pc[9] {};
|
||||
std::snprintf(pc, sizeof(pc), "%08x", state.unsupported_ir_pc);
|
||||
return Fail(error, "SPIR-V emitter has no handler for IR opcode " +
|
||||
std::string(IR::OpcodeName(state.unsupported_ir_opcode)) +
|
||||
" at pc 0x" + pc);
|
||||
}
|
||||
|
||||
ReportReserveExceeded(program, "registers", state.registers.size());
|
||||
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
||||
|
||||
@@ -1486,17 +1486,20 @@ void EmitBitCompareB32(EmitterState& state, const IR::Instruction& inst, bool bi
|
||||
EmitStoreU32(state, inst.dst, ret);
|
||||
}
|
||||
|
||||
void EmitCompareNeU64(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0);
|
||||
const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1);
|
||||
const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0);
|
||||
const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1);
|
||||
const auto ne_low = state.builder.AllocateId();
|
||||
const auto ne_high = state.builder.AllocateId();
|
||||
const auto cond = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpINotEqual, state.bool_type, ne_low, lhs_low, rhs_low});
|
||||
state.builder.AddFunction({OpINotEqual, state.bool_type, ne_high, lhs_high, rhs_high});
|
||||
state.builder.AddFunction({OpLogicalOr, state.bool_type, cond, ne_low, ne_high});
|
||||
void EmitCompareU64(EmitterState& state, const IR::Instruction& inst) {
|
||||
const bool equal = inst.op == IR::Opcode::CompareEqU64;
|
||||
const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0);
|
||||
const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1);
|
||||
const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0);
|
||||
const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1);
|
||||
const auto low = state.builder.AllocateId();
|
||||
const auto high = state.builder.AllocateId();
|
||||
const auto cond = state.builder.AllocateId();
|
||||
const auto compare_op = equal ? OpIEqual : OpINotEqual;
|
||||
state.builder.AddFunction({compare_op, state.bool_type, low, lhs_low, rhs_low});
|
||||
state.builder.AddFunction({compare_op, state.bool_type, high, lhs_high, rhs_high});
|
||||
state.builder.AddFunction(
|
||||
{equal ? OpLogicalAnd : OpLogicalOr, state.bool_type, cond, low, high});
|
||||
EmitCompareResult(state, inst.dst, cond);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,6 @@ bool IsSccOperand(const IR::Operand& operand) {
|
||||
return operand.kind == IR::OperandKind::Register && operand.reg.file == IR::RegisterFile::Scc;
|
||||
}
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
void CollectMaskStateRegisters(std::vector<RegisterBinding>& registers) {
|
||||
CollectRegister(registers, {IR::RegisterFile::Exec, 0});
|
||||
CollectRegister(registers, {IR::RegisterFile::Exec, 1});
|
||||
@@ -189,11 +187,11 @@ void CollectRegisters(const IR::Program& program, std::vector<RegisterBinding>&
|
||||
if (inst.op == IR::Opcode::BitCountU64 || inst.op == IR::Opcode::FindMsbFromHighU64) {
|
||||
CollectSequentialRegisters(registers, inst.src[0], 2);
|
||||
}
|
||||
if (inst.op == IR::Opcode::CompareNeU64) {
|
||||
if (IR::IsCompare64Opcode(inst.op)) {
|
||||
CollectSequentialRegisters(registers, inst.src[0], 2);
|
||||
CollectSequentialRegisters(registers, inst.src[1], 2);
|
||||
}
|
||||
if (IsCompareOpcode(inst.op) && inst.dst.kind == IR::OperandKind::Register &&
|
||||
if (IR::IsCompareOpcode(inst.op) && inst.dst.kind == IR::OperandKind::Register &&
|
||||
inst.dst.reg.file != IR::RegisterFile::Scc) {
|
||||
CollectSequentialRegisters(registers, inst.dst, 2);
|
||||
}
|
||||
@@ -384,93 +382,6 @@ bool ProgramNeedsSubgroupShuffle(const IR::Program& program) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op) {
|
||||
switch (op) {
|
||||
case IR::Opcode::CompareFalse:
|
||||
case IR::Opcode::CompareTrue:
|
||||
case IR::Opcode::CompareEqU32:
|
||||
case IR::Opcode::CompareNeU32:
|
||||
case IR::Opcode::CompareGtU32:
|
||||
case IR::Opcode::CompareGeU32:
|
||||
case IR::Opcode::CompareLtU32:
|
||||
case IR::Opcode::CompareLeU32:
|
||||
case IR::Opcode::CompareNeU64:
|
||||
case IR::Opcode::CompareMaskEqU32:
|
||||
case IR::Opcode::CompareMaskNeU32:
|
||||
case IR::Opcode::CompareMaskGtU32:
|
||||
case IR::Opcode::CompareMaskGeU32:
|
||||
case IR::Opcode::CompareMaskLtU32:
|
||||
case IR::Opcode::CompareMaskLeU32:
|
||||
case IR::Opcode::CompareEqI32:
|
||||
case IR::Opcode::CompareNeI32:
|
||||
case IR::Opcode::CompareGtI32:
|
||||
case IR::Opcode::CompareGeI32:
|
||||
case IR::Opcode::CompareLtI32:
|
||||
case IR::Opcode::CompareLeI32:
|
||||
case IR::Opcode::CompareEqI16:
|
||||
case IR::Opcode::CompareNeI16:
|
||||
case IR::Opcode::CompareGtI16:
|
||||
case IR::Opcode::CompareGeI16:
|
||||
case IR::Opcode::CompareLtI16:
|
||||
case IR::Opcode::CompareLeI16:
|
||||
case IR::Opcode::CompareMaskEqI32:
|
||||
case IR::Opcode::CompareMaskNeI32:
|
||||
case IR::Opcode::CompareMaskGtI32:
|
||||
case IR::Opcode::CompareMaskGeI32:
|
||||
case IR::Opcode::CompareMaskLtI32:
|
||||
case IR::Opcode::CompareMaskLeI32:
|
||||
case IR::Opcode::CompareEqU16:
|
||||
case IR::Opcode::CompareNeU16:
|
||||
case IR::Opcode::CompareGtU16:
|
||||
case IR::Opcode::CompareGeU16:
|
||||
case IR::Opcode::CompareLtU16:
|
||||
case IR::Opcode::CompareLeU16:
|
||||
case IR::Opcode::CompareEqF32:
|
||||
case IR::Opcode::CompareNeF32:
|
||||
case IR::Opcode::CompareGtF32:
|
||||
case IR::Opcode::CompareGeF32:
|
||||
case IR::Opcode::CompareLtF32:
|
||||
case IR::Opcode::CompareLeF32:
|
||||
case IR::Opcode::CompareOrderedF32:
|
||||
case IR::Opcode::CompareUnorderedF32:
|
||||
case IR::Opcode::CompareUnordEqF32:
|
||||
case IR::Opcode::CompareUnordNeF32:
|
||||
case IR::Opcode::CompareUnordGtF32:
|
||||
case IR::Opcode::CompareUnordGeF32:
|
||||
case IR::Opcode::CompareUnordLtF32:
|
||||
case IR::Opcode::CompareUnordLeF32:
|
||||
case IR::Opcode::CompareClassF32:
|
||||
case IR::Opcode::CompareEqF16:
|
||||
case IR::Opcode::CompareNeF16:
|
||||
case IR::Opcode::CompareGtF16:
|
||||
case IR::Opcode::CompareGeF16:
|
||||
case IR::Opcode::CompareLtF16:
|
||||
case IR::Opcode::CompareLeF16:
|
||||
case IR::Opcode::CompareUnordNeF16:
|
||||
case IR::Opcode::CompareMaskEqF16:
|
||||
case IR::Opcode::CompareMaskNeF16:
|
||||
case IR::Opcode::CompareMaskGtF16:
|
||||
case IR::Opcode::CompareMaskGeF16:
|
||||
case IR::Opcode::CompareMaskLtF16:
|
||||
case IR::Opcode::CompareMaskLeF16:
|
||||
case IR::Opcode::CompareMaskUnordNeF16:
|
||||
case IR::Opcode::CompareMaskUnordGeF16:
|
||||
case IR::Opcode::CompareMaskEqF32:
|
||||
case IR::Opcode::CompareMaskNeF32:
|
||||
case IR::Opcode::CompareMaskGtF32:
|
||||
case IR::Opcode::CompareMaskGeF32:
|
||||
case IR::Opcode::CompareMaskLtF32:
|
||||
case IR::Opcode::CompareMaskLeF32:
|
||||
case IR::Opcode::CompareMaskUnordEqF32:
|
||||
case IR::Opcode::CompareMaskUnordNeF32:
|
||||
case IR::Opcode::CompareMaskUnordGtF32:
|
||||
case IR::Opcode::CompareMaskUnordGeF32:
|
||||
case IR::Opcode::CompareMaskUnordLtF32:
|
||||
case IR::Opcode::CompareMaskUnordLeF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program) {
|
||||
for (const auto& block: program.blocks) {
|
||||
for (const auto& inst: block.instructions) {
|
||||
@@ -481,7 +392,7 @@ bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program) {
|
||||
InstructionHasDppSource(inst)) {
|
||||
return true;
|
||||
}
|
||||
if (IsCompareOpcode(inst.op)) {
|
||||
if (IR::IsCompareOpcode(inst.op)) {
|
||||
return true;
|
||||
}
|
||||
if (inst.dst.kind == IR::OperandKind::Register &&
|
||||
|
||||
@@ -516,7 +516,8 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
case IR::Opcode::CompareGeU32: EmitCompareU32(state, inst, OpUGreaterThanEqual); break;
|
||||
case IR::Opcode::CompareLtU32: EmitCompareU32(state, inst, OpULessThan); break;
|
||||
case IR::Opcode::CompareLeU32: EmitCompareU32(state, inst, OpULessThanEqual); break;
|
||||
case IR::Opcode::CompareNeU64: EmitCompareNeU64(state, inst); break;
|
||||
case IR::Opcode::CompareEqU64:
|
||||
case IR::Opcode::CompareNeU64: EmitCompareU64(state, inst); break;
|
||||
case IR::Opcode::CompareMaskEqU32: EmitCompareMaskU32(state, inst, OpIEqual); break;
|
||||
case IR::Opcode::CompareMaskNeU32: EmitCompareMaskU32(state, inst, OpINotEqual); break;
|
||||
case IR::Opcode::CompareMaskGtU32: EmitCompareMaskU32(state, inst, OpUGreaterThan); break;
|
||||
@@ -774,6 +775,12 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicU32(state, inst, OpAtomicXor); });
|
||||
break;
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMinF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMaxF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::FlatLoadUbyte: EmitFlatLoadUbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadSbyte: EmitFlatLoadSbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadUshort: EmitFlatLoadUshort(state, inst); break;
|
||||
@@ -850,7 +857,13 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
}
|
||||
EmitGuardedByExec(state, [&]() { EmitExport(state, inst); });
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
if (!state.unsupported_ir_instruction) {
|
||||
state.unsupported_ir_instruction = true;
|
||||
state.unsupported_ir_opcode = inst.op;
|
||||
state.unsupported_ir_pc = inst.pc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,9 @@ struct EmitterState {
|
||||
bool needs_image_gather_extended = false;
|
||||
bool needs_function_lds = false;
|
||||
bool needs_pixel_valid_mask = false;
|
||||
bool unsupported_ir_instruction = false;
|
||||
IR::Opcode unsupported_ir_opcode = IR::Opcode::Count;
|
||||
uint32_t unsupported_ir_pc = 0;
|
||||
std::vector<RegisterBinding> registers;
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
@@ -611,8 +614,6 @@ bool IsMaskRegisterFile(IR::RegisterFile file);
|
||||
|
||||
bool IsSccOperand(const IR::Operand& operand);
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
void CollectMaskStateRegisters(std::vector<RegisterBinding>& registers);
|
||||
|
||||
void CollectSequentialRegisters(std::vector<RegisterBinding>& registers, const IR::Operand& base,
|
||||
@@ -651,8 +652,6 @@ bool ProgramNeedsSubgroupBallot(const IR::Program& program);
|
||||
|
||||
bool ProgramNeedsSubgroupShuffle(const IR::Program& program);
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program);
|
||||
|
||||
uint32_t PointerForRegister(const EmitterState& state, IR::Register reg);
|
||||
@@ -894,7 +893,8 @@ uint32_t EmitByteAddress(EmitterState& state, const IR::Instruction& inst, uint3
|
||||
uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc);
|
||||
|
||||
uint32_t StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem, uint32_t use_pc);
|
||||
Prospero::BufferFormat StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc);
|
||||
|
||||
bool ShouldApplyBufferAddTid(const IR::Instruction& inst);
|
||||
|
||||
@@ -1015,6 +1015,9 @@ void EmitDeviceAtomicMemoryBarrier(EmitterState& state);
|
||||
|
||||
void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode);
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst);
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitLoadSrtDword(EmitterState& state, const IR::Instruction& inst);
|
||||
@@ -1345,7 +1348,7 @@ void EmitCompareI16(EmitterState& state, const IR::Instruction& inst, uint32_t o
|
||||
|
||||
void EmitBitCompareB32(EmitterState& state, const IR::Instruction& inst, bool bit_set);
|
||||
|
||||
void EmitCompareNeU64(EmitterState& state, const IR::Instruction& inst);
|
||||
void EmitCompareU64(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitCompareConstant(EmitterState& state, const IR::Instruction& inst, bool value);
|
||||
|
||||
@@ -1577,19 +1580,22 @@ uint32_t EmitValueOrZeroIfCondition(EmitterState& state, uint32_t condition, Fn&
|
||||
}
|
||||
|
||||
const auto then_label = state.builder.AllocateId();
|
||||
const auto then_exit = state.builder.AllocateId();
|
||||
const auto else_label = state.builder.AllocateId();
|
||||
const auto merge_label = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelectionMerge, merge_label, SelectionControlNone});
|
||||
state.builder.AddFunction({OpBranchConditional, condition, then_label, else_label});
|
||||
state.builder.AddFunction({OpLabel, then_label});
|
||||
const auto then_value = fn();
|
||||
state.builder.AddFunction({OpBranch, then_exit});
|
||||
state.builder.AddFunction({OpLabel, then_exit});
|
||||
state.builder.AddFunction({OpBranch, merge_label});
|
||||
state.builder.AddFunction({OpLabel, else_label});
|
||||
state.builder.AddFunction({OpBranch, merge_label});
|
||||
state.builder.AddFunction({OpLabel, merge_label});
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpPhi, state.uint_type, value, then_value, then_label, ConstantU32(state, 0), else_label});
|
||||
{OpPhi, state.uint_type, value, then_value, then_exit, ConstantU32(state, 0), else_label});
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +153,8 @@ uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryIn
|
||||
return state.program.info.buffers[mem.resource].packed_stride;
|
||||
}
|
||||
|
||||
uint32_t StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc) {
|
||||
Prospero::BufferFormat StorageBufferFormat(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc) {
|
||||
(void)use_pc;
|
||||
if (mem.resource >= state.program.info.buffers.size()) {
|
||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::Buffers, mem.resource,
|
||||
@@ -640,8 +640,8 @@ void EmitMemoryStoreU32(EmitterState& state, const IR::Instruction& inst, IR::Re
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind kind,
|
||||
Fn&& desired_value) {
|
||||
uint32_t EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind kind,
|
||||
Fn&& desired_value) {
|
||||
const auto scope = kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
const auto memory = kind == IR::ResourceKind::Lds ? MemorySemanticsWorkgroupMemory
|
||||
: MemorySemanticsUniformMemory;
|
||||
@@ -675,6 +675,7 @@ void EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind
|
||||
const auto semantics = MemorySemanticsAcquireRelease | memory;
|
||||
state.builder.AddFunction(
|
||||
{OpMemoryBarrier, ConstantU32(state, scope), ConstantU32(state, semantics)});
|
||||
return observed;
|
||||
}
|
||||
|
||||
void EmitMemoryStoreSubDwordU32(EmitterState& state, const IR::Instruction& inst,
|
||||
@@ -748,8 +749,7 @@ Prospero::BufferFormat FormattedBufferFormat(const EmitterState& state,
|
||||
const IR::Instruction& inst) {
|
||||
return inst.memory.typed
|
||||
? Format::DecodeTBufferFormat(inst.memory.data_format, inst.memory.number_format)
|
||||
: static_cast<Prospero::BufferFormat>(
|
||||
StorageBufferFormat(state, inst.memory, inst.pc));
|
||||
: StorageBufferFormat(state, inst.memory, inst.pc);
|
||||
}
|
||||
|
||||
IR::Instruction WithFormatComponentByteOffset(const IR::Instruction& inst,
|
||||
@@ -1181,6 +1181,79 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
EmitStoreU32(state, inst.dst, old);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t EmitF32BitsOrderedCompare(EmitterState& state, uint32_t lhs_bits, uint32_t rhs_bits,
|
||||
uint32_t comparison_opcode) {
|
||||
struct ClassifiedBits {
|
||||
uint32_t nan = 0;
|
||||
uint32_t zero = 0;
|
||||
uint32_t key = 0;
|
||||
};
|
||||
const auto Classify = [&](uint32_t bits) {
|
||||
ClassifiedBits cls;
|
||||
const auto abs_bits = EmitAndConstant(state, bits, 0x7fffffffu);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu);
|
||||
const auto exponent_max =
|
||||
EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0x7f800000u);
|
||||
const auto mantissa_nonzero = EmitCompareU32Constant(state, OpINotEqual, mantissa_bits, 0);
|
||||
const auto negative = EmitCompareU32Constant(state, OpINotEqual,
|
||||
EmitAndConstant(state, bits, 0x80000000u), 0);
|
||||
const auto negative_key = state.builder.AllocateId();
|
||||
const auto positive_key = state.builder.AllocateId();
|
||||
// Map all non-NaN IEEE-754 encodings to monotonically increasing unsigned keys.
|
||||
state.builder.AddFunction({OpNot, state.uint_type, negative_key, bits});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseXor, state.uint_type, positive_key, bits, ConstantU32(state, 0x80000000u)});
|
||||
cls.nan = EmitLogicalAndBool(state, exponent_max, mantissa_nonzero);
|
||||
cls.zero = EmitCompareU32Constant(state, OpIEqual, abs_bits, 0);
|
||||
cls.key = EmitSelectValueU32(state, negative, negative_key, positive_key);
|
||||
return cls;
|
||||
};
|
||||
|
||||
const auto lhs = Classify(lhs_bits);
|
||||
const auto rhs = Classify(rhs_bits);
|
||||
const auto any_nan = EmitLogicalOrBool(state, lhs.nan, rhs.nan);
|
||||
// The key transform orders -0 below +0, while the floating comparison treats them as equal.
|
||||
const auto both_zero = EmitLogicalAndBool(state, lhs.zero, rhs.zero);
|
||||
const auto ordered_nonzero =
|
||||
EmitLogicalNotBool(state, EmitLogicalOrBool(state, any_nan, both_zero));
|
||||
const auto comparison = state.builder.AllocateId();
|
||||
state.builder.AddFunction({comparison_opcode, state.bool_type, comparison, lhs.key, rhs.key});
|
||||
return EmitLogicalAndBool(state, ordered_nonzero, comparison);
|
||||
}
|
||||
|
||||
void EmitAtomicFMinMaxF32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t comparison_opcode) {
|
||||
const auto index =
|
||||
EmitMemoryDwordIndex(state, inst, inst.memory, 1, AddressSourceCount(inst, 1));
|
||||
const auto in_bounds = EmitStorageBufferElementInBounds(state, inst.memory, index, inst.pc);
|
||||
const auto src_u32 = EmitValueLoad(state, inst.src[0]);
|
||||
const auto old = EmitValueOrZeroIfCondition(state, in_bounds, [&]() {
|
||||
const auto pointer = EmitStorageBufferElementPointer(state, inst.memory, index, inst.pc);
|
||||
return EmitAtomicUpdateU32(state, pointer, inst.memory.kind, [&](uint32_t old_u32) {
|
||||
const auto replace_old = state.builder.AllocateId();
|
||||
const auto replace =
|
||||
EmitF32BitsOrderedCompare(state, src_u32, old_u32, comparison_opcode);
|
||||
state.builder.AddFunction(
|
||||
{OpSelect, state.uint_type, replace_old, replace, src_u32, old_u32});
|
||||
return replace_old;
|
||||
});
|
||||
});
|
||||
EmitStoreU32(state, inst.dst, old);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpULessThan);
|
||||
}
|
||||
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpUGreaterThan);
|
||||
}
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
if (state.address_memory_variable == 0) {
|
||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||
|
||||
@@ -64,26 +64,26 @@ VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint3
|
||||
}
|
||||
|
||||
switch (state.vertex_input_info->resources[location].Format()) {
|
||||
case 5:
|
||||
case 11:
|
||||
case 18:
|
||||
case 20:
|
||||
case 27:
|
||||
case 60:
|
||||
case 62:
|
||||
case 69:
|
||||
case 72:
|
||||
case 75: return VertexInputScalarKind::Uint;
|
||||
case 6:
|
||||
case 12:
|
||||
case 19:
|
||||
case 21:
|
||||
case 28:
|
||||
case 61:
|
||||
case 63:
|
||||
case 70:
|
||||
case 73:
|
||||
case 76: return VertexInputScalarKind::Sint;
|
||||
case Prospero::BufferFormat::k8UInt:
|
||||
case Prospero::BufferFormat::k16UInt:
|
||||
case Prospero::BufferFormat::k8_8UInt:
|
||||
case Prospero::BufferFormat::k32UInt:
|
||||
case Prospero::BufferFormat::k16_16UInt:
|
||||
case Prospero::BufferFormat::k8_8_8_8UInt:
|
||||
case Prospero::BufferFormat::k32_32UInt:
|
||||
case Prospero::BufferFormat::k16_16_16_16UInt:
|
||||
case Prospero::BufferFormat::k32_32_32UInt:
|
||||
case Prospero::BufferFormat::k32_32_32_32UInt: return VertexInputScalarKind::Uint;
|
||||
case Prospero::BufferFormat::k8SInt:
|
||||
case Prospero::BufferFormat::k16SInt:
|
||||
case Prospero::BufferFormat::k8_8SInt:
|
||||
case Prospero::BufferFormat::k32SInt:
|
||||
case Prospero::BufferFormat::k16_16SInt:
|
||||
case Prospero::BufferFormat::k8_8_8_8SInt:
|
||||
case Prospero::BufferFormat::k32_32SInt:
|
||||
case Prospero::BufferFormat::k16_16_16_16SInt:
|
||||
case Prospero::BufferFormat::k32_32_32SInt:
|
||||
case Prospero::BufferFormat::k32_32_32_32SInt: return VertexInputScalarKind::Sint;
|
||||
default: return VertexInputScalarKind::Float;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,13 +220,13 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto format = (descriptor.dwords[1] >> 20u) & 0x1ffu;
|
||||
const bool raw_sint_storage =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
|
||||
!image.read && !image.atomic;
|
||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const auto format =
|
||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||
!image.read && !image.atomic;
|
||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
@@ -415,12 +415,12 @@ bool SpecializeResources(Program& program, const ResourceSnapshot& snapshot, std
|
||||
image.kind == ResourceKind::StorageImageUint) {
|
||||
image.storage_swizzle = DescriptorImageSwizzle(descriptor);
|
||||
}
|
||||
const auto format = (descriptor.dwords[1] >> 20u) & 0x1ffu;
|
||||
const auto format =
|
||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const bool raw_sint_storage =
|
||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
|
||||
!image.read && !image.atomic;
|
||||
storage && format == Prospero::BufferFormat::k32SInt && !image.read && !image.atomic;
|
||||
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
if (uint_image) {
|
||||
switch (image.kind) {
|
||||
|
||||
@@ -30,7 +30,9 @@ bool IsAtomic(Opcode op) {
|
||||
case Opcode::AtomicUMaxU32:
|
||||
case Opcode::AtomicAndU32:
|
||||
case Opcode::AtomicOrU32:
|
||||
case Opcode::AtomicXorU32: return true;
|
||||
case Opcode::AtomicXorU32:
|
||||
case Opcode::AtomicFMinF32:
|
||||
case Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,13 +490,26 @@ bool IsScalarMinMaxOpcode(Decoder::Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ResolveIrOpcode(Decoder::Opcode decoded, Opcode& lowered, std::string* error) {
|
||||
const auto opcode = LookupIrOpcode(decoded);
|
||||
if (!opcode.has_value()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded opcode has no IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
lowered = *opcode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LowerScalarMinMaxWithScc(const Decoder::Instruction& decoded, BasicBlock& block,
|
||||
std::string* error) {
|
||||
Instruction result;
|
||||
result.pc = decoded.pc;
|
||||
result.op = LookupIrOpcode(decoded.opcode);
|
||||
result.src_count = 2;
|
||||
if (!LowerRegisterOperand(decoded.dst, result.dst, error) ||
|
||||
if (!ResolveIrOpcode(decoded.opcode, result.op, error) ||
|
||||
!LowerRegisterOperand(decoded.dst, result.dst, error) ||
|
||||
!LowerSourceOperand(decoded.src0, result.src[0], error) ||
|
||||
!LowerSourceOperand(decoded.src1, result.src[1], error)) {
|
||||
return false;
|
||||
@@ -962,7 +975,12 @@ bool LowerControlInstruction(const Decoder::Instruction& decoded, BasicBlock& bl
|
||||
return LowerControlMarker(decoded, block, Opcode::TtraceData, true, error);
|
||||
case Decoder::Opcode::SInstPrefetch:
|
||||
return LowerControlMarker(decoded, block, Opcode::InstPrefetch, true, error);
|
||||
default: return false;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("control opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,6 +1069,18 @@ bool IsTerminatorOpcode(Decoder::Opcode opcode) {
|
||||
|
||||
bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error);
|
||||
|
||||
bool IsMemoryFamily(Decoder::Family family) {
|
||||
switch (family) {
|
||||
case Decoder::Family::SMEM:
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF:
|
||||
case Decoder::Family::FLAT:
|
||||
case Decoder::Family::DS:
|
||||
case Decoder::Family::MIMG: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block,
|
||||
std::string* error) {
|
||||
switch (inst.opcode) {
|
||||
@@ -1104,6 +1134,15 @@ bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block
|
||||
if (IsMemoryOpcode(inst.opcode)) {
|
||||
return LowerMemoryInstruction(inst, block, error);
|
||||
}
|
||||
// Memory instructions require specialized lowering to populate MemoryInfo. Never let a
|
||||
// newly decoded memory-family opcode fall through to generic register-only IR.
|
||||
if (IsMemoryFamily(inst.family)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded memory-family opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(inst.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (ScalarShiftLeftAddAmount(inst.opcode) != 0) {
|
||||
return LowerScalarShiftLeftAdd(inst, block, error);
|
||||
}
|
||||
@@ -1116,23 +1155,16 @@ bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block
|
||||
if (VectorByteConvertIndex(inst.opcode) <= 3u) {
|
||||
return LowerVectorByteConvert(inst, block, error);
|
||||
}
|
||||
if (!IsImplemented(inst.opcode)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded opcode has no IR lowering yet: {}",
|
||||
Decoder::InstructionToString(inst).c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return LowerImplemented(inst, block, error);
|
||||
}
|
||||
|
||||
bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
|
||||
Instruction inst;
|
||||
inst.pc = decoded.pc;
|
||||
inst.op = LookupIrOpcode(decoded.opcode);
|
||||
inst.src_count = decoded.src_count;
|
||||
|
||||
if (!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
|
||||
if (!ResolveIrOpcode(decoded.opcode, inst.op, error) ||
|
||||
!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
|
||||
return false;
|
||||
}
|
||||
ApplyDppDestinationMask(decoded, inst.dst);
|
||||
|
||||
@@ -3,355 +3,54 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
|
||||
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
enum class Opcode {
|
||||
ControlNop,
|
||||
Waitcnt,
|
||||
Barrier,
|
||||
Sendmsg,
|
||||
TtraceData,
|
||||
InstPrefetch,
|
||||
MoveU32,
|
||||
MoveF32Bits,
|
||||
MoveRelDestU32,
|
||||
MoveRelSourceU32,
|
||||
MoveU64,
|
||||
WqmB64,
|
||||
SaveexecB32,
|
||||
SaveexecB64,
|
||||
ReadFirstLaneU32,
|
||||
ReadLaneU32,
|
||||
WriteLaneU32,
|
||||
Permlane16B32,
|
||||
Permlanex16B32,
|
||||
AbsI32,
|
||||
IAddU32,
|
||||
IAddCarryU32,
|
||||
ISubBorrowU32,
|
||||
ScalarAddCarryU32,
|
||||
ScalarSubBorrowU32,
|
||||
ScalarSubBorrowCarryU32,
|
||||
ScalarSignedAddOverflowI32,
|
||||
ScalarSignedSubOverflowI32,
|
||||
ScalarShiftLeftAddCarryU32,
|
||||
ISubU32,
|
||||
IMulU32,
|
||||
UMulHighU32,
|
||||
SMulHighI32,
|
||||
IMadI24U32,
|
||||
UMadU24U32,
|
||||
UMadU64U32,
|
||||
SadU32,
|
||||
IAdd3U32,
|
||||
IMulI24U32,
|
||||
UMulU24U32,
|
||||
IMinI32,
|
||||
IMaxI32,
|
||||
IMin3I32,
|
||||
IMax3I32,
|
||||
IMed3I32,
|
||||
UMinU32,
|
||||
UMaxU32,
|
||||
UMin3U32,
|
||||
UMax3U32,
|
||||
UMed3U32,
|
||||
BitwiseAndU32,
|
||||
BitwiseAndU64,
|
||||
BitwiseAndNotU32,
|
||||
BitwiseAndNotU64,
|
||||
BitwiseOrU32,
|
||||
BitwiseOrU64,
|
||||
BitwiseOrNotU32,
|
||||
BitwiseOrNotU64,
|
||||
BitwiseAndOrU32,
|
||||
BitwiseOr3U32,
|
||||
BitwiseXorU32,
|
||||
BitwiseXorU64,
|
||||
BitwiseXor3U32,
|
||||
BitwiseNandU32,
|
||||
BitwiseNandU64,
|
||||
BitwiseNorU32,
|
||||
BitwiseNorU64,
|
||||
BitwiseXnorU32,
|
||||
BitwiseXnorU64,
|
||||
BitwiseNotU32,
|
||||
BitwiseNotU64,
|
||||
BitClearU32,
|
||||
BitSetU32,
|
||||
BitReverseU32,
|
||||
BitCountU32,
|
||||
BitCountU64,
|
||||
BitReplicateB64B32,
|
||||
BitCountAddU32,
|
||||
MaskedBitCountLowU32,
|
||||
MaskedBitCountHighU32,
|
||||
FindLsbU32,
|
||||
FindMsbFromHighU32,
|
||||
FindMsbFromHighU64,
|
||||
BitFieldMaskU32,
|
||||
BitFieldMaskU64,
|
||||
BitFieldExtractU32,
|
||||
BitFieldExtractU64,
|
||||
BitFieldExtract3U32,
|
||||
BitFieldExtract3I32,
|
||||
BitFieldInsertSelectU32,
|
||||
BitCompare0B32,
|
||||
BitCompare1B32,
|
||||
AlignBitU32,
|
||||
ShiftLeftAddU32,
|
||||
AddShiftLeftU32,
|
||||
XorAddU32,
|
||||
ShiftLeftOrU32,
|
||||
ShiftLeftLogicalU32,
|
||||
ShiftLeftLogicalU64,
|
||||
ShiftLeftLogicalU16,
|
||||
ShiftRightLogicalU32,
|
||||
ShiftRightLogicalU64,
|
||||
ShiftRightLogicalU16,
|
||||
ShiftRightArithmeticI32,
|
||||
ShiftRightArithmeticI16,
|
||||
SelectU32,
|
||||
SelectMaskU32,
|
||||
SelectF32Bits,
|
||||
SelectMaskF32Bits,
|
||||
SelectU64,
|
||||
PackLowLowU16,
|
||||
PackLowHighU16,
|
||||
PackHighHighU16,
|
||||
CompareFalse,
|
||||
CompareTrue,
|
||||
CompareEqU32,
|
||||
CompareNeU32,
|
||||
CompareGtU32,
|
||||
CompareGeU32,
|
||||
CompareLtU32,
|
||||
CompareLeU32,
|
||||
CompareNeU64,
|
||||
CompareMaskEqU32,
|
||||
CompareMaskNeU32,
|
||||
CompareMaskGtU32,
|
||||
CompareMaskGeU32,
|
||||
CompareMaskLtU32,
|
||||
CompareMaskLeU32,
|
||||
CompareEqI32,
|
||||
CompareNeI32,
|
||||
CompareGtI32,
|
||||
CompareGeI32,
|
||||
CompareLtI32,
|
||||
CompareLeI32,
|
||||
CompareEqI16,
|
||||
CompareNeI16,
|
||||
CompareGtI16,
|
||||
CompareGeI16,
|
||||
CompareLtI16,
|
||||
CompareLeI16,
|
||||
CompareMaskEqI32,
|
||||
CompareMaskNeI32,
|
||||
CompareMaskGtI32,
|
||||
CompareMaskGeI32,
|
||||
CompareMaskLtI32,
|
||||
CompareMaskLeI32,
|
||||
CompareEqU16,
|
||||
CompareNeU16,
|
||||
CompareGtU16,
|
||||
CompareGeU16,
|
||||
CompareLtU16,
|
||||
CompareLeU16,
|
||||
CompareEqF32,
|
||||
CompareNeF32,
|
||||
CompareGtF32,
|
||||
CompareGeF32,
|
||||
CompareLtF32,
|
||||
CompareLeF32,
|
||||
CompareOrderedF32,
|
||||
CompareUnorderedF32,
|
||||
CompareUnordEqF32,
|
||||
CompareUnordNeF32,
|
||||
CompareUnordGtF32,
|
||||
CompareUnordGeF32,
|
||||
CompareUnordLtF32,
|
||||
CompareUnordLeF32,
|
||||
CompareClassF32,
|
||||
CompareEqF16,
|
||||
CompareNeF16,
|
||||
CompareGtF16,
|
||||
CompareGeF16,
|
||||
CompareLtF16,
|
||||
CompareLeF16,
|
||||
CompareUnordNeF16,
|
||||
CompareMaskEqF16,
|
||||
CompareMaskNeF16,
|
||||
CompareMaskGtF16,
|
||||
CompareMaskGeF16,
|
||||
CompareMaskLtF16,
|
||||
CompareMaskLeF16,
|
||||
CompareMaskUnordNeF16,
|
||||
CompareMaskUnordGeF16,
|
||||
CompareMaskEqF32,
|
||||
CompareMaskNeF32,
|
||||
CompareMaskGtF32,
|
||||
CompareMaskGeF32,
|
||||
CompareMaskLtF32,
|
||||
CompareMaskLeF32,
|
||||
CompareMaskUnordEqF32,
|
||||
CompareMaskUnordNeF32,
|
||||
CompareMaskUnordGtF32,
|
||||
CompareMaskUnordGeF32,
|
||||
CompareMaskUnordLtF32,
|
||||
CompareMaskUnordLeF32,
|
||||
ConvertByteU32ToF32,
|
||||
ConvertU32ToF32,
|
||||
ConvertI32ToF32,
|
||||
ConvertF32ToU32,
|
||||
ConvertF32ToI32,
|
||||
ConvertF32ToF16,
|
||||
ConvertF16ToF32,
|
||||
ConvertU16ToF16,
|
||||
ConvertF16ToU16,
|
||||
ConvertI16ToF16,
|
||||
ConvertF16ToI16,
|
||||
ConvertRoundPlusInfF32ToI32,
|
||||
ConvertFloorF32ToI32,
|
||||
ConvertI4ToOffsetF32,
|
||||
LdexpF32,
|
||||
PackF32ToF16Rtz,
|
||||
PackSnorm2x16F32,
|
||||
PackUnorm2x16F32,
|
||||
PackU16U32,
|
||||
PackU8F32,
|
||||
PackB32F16,
|
||||
PackedMadI16,
|
||||
PackedMulLoU16,
|
||||
PackedAddI16,
|
||||
PackedSubI16,
|
||||
PackedLshlrevB16,
|
||||
PackedLshrrevB16,
|
||||
PackedAshrrevI16,
|
||||
PackedMaxI16,
|
||||
PackedMinI16,
|
||||
PackedMadU16,
|
||||
PackedAddU16,
|
||||
PackedSubU16,
|
||||
PackedMaxU16,
|
||||
PackedMinU16,
|
||||
PackedAddF16,
|
||||
PackedMulF16,
|
||||
PackedMinF16,
|
||||
PackedMaxF16,
|
||||
PackedFmaF16,
|
||||
AddF16,
|
||||
SubF16,
|
||||
MulF16,
|
||||
MinF16,
|
||||
MaxF16,
|
||||
FmaF16,
|
||||
MadMixF16,
|
||||
IAddU16,
|
||||
ISubI16,
|
||||
IMinI16,
|
||||
IMaxI16,
|
||||
UMinU16,
|
||||
UMaxU16,
|
||||
RcpF32,
|
||||
FractF32,
|
||||
TruncF32,
|
||||
CeilF32,
|
||||
RoundEvenF32,
|
||||
FloorF32,
|
||||
Exp2F32,
|
||||
Log2F32,
|
||||
InverseSqrtF32,
|
||||
SqrtF32,
|
||||
RcpF16,
|
||||
SqrtF16,
|
||||
InverseSqrtF16,
|
||||
Log2F16,
|
||||
Exp2F16,
|
||||
FloorF16,
|
||||
CeilF16,
|
||||
TruncF16,
|
||||
RoundEvenF16,
|
||||
SinF32,
|
||||
CosF32,
|
||||
CubeIdF32,
|
||||
CubeScF32,
|
||||
CubeTcF32,
|
||||
CubeMaF32,
|
||||
FAddF32,
|
||||
FSubF32,
|
||||
FMulF32,
|
||||
FMinF32,
|
||||
FMaxF32,
|
||||
FMadF32,
|
||||
Dot2AccF32F16,
|
||||
FMin3F32,
|
||||
FMax3F32,
|
||||
FMed3F32,
|
||||
Min3F16,
|
||||
Max3F16,
|
||||
Med3F16,
|
||||
LoadSrtDword,
|
||||
SLoadDword,
|
||||
SBufferLoadDword,
|
||||
BufferLoadUbyte,
|
||||
BufferLoadSbyte,
|
||||
BufferLoadUshort,
|
||||
BufferLoadSshort,
|
||||
BufferLoadDword,
|
||||
BufferStoreByte,
|
||||
BufferStoreShort,
|
||||
BufferStoreDword,
|
||||
AtomicSwapU32,
|
||||
AtomicAddU32,
|
||||
AtomicSubU32,
|
||||
AtomicSMinI32,
|
||||
AtomicUMinU32,
|
||||
AtomicSMaxI32,
|
||||
AtomicUMaxU32,
|
||||
AtomicAndU32,
|
||||
AtomicOrU32,
|
||||
AtomicXorU32,
|
||||
FlatLoadUbyte,
|
||||
FlatLoadSbyte,
|
||||
FlatLoadUshort,
|
||||
FlatLoadSshort,
|
||||
FlatLoadDword,
|
||||
FlatStoreByte,
|
||||
FlatStoreShort,
|
||||
FlatStoreDword,
|
||||
DsReadUbyte,
|
||||
DsReadSbyte,
|
||||
DsReadUshort,
|
||||
DsReadSshort,
|
||||
DsReadB32,
|
||||
DsWriteByte,
|
||||
DsWriteShort,
|
||||
DsWriteB32,
|
||||
DsMinF32,
|
||||
DsMaxF32,
|
||||
DsSwizzleB32,
|
||||
DsConsume,
|
||||
DsAppend,
|
||||
DsWriteAddtidB32,
|
||||
DsReadAddtidB32,
|
||||
ImageGetResinfo,
|
||||
ImageGetLod,
|
||||
ImageLoad,
|
||||
ImageStore,
|
||||
ImageSample,
|
||||
ImageGather4,
|
||||
LoadInputF32,
|
||||
Export,
|
||||
#define IR_OPCODE(name, category) name,
|
||||
#include "graphics/shader/recompiler/ir/ShaderIROpcodes.inc"
|
||||
#undef IR_OPCODE
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class OpcodeClass { General, Compare, Compare64 };
|
||||
|
||||
struct OpcodeInfo {
|
||||
std::string_view name;
|
||||
OpcodeClass opcode_class = OpcodeClass::General;
|
||||
};
|
||||
|
||||
inline constexpr OpcodeInfo OPCODE_INFO[] = {
|
||||
#define IR_OPCODE(name, category) {#name, OpcodeClass::category},
|
||||
#include "graphics/shader/recompiler/ir/ShaderIROpcodes.inc"
|
||||
#undef IR_OPCODE
|
||||
};
|
||||
static_assert(sizeof(OPCODE_INFO) / sizeof(OPCODE_INFO[0]) == static_cast<size_t>(Opcode::Count));
|
||||
|
||||
constexpr const OpcodeInfo& GetOpcodeInfo(Opcode opcode) {
|
||||
return OPCODE_INFO[static_cast<size_t>(opcode)];
|
||||
}
|
||||
|
||||
constexpr std::string_view OpcodeName(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).name;
|
||||
}
|
||||
|
||||
constexpr bool IsCompareOpcode(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).opcode_class != OpcodeClass::General;
|
||||
}
|
||||
|
||||
constexpr bool IsCompare64Opcode(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).opcode_class == OpcodeClass::Compare64;
|
||||
}
|
||||
|
||||
enum class RegisterFile { Scalar, Vector, Vcc, Exec, Scc, M0 };
|
||||
|
||||
struct Register {
|
||||
@@ -578,17 +277,17 @@ struct SrtPlan {
|
||||
struct BufferResource {
|
||||
static constexpr uint32_t NoImageAlias = UINT32_MAX;
|
||||
|
||||
uint32_t source = 0;
|
||||
uint32_t first_use_pc = 0;
|
||||
uint32_t max_byte_extent = 0;
|
||||
uint32_t packed_stride = 0;
|
||||
uint32_t descriptor_format = 0;
|
||||
uint32_t image_alias = NoImageAlias;
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool formatted = false;
|
||||
bool scalar = false;
|
||||
uint32_t source = 0;
|
||||
uint32_t first_use_pc = 0;
|
||||
uint32_t max_byte_extent = 0;
|
||||
uint32_t packed_stride = 0;
|
||||
Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid;
|
||||
uint32_t image_alias = NoImageAlias;
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
bool formatted = false;
|
||||
bool scalar = false;
|
||||
|
||||
bool operator==(const BufferResource& other) const = default;
|
||||
};
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
void SetError(std::string* error, const char* message);
|
||||
|
||||
Opcode LookupIrOpcode(Decoder::Opcode opcode);
|
||||
bool IsImplemented(Decoder::Opcode opcode);
|
||||
bool IsReversedBinary(Decoder::Opcode opcode);
|
||||
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode);
|
||||
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode);
|
||||
bool ScalarResultIs64Bit(Decoder::Opcode opcode);
|
||||
std::optional<Opcode> LookupIrOpcode(Decoder::Opcode opcode);
|
||||
bool IsReversedBinary(Decoder::Opcode opcode);
|
||||
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode);
|
||||
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode);
|
||||
bool ScalarResultIs64Bit(Decoder::Opcode opcode);
|
||||
|
||||
Operand MakeSccOperand();
|
||||
Operand MakeImmediateU32(uint32_t value);
|
||||
|
||||
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user