mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d09c81d1c6 | ||
|
|
26f0c7b0bf | ||
|
|
2e9e2ae566 | ||
|
|
53feb6cb19 | ||
|
|
05d14f5421 | ||
|
|
1a164c3628 | ||
|
|
86a586025f | ||
|
|
06cbd602c1 | ||
|
|
055d0920d7 | ||
|
|
f2ee98fe31 | ||
|
|
207cef9602 | ||
|
|
c4134a95e2 |
@@ -73,7 +73,7 @@ jobs:
|
|||||||
- name: Configure
|
- name: Configure
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
cmake -S src -B _Build/windows ^
|
cmake -S . -B _Build/windows ^
|
||||||
-G Ninja ^
|
-G Ninja ^
|
||||||
-DCMAKE_BUILD_TYPE=Release ^
|
-DCMAKE_BUILD_TYPE=Release ^
|
||||||
-DCMAKE_C_COMPILER=clang-cl ^
|
-DCMAKE_C_COMPILER=clang-cl ^
|
||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
- name: Configure
|
- name: Configure
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cmake -S src -B _Build/macos \
|
cmake -S . -B _Build/macos \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||||
@@ -270,7 +270,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p _Build
|
mkdir -p _Build
|
||||||
cmake -S src -B _Build/linux \
|
cmake -S . -B _Build/linux \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_C_COMPILER=clang \
|
-DCMAKE_C_COMPILER=clang \
|
||||||
|
|||||||
+2
-1
@@ -3,4 +3,5 @@
|
|||||||
.idea/
|
.idea/
|
||||||
build/
|
build/
|
||||||
_Build/vscode-clang/
|
_Build/vscode-clang/
|
||||||
_Build/
|
_Build/
|
||||||
|
cmake-build-debug/
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cmake.sourceDirectory": "${workspaceFolder}/src",
|
"cmake.sourceDirectory": "${workspaceFolder}",
|
||||||
"cmake.buildDirectory": "${workspaceFolder}/_Build/vscode-clang",
|
"cmake.buildDirectory": "${workspaceFolder}/_Build/vscode-clang",
|
||||||
"cmake.generator": "Ninja",
|
"cmake.generator": "Ninja",
|
||||||
"cmake.environment": {
|
"cmake.environment": {
|
||||||
|
|||||||
+112
-110
@@ -6,6 +6,10 @@ endif()
|
|||||||
|
|
||||||
project(Kyty)
|
project(Kyty)
|
||||||
|
|
||||||
|
set(KYTY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||||
|
set(KYTY_TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||||
|
set(KYTY_THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty")
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES ".*Linux")
|
if(CMAKE_SYSTEM_NAME MATCHES ".*Linux")
|
||||||
set(LINUX TRUE)
|
set(LINUX TRUE)
|
||||||
endif()
|
endif()
|
||||||
@@ -19,13 +23,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
|
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
|
||||||
|
|
||||||
include(utils.cmake)
|
include("${KYTY_SOURCE_DIR}/utils.cmake")
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
option(KYTY_ENABLE_CLANG_TIDY "Run clang-tidy checks during builds" OFF)
|
option(KYTY_ENABLE_CLANG_TIDY "Run clang-tidy checks during builds" OFF)
|
||||||
|
|
||||||
set(KYTY_THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty")
|
|
||||||
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
|
||||||
include(TestBigEndian)
|
include(TestBigEndian)
|
||||||
@@ -97,36 +99,36 @@ endif()
|
|||||||
|
|
||||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.2.2)
|
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.2.2)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${KYTY_THIRD_PARTY_DIR}/gtest/include
|
${KYTY_THIRD_PARTY_DIR}/gtest/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/gtest
|
${KYTY_THIRD_PARTY_DIR}/gtest
|
||||||
${KYTY_THIRD_PARTY_DIR}/fmt/include
|
${KYTY_THIRD_PARTY_DIR}/fmt/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/magic_enum/include/magic_enum
|
${KYTY_THIRD_PARTY_DIR}/magic_enum/include/magic_enum
|
||||||
${PROJECT_BINARY_DIR}
|
${PROJECT_BINARY_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${KYTY_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(KYTY_VERSION "${PROJECT_VERSION}")
|
set(KYTY_VERSION "${PROJECT_VERSION}")
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
${PROJECT_SOURCE_DIR}/cmake_config.h.in
|
${KYTY_SOURCE_DIR}/cmake_config.h.in
|
||||||
${PROJECT_BINARY_DIR}/cmake_config.h
|
${PROJECT_BINARY_DIR}/cmake_config.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Git)
|
find_package(Git)
|
||||||
|
|
||||||
add_custom_target( KytyGitVersion
|
add_custom_target( KytyGitVersion
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-D INPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/kytyGitVersion.h.in
|
-D INPUT_FILE=${KYTY_SOURCE_DIR}/kytyGitVersion.h.in
|
||||||
-D OUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/kytyGitVersion.h
|
-D OUTPUT_FILE=${PROJECT_BINARY_DIR}/kytyGitVersion.h
|
||||||
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
|
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||||
-D GIT_WORKING_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}
|
-D GIT_WORKING_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_version.cmake
|
-P ${KYTY_SOURCE_DIR}/generate_version.cmake
|
||||||
COMMENT "Generate kytyGitVersion.h"
|
COMMENT "Generate kytyGitVersion.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
||||||
list(APPEND KYTY_IWYU
|
list(APPEND KYTY_IWYU
|
||||||
kyty_emulator
|
kyty_emulator
|
||||||
common
|
common
|
||||||
#launcher
|
#launcher
|
||||||
@@ -139,55 +141,55 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(KYTY_BUILD_LAUNCHER "Build Qt launcher" ON)
|
option(KYTY_BUILD_LAUNCHER "Build Qt launcher" ON)
|
||||||
|
|
||||||
config_compiler_and_linker()
|
config_compiler_and_linker()
|
||||||
|
|
||||||
add_subdirectory("${KYTY_THIRD_PARTY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/3rdparty")
|
add_subdirectory("${KYTY_THIRD_PARTY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/3rdparty")
|
||||||
add_subdirectory(common)
|
add_subdirectory("${KYTY_SOURCE_DIR}/common" "${CMAKE_CURRENT_BINARY_DIR}/common")
|
||||||
|
|
||||||
file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
|
file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
|
||||||
libs/*.cpp
|
"${KYTY_SOURCE_DIR}/libs/*.cpp"
|
||||||
libs/*.h
|
"${KYTY_SOURCE_DIR}/libs/*.h"
|
||||||
graphics/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/*.cpp"
|
||||||
graphics/*.h
|
"${KYTY_SOURCE_DIR}/graphics/*.h"
|
||||||
graphics/guest_gpu/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/*.cpp"
|
||||||
graphics/guest_gpu/*.h
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/*.h"
|
||||||
graphics/guest_gpu/command_processor/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/command_processor/*.cpp"
|
||||||
graphics/guest_gpu/command_processor/*.h
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/command_processor/*.h"
|
||||||
graphics/host_gpu/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/*.cpp"
|
||||||
graphics/host_gpu/*.h
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/*.h"
|
||||||
graphics/host_gpu/renderer/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/*.cpp"
|
||||||
graphics/host_gpu/renderer/*.h
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/*.h"
|
||||||
graphics/host_gpu/renderer/cache/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/cache/*.cpp"
|
||||||
graphics/host_gpu/renderer/cache/*.h
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/cache/*.h"
|
||||||
graphics/host_gpu/renderer/image/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/image/*.cpp"
|
||||||
graphics/host_gpu/renderer/image/*.h
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/image/*.h"
|
||||||
graphics/host_gpu/renderer/pipeline/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/pipeline/*.cpp"
|
||||||
graphics/host_gpu/renderer/pipeline/*.h
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/pipeline/*.h"
|
||||||
graphics/shader/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/*.cpp"
|
||||||
graphics/shader/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/*.h"
|
||||||
graphics/shader/recompiler/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/*.cpp"
|
||||||
graphics/shader/recompiler/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/*.h"
|
||||||
graphics/shader/recompiler/cfg/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/cfg/*.cpp"
|
||||||
graphics/shader/recompiler/cfg/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/cfg/*.h"
|
||||||
graphics/shader/recompiler/decompiler/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/decompiler/*.cpp"
|
||||||
graphics/shader/recompiler/decompiler/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/decompiler/*.h"
|
||||||
graphics/shader/recompiler/emitter/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/emitter/*.cpp"
|
||||||
graphics/shader/recompiler/emitter/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/emitter/*.h"
|
||||||
graphics/shader/recompiler/ir/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/*.cpp"
|
||||||
graphics/shader/recompiler/ir/*.h
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/*.h"
|
||||||
graphics/presentation/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/presentation/*.cpp"
|
||||||
graphics/presentation/*.h
|
"${KYTY_SOURCE_DIR}/graphics/presentation/*.h"
|
||||||
graphics/presentation/window/*.cpp
|
"${KYTY_SOURCE_DIR}/graphics/presentation/window/*.cpp"
|
||||||
graphics/presentation/window/*.h
|
"${KYTY_SOURCE_DIR}/graphics/presentation/window/*.h"
|
||||||
kernel/*.cpp
|
"${KYTY_SOURCE_DIR}/kernel/*.cpp"
|
||||||
kernel/*.h
|
"${KYTY_SOURCE_DIR}/kernel/*.h"
|
||||||
loader/*.cpp
|
"${KYTY_SOURCE_DIR}/loader/*.cpp"
|
||||||
loader/*.h
|
"${KYTY_SOURCE_DIR}/loader/*.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_program(KYTY_GLSLANG_VALIDATOR glslangValidator REQUIRED)
|
find_program(KYTY_GLSLANG_VALIDATOR glslangValidator REQUIRED)
|
||||||
set(gpu_tiler_shader_dir "${CMAKE_CURRENT_SOURCE_DIR}/graphics/host_gpu/shaders")
|
set(gpu_tiler_shader_dir "${KYTY_SOURCE_DIR}/graphics/host_gpu/shaders")
|
||||||
set(gpu_tiler_generated_dir "${PROJECT_BINARY_DIR}/gpu_tiler_shaders")
|
set(gpu_tiler_generated_dir "${PROJECT_BINARY_DIR}/gpu_tiler_shaders")
|
||||||
set(gpu_tiler_shader_names
|
set(gpu_tiler_shader_names
|
||||||
standard256
|
standard256
|
||||||
@@ -215,7 +217,7 @@ foreach(shader_name IN LISTS gpu_tiler_shader_names)
|
|||||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
||||||
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
||||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
||||||
-DSYMBOL=${shader_symbol} -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
|
-DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||||
DEPENDS "${shader_source}" ${gpu_tiler_shader_includes}
|
DEPENDS "${shader_source}" ${gpu_tiler_shader_includes}
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
@@ -239,7 +241,7 @@ foreach(shader_source IN LISTS gpu_blit_shader_sources)
|
|||||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
||||||
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
||||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
||||||
-DSYMBOL=${shader_symbol} -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
|
-DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||||
DEPENDS "${shader_source}"
|
DEPENDS "${shader_source}"
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
@@ -248,8 +250,8 @@ endforeach()
|
|||||||
list(APPEND kyty_emulator_src ${gpu_blit_shader_headers})
|
list(APPEND kyty_emulator_src ${gpu_blit_shader_headers})
|
||||||
|
|
||||||
list(APPEND kyty_emulator_src
|
list(APPEND kyty_emulator_src
|
||||||
emulator.h
|
"${KYTY_SOURCE_DIR}/emulator.h"
|
||||||
emulator.cpp
|
"${KYTY_SOURCE_DIR}/emulator.cpp"
|
||||||
)
|
)
|
||||||
list(REMOVE_DUPLICATES kyty_emulator_src)
|
list(REMOVE_DUPLICATES kyty_emulator_src)
|
||||||
|
|
||||||
@@ -280,7 +282,7 @@ if(LINUX)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(inc_headers
|
set(inc_headers
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${KYTY_SOURCE_DIR}
|
||||||
${KYTY_THIRD_PARTY_DIR}/SDL2/include
|
${KYTY_THIRD_PARTY_DIR}/SDL2/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
||||||
@@ -292,13 +294,13 @@ set(inc_headers
|
|||||||
|
|
||||||
if (KYTY_CLANG_CL)
|
if (KYTY_CLANG_CL)
|
||||||
list(APPEND kyty_emulator_link_libraries winpthread)
|
list(APPEND kyty_emulator_link_libraries winpthread)
|
||||||
list(APPEND inc_headers
|
list(APPEND inc_headers
|
||||||
${KYTY_THIRD_PARTY_DIR}/winpthread/include
|
${KYTY_THIRD_PARTY_DIR}/winpthread/include
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND check_headers
|
list(APPEND check_headers
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${KYTY_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
function(add_kyty_full_emulator_test target source)
|
function(add_kyty_full_emulator_test target source)
|
||||||
@@ -320,108 +322,108 @@ endfunction()
|
|||||||
function(configure_macos_guest_address_space target)
|
function(configure_macos_guest_address_space target)
|
||||||
if(APPLE AND (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
|
if(APPLE AND (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
|
||||||
(NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")))
|
(NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")))
|
||||||
target_sources(${target} PRIVATE kernel/macosGuestAddressSpace.cpp)
|
target_sources(${target} PRIVATE "${KYTY_SOURCE_DIR}/kernel/macosGuestAddressSpace.cpp")
|
||||||
target_compile_definitions(${target} PRIVATE KYTY_LINKED_GUEST_ADDRESS_SPACE=1)
|
target_compile_definitions(${target} PRIVATE KYTY_LINKED_GUEST_ADDRESS_SPACE=1)
|
||||||
target_link_options(${target} PRIVATE
|
target_link_options(${target} PRIVATE
|
||||||
-Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x40000,-segaddr,SYSTEM_MANAGED,0x40000,-segaddr,SYSTEM_RESERVED,0x7ffffc000,-segaddr,USER_AREA,0x7000000000,-image_base,0x700000000000)
|
-Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x40000,-segaddr,SYSTEM_MANAGED,0x40000,-segaddr,SYSTEM_RESERVED,0x7ffffc000,-segaddr,USER_AREA,0x7000000000,-image_base,0x700000000000)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_kyty_full_emulator_test(shader_cfg_tests ../tests/shaderCfgTests.cpp)
|
add_kyty_full_emulator_test(shader_cfg_tests "${KYTY_TESTS_DIR}/shaderCfgTests.cpp")
|
||||||
|
|
||||||
add_executable(scalar_provenance_tests EXCLUDE_FROM_ALL
|
add_executable(scalar_provenance_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ScalarProvenanceTests.cpp
|
"${KYTY_TESTS_DIR}/ScalarProvenanceTests.cpp"
|
||||||
graphics/host_gpu/hostMemory.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||||
graphics/shader/recompiler/ir/ReadLaneElimination.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ReadLaneElimination.cpp"
|
||||||
graphics/shader/recompiler/ir/ScalarProvenance.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||||
graphics/shader/recompiler/ir/SrtWalker.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(scalar_provenance_tests fmt::fmt)
|
target_link_libraries(scalar_provenance_tests fmt::fmt)
|
||||||
target_include_directories(scalar_provenance_tests PRIVATE ${inc_headers})
|
target_include_directories(scalar_provenance_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(page_manager_tests EXCLUDE_FROM_ALL
|
add_executable(page_manager_tests EXCLUDE_FROM_ALL
|
||||||
../tests/PageManagerTests.cpp
|
"${KYTY_TESTS_DIR}/PageManagerTests.cpp"
|
||||||
graphics/host_gpu/pageManager.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||||
)
|
)
|
||||||
target_include_directories(page_manager_tests PRIVATE ${inc_headers})
|
target_include_directories(page_manager_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
||||||
../tests/BitArrayTests.cpp
|
"${KYTY_TESTS_DIR}/BitArrayTests.cpp"
|
||||||
)
|
)
|
||||||
target_include_directories(bit_array_tests PRIVATE ${inc_headers})
|
target_include_directories(bit_array_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(memory_tracker_tests EXCLUDE_FROM_ALL
|
add_executable(memory_tracker_tests EXCLUDE_FROM_ALL
|
||||||
../tests/MemoryTrackerTests.cpp
|
"${KYTY_TESTS_DIR}/MemoryTrackerTests.cpp"
|
||||||
graphics/host_gpu/pageManager.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||||
graphics/host_gpu/memoryTracker.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/memoryTracker.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(memory_tracker_tests fmt::fmt common)
|
target_link_libraries(memory_tracker_tests fmt::fmt common)
|
||||||
target_include_directories(memory_tracker_tests PRIVATE ${inc_headers})
|
target_include_directories(memory_tracker_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(shader_vertex_metadata_tests EXCLUDE_FROM_ALL
|
add_executable(shader_vertex_metadata_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ShaderVertexMetadataTests.cpp
|
"${KYTY_TESTS_DIR}/ShaderVertexMetadataTests.cpp"
|
||||||
graphics/host_gpu/hostMemory.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||||
graphics/shader/shaderVertexMetadata.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/shaderVertexMetadata.cpp"
|
||||||
)
|
)
|
||||||
target_include_directories(shader_vertex_metadata_tests PRIVATE ${inc_headers})
|
target_include_directories(shader_vertex_metadata_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(shader_stage_runtime_tests EXCLUDE_FROM_ALL
|
add_executable(shader_stage_runtime_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ShaderStageRuntimeTests.cpp
|
"${KYTY_TESTS_DIR}/ShaderStageRuntimeTests.cpp"
|
||||||
graphics/guest_gpu/gpu_format.cpp
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
|
||||||
graphics/host_gpu/hostMemory.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||||
graphics/shader/shaderStageRuntime.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/shaderStageRuntime.cpp"
|
||||||
graphics/shader/recompiler/ir/ResourceMaterialization.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceMaterialization.cpp"
|
||||||
graphics/shader/recompiler/ir/ScalarProvenance.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||||
graphics/shader/recompiler/ir/SrtWalker.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(shader_stage_runtime_tests fmt::fmt)
|
target_link_libraries(shader_stage_runtime_tests fmt::fmt)
|
||||||
target_include_directories(shader_stage_runtime_tests PRIVATE ${inc_headers})
|
target_include_directories(shader_stage_runtime_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ResourceTrackingTests.cpp
|
"${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp"
|
||||||
graphics/guest_gpu/gpu_format.cpp
|
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
|
||||||
graphics/host_gpu/hostMemory.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||||
graphics/shader/recompiler/ir/ScalarProvenance.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||||
graphics/shader/recompiler/ir/SrtWalker.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||||
graphics/shader/recompiler/ir/SrtPatcher.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtPatcher.cpp"
|
||||||
graphics/shader/recompiler/ir/ResourceTracking.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceTracking.cpp"
|
||||||
graphics/shader/recompiler/ir/ResourceMaterialization.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceMaterialization.cpp"
|
||||||
graphics/shader/recompiler/ir/ShaderInfoCollection.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ShaderInfoCollection.cpp"
|
||||||
graphics/shader/recompiler/ir/BindingLayout.cpp
|
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/BindingLayout.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(resource_tracking_tests fmt::fmt)
|
target_link_libraries(resource_tracking_tests fmt::fmt)
|
||||||
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
|
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(resource_mutex_tests EXCLUDE_FROM_ALL
|
add_executable(resource_mutex_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ResourceMutexTests.cpp
|
"${KYTY_TESTS_DIR}/ResourceMutexTests.cpp"
|
||||||
graphics/host_gpu/renderer/cache/resourceMutex.cpp
|
"${KYTY_SOURCE_DIR}/graphics/host_gpu/renderer/cache/resourceMutex.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(resource_mutex_tests common)
|
target_link_libraries(resource_mutex_tests common)
|
||||||
target_include_directories(resource_mutex_tests PRIVATE ${inc_headers})
|
target_include_directories(resource_mutex_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
|
add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
|
||||||
../tests/AudioOut2PortTests.cpp
|
"${KYTY_TESTS_DIR}/AudioOut2PortTests.cpp"
|
||||||
libs/libAudio2.cpp
|
"${KYTY_SOURCE_DIR}/libs/libAudio2.cpp"
|
||||||
loader/timer.cpp
|
"${KYTY_SOURCE_DIR}/loader/timer.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(audio_out2_port_tests common fmt::fmt)
|
target_link_libraries(audio_out2_port_tests common fmt::fmt)
|
||||||
target_include_directories(audio_out2_port_tests PRIVATE ${inc_headers})
|
target_include_directories(audio_out2_port_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
|
add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
|
||||||
../tests/EventQueueLifetimeTests.cpp
|
"${KYTY_TESTS_DIR}/EventQueueLifetimeTests.cpp"
|
||||||
kernel/eventQueue.cpp
|
"${KYTY_SOURCE_DIR}/kernel/eventQueue.cpp"
|
||||||
loader/timer.cpp
|
"${KYTY_SOURCE_DIR}/loader/timer.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(event_queue_lifetime_tests common fmt::fmt)
|
target_link_libraries(event_queue_lifetime_tests common fmt::fmt)
|
||||||
target_include_directories(event_queue_lifetime_tests PRIVATE ${inc_headers})
|
target_include_directories(event_queue_lifetime_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_executable(image_page_table_tests EXCLUDE_FROM_ALL
|
add_executable(image_page_table_tests EXCLUDE_FROM_ALL
|
||||||
../tests/ImagePageTableTests.cpp
|
"${KYTY_TESTS_DIR}/ImagePageTableTests.cpp"
|
||||||
)
|
)
|
||||||
target_link_libraries(image_page_table_tests common fmt::fmt)
|
target_link_libraries(image_page_table_tests common fmt::fmt)
|
||||||
target_include_directories(image_page_table_tests PRIVATE ${inc_headers})
|
target_include_directories(image_page_table_tests PRIVATE ${inc_headers})
|
||||||
|
|
||||||
add_kyty_full_emulator_test(shader_recompiler_compute_tests ../tests/ShaderRecompilerComputeTests.cpp)
|
add_kyty_full_emulator_test(shader_recompiler_compute_tests "${KYTY_TESTS_DIR}/ShaderRecompilerComputeTests.cpp")
|
||||||
|
|
||||||
set(gpu_test_generated_dir "${PROJECT_BINARY_DIR}/gpu_test_shaders")
|
set(gpu_test_generated_dir "${PROJECT_BINARY_DIR}/gpu_test_shaders")
|
||||||
set(gpu_test_ms_depth_source
|
set(gpu_test_ms_depth_source
|
||||||
@@ -437,14 +439,14 @@ add_custom_command(
|
|||||||
-o "${gpu_test_ms_depth_spv}" "${gpu_test_ms_depth_source}"
|
-o "${gpu_test_ms_depth_spv}" "${gpu_test_ms_depth_source}"
|
||||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${gpu_test_ms_depth_spv}
|
COMMAND ${CMAKE_COMMAND} -DINPUT=${gpu_test_ms_depth_spv}
|
||||||
-DOUTPUT=${gpu_test_ms_depth_header} -DSYMBOL=GPU_TEST_MS_DEPTH_SPV
|
-DOUTPUT=${gpu_test_ms_depth_header} -DSYMBOL=GPU_TEST_MS_DEPTH_SPV
|
||||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
|
-P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||||
DEPENDS "${gpu_test_ms_depth_source}"
|
DEPENDS "${gpu_test_ms_depth_source}"
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
target_sources(shader_recompiler_compute_tests PRIVATE
|
target_sources(shader_recompiler_compute_tests PRIVATE
|
||||||
"${gpu_test_ms_depth_header}")
|
"${gpu_test_ms_depth_header}")
|
||||||
|
|
||||||
add_kyty_full_emulator_test(virtual_memory_allocation_tests ../tests/VirtualMemoryAllocationTests.cpp)
|
add_kyty_full_emulator_test(virtual_memory_allocation_tests "${KYTY_TESTS_DIR}/VirtualMemoryAllocationTests.cpp")
|
||||||
target_compile_definitions(virtual_memory_allocation_tests PRIVATE
|
target_compile_definitions(virtual_memory_allocation_tests PRIVATE
|
||||||
KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS=1)
|
KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS=1)
|
||||||
|
|
||||||
@@ -520,7 +522,7 @@ if(BUILD_TESTING)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_executable(kyty_emulator main.cpp ${kyty_emulator_src})
|
add_executable(kyty_emulator "${KYTY_SOURCE_DIR}/main.cpp" ${kyty_emulator_src})
|
||||||
configure_macos_guest_address_space(kyty_emulator)
|
configure_macos_guest_address_space(kyty_emulator)
|
||||||
|
|
||||||
target_link_libraries(kyty_emulator ${kyty_emulator_link_libraries})
|
target_link_libraries(kyty_emulator ${kyty_emulator_link_libraries})
|
||||||
@@ -570,14 +572,14 @@ if(APPLE)
|
|||||||
# reverts to the hardened defaults and aborts when it first executes written code).
|
# reverts to the hardened defaults and aborts when it first executes written code).
|
||||||
add_custom_command(TARGET kyty_emulator POST_BUILD
|
add_custom_command(TARGET kyty_emulator POST_BUILD
|
||||||
COMMAND codesign -s - --force --options runtime
|
COMMAND codesign -s - --force --options runtime
|
||||||
--entitlements "${CMAKE_CURRENT_SOURCE_DIR}/macos_jit.entitlements"
|
--entitlements "${KYTY_SOURCE_DIR}/macos_jit.entitlements"
|
||||||
$<TARGET_FILE:kyty_emulator>
|
$<TARGET_FILE:kyty_emulator>
|
||||||
COMMENT "Codesign kyty_emulator with JIT entitlements (macOS)")
|
COMMENT "Codesign kyty_emulator with JIT entitlements (macOS)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS kyty_emulator DESTINATION .)
|
install(TARGETS kyty_emulator DESTINATION .)
|
||||||
if(KYTY_BUILD_LAUNCHER)
|
if(KYTY_BUILD_LAUNCHER)
|
||||||
add_subdirectory(launcher)
|
add_subdirectory("${KYTY_SOURCE_DIR}/launcher" "${CMAKE_CURRENT_BINARY_DIR}/launcher")
|
||||||
endif()
|
endif()
|
||||||
if(KYTY_CLANG_CL)
|
if(KYTY_CLANG_CL)
|
||||||
install(FILES "${KYTY_THIRD_PARTY_DIR}/winpthread/bin/libwinpthread-1.dll" DESTINATION .)
|
install(FILES "${KYTY_THIRD_PARTY_DIR}/winpthread/bin/libwinpthread-1.dll" DESTINATION .)
|
||||||
@@ -141,7 +141,7 @@ git submodule update --init --recursive
|
|||||||
Configure the project. Replace the Qt path with the version installed on your system:
|
Configure the project. Replace the Qt path with the version installed on your system:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cmake -S src -B _Build/windows -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_PREFIX_PATH="C:/Qt/6.x.x/msvc2022_64"
|
cmake -S . -B _Build/windows -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_PREFIX_PATH="C:/Qt/6.x.x/msvc2022_64"
|
||||||
```
|
```
|
||||||
|
|
||||||
Build the launcher and stage a runnable installation:
|
Build the launcher and stage a runnable installation:
|
||||||
@@ -174,7 +174,7 @@ Qt 6 (Concurrent, Network, Widgets) is also required — either the distribution
|
|||||||
```bash
|
```bash
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
|
||||||
cmake -S src -B _Build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
cmake -S . -B _Build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
||||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR"
|
-DCMAKE_PREFIX_PATH="$Qt6_DIR"
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ The install step copies the Qt libraries and plugins next to the binaries, so
|
|||||||
As on Windows, the MSVC compiler is not used; Clang is required. `cl.exe` is rejected at configure
|
As on Windows, the MSVC compiler is not used; Clang is required. `cl.exe` is rejected at configure
|
||||||
time.
|
time.
|
||||||
|
|
||||||
Note that the CMake source root is `src`, not the repository root.
|
The CMake source root is the repository root.
|
||||||
|
|
||||||
### Building on macOS
|
### Building on macOS
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ Requirements:
|
|||||||
```bash
|
```bash
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
|
||||||
cmake -S src -B _Build/macos -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
cmake -S . -B _Build/macos -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||||
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
||||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR"
|
-DCMAKE_PREFIX_PATH="$Qt6_DIR"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ endif()
|
|||||||
add_library(common STATIC ${common_src} ${common_headers})
|
add_library(common STATIC ${common_src} ${common_headers})
|
||||||
|
|
||||||
target_include_directories(common
|
target_include_directories(common
|
||||||
PUBLIC ${PROJECT_SOURCE_DIR}
|
PUBLIC ${KYTY_SOURCE_DIR}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${KYTY_THIRD_PARTY_DIR}/cpuinfo/include
|
${KYTY_THIRD_PARTY_DIR}/cpuinfo/include
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ uint32_t GetVblankFrequency() {
|
|||||||
return std::clamp(g_config->vblank_frequency, 30u, 360u);
|
return std::clamp(g_config->vblank_frequency, 30u, 360u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GetConsoleLanguage() {
|
||||||
|
return g_config->console_language;
|
||||||
|
}
|
||||||
|
|
||||||
bool VulkanValidationEnabled() {
|
bool VulkanValidationEnabled() {
|
||||||
return g_config->vulkan_validation_enabled;
|
return g_config->vulkan_validation_enabled;
|
||||||
}
|
}
|
||||||
@@ -89,10 +93,6 @@ bool RenderDocEnabled() {
|
|||||||
return g_config->renderdoc_enabled;
|
return g_config->renderdoc_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NggRectlistDrawEnabled() {
|
|
||||||
return g_config->ngg_rectlist_draw_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadbackLinearImagesEnabled() {
|
bool ReadbackLinearImagesEnabled() {
|
||||||
return g_config->readback_linear_images;
|
return g_config->readback_linear_images;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ enum class ProfilerDirection { None, Network };
|
|||||||
|
|
||||||
enum class OutputDirection { Silent, Console, File };
|
enum class OutputDirection { Silent, Console, File };
|
||||||
|
|
||||||
|
constexpr uint32_t DEFAULT_CONSOLE_LANGUAGE = 1;
|
||||||
|
constexpr uint32_t MAX_CONSOLE_LANGUAGE = 29;
|
||||||
|
|
||||||
struct ConfigOptions {
|
struct ConfigOptions {
|
||||||
uint32_t screen_width = 1280;
|
uint32_t screen_width = 1280;
|
||||||
uint32_t screen_height = 720;
|
uint32_t screen_height = 720;
|
||||||
uint32_t vblank_frequency = 60;
|
uint32_t vblank_frequency = 60;
|
||||||
|
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||||
bool vulkan_validation_enabled = false;
|
bool vulkan_validation_enabled = false;
|
||||||
bool shader_validation_enabled = false;
|
bool shader_validation_enabled = false;
|
||||||
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
|
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
|
||||||
@@ -35,7 +39,6 @@ struct ConfigOptions {
|
|||||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||||
bool spirv_debug_printf_enabled = false;
|
bool spirv_debug_printf_enabled = false;
|
||||||
bool renderdoc_enabled = false;
|
bool renderdoc_enabled = false;
|
||||||
bool ngg_rectlist_draw_enabled = true;
|
|
||||||
bool readback_linear_images = false;
|
bool readback_linear_images = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ void Load(const ConfigOptions& cfg);
|
|||||||
uint32_t GetScreenWidth();
|
uint32_t GetScreenWidth();
|
||||||
uint32_t GetScreenHeight();
|
uint32_t GetScreenHeight();
|
||||||
uint32_t GetVblankFrequency();
|
uint32_t GetVblankFrequency();
|
||||||
|
uint32_t GetConsoleLanguage();
|
||||||
bool VulkanValidationEnabled();
|
bool VulkanValidationEnabled();
|
||||||
|
|
||||||
bool ShaderValidationEnabled();
|
bool ShaderValidationEnabled();
|
||||||
@@ -64,7 +68,6 @@ ProfilerDirection GetProfilerDirection();
|
|||||||
bool SpirvDebugPrintfEnabled();
|
bool SpirvDebugPrintfEnabled();
|
||||||
|
|
||||||
bool RenderDocEnabled();
|
bool RenderDocEnabled();
|
||||||
bool NggRectlistDrawEnabled();
|
|
||||||
bool ReadbackLinearImagesEnabled();
|
bool ReadbackLinearImagesEnabled();
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|||||||
@@ -83,8 +83,7 @@ public:
|
|||||||
uint32_t first_instance = 0);
|
uint32_t first_instance = 0);
|
||||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
|
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
|
||||||
void DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
void DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||||
uint32_t render_target_slice_offset = 0, uint32_t instance_count = 1,
|
uint32_t render_target_slice_offset = 0);
|
||||||
uint32_t first_vertex = 0, uint32_t first_instance = 0);
|
|
||||||
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
|
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
|
||||||
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||||
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
|
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
|
||||||
@@ -152,6 +151,9 @@ private:
|
|||||||
uint32_t interrupt_context_id);
|
uint32_t interrupt_context_id);
|
||||||
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
||||||
void SuspendPm4();
|
void SuspendPm4();
|
||||||
|
void SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||||
|
uint32_t render_target_slice_offset, uint32_t first_vertex,
|
||||||
|
uint32_t first_instance);
|
||||||
|
|
||||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||||
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||||
@@ -168,7 +170,8 @@ private:
|
|||||||
uint64_t m_index_base_addr = 0;
|
uint64_t m_index_base_addr = 0;
|
||||||
uint64_t m_draw_indirect_args_base_addr = 0;
|
uint64_t m_draw_indirect_args_base_addr = 0;
|
||||||
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
||||||
uint32_t m_num_instances = 1;
|
// 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_de_count = 0;
|
||||||
uint32_t m_ce_count = 0;
|
uint32_t m_ce_count = 0;
|
||||||
|
|||||||
@@ -983,8 +983,9 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
|||||||
args.start_vertex_location, args.start_instance_location);
|
args.start_vertex_location, args.start_instance_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DrawIndexAuto(args.vertex_count_per_instance, 0, 0, args.instance_count,
|
m_num_instances = args.instance_count;
|
||||||
args.start_vertex_location, args.start_instance_location);
|
SubmitNonIndexedDraw(args.vertex_count_per_instance, 0, 0, args.start_vertex_location,
|
||||||
|
args.start_instance_location);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,6 +1025,7 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_num_instances = args.instance_count;
|
||||||
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
||||||
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
||||||
}
|
}
|
||||||
@@ -1081,8 +1083,9 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
|||||||
args->start_vertex_location, args->start_instance_location);
|
args->start_vertex_location, args->start_instance_location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DrawIndexAuto(args->vertex_count_per_instance, 0, 0, args->instance_count,
|
m_num_instances = args->instance_count;
|
||||||
args->start_vertex_location, args->start_instance_location);
|
SubmitNonIndexedDraw(args->vertex_count_per_instance, 0, 0, args->start_vertex_location,
|
||||||
|
args->start_instance_location);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,6 +1126,7 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_num_instances = args->instance_count;
|
||||||
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
||||||
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
||||||
}
|
}
|
||||||
@@ -1214,12 +1218,17 @@ void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||||
uint32_t render_target_slice_offset, uint32_t instance_count,
|
uint32_t render_target_slice_offset) {
|
||||||
uint32_t first_vertex, uint32_t first_instance) {
|
SubmitNonIndexedDraw(index_count, flags, render_target_slice_offset, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandProcessor::SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||||
|
uint32_t render_target_slice_offset,
|
||||||
|
uint32_t first_vertex, uint32_t first_instance) {
|
||||||
CheckBuffer();
|
CheckBuffer();
|
||||||
|
|
||||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), index_count, flags,
|
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), vertex_count, flags,
|
||||||
render_target_slice_offset, instance_count,
|
render_target_slice_offset, m_num_instances,
|
||||||
first_vertex, first_instance);
|
first_vertex, first_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+154
-242
@@ -7,8 +7,9 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <list>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -103,6 +104,158 @@ private:
|
|||||||
size_t m_allocated_buckets = 0;
|
size_t m_allocated_buckets = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Inline owner storage for page-table entries. Texture pages normally have only a
|
||||||
|
// handful of owners, so avoid a heap allocation on the first 16 registrations.
|
||||||
|
//
|
||||||
|
// Owners are packed into live uint64_t objects instead of being constructed in
|
||||||
|
// raw storage. This keeps sparse bucket construction cheap without relying on
|
||||||
|
// subtle implicit-lifetime or pointer-provenance rules.
|
||||||
|
template <typename OwnerT, size_t InlineCapacity = 16>
|
||||||
|
class InlinePageOwnerList final {
|
||||||
|
public:
|
||||||
|
using value_type = OwnerT;
|
||||||
|
using size_type = size_t;
|
||||||
|
|
||||||
|
class const_iterator final {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] OwnerT operator*() const noexcept { return m_owners->At(m_index); }
|
||||||
|
const_iterator& operator++() noexcept {
|
||||||
|
++m_index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(const const_iterator&) const = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class InlinePageOwnerList;
|
||||||
|
const_iterator(const InlinePageOwnerList* owners, size_type index) noexcept
|
||||||
|
: m_owners(owners), m_index(index) {}
|
||||||
|
|
||||||
|
const InlinePageOwnerList* m_owners = nullptr;
|
||||||
|
size_type m_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(InlineCapacity > 0);
|
||||||
|
static_assert(std::is_default_constructible_v<OwnerT>);
|
||||||
|
static_assert(std::is_trivially_copyable_v<OwnerT>);
|
||||||
|
static_assert(std::has_unique_object_representations_v<OwnerT>);
|
||||||
|
static_assert(sizeof(OwnerT) <= sizeof(uint64_t));
|
||||||
|
|
||||||
|
InlinePageOwnerList() noexcept {}
|
||||||
|
InlinePageOwnerList(const InlinePageOwnerList&) = delete;
|
||||||
|
InlinePageOwnerList& operator=(const InlinePageOwnerList&) = delete;
|
||||||
|
InlinePageOwnerList(InlinePageOwnerList&& other) noexcept
|
||||||
|
: m_overflow(std::move(other.m_overflow)),
|
||||||
|
m_inline_size(std::exchange(other.m_inline_size, 0)) {
|
||||||
|
if (m_overflow == nullptr) {
|
||||||
|
std::copy_n(other.m_inline.begin(), m_inline_size, m_inline.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InlinePageOwnerList& operator=(InlinePageOwnerList&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
m_overflow = std::move(other.m_overflow);
|
||||||
|
m_inline_size = std::exchange(other.m_inline_size, 0);
|
||||||
|
if (m_overflow == nullptr) {
|
||||||
|
std::copy_n(other.m_inline.begin(), m_inline_size, m_inline.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const_iterator begin() const noexcept { return {this, 0}; }
|
||||||
|
[[nodiscard]] const_iterator end() const noexcept { return {this, size()}; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
|
||||||
|
[[nodiscard]] size_type size() const noexcept {
|
||||||
|
return m_overflow == nullptr ? m_inline_size : m_overflow->size();
|
||||||
|
}
|
||||||
|
[[nodiscard]] OwnerT front() const noexcept { return At(0); }
|
||||||
|
[[nodiscard]] OwnerT operator[](size_type index) const noexcept { return At(index); }
|
||||||
|
|
||||||
|
void push_back(const OwnerT& owner) {
|
||||||
|
const uint64_t packed = Pack(owner);
|
||||||
|
if (m_overflow != nullptr) {
|
||||||
|
m_overflow->push_back(packed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_inline_size < InlineCapacity) {
|
||||||
|
m_inline[m_inline_size] = packed;
|
||||||
|
++m_inline_size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto overflow = std::make_unique<std::vector<uint64_t>>();
|
||||||
|
overflow->reserve(InlineCapacity * 2);
|
||||||
|
overflow->assign(m_inline.begin(), m_inline.end());
|
||||||
|
overflow->push_back(packed);
|
||||||
|
m_overflow = std::move(overflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool Contains(const OwnerT& owner) const noexcept {
|
||||||
|
const uint64_t packed = Pack(owner);
|
||||||
|
if (m_overflow != nullptr) {
|
||||||
|
return std::find(m_overflow->begin(), m_overflow->end(), packed) != m_overflow->end();
|
||||||
|
}
|
||||||
|
return std::find(m_inline.begin(), m_inline.begin() + static_cast<ptrdiff_t>(m_inline_size),
|
||||||
|
packed) != m_inline.begin() + static_cast<ptrdiff_t>(m_inline_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool Erase(const OwnerT& owner) noexcept {
|
||||||
|
const uint64_t packed = Pack(owner);
|
||||||
|
if (m_overflow != nullptr) {
|
||||||
|
const auto found = std::find(m_overflow->begin(), m_overflow->end(), packed);
|
||||||
|
if (found == m_overflow->end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_overflow->erase(found);
|
||||||
|
if (m_overflow->size() == InlineCapacity) {
|
||||||
|
std::copy(m_overflow->begin(), m_overflow->end(), m_inline.begin());
|
||||||
|
m_inline_size = InlineCapacity;
|
||||||
|
m_overflow.reset();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto end = m_inline.begin() + static_cast<ptrdiff_t>(m_inline_size);
|
||||||
|
const auto found = std::find(m_inline.begin(), end, packed);
|
||||||
|
if (found == end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::move(found + 1, end, found);
|
||||||
|
--m_inline_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void ForEach(Func&& func) const {
|
||||||
|
if (m_overflow != nullptr) {
|
||||||
|
for (const uint64_t owner: *m_overflow) {
|
||||||
|
func(Unpack(owner));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_type index = 0; index < m_inline_size; ++index) {
|
||||||
|
func(Unpack(m_inline[index]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] OwnerT At(size_type index) const noexcept {
|
||||||
|
return Unpack(m_overflow == nullptr ? m_inline[index] : (*m_overflow)[index]);
|
||||||
|
}
|
||||||
|
[[nodiscard]] static uint64_t Pack(const OwnerT& owner) noexcept {
|
||||||
|
uint64_t packed = 0;
|
||||||
|
std::memcpy(&packed, &owner, sizeof(owner));
|
||||||
|
return packed;
|
||||||
|
}
|
||||||
|
[[nodiscard]] static OwnerT Unpack(uint64_t packed) noexcept {
|
||||||
|
OwnerT owner {};
|
||||||
|
std::memcpy(&owner, &packed, sizeof(owner));
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint64_t, InlineCapacity> m_inline;
|
||||||
|
std::unique_ptr<std::vector<uint64_t>> m_overflow;
|
||||||
|
size_type m_inline_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// Removes only the requested owner. Other owners in the same page entry remain intact.
|
// Removes only the requested owner. Other owners in the same page entry remain intact.
|
||||||
template <typename Container, typename Value>
|
template <typename Container, typename Value>
|
||||||
[[nodiscard]] bool EraseExact(Container& owners, const Value& owner) {
|
[[nodiscard]] bool EraseExact(Container& owners, const Value& owner) {
|
||||||
@@ -114,247 +267,6 @@ template <typename Container, typename Value>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-range ownership with 1 MiB candidate buckets and precise 4 KiB
|
|
||||||
// lifetime accounting. OwnerT only needs equality.
|
|
||||||
template <typename OwnerT>
|
|
||||||
class MultiRangePageOwnerIndex final {
|
|
||||||
private:
|
|
||||||
struct Registration;
|
|
||||||
using MembershipList = std::vector<const Registration*>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct ByteRange final {
|
|
||||||
uint64_t address = 0;
|
|
||||||
uint64_t size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using CoarseTable = MultiLevelPageTable<MembershipList, 20, 40, 10>;
|
|
||||||
using TrackingTable = MultiLevelPageTable<MembershipList, 12, 40, 18>;
|
|
||||||
|
|
||||||
[[nodiscard]] bool Register(const OwnerT& owner, const std::vector<ByteRange>& ranges) {
|
|
||||||
if (FindRegistration(owner) != m_registrations.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto normalized = Normalize(ranges);
|
|
||||||
if (normalized.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_registrations.push_back({owner, std::move(normalized)});
|
|
||||||
const Registration* registration = &m_registrations.back();
|
|
||||||
for (const size_t page: CollectPages<20>(registration->ranges)) {
|
|
||||||
m_coarse_pages[page].push_back(registration);
|
|
||||||
}
|
|
||||||
for (const size_t page: CollectPages<12>(registration->ranges)) {
|
|
||||||
m_tracking_pages[page].push_back(registration);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No state changes if an expected membership is absent. Final releases are
|
|
||||||
// sorted and coalesced 4 KiB spans whose final owner disappeared.
|
|
||||||
[[nodiscard]] bool Unregister(const OwnerT& owner, std::vector<ByteRange>& final_releases) {
|
|
||||||
final_releases.clear();
|
|
||||||
auto registration = FindRegistration(owner);
|
|
||||||
if (registration == m_registrations.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto coarse_pages = CollectPages<20>(registration->ranges);
|
|
||||||
const auto tracking_pages = CollectPages<12>(registration->ranges);
|
|
||||||
const Registration* registration_ptr = &*registration;
|
|
||||||
if (!HasAllMemberships(m_coarse_pages, coarse_pages, registration_ptr) ||
|
|
||||||
!HasAllMemberships(m_tracking_pages, tracking_pages, registration_ptr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::vector<size_t> final_pages;
|
|
||||||
for (const size_t page: coarse_pages) {
|
|
||||||
(void)EraseExact(*m_coarse_pages.Find(page), registration_ptr);
|
|
||||||
}
|
|
||||||
for (const size_t page: tracking_pages) {
|
|
||||||
auto& owners = *m_tracking_pages.Find(page);
|
|
||||||
if (owners.size() == 1) {
|
|
||||||
final_pages.push_back(page);
|
|
||||||
}
|
|
||||||
(void)EraseExact(owners, registration_ptr);
|
|
||||||
}
|
|
||||||
m_registrations.erase(registration);
|
|
||||||
final_releases = CoalesceTrackingPages(final_pages);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<OwnerT> Query(uint64_t address, uint64_t size) const {
|
|
||||||
return Query(address, size, [](const OwnerT&) { return true; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Predicate>
|
|
||||||
[[nodiscard]] std::vector<OwnerT> Query(uint64_t address, uint64_t size,
|
|
||||||
Predicate&& predicate) const {
|
|
||||||
return QueryImpl(address, size, true, std::forward<Predicate>(predicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fault paths use page candidates: exact byte-disjoint owners sharing a
|
|
||||||
// touched 4 KiB page are intentionally retained.
|
|
||||||
[[nodiscard]] std::vector<OwnerT> QueryCandidates(uint64_t address, uint64_t size) const {
|
|
||||||
return QueryCandidates(address, size, [](const OwnerT&) { return true; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Predicate>
|
|
||||||
[[nodiscard]] std::vector<OwnerT> QueryCandidates(uint64_t address, uint64_t size,
|
|
||||||
Predicate&& predicate) const {
|
|
||||||
return QueryImpl(address, size, false, std::forward<Predicate>(predicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t CoarseMembershipCount(size_t page) const {
|
|
||||||
const auto* owners = m_coarse_pages.Find(page);
|
|
||||||
return owners == nullptr ? 0 : owners->size();
|
|
||||||
}
|
|
||||||
[[nodiscard]] size_t TrackingMembershipCount(size_t page) const {
|
|
||||||
const auto* owners = m_tracking_pages.Find(page);
|
|
||||||
return owners == nullptr ? 0 : owners->size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename Predicate>
|
|
||||||
[[nodiscard]] std::vector<OwnerT> QueryImpl(uint64_t address, uint64_t size, bool strict_bytes,
|
|
||||||
Predicate&& predicate) const {
|
|
||||||
typename CoarseTable::PageRange coarse_range {};
|
|
||||||
typename TrackingTable::PageRange tracking_range {};
|
|
||||||
if (!CoarseTable::TryGetPageRange(address, size, coarse_range) ||
|
|
||||||
(!strict_bytes && !TrackingTable::TryGetPageRange(address, size, tracking_range))) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
MembershipList candidates;
|
|
||||||
for (size_t page = coarse_range.first; page < coarse_range.last_exclusive; ++page) {
|
|
||||||
if (const auto* owners = m_coarse_pages.Find(page); owners != nullptr) {
|
|
||||||
AppendUnique(candidates, *owners);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<OwnerT> result;
|
|
||||||
for (const Registration* registration: candidates) {
|
|
||||||
if ((strict_bytes ? Overlaps(registration->ranges, address, size)
|
|
||||||
: HasTrackingMembership(registration, tracking_range)) &&
|
|
||||||
predicate(registration->owner)) {
|
|
||||||
result.push_back(registration->owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Registration final {
|
|
||||||
OwnerT owner;
|
|
||||||
std::vector<ByteRange> ranges;
|
|
||||||
};
|
|
||||||
using RegistrationIterator = typename std::list<Registration>::iterator;
|
|
||||||
using ConstRegistrationIterator = typename std::list<Registration>::const_iterator;
|
|
||||||
|
|
||||||
[[nodiscard]] RegistrationIterator FindRegistration(const OwnerT& owner) {
|
|
||||||
return std::find_if(m_registrations.begin(), m_registrations.end(),
|
|
||||||
[&](const Registration& item) { return item.owner == owner; });
|
|
||||||
}
|
|
||||||
[[nodiscard]] ConstRegistrationIterator FindRegistration(const OwnerT& owner) const {
|
|
||||||
return std::find_if(m_registrations.begin(), m_registrations.end(),
|
|
||||||
[&](const Registration& item) { return item.owner == owner; });
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static std::vector<ByteRange> Normalize(const std::vector<ByteRange>& ranges) {
|
|
||||||
std::vector<ByteRange> sorted;
|
|
||||||
for (const auto& range: ranges) {
|
|
||||||
typename CoarseTable::PageRange ignored {};
|
|
||||||
if (!CoarseTable::TryGetPageRange(range.address, range.size, ignored)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
sorted.push_back(range);
|
|
||||||
}
|
|
||||||
std::sort(sorted.begin(), sorted.end(), [](const ByteRange& lhs, const ByteRange& rhs) {
|
|
||||||
return lhs.address < rhs.address;
|
|
||||||
});
|
|
||||||
std::vector<ByteRange> merged;
|
|
||||||
for (const auto& range: sorted) {
|
|
||||||
if (merged.empty() || range.address > merged.back().address + merged.back().size) {
|
|
||||||
merged.push_back(range);
|
|
||||||
} else {
|
|
||||||
const uint64_t end = std::max(merged.back().address + merged.back().size,
|
|
||||||
range.address + range.size);
|
|
||||||
merged.back().size = end - merged.back().address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t Bits>
|
|
||||||
[[nodiscard]] static std::vector<size_t> CollectPages(const std::vector<ByteRange>& ranges) {
|
|
||||||
std::vector<size_t> pages;
|
|
||||||
for (const auto& range: ranges) {
|
|
||||||
const size_t first = static_cast<size_t>(range.address >> Bits);
|
|
||||||
const size_t last = static_cast<size_t>((range.address + range.size - 1) >> Bits);
|
|
||||||
for (size_t page = first; page <= last; ++page) {
|
|
||||||
pages.push_back(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::sort(pages.begin(), pages.end());
|
|
||||||
pages.erase(std::unique(pages.begin(), pages.end()), pages.end());
|
|
||||||
return pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Table>
|
|
||||||
[[nodiscard]] static bool HasAllMemberships(const Table& table,
|
|
||||||
const std::vector<size_t>& pages,
|
|
||||||
const Registration* registration) {
|
|
||||||
for (const size_t page: pages) {
|
|
||||||
const auto* owners = table.Find(page);
|
|
||||||
if (owners == nullptr ||
|
|
||||||
std::find(owners->begin(), owners->end(), registration) == owners->end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasTrackingMembership(const Registration* registration,
|
|
||||||
const typename TrackingTable::PageRange& range) const {
|
|
||||||
for (size_t page = range.first; page < range.last_exclusive; ++page) {
|
|
||||||
const auto* owners = m_tracking_pages.Find(page);
|
|
||||||
if (owners != nullptr &&
|
|
||||||
std::find(owners->begin(), owners->end(), registration) != owners->end()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static bool Overlaps(const std::vector<ByteRange>& ranges, uint64_t address,
|
|
||||||
uint64_t size) {
|
|
||||||
const uint64_t end = address + size;
|
|
||||||
return std::any_of(ranges.begin(), ranges.end(), [&](const ByteRange& range) {
|
|
||||||
return range.address < end && address < range.address + range.size;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
static void AppendUnique(MembershipList& destination, const MembershipList& source) {
|
|
||||||
for (const Registration* registration: source) {
|
|
||||||
if (std::find(destination.begin(), destination.end(), registration) ==
|
|
||||||
destination.end()) {
|
|
||||||
destination.push_back(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[nodiscard]] static std::vector<ByteRange>
|
|
||||||
CoalesceTrackingPages(const std::vector<size_t>& pages) {
|
|
||||||
std::vector<ByteRange> result;
|
|
||||||
for (const size_t page: pages) {
|
|
||||||
const uint64_t address = static_cast<uint64_t>(page) << 12;
|
|
||||||
if (!result.empty() && result.back().address + result.back().size == address) {
|
|
||||||
result.back().size += uint64_t {1} << 12;
|
|
||||||
} else {
|
|
||||||
result.push_back({address, uint64_t {1} << 12});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoarseTable m_coarse_pages;
|
|
||||||
TrackingTable m_tracking_pages;
|
|
||||||
std::list<Registration> m_registrations;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_MULTILEVELPAGETABLE_H_
|
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_MULTILEVELPAGETABLE_H_
|
||||||
|
|||||||
+83
-18
@@ -195,10 +195,12 @@ void TextureCache::RegisterImage(ImageId id) {
|
|||||||
if (image.registered || image.info.data.Empty()) {
|
if (image.registered || image.info.data.Empty()) {
|
||||||
EXIT("TextureCache: invalid image registration\n");
|
EXIT("TextureCache: invalid image registration\n");
|
||||||
}
|
}
|
||||||
std::vector<ImageOwnerIndex::ByteRange> ranges;
|
ImagePageTable::PageRange pages {};
|
||||||
ranges.push_back({image.info.data.address, image.info.data.size});
|
if (!ImagePageTable::TryGetPageRange(image.info.data.address, image.info.data.size, pages)) {
|
||||||
if (!m_image_owner_index.Register(id, ranges)) {
|
EXIT("TextureCache: image registration is outside the guest address space\n");
|
||||||
EXIT("TextureCache: duplicate or invalid image registration\n");
|
}
|
||||||
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
|
m_image_page_table[page].push_back(id);
|
||||||
}
|
}
|
||||||
image.registered = true;
|
image.registered = true;
|
||||||
image.lru_id = m_lru_cache.Insert(id, m_gc_tick);
|
image.lru_id = m_lru_cache.Insert(id, m_gc_tick);
|
||||||
@@ -211,9 +213,15 @@ void TextureCache::UnregisterImage(ImageId id) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UntrackImage(id);
|
UntrackImage(id);
|
||||||
std::vector<ImageOwnerIndex::ByteRange> releases;
|
ImagePageTable::PageRange pages {};
|
||||||
if (!m_image_owner_index.Unregister(id, releases)) {
|
if (!ImagePageTable::TryGetPageRange(image.info.data.address, image.info.data.size, pages)) {
|
||||||
EXIT("TextureCache: image missing from owner index\n");
|
EXIT("TextureCache: registered image is outside the guest address space\n");
|
||||||
|
}
|
||||||
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
|
auto* owners = m_image_page_table.Find(page);
|
||||||
|
if (owners == nullptr || !owners->Erase(id)) {
|
||||||
|
EXIT("TextureCache: image missing from page owner index\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_lru_cache.Free(image.lru_id);
|
m_lru_cache.Free(image.lru_id);
|
||||||
const auto accounted = image.AccountedSize();
|
const auto accounted = image.AccountedSize();
|
||||||
@@ -451,10 +459,48 @@ const Image& TextureCache::GetImage(ImageId id) const {
|
|||||||
return ResolveImage(id);
|
return ResolveImage(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ImageId> TextureCache::FindImagesInRegion(uint64_t address, uint64_t size,
|
TextureCache::ImageIds TextureCache::FindImagesInRegion(uint64_t address, uint64_t size,
|
||||||
bool page_overlap) const {
|
bool page_overlap) const {
|
||||||
return page_overlap ? m_image_owner_index.QueryCandidates(address, size)
|
ImagePageTable::PageRange pages {};
|
||||||
: m_image_owner_index.Query(address, size);
|
if (!ImagePageTable::TryGetPageRange(address, size, pages)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t query_epoch = ++m_image_query_epoch;
|
||||||
|
if (query_epoch == 0) {
|
||||||
|
for (const auto& slot: m_slots) {
|
||||||
|
if (slot.image != nullptr) {
|
||||||
|
slot.image->query_epoch = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query_epoch = ++m_image_query_epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIds result;
|
||||||
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
|
const auto* owners = m_image_page_table.Find(page);
|
||||||
|
if (owners == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
owners->ForEach([&](ImageId id) {
|
||||||
|
if (!id || id.index >= m_slots.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& slot = m_slots[id.index];
|
||||||
|
if (slot.generation != id.generation || slot.image == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& image = *slot.image;
|
||||||
|
if (image.query_epoch == query_epoch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
image.query_epoch = query_epoch;
|
||||||
|
if (image.Overlaps(address, size, page_overlap)) {
|
||||||
|
result.push_back(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageId TextureCache::GetNullImage(const ImageDesc& desc) {
|
ImageId TextureCache::GetNullImage(const ImageDesc& desc) {
|
||||||
@@ -838,11 +884,20 @@ struct TextureCache::ColorTransferPlan {
|
|||||||
TextureUploadLayout layout;
|
TextureUploadLayout layout;
|
||||||
std::vector<vk::BufferImageCopy> regions;
|
std::vector<vk::BufferImageCopy> regions;
|
||||||
std::vector<GpuTileInfo> tiles;
|
std::vector<GpuTileInfo> tiles;
|
||||||
|
uint64_t linear_size = 0;
|
||||||
bool tiled = false;
|
bool tiled = false;
|
||||||
bool swap_bgra16 = false;
|
bool swap_bgra16 = false;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint64_t GetLinearSize(std::span<const GpuTileInfo> tiles) {
|
||||||
|
uint64_t size = 0;
|
||||||
|
for (const auto& tile: tiles) {
|
||||||
|
size = std::max(size, tile.linear_offset + tile.linear_size);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
struct TextureCache::DownloadPlan {
|
struct TextureCache::DownloadPlan {
|
||||||
ColorTransferPlan color;
|
ColorTransferPlan color;
|
||||||
bool depth = false;
|
bool depth = false;
|
||||||
@@ -919,6 +974,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding,
|
|||||||
info.resources.levels, plan.tiles)) {
|
info.resources.levels, plan.tiles)) {
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
plan.linear_size = GetLinearSize(plan.tiles);
|
||||||
}
|
}
|
||||||
plan.valid = true;
|
plan.valid = true;
|
||||||
return plan;
|
return plan;
|
||||||
@@ -956,11 +1012,19 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour
|
|||||||
|
|
||||||
if (desc.type != BindingType::DepthTarget) {
|
if (desc.type != BindingType::DepthTarget) {
|
||||||
auto plan = BuildColorTransfer(image, desc.type, TransferDirection::Upload);
|
auto plan = BuildColorTransfer(image, desc.type, TransferDirection::Upload);
|
||||||
EXIT_NOT_IMPLEMENTED(!plan.valid);
|
if (!plan.valid) {
|
||||||
|
EXIT("TextureCache: invalid color upload: binding=%u addr=0x%016" PRIx64
|
||||||
|
" 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);
|
||||||
|
}
|
||||||
TileManager::Result linear {source.Handle(), source_offset, info.data.size};
|
TileManager::Result linear {source.Handle(), source_offset, info.data.size};
|
||||||
if (plan.tiled) {
|
if (plan.tiled) {
|
||||||
linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size, info.data.size,
|
linear = m_tiler->Detile(source.Handle(), source_offset, info.data.size,
|
||||||
plan.tiles);
|
plan.linear_size, plan.tiles);
|
||||||
}
|
}
|
||||||
if (plan.swap_bgra16) {
|
if (plan.swap_bgra16) {
|
||||||
linear = m_tiler->SwapBgra16(linear);
|
linear = m_tiler->SwapBgra16(linear);
|
||||||
@@ -1134,9 +1198,9 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
|||||||
ImageId result {};
|
ImageId result {};
|
||||||
bool inserted_new = false;
|
bool inserted_new = false;
|
||||||
{
|
{
|
||||||
std::lock_guard transaction(m_resource_mutex);
|
std::lock_guard transaction(m_resource_mutex);
|
||||||
CacheLock lock(*this, m_lock);
|
CacheLock lock(*this, m_lock);
|
||||||
const std::vector<ImageId> candidates =
|
const auto candidates =
|
||||||
FindImagesInRegion(desc.info.data.address, desc.info.data.size, false);
|
FindImagesInRegion(desc.info.data.address, desc.info.data.size, false);
|
||||||
|
|
||||||
for (const auto id: candidates) {
|
for (const auto id: candidates) {
|
||||||
@@ -1566,7 +1630,7 @@ void TextureCache::DownloadImageData(Image& image, Buffer& destination, uint64_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_tiler->TileImage(image, color.regions, destination.Handle(), destination_offset,
|
m_tiler->TileImage(image, color.regions, destination.Handle(), destination_offset,
|
||||||
destination_size, destination_size, color.tiles, transform);
|
destination_size, color.linear_size, color.tiles, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size) {
|
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size) {
|
||||||
@@ -1659,6 +1723,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin
|
|||||||
image.info.TransferLayers(), levels, color.tiles)) {
|
image.info.TransferLayers(), levels, color.tiles)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
color.linear_size = GetLinearSize(color.tiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_texture_cache.DownloadImageData(image, buffer, buf_offset, copy_size, std::move(plan));
|
m_texture_cache.DownloadImageData(image, buffer, buf_offset, copy_size, std::move(plan));
|
||||||
|
|||||||
+10
-7
@@ -99,7 +99,8 @@ private:
|
|||||||
int32_t layer = -1;
|
int32_t layer = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ImageOwnerIndex = MultiRangePageOwnerIndex<ImageId>;
|
using ImageIds = InlinePageOwnerList<ImageId, 16>;
|
||||||
|
using ImagePageTable = MultiLevelPageTable<ImageIds, 20, 40, 10>;
|
||||||
|
|
||||||
[[nodiscard]] Image& ResolveImage(ImageId id);
|
[[nodiscard]] Image& ResolveImage(ImageId id);
|
||||||
[[nodiscard]] const Image& ResolveImage(ImageId id) const;
|
[[nodiscard]] const Image& ResolveImage(ImageId id) const;
|
||||||
@@ -125,8 +126,9 @@ private:
|
|||||||
[[nodiscard]] static BindingType UploadBinding(const Image& image);
|
[[nodiscard]] static BindingType UploadBinding(const Image& image);
|
||||||
[[nodiscard]] bool SafeToDownload(const Image& image);
|
[[nodiscard]] bool SafeToDownload(const Image& image);
|
||||||
|
|
||||||
[[nodiscard]] std::vector<ImageId> FindImagesInRegion(uint64_t address, uint64_t size,
|
// Caller holds m_lock; it also serializes the per-image query epoch.
|
||||||
bool page_overlap) const;
|
[[nodiscard]] ImageIds FindImagesInRegion(uint64_t address, uint64_t size,
|
||||||
|
bool page_overlap) const;
|
||||||
[[nodiscard]] OverlapResult ResolveOverlap(const ImageInfo& requested, BindingType binding,
|
[[nodiscard]] OverlapResult ResolveOverlap(const ImageInfo& requested, BindingType binding,
|
||||||
ImageId cached, ImageId merged);
|
ImageId cached, ImageId merged);
|
||||||
[[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding,
|
[[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding,
|
||||||
@@ -169,7 +171,7 @@ private:
|
|||||||
ResourceMutex& m_resource_mutex;
|
ResourceMutex& m_resource_mutex;
|
||||||
std::vector<Slot> m_slots;
|
std::vector<Slot> m_slots;
|
||||||
std::vector<uint32_t> m_free_slots;
|
std::vector<uint32_t> m_free_slots;
|
||||||
ImageOwnerIndex m_image_owner_index;
|
ImagePageTable m_image_page_table;
|
||||||
std::map<vk::Format, ImageId> m_null_images;
|
std::map<vk::Format, ImageId> m_null_images;
|
||||||
Common::LeastRecentlyUsedCache<ImageId, uint64_t> m_lru_cache;
|
Common::LeastRecentlyUsedCache<ImageId, uint64_t> m_lru_cache;
|
||||||
std::set<ImageId> m_download_images;
|
std::set<ImageId> m_download_images;
|
||||||
@@ -177,9 +179,10 @@ private:
|
|||||||
uint64_t m_total_used_memory = 0;
|
uint64_t m_total_used_memory = 0;
|
||||||
uint64_t m_trigger_gc_memory = 0;
|
uint64_t m_trigger_gc_memory = 0;
|
||||||
uint64_t m_pressure_gc_memory = 1536ull * 1024 * 1024;
|
uint64_t m_pressure_gc_memory = 1536ull * 1024 * 1024;
|
||||||
uint64_t m_critical_gc_memory = 3ull * 1024 * 1024 * 1024;
|
uint64_t m_critical_gc_memory = 3ull * 1024 * 1024 * 1024;
|
||||||
uint64_t m_gc_tick = 0;
|
uint64_t m_gc_tick = 0;
|
||||||
bool m_readback_linear_images = false;
|
mutable uint32_t m_image_query_epoch = 0;
|
||||||
|
bool m_readback_linear_images = false;
|
||||||
|
|
||||||
friend struct TextureCacheTestAccess;
|
friend struct TextureCacheTestAccess;
|
||||||
friend class BufferCache;
|
friend class BufferCache;
|
||||||
|
|||||||
@@ -154,17 +154,18 @@ public:
|
|||||||
}
|
}
|
||||||
[[nodiscard]] uint64_t HashGuestEdges() const;
|
[[nodiscard]] uint64_t HashGuestEdges() const;
|
||||||
|
|
||||||
ImageInfo info;
|
ImageInfo info;
|
||||||
VulkanImage backing;
|
VulkanImage backing;
|
||||||
ImageViewCache views;
|
ImageViewCache views;
|
||||||
ImageUsage usage;
|
ImageUsage usage;
|
||||||
ImageBinding binding;
|
ImageBinding binding;
|
||||||
bool registered = false;
|
bool registered = false;
|
||||||
uint64_t track_addr = 0;
|
mutable uint32_t query_epoch = 0;
|
||||||
uint64_t track_addr_end = 0;
|
uint64_t track_addr = 0;
|
||||||
ImageId depth_id {};
|
uint64_t track_addr_end = 0;
|
||||||
uint64_t tick_accessed_last = 0;
|
ImageId depth_id {};
|
||||||
size_t lru_id = 0;
|
uint64_t tick_accessed_last = 0;
|
||||||
|
size_t lru_id = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct ImageTestAccess;
|
friend struct ImageTestAccess;
|
||||||
|
|||||||
@@ -482,10 +482,10 @@ static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||||
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
uint64_t levels, std::vector<GpuTileInfo>& out_infos) {
|
||||||
if (size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
if (tiled_size == 0 || levels == 0 || levels > 16 || depth == 0 ||
|
||||||
regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
|
regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
|
||||||
Prospero::IsFmaskTextureFormat(fmt)) {
|
Prospero::IsFmaskTextureFormat(fmt)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -538,8 +538,9 @@ bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCo
|
|||||||
const uint64_t linear_span =
|
const uint64_t linear_span =
|
||||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||||
layout.level_sizes[level].size;
|
layout.level_sizes[level].size;
|
||||||
if (!SetGpuTileSize(info.linear_offset, linear_span, size, info.linear_size) ||
|
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], size,
|
info.linear_size) ||
|
||||||
|
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], tiled_size,
|
||||||
info.tiled_size)) {
|
info.tiled_size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -588,8 +589,9 @@ bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCo
|
|||||||
info.bytes_per_element = block.bytes_per_element;
|
info.bytes_per_element = block.bytes_per_element;
|
||||||
info.linear_offset = region.bufferOffset;
|
info.linear_offset = region.bufferOffset;
|
||||||
info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z);
|
info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z);
|
||||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, size, info.linear_size) ||
|
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), size,
|
info.linear_size) ||
|
||||||
|
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), tiled_size,
|
||||||
info.tiled_size)) {
|
info.tiled_size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
|||||||
uint32_t width, uint32_t height,
|
uint32_t width, uint32_t height,
|
||||||
uint32_t depth, uint64_t levels,
|
uint32_t depth, uint64_t levels,
|
||||||
bool array_texture, bool volume_texture);
|
bool array_texture, bool volume_texture);
|
||||||
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<vk::BufferImageCopy>& regions,
|
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||||
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
|
||||||
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
uint64_t levels, std::vector<GpuTileInfo>& infos);
|
||||||
|
|
||||||
|
|||||||
@@ -391,9 +391,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
|||||||
const bool supported_swizzle =
|
const bool supported_swizzle =
|
||||||
IsValidImageSwizzle(swizzle) &&
|
IsValidImageSwizzle(swizzle) &&
|
||||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
|
|
||||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
||||||
supported_mip_view && descriptor.BaseLevel() == descriptor.LastLevel() &&
|
descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||||
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
|
||||||
}
|
}
|
||||||
@@ -618,14 +617,15 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
const bool multisampled = IsMultisampledTexture(type);
|
const bool multisampled = IsMultisampledTexture(type);
|
||||||
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
|
const auto levels = multisampled ? 1u : static_cast<uint32_t>(descriptor.MaxMip()) + 1u;
|
||||||
const auto tile = descriptor.TileMode();
|
const auto tile = descriptor.TileMode();
|
||||||
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
const bool depth_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||||
const bool msaa_tile =
|
const bool msaa_tile =
|
||||||
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
depth_tile || tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||||
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray;
|
||||||
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
|
if ((!multisampled && (base_level > last_level || last_level >= levels)) ||
|
||||||
(multisampled &&
|
(multisampled &&
|
||||||
(base_level != 0 || last_level == 0 || last_level > 3 ||
|
(base_level != 0 || last_level == 0 || last_level > 3 ||
|
||||||
descriptor.MaxMip() != last_level || !msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
descriptor.MaxMip() != last_level || !msaa_tile ||
|
||||||
|
(descriptor.MsaaDepth() && !depth_tile) ||
|
||||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
||||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
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 "
|
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||||
@@ -634,7 +634,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6],
|
||||||
|
descriptor.fields[7]);
|
||||||
}
|
}
|
||||||
const auto samples = multisampled ? 1u << last_level : 1u;
|
const auto samples = multisampled ? 1u << last_level : 1u;
|
||||||
const auto view_levels =
|
const auto view_levels =
|
||||||
@@ -661,8 +662,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
TileSizeAlign size {};
|
TileSizeAlign size {};
|
||||||
if (multisampled) {
|
if (multisampled) {
|
||||||
const auto bytes = Prospero::NumBytesPerElement(format);
|
const auto bytes = Prospero::NumBytesPerElement(format);
|
||||||
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
pitch = depth_tile ? TileGetDepthPitch(width, bytes, last_level)
|
||||||
: TileGetRenderTargetPitch(width, bytes, last_level);
|
: TileGetRenderTargetPitch(width, bytes, last_level);
|
||||||
if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) ||
|
if (pitch == 0 || !TileGetRenderTargetSize(width, height, pitch, bytes, size, last_level) ||
|
||||||
size.size > UINT32_MAX / image_layers) {
|
size.size > UINT32_MAX / image_layers) {
|
||||||
EXIT("unsupported multisample texture layout\n");
|
EXIT("unsupported multisample texture layout\n");
|
||||||
@@ -679,8 +680,8 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
|
|||||||
ValidateStorageTexture(resource, descriptor, size.size);
|
ValidateStorageTexture(resource, descriptor, size.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pixel_format = TextureGetFormat(format);
|
const auto pixel_format = TextureGetFormat(format);
|
||||||
const auto storage_view_format =
|
const auto storage_view_format =
|
||||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
|
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt)
|
||||||
? vk::Format::eR32Uint
|
? vk::Format::eR32Uint
|
||||||
: SrgbStorageViewFormat(pixel_format);
|
: SrgbStorageViewFormat(pixel_format);
|
||||||
|
|||||||
@@ -154,8 +154,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
||||||
static_params.color_mask[i] = color_mask[i];
|
static_params.color_mask[i] = color_mask[i];
|
||||||
}
|
}
|
||||||
static_params.cull_back = mc.cull_back;
|
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
||||||
static_params.cull_front = mc.cull_front;
|
static_params.cull_back = !rect_list && mc.cull_back;
|
||||||
|
static_params.cull_front = !rect_list && mc.cull_front;
|
||||||
static_params.face = mc.face;
|
static_params.face = mc.face;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < color_count; i++) {
|
for (uint32_t i = 0; i < color_count; i++) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -463,8 +464,12 @@ void CreatePipelineInternal(
|
|||||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||||
|
|
||||||
vk::ShaderModule vert_shader_module = nullptr;
|
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
|
||||||
vk::ShaderModule frag_shader_module = nullptr;
|
|
||||||
|
vk::ShaderModule vert_shader_module = nullptr;
|
||||||
|
vk::ShaderModule tess_control_shader_module = nullptr;
|
||||||
|
vk::ShaderModule tess_eval_shader_module = nullptr;
|
||||||
|
vk::ShaderModule frag_shader_module = nullptr;
|
||||||
|
|
||||||
vk::ShaderModuleCreateInfo create_info {};
|
vk::ShaderModuleCreateInfo create_info {};
|
||||||
|
|
||||||
@@ -491,8 +496,33 @@ void CreatePipelineInternal(
|
|||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
}
|
}
|
||||||
|
if (rect_list) {
|
||||||
|
const auto shaders =
|
||||||
|
BuildRectListShaders(vs_input_info, ps_active ? ps_input_info : nullptr);
|
||||||
|
create_info.codeSize = shaders.control.size() * 4;
|
||||||
|
create_info.pCode = shaders.control.data();
|
||||||
|
result =
|
||||||
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_control_shader_module);
|
||||||
|
if (graphics_debug_dump_enabled()) {
|
||||||
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TCS done result=%s module=%p\n",
|
||||||
|
VulkanToString(result).c_str(), static_cast<void*>(tess_control_shader_module));
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
|
|
||||||
|
create_info.codeSize = shaders.evaluation.size() * 4;
|
||||||
|
create_info.pCode = shaders.evaluation.data();
|
||||||
|
result =
|
||||||
|
graphics.device.createShaderModule(&create_info, nullptr, &tess_eval_shader_module);
|
||||||
|
if (graphics_debug_dump_enabled()) {
|
||||||
|
LOGF("PipelineTrace: vkCreateShaderModule RectList TES done result=%s module=%p\n",
|
||||||
|
VulkanToString(result).c_str(), static_cast<void*>(tess_eval_shader_module));
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr);
|
EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr);
|
||||||
|
EXIT_NOT_IMPLEMENTED(
|
||||||
|
rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr));
|
||||||
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
|
EXIT_NOT_IMPLEMENTED(ps_active && frag_shader_module == nullptr);
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
vk::PipelineShaderStageCreateInfo vert_shader_stage_info {};
|
||||||
@@ -525,9 +555,28 @@ void CreatePipelineInternal(
|
|||||||
frag_shader_stage_info);
|
frag_shader_stage_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info,
|
vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {};
|
||||||
frag_shader_stage_info};
|
tess_control_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||||
const uint32_t shader_stage_count = ps_active ? 2u : 1u;
|
tess_control_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationControl;
|
||||||
|
tess_control_shader_stage_info.module = tess_control_shader_module;
|
||||||
|
tess_control_shader_stage_info.pName = "main";
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo tess_eval_shader_stage_info {};
|
||||||
|
tess_eval_shader_stage_info.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||||
|
tess_eval_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationEvaluation;
|
||||||
|
tess_eval_shader_stage_info.module = tess_eval_shader_module;
|
||||||
|
tess_eval_shader_stage_info.pName = "main";
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo shader_stages[4] = {};
|
||||||
|
uint32_t shader_stage_count = 0;
|
||||||
|
shader_stages[shader_stage_count++] = vert_shader_stage_info;
|
||||||
|
if (rect_list) {
|
||||||
|
shader_stages[shader_stage_count++] = tess_control_shader_stage_info;
|
||||||
|
shader_stages[shader_stage_count++] = tess_eval_shader_stage_info;
|
||||||
|
}
|
||||||
|
if (ps_active) {
|
||||||
|
shader_stages[shader_stage_count++] = frag_shader_stage_info;
|
||||||
|
}
|
||||||
|
|
||||||
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX];
|
vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX];
|
||||||
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
|
vk::VertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX];
|
||||||
@@ -929,10 +978,13 @@ void CreatePipelineInternal(
|
|||||||
pipeline_info.pStages = shader_stages;
|
pipeline_info.pStages = shader_stages;
|
||||||
pipeline_info.pVertexInputState = &vertex_input_info;
|
pipeline_info.pVertexInputState = &vertex_input_info;
|
||||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||||
pipeline_info.pTessellationState = nullptr;
|
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
||||||
pipeline_info.pViewportState = &viewport_state;
|
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
|
||||||
pipeline_info.pRasterizationState = &rasterizer;
|
tessellation_state.patchControlPoints = 3;
|
||||||
pipeline_info.pMultisampleState = &multisampling;
|
pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr);
|
||||||
|
pipeline_info.pViewportState = &viewport_state;
|
||||||
|
pipeline_info.pRasterizationState = &rasterizer;
|
||||||
|
pipeline_info.pMultisampleState = &multisampling;
|
||||||
pipeline_info.pDepthStencilState = (static_params.with_depth ? &depth_stencil_info : nullptr);
|
pipeline_info.pDepthStencilState = (static_params.with_depth ? &depth_stencil_info : nullptr);
|
||||||
pipeline_info.pColorBlendState = &color_blending;
|
pipeline_info.pColorBlendState = &color_blending;
|
||||||
pipeline_info.pDynamicState = &dynamic_state;
|
pipeline_info.pDynamicState = &dynamic_state;
|
||||||
@@ -968,6 +1020,12 @@ void CreatePipelineInternal(
|
|||||||
if (frag_shader_module != nullptr) {
|
if (frag_shader_module != nullptr) {
|
||||||
graphics.device.destroyShaderModule(frag_shader_module, nullptr);
|
graphics.device.destroyShaderModule(frag_shader_module, nullptr);
|
||||||
}
|
}
|
||||||
|
if (tess_control_shader_module != nullptr) {
|
||||||
|
graphics.device.destroyShaderModule(tess_control_shader_module, nullptr);
|
||||||
|
}
|
||||||
|
if (tess_eval_shader_module != nullptr) {
|
||||||
|
graphics.device.destroyShaderModule(tess_eval_shader_module, nullptr);
|
||||||
|
}
|
||||||
graphics.device.destroyShaderModule(vert_shader_module, nullptr);
|
graphics.device.destroyShaderModule(vert_shader_module, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
|
||||||
#include "common/file.h"
|
#include "common/file.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/profiler.h"
|
#include "common/profiler.h"
|
||||||
@@ -650,11 +649,9 @@ static bool ConsumeMetadataColorOperation(const RenderCommandBuffer& buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DrawEmitInfo {
|
struct DrawEmitInfo {
|
||||||
bool indexed = false;
|
bool indexed = false;
|
||||||
bool draw_prim7_as_ngg = false;
|
int32_t vertex_offset = 0;
|
||||||
uint32_t draw_vertex_count = 0;
|
uint32_t first_vertex = 0;
|
||||||
int32_t vertex_offset = 0;
|
|
||||||
uint32_t first_vertex = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawIndexBufferSource {
|
struct DrawIndexBufferSource {
|
||||||
@@ -767,7 +764,7 @@ static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
|
|||||||
draw.flags, draw.instance_count, draw.first_instance);
|
draw.flags, draw.instance_count, draw.first_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use_ngg_rectlist_draw,
|
static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||||
vk::PrimitiveTopology& topology) {
|
vk::PrimitiveTopology& topology) {
|
||||||
|
|
||||||
topology = vk::PrimitiveTopology::ePointList;
|
topology = vk::PrimitiveTopology::ePointList;
|
||||||
@@ -791,8 +788,7 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
|
|||||||
topology = vk::PrimitiveTopology::eTriangleStrip;
|
topology = vk::PrimitiveTopology::eTriangleStrip;
|
||||||
break;
|
break;
|
||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
topology = (auto_draw && use_ngg_rectlist_draw ? vk::PrimitiveTopology::eTriangleStrip
|
topology = vk::PrimitiveTopology::ePatchList;
|
||||||
: vk::PrimitiveTopology::eTriangleList);
|
|
||||||
break;
|
break;
|
||||||
case Prospero::PrimitiveType::kRectListLegacy:
|
case Prospero::PrimitiveType::kRectListLegacy:
|
||||||
if (!auto_draw) {
|
if (!auto_draw) {
|
||||||
@@ -991,20 +987,6 @@ static void LogDrawStateIfNeeded(const RenderCommandBuffer& buffer, const DrawCa
|
|||||||
// LogDrawTextureState(draw.name, state.color_info[0], state.ps_input_info);
|
// LogDrawTextureState(draw.name, state.color_info[0], state.ps_input_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsHostExpandedRectListDrawSupported(const ShaderVertexInputInfo& vs_input_info,
|
|
||||||
const DrawCallInfo& draw,
|
|
||||||
const DrawEmitInfo& emit) {
|
|
||||||
if (!emit.draw_prim7_as_ngg) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vs_input_info.buffers_num != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return draw.index_count == 3 || draw.index_count == emit.draw_vertex_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
|
static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer,
|
||||||
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
|
const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw,
|
||||||
const DrawEmitInfo& emit) {
|
const DrawEmitInfo& emit) {
|
||||||
@@ -1017,22 +999,12 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
|||||||
case Prospero::PrimitiveType::kTriList:
|
case Prospero::PrimitiveType::kTriList:
|
||||||
case Prospero::PrimitiveType::kTriFan:
|
case Prospero::PrimitiveType::kTriFan:
|
||||||
case Prospero::PrimitiveType::kTriStrip:
|
case Prospero::PrimitiveType::kTriStrip:
|
||||||
if (emit.indexed) {
|
|
||||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
|
||||||
draw.first_instance);
|
|
||||||
} else {
|
|
||||||
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
|
|
||||||
draw.first_instance);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
if (emit.indexed) {
|
if (emit.indexed) {
|
||||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
||||||
draw.first_instance);
|
draw.first_instance);
|
||||||
} else {
|
} else {
|
||||||
EXIT_NOT_IMPLEMENTED(
|
vk_buffer.draw(draw.index_count, draw.instance_count, emit.first_vertex,
|
||||||
!IsHostExpandedRectListDrawSupported(vs_input_info, draw, emit));
|
|
||||||
vk_buffer.draw(emit.draw_vertex_count, draw.instance_count, emit.first_vertex,
|
|
||||||
draw.first_instance);
|
draw.first_instance);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1159,7 +1131,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
|||||||
reinterpret_cast<uint64_t>(index_addr));
|
reinterpret_cast<uint64_t>(index_addr));
|
||||||
|
|
||||||
Common::LockGuard lock(m_context.GetMutex());
|
Common::LockGuard lock(m_context.GetMutex());
|
||||||
if (index_count == 0) {
|
if (index_count == 0 || instance_count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,7 +1173,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
|||||||
hw_check(buffer);
|
hw_check(buffer);
|
||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
if (!GetDrawTopology(ucfg, false, false, topology)) {
|
if (!GetDrawTopology(ucfg, false, topology)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1229,10 +1201,6 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
|
|||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||||
EXIT_NOT_IMPLEMENTED(type != 1);
|
EXIT_NOT_IMPLEMENTED(type != 1);
|
||||||
if (instance_count == 0) {
|
|
||||||
instance_count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex,
|
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex,
|
||||||
index_count, flags,
|
index_count, flags,
|
||||||
instance_count, first_instance};
|
instance_count, first_instance};
|
||||||
@@ -1292,7 +1260,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
index_count, flags, first_vertex, instance_count, first_instance);
|
index_count, flags, first_vertex, instance_count, first_instance);
|
||||||
|
|
||||||
Common::LockGuard lock(m_context.GetMutex());
|
Common::LockGuard lock(m_context.GetMutex());
|
||||||
if (index_count == 0) {
|
if (index_count == 0 || instance_count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1330,10 +1298,6 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
hw_check(buffer);
|
hw_check(buffer);
|
||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||||
if (instance_count == 0) {
|
|
||||||
instance_count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto,
|
const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto,
|
||||||
index_count, flags,
|
index_count, flags,
|
||||||
instance_count, first_instance};
|
instance_count, first_instance};
|
||||||
@@ -1345,20 +1309,15 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
const bool use_ngg_rectlist_draw = Config::NggRectlistDrawEnabled();
|
if (!GetDrawTopology(ucfg, true, topology)) {
|
||||||
|
|
||||||
if (!GetDrawTopology(ucfg, true, use_ngg_rectlist_draw, topology)) {
|
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool draw_prim7_as_ngg =
|
|
||||||
(use_ngg_rectlist_draw &&
|
|
||||||
ucfg.GetPrimType() == Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectList));
|
|
||||||
|
|
||||||
RefreshShaders(buffer, draw, false, state);
|
RefreshShaders(buffer, draw, false, state);
|
||||||
|
|
||||||
if (draw_prim7_as_ngg && state.vs_input_info.buffers_num == 0 &&
|
const bool rect_list = topology == vk::PrimitiveTopology::ePatchList;
|
||||||
|
if (rect_list && state.vs_input_info.buffers_num == 0 &&
|
||||||
state.vs_input_info.param_export_mask == 0 && state.ps_input_info.input_num != 0) {
|
state.vs_input_info.param_export_mask == 0 && state.ps_input_info.input_num != 0) {
|
||||||
if (graphics_debug_dump_enabled()) {
|
if (graphics_debug_dump_enabled()) {
|
||||||
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
||||||
@@ -1375,13 +1334,10 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, u
|
|||||||
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
Prospero::GpuEnumValue(Prospero::PrimitiveType::kRectListLegacy),
|
||||||
0, nullptr);
|
0, nullptr);
|
||||||
|
|
||||||
const uint32_t draw_vertex_count = (draw_prim7_as_ngg ? 4u : index_count);
|
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
||||||
const auto vertex_offset = ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) +
|
static_cast<int32_t>(first_vertex);
|
||||||
static_cast<int32_t>(first_vertex);
|
DrawEmitInfo emit {};
|
||||||
DrawEmitInfo emit {};
|
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
|
||||||
emit.draw_prim7_as_ngg = draw_prim7_as_ngg;
|
|
||||||
emit.draw_vertex_count = draw_vertex_count;
|
|
||||||
emit.first_vertex = static_cast<uint32_t>(vertex_offset);
|
|
||||||
|
|
||||||
DrawIndexBufferSource index_source {};
|
DrawIndexBufferSource index_source {};
|
||||||
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false,
|
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ static void AppendInstructionWords(std::vector<uint32_t>& section, const uint32_
|
|||||||
section.insert(section.end(), words + 1, words + words_num);
|
section.insert(section.end(), words + 1, words + words_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
Builder::Builder() {
|
Builder::Builder(uint32_t version): m_version(version) {
|
||||||
m_debug.reserve(InitialSpirvSectionReserve);
|
m_debug.reserve(InitialSpirvSectionReserve);
|
||||||
m_annotations.reserve(InitialSpirvSectionReserve);
|
m_annotations.reserve(InitialSpirvSectionReserve);
|
||||||
m_types.reserve(InitialSpirvSectionReserve);
|
m_types.reserve(InitialSpirvSectionReserve);
|
||||||
@@ -138,7 +138,7 @@ std::vector<uint32_t> Builder::Build() const {
|
|||||||
m_debug.size() + m_annotations.size() + m_types.size() + m_functions.size());
|
m_debug.size() + m_annotations.size() + m_types.size() + m_functions.size());
|
||||||
|
|
||||||
module.push_back(0x07230203u);
|
module.push_back(0x07230203u);
|
||||||
module.push_back(0x00010300u);
|
module.push_back(m_version);
|
||||||
module.push_back(0u);
|
module.push_back(0u);
|
||||||
module.push_back(m_next_id);
|
module.push_back(m_next_id);
|
||||||
module.push_back(0u);
|
module.push_back(0u);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
|||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
public:
|
public:
|
||||||
Builder();
|
explicit Builder(uint32_t version = 0x00010300u);
|
||||||
~Builder() = default;
|
~Builder() = default;
|
||||||
KYTY_CLASS_DEFAULT_COPY(Builder);
|
KYTY_CLASS_DEFAULT_COPY(Builder);
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ private:
|
|||||||
static void AppendString(std::vector<uint32_t>& words, const char* text);
|
static void AppendString(std::vector<uint32_t>& words, const char* text);
|
||||||
|
|
||||||
uint32_t m_next_id = 1;
|
uint32_t m_next_id = 1;
|
||||||
|
uint32_t m_version = 0;
|
||||||
std::vector<uint32_t> m_capabilities;
|
std::vector<uint32_t> m_capabilities;
|
||||||
std::vector<uint32_t> m_extensions;
|
std::vector<uint32_t> m_extensions;
|
||||||
std::vector<uint32_t> m_ext_inst_imports;
|
std::vector<uint32_t> m_ext_inst_imports;
|
||||||
|
|||||||
@@ -7,51 +7,30 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
|||||||
|
|
||||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
||||||
const auto* ps = state.pixel_input_info;
|
const auto* ps = state.pixel_input_info;
|
||||||
if (state.stage != ShaderType::Pixel || ps == nullptr || attr >= ps->input_num) {
|
if (state.stage != ShaderType::Pixel || ps == nullptr) {
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
// VINTRP ATTR selects the PS input slot. SPI_PS_INPUT_CNTL maps that slot to a
|
return ShaderPixelParameterMappedLocation(*ps, attr);
|
||||||
// VS parameter export, which is the SPIR-V location we must link against.
|
|
||||||
return ps->interpolator_settings[attr] & PsInputOffsetMask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
||||||
bool used_locations[32] = {};
|
std::array<uint32_t, 32> active_inputs {};
|
||||||
|
uint32_t active_count = 0;
|
||||||
for (const auto& input: state.inputs) {
|
for (const auto& input: state.inputs) {
|
||||||
if (input.kind != IR::StageInputKind::Parameter) {
|
if (input.kind == IR::StageInputKind::Parameter) {
|
||||||
continue;
|
active_inputs[active_count++] = input.location;
|
||||||
}
|
|
||||||
|
|
||||||
auto location = PixelParameterMappedLocation(state, input.location);
|
|
||||||
if (location < std::size(used_locations) && used_locations[location]) {
|
|
||||||
auto fallback_location = input.location;
|
|
||||||
while (fallback_location < std::size(used_locations) &&
|
|
||||||
used_locations[fallback_location]) {
|
|
||||||
fallback_location++;
|
|
||||||
}
|
|
||||||
EXIT_NOT_IMPLEMENTED(fallback_location >= std::size(used_locations));
|
|
||||||
location = fallback_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.location == attr) {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location < std::size(used_locations)) {
|
|
||||||
used_locations[location] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return state.stage == ShaderType::Pixel && state.pixel_input_info != nullptr
|
||||||
return PixelParameterMappedLocation(state, attr);
|
? ShaderPixelParameterLocation(*state.pixel_input_info,
|
||||||
|
{active_inputs.data(), active_count}, attr)
|
||||||
|
: attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
|
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
|
||||||
const auto* ps = state.pixel_input_info;
|
const auto* ps = state.pixel_input_info;
|
||||||
if (state.stage != ShaderType::Pixel || ps == nullptr || attr >= ps->input_num) {
|
return state.stage == ShaderType::Pixel && ps != nullptr &&
|
||||||
return false;
|
ShaderPixelParameterIsFlat(*ps, attr);
|
||||||
}
|
|
||||||
return (ps->interpolator_settings[attr] & PsInputFlatShade) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetError(std::string* error, const char* message) {
|
void SetError(std::string* error, const char* message) {
|
||||||
|
|||||||
@@ -425,9 +425,6 @@ struct EmitterState {
|
|||||||
std::map<uint32_t, uint32_t> float_constants;
|
std::map<uint32_t, uint32_t> float_constants;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
|
||||||
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
|
||||||
|
|
||||||
enum class VertexInputScalarKind { Float, Sint, Uint };
|
enum class VertexInputScalarKind { Float, Sint, Uint };
|
||||||
|
|
||||||
constexpr uint32_t NoImageComponent = 0xffffffffu;
|
constexpr uint32_t NoImageComponent = 0xffffffffu;
|
||||||
|
|||||||
@@ -200,12 +200,12 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension ||
|
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension ||
|
||||||
DescriptorIsCube(descriptor) != image.cube) {
|
DescriptorIsCube(descriptor) != image.cube) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = fmt::format(
|
*error =
|
||||||
"image descriptor {} no longer matches specialized dimension: "
|
fmt::format("image descriptor {} no longer matches specialized dimension: "
|
||||||
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||||
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||||
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||||
descriptor.dwords[6], descriptor.dwords[7]);
|
descriptor.dwords[6], descriptor.dwords[7]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -224,14 +224,17 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
|||||||
const bool raw_sint_storage =
|
const bool raw_sint_storage =
|
||||||
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
|
storage && format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32SInt) &&
|
||||||
!image.read && !image.atomic;
|
!image.read && !image.atomic;
|
||||||
const bool uint_descriptor =
|
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||||
Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
image.kind == ResourceKind::StorageImageUint;
|
||||||
image.kind == ResourceKind::StorageImageUint;
|
|
||||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error =
|
*error = fmt::format(
|
||||||
fmt::format("image descriptor {} no longer matches specialized format", i);
|
"image descriptor {} no longer matches specialized format: "
|
||||||
|
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||||
|
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||||
|
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||||
|
descriptor.dwords[6], descriptor.dwords[7]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,397 @@
|
|||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||||
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
|
#include "graphics/shader/shader.h"
|
||||||
|
#include "spirv/unified1/spirv.hpp11"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <bit>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ShaderRecompiler::Spirv::Builder;
|
||||||
|
|
||||||
|
constexpr uint32_t SpirvVersion15 = 0x00010500u;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr uint32_t Word(T value) {
|
||||||
|
return static_cast<uint32_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parameter {
|
||||||
|
uint32_t input_location = 0;
|
||||||
|
uint32_t output_location = 0;
|
||||||
|
bool flat = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Parameter> GetParameters(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info) {
|
||||||
|
if (pixel_info == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
EXIT_IF(pixel_info->input_num > ShaderVertexInputInfo::RES_MAX);
|
||||||
|
EXIT_IF(pixel_info->stage.program == nullptr);
|
||||||
|
|
||||||
|
std::vector<uint32_t> active_inputs;
|
||||||
|
for (const auto& input: pixel_info->stage.program->info.inputs) {
|
||||||
|
if (input.kind == ShaderRecompiler::IR::StageInputKind::Parameter) {
|
||||||
|
active_inputs.push_back(input.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Parameter> parameters;
|
||||||
|
for (const auto input: active_inputs) {
|
||||||
|
const auto input_location = ShaderPixelParameterMappedLocation(*pixel_info, input);
|
||||||
|
if ((vertex_info.param_export_mask & (1u << input_location)) != 0) {
|
||||||
|
parameters.push_back({input_location,
|
||||||
|
ShaderPixelParameterLocation(*pixel_info, active_inputs, input),
|
||||||
|
ShaderPixelParameterIsFlat(*pixel_info, input)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RectListEmitter {
|
||||||
|
public:
|
||||||
|
RectListEmitter(const std::vector<Parameter>& parameters_, spv::ExecutionModel model)
|
||||||
|
: parameters(parameters_) {
|
||||||
|
builder.AddMemoryModel(
|
||||||
|
{Word(spv::AddressingModel::Logical), Word(spv::MemoryModel::GLSL450)});
|
||||||
|
|
||||||
|
void_type = Type(spv::Op::OpTypeVoid);
|
||||||
|
uint_type = Type(spv::Op::OpTypeInt, 32u, 0u);
|
||||||
|
int_type = Type(spv::Op::OpTypeInt, 32u, 1u);
|
||||||
|
float_type = Type(spv::Op::OpTypeFloat, 32u);
|
||||||
|
vec4_float_type = Type(spv::Op::OpTypeVector, float_type, 4u);
|
||||||
|
function_type = Type(spv::Op::OpTypeFunction, void_type);
|
||||||
|
|
||||||
|
per_vertex_type = Type(spv::Op::OpTypeStruct, vec4_float_type);
|
||||||
|
builder.AddAnnotation({Word(spv::Op::OpMemberDecorate), per_vertex_type, 0u,
|
||||||
|
Word(spv::Decoration::BuiltIn), Word(spv::BuiltIn::Position)});
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), per_vertex_type, Word(spv::Decoration::Block)});
|
||||||
|
|
||||||
|
ptr_input_vec4_float = Pointer(spv::StorageClass::Input, vec4_float_type);
|
||||||
|
ptr_output_vec4_float = Pointer(spv::StorageClass::Output, vec4_float_type);
|
||||||
|
if (model == spv::ExecutionModel::TessellationControl) {
|
||||||
|
bool_type = Type(spv::Op::OpTypeBool);
|
||||||
|
vec2_bool_type = Type(spv::Op::OpTypeVector, bool_type, 2u);
|
||||||
|
vec2_float_type = Type(spv::Op::OpTypeVector, float_type, 2u);
|
||||||
|
ptr_output_float = Pointer(spv::StorageClass::Output, float_type);
|
||||||
|
} else {
|
||||||
|
vec3_float_type = Type(spv::Op::OpTypeVector, float_type, 3u);
|
||||||
|
ptr_input_float = Pointer(spv::StorageClass::Input, float_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> EmitControl() {
|
||||||
|
DefineEntry(spv::ExecutionModel::TessellationControl);
|
||||||
|
const auto float_one = Constant(float_type, std::bit_cast<uint32_t>(1.0f));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < 4; i++) {
|
||||||
|
Store(Access(ptr_output_float, tess_outer, Int(i)), float_one);
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < 2; i++) {
|
||||||
|
Store(Access(ptr_output_float, tess_inner, Int(i)), float_one);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> positions {};
|
||||||
|
for (uint32_t i = 0; i < positions.size(); i++) {
|
||||||
|
positions[i] =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, Int(i), Int(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> coordinate_equal {};
|
||||||
|
for (uint32_t i = 0; i < coordinate_equal.size(); i++) {
|
||||||
|
const auto left = Result(spv::Op::OpVectorShuffle, vec2_float_type, positions[i],
|
||||||
|
positions[i], 0u, 1u);
|
||||||
|
const auto right = Result(spv::Op::OpVectorShuffle, vec2_float_type,
|
||||||
|
positions[(i + 1u) % 3u], positions[(i + 1u) % 3u], 0u, 1u);
|
||||||
|
coordinate_equal[i] = Result(spv::Op::OpFOrdEqual, vec2_bool_type, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 3> barycentric {};
|
||||||
|
std::array<uint32_t, 3> edge_vertex {};
|
||||||
|
const auto float_minus_one = Constant(float_type, std::bit_cast<uint32_t>(-1.0f));
|
||||||
|
for (uint32_t i = 0; i < edge_vertex.size(); i++) {
|
||||||
|
const auto previous = (i + 2u) % 3u;
|
||||||
|
const auto xy = Result(
|
||||||
|
spv::Op::OpLogicalAnd, bool_type,
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[i], 0u),
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[previous], 1u));
|
||||||
|
const auto yx = Result(
|
||||||
|
spv::Op::OpLogicalAnd, bool_type,
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[i], 1u),
|
||||||
|
Result(spv::Op::OpCompositeExtract, bool_type, coordinate_equal[previous], 0u));
|
||||||
|
edge_vertex[i] = Result(spv::Op::OpLogicalOr, bool_type, xy, yx);
|
||||||
|
barycentric[i] =
|
||||||
|
Result(spv::Op::OpSelect, float_type, edge_vertex[i], float_minus_one, float_one);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vertex_index = Result(spv::Op::OpSelect, int_type, edge_vertex[2], Int(2), Int(0));
|
||||||
|
vertex_index = Result(spv::Op::OpSelect, int_type, edge_vertex[1], Int(1), vertex_index);
|
||||||
|
const auto invocation = Load(int_type, invocation_id);
|
||||||
|
const auto is_fourth = Result(spv::Op::OpIEqual, bool_type, invocation, Int(3));
|
||||||
|
const auto index =
|
||||||
|
Result(spv::Op::OpSMod, int_type,
|
||||||
|
Result(spv::Op::OpIAdd, int_type, vertex_index, invocation), Int(3));
|
||||||
|
|
||||||
|
const auto position3 = Interpolate(positions[0], positions[1], positions[2], barycentric);
|
||||||
|
const auto position =
|
||||||
|
Result(spv::Op::OpSelect, vec4_float_type, is_fourth, position3,
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, index, Int(0))));
|
||||||
|
Store(Access(ptr_output_vec4_float, gl_out, invocation, Int(0)), position);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
const auto input0 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(0)));
|
||||||
|
if (parameters[i].flat) {
|
||||||
|
Store(Access(ptr_output_vec4_float, outputs[i], invocation), input0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto input1 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(1)));
|
||||||
|
const auto input2 =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], Int(2)));
|
||||||
|
const auto input3 = Interpolate(input0, input1, input2, barycentric);
|
||||||
|
const auto value =
|
||||||
|
Result(spv::Op::OpSelect, vec4_float_type, is_fourth, input3,
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
|
||||||
|
Store(Access(ptr_output_vec4_float, outputs[i], invocation), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(spv::Op::OpReturn);
|
||||||
|
Emit(spv::Op::OpFunctionEnd);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> EmitEvaluation() {
|
||||||
|
DefineEntry(spv::ExecutionModel::TessellationEvaluation);
|
||||||
|
|
||||||
|
const auto x = Load(float_type, Access(ptr_input_float, tess_coord, Int(0)));
|
||||||
|
const auto y = Load(float_type, Access(ptr_input_float, tess_coord, Int(1)));
|
||||||
|
const auto index = Result(
|
||||||
|
spv::Op::OpIAdd, int_type,
|
||||||
|
Result(spv::Op::OpIMul, int_type, Result(spv::Op::OpConvertFToS, int_type, y), Int(2)),
|
||||||
|
Result(spv::Op::OpConvertFToS, int_type, x));
|
||||||
|
|
||||||
|
const auto position =
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, gl_in, index, Int(0)));
|
||||||
|
Store(Access(ptr_output_vec4_float, gl_out, Int(0)), position);
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
Store(outputs[i],
|
||||||
|
Load(vec4_float_type, Access(ptr_input_vec4_float, inputs[i], index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(spv::Op::OpReturn);
|
||||||
|
Emit(spv::Op::OpFunctionEnd);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Type(spv::Op opcode, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddType({Word(opcode), id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Constant(uint32_t type, uint32_t value) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddType({Word(spv::Op::OpConstant), type, id, value});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Pointer(spv::StorageClass storage, uint32_t type) {
|
||||||
|
return Type(spv::Op::OpTypePointer, storage, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Array(uint32_t type, uint32_t size) {
|
||||||
|
return Type(spv::Op::OpTypeArray, type, Uint(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Result(spv::Op opcode, uint32_t type, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddFunction({Word(opcode), type, id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t ResultWithoutType(spv::Op opcode, Args... operands) {
|
||||||
|
const auto id = builder.AllocateId();
|
||||||
|
builder.AddFunction({Word(opcode), id, Word(operands)...});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void Emit(spv::Op opcode, Args... operands) {
|
||||||
|
builder.AddFunction({Word(opcode), Word(operands)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
uint32_t Access(uint32_t pointer_type, uint32_t base, Args... indices) {
|
||||||
|
return Result(spv::Op::OpAccessChain, pointer_type, base, Word(indices)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Load(uint32_t type, uint32_t pointer) {
|
||||||
|
return Result(spv::Op::OpLoad, type, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Store(uint32_t pointer, uint32_t value) { Emit(spv::Op::OpStore, pointer, value); }
|
||||||
|
|
||||||
|
uint32_t Int(uint32_t value) {
|
||||||
|
auto& id = int_constants[value];
|
||||||
|
if (id == 0) {
|
||||||
|
id = Constant(int_type, value);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Uint(uint32_t value) {
|
||||||
|
auto& id = uint_constants[value];
|
||||||
|
if (id == 0) {
|
||||||
|
id = Constant(uint_type, value);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AddInterface(spv::StorageClass storage, uint32_t type) {
|
||||||
|
const auto variable = builder.AllocateId();
|
||||||
|
builder.AddType(
|
||||||
|
{Word(spv::Op::OpVariable), Pointer(storage, type), variable, Word(storage)});
|
||||||
|
interfaces.push_back(variable);
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decorate(uint32_t target, spv::Decoration decoration, uint32_t value) {
|
||||||
|
builder.AddAnnotation({Word(spv::Op::OpDecorate), target, Word(decoration), value});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineEntry(spv::ExecutionModel model) {
|
||||||
|
builder.AddCapability({Word(spv::Capability::Shader)});
|
||||||
|
builder.AddCapability({Word(spv::Capability::Tessellation)});
|
||||||
|
main = Result(spv::Op::OpFunction, void_type, spv::FunctionControlMask::MaskNone,
|
||||||
|
function_type);
|
||||||
|
if (model == spv::ExecutionModel::TessellationControl) {
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::OutputVertices), 4u});
|
||||||
|
} else {
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::Quads)});
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::SpacingEqual)});
|
||||||
|
builder.AddExecutionMode({main, Word(spv::ExecutionMode::VertexOrderCw)});
|
||||||
|
}
|
||||||
|
DefineInputs(model);
|
||||||
|
DefineOutputs(model);
|
||||||
|
builder.AddEntryPoint(Word(model), main, "main", interfaces);
|
||||||
|
ResultWithoutType(spv::Op::OpLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineInputs(spv::ExecutionModel model) {
|
||||||
|
const auto tess_control = model == spv::ExecutionModel::TessellationControl;
|
||||||
|
if (tess_control) {
|
||||||
|
invocation_id = AddInterface(spv::StorageClass::Input, int_type);
|
||||||
|
Decorate(invocation_id, spv::Decoration::BuiltIn, Word(spv::BuiltIn::InvocationId));
|
||||||
|
} else {
|
||||||
|
tess_coord = AddInterface(spv::StorageClass::Input, vec3_float_type);
|
||||||
|
Decorate(tess_coord, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessCoord));
|
||||||
|
}
|
||||||
|
gl_in =
|
||||||
|
AddInterface(spv::StorageClass::Input, Array(per_vertex_type, tess_control ? 3u : 4u));
|
||||||
|
|
||||||
|
inputs.resize(parameters.size());
|
||||||
|
std::array<uint32_t, ShaderVertexInputInfo::RES_MAX> locations {};
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
const auto location =
|
||||||
|
tess_control ? parameters[i].input_location : parameters[i].output_location;
|
||||||
|
if (tess_control && locations[location] != 0) {
|
||||||
|
inputs[i] = locations[location];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inputs[i] = AddInterface(spv::StorageClass::Input,
|
||||||
|
Array(vec4_float_type, tess_control ? 3u : 4u));
|
||||||
|
Decorate(inputs[i], spv::Decoration::Location, location);
|
||||||
|
locations[location] = inputs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineOutputs(spv::ExecutionModel model) {
|
||||||
|
const auto tess_control = model == spv::ExecutionModel::TessellationControl;
|
||||||
|
if (tess_control) {
|
||||||
|
gl_out = AddInterface(spv::StorageClass::Output, Array(per_vertex_type, 4u));
|
||||||
|
tess_inner = AddInterface(spv::StorageClass::Output, Array(float_type, 2u));
|
||||||
|
Decorate(tess_inner, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessLevelInner));
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), tess_inner, Word(spv::Decoration::Patch)});
|
||||||
|
tess_outer = AddInterface(spv::StorageClass::Output, Array(float_type, 4u));
|
||||||
|
Decorate(tess_outer, spv::Decoration::BuiltIn, Word(spv::BuiltIn::TessLevelOuter));
|
||||||
|
builder.AddAnnotation(
|
||||||
|
{Word(spv::Op::OpDecorate), tess_outer, Word(spv::Decoration::Patch)});
|
||||||
|
} else {
|
||||||
|
gl_out = AddInterface(spv::StorageClass::Output, per_vertex_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs.resize(parameters.size());
|
||||||
|
for (uint32_t i = 0; i < parameters.size(); i++) {
|
||||||
|
outputs[i] = AddInterface(spv::StorageClass::Output,
|
||||||
|
tess_control ? Array(vec4_float_type, 4u) : vec4_float_type);
|
||||||
|
Decorate(outputs[i], spv::Decoration::Location, parameters[i].output_location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Interpolate(uint32_t v0, uint32_t v1, uint32_t v2,
|
||||||
|
const std::array<uint32_t, 3>& barycentric) {
|
||||||
|
const auto p0 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v0, barycentric[0]);
|
||||||
|
const auto p1 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v1, barycentric[1]);
|
||||||
|
const auto p2 = Result(spv::Op::OpVectorTimesScalar, vec4_float_type, v2, barycentric[2]);
|
||||||
|
return Result(spv::Op::OpFAdd, vec4_float_type, p0,
|
||||||
|
Result(spv::Op::OpFAdd, vec4_float_type, p1, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder builder {SpirvVersion15};
|
||||||
|
const std::vector<Parameter>& parameters;
|
||||||
|
std::vector<uint32_t> interfaces;
|
||||||
|
std::vector<uint32_t> inputs;
|
||||||
|
std::vector<uint32_t> outputs;
|
||||||
|
std::array<uint32_t, 5> int_constants {};
|
||||||
|
std::array<uint32_t, 5> uint_constants {};
|
||||||
|
uint32_t main = 0;
|
||||||
|
uint32_t void_type = 0;
|
||||||
|
uint32_t bool_type = 0;
|
||||||
|
uint32_t uint_type = 0;
|
||||||
|
uint32_t int_type = 0;
|
||||||
|
uint32_t float_type = 0;
|
||||||
|
uint32_t vec2_bool_type = 0;
|
||||||
|
uint32_t vec2_float_type = 0;
|
||||||
|
uint32_t vec3_float_type = 0;
|
||||||
|
uint32_t vec4_float_type = 0;
|
||||||
|
uint32_t function_type = 0;
|
||||||
|
uint32_t per_vertex_type = 0;
|
||||||
|
uint32_t ptr_input_float = 0;
|
||||||
|
uint32_t ptr_input_vec4_float = 0;
|
||||||
|
uint32_t ptr_output_float = 0;
|
||||||
|
uint32_t ptr_output_vec4_float = 0;
|
||||||
|
uint32_t gl_in = 0;
|
||||||
|
uint32_t gl_out = 0;
|
||||||
|
uint32_t tess_inner = 0;
|
||||||
|
uint32_t tess_outer = 0;
|
||||||
|
uint32_t tess_coord = 0;
|
||||||
|
uint32_t invocation_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
RectListShaders BuildRectListShaders(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info) {
|
||||||
|
const auto parameters = GetParameters(vertex_info, pixel_info);
|
||||||
|
RectListEmitter control(parameters, spv::ExecutionModel::TessellationControl);
|
||||||
|
RectListEmitter evaluation(parameters, spv::ExecutionModel::TessellationEvaluation);
|
||||||
|
return {control.EmitControl(), evaluation.EmitEvaluation()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
|
#define EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
struct ShaderPixelInputInfo;
|
||||||
|
struct ShaderVertexInputInfo;
|
||||||
|
|
||||||
|
struct RectListShaders {
|
||||||
|
std::vector<uint32_t> control;
|
||||||
|
std::vector<uint32_t> evaluation;
|
||||||
|
};
|
||||||
|
|
||||||
|
RectListShaders BuildRectListShaders(const ShaderVertexInputInfo& vertex_info,
|
||||||
|
const ShaderPixelInputInfo* pixel_info);
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
#endif // EMULATOR_SRC_GRAPHICS_SHADER_RECTLISTSHADER_H_
|
||||||
@@ -45,6 +45,42 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
||||||
|
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input) {
|
||||||
|
return input < info.input_num ? info.interpolator_settings[input] & PsInputOffsetMask : input;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||||
|
std::span<const uint32_t> active_inputs, uint32_t input) {
|
||||||
|
std::array<bool, 32> used_locations {};
|
||||||
|
for (const auto active_input: active_inputs) {
|
||||||
|
auto location = ShaderPixelParameterMappedLocation(info, active_input);
|
||||||
|
if (location < used_locations.size() && used_locations[location]) {
|
||||||
|
location = active_input;
|
||||||
|
while (location < used_locations.size() && used_locations[location]) {
|
||||||
|
location++;
|
||||||
|
}
|
||||||
|
EXIT_NOT_IMPLEMENTED(location >= used_locations.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_input == input) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
used_locations[location] = true;
|
||||||
|
}
|
||||||
|
return ShaderPixelParameterMappedLocation(info, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) {
|
||||||
|
return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct ShaderBinaryInfo {
|
struct ShaderBinaryInfo {
|
||||||
uint8_t signature[7];
|
uint8_t signature[7];
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@@ -1606,6 +1642,7 @@ ShaderId ShaderGetIdPS(const HW::PixelShaderInfo& regs, const ShaderPixelInputIn
|
|||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_w));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_w));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
||||||
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_no_perspective));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
||||||
|
|||||||
@@ -122,6 +122,11 @@ struct ShaderPixelInputInfo {
|
|||||||
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input);
|
||||||
|
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
|
||||||
|
std::span<const uint32_t> active_inputs, uint32_t input);
|
||||||
|
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input);
|
||||||
|
|
||||||
struct ShaderSharp {
|
struct ShaderSharp {
|
||||||
uint16_t offset_dw : 15;
|
uint16_t offset_dw : 15;
|
||||||
uint16_t size : 1;
|
uint16_t size : 1;
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ struct File {
|
|||||||
std::filesystem::path real_name;
|
std::filesystem::path real_name;
|
||||||
std::atomic_bool opened;
|
std::atomic_bool opened;
|
||||||
std::atomic_bool directory;
|
std::atomic_bool directory;
|
||||||
|
std::atomic_bool writable;
|
||||||
std::atomic_bool append;
|
std::atomic_bool append;
|
||||||
std::atomic_bool sync_writes;
|
std::atomic_bool sync_writes;
|
||||||
SpecialFile special;
|
SpecialFile special;
|
||||||
@@ -129,6 +130,7 @@ int FileDescriptors::CreateDescriptor() {
|
|||||||
auto* file = new File {};
|
auto* file = new File {};
|
||||||
file->opened = false;
|
file->opened = false;
|
||||||
file->directory = false;
|
file->directory = false;
|
||||||
|
file->writable = false;
|
||||||
file->append = false;
|
file->append = false;
|
||||||
file->sync_writes = false;
|
file->sync_writes = false;
|
||||||
file->special = SpecialFile::None;
|
file->special = SpecialFile::None;
|
||||||
@@ -408,6 +410,7 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
|
|||||||
EXIT_IF(file == nullptr || file->opened || file->directory);
|
EXIT_IF(file == nullptr || file->opened || file->directory);
|
||||||
|
|
||||||
file->name = path;
|
file->name = path;
|
||||||
|
file->writable = rw_mode != Common::File::Mode::Read;
|
||||||
file->append = append;
|
file->append = append;
|
||||||
file->sync_writes = fsync || sync || dsync;
|
file->sync_writes = fsync || sync || dsync;
|
||||||
|
|
||||||
@@ -953,6 +956,47 @@ int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb) {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length) {
|
||||||
|
PRINT_NAME();
|
||||||
|
|
||||||
|
if (d < DESCRIPTOR_MIN) {
|
||||||
|
return KERNEL_ERROR_EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::Libs::Network::Net::IsSocket(d)) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* file = g_files->GetFile(d);
|
||||||
|
|
||||||
|
if (file == nullptr || !file->opened) {
|
||||||
|
return KERNEL_ERROR_EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file->writable) {
|
||||||
|
return KERNEL_ERROR_EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->directory || file->special != SpecialFile::None) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::LockGuard lock(file->mutex);
|
||||||
|
|
||||||
|
if (file->f.IsInvalid() || !file->f.Truncate(static_cast<uint64_t>(length))) {
|
||||||
|
return KERNEL_ERROR_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGF("\tFtruncate (size = %" PRId64 ") file: %s\n", length,
|
||||||
|
Common::PathToString(file->real_name).c_str());
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI KernelUnlink(const char* path) {
|
int KYTY_SYSV_ABI KernelUnlink(const char* path) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_
|
|||||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
||||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
||||||
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
||||||
|
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length);
|
||||||
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
||||||
int KYTY_SYSV_ABI KernelRename(const char* from, const char* to);
|
int KYTY_SYSV_ABI KernelRename(const char* from, const char* to);
|
||||||
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep);
|
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep);
|
||||||
|
|||||||
+41
-50
@@ -365,6 +365,7 @@ struct PthreadAttrPrivate {
|
|||||||
uint64_t stack_map_addr;
|
uint64_t stack_map_addr;
|
||||||
size_t stack_map_size;
|
size_t stack_map_size;
|
||||||
int policy;
|
int policy;
|
||||||
|
int guest_priority;
|
||||||
int inherit_sched;
|
int inherit_sched;
|
||||||
int solosched;
|
int solosched;
|
||||||
bool detached;
|
bool detached;
|
||||||
@@ -875,9 +876,11 @@ bool TestGuestStackOwnerLifecycle(uint64_t* first_address, uint64_t* second_addr
|
|||||||
|
|
||||||
static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func, void* stack_top) {
|
static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func, void* stack_top) {
|
||||||
#if defined(__x86_64__) || defined(_M_X64)
|
#if defined(__x86_64__) || defined(_M_X64)
|
||||||
void* ret = nullptr;
|
void* ret = nullptr;
|
||||||
const auto guest_rsp = reinterpret_cast<uintptr_t>(stack_top) & ~static_cast<uintptr_t>(0x0f);
|
const auto aligned_stack_top =
|
||||||
const auto guest_rbp = guest_rsp - 4u * sizeof(uint64_t);
|
reinterpret_cast<uintptr_t>(stack_top) & ~static_cast<uintptr_t>(0x0f);
|
||||||
|
const auto guest_rsp = aligned_stack_top - 2u * sizeof(uintptr_t);
|
||||||
|
const auto guest_rbp = guest_rsp;
|
||||||
|
|
||||||
auto* guest_root_frame = reinterpret_cast<uintptr_t*>(guest_rbp);
|
auto* guest_root_frame = reinterpret_cast<uintptr_t*>(guest_rbp);
|
||||||
guest_root_frame[0] = 0;
|
guest_root_frame[0] = 0;
|
||||||
@@ -2128,13 +2131,8 @@ int KYTY_SYSV_ABI PthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedP
|
|||||||
|
|
||||||
int result = pthread_attr_getschedparam(&(*attr)->p, param);
|
int result = pthread_attr_getschedparam(&(*attr)->p, param);
|
||||||
|
|
||||||
if (param->sched_priority <= -2) {
|
// Host priority mapping is lossy; return the exact guest value.
|
||||||
param->sched_priority = 767;
|
param->sched_priority = (*attr)->guest_priority;
|
||||||
} else if (param->sched_priority >= +2) {
|
|
||||||
param->sched_priority = 256;
|
|
||||||
} else {
|
|
||||||
param->sched_priority = 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return OK;
|
return OK;
|
||||||
@@ -2298,6 +2296,7 @@ int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedP
|
|||||||
return KERNEL_ERROR_EINVAL;
|
return KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
KernelSchedParam pparam {};
|
KernelSchedParam pparam {};
|
||||||
if (param->sched_priority <= 478) {
|
if (param->sched_priority <= 478) {
|
||||||
pparam.sched_priority = +2;
|
pparam.sched_priority = +2;
|
||||||
@@ -2307,12 +2306,13 @@ int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedP
|
|||||||
pparam.sched_priority = 0;
|
pparam.sched_priority = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = pthread_attr_setschedparam(&attr_value->p, &pparam);
|
if (pthread_attr_setschedparam(&attr_value->p, &pparam) != 0) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
if (result == 0) {
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
return KERNEL_ERROR_EINVAL;
|
#endif
|
||||||
|
|
||||||
|
attr_value->guest_priority = param->sched_priority;
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) {
|
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy) {
|
||||||
@@ -3639,26 +3639,15 @@ int KYTY_SYSV_ABI PthreadGetprio(Pthread thread, int* prio) {
|
|||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(prio == nullptr);
|
EXIT_NOT_IMPLEMENTED(prio == nullptr);
|
||||||
|
|
||||||
sched_param param {};
|
sched_param native_param {};
|
||||||
int pol = 0;
|
int native_policy = 0;
|
||||||
|
if (pthread_getschedparam(thread->p, &native_policy, &native_param) != 0) {
|
||||||
int result = pthread_getschedparam(thread->p, &pol, ¶m);
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
|
||||||
if (result == 0) {
|
|
||||||
if (param.sched_priority <= -2) {
|
|
||||||
*prio = 767;
|
|
||||||
} else if (param.sched_priority >= +2) {
|
|
||||||
*prio = 256;
|
|
||||||
} else {
|
|
||||||
*prio = 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGF("\t PthreadGetprio: %d, %d\n", thread->unique_id, *prio);
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return KERNEL_ERROR_EINVAL;
|
*prio = thread->attr->guest_priority;
|
||||||
|
LOGF("\t PthreadGetprio: %d, %d\n", thread->unique_id, *prio);
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
|
int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
|
||||||
@@ -3673,25 +3662,27 @@ int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio) {
|
|||||||
|
|
||||||
int result = pthread_getschedparam(thread->p, &pol, ¶m);
|
int result = pthread_getschedparam(thread->p, &pol, ¶m);
|
||||||
|
|
||||||
if (result == 0) {
|
if (result != 0) {
|
||||||
if (prio <= 478) {
|
return KERNEL_ERROR_EINVAL;
|
||||||
param.sched_priority = +2;
|
|
||||||
} else if (prio >= 733) {
|
|
||||||
param.sched_priority = -2;
|
|
||||||
} else {
|
|
||||||
param.sched_priority = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = pthread_setschedparam(thread->p, pol, ¶m);
|
|
||||||
|
|
||||||
if (result == 0) {
|
|
||||||
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return KERNEL_ERROR_EINVAL;
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
|
if (prio <= 478) {
|
||||||
|
param.sched_priority = +2;
|
||||||
|
} else if (prio >= 733) {
|
||||||
|
param.sched_priority = -2;
|
||||||
|
} else {
|
||||||
|
param.sched_priority = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_setschedparam(thread->p, pol, ¶m) != 0) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
thread->attr->guest_priority = prio;
|
||||||
|
LOGF("\t PthreadSetprio: %d, %d\n", thread->unique_id, prio);
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KYTY_SYSV_ABI PthreadTestcancel() {
|
void KYTY_SYSV_ABI PthreadTestcancel() {
|
||||||
|
|||||||
@@ -141,14 +141,14 @@ elseif(LINUX)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
set(tidy_dirs "${CMAKE_SOURCE_DIR}/launcher/include")
|
set(tidy_dirs "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
set(iwyu_maps "${CMAKE_SOURCE_DIR}/launcher/utils/qt6_16.imp")
|
set(iwyu_maps "${CMAKE_CURRENT_SOURCE_DIR}/utils/qt6_16.imp")
|
||||||
|
|
||||||
get_property(inc_headers TARGET launcher PROPERTY INCLUDE_DIRECTORIES)
|
get_property(inc_headers TARGET launcher PROPERTY INCLUDE_DIRECTORIES)
|
||||||
|
|
||||||
list(APPEND inc_headers
|
list(APPEND inc_headers
|
||||||
${CMAKE_SOURCE_DIR}/launcher
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_BINARY_DIR}/launcher/launcher_autogen/include
|
${CMAKE_CURRENT_BINARY_DIR}/launcher_autogen/include
|
||||||
${Qt6Widgets_INCLUDE_DIRS}
|
${Qt6Widgets_INCLUDE_DIRS}
|
||||||
${Qt6Core_INCLUDE_DIRS}
|
${Qt6Core_INCLUDE_DIRS}
|
||||||
${Qt6Gui_INCLUDE_DIRS}
|
${Qt6Gui_INCLUDE_DIRS}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>560</width>
|
<width>560</width>
|
||||||
<height>420</height>
|
<height>450</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -78,19 +78,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_ngg_rectlist_draw">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Use the NGG 4-vertex path for rect-list DrawIndexAuto primitive 7</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Use NGG rect-list draw</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -132,41 +119,55 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_console_language">
|
||||||
|
<property name="text">
|
||||||
|
<string>Console language:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox_console_language">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Language</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_20">
|
<widget class="QLabel" name="label_20">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Shader optimization type:</string>
|
<string>Shader optimization type:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_shader_optimization_type">
|
<widget class="QComboBox" name="comboBox_shader_optimization_type">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Optimize shaders for code size or performance</string>
|
<string>Optimize shaders for code size or performance</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_21">
|
<widget class="QLabel" name="label_21">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Shader log direction:</string>
|
<string>Shader log direction:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_shader_log_direction">
|
<widget class="QComboBox" name="comboBox_shader_log_direction">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Dump shaders to file or console window. If enabled may decrease emulator performance</string>
|
<string>Dump shaders to file or console window. If enabled may decrease emulator performance</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_22">
|
<widget class="QLabel" name="label_22">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Shader log folder:</string>
|
<string>Shader log folder:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="MandatoryLineEdit" name="lineEdit_shader_log_folder">
|
<widget class="MandatoryLineEdit" name="lineEdit_shader_log_folder">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Specify directory to dump shaders</string>
|
<string>Specify directory to dump shaders</string>
|
||||||
@@ -176,14 +177,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="7" column="0">
|
||||||
<widget class="QLabel" name="label_24">
|
<widget class="QLabel" name="label_24">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Command buffer dump folder:</string>
|
<string>Command buffer dump folder:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="MandatoryLineEdit" name="lineEdit_cmd_dump_folder">
|
<widget class="MandatoryLineEdit" name="lineEdit_cmd_dump_folder">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Specify directory to dump command buffers</string>
|
<string>Specify directory to dump command buffers</string>
|
||||||
@@ -193,28 +194,28 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QLabel" name="label_25">
|
<widget class="QLabel" name="label_25">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Printf direction:</string>
|
<string>Printf direction:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="8" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_printf_direction">
|
<widget class="QComboBox" name="comboBox_printf_direction">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Print logs to file or console window. If enabled may decrease emulator performance</string>
|
<string>Print logs to file or console window. If enabled may decrease emulator performance</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0">
|
<item row="9" column="0">
|
||||||
<widget class="QLabel" name="label_26">
|
<widget class="QLabel" name="label_26">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Printf output file:</string>
|
<string>Printf output file:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="MandatoryLineEdit" name="lineEdit_printf_file">
|
<widget class="MandatoryLineEdit" name="lineEdit_printf_file">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Specify file to dump logs</string>
|
<string>Specify file to dump logs</string>
|
||||||
@@ -224,14 +225,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="10" column="0">
|
||||||
<widget class="QLabel" name="label_27">
|
<widget class="QLabel" name="label_27">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Profiler direction:</string>
|
<string>Profiler direction:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="10" column="1">
|
||||||
<widget class="QComboBox" name="comboBox_profiler_direction">
|
<widget class="QComboBox" name="comboBox_profiler_direction">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Enable or disable profiler. If enabled may decrease emulator performance</string>
|
<string>Enable or disable profiler. If enabled may decrease emulator performance</string>
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class Configuration: public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static constexpr int DEFAULT_CONSOLE_LANGUAGE = 1;
|
||||||
|
static constexpr int MAX_CONSOLE_LANGUAGE = 29;
|
||||||
|
|
||||||
enum class Resolution {
|
enum class Resolution {
|
||||||
R1280X720,
|
R1280X720,
|
||||||
R1920X1080,
|
R1920X1080,
|
||||||
@@ -83,6 +86,7 @@ public:
|
|||||||
|
|
||||||
Resolution screen_resolution = Resolution::R1280X720;
|
Resolution screen_resolution = Resolution::R1280X720;
|
||||||
int vblank_frequency = 60;
|
int vblank_frequency = 60;
|
||||||
|
int console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||||
bool vulkan_validation_enabled = true;
|
bool vulkan_validation_enabled = true;
|
||||||
bool shader_validation_enabled = true;
|
bool shader_validation_enabled = true;
|
||||||
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::Performance;
|
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::Performance;
|
||||||
@@ -94,13 +98,13 @@ public:
|
|||||||
QString printf_output_file = "_kyty.txt";
|
QString printf_output_file = "_kyty.txt";
|
||||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||||
bool renderdoc_enabled = false;
|
bool renderdoc_enabled = false;
|
||||||
bool ngg_rectlist_draw_enabled = true;
|
|
||||||
|
|
||||||
QString elf = QStringLiteral("eboot.bin");
|
QString elf = QStringLiteral("eboot.bin");
|
||||||
|
|
||||||
void CopyEmulatorSettingsFrom(const Configuration& other) {
|
void CopyEmulatorSettingsFrom(const Configuration& other) {
|
||||||
screen_resolution = other.screen_resolution;
|
screen_resolution = other.screen_resolution;
|
||||||
vblank_frequency = other.vblank_frequency;
|
vblank_frequency = other.vblank_frequency;
|
||||||
|
console_language = other.console_language;
|
||||||
vulkan_validation_enabled = other.vulkan_validation_enabled;
|
vulkan_validation_enabled = other.vulkan_validation_enabled;
|
||||||
shader_validation_enabled = other.shader_validation_enabled;
|
shader_validation_enabled = other.shader_validation_enabled;
|
||||||
shader_optimization_type = other.shader_optimization_type;
|
shader_optimization_type = other.shader_optimization_type;
|
||||||
@@ -112,7 +116,6 @@ public:
|
|||||||
printf_output_file = other.printf_output_file;
|
printf_output_file = other.printf_output_file;
|
||||||
profiler_direction = other.profiler_direction;
|
profiler_direction = other.profiler_direction;
|
||||||
renderdoc_enabled = other.renderdoc_enabled;
|
renderdoc_enabled = other.renderdoc_enabled;
|
||||||
ngg_rectlist_draw_enabled = other.ngg_rectlist_draw_enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyFrom(const Configuration& other) {
|
void CopyFrom(const Configuration& other) {
|
||||||
@@ -136,6 +139,7 @@ public:
|
|||||||
KYTY_CFG_SET(custom_settings);
|
KYTY_CFG_SET(custom_settings);
|
||||||
KYTY_CFG_SET(screen_resolution);
|
KYTY_CFG_SET(screen_resolution);
|
||||||
KYTY_CFG_SET(vblank_frequency);
|
KYTY_CFG_SET(vblank_frequency);
|
||||||
|
KYTY_CFG_SET(console_language);
|
||||||
KYTY_CFG_SET(vulkan_validation_enabled);
|
KYTY_CFG_SET(vulkan_validation_enabled);
|
||||||
KYTY_CFG_SET(shader_validation_enabled);
|
KYTY_CFG_SET(shader_validation_enabled);
|
||||||
KYTY_CFG_SET(shader_optimization_type);
|
KYTY_CFG_SET(shader_optimization_type);
|
||||||
@@ -147,7 +151,6 @@ public:
|
|||||||
KYTY_CFG_SET(printf_output_file);
|
KYTY_CFG_SET(printf_output_file);
|
||||||
KYTY_CFG_SET(profiler_direction);
|
KYTY_CFG_SET(profiler_direction);
|
||||||
KYTY_CFG_SET(renderdoc_enabled);
|
KYTY_CFG_SET(renderdoc_enabled);
|
||||||
KYTY_CFG_SET(ngg_rectlist_draw_enabled);
|
|
||||||
KYTY_CFG_SET(elf);
|
KYTY_CFG_SET(elf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +161,10 @@ public:
|
|||||||
KYTY_CFG_GET(custom_settings);
|
KYTY_CFG_GET(custom_settings);
|
||||||
KYTY_CFG_GET(screen_resolution);
|
KYTY_CFG_GET(screen_resolution);
|
||||||
vblank_frequency = s->value("vblank_frequency", vblank_frequency).toInt();
|
vblank_frequency = s->value("vblank_frequency", vblank_frequency).toInt();
|
||||||
|
console_language = s->value("console_language", console_language).toInt();
|
||||||
|
if (console_language < 0 || console_language > MAX_CONSOLE_LANGUAGE) {
|
||||||
|
console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||||
|
}
|
||||||
KYTY_CFG_GET(vulkan_validation_enabled);
|
KYTY_CFG_GET(vulkan_validation_enabled);
|
||||||
KYTY_CFG_GET(shader_validation_enabled);
|
KYTY_CFG_GET(shader_validation_enabled);
|
||||||
KYTY_CFG_GET(shader_optimization_type);
|
KYTY_CFG_GET(shader_optimization_type);
|
||||||
@@ -169,8 +176,6 @@ public:
|
|||||||
KYTY_CFG_GET(printf_output_file);
|
KYTY_CFG_GET(printf_output_file);
|
||||||
KYTY_CFG_GET(profiler_direction);
|
KYTY_CFG_GET(profiler_direction);
|
||||||
KYTY_CFG_GET(renderdoc_enabled);
|
KYTY_CFG_GET(renderdoc_enabled);
|
||||||
ngg_rectlist_draw_enabled =
|
|
||||||
s->value("ngg_rectlist_draw_enabled", ngg_rectlist_draw_enabled).toBool();
|
|
||||||
elf = s->value("elf", elf).toString();
|
elf = s->value("elf", elf).toString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,39 @@ constexpr char SETTINGS_CFG_DIALOG[] = "ConfigurationEditDialog";
|
|||||||
constexpr char SETTINGS_CFG_LAST_GEOMETRY[] = "geometry";
|
constexpr char SETTINGS_CFG_LAST_GEOMETRY[] = "geometry";
|
||||||
constexpr int GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH = 560;
|
constexpr int GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH = 560;
|
||||||
|
|
||||||
|
const QStringList CONSOLE_LANGUAGE_NAMES = {
|
||||||
|
"Japanese",
|
||||||
|
"English (United States)",
|
||||||
|
"French (France)",
|
||||||
|
"Spanish (Spain)",
|
||||||
|
"German",
|
||||||
|
"Italian",
|
||||||
|
"Dutch",
|
||||||
|
"Portuguese (Portugal)",
|
||||||
|
"Russian",
|
||||||
|
"Korean",
|
||||||
|
"Chinese (Traditional)",
|
||||||
|
"Chinese (Simplified)",
|
||||||
|
"Finnish",
|
||||||
|
"Swedish",
|
||||||
|
"Danish",
|
||||||
|
"Norwegian",
|
||||||
|
"Polish",
|
||||||
|
"Portuguese (Brazil)",
|
||||||
|
"English (United Kingdom)",
|
||||||
|
"Turkish",
|
||||||
|
"Spanish (Latin America)",
|
||||||
|
"Arabic",
|
||||||
|
"French (Canada)",
|
||||||
|
"Czech",
|
||||||
|
"Hungarian",
|
||||||
|
"Greek",
|
||||||
|
"Romanian",
|
||||||
|
"Thai",
|
||||||
|
"Vietnamese",
|
||||||
|
"Indonesian",
|
||||||
|
};
|
||||||
|
|
||||||
static QString NormalizeGameDirectory(const QString& dir) {
|
static QString NormalizeGameDirectory(const QString& dir) {
|
||||||
const auto trimmed = dir.trimmed();
|
const auto trimmed = dir.trimmed();
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
@@ -121,10 +154,15 @@ static void ListInit(QComboBox* combo, T value) {
|
|||||||
void ConfigurationEditDialog::Init(const Configuration& info) {
|
void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||||
ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution);
|
ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution);
|
||||||
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
|
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
|
||||||
|
m_ui->comboBox_console_language->clear();
|
||||||
|
m_ui->comboBox_console_language->addItems(CONSOLE_LANGUAGE_NAMES);
|
||||||
|
m_ui->comboBox_console_language->setCurrentIndex(
|
||||||
|
info.console_language >= 0 && info.console_language < CONSOLE_LANGUAGE_NAMES.size()
|
||||||
|
? info.console_language
|
||||||
|
: Configuration::DEFAULT_CONSOLE_LANGUAGE);
|
||||||
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
|
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
|
||||||
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_enabled);
|
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_enabled);
|
||||||
m_ui->checkBox_renderdoc_capture->setChecked(info.renderdoc_enabled);
|
m_ui->checkBox_renderdoc_capture->setChecked(info.renderdoc_enabled);
|
||||||
m_ui->checkBox_ngg_rectlist_draw->setChecked(info.ngg_rectlist_draw_enabled);
|
|
||||||
ListInit(m_ui->comboBox_shader_optimization_type, info.shader_optimization_type);
|
ListInit(m_ui->comboBox_shader_optimization_type, info.shader_optimization_type);
|
||||||
ListInit(m_ui->comboBox_shader_log_direction, info.shader_log_direction);
|
ListInit(m_ui->comboBox_shader_log_direction, info.shader_log_direction);
|
||||||
m_ui->lineEdit_shader_log_folder->setText(info.shader_log_folder);
|
m_ui->lineEdit_shader_log_folder->setText(info.shader_log_folder);
|
||||||
@@ -242,10 +280,10 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
|
|||||||
info.screen_resolution =
|
info.screen_resolution =
|
||||||
TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText());
|
TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText());
|
||||||
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
|
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
|
||||||
|
info.console_language = ui.comboBox_console_language->currentIndex();
|
||||||
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
|
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
|
||||||
info.shader_validation_enabled = ui.checkBox_shader_validation->isChecked();
|
info.shader_validation_enabled = ui.checkBox_shader_validation->isChecked();
|
||||||
info.renderdoc_enabled = ui.checkBox_renderdoc_capture->isChecked();
|
info.renderdoc_enabled = ui.checkBox_renderdoc_capture->isChecked();
|
||||||
info.ngg_rectlist_draw_enabled = ui.checkBox_ngg_rectlist_draw->isChecked();
|
|
||||||
info.shader_optimization_type = TextToEnum<Configuration::ShaderOptimizationType>(
|
info.shader_optimization_type = TextToEnum<Configuration::ShaderOptimizationType>(
|
||||||
ui.comboBox_shader_optimization_type->currentText());
|
ui.comboBox_shader_optimization_type->currentText());
|
||||||
info.shader_log_direction = TextToEnum<Configuration::ShaderLogDirection>(
|
info.shader_log_direction = TextToEnum<Configuration::ShaderLogDirection>(
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
|
|||||||
args << "--screen-width" << r.at(0);
|
args << "--screen-width" << r.at(0);
|
||||||
args << "--screen-height" << r.at(1);
|
args << "--screen-height" << r.at(1);
|
||||||
args << "--vblank-frequency" << QString::number(info.vblank_frequency);
|
args << "--vblank-frequency" << QString::number(info.vblank_frequency);
|
||||||
|
args << "--console-language" << QString::number(info.console_language);
|
||||||
args << "--vulkan-validation" << BoolArg(info.vulkan_validation_enabled);
|
args << "--vulkan-validation" << BoolArg(info.vulkan_validation_enabled);
|
||||||
args << "--shader-validation" << BoolArg(info.shader_validation_enabled);
|
args << "--shader-validation" << BoolArg(info.shader_validation_enabled);
|
||||||
args << "--shader-optimization-type" << EnumToText(info.shader_optimization_type);
|
args << "--shader-optimization-type" << EnumToText(info.shader_optimization_type);
|
||||||
@@ -216,7 +217,6 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
|
|||||||
args << "--printf-output-file" << info.printf_output_file;
|
args << "--printf-output-file" << info.printf_output_file;
|
||||||
args << "--profiler-direction" << EnumToText(info.profiler_direction);
|
args << "--profiler-direction" << EnumToText(info.profiler_direction);
|
||||||
args << "--spirv-debug-printf" << "false";
|
args << "--spirv-debug-printf" << "false";
|
||||||
args << "--ngg-rectlist-draw" << BoolArg(info.ngg_rectlist_draw_enabled);
|
|
||||||
if (info.renderdoc_enabled) {
|
if (info.renderdoc_enabled) {
|
||||||
args << "--rd";
|
args << "--rd";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2021,6 +2021,12 @@ int64_t KYTY_SYSV_ABI fstat(int d, LibKernel::FileSystem::FileStat* sb) {
|
|||||||
return POSIX_N_CALL(LibKernel::FileSystem::KernelFstat(d, sb));
|
return POSIX_N_CALL(LibKernel::FileSystem::KernelFstat(d, sb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int KYTY_SYSV_ABI ftruncate(int d, int64_t length) {
|
||||||
|
PRINT_NAME();
|
||||||
|
|
||||||
|
return POSIX_CALL(LibKernel::FileSystem::KernelFtruncate(d, length));
|
||||||
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI socket(int family, int type, int protocol) {
|
int KYTY_SYSV_ABI socket(int family, int type, int protocol) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
return Network::Net::Socket(family, type, protocol);
|
return Network::Net::Socket(family, type, protocol);
|
||||||
@@ -2159,6 +2165,7 @@ LIB_DEFINE(InitLibKernel_1_Posix) {
|
|||||||
LIB_FUNC("yS8U2TGCe1A", nanosleep);
|
LIB_FUNC("yS8U2TGCe1A", nanosleep);
|
||||||
LIB_FUNC("E6ao34wPw+U", stat);
|
LIB_FUNC("E6ao34wPw+U", stat);
|
||||||
LIB_FUNC("JGMio+21L4c", mkdir);
|
LIB_FUNC("JGMio+21L4c", mkdir);
|
||||||
|
LIB_FUNC("ih4CD9-gghM", Posix::ftruncate);
|
||||||
LIB_FUNC("pDuPEf3m4fI", Posix::sem_init);
|
LIB_FUNC("pDuPEf3m4fI", Posix::sem_init);
|
||||||
LIB_FUNC("cDW233RAwWo", Posix::sem_destroy);
|
LIB_FUNC("cDW233RAwWo", Posix::sem_destroy);
|
||||||
LIB_FUNC("YCV5dGGBcCo", Posix::sem_wait);
|
LIB_FUNC("YCV5dGGBcCo", Posix::sem_wait);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "common/abi.h"
|
#include "common/abi.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/stringUtils.h"
|
#include "common/stringUtils.h"
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
@@ -25,37 +26,6 @@ namespace SystemService {
|
|||||||
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
|
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
|
||||||
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
|
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
|
||||||
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_JAPANESE = 0;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_ENGLISH_US = 1;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_FRENCH = 2;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_SPANISH = 3;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_GERMAN = 4;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_ITALIAN = 5;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_DUTCH = 6;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_PORTUGUESE_PT = 7;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_RUSSIAN = 8;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_KOREAN = 9;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_CHINESE_T = 10;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_CHINESE_S = 11;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_FINNISH = 12;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_SWEDISH = 13;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_DANISH = 14;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_NORWEGIAN = 15;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_POLISH = 16;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_PORTUGUESE_BR = 17;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_ENGLISH_GB = 18;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_TURKISH = 19;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_SPANISH_LA = 20;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_ARABIC = 21;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_FRENCH_CA = 22;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_CZECH = 23;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_HUNGARIAN = 24;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_GREEK = 25;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_ROMANIAN = 26;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_THAI = 27;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_VIETNAMESE = 28;
|
|
||||||
[[maybe_unused]] constexpr int PARAM_LANG_INDONESIAN = 29;
|
|
||||||
|
|
||||||
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_YYYYMMDD = 0;
|
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_YYYYMMDD = 0;
|
||||||
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_DDMMYYYY = 1;
|
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_DDMMYYYY = 1;
|
||||||
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_MMDDYYYY = 2;
|
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_MMDDYYYY = 2;
|
||||||
@@ -121,7 +91,7 @@ static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
|
|||||||
int v = 0;
|
int v = 0;
|
||||||
|
|
||||||
switch (param_id) {
|
switch (param_id) {
|
||||||
case PARAM_ID_LANG: v = PARAM_LANG_ENGLISH_US; break;
|
case PARAM_ID_LANG: v = static_cast<int>(Config::GetConsoleLanguage()); break;
|
||||||
case PARAM_ID_DATE_FORMAT: v = PARAM_DATE_FORMAT_DDMMYYYY; break;
|
case PARAM_ID_DATE_FORMAT: v = PARAM_DATE_FORMAT_DDMMYYYY; break;
|
||||||
case PARAM_ID_TIME_FORMAT: v = PARAM_TIME_FORMAT_24HOUR; break;
|
case PARAM_ID_TIME_FORMAT: v = PARAM_TIME_FORMAT_24HOUR; break;
|
||||||
case PARAM_ID_TIME_ZONE: v = +180; break;
|
case PARAM_ID_TIME_ZONE: v = +180; break;
|
||||||
|
|||||||
@@ -360,9 +360,10 @@ static KYTY_SYSV_ABI void RunEntry(uint64_t addr, EntryParams* params, atexit_fu
|
|||||||
auto* func = reinterpret_cast<entry_func_t>(addr);
|
auto* func = reinterpret_cast<entry_func_t>(addr);
|
||||||
|
|
||||||
if (stack_top != nullptr) {
|
if (stack_top != nullptr) {
|
||||||
const auto guest_rsp =
|
const auto aligned_stack_top =
|
||||||
reinterpret_cast<uintptr_t>(stack_top) & ~static_cast<uintptr_t>(0x0f);
|
reinterpret_cast<uintptr_t>(stack_top) & ~static_cast<uintptr_t>(0x0f);
|
||||||
const auto guest_rbp = guest_rsp - 4u * sizeof(uint64_t);
|
const auto guest_rsp = aligned_stack_top - 2u * sizeof(uintptr_t);
|
||||||
|
const auto guest_rbp = guest_rsp;
|
||||||
|
|
||||||
auto* guest_root_frame = reinterpret_cast<uintptr_t*>(guest_rbp);
|
auto* guest_root_frame = reinterpret_cast<uintptr_t*>(guest_rbp);
|
||||||
guest_root_frame[0] = 0;
|
guest_root_frame[0] = 0;
|
||||||
@@ -507,6 +508,13 @@ struct MainEntryStackTestState {
|
|||||||
static KYTY_SYSV_ABI void TestMainEntryStackCallback(EntryParams* params,
|
static KYTY_SYSV_ABI void TestMainEntryStackCallback(EntryParams* params,
|
||||||
atexit_func_t /*atexit_func*/) {
|
atexit_func_t /*atexit_func*/) {
|
||||||
auto* state = reinterpret_cast<MainEntryStackTestState*>(const_cast<char*>(params->argv[0]));
|
auto* state = reinterpret_cast<MainEntryStackTestState*>(const_cast<char*>(params->argv[0]));
|
||||||
|
asm volatile("pushq %%r15\n\t"
|
||||||
|
"pushq %%r14\n\t"
|
||||||
|
"popq %%r14\n\t"
|
||||||
|
"popq %%r15\n\t"
|
||||||
|
:
|
||||||
|
:
|
||||||
|
: "memory");
|
||||||
asm volatile("movq %%rsp, %0" : "=r"(state->rsp) : : "memory");
|
asm volatile("movq %%rsp, %0" : "=r"(state->rsp) : : "memory");
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
asm volatile("movq %%gs:0x08, %0\n\t"
|
asm volatile("movq %%gs:0x08, %0\n\t"
|
||||||
@@ -529,6 +537,8 @@ bool TestMainEntryUsesGuestStack() {
|
|||||||
MainEntryStackTestState state {};
|
MainEntryStackTestState state {};
|
||||||
EntryParams params {};
|
EntryParams params {};
|
||||||
params.argv[0] = reinterpret_cast<const char*>(&state);
|
params.argv[0] = reinterpret_cast<const char*>(&state);
|
||||||
|
std::memset(reinterpret_cast<void*>(stack_base), 0xcd, stack_size);
|
||||||
|
auto* root_frame = reinterpret_cast<const uintptr_t*>(stack_base + stack_size) - 2;
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
uintptr_t original_teb_stack_base = 0;
|
uintptr_t original_teb_stack_base = 0;
|
||||||
@@ -558,9 +568,10 @@ bool TestMainEntryUsesGuestStack() {
|
|||||||
constexpr bool teb_ok = true;
|
constexpr bool teb_ok = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const bool rsp_ok = state.rsp >= stack_base && state.rsp < stack_base + stack_size;
|
const bool rsp_ok = state.rsp >= stack_base && state.rsp < stack_base + stack_size;
|
||||||
const bool freed = Libs::LibKernel::Memory::FreeGuestMemory(stack_base, stack_size);
|
const bool root_ok = root_frame[0] == 0 && root_frame[1] == 0;
|
||||||
return state.called && rsp_ok && teb_ok && freed;
|
const bool freed = Libs::LibKernel::Memory::FreeGuestMemory(stack_base, stack_size);
|
||||||
|
return state.called && rsp_ok && root_ok && teb_ok && freed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestModuleRelocationUsesWritableHostMapping() {
|
bool TestModuleRelocationUsesWritableHostMapping() {
|
||||||
|
|||||||
+18
-7
@@ -10,6 +10,7 @@
|
|||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#include "kytyGitVersion.h"
|
#include "kytyGitVersion.h"
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ static void PrintUsage() {
|
|||||||
::printf(" --screen-width <num> Window width. Default: 1280.\n");
|
::printf(" --screen-width <num> Window width. Default: 1280.\n");
|
||||||
::printf(" --screen-height <num> Window height. Default: 720.\n");
|
::printf(" --screen-height <num> Window height. Default: 720.\n");
|
||||||
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
|
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
|
||||||
|
::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n");
|
||||||
::printf(" --vulkan-validation <true|false> Enable Vulkan validation.\n");
|
::printf(" --vulkan-validation <true|false> Enable Vulkan validation.\n");
|
||||||
::printf(" --shader-validation <true|false> Enable shader validation.\n");
|
::printf(" --shader-validation <true|false> Enable shader validation.\n");
|
||||||
::printf(" --shader-optimization-type <value> None, Size, or Performance.\n");
|
::printf(" --shader-optimization-type <value> None, Size, or Performance.\n");
|
||||||
@@ -59,8 +61,6 @@ static void PrintUsage() {
|
|||||||
::printf(" --printf-output-file <path> Guest printf output file.\n");
|
::printf(" --printf-output-file <path> Guest printf output file.\n");
|
||||||
::printf(" --profiler-direction <value> None or Network.\n");
|
::printf(" --profiler-direction <value> None or Network.\n");
|
||||||
::printf(" --spirv-debug-printf <true|false> Enable SPIR-V debug printf.\n");
|
::printf(" --spirv-debug-printf <true|false> Enable SPIR-V debug printf.\n");
|
||||||
::printf(" --ngg-rectlist-draw <true|false> Draw rect-list auto draws using the NGG "
|
|
||||||
"4-vertex path.\n");
|
|
||||||
::printf(
|
::printf(
|
||||||
" --readback-linear-images <true|false> Read back writable linear images on submit.\n");
|
" --readback-linear-images <true|false> Read back writable linear images on submit.\n");
|
||||||
::printf(" --rd Enable RenderDoc capture.\n");
|
::printf(" --rd Enable RenderDoc capture.\n");
|
||||||
@@ -103,6 +103,17 @@ static bool ParseEnum(const std::string& value, E& out) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ParseConsoleLanguage(const std::string& value, uint32_t& out) {
|
||||||
|
uint32_t language = 0;
|
||||||
|
auto [end, error] = std::from_chars(value.data(), value.data() + value.size(), language);
|
||||||
|
if (error != std::errc {} || end != value.data() + value.size() ||
|
||||||
|
language > Config::MAX_CONSOLE_LANGUAGE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out = language;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_help) {
|
static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_help) {
|
||||||
show_help = false;
|
show_help = false;
|
||||||
|
|
||||||
@@ -169,6 +180,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
|||||||
const int32_t vblank_frequency = Common::ToInt32(value);
|
const int32_t vblank_frequency = Common::ToInt32(value);
|
||||||
options.config.vblank_frequency =
|
options.config.vblank_frequency =
|
||||||
static_cast<uint32_t>(vblank_frequency < 0 ? 0 : vblank_frequency);
|
static_cast<uint32_t>(vblank_frequency < 0 ? 0 : vblank_frequency);
|
||||||
|
} else if (arg == "--console-language") {
|
||||||
|
if (!ParseConsoleLanguage(value, options.config.console_language)) {
|
||||||
|
::printf("invalid console language: %s\n", value.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (arg == "--vulkan-validation") {
|
} else if (arg == "--vulkan-validation") {
|
||||||
if (!ParseBool(value, options.config.vulkan_validation_enabled)) {
|
if (!ParseBool(value, options.config.vulkan_validation_enabled)) {
|
||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||||
@@ -220,11 +236,6 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
|||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (arg == "--ngg-rectlist-draw") {
|
|
||||||
if (!ParseBool(value, options.config.ngg_rectlist_draw_enabled)) {
|
|
||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (arg == "--readback-linear-images") {
|
} else if (arg == "--readback-linear-images") {
|
||||||
if (!ParseBool(value, options.config.readback_linear_images)) {
|
if (!ParseBool(value, options.config.readback_linear_images)) {
|
||||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||||
|
|||||||
@@ -3,13 +3,15 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Owners = std::vector<uint32_t>;
|
using Owners = std::vector<uint32_t>;
|
||||||
using Table = Libs::Graphics::MultiLevelPageTable<Owners>;
|
using Table = Libs::Graphics::MultiLevelPageTable<Owners>;
|
||||||
using OwnerIndex = Libs::Graphics::MultiRangePageOwnerIndex<uint32_t>;
|
using PageOwners = Libs::Graphics::InlinePageOwnerList<uint32_t, 16>;
|
||||||
|
using OwnerTable = Libs::Graphics::MultiLevelPageTable<PageOwners, 20, 40, 10>;
|
||||||
|
|
||||||
void Check(bool value, const char* text) {
|
void Check(bool value, const char* text) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -77,76 +79,93 @@ void TestAddressSpaceBoundaries() {
|
|||||||
"final page supports allocating and nonallocating access");
|
"final page supports allocating and nonallocating access");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestMultiRangeRegistrationDeduplicatesPages() {
|
void TestInlineOwnerStorageAndOverflow() {
|
||||||
OwnerIndex index;
|
PageOwners owners;
|
||||||
// Depth and stencil-like planes overlap tracking pages and share one 1 MiB bucket.
|
for (uint32_t owner = 1; owner <= 18; ++owner) {
|
||||||
Check(index.Register(7, {{0x101000, 0x2800}, {0x102000, 0x3000}}),
|
owners.push_back(owner);
|
||||||
"multi-range owner registers");
|
}
|
||||||
Check(index.CoarseMembershipCount(1) == 1,
|
Check(owners.size() == 18 && owners.front() == 1,
|
||||||
"one owner is inserted once in a shared 1 MiB bucket");
|
"inline owner storage grows past its 16-owner capacity");
|
||||||
Check(index.TrackingMembershipCount(0x102) == 1,
|
uint32_t expected = 1;
|
||||||
"overlapping planes insert one 4 KiB membership");
|
for (const uint32_t owner: owners) {
|
||||||
Check(!index.Register(7, {{0x101000, 0x1000}}), "duplicate owner registration hard-fails");
|
Check(owner == expected++, "overflow preserves registration order");
|
||||||
|
}
|
||||||
|
Check(owners.Erase(5) && owners.size() == 17 && !owners.Contains(5),
|
||||||
|
"overflow erase removes only the requested owner");
|
||||||
|
Check(owners.Erase(18) && owners.size() == 16,
|
||||||
|
"overflow storage shrinks back to inline capacity");
|
||||||
|
const std::vector<uint32_t> remaining {1, 2, 3, 4, 6, 7, 8, 9,
|
||||||
|
10, 11, 12, 13, 14, 15, 16, 17};
|
||||||
|
size_t remaining_index = 0;
|
||||||
|
for (const uint32_t owner: owners) {
|
||||||
|
Check(owner == remaining[remaining_index++],
|
||||||
|
"overflow-to-inline shrink preserves every owner");
|
||||||
|
}
|
||||||
|
Check(!owners.Erase(99), "missing inline owner is reported without mutation");
|
||||||
|
|
||||||
const auto owners = index.Query(0x100000, 0x10000);
|
PageOwners moved = std::move(owners);
|
||||||
Check(owners.size() == 1 && owners.front() == 7, "multi-page query returns an owner once");
|
Check(owners.empty() && moved.size() == remaining.size() && moved.front() == 1,
|
||||||
|
"moving a full inline owner list leaves the source empty");
|
||||||
|
PageOwners partial;
|
||||||
|
partial.push_back(41);
|
||||||
|
partial.push_back(42);
|
||||||
|
PageOwners partial_moved = std::move(partial);
|
||||||
|
Check(partial.empty() && partial_moved.size() == 2 && partial_moved[1] == 42,
|
||||||
|
"moving a partially populated list copies only live owners");
|
||||||
|
PageOwners assigned;
|
||||||
|
assigned.push_back(99);
|
||||||
|
assigned = std::move(partial_moved);
|
||||||
|
Check(partial_moved.empty() && assigned.size() == 2 && assigned.front() == 41,
|
||||||
|
"move assignment replaces an inline list without reading inactive slots");
|
||||||
|
PageOwners empty;
|
||||||
|
PageOwners empty_moved = std::move(empty);
|
||||||
|
Check(empty.empty() && empty_moved.empty(), "moving an empty owner list is safe");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSharedPageUnregisterLifecycle() {
|
void TestOneMiBRegistrationGranularity() {
|
||||||
OwnerIndex index;
|
static_assert(OwnerTable::kPageBits == 20);
|
||||||
const std::vector<OwnerIndex::ByteRange> ranges {{0x202000, 0x2000}};
|
OwnerTable table;
|
||||||
Check(index.Register(11, ranges) && index.Register(22, ranges),
|
OwnerTable::PageRange pages {};
|
||||||
"two owners register on identical pages");
|
constexpr uint64_t range_size = 64ull * 1024 * 1024;
|
||||||
Check(index.CoarseMembershipCount(2) == 2 && index.TrackingMembershipCount(0x202) == 2,
|
Check(OwnerTable::TryGetPageRange(0, range_size, pages), "large owner range is valid");
|
||||||
"coarse and tracking pages retain both owners");
|
Check(pages.first == 0 && pages.last_exclusive == 64,
|
||||||
|
"64 MiB registration touches exactly 64 one-MiB entries");
|
||||||
std::vector<OwnerIndex::ByteRange> releases;
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
Check(index.Unregister(11, releases), "first owner unregisters");
|
table[page].push_back(7);
|
||||||
Check(releases.empty(), "shared tracking pages are not released with one owner remaining");
|
}
|
||||||
Check(index.Query(0x202000, 1).size() == 1 && index.Query(0x202000, 1).front() == 22,
|
Check(table.AllocatedBucketCount() == 1,
|
||||||
"unregistering one owner preserves the other");
|
"large registration uses one sparse second-level bucket");
|
||||||
Check(!index.Unregister(11, releases), "missing membership hard-fails without mutation");
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
|
auto* owners = table.Find(page);
|
||||||
Check(index.Unregister(22, releases), "final owner unregisters");
|
Check(owners != nullptr && owners->size() == 1 && owners->front() == 7,
|
||||||
Check(releases.size() == 1 && releases.front().address == 0x202000 &&
|
"each touched one-MiB entry retains the owner once");
|
||||||
releases.front().size == 0x2000,
|
Check(owners->Erase(7), "large owner unregisters by coarse page");
|
||||||
"adjacent final-owner tracking pages return one contiguous release");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestStrictByteFilteringAndPredicate() {
|
void TestSharedCoarsePageLifecycle() {
|
||||||
OwnerIndex index;
|
OwnerTable table;
|
||||||
Check(index.Register(31, {{0x300100, 0x100}}), "first byte-disjoint owner registers");
|
auto& owners = table[2];
|
||||||
Check(index.Register(32, {{0x300800, 0x100}}), "second byte-disjoint owner registers");
|
owners.push_back(11);
|
||||||
Check(index.Register(33, {{0x30f000, 0x100}}), "coarse-only owner registers");
|
owners.push_back(22);
|
||||||
Check(index.TrackingMembershipCount(0x300) == 2,
|
Check(owners.size() == 2, "two images share one coarse page");
|
||||||
"byte-disjoint owners share one tracking page");
|
Check(owners.Erase(11) && owners.size() == 1 && owners.front() == 22,
|
||||||
Check(index.CoarseMembershipCount(3) == 3,
|
"unregistering one image preserves its coarse-page neighbor");
|
||||||
"all owners share one coarse candidate bucket");
|
Check(!owners.Erase(11), "double unregister is rejected without mutation");
|
||||||
Check(index.Query(0x300400, 0x40).empty(), "page hit without byte overlap is filtered out");
|
Check(owners.Erase(22) && owners.empty(), "final coarse-page owner unregisters");
|
||||||
const auto page_candidates = index.QueryCandidates(0x300400, 0x40);
|
|
||||||
Check(page_candidates.size() == 2,
|
|
||||||
"fault candidate query retains touched-page owners and rejects coarse-only owners");
|
|
||||||
const auto first = index.Query(0x300180, 0x10);
|
|
||||||
Check(first.size() == 1 && first.front() == 31,
|
|
||||||
"strict byte overlap selects only the matching owner");
|
|
||||||
const auto predicate_filtered =
|
|
||||||
index.Query(0x300000, 0x1000, [](uint32_t owner) { return owner == 32; });
|
|
||||||
Check(predicate_filtered.size() == 1 && predicate_filtered.front() == 32,
|
|
||||||
"supplied predicate filters query owners");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestOwnerIndexAddressSpaceBoundary() {
|
void TestOneMiBBoundaries() {
|
||||||
OwnerIndex index;
|
OwnerTable::PageRange range {};
|
||||||
constexpr uint64_t last_byte = OwnerIndex::CoarseTable::kAddressSpaceSize - 1;
|
Check(OwnerTable::TryGetPageRange(0x0fffff, 2, range),
|
||||||
Check(index.Register(41, {{last_byte, 1}}), "final guest byte registers");
|
"range crossing a one-MiB boundary is valid");
|
||||||
const auto exact = index.Query(last_byte, 1);
|
Check(range.first == 0 && range.last_exclusive == 2,
|
||||||
Check(exact.size() == 1 && exact.front() == 41,
|
"cross-boundary registration touches both coarse pages");
|
||||||
"strict query finds an exact overlap at the final guest byte");
|
Check(OwnerTable::TryGetPageRange(OwnerTable::kAddressSpaceSize - 1, 1, range),
|
||||||
Check(index.Query(last_byte - 1, 1).empty(),
|
"final guest byte maps to a coarse owner page");
|
||||||
"strict query preserves half-open overlap boundaries");
|
Check(range.first == OwnerTable::kPageCount - 1 &&
|
||||||
const auto page_candidates = index.QueryCandidates(last_byte - 1, 1);
|
range.last_exclusive == OwnerTable::kPageCount,
|
||||||
Check(page_candidates.size() == 1 && page_candidates.front() == 41,
|
"final guest byte uses the final one-MiB page");
|
||||||
"page candidate query retains a byte-disjoint owner on the final tracking page");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -156,10 +175,10 @@ int main() {
|
|||||||
TestCrossBucketRange();
|
TestCrossBucketRange();
|
||||||
TestQueriesDoNotAllocate();
|
TestQueriesDoNotAllocate();
|
||||||
TestAddressSpaceBoundaries();
|
TestAddressSpaceBoundaries();
|
||||||
TestMultiRangeRegistrationDeduplicatesPages();
|
TestInlineOwnerStorageAndOverflow();
|
||||||
TestSharedPageUnregisterLifecycle();
|
TestOneMiBRegistrationGranularity();
|
||||||
TestStrictByteFilteringAndPredicate();
|
TestSharedCoarsePageLifecycle();
|
||||||
TestOwnerIndexAddressSpaceBoundary();
|
TestOneMiBBoundaries();
|
||||||
std::printf("ImagePageTableTests: all cases passed\n");
|
std::printf("ImagePageTableTests: all cases passed\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
|
||||||
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
|
||||||
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
#include "graphics/shader/recompiler/ir/BindingLayout.h"
|
||||||
|
#include "graphics/shader/rectListShader.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
#include "kernel/memory.h"
|
#include "kernel/memory.h"
|
||||||
#include "spirv-tools/libspirv.hpp"
|
#include "spirv-tools/libspirv.hpp"
|
||||||
@@ -162,6 +163,10 @@ struct TileManagerTestAccess {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct TextureCacheTestAccess {
|
struct TextureCacheTestAccess {
|
||||||
|
static_assert(TextureCache::ImagePageTable::kPageBits == 20);
|
||||||
|
static_assert(TextureCache::ImagePageTable::kAddressSpaceBits == 40);
|
||||||
|
static_assert(TextureCache::ImagePageTable::kFirstLevelBits == 10);
|
||||||
|
|
||||||
static void ConfigureGarbageCollection(TextureCache& cache, std::span<const ImageId> oldest,
|
static void ConfigureGarbageCollection(TextureCache& cache, std::span<const ImageId> oldest,
|
||||||
uint64_t tick, uint64_t pressure) {
|
uint64_t tick, uint64_t pressure) {
|
||||||
cache.m_trigger_gc_memory = 0;
|
cache.m_trigger_gc_memory = 0;
|
||||||
@@ -196,6 +201,75 @@ struct TextureCacheTestAccess {
|
|||||||
return owner != nullptr && owner->registered;
|
return owner != nullptr && owner->registered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<ImageId> FindImages(TextureCache& cache, uint64_t address, uint64_t size,
|
||||||
|
bool page_overlap) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
const auto found = cache.FindImagesInRegion(address, size, page_overlap);
|
||||||
|
std::vector<ImageId> result;
|
||||||
|
result.reserve(found.size());
|
||||||
|
for (const auto id: found) {
|
||||||
|
result.push_back(id);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t PageOwnerCount(TextureCache& cache, uint64_t address) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
const auto* owners = cache.m_image_page_table.Find(
|
||||||
|
static_cast<size_t>(address >> TextureCache::ImagePageTable::kPageBits));
|
||||||
|
return owners == nullptr ? 0 : owners->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t OwnedPageCount(TextureCache& cache, uint64_t address, uint64_t size, ImageId id) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
TextureCache::ImagePageTable::PageRange pages {};
|
||||||
|
if (!TextureCache::ImagePageTable::TryGetPageRange(address, size, pages)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t page = pages.first; page < pages.last_exclusive; ++page) {
|
||||||
|
const auto* owners = cache.m_image_page_table.Find(page);
|
||||||
|
count += owners != nullptr && owners->Contains(id) ? 1 : 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddPageOwner(TextureCache& cache, uint64_t address, ImageId id) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
cache.m_image_page_table[static_cast<size_t>(
|
||||||
|
address >> TextureCache::ImagePageTable::kPageBits)]
|
||||||
|
.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RemovePageOwner(TextureCache& cache, uint64_t address, ImageId id) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
auto* owners = cache.m_image_page_table.Find(
|
||||||
|
static_cast<size_t>(address >> TextureCache::ImagePageTable::kPageBits));
|
||||||
|
return owners != nullptr && owners->Erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetQueryEpoch(TextureCache& cache, uint32_t epoch) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
cache.m_image_query_epoch = epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t QueryEpoch(TextureCache& cache) {
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
return cache.m_image_query_epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImageId InsertImage(TextureCache& cache, const ImageInfo& info) {
|
||||||
|
std::lock_guard transaction(cache.m_resource_mutex);
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
return cache.InsertImage(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DeleteImage(TextureCache& cache, ImageId id) {
|
||||||
|
std::lock_guard transaction(cache.m_resource_mutex);
|
||||||
|
std::lock_guard lock(cache.m_lock);
|
||||||
|
cache.DeleteImage(id);
|
||||||
|
}
|
||||||
|
|
||||||
static std::shared_ptr<Image> Owner(const TextureCache& cache, ImageId id) {
|
static std::shared_ptr<Image> Owner(const TextureCache& cache, ImageId id) {
|
||||||
return cache.ResolveOwner(id);
|
return cache.ResolveOwner(id);
|
||||||
}
|
}
|
||||||
@@ -771,6 +845,91 @@ void ValidateSpirv(const char* shader_name, const std::vector<u32>& spirv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t CountText(const std::string& text, const std::string& needle) {
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t offset = 0; (offset = text.find(needle, offset)) != std::string::npos;
|
||||||
|
offset += needle.size()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckRectListShaders() {
|
||||||
|
constexpr const char* name = "RectListShaders";
|
||||||
|
|
||||||
|
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||||
|
program->info.inputs.push_back(
|
||||||
|
{ShaderRecompiler::IR::StageInputKind::Parameter, 0, 4, "in_param_0"});
|
||||||
|
program->info.inputs.push_back(
|
||||||
|
{ShaderRecompiler::IR::StageInputKind::Parameter, 1, 4, "in_param_1"});
|
||||||
|
|
||||||
|
ShaderVertexInputInfo vertex {};
|
||||||
|
vertex.param_export_mask = 1u;
|
||||||
|
ShaderPixelInputInfo pixel {};
|
||||||
|
pixel.input_num = 2;
|
||||||
|
pixel.interpolator_settings[0] = 0x400u;
|
||||||
|
pixel.interpolator_settings[1] = 0;
|
||||||
|
pixel.stage.program = program;
|
||||||
|
HW::PixelShaderInfo ps_regs {};
|
||||||
|
const auto perspective_id = ShaderGetIdPS(ps_regs, pixel, false);
|
||||||
|
pixel.ps_no_perspective = true;
|
||||||
|
const auto no_perspective_id = ShaderGetIdPS(ps_regs, pixel, false);
|
||||||
|
pixel.ps_no_perspective = false;
|
||||||
|
Require(name, "pipeline identity", perspective_id != no_perspective_id,
|
||||||
|
"pixel interpolation mode must participate in the shader and pipeline key");
|
||||||
|
|
||||||
|
const std::array<uint32_t, 2> active_inputs = {0, 1};
|
||||||
|
Require(name, "duplicate mapping",
|
||||||
|
ShaderPixelParameterLocation(pixel, active_inputs, 0) == 0 &&
|
||||||
|
ShaderPixelParameterLocation(pixel, active_inputs, 1) == 1,
|
||||||
|
"duplicate pixel mappings must receive distinct effective output locations");
|
||||||
|
|
||||||
|
const auto shaders = BuildRectListShaders(vertex, &pixel);
|
||||||
|
Require(name, "SPIR-V version",
|
||||||
|
shaders.control.size() > 1 && shaders.control[1] == 0x00010500u &&
|
||||||
|
shaders.evaluation.size() > 1 && shaders.evaluation[1] == 0x00010500u,
|
||||||
|
"shadPS4-compatible vector selection requires SPIR-V 1.5");
|
||||||
|
ValidateSpirv(name, shaders.control);
|
||||||
|
ValidateSpirv(name, shaders.evaluation);
|
||||||
|
|
||||||
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2);
|
||||||
|
std::string control_text;
|
||||||
|
std::string evaluation_text;
|
||||||
|
Require(name, "control disassembly", tools.Disassemble(shaders.control, &control_text),
|
||||||
|
"failed to disassemble rectangle-list tessellation control shader");
|
||||||
|
Require(name, "evaluation disassembly",
|
||||||
|
tools.Disassemble(shaders.evaluation, &evaluation_text),
|
||||||
|
"failed to disassemble rectangle-list tessellation evaluation shader");
|
||||||
|
Require(name, "control execution mode",
|
||||||
|
control_text.find("TessellationControl") != std::string::npos &&
|
||||||
|
control_text.find("OutputVertices 4") != std::string::npos,
|
||||||
|
"rectangle-list control shader must produce four control points");
|
||||||
|
Require(name, "evaluation execution modes",
|
||||||
|
evaluation_text.find("TessellationEvaluation") != std::string::npos &&
|
||||||
|
evaluation_text.find("Quads") != std::string::npos &&
|
||||||
|
evaluation_text.find("SpacingEqual") != std::string::npos &&
|
||||||
|
evaluation_text.find("VertexOrderCw") != std::string::npos,
|
||||||
|
"rectangle-list evaluation shader has the wrong patch modes");
|
||||||
|
Require(name, "no geometry stage",
|
||||||
|
control_text.find("Geometry") == std::string::npos &&
|
||||||
|
evaluation_text.find("Geometry") == std::string::npos,
|
||||||
|
"rectangle-list expansion must not use geometry shaders");
|
||||||
|
Require(name, "flat broadcast",
|
||||||
|
CountText(control_text, "OpVectorTimesScalar") == 6 &&
|
||||||
|
CountText(control_text, "OpSelect %v4float") == 2,
|
||||||
|
"flat parameters must use guest vertex zero instead of reconstructed values");
|
||||||
|
Require(name, "remapped interface",
|
||||||
|
CountText(control_text, " Location 0") == 2 &&
|
||||||
|
CountText(control_text, " Location 1") == 1 &&
|
||||||
|
CountText(evaluation_text, " Location 0") == 2 &&
|
||||||
|
CountText(evaluation_text, " Location 1") == 2,
|
||||||
|
"duplicate pixel mappings must share one vertex input and keep distinct patch outputs");
|
||||||
|
|
||||||
|
const auto position_only = BuildRectListShaders(vertex, nullptr);
|
||||||
|
ValidateSpirv(name, position_only.control);
|
||||||
|
ValidateSpirv(name, position_only.evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckSpirvText(const TestCase& test, const std::vector<u32>& spirv) {
|
void CheckSpirvText(const TestCase& test, const std::vector<u32>& spirv) {
|
||||||
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
|
if (test.required_spirv.empty() && test.forbidden_spirv.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -2402,6 +2561,110 @@ public:
|
|||||||
vk::Format::eR8G8B8A8Srgb,
|
vk::Format::eR8G8B8A8Srgb,
|
||||||
"registered compatible backing did not reuse one ImageId");
|
"registered compatible backing did not reuse one ImageId");
|
||||||
|
|
||||||
|
const auto IsOnlyImage = [](const std::vector<ImageId>& ids, ImageId expected) {
|
||||||
|
return ids.size() == 1 && ids.front() == expected;
|
||||||
|
};
|
||||||
|
const auto exact_byte_miss =
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache, base + 8, 1, false);
|
||||||
|
const auto touched_page_hit =
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache, base + 8, 1, true);
|
||||||
|
const auto next_page_miss =
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache, base + 0x1000, 1, true);
|
||||||
|
Require(name, "exact and 4-KiB page filtering",
|
||||||
|
exact_byte_miss.empty() && IsOnlyImage(touched_page_hit, first) &&
|
||||||
|
next_page_miss.empty(),
|
||||||
|
"coarse candidates did not preserve exact-byte and touched-page semantics");
|
||||||
|
|
||||||
|
const auto MakeOwnershipInfo = [](uint64_t address, uint64_t size) {
|
||||||
|
ImageInfo info {};
|
||||||
|
info.data = {address, size};
|
||||||
|
info.extent = {1, 1, 1};
|
||||||
|
info.resources = {1, 1};
|
||||||
|
info.samples = 1;
|
||||||
|
return info;
|
||||||
|
};
|
||||||
|
const auto spanning_info = MakeOwnershipInfo(base + 0x1ff000, 0x2000);
|
||||||
|
const auto spanning = TextureCacheTestAccess::InsertImage(texture_cache, spanning_info);
|
||||||
|
const auto spanning_results = TextureCacheTestAccess::FindImages(
|
||||||
|
texture_cache, spanning_info.data.address, spanning_info.data.size, false);
|
||||||
|
Require(name, "one-MiB cross-page deduplication",
|
||||||
|
spanning && IsOnlyImage(spanning_results, spanning) &&
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(texture_cache,
|
||||||
|
spanning_info.data.address) == 1 &&
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(
|
||||||
|
texture_cache, spanning_info.data.End() - 1) == 1,
|
||||||
|
"an image spanning two coarse pages was missing or returned more than once");
|
||||||
|
TextureCacheTestAccess::DeleteImage(texture_cache, spanning);
|
||||||
|
Require(name, "cross-page owner cleanup",
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(texture_cache,
|
||||||
|
spanning_info.data.address) == 0 &&
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(
|
||||||
|
texture_cache, spanning_info.data.End() - 1) == 0 &&
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache,
|
||||||
|
spanning_info.data.address,
|
||||||
|
spanning_info.data.size, false)
|
||||||
|
.empty(),
|
||||||
|
"cross-page unregister left a stale coarse-page membership");
|
||||||
|
|
||||||
|
const auto shared_info = MakeOwnershipInfo(base + 0x500100, 0x100);
|
||||||
|
const auto shared_first =
|
||||||
|
TextureCacheTestAccess::InsertImage(texture_cache, shared_info);
|
||||||
|
const auto shared_second =
|
||||||
|
TextureCacheTestAccess::InsertImage(texture_cache, shared_info);
|
||||||
|
const auto shared_results = TextureCacheTestAccess::FindImages(
|
||||||
|
texture_cache, shared_info.data.address, shared_info.data.size, false);
|
||||||
|
Require(name, "shared coarse-page registration",
|
||||||
|
shared_results.size() == 2 &&
|
||||||
|
std::find(shared_results.begin(), shared_results.end(), shared_first) !=
|
||||||
|
shared_results.end() &&
|
||||||
|
std::find(shared_results.begin(), shared_results.end(), shared_second) !=
|
||||||
|
shared_results.end(),
|
||||||
|
"two registered owners were not retained in one coarse page");
|
||||||
|
TextureCacheTestAccess::DeleteImage(texture_cache, shared_first);
|
||||||
|
const auto shared_survivor = TextureCacheTestAccess::FindImages(
|
||||||
|
texture_cache, shared_info.data.address, shared_info.data.size, false);
|
||||||
|
Require(name, "shared coarse-page unregister",
|
||||||
|
IsOnlyImage(shared_survivor, shared_second) &&
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(texture_cache,
|
||||||
|
shared_info.data.address) == 1,
|
||||||
|
"unregistering one owner removed its coarse-page neighbor");
|
||||||
|
TextureCacheTestAccess::DeleteImage(texture_cache, shared_second);
|
||||||
|
Require(name, "final shared coarse-page unregister",
|
||||||
|
TextureCacheTestAccess::PageOwnerCount(texture_cache,
|
||||||
|
shared_info.data.address) == 0,
|
||||||
|
"the final shared owner remained registered");
|
||||||
|
|
||||||
|
constexpr uint64_t large_owner_size = 64ull * 1024 * 1024;
|
||||||
|
const auto large_info = MakeOwnershipInfo(base + 0x800000, large_owner_size);
|
||||||
|
const auto large_owner =
|
||||||
|
TextureCacheTestAccess::InsertImage(texture_cache, large_info);
|
||||||
|
Require(name, "production one-MiB registration granularity",
|
||||||
|
TextureCacheTestAccess::OwnedPageCount(
|
||||||
|
texture_cache, large_info.data.address, large_info.data.size, large_owner) == 64,
|
||||||
|
"64 MiB image registration did not create exactly 64 coarse memberships");
|
||||||
|
TextureCacheTestAccess::DeleteImage(texture_cache, large_owner);
|
||||||
|
Require(name, "production one-MiB unregister granularity",
|
||||||
|
TextureCacheTestAccess::OwnedPageCount(
|
||||||
|
texture_cache, large_info.data.address, large_info.data.size, large_owner) == 0,
|
||||||
|
"large image unregister left coarse memberships behind");
|
||||||
|
|
||||||
|
const ImageId stale {first.index, first.generation + 1};
|
||||||
|
TextureCacheTestAccess::AddPageOwner(texture_cache, base, stale);
|
||||||
|
const auto stale_filtered =
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache, base, sizeof(initial), false);
|
||||||
|
Require(name, "stale page owner filtering",
|
||||||
|
IsOnlyImage(stale_filtered, first) &&
|
||||||
|
TextureCacheTestAccess::RemovePageOwner(texture_cache, base, stale),
|
||||||
|
"a stale generation escaped the direct page-owner lookup");
|
||||||
|
|
||||||
|
TextureCacheTestAccess::SetQueryEpoch(texture_cache, UINT32_MAX);
|
||||||
|
const auto wrap_results =
|
||||||
|
TextureCacheTestAccess::FindImages(texture_cache, base, sizeof(initial), false);
|
||||||
|
Require(name, "page-owner query epoch wrap",
|
||||||
|
IsOnlyImage(wrap_results, first) &&
|
||||||
|
TextureCacheTestAccess::QueryEpoch(texture_cache) == 1,
|
||||||
|
"query deduplication failed while wrapping its epoch");
|
||||||
|
|
||||||
auto& image = texture_cache.GetImage(first);
|
auto& image = texture_cache.GetImage(first);
|
||||||
constexpr uint32_t final_sampled_value = 0x88776655u;
|
constexpr uint32_t final_sampled_value = 0x88776655u;
|
||||||
Require(name, "sampled write between discovery and acquisition",
|
Require(name, "sampled write between discovery and acquisition",
|
||||||
@@ -7279,26 +7542,30 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 case_index = 0;
|
u32 case_index = 0;
|
||||||
auto check_round_trip = [&](const char* stage, uint64_t size,
|
auto check_round_trip = [&](const char* stage, uint64_t tiled_size,
|
||||||
std::span<const GpuTileInfo> infos) {
|
std::span<const GpuTileInfo> infos) {
|
||||||
std::vector<uint8_t> tiled(size);
|
uint64_t linear_size = 0;
|
||||||
std::vector<uint8_t> cpu(size, 0);
|
for (const auto& info: infos) {
|
||||||
std::vector<uint8_t> gpu(size, 0xab);
|
linear_size = std::max(linear_size, info.linear_offset + info.linear_size);
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> tiled(tiled_size);
|
||||||
|
std::vector<uint8_t> cpu(linear_size, 0);
|
||||||
|
std::vector<uint8_t> gpu(linear_size, 0xab);
|
||||||
fill(&tiled, ++case_index);
|
fill(&tiled, ++case_index);
|
||||||
for (const auto& info: infos) {
|
for (const auto& info: infos) {
|
||||||
convert_reference(false, &cpu, tiled, info);
|
convert_reference(false, &cpu, tiled, info);
|
||||||
}
|
}
|
||||||
gpu_detile(tiled, &gpu, size, size, infos);
|
gpu_detile(tiled, &gpu, tiled_size, linear_size, infos);
|
||||||
compare((std::string(stage) + " detile bytes").c_str(), cpu, gpu);
|
compare((std::string(stage) + " detile bytes").c_str(), cpu, gpu);
|
||||||
|
|
||||||
std::vector<uint8_t> linear(size);
|
std::vector<uint8_t> linear(linear_size);
|
||||||
std::vector<uint8_t> cpu_tiled(size, 0xab);
|
std::vector<uint8_t> cpu_tiled(tiled_size, 0xab);
|
||||||
std::vector<uint8_t> gpu_tiled(size, 0xab);
|
std::vector<uint8_t> gpu_tiled(tiled_size, 0xab);
|
||||||
fill(&linear, 0x280u + case_index);
|
fill(&linear, 0x280u + case_index);
|
||||||
for (const auto& info: infos) {
|
for (const auto& info: infos) {
|
||||||
convert_reference(true, &cpu_tiled, linear, info);
|
convert_reference(true, &cpu_tiled, linear, info);
|
||||||
}
|
}
|
||||||
gpu_tile(linear, &gpu_tiled, size, size, infos);
|
gpu_tile(linear, &gpu_tiled, tiled_size, linear_size, infos);
|
||||||
compare((std::string(stage) + " tile bytes").c_str(), cpu_tiled, gpu_tiled);
|
compare((std::string(stage) + " tile bytes").c_str(), cpu_tiled, gpu_tiled);
|
||||||
};
|
};
|
||||||
for (const auto family: families) {
|
for (const auto family: families) {
|
||||||
@@ -7436,6 +7703,32 @@ public:
|
|||||||
Require(name, "format coverage", format_cases != 0,
|
Require(name, "format coverage", format_cases != 0,
|
||||||
"no CPU-supported standard formats were tested");
|
"no CPU-supported standard formats were tested");
|
||||||
|
|
||||||
|
{
|
||||||
|
constexpr u32 format = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc1UNorm);
|
||||||
|
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
|
||||||
|
constexpr u32 width = 256, height = 256, levels = 9;
|
||||||
|
const u32 pitch = TileGetTexturePitch(format, width, levels, tile);
|
||||||
|
TileSizeAlign total {};
|
||||||
|
TileGetTextureSize(format, width, height, pitch, levels, tile, &total, nullptr,
|
||||||
|
nullptr);
|
||||||
|
const auto layout = TextureCalcUploadLayout(format, width, height, levels, 1, pitch,
|
||||||
|
tile, total.size, false, false, name);
|
||||||
|
const auto regions =
|
||||||
|
TextureBuildImageCopies(layout, width, height, 1, levels, false, false);
|
||||||
|
std::vector<GpuTileInfo> infos;
|
||||||
|
const bool built =
|
||||||
|
TextureBuildGpuTileInfos(total.size, regions, layout, format, 1, levels, infos);
|
||||||
|
uint64_t linear_size = 0;
|
||||||
|
for (const auto& info: infos) {
|
||||||
|
linear_size = std::max(linear_size, info.linear_offset + info.linear_size);
|
||||||
|
}
|
||||||
|
Require(name, "BC1 mip-tail capacities",
|
||||||
|
built && total.size == 0x10000 && layout.first_tail_level == 0 &&
|
||||||
|
linear_size == 0x15560 && linear_size > total.size,
|
||||||
|
"BC1 mip tail conflated tiled and linear capacities");
|
||||||
|
check_round_trip("BC1 mip tail", total.size, infos);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
constexpr u32 format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
constexpr u32 format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||||
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
constexpr u32 tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||||
@@ -15959,11 +16252,6 @@ ShaderTextureResource AtomicStorageTextureDescriptor() {
|
|||||||
resource = BasicArrayStorageTextureResource();
|
resource = BasicArrayStorageTextureResource();
|
||||||
descriptor = BasicArrayStorageTextureDescriptor();
|
descriptor = BasicArrayStorageTextureDescriptor();
|
||||||
descriptor.fields[4] |= 1u << 16u;
|
descriptor.fields[4] |= 1u << 16u;
|
||||||
} else if (std::strcmp(kind, "array-mip-view") == 0) {
|
|
||||||
resource = BasicArrayStorageTextureResource();
|
|
||||||
descriptor = BasicArrayStorageTextureDescriptor();
|
|
||||||
descriptor.fields[3] |= (1u << 12u) | (1u << 16u);
|
|
||||||
descriptor.fields[5] |= 1u << 4u;
|
|
||||||
} else if (std::strcmp(kind, "reserved") == 0) {
|
} else if (std::strcmp(kind, "reserved") == 0) {
|
||||||
descriptor.fields[1] |= 1u << 29u;
|
descriptor.fields[1] |= 1u << 29u;
|
||||||
} else if (std::strcmp(kind, "uint-format") == 0) {
|
} else if (std::strcmp(kind, "uint-format") == 0) {
|
||||||
@@ -16139,6 +16427,16 @@ void CheckBasicStorageTextureDescriptor() {
|
|||||||
array.DstSelXYZW() == DstSel(6, 5, 4, 7),
|
array.DstSelXYZW() == DstSel(6, 5, 4, 7),
|
||||||
"PPSA21268 2D-array storage descriptor fixture is malformed");
|
"PPSA21268 2D-array storage descriptor fixture is malformed");
|
||||||
ValidateStorageTexture(BasicArrayStorageTextureResource(), array, 0x10000);
|
ValidateStorageTexture(BasicArrayStorageTextureResource(), array, 0x10000);
|
||||||
|
const ShaderTextureResource mip_array {{0x20268d00u, 0xc4700000u, 0x001fc01fu,
|
||||||
|
0xd1b11facu, 0x00000000u, 0x00700070u,
|
||||||
|
0x00000000u, 0x00000000u}};
|
||||||
|
Require("BasicStorageTexture", "PPSA14457 mip-one 2D-array descriptor",
|
||||||
|
mip_array.BaseLevel() == 1 && mip_array.LastLevel() == 1 &&
|
||||||
|
mip_array.MaxMip() == 7 &&
|
||||||
|
mip_array.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) &&
|
||||||
|
mip_array.Depth() == 0 && mip_array.BaseArray5() == 0,
|
||||||
|
"PPSA14457 mip-one 2D-array storage descriptor fixture is malformed");
|
||||||
|
ValidateStorageTexture(BasicArrayStorageTextureResource(), mip_array, 0x30000);
|
||||||
|
|
||||||
const auto uint_array = BasicUintArrayStorageTextureDescriptor();
|
const auto uint_array = BasicUintArrayStorageTextureDescriptor();
|
||||||
Require("BasicStorageTexture", "uint 2D-array descriptor",
|
Require("BasicStorageTexture", "uint 2D-array descriptor",
|
||||||
@@ -16299,7 +16597,6 @@ void CheckBasicStorageTextureDescriptor() {
|
|||||||
"yzwx-read",
|
"yzwx-read",
|
||||||
"reserved-swizzle",
|
"reserved-swizzle",
|
||||||
"array-base-out-of-range",
|
"array-base-out-of-range",
|
||||||
"array-mip-view",
|
|
||||||
"reserved",
|
"reserved",
|
||||||
"uint-format",
|
"uint-format",
|
||||||
"uint-resource-float-format",
|
"uint-resource-float-format",
|
||||||
@@ -17468,6 +17765,7 @@ int main(int argc, char** argv) {
|
|||||||
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
CheckPm4CeCompletion(vulkan.RuntimeRenderer());
|
||||||
CheckEmbeddedFetchVertexOffset();
|
CheckEmbeddedFetchVertexOffset();
|
||||||
CheckEmbeddedFetchLaneSpill();
|
CheckEmbeddedFetchLaneSpill();
|
||||||
|
CheckRectListShaders();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
vulkan.CheckSchedulerTimeline();
|
vulkan.CheckSchedulerTimeline();
|
||||||
vulkan.CheckGpuMappedRangeLifecycle();
|
vulkan.CheckGpuMappedRangeLifecycle();
|
||||||
|
|||||||
Reference in New Issue
Block a user