Merge master
This commit is contained in:
commit
9854cf63dc
@ -7,13 +7,6 @@ REV_NAME="citra-${OS}-${TARGET}-${GITDATE}-${GITREV}"
|
|||||||
# Find out what release we are building
|
# Find out what release we are building
|
||||||
if [[ "$GITHUB_REF_NAME" =~ ^canary- ]] || [[ "$GITHUB_REF_NAME" =~ ^nightly- ]]; then
|
if [[ "$GITHUB_REF_NAME" =~ ^canary- ]] || [[ "$GITHUB_REF_NAME" =~ ^nightly- ]]; then
|
||||||
RELEASE_NAME=$(echo $GITHUB_REF_NAME | cut -d- -f1)
|
RELEASE_NAME=$(echo $GITHUB_REF_NAME | cut -d- -f1)
|
||||||
# For compatibility with existing installs, use mingw/osx in the archive and target names.
|
|
||||||
if [ "$TARGET" = "msys2" ]; then
|
|
||||||
REV_NAME="citra-${OS}-mingw-${GITDATE}-${GITREV}"
|
|
||||||
RELEASE_NAME="${RELEASE_NAME}-mingw"
|
|
||||||
elif [ "$OS" = "macos" ]; then
|
|
||||||
REV_NAME="citra-osx-${TARGET}-${GITDATE}-${GITREV}"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
RELEASE_NAME=head
|
RELEASE_NAME=head
|
||||||
fi
|
fi
|
||||||
|
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
@ -161,9 +161,16 @@ jobs:
|
|||||||
- name: Set up MSVC
|
- name: Set up MSVC
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
- name: Install MSVC extra tools
|
- name: Install extra tools (MSVC)
|
||||||
run: choco install ccache ninja wget
|
run: choco install ccache ninja wget
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
|
- name: Set up Vulkan SDK (MSVC)
|
||||||
|
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||||
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
|
with:
|
||||||
|
vulkan-query-version: latest
|
||||||
|
vulkan-components: Glslang
|
||||||
|
vulkan-use-cache: true
|
||||||
- name: Set up MSYS2
|
- name: Set up MSYS2
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
if: ${{ matrix.target == 'msys2' }}
|
if: ${{ matrix.target == 'msys2' }}
|
||||||
@ -172,16 +179,10 @@ jobs:
|
|||||||
update: true
|
update: true
|
||||||
install: git make p7zip
|
install: git make p7zip
|
||||||
pacboy: >-
|
pacboy: >-
|
||||||
toolchain:p ccache:p cmake:p ninja:p
|
toolchain:p ccache:p cmake:p ninja:p glslang:p
|
||||||
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
|
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
|
||||||
- name: Setup Vulkan SDK
|
- name: Test glslang
|
||||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
run: glslang --version || glslangValidator --version
|
||||||
with:
|
|
||||||
vulkan-query-version: latest
|
|
||||||
vulkan-components: Glslang
|
|
||||||
vulkan-use-cache: true
|
|
||||||
- name: Test glslangValidator
|
|
||||||
run: glslangValidator --version
|
|
||||||
- name: Disable line ending translation
|
- name: Disable line ending translation
|
||||||
run: git config --global core.autocrlf input
|
run: git config --global core.autocrlf input
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -57,6 +57,7 @@ CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT
|
|||||||
CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
|
||||||
|
|
||||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||||
|
option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
|
||||||
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
|
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
|
||||||
@ -68,6 +69,8 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
|||||||
CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_AUDIOTOOLBOX "Use AudioToolbox decoder (preferred over FFmpeg)" ON "APPLE" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_AUDIOTOOLBOX "Use AudioToolbox decoder (preferred over FFmpeg)" ON "APPLE" OFF)
|
||||||
|
|
||||||
|
CMAKE_DEPENDENT_OPTION(CITRA_ENABLE_BUNDLE_TARGET "Enable the distribution bundling target." ON "NOT ANDROID AND NOT IOS" OFF)
|
||||||
|
|
||||||
# Compile options
|
# Compile options
|
||||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
||||||
option(ENABLE_LTO "Enable link time optimization" OFF)
|
option(ENABLE_LTO "Enable link time optimization" OFF)
|
||||||
@ -219,7 +222,7 @@ find_package(Threads REQUIRED)
|
|||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
if (NOT USE_SYSTEM_QT)
|
if (NOT USE_SYSTEM_QT)
|
||||||
download_qt(6.5.0)
|
download_qt(6.5.1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
||||||
@ -341,13 +344,6 @@ function(get_timestamp _var)
|
|||||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Prevent boost from linking against libs when building
|
|
||||||
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
|
|
||||||
-DBOOST_SYSTEM_NO_LIB
|
|
||||||
-DBOOST_DATE_TIME_NO_LIB
|
|
||||||
-DBOOST_REGEX_NO_LIB
|
|
||||||
)
|
|
||||||
|
|
||||||
# generate git/build information
|
# generate git/build information
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||||
@ -355,17 +351,23 @@ git_describe(GIT_DESC --always --long --dirty)
|
|||||||
git_branch_name(GIT_BRANCH)
|
git_branch_name(GIT_BRANCH)
|
||||||
get_timestamp(BUILD_DATE)
|
get_timestamp(BUILD_DATE)
|
||||||
|
|
||||||
if (NOT USE_SYSTEM_BOOST)
|
# Boost
|
||||||
add_definitions( -DBOOST_ALL_NO_LIB )
|
# Prevent boost from linking against libs when building
|
||||||
|
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
|
||||||
|
-DBOOST_SYSTEM_NO_LIB
|
||||||
|
-DBOOST_DATE_TIME_NO_LIB
|
||||||
|
-DBOOST_REGEX_NO_LIB
|
||||||
|
)
|
||||||
|
if (USE_SYSTEM_BOOST)
|
||||||
|
find_package(Boost 1.70.0 COMPONENTS container locale serialization iostreams REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
# Boost
|
# Boost (bundled)
|
||||||
if (USE_SYSTEM_BOOST)
|
if (NOT USE_SYSTEM_BOOST)
|
||||||
find_package(Boost 1.70.0 COMPONENTS serialization iostreams REQUIRED)
|
add_definitions( -DBOOST_ALL_NO_LIB )
|
||||||
else()
|
|
||||||
add_library(Boost::boost ALIAS boost)
|
add_library(Boost::boost ALIAS boost)
|
||||||
add_library(Boost::serialization ALIAS boost_serialization)
|
add_library(Boost::serialization ALIAS boost_serialization)
|
||||||
add_library(Boost::iostreams ALIAS boost_iostreams)
|
add_library(Boost::iostreams ALIAS boost_iostreams)
|
||||||
@ -397,7 +399,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Create target for outputting distributable bundles.
|
# Create target for outputting distributable bundles.
|
||||||
if (NOT ANDROID AND NOT IOS)
|
if (CITRA_ENABLE_BUNDLE_TARGET)
|
||||||
include(BundleTarget)
|
include(BundleTarget)
|
||||||
if (ENABLE_SDL2_FRONTEND)
|
if (ENABLE_SDL2_FRONTEND)
|
||||||
bundle_target(citra)
|
bundle_target(citra)
|
||||||
|
@ -189,6 +189,12 @@ else()
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET bundle
|
TARGET bundle
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/")
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET bundle
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/dist/")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET bundle
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dist/icon.png" "${CMAKE_BINARY_DIR}/bundle/dist/citra.png")
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET bundle
|
TARGET bundle
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/license.txt" "${CMAKE_BINARY_DIR}/bundle/")
|
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/license.txt" "${CMAKE_BINARY_DIR}/bundle/")
|
||||||
|
@ -54,7 +54,8 @@ function(download_qt target)
|
|||||||
set(host_flag "--autodesktop")
|
set(host_flag "--autodesktop")
|
||||||
set(host_prefix "${base_path}/${target}/${host_arch_path}")
|
set(host_prefix "${base_path}/${target}/${host_arch_path}")
|
||||||
endif()
|
endif()
|
||||||
set(install_args install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag} -m qtmultimedia)
|
set(install_args install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag}
|
||||||
|
-m qtmultimedia --archives qttranslations qttools qtsvg qtbase)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT EXISTS "${prefix}")
|
if (NOT EXISTS "${prefix}")
|
||||||
|
@ -12,8 +12,16 @@ set(HASH_FILES
|
|||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
||||||
"${VIDEO_CORE}/shader/shader.cpp"
|
"${VIDEO_CORE}/shader/shader.cpp"
|
||||||
"${VIDEO_CORE}/shader/shader.h"
|
"${VIDEO_CORE}/shader/shader.h"
|
||||||
|
"${VIDEO_CORE}/shader/shader_uniforms.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/shader_uniforms.h"
|
||||||
"${VIDEO_CORE}/pica.cpp"
|
"${VIDEO_CORE}/pica.cpp"
|
||||||
"${VIDEO_CORE}/pica.h"
|
"${VIDEO_CORE}/pica.h"
|
||||||
"${VIDEO_CORE}/regs_framebuffer.h"
|
"${VIDEO_CORE}/regs_framebuffer.h"
|
||||||
|
5
dist/qt_themes/qdarkstyle/style.qss
vendored
5
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -298,6 +298,11 @@ QAbstractItemView:read-only {
|
|||||||
alternate-background-color: #232629;
|
alternate-background-color: #232629;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
|
||||||
|
QAbstractItemView:item {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget:focus {
|
QWidget:focus {
|
||||||
border: 1px solid #3daee9;
|
border: 1px solid #3daee9;
|
||||||
}
|
}
|
||||||
|
@ -481,6 +481,11 @@ QAbstractItemView QLineEdit {
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
|
||||||
|
QAbstractItemView:item {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
/* QAbstractScrollArea ----------------------------------------------------
|
/* QAbstractScrollArea ----------------------------------------------------
|
||||||
|
|
||||||
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea
|
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea
|
||||||
|
17
externals/CMakeLists.txt
vendored
17
externals/CMakeLists.txt
vendored
@ -12,6 +12,8 @@ include(DownloadExternals)
|
|||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
|
if (NOT USE_SYSTEM_BOOST)
|
||||||
|
message(STATUS "Including vendored Boost library")
|
||||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||||
@ -31,8 +33,8 @@ add_library(
|
|||||||
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
|
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(boost_iostreams PUBLIC boost)
|
target_link_libraries(boost_iostreams PUBLIC boost)
|
||||||
|
|
||||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||||
|
endif()
|
||||||
|
|
||||||
# Catch2
|
# Catch2
|
||||||
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
||||||
@ -95,6 +97,7 @@ set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "")
|
|||||||
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
|
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
|
||||||
set(ENABLE_CTEST OFF CACHE BOOL "")
|
set(ENABLE_CTEST OFF CACHE BOOL "")
|
||||||
set(ENABLE_HLSL OFF CACHE BOOL "")
|
set(ENABLE_HLSL OFF CACHE BOOL "")
|
||||||
|
set(BUILD_EXTERNAL OFF CACHE BOOL "")
|
||||||
add_subdirectory(glslang)
|
add_subdirectory(glslang)
|
||||||
|
|
||||||
# inih
|
# inih
|
||||||
@ -171,7 +174,7 @@ endif()
|
|||||||
add_library(json-headers INTERFACE)
|
add_library(json-headers INTERFACE)
|
||||||
target_include_directories(json-headers INTERFACE ./json)
|
target_include_directories(json-headers INTERFACE ./json)
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
# OpenSSL
|
||||||
if (USE_SYSTEM_OPENSSL)
|
if (USE_SYSTEM_OPENSSL)
|
||||||
find_package(OpenSSL 1.1)
|
find_package(OpenSSL 1.1)
|
||||||
if (OPENSSL_FOUND)
|
if (OPENSSL_FOUND)
|
||||||
@ -191,17 +194,19 @@ if (ENABLE_WEB_SERVICE)
|
|||||||
DEFINITION OPENSSL_LIBS)
|
DEFINITION OPENSSL_LIBS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
|
||||||
add_subdirectory(android-ifaddrs)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# httplib
|
# httplib
|
||||||
add_library(httplib INTERFACE)
|
add_library(httplib INTERFACE)
|
||||||
target_include_directories(httplib INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT -DCPPHTTPLIB_NO_DEFAULT_USER_AGENT)
|
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT -DCPPHTTPLIB_NO_DEFAULT_USER_AGENT)
|
||||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
add_subdirectory(android-ifaddrs)
|
||||||
|
target_link_libraries(httplib INTERFACE ifaddrs)
|
||||||
|
endif()
|
||||||
|
|
||||||
# cpp-jwt
|
# cpp-jwt
|
||||||
|
if (ENABLE_WEB_SERVICE)
|
||||||
add_library(cpp-jwt INTERFACE)
|
add_library(cpp-jwt INTERFACE)
|
||||||
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
|
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
|
||||||
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
||||||
|
2
externals/boost
vendored
2
externals/boost
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 700ae2eff3134792f09cea2b051666688b1d5b97
|
Subproject commit 3c27c785ad0f8a742af02e620dc225673f3a12d8
|
@ -47,7 +47,7 @@ add_library(audio_core STATIC
|
|||||||
|
|
||||||
create_target_directory_groups(audio_core)
|
create_target_directory_groups(audio_core)
|
||||||
|
|
||||||
target_link_libraries(audio_core PUBLIC citra_common)
|
target_link_libraries(audio_core PUBLIC citra_common citra_core)
|
||||||
target_link_libraries(audio_core PRIVATE SoundTouch teakra)
|
target_link_libraries(audio_core PRIVATE SoundTouch teakra)
|
||||||
set_target_properties(audio_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
set_target_properties(audio_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||||
add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES)
|
add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES)
|
||||||
|
@ -342,7 +342,7 @@ if (USE_DISCORD_PRESENCE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(citra-qt PRIVATE -DENABLE_WEB_SERVICE)
|
target_link_libraries(citra-qt PRIVATE web_service)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
|
@ -13,6 +13,7 @@ ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->toggle_vsync_new->setEnabled(!is_powered_on);
|
ui->toggle_vsync_new->setEnabled(!is_powered_on);
|
||||||
|
ui->graphics_api_combo->setEnabled(!is_powered_on);
|
||||||
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
|
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex
|
||||||
ui->graphics_api_combo->setCurrentIndex(-1);
|
ui->graphics_api_combo->setCurrentIndex(-1);
|
||||||
|
|
||||||
@ -28,9 +29,10 @@ ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
|
|||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
|
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
|
||||||
|
const bool enabled = ui->toggle_hw_shader->isEnabled();
|
||||||
const bool checked = ui->toggle_hw_shader->isChecked();
|
const bool checked = ui->toggle_hw_shader->isChecked();
|
||||||
ui->hw_shader_group->setEnabled(checked);
|
ui->hw_shader_group->setEnabled(checked && enabled);
|
||||||
ui->toggle_disk_shader_cache->setEnabled(checked);
|
ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>ConfigureInput</string>
|
<string>ConfigureInput</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
<layout class="QVBoxLayout" name="rootLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
@ -62,6 +62,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaContents">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_7">
|
<layout class="QGridLayout" name="gridLayout_7">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
@ -712,6 +719,10 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
<item>
|
<item>
|
||||||
|
@ -551,7 +551,6 @@ void ConfigureSystem::SetupPerGameUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSystem::DownloadFromNUS() {
|
void ConfigureSystem::DownloadFromNUS() {
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
|
||||||
ui->button_start_download->setEnabled(false);
|
ui->button_start_download->setEnabled(false);
|
||||||
|
|
||||||
const auto mode =
|
const auto mode =
|
||||||
@ -590,5 +589,4 @@ void ConfigureSystem::DownloadFromNUS() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui->button_start_download->setEnabled(true);
|
ui->button_start_download->setEnabled(true);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -64,15 +64,22 @@ void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_co
|
|||||||
}
|
}
|
||||||
|
|
||||||
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
|
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
|
||||||
: QDockWidget(tr("Graphics Debugger"), parent) {
|
: QDockWidget(tr("Graphics Debugger"), parent), model(this) {
|
||||||
setObjectName(QStringLiteral("GraphicsDebugger"));
|
setObjectName(QStringLiteral("GraphicsDebugger"));
|
||||||
|
|
||||||
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);
|
auto* command_list = new QListView;
|
||||||
g_debugger.RegisterObserver(command_model);
|
command_list->setModel(&model);
|
||||||
|
|
||||||
QListView* command_list = new QListView;
|
|
||||||
command_list->setModel(command_model);
|
|
||||||
command_list->setFont(GetMonospaceFont());
|
command_list->setFont(GetMonospaceFont());
|
||||||
|
|
||||||
setWidget(command_list);
|
setWidget(command_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPUCommandStreamWidget::showEvent(QShowEvent* event) {
|
||||||
|
g_debugger.RegisterObserver(&model);
|
||||||
|
QDockWidget::showEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPUCommandStreamWidget::hideEvent(QHideEvent* event) {
|
||||||
|
g_debugger.UnregisterObserver(&model);
|
||||||
|
QDockWidget::hideEvent(event);
|
||||||
|
}
|
||||||
|
@ -37,5 +37,10 @@ class GPUCommandStreamWidget : public QDockWidget {
|
|||||||
public:
|
public:
|
||||||
GPUCommandStreamWidget(QWidget* parent = nullptr);
|
GPUCommandStreamWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent* event) override;
|
||||||
|
void hideEvent(QHideEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GPUCommandStreamItemModel model;
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "citra_qt/debugger/graphics/graphics_cmdlists.h"
|
#include "citra_qt/debugger/graphics/graphics_cmdlists.h"
|
||||||
#include "citra_qt/util/util.h"
|
#include "citra_qt/util/util.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
#include "video_core/pica_state.h"
|
#include "video_core/pica_state.h"
|
||||||
@ -166,7 +167,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
|||||||
const auto format = texture.format;
|
const auto format = texture.format;
|
||||||
|
|
||||||
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
|
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
|
||||||
const u8* src = memory.GetPhysicalPointer(config.GetPhysicalAddress());
|
const u8* src = system.Memory().GetPhysicalPointer(config.GetPhysicalAddress());
|
||||||
new_info_widget = new TextureInfoWidget(src, info);
|
new_info_widget = new TextureInfoWidget(src, info);
|
||||||
}
|
}
|
||||||
if (command_info_widget) {
|
if (command_info_widget) {
|
||||||
@ -180,8 +181,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
|||||||
}
|
}
|
||||||
#undef COMMAND_IN_RANGE
|
#undef COMMAND_IN_RANGE
|
||||||
|
|
||||||
GPUCommandListWidget::GPUCommandListWidget(Memory::MemorySystem& memory_, QWidget* parent)
|
GPUCommandListWidget::GPUCommandListWidget(Core::System& system_, QWidget* parent)
|
||||||
: QDockWidget(tr("Pica Command List"), parent), memory{memory_} {
|
: QDockWidget(tr("Pica Command List"), parent), system{system_} {
|
||||||
setObjectName(QStringLiteral("Pica Command List"));
|
setObjectName(QStringLiteral("Pica Command List"));
|
||||||
GPUCommandListModel* model = new GPUCommandListModel(this);
|
GPUCommandListModel* model = new GPUCommandListModel(this);
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QTreeView;
|
class QTreeView;
|
||||||
|
|
||||||
namespace Memory {
|
namespace Core {
|
||||||
class MemorySystem;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GPUCommandListModel : public QAbstractListModel {
|
class GPUCommandListModel : public QAbstractListModel {
|
||||||
@ -42,7 +42,7 @@ class GPUCommandListWidget : public QDockWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GPUCommandListWidget(Memory::MemorySystem& memory, QWidget* parent = nullptr);
|
explicit GPUCommandListWidget(Core::System& system, QWidget* parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnToggleTracing();
|
void OnToggleTracing();
|
||||||
@ -57,7 +57,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
|
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
|
||||||
Memory::MemorySystem& memory;
|
Core::System& system;
|
||||||
QTreeView* list_widget;
|
QTreeView* list_widget;
|
||||||
QWidget* command_info_widget;
|
QWidget* command_info_widget;
|
||||||
QPushButton* toggle_tracing;
|
QPushButton* toggle_tracing;
|
||||||
|
@ -448,7 +448,7 @@ void GMainWindow::InitializeDebugWidgets() {
|
|||||||
graphicsWidget->hide();
|
graphicsWidget->hide();
|
||||||
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
||||||
|
|
||||||
graphicsCommandsWidget = new GPUCommandListWidget(system.Memory(), this);
|
graphicsCommandsWidget = new GPUCommandListWidget(system, this);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
||||||
graphicsCommandsWidget->hide();
|
graphicsCommandsWidget->hide();
|
||||||
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
||||||
@ -1071,8 +1071,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
|||||||
ShutdownGame();
|
ShutdownGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
render_window->InitRenderTarget();
|
if (!render_window->InitRenderTarget() || !secondary_window->InitRenderTarget()) {
|
||||||
secondary_window->InitRenderTarget();
|
LOG_CRITICAL(Frontend, "Failed to initialize render targets!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const auto scope = render_window->Acquire();
|
const auto scope = render_window->Acquire();
|
||||||
|
|
||||||
@ -1420,10 +1422,17 @@ void GMainWindow::UpdateSaveStates() {
|
|||||||
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
|
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||||
}
|
}
|
||||||
for (const auto& savestate : savestates) {
|
for (const auto& savestate : savestates) {
|
||||||
const auto text = tr("Slot %1 - %2")
|
const bool display_name =
|
||||||
|
savestate.status == Core::SaveStateInfo::ValidationStatus::RevisionDismatch &&
|
||||||
|
!savestate.build_name.empty();
|
||||||
|
const auto text =
|
||||||
|
tr("Slot %1 - %2 %3")
|
||||||
.arg(savestate.slot)
|
.arg(savestate.slot)
|
||||||
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
|
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
|
||||||
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")));
|
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")))
|
||||||
|
.arg(display_name ? QString::fromStdString(savestate.build_name) : QLatin1String())
|
||||||
|
.trimmed();
|
||||||
|
|
||||||
actions_load_state[savestate.slot - 1]->setEnabled(true);
|
actions_load_state[savestate.slot - 1]->setEnabled(true);
|
||||||
actions_load_state[savestate.slot - 1]->setText(text);
|
actions_load_state[savestate.slot - 1]->setText(text);
|
||||||
actions_save_state[savestate.slot - 1]->setText(text);
|
actions_save_state[savestate.slot - 1]->setText(text);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -324,31 +325,32 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
|||||||
return AndroidStorage::CopyFile(srcFilename, std::string(GetParentPath(destFilename)),
|
return AndroidStorage::CopyFile(srcFilename, std::string(GetParentPath(destFilename)),
|
||||||
std::string(GetFilename(destFilename)));
|
std::string(GetFilename(destFilename)));
|
||||||
#else
|
#else
|
||||||
using CFilePointer = std::unique_ptr<FILE, decltype(&std::fclose)>;
|
|
||||||
|
|
||||||
// Open input file
|
// Open input file
|
||||||
CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose};
|
FILE* input = fopen(srcFilename.c_str(), "rb");
|
||||||
if (!input) {
|
if (!input) {
|
||||||
LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
|
LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
|
||||||
destFilename, GetLastErrorMsg());
|
destFilename, GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
SCOPE_EXIT({ fclose(input); });
|
||||||
|
|
||||||
// open output file
|
// open output file
|
||||||
CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose};
|
FILE* output = fopen(destFilename.c_str(), "wb");
|
||||||
if (!output) {
|
if (!output) {
|
||||||
LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
|
LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
|
||||||
destFilename, GetLastErrorMsg());
|
destFilename, GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
SCOPE_EXIT({ fclose(output); });
|
||||||
|
|
||||||
// copy loop
|
// copy loop
|
||||||
std::array<char, 1024> buffer;
|
std::array<char, 1024> buffer;
|
||||||
while (!feof(input.get())) {
|
while (!feof(input)) {
|
||||||
// read input
|
// read input
|
||||||
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input);
|
||||||
if (rnum != buffer.size()) {
|
if (rnum != buffer.size()) {
|
||||||
if (ferror(input.get()) != 0) {
|
if (ferror(input) != 0) {
|
||||||
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
|
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
|
||||||
srcFilename, destFilename, GetLastErrorMsg());
|
srcFilename, destFilename, GetLastErrorMsg());
|
||||||
return false;
|
return false;
|
||||||
@ -356,7 +358,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write output
|
// write output
|
||||||
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output);
|
||||||
if (wnum != rnum) {
|
if (wnum != rnum) {
|
||||||
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
|
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
|
||||||
destFilename, GetLastErrorMsg());
|
destFilename, GetLastErrorMsg());
|
||||||
|
@ -458,19 +458,14 @@ add_library(citra_core STATIC
|
|||||||
mmio.h
|
mmio.h
|
||||||
movie.cpp
|
movie.cpp
|
||||||
movie.h
|
movie.h
|
||||||
|
nus_download.cpp
|
||||||
|
nus_download.h
|
||||||
perf_stats.cpp
|
perf_stats.cpp
|
||||||
perf_stats.h
|
perf_stats.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
rpc/packet.cpp
|
|
||||||
rpc/packet.h
|
|
||||||
rpc/rpc_server.cpp
|
|
||||||
rpc/rpc_server.h
|
|
||||||
rpc/server.cpp
|
|
||||||
rpc/server.h
|
|
||||||
rpc/udp_server.cpp
|
|
||||||
rpc/udp_server.h
|
|
||||||
savestate.cpp
|
savestate.cpp
|
||||||
savestate.h
|
savestate.h
|
||||||
|
savestate_data.h
|
||||||
system_titles.cpp
|
system_titles.cpp
|
||||||
system_titles.h
|
system_titles.h
|
||||||
telemetry_session.cpp
|
telemetry_session.cpp
|
||||||
@ -483,16 +478,26 @@ add_library(citra_core STATIC
|
|||||||
create_target_directory_groups(citra_core)
|
create_target_directory_groups(citra_core)
|
||||||
|
|
||||||
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
|
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
|
||||||
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams)
|
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib)
|
||||||
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives)
|
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives)
|
||||||
set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(citra_core PRIVATE -DENABLE_WEB_SERVICE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_link_libraries(citra_core PRIVATE web_service)
|
||||||
target_link_libraries(citra_core PRIVATE web_service ${OPENSSL_LIBS} httplib)
|
|
||||||
if (ANDROID)
|
|
||||||
target_link_libraries(citra_core PRIVATE ifaddrs)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ENABLE_SCRIPTING)
|
||||||
|
target_compile_definitions(citra_core PUBLIC -DENABLE_SCRIPTING)
|
||||||
|
target_sources(citra_core PRIVATE
|
||||||
|
rpc/packet.cpp
|
||||||
|
rpc/packet.h
|
||||||
|
rpc/rpc_server.cpp
|
||||||
|
rpc/rpc_server.h
|
||||||
|
rpc/server.cpp
|
||||||
|
rpc/server.h
|
||||||
|
rpc/udp_server.cpp
|
||||||
|
rpc/udp_server.h
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE)
|
if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE)
|
||||||
|
@ -45,7 +45,9 @@
|
|||||||
#include "core/hw/lcd.h"
|
#include "core/hw/lcd.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/movie.h"
|
#include "core/movie.h"
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
#include "core/rpc/server.h"
|
#include "core/rpc/server.h"
|
||||||
|
#endif
|
||||||
#include "core/telemetry_session.h"
|
#include "core/telemetry_session.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include "video_core/custom_textures/custom_tex_manager.h"
|
#include "video_core/custom_textures/custom_tex_manager.h"
|
||||||
@ -418,7 +420,9 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
|||||||
|
|
||||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||||
|
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
rpc_server = std::make_unique<RPC::Server>(*this);
|
rpc_server = std::make_unique<RPC::Server>(*this);
|
||||||
|
#endif
|
||||||
|
|
||||||
service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
|
service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
|
||||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
||||||
@ -555,7 +559,9 @@ void System::Shutdown(bool is_deserializing) {
|
|||||||
}
|
}
|
||||||
custom_tex_manager.reset();
|
custom_tex_manager.reset();
|
||||||
telemetry_session.reset();
|
telemetry_session.reset();
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
rpc_server.reset();
|
rpc_server.reset();
|
||||||
|
#endif
|
||||||
archive_manager.reset();
|
archive_manager.reset();
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
dsp_core.reset();
|
dsp_core.reset();
|
||||||
|
@ -405,8 +405,10 @@ private:
|
|||||||
/// Image interface
|
/// Image interface
|
||||||
std::shared_ptr<Frontend::ImageInterface> registered_image_interface;
|
std::shared_ptr<Frontend::ImageInterface> registered_image_interface;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
/// RPC Server for scripting support
|
/// RPC Server for scripting support
|
||||||
std::unique_ptr<RPC::Server> rpc_server;
|
std::unique_ptr<RPC::Server> rpc_server;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
|
std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
|
||||||
|
|
||||||
|
@ -151,6 +151,17 @@ void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx)
|
|||||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetConnectingProxyEnable(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
constexpr bool proxy_enabled = false;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(proxy_enabled);
|
||||||
|
|
||||||
|
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u32 unk = rp.Pop<u32>();
|
u32 unk = rp.Pop<u32>();
|
||||||
|
@ -120,6 +120,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
|
void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AC::GetConnectingProxyEnable service function
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : bool, is proxy enabled
|
||||||
|
*/
|
||||||
|
void GetConnectingProxyEnable(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AC::IsConnected service function
|
* AC::IsConnected service function
|
||||||
* Outputs:
|
* Outputs:
|
||||||
|
@ -27,6 +27,7 @@ AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i"
|
|||||||
{0x0027, &AC_I::GetInfraPriority, "GetInfraPriority"},
|
{0x0027, &AC_I::GetInfraPriority, "GetInfraPriority"},
|
||||||
{0x002D, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
{0x002D, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||||
{0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
{0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||||
|
{0x0036, &AC_I::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
|
||||||
{0x003C, nullptr, "GetAPSSIDList"},
|
{0x003C, nullptr, "GetAPSSIDList"},
|
||||||
{0x003E, &AC_I::IsConnected, "IsConnected"},
|
{0x003E, &AC_I::IsConnected, "IsConnected"},
|
||||||
{0x0040, &AC_I::SetClientVersion, "SetClientVersion"},
|
{0x0040, &AC_I::SetClientVersion, "SetClientVersion"},
|
||||||
|
@ -27,6 +27,7 @@ AC_U::AC_U(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:u"
|
|||||||
{0x0027, &AC_U::GetInfraPriority, "GetInfraPriority"},
|
{0x0027, &AC_U::GetInfraPriority, "GetInfraPriority"},
|
||||||
{0x002D, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
{0x002D, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||||
{0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
{0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||||
|
{0x0036, &AC_U::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
|
||||||
{0x003C, nullptr, "GetAPSSIDList"},
|
{0x003C, nullptr, "GetAPSSIDList"},
|
||||||
{0x003E, &AC_U::IsConnected, "IsConnected"},
|
{0x003E, &AC_U::IsConnected, "IsConnected"},
|
||||||
{0x0040, &AC_U::SetClientVersion, "SetClientVersion"},
|
{0x0040, &AC_U::SetClientVersion, "SetClientVersion"},
|
||||||
|
@ -31,9 +31,7 @@
|
|||||||
#include "core/hle/service/fs/fs_user.h"
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/loader/smdh.h"
|
#include "core/loader/smdh.h"
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
#include "core/nus_download.h"
|
||||||
#include "web_service/nus_download.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
@ -463,7 +461,6 @@ InstallStatus InstallCIA(const std::string& path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
InstallStatus InstallFromNus(u64 title_id, int version) {
|
InstallStatus InstallFromNus(u64 title_id, int version) {
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
|
||||||
LOG_DEBUG(Service_AM, "Downloading {:X}", title_id);
|
LOG_DEBUG(Service_AM, "Downloading {:X}", title_id);
|
||||||
|
|
||||||
CIAFile install_file{GetTitleMediaType(title_id)};
|
CIAFile install_file{GetTitleMediaType(title_id)};
|
||||||
@ -472,7 +469,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
|
|||||||
if (version != -1) {
|
if (version != -1) {
|
||||||
path += fmt::format(".{}", version);
|
path += fmt::format(".{}", version);
|
||||||
}
|
}
|
||||||
auto tmd_response = WebService::NUS::Download(path);
|
auto tmd_response = Core::NUS::Download(path);
|
||||||
if (!tmd_response) {
|
if (!tmd_response) {
|
||||||
LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id);
|
LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id);
|
||||||
return InstallStatus::ErrorFileNotFound;
|
return InstallStatus::ErrorFileNotFound;
|
||||||
@ -481,7 +478,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
|
|||||||
tmd.Load(*tmd_response);
|
tmd.Load(*tmd_response);
|
||||||
|
|
||||||
path = fmt::format("/ccs/download/{:016X}/cetk", title_id);
|
path = fmt::format("/ccs/download/{:016X}/cetk", title_id);
|
||||||
auto cetk_response = WebService::NUS::Download(path);
|
auto cetk_response = Core::NUS::Download(path);
|
||||||
if (!cetk_response) {
|
if (!cetk_response) {
|
||||||
LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id);
|
LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id);
|
||||||
return InstallStatus::ErrorFileNotFound;
|
return InstallStatus::ErrorFileNotFound;
|
||||||
@ -492,7 +489,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
|
|||||||
for (std::size_t i = 0; i < content_count; ++i) {
|
for (std::size_t i = 0; i < content_count; ++i) {
|
||||||
const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i));
|
const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i));
|
||||||
path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename);
|
path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename);
|
||||||
const auto temp_response = WebService::NUS::Download(path);
|
const auto temp_response = Core::NUS::Download(path);
|
||||||
if (!temp_response) {
|
if (!temp_response) {
|
||||||
LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id);
|
LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id);
|
||||||
return InstallStatus::ErrorFileNotFound;
|
return InstallStatus::ErrorFileNotFound;
|
||||||
@ -550,9 +547,6 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return InstallStatus::Success;
|
return InstallStatus::Success;
|
||||||
#else
|
|
||||||
return InstallStatus::ErrorFileNotFound;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetTitleUpdateId(u64 title_id) {
|
u64 GetTitleUpdateId(u64 title_id) {
|
||||||
|
@ -620,9 +620,9 @@ void Module::Interface::SetData(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
const u32 dest_buffer_size = rp.Pop<u32>();
|
const auto dest_buffer_size = rp.Pop<u32>();
|
||||||
const CecSystemInfoType info_type = rp.PopEnum<CecSystemInfoType>();
|
const auto info_type = rp.PopEnum<CecSystemInfoType>();
|
||||||
const u32 param_buffer_size = rp.Pop<u32>();
|
const auto param_buffer_size = rp.Pop<u32>();
|
||||||
auto& param_buffer = rp.PopMappedBuffer();
|
auto& param_buffer = rp.PopMappedBuffer();
|
||||||
auto& dest_buffer = rp.PopMappedBuffer();
|
auto& dest_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
@ -631,22 +631,23 @@ void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) {
|
|||||||
std::vector<u8> buffer;
|
std::vector<u8> buffer;
|
||||||
switch (info_type) {
|
switch (info_type) {
|
||||||
case CecSystemInfoType::EulaVersion: {
|
case CecSystemInfoType::EulaVersion: {
|
||||||
auto cfg = Service::CFG::GetModule(cecd->system);
|
const auto cfg = Service::CFG::GetModule(cecd->system);
|
||||||
Service::CFG::EULAVersion version = cfg->GetEULAVersion();
|
const auto version = cfg->GetEULAVersion();
|
||||||
dest_buffer.Write(&version, 0, sizeof(version));
|
buffer = {version.minor, version.major};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CecSystemInfoType::Eula:
|
case CecSystemInfoType::Eula:
|
||||||
buffer = {0x01}; // Eula agreed
|
buffer = {true}; // Eula agreed
|
||||||
dest_buffer.Write(buffer.data(), 0, buffer.size());
|
|
||||||
break;
|
break;
|
||||||
case CecSystemInfoType::ParentControl:
|
case CecSystemInfoType::ParentControl:
|
||||||
buffer = {0x00}; // No parent control
|
buffer = {false}; // No parent control
|
||||||
dest_buffer.Write(buffer.data(), 0, buffer.size());
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_CECD, "Unknown system info type={:#x}", info_type);
|
LOG_ERROR(Service_CECD, "Unknown system info type={:#x}", info_type);
|
||||||
|
buffer = {};
|
||||||
}
|
}
|
||||||
|
dest_buffer.Write(buffer.data(), 0,
|
||||||
|
std::min(static_cast<size_t>(dest_buffer_size), buffer.size()));
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushMappedBuffer(param_buffer);
|
rb.PushMappedBuffer(param_buffer);
|
||||||
|
@ -314,6 +314,7 @@ void HTTP_C::ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout) {
|
|||||||
} else {
|
} else {
|
||||||
LOG_WARNING(Service_HTTP, "(STUBBED) called");
|
LOG_WARNING(Service_HTTP, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
[[maybe_unused]] Kernel::MappedBuffer& buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
Kernel::MappedBuffer& buffer = rp.PopMappedBuffer();
|
Kernel::MappedBuffer& buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
@ -17,12 +17,10 @@
|
|||||||
#include <boost/serialization/unordered_map.hpp>
|
#include <boost/serialization/unordered_map.hpp>
|
||||||
#include <boost/serialization/vector.hpp>
|
#include <boost/serialization/vector.hpp>
|
||||||
#include <boost/serialization/weak_ptr.hpp>
|
#include <boost/serialization/weak_ptr.hpp>
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#endif
|
#endif
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#endif
|
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
@ -214,7 +212,6 @@ public:
|
|||||||
bool uses_default_client_cert{};
|
bool uses_default_client_cert{};
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
httplib::Response response;
|
httplib::Response response;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
||||||
|
@ -316,7 +316,7 @@ void Module::Interface::GetTagInfo2(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
if (nfc->nfc_mode == CommunicationMode::TrainTag) {
|
if (nfc->nfc_mode == CommunicationMode::TrainTag) {
|
||||||
LOG_ERROR(Service_NFC, "CommunicationMode {} not implemented", nfc->nfc_mode);
|
LOG_ERROR(Service_NFC, "CommunicationMode {} not implemented", nfc->nfc_mode);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(26, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(25, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushRaw<TagInfo2>({});
|
rb.PushRaw<TagInfo2>({});
|
||||||
return;
|
return;
|
||||||
@ -324,7 +324,7 @@ void Module::Interface::GetTagInfo2(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
TagInfo2 tag_info{};
|
TagInfo2 tag_info{};
|
||||||
const auto result = nfc->device->GetTagInfo2(tag_info);
|
const auto result = nfc->device->GetTagInfo2(tag_info);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(26, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(25, 0);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.PushRaw<TagInfo2>(tag_info);
|
rb.PushRaw<TagInfo2>(tag_info);
|
||||||
}
|
}
|
||||||
@ -383,10 +383,14 @@ void Module::Interface::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
|
|||||||
void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u32 access_id = rp.Pop<u32>();
|
u32 access_id = rp.Pop<u32>();
|
||||||
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
u32 size = rp.Pop<u32>();
|
||||||
std::vector<u8> buffer = rp.PopStaticBuffer();
|
std::vector<u8> buffer = rp.PopStaticBuffer();
|
||||||
|
|
||||||
LOG_CRITICAL(Service_NFC, "called, size={}", size);
|
LOG_INFO(Service_NFC, "called, size={}", size);
|
||||||
|
|
||||||
|
if (buffer.size() > size) {
|
||||||
|
buffer.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
if (nfc->nfc_mode != CommunicationMode::Amiibo) {
|
if (nfc->nfc_mode != CommunicationMode::Amiibo) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
@ -402,8 +406,9 @@ void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
u32 size = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_INFO(Service_NFC, "called");
|
LOG_INFO(Service_NFC, "called, size={}", size);
|
||||||
|
|
||||||
nfc->device->RescheduleTagRemoveEvent();
|
nfc->device->RescheduleTagRemoveEvent();
|
||||||
|
|
||||||
@ -413,7 +418,7 @@ void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> buffer(sizeof(ApplicationArea));
|
std::vector<u8> buffer(size);
|
||||||
const auto result = nfc->device->GetApplicationArea(buffer);
|
const auto result = nfc->device->GetApplicationArea(buffer);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
@ -423,11 +428,15 @@ void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void Module::Interface::WriteApplicationArea(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::WriteApplicationArea(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
u32 size = rp.Pop<u32>();
|
||||||
std::vector<u8> tag_uuid_info = rp.PopStaticBuffer();
|
std::vector<u8> tag_uuid_info = rp.PopStaticBuffer();
|
||||||
std::vector<u8> buffer = rp.PopStaticBuffer();
|
std::vector<u8> buffer = rp.PopStaticBuffer();
|
||||||
|
|
||||||
LOG_CRITICAL(Service_NFC, "called, size={}", size);
|
LOG_INFO(Service_NFC, "called, size={}", size);
|
||||||
|
|
||||||
|
if (buffer.size() > size) {
|
||||||
|
buffer.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
if (nfc->nfc_mode != CommunicationMode::Amiibo) {
|
if (nfc->nfc_mode != CommunicationMode::Amiibo) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
@ -540,7 +549,7 @@ void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) {
|
|||||||
ModelInfo model_info{};
|
ModelInfo model_info{};
|
||||||
const auto result = nfc->device->GetModelInfo(model_info);
|
const auto result = nfc->device->GetModelInfo(model_info);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(14, 0);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.PushRaw<ModelInfo>(model_info);
|
rb.PushRaw<ModelInfo>(model_info);
|
||||||
}
|
}
|
||||||
|
@ -1101,8 +1101,8 @@ void NfcDevice::BuildAmiiboWithoutKeys() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NfcDevice::RescheduleTagRemoveEvent() {
|
void NfcDevice::RescheduleTagRemoveEvent() {
|
||||||
/// The interval at which the amiibo will be removed automatically 1.5s
|
/// The interval at which the amiibo will be removed automatically 3s
|
||||||
static constexpr u64 amiibo_removal_interval = nsToCycles(1500 * 1000 * 1000);
|
static constexpr u64 amiibo_removal_interval = msToCycles(3 * 1000);
|
||||||
|
|
||||||
system.CoreTiming().UnscheduleEvent(remove_amiibo_event, 0);
|
system.CoreTiming().UnscheduleEvent(remove_amiibo_event, 0);
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "web_service/nus_download.h"
|
#include "core/nus_download.h"
|
||||||
|
|
||||||
namespace WebService::NUS {
|
namespace Core::NUS {
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Download(const std::string& path) {
|
std::optional<std::vector<u8>> Download(const std::string& path) {
|
||||||
constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net";
|
constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net";
|
||||||
@ -46,4 +46,4 @@ std::optional<std::vector<u8>> Download(const std::string& path) {
|
|||||||
return std::vector<u8>(response.body.begin(), response.body.end());
|
return std::vector<u8>(response.body.begin(), response.body.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace WebService::NUS
|
} // namespace Core::NUS
|
@ -8,8 +8,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace WebService::NUS {
|
namespace Core::NUS {
|
||||||
|
|
||||||
std::optional<std::vector<u8>> Download(const std::string& path);
|
std::optional<std::vector<u8>> Download(const std::string& path);
|
||||||
|
|
||||||
}
|
} // namespace Core::NUS
|
@ -15,6 +15,7 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/movie.h"
|
#include "core/movie.h"
|
||||||
#include "core/savestate.h"
|
#include "core/savestate.h"
|
||||||
|
#include "core/savestate_data.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@ -25,8 +26,10 @@ struct CSTHeader {
|
|||||||
u64_le program_id; /// ID of the ROM being executed. Also called title_id
|
u64_le program_id; /// ID of the ROM being executed. Also called title_id
|
||||||
std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
|
std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
|
||||||
u64_le time; /// The time when this save state was created
|
u64_le time; /// The time when this save state was created
|
||||||
|
std::array<u8, 20> build_name; /// The build name (Canary/Nightly) with the version number
|
||||||
|
u32_le zero = 0; /// Should be zero, just in case.
|
||||||
|
|
||||||
std::array<u8, 216> reserved{}; /// Make heading 256 bytes so it has consistent size
|
std::array<u8, 192> reserved{}; /// Make heading 256 bytes so it has consistent size
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes");
|
static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
@ -58,11 +61,26 @@ static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, ""));
|
const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, ""));
|
||||||
|
const std::string build_name =
|
||||||
|
header.zero == 0 ? reinterpret_cast<const char*>(header.build_name.data()) : "";
|
||||||
|
|
||||||
if (revision == Common::g_scm_rev) {
|
if (revision == Common::g_scm_rev) {
|
||||||
info.status = SaveStateInfo::ValidationStatus::OK;
|
info.status = SaveStateInfo::ValidationStatus::OK;
|
||||||
} else {
|
} else {
|
||||||
|
if (!build_name.empty()) {
|
||||||
|
info.build_name = build_name;
|
||||||
|
} else if (hash_to_version.find(revision) != hash_to_version.end()) {
|
||||||
|
info.build_name = hash_to_version.at(revision);
|
||||||
|
}
|
||||||
|
if (info.build_name.empty()) {
|
||||||
LOG_WARNING(Core, "Save state file {} created from a different revision {}", path,
|
LOG_WARNING(Core, "Save state file {} created from a different revision {}", path,
|
||||||
revision);
|
revision);
|
||||||
|
} else {
|
||||||
|
LOG_WARNING(Core,
|
||||||
|
"Save state file {} created from a different build {} with revision {}",
|
||||||
|
path, info.build_name, revision);
|
||||||
|
}
|
||||||
|
|
||||||
info.status = SaveStateInfo::ValidationStatus::RevisionDismatch;
|
info.status = SaveStateInfo::ValidationStatus::RevisionDismatch;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -134,6 +152,10 @@ void System::SaveState(u32 slot) const {
|
|||||||
header.time = std::chrono::duration_cast<std::chrono::seconds>(
|
header.time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
|
const std::string build_fullname = Common::g_build_fullname;
|
||||||
|
std::memset(header.build_name.data(), 0, sizeof(header.build_name));
|
||||||
|
std::memcpy(header.build_name.data(), build_fullname.c_str(),
|
||||||
|
std::min(build_fullname.length(), sizeof(header.build_name) - 1));
|
||||||
|
|
||||||
if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) ||
|
if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) ||
|
||||||
file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) {
|
file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ struct SaveStateInfo {
|
|||||||
OK,
|
OK,
|
||||||
RevisionDismatch,
|
RevisionDismatch,
|
||||||
} status;
|
} status;
|
||||||
|
std::string build_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots
|
constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots
|
||||||
|
1427
src/core/savestate_data.h
Normal file
1427
src/core/savestate_data.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@ create_target_directory_groups(citra-room)
|
|||||||
|
|
||||||
target_link_libraries(citra-room PRIVATE citra_common network)
|
target_link_libraries(citra-room PRIVATE citra_common network)
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(citra-room PRIVATE -DENABLE_WEB_SERVICE)
|
|
||||||
target_link_libraries(citra-room PRIVATE web_service)
|
target_link_libraries(citra-room PRIVATE web_service)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -21,14 +21,10 @@ add_library(network STATIC
|
|||||||
create_target_directory_groups(network)
|
create_target_directory_groups(network)
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_link_libraries(network PRIVATE web_service)
|
||||||
target_link_libraries(network PRIVATE web_service httplib)
|
|
||||||
if (ANDROID)
|
|
||||||
target_link_libraries(network PRIVATE ifaddrs)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(network PRIVATE citra_common enet Boost::serialization cryptopp)
|
target_link_libraries(network PRIVATE citra_common enet Boost::serialization cryptopp httplib)
|
||||||
set_target_properties(network PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
set_target_properties(network PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||||
|
|
||||||
if (CITRA_USE_PRECOMPILED_HEADERS)
|
if (CITRA_USE_PRECOMPILED_HEADERS)
|
||||||
|
@ -170,9 +170,13 @@ bool CustomTexManager::ParseFilename(const FileUtil::FSTEntry& file, CustomTextu
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CustomTexManager::PrepareDumping(u64 title_id) {
|
void CustomTexManager::PrepareDumping(u64 title_id) {
|
||||||
// If a pack exists in the load folder that uses the old hash
|
// If a pack exists in the load folder that uses the old hash, dump textures using the old hash.
|
||||||
// dump textures using the old hash.
|
// This occurs either if a configuration file doesn't exist or that file sets the old hash.
|
||||||
ReadConfig(title_id, true);
|
const std::string load_path =
|
||||||
|
fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::LoadDir), title_id);
|
||||||
|
if (FileUtil::Exists(load_path) && !ReadConfig(title_id, true)) {
|
||||||
|
use_new_hash = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Write template config file
|
// Write template config file
|
||||||
const std::string dump_path =
|
const std::string dump_path =
|
||||||
|
@ -27,9 +27,12 @@ set(SHADER_FILES
|
|||||||
vulkan_blit_depth_stencil.frag
|
vulkan_blit_depth_stencil.frag
|
||||||
)
|
)
|
||||||
|
|
||||||
find_program(GLSLANGVALIDATOR "glslangValidator")
|
find_program(GLSLANG "glslang")
|
||||||
if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND")
|
if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
|
||||||
message(FATAL_ERROR "Required program `glslangValidator` not found.")
|
find_program(GLSLANG "glslangValidator")
|
||||||
|
if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Required program `glslang` (or `glslangValidator`) not found.")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MACROS "-Dgl_VertexID=gl_VertexIndex")
|
set(MACROS "-Dgl_VertexID=gl_VertexIndex")
|
||||||
@ -42,11 +45,11 @@ set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
|
|||||||
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
|
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
|
||||||
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
|
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
|
||||||
|
|
||||||
# Check if `--quiet` is available on host's glslangValidator version
|
# Check if `--quiet` is available on host's glslang version
|
||||||
# glslangValidator prints to STDERR iff an unrecognized flag is passed to it
|
# glslang prints to STDERR iff an unrecognized flag is passed to it
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND
|
COMMAND
|
||||||
${GLSLANGVALIDATOR} ${QUIET_FLAG}
|
${GLSLANG} ${QUIET_FLAG}
|
||||||
ERROR_VARIABLE
|
ERROR_VARIABLE
|
||||||
GLSLANG_ERROR
|
GLSLANG_ERROR
|
||||||
# STDOUT variable defined to silence unnecessary output during CMake configuration
|
# STDOUT variable defined to silence unnecessary output during CMake configuration
|
||||||
@ -55,7 +58,7 @@ execute_process(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (NOT GLSLANG_ERROR STREQUAL "")
|
if (NOT GLSLANG_ERROR STREQUAL "")
|
||||||
message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANGVALIDATOR}`")
|
message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANG}`")
|
||||||
set(QUIET_FLAG "")
|
set(QUIET_FLAG "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
|
|||||||
OUTPUT
|
OUTPUT
|
||||||
${SPIRV_HEADER_FILE}
|
${SPIRV_HEADER_FILE}
|
||||||
COMMAND
|
COMMAND
|
||||||
${GLSLANGVALIDATOR} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
|
${GLSLANG} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
|
||||||
MAIN_DEPENDENCY
|
MAIN_DEPENDENCY
|
||||||
${SOURCE_FILE}
|
${SOURCE_FILE}
|
||||||
)
|
)
|
||||||
|
@ -599,6 +599,17 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
|||||||
SyncTextureLodBias(2);
|
SyncTextureLodBias(2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Texture borders
|
||||||
|
case PICA_REG_INDEX(texturing.texture0.border_color):
|
||||||
|
SyncTextureBorderColor(0);
|
||||||
|
break;
|
||||||
|
case PICA_REG_INDEX(texturing.texture1.border_color):
|
||||||
|
SyncTextureBorderColor(1);
|
||||||
|
break;
|
||||||
|
case PICA_REG_INDEX(texturing.texture2.border_color):
|
||||||
|
SyncTextureBorderColor(2);
|
||||||
|
break;
|
||||||
|
|
||||||
// Clipping plane
|
// Clipping plane
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
||||||
@ -821,6 +832,16 @@ void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
|
||||||
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
|
const auto params = pica_textures[tex_index].config;
|
||||||
|
const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
|
||||||
|
if (border_color != uniform_block_data.data.tex_border_color[tex_index]) {
|
||||||
|
uniform_block_data.data.tex_border_color[tex_index] = border_color;
|
||||||
|
uniform_block_data.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncClipCoef() {
|
void RasterizerAccelerated::SyncClipCoef() {
|
||||||
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
||||||
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
||||||
|
@ -97,6 +97,9 @@ protected:
|
|||||||
/// Syncs the texture LOD bias to match the PICA register
|
/// Syncs the texture LOD bias to match the PICA register
|
||||||
void SyncTextureLodBias(int tex_index);
|
void SyncTextureLodBias(int tex_index);
|
||||||
|
|
||||||
|
/// Syncs the texture border color to match the PICA registers
|
||||||
|
void SyncTextureBorderColor(int tex_index);
|
||||||
|
|
||||||
/// Syncs the clip coefficients to match the PICA register
|
/// Syncs the clip coefficients to match the PICA register
|
||||||
void SyncClipCoef();
|
void SyncClipCoef();
|
||||||
|
|
||||||
|
@ -127,6 +127,29 @@ void RasterizerCache<T>::RemoveFramebuffers(SurfaceId surface_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void RasterizerCache<T>::RemoveTextureCubeFace(SurfaceId surface_id) {
|
||||||
|
if (False(slot_surfaces[surface_id].flags & SurfaceFlagBits::Tracked)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = texture_cube_cache.begin(); it != texture_cube_cache.end();) {
|
||||||
|
TextureCube& cube = it->second;
|
||||||
|
for (SurfaceId& face_id : cube.face_ids) {
|
||||||
|
if (face_id == surface_id) {
|
||||||
|
face_id = SurfaceId{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (std::none_of(cube.face_ids.begin(), cube.face_ids.end(),
|
||||||
|
[](SurfaceId id) { return id; })) {
|
||||||
|
sentenced.emplace_back(cube.surface_id, frame_tick);
|
||||||
|
it = texture_cube_cache.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool RasterizerCache<T>::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) {
|
bool RasterizerCache<T>::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) {
|
||||||
const DebugScope scope{runtime, Common::Vec4f{0.f, 0.f, 1.f, 1.f},
|
const DebugScope scope{runtime, Common::Vec4f{0.f, 0.f, 1.f, 1.f},
|
||||||
@ -866,39 +889,6 @@ SurfaceId RasterizerCache<T>::FindMatch(const SurfaceParams& params, ScaleMatch
|
|||||||
return match_id;
|
return match_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void RasterizerCache<T>::DuplicateSurface(SurfaceId src_id, SurfaceId dst_id) {
|
|
||||||
Surface& src_surface = slot_surfaces[src_id];
|
|
||||||
Surface& dst_surface = slot_surfaces[dst_id];
|
|
||||||
ASSERT(dst_surface.addr <= src_surface.addr && dst_surface.end >= src_surface.end);
|
|
||||||
|
|
||||||
const auto src_rect = src_surface.GetScaledRect();
|
|
||||||
const auto dst_rect = dst_surface.GetScaledSubRect(src_surface);
|
|
||||||
ASSERT(src_rect.GetWidth() == dst_rect.GetWidth());
|
|
||||||
|
|
||||||
const TextureCopy copy = {
|
|
||||||
.src_level = 0,
|
|
||||||
.dst_level = 0,
|
|
||||||
.src_offset = {src_rect.left, src_rect.bottom},
|
|
||||||
.dst_offset = {dst_rect.left, dst_rect.bottom},
|
|
||||||
.extent = {src_rect.GetWidth(), src_rect.GetHeight()},
|
|
||||||
};
|
|
||||||
runtime.CopyTextures(src_surface, dst_surface, copy);
|
|
||||||
|
|
||||||
dst_surface.invalid_regions -= src_surface.GetInterval();
|
|
||||||
dst_surface.invalid_regions += src_surface.invalid_regions;
|
|
||||||
|
|
||||||
SurfaceRegions regions;
|
|
||||||
for (const auto& pair : RangeFromInterval(dirty_regions, src_surface.GetInterval())) {
|
|
||||||
if (pair.second == src_id) {
|
|
||||||
regions += pair.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto& interval : regions) {
|
|
||||||
dirty_regions.set({interval, dst_id});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void RasterizerCache<T>::ValidateSurface(SurfaceId surface_id, PAddr addr, u32 size) {
|
void RasterizerCache<T>::ValidateSurface(SurfaceId surface_id, PAddr addr, u32 size) {
|
||||||
if (size == 0) [[unlikely]] {
|
if (size == 0) [[unlikely]] {
|
||||||
@ -1051,15 +1041,16 @@ bool RasterizerCache<T>::UploadCustomSurface(SurfaceId surface_id, SurfaceInterv
|
|||||||
surface.flags |= SurfaceFlagBits::Custom;
|
surface.flags |= SurfaceFlagBits::Custom;
|
||||||
|
|
||||||
const auto upload = [this, level, surface_id, material]() -> bool {
|
const auto upload = [this, level, surface_id, material]() -> bool {
|
||||||
Surface& surface = slot_surfaces[surface_id];
|
ASSERT_MSG(True(slot_surfaces[surface_id].flags & SurfaceFlagBits::Custom),
|
||||||
ASSERT_MSG(True(surface.flags & SurfaceFlagBits::Custom),
|
|
||||||
"Surface is not suitable for custom upload, aborting!");
|
"Surface is not suitable for custom upload, aborting!");
|
||||||
if (!surface.IsCustom()) {
|
if (!slot_surfaces[surface_id].IsCustom()) {
|
||||||
const SurfaceBase old_surface{surface};
|
const SurfaceBase old_surface{slot_surfaces[surface_id]};
|
||||||
const SurfaceId old_id =
|
const SurfaceId old_id =
|
||||||
slot_surfaces.swap_and_insert(surface_id, runtime, old_surface, material);
|
slot_surfaces.swap_and_insert(surface_id, runtime, old_surface, material);
|
||||||
|
slot_surfaces[old_id].flags &= ~SurfaceFlagBits::Registered;
|
||||||
sentenced.emplace_back(old_id, frame_tick);
|
sentenced.emplace_back(old_id, frame_tick);
|
||||||
}
|
}
|
||||||
|
Surface& surface = slot_surfaces[surface_id];
|
||||||
surface.UploadCustom(material, level);
|
surface.UploadCustom(material, level);
|
||||||
if (custom_tex_manager.SkipMipmaps()) {
|
if (custom_tex_manager.SkipMipmaps()) {
|
||||||
runtime.GenerateMipmaps(surface);
|
runtime.GenerateMipmaps(surface);
|
||||||
@ -1203,7 +1194,6 @@ void RasterizerCache<T>::ClearAll(bool flush) {
|
|||||||
cached_pages -= flush_interval;
|
cached_pages -= flush_interval;
|
||||||
dirty_regions.clear();
|
dirty_regions.clear();
|
||||||
page_table.clear();
|
page_table.clear();
|
||||||
remove_surfaces.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -1232,7 +1222,7 @@ void RasterizerCache<T>::FlushRegion(PAddr addr, u32 size, SurfaceId flush_surfa
|
|||||||
interval.lower(), interval.upper()};
|
interval.lower(), interval.upper()};
|
||||||
|
|
||||||
SCOPE_EXIT({ flushed_intervals += interval; });
|
SCOPE_EXIT({ flushed_intervals += interval; });
|
||||||
if (surface.IsFill()) {
|
if (surface.type == SurfaceType::Fill) {
|
||||||
DownloadFillSurface(surface, interval);
|
DownloadFillSurface(surface, interval);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1274,6 +1264,7 @@ void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, SurfaceId region
|
|||||||
region_owner.MarkValid(invalid_interval);
|
region_owner.MarkValid(invalid_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::container::small_vector<SurfaceId, 4> remove_surfaces;
|
||||||
ForEachSurfaceInRegion(addr, size, [&](SurfaceId surface_id, Surface& surface) {
|
ForEachSurfaceInRegion(addr, size, [&](SurfaceId surface_id, Surface& surface) {
|
||||||
if (surface_id == region_owner_id) {
|
if (surface_id == region_owner_id) {
|
||||||
return;
|
return;
|
||||||
@ -1301,13 +1292,12 @@ void RasterizerCache<T>::InvalidateRegion(PAddr addr, u32 size, SurfaceId region
|
|||||||
|
|
||||||
for (const SurfaceId surface_id : remove_surfaces) {
|
for (const SurfaceId surface_id : remove_surfaces) {
|
||||||
UnregisterSurface(surface_id);
|
UnregisterSurface(surface_id);
|
||||||
if (!slot_surfaces[surface_id].IsFill()) {
|
if (slot_surfaces[surface_id].type != SurfaceType::Fill) {
|
||||||
sentenced.emplace_back(surface_id, frame_tick);
|
sentenced.emplace_back(surface_id, frame_tick);
|
||||||
} else {
|
} else {
|
||||||
slot_surfaces.erase(surface_id);
|
slot_surfaces.erase(surface_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remove_surfaces.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -1316,14 +1306,17 @@ SurfaceId RasterizerCache<T>::CreateSurface(const SurfaceParams& params) {
|
|||||||
const auto it = std::find_if(sentenced.begin(), sentenced.end(), [&](const auto& pair) {
|
const auto it = std::find_if(sentenced.begin(), sentenced.end(), [&](const auto& pair) {
|
||||||
return slot_surfaces[pair.first] == params;
|
return slot_surfaces[pair.first] == params;
|
||||||
});
|
});
|
||||||
if (it != sentenced.end()) {
|
if (it == sentenced.end()) {
|
||||||
|
return slot_surfaces.insert(runtime, params);
|
||||||
|
}
|
||||||
const SurfaceId surface_id = it->first;
|
const SurfaceId surface_id = it->first;
|
||||||
sentenced.erase(it);
|
sentenced.erase(it);
|
||||||
return surface_id;
|
return surface_id;
|
||||||
}
|
|
||||||
return slot_surfaces.insert(runtime, params);
|
|
||||||
}();
|
}();
|
||||||
Surface& surface = slot_surfaces[surface_id];
|
Surface& surface = slot_surfaces[surface_id];
|
||||||
|
if (params.res_scale > surface.res_scale) {
|
||||||
|
surface.ScaleUp(params.res_scale);
|
||||||
|
}
|
||||||
surface.MarkInvalid(surface.GetInterval());
|
surface.MarkInvalid(surface.GetInterval());
|
||||||
return surface_id;
|
return surface_id;
|
||||||
}
|
}
|
||||||
@ -1364,24 +1357,7 @@ void RasterizerCache<T>::UnregisterSurface(SurfaceId surface_id) {
|
|||||||
surfaces.erase(vector_it);
|
surfaces.erase(vector_it);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (False(surface.flags & SurfaceFlagBits::Tracked)) {
|
RemoveTextureCubeFace(surface_id);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::erase_if(texture_cube_cache, [&](auto& pair) {
|
|
||||||
TextureCube& cube = pair.second;
|
|
||||||
for (SurfaceId& face_id : cube.face_ids) {
|
|
||||||
if (face_id == surface_id) {
|
|
||||||
face_id = SurfaceId{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (std::none_of(cube.face_ids.begin(), cube.face_ids.end(),
|
|
||||||
[](SurfaceId id) { return id; })) {
|
|
||||||
sentenced.emplace_back(cube.surface_id, frame_tick);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -1393,7 +1369,6 @@ void RasterizerCache<T>::UnregisterAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
texture_cube_cache.clear();
|
texture_cube_cache.clear();
|
||||||
remove_surfaces.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -165,8 +165,8 @@ private:
|
|||||||
/// Removes any framebuffers that reference the provided surface_id.
|
/// Removes any framebuffers that reference the provided surface_id.
|
||||||
void RemoveFramebuffers(SurfaceId surface_id);
|
void RemoveFramebuffers(SurfaceId surface_id);
|
||||||
|
|
||||||
/// Transfers ownership of a memory region from src_surface to dest_surface
|
/// Removes any references of the provided surface id from cached texture cubes.
|
||||||
void DuplicateSurface(SurfaceId src_id, SurfaceId dst_id);
|
void RemoveTextureCubeFace(SurfaceId surface_id);
|
||||||
|
|
||||||
/// Computes the hash of the provided texture data.
|
/// Computes the hash of the provided texture data.
|
||||||
u64 ComputeHash(const SurfaceParams& load_info, std::span<u8> upload_data);
|
u64 ComputeHash(const SurfaceParams& load_info, std::span<u8> upload_data);
|
||||||
@ -224,7 +224,6 @@ private:
|
|||||||
Common::SlotVector<Framebuffer> slot_framebuffers;
|
Common::SlotVector<Framebuffer> slot_framebuffers;
|
||||||
SurfaceMap dirty_regions;
|
SurfaceMap dirty_regions;
|
||||||
PageMap cached_pages;
|
PageMap cached_pages;
|
||||||
std::vector<SurfaceId> remove_surfaces;
|
|
||||||
u32 resolution_scale_factor;
|
u32 resolution_scale_factor;
|
||||||
u64 frame_tick{};
|
u64 frame_tick{};
|
||||||
FramebufferParams fb_params;
|
FramebufferParams fb_params;
|
||||||
|
@ -46,10 +46,6 @@ public:
|
|||||||
/// Returns true if the surface contains a custom material with a normal map.
|
/// Returns true if the surface contains a custom material with a normal map.
|
||||||
bool HasNormalMap() const noexcept;
|
bool HasNormalMap() const noexcept;
|
||||||
|
|
||||||
bool IsFill() const noexcept {
|
|
||||||
return type == SurfaceType::Fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept {
|
bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept {
|
||||||
const PAddr overlap_end = overlap_addr + static_cast<PAddr>(overlap_size);
|
const PAddr overlap_end = overlap_addr + static_cast<PAddr>(overlap_size);
|
||||||
return addr < overlap_end && overlap_addr < end;
|
return addr < overlap_end && overlap_addr < end;
|
||||||
|
@ -227,4 +227,11 @@ std::string SurfaceParams::DebugName(bool scaled, bool custom) const noexcept {
|
|||||||
custom ? "custom," : "", scaled ? "scaled" : "unscaled");
|
custom ? "custom," : "", scaled ? "scaled" : "unscaled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SurfaceParams::operator==(const SurfaceParams& other) const noexcept {
|
||||||
|
return std::tie(addr, end, width, height, stride, levels, is_tiled, texture_type, pixel_format,
|
||||||
|
custom_format) ==
|
||||||
|
std::tie(other.addr, other.end, other.width, other.height, other.stride, other.levels,
|
||||||
|
other.is_tiled, other.texture_type, other.pixel_format, other.custom_format);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
@ -53,9 +53,7 @@ public:
|
|||||||
/// Returns a string identifier of the params object
|
/// Returns a string identifier of the params object
|
||||||
std::string DebugName(bool scaled, bool custom = false) const noexcept;
|
std::string DebugName(bool scaled, bool custom = false) const noexcept;
|
||||||
|
|
||||||
bool operator==(const SurfaceParams& other) const noexcept {
|
bool operator==(const SurfaceParams& other) const noexcept;
|
||||||
return std::memcmp(this, &other, sizeof(SurfaceParams)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] SurfaceInterval GetInterval() const noexcept {
|
[[nodiscard]] SurfaceInterval GetInterval() const noexcept {
|
||||||
return SurfaceInterval{addr, end};
|
return SurfaceInterval{addr, end};
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/dumping/backend.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "video_core/renderer_opengl/frame_dumper_opengl.h"
|
#include "video_core/renderer_opengl/frame_dumper_opengl.h"
|
||||||
#include "video_core/renderer_opengl/gl_texture_mailbox.h"
|
#include "video_core/renderer_opengl/gl_texture_mailbox.h"
|
||||||
@ -12,13 +15,9 @@
|
|||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
FrameDumperOpenGL::FrameDumperOpenGL(Core::System& system_, Frontend::EmuWindow& emu_window)
|
FrameDumperOpenGL::FrameDumperOpenGL(Core::System& system_, Frontend::EmuWindow& emu_window)
|
||||||
: system(system_), context(emu_window.CreateSharedContext()) {}
|
: system{system_}, context{emu_window.CreateSharedContext()} {}
|
||||||
|
|
||||||
FrameDumperOpenGL::~FrameDumperOpenGL() {
|
FrameDumperOpenGL::~FrameDumperOpenGL() = default;
|
||||||
if (present_thread.joinable()) {
|
|
||||||
present_thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FrameDumperOpenGL::IsDumping() const {
|
bool FrameDumperOpenGL::IsDumping() const {
|
||||||
auto video_dumper = system.GetVideoDumper();
|
auto video_dumper = system.GetVideoDumper();
|
||||||
@ -35,19 +34,19 @@ void FrameDumperOpenGL::StartDumping() {
|
|||||||
present_thread.join();
|
present_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
present_thread = std::thread(&FrameDumperOpenGL::PresentLoop, this);
|
present_thread = std::jthread([this](std::stop_token stop_token) { PresentLoop(stop_token); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameDumperOpenGL::StopDumping() {
|
void FrameDumperOpenGL::StopDumping() {
|
||||||
stop_requested.store(true, std::memory_order_relaxed);
|
present_thread.request_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameDumperOpenGL::PresentLoop() {
|
void FrameDumperOpenGL::PresentLoop(std::stop_token stop_token) {
|
||||||
const auto scope = context->Acquire();
|
const auto scope = context->Acquire();
|
||||||
InitializeOpenGLObjects();
|
InitializeOpenGLObjects();
|
||||||
|
|
||||||
const auto& layout = GetLayout();
|
const auto& layout = GetLayout();
|
||||||
while (!stop_requested.exchange(false)) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto frame = mailbox->TryGetPresentFrame(200);
|
auto frame = mailbox->TryGetPresentFrame(200);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
|
||||||
#include "core/core.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "core/dumping/backend.h"
|
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
|
||||||
@ -18,6 +17,10 @@ class GraphicsContext;
|
|||||||
class TextureMailbox;
|
class TextureMailbox;
|
||||||
} // namespace Frontend
|
} // namespace Frontend
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
class RendererOpenGL;
|
class RendererOpenGL;
|
||||||
@ -42,12 +45,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
void InitializeOpenGLObjects();
|
void InitializeOpenGLObjects();
|
||||||
void CleanupOpenGLObjects();
|
void CleanupOpenGLObjects();
|
||||||
void PresentLoop();
|
void PresentLoop(std::stop_token stop_token);
|
||||||
|
|
||||||
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
std::unique_ptr<Frontend::GraphicsContext> context;
|
std::unique_ptr<Frontend::GraphicsContext> context;
|
||||||
std::thread present_thread;
|
std::jthread present_thread;
|
||||||
std::atomic_bool stop_requested{false};
|
|
||||||
|
|
||||||
// PBOs used to dump frames faster
|
// PBOs used to dump frames faster
|
||||||
std::array<OGLBuffer, 2> pbos;
|
std::array<OGLBuffer, 2> pbos;
|
||||||
|
@ -188,6 +188,10 @@ void Driver::FindBugs() {
|
|||||||
if (vendor == Vendor::AMD || (vendor == Vendor::Intel && !is_linux)) {
|
if (vendor == Vendor::AMD || (vendor == Vendor::Intel && !is_linux)) {
|
||||||
bugs |= DriverBug::BrokenTextureView;
|
bugs |= DriverBug::BrokenTextureView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vendor == Vendor::Intel && !is_linux) {
|
||||||
|
bugs |= DriverBug::BrokenClearTexture;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
@ -38,6 +38,8 @@ enum class DriverBug {
|
|||||||
VertexArrayOutOfBound = 1 << 1,
|
VertexArrayOutOfBound = 1 << 1,
|
||||||
// On AMD and Intel drivers on Windows glTextureView produces incorrect results
|
// On AMD and Intel drivers on Windows glTextureView produces incorrect results
|
||||||
BrokenTextureView = 1 << 2,
|
BrokenTextureView = 1 << 2,
|
||||||
|
// On Haswell and Broadwell Intel drivers glClearTexSubImage produces a black screen
|
||||||
|
BrokenClearTexture = 1 << 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,8 +109,8 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs, bool use_normal
|
|||||||
<< 4;
|
<< 4;
|
||||||
|
|
||||||
// Fragment lighting
|
// Fragment lighting
|
||||||
|
|
||||||
state.lighting.enable = !regs.lighting.disable;
|
state.lighting.enable = !regs.lighting.disable;
|
||||||
|
if (state.lighting.enable) {
|
||||||
state.lighting.src_num = regs.lighting.max_light_index + 1;
|
state.lighting.src_num = regs.lighting.max_light_index + 1;
|
||||||
|
|
||||||
for (unsigned light_index = 0; light_index < state.lighting.src_num; ++light_index) {
|
for (unsigned light_index = 0; light_index < state.lighting.src_num; ++light_index) {
|
||||||
@ -118,9 +118,12 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs, bool use_normal
|
|||||||
const auto& light = regs.lighting.light[num];
|
const auto& light = regs.lighting.light[num];
|
||||||
state.lighting.light[light_index].num = num;
|
state.lighting.light[light_index].num = num;
|
||||||
state.lighting.light[light_index].directional = light.config.directional != 0;
|
state.lighting.light[light_index].directional = light.config.directional != 0;
|
||||||
state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0;
|
state.lighting.light[light_index].two_sided_diffuse =
|
||||||
state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0;
|
light.config.two_sided_diffuse != 0;
|
||||||
state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0;
|
state.lighting.light[light_index].geometric_factor_0 =
|
||||||
|
light.config.geometric_factor_0 != 0;
|
||||||
|
state.lighting.light[light_index].geometric_factor_1 =
|
||||||
|
light.config.geometric_factor_1 != 0;
|
||||||
state.lighting.light[light_index].dist_atten_enable =
|
state.lighting.light[light_index].dist_atten_enable =
|
||||||
!regs.lighting.IsDistAttenDisabled(num);
|
!regs.lighting.IsDistAttenDisabled(num);
|
||||||
state.lighting.light[light_index].spot_atten_enable =
|
state.lighting.light[light_index].spot_atten_enable =
|
||||||
@ -129,14 +132,20 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs, bool use_normal
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0;
|
state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0;
|
||||||
|
if (state.lighting.lut_d0.enable) {
|
||||||
state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
|
state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
|
||||||
state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
|
state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
|
||||||
state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
|
state.lighting.lut_d0.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_d1.enable = regs.lighting.config1.disable_lut_d1 == 0;
|
state.lighting.lut_d1.enable = regs.lighting.config1.disable_lut_d1 == 0;
|
||||||
|
if (state.lighting.lut_d1.enable) {
|
||||||
state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
|
state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
|
||||||
state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
|
state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
|
||||||
state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
|
state.lighting.lut_d1.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
|
||||||
|
}
|
||||||
|
|
||||||
// this is a dummy field due to lack of the corresponding register
|
// this is a dummy field due to lack of the corresponding register
|
||||||
state.lighting.lut_sp.enable = true;
|
state.lighting.lut_sp.enable = true;
|
||||||
@ -145,24 +154,36 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs, bool use_normal
|
|||||||
state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp);
|
state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp);
|
||||||
|
|
||||||
state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0;
|
state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0;
|
||||||
|
if (state.lighting.lut_fr.enable) {
|
||||||
state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
|
state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
|
||||||
state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
|
state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
|
||||||
state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
|
state.lighting.lut_fr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rr.enable = regs.lighting.config1.disable_lut_rr == 0;
|
state.lighting.lut_rr.enable = regs.lighting.config1.disable_lut_rr == 0;
|
||||||
|
if (state.lighting.lut_rr.enable) {
|
||||||
state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
|
state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
|
||||||
state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
|
state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
|
||||||
state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
|
state.lighting.lut_rr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rg.enable = regs.lighting.config1.disable_lut_rg == 0;
|
state.lighting.lut_rg.enable = regs.lighting.config1.disable_lut_rg == 0;
|
||||||
|
if (state.lighting.lut_rg.enable) {
|
||||||
state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
|
state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
|
||||||
state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
|
state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
|
||||||
state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
|
state.lighting.lut_rg.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rb.enable = regs.lighting.config1.disable_lut_rb == 0;
|
state.lighting.lut_rb.enable = regs.lighting.config1.disable_lut_rb == 0;
|
||||||
|
if (state.lighting.lut_rb.enable) {
|
||||||
state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
|
state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
|
||||||
state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
|
state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
|
||||||
state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
|
state.lighting.lut_rb.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.config = regs.lighting.config0.config;
|
state.lighting.config = regs.lighting.config0.config;
|
||||||
state.lighting.enable_primary_alpha = regs.lighting.config0.enable_primary_alpha;
|
state.lighting.enable_primary_alpha = regs.lighting.config0.enable_primary_alpha;
|
||||||
@ -173,11 +194,14 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs, bool use_normal
|
|||||||
state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0;
|
state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0;
|
||||||
|
|
||||||
state.lighting.enable_shadow = regs.lighting.config0.enable_shadow != 0;
|
state.lighting.enable_shadow = regs.lighting.config0.enable_shadow != 0;
|
||||||
|
if (state.lighting.enable_shadow) {
|
||||||
state.lighting.shadow_primary = regs.lighting.config0.shadow_primary != 0;
|
state.lighting.shadow_primary = regs.lighting.config0.shadow_primary != 0;
|
||||||
state.lighting.shadow_secondary = regs.lighting.config0.shadow_secondary != 0;
|
state.lighting.shadow_secondary = regs.lighting.config0.shadow_secondary != 0;
|
||||||
state.lighting.shadow_invert = regs.lighting.config0.shadow_invert != 0;
|
state.lighting.shadow_invert = regs.lighting.config0.shadow_invert != 0;
|
||||||
state.lighting.shadow_alpha = regs.lighting.config0.shadow_alpha != 0;
|
state.lighting.shadow_alpha = regs.lighting.config0.shadow_alpha != 0;
|
||||||
state.lighting.shadow_selector = regs.lighting.config0.shadow_selector;
|
state.lighting.shadow_selector = regs.lighting.config0.shadow_selector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.proctex.enable = regs.texturing.main_config.texture3_enable;
|
state.proctex.enable = regs.texturing.main_config.texture3_enable;
|
||||||
if (state.proctex.enable) {
|
if (state.proctex.enable) {
|
||||||
@ -1246,13 +1270,13 @@ float LookupLightingLUT(int lut_index, int index, float delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float LookupLightingLUTUnsigned(int lut_index, float pos) {
|
float LookupLightingLUTUnsigned(int lut_index, float pos) {
|
||||||
int index = clamp(int(pos * 256.0), 0, 255);
|
int index = int(clamp(floor(pos * 256.0), 0.f, 255.f));
|
||||||
float delta = pos * 256.0 - float(index);
|
float delta = pos * 256.0 - float(index);
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
float LookupLightingLUTSigned(int lut_index, float pos) {
|
float LookupLightingLUTSigned(int lut_index, float pos) {
|
||||||
int index = clamp(int(pos * 128.0), -128, 127);
|
int index = int(clamp(floor(pos * 128.0), -128.f, 127.f));
|
||||||
float delta = pos * 128.0 - float(index);
|
float delta = pos * 128.0 - float(index);
|
||||||
if (index < 0) index += 256;
|
if (index < 0) index += 256;
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
|
@ -187,7 +187,41 @@ bool TextureRuntime::Reinterpret(Surface& source, Surface& dest,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) {
|
bool TextureRuntime::ClearTextureWithoutFbo(Surface& surface,
|
||||||
|
const VideoCore::TextureClear& clear) {
|
||||||
|
if (!driver.HasArbClearTexture() || driver.HasBug(DriverBug::BrokenClearTexture)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GLenum format{};
|
||||||
|
GLenum type{};
|
||||||
|
switch (surface.type) {
|
||||||
|
case SurfaceType::Color:
|
||||||
|
case SurfaceType::Texture:
|
||||||
|
format = GL_RGBA;
|
||||||
|
type = GL_FLOAT;
|
||||||
|
break;
|
||||||
|
case SurfaceType::Depth:
|
||||||
|
format = GL_DEPTH_COMPONENT;
|
||||||
|
type = GL_FLOAT;
|
||||||
|
break;
|
||||||
|
case SurfaceType::DepthStencil:
|
||||||
|
format = GL_DEPTH_STENCIL;
|
||||||
|
type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown surface type {}", surface.type);
|
||||||
|
}
|
||||||
|
glClearTexSubImage(surface.Handle(), clear.texture_level, clear.texture_rect.left,
|
||||||
|
clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(),
|
||||||
|
clear.texture_rect.GetHeight(), 1, format, type, &clear.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) {
|
||||||
|
if (ClearTextureWithoutFbo(surface, clear)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OpenGLState state = OpenGLState::GetCurState();
|
OpenGLState state = OpenGLState::GetCurState();
|
||||||
state.scissor.enabled = true;
|
state.scissor.enabled = true;
|
||||||
state.scissor.x = clear.texture_rect.left;
|
state.scissor.x = clear.texture_rect.left;
|
||||||
@ -222,10 +256,7 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE_MSG("Unknown surface type {}", surface.type);
|
UNREACHABLE_MSG("Unknown surface type {}", surface.type);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
||||||
|
@ -59,7 +59,7 @@ public:
|
|||||||
bool Reinterpret(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
bool Reinterpret(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||||
|
|
||||||
/// Fills the rectangle of the texture with the clear value provided
|
/// Fills the rectangle of the texture with the clear value provided
|
||||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear);
|
void ClearTexture(Surface& surface, const VideoCore::TextureClear& clear);
|
||||||
|
|
||||||
/// Copies a rectangle of source to another rectange of dest
|
/// Copies a rectangle of source to another rectange of dest
|
||||||
bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy);
|
bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy);
|
||||||
@ -76,6 +76,9 @@ private:
|
|||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fills the rectangle of the surface with the value provided, without an fbo.
|
||||||
|
bool ClearTextureWithoutFbo(Surface& surface, const VideoCore::TextureClear& clear);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Driver& driver;
|
const Driver& driver;
|
||||||
BlitHelper blit_helper;
|
BlitHelper blit_helper;
|
||||||
|
@ -41,9 +41,9 @@ constexpr char dolphin_shader_header[] = R"(
|
|||||||
#define lerp mix
|
#define lerp mix
|
||||||
|
|
||||||
// Output variable
|
// Output variable
|
||||||
out float4 color;
|
layout (location = 0) out float4 color;
|
||||||
// Input coordinates
|
// Input coordinates
|
||||||
in float2 frag_tex_coord;
|
layout (location = 0) in float2 frag_tex_coord;
|
||||||
// Resolution
|
// Resolution
|
||||||
uniform float4 i_resolution;
|
uniform float4 i_resolution;
|
||||||
uniform float4 o_resolution;
|
uniform float4 o_resolution;
|
||||||
|
@ -409,7 +409,9 @@ bool Instance::CreateDevice() {
|
|||||||
const bool has_extended_dynamic_state =
|
const bool has_extended_dynamic_state =
|
||||||
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, is_arm || is_qualcomm,
|
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, is_arm || is_qualcomm,
|
||||||
"it is broken on Qualcomm and ARM drivers");
|
"it is broken on Qualcomm and ARM drivers");
|
||||||
const bool has_custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
const bool has_custom_border_color =
|
||||||
|
add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, is_qualcomm,
|
||||||
|
"it is broken on most Qualcomm driver versions");
|
||||||
const bool has_index_type_uint8 = add_extension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
const bool has_index_type_uint8 = add_extension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
||||||
const bool has_pipeline_creation_cache_control =
|
const bool has_pipeline_creation_cache_control =
|
||||||
add_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME);
|
add_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME);
|
||||||
|
@ -69,6 +69,17 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
|
|
||||||
state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0);
|
state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0);
|
||||||
|
|
||||||
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
|
for (u32 tex_index = 0; tex_index < 3; tex_index++) {
|
||||||
|
const auto config = pica_textures[tex_index].config;
|
||||||
|
state.texture_border_color[tex_index].enable_s.Assign(
|
||||||
|
!instance.IsCustomBorderColorSupported() &&
|
||||||
|
config.wrap_s == TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
state.texture_border_color[tex_index].enable_t.Assign(
|
||||||
|
!instance.IsCustomBorderColorSupported() &&
|
||||||
|
config.wrap_t == TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
}
|
||||||
|
|
||||||
// Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
|
// Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
|
||||||
const bool emulate_logic_op = instance.NeedsLogicOpEmulation() &&
|
const bool emulate_logic_op = instance.NeedsLogicOpEmulation() &&
|
||||||
!Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
|
!Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
|
||||||
@ -101,8 +112,8 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
regs.texturing.tev_combiner_buffer_input.update_mask_a.Value() << 4);
|
regs.texturing.tev_combiner_buffer_input.update_mask_a.Value() << 4);
|
||||||
|
|
||||||
// Fragment lighting
|
// Fragment lighting
|
||||||
|
|
||||||
state.lighting.enable.Assign(!regs.lighting.disable);
|
state.lighting.enable.Assign(!regs.lighting.disable);
|
||||||
|
if (state.lighting.enable) {
|
||||||
state.lighting.src_num.Assign(regs.lighting.max_light_index + 1);
|
state.lighting.src_num.Assign(regs.lighting.max_light_index + 1);
|
||||||
|
|
||||||
for (u32 light_index = 0; light_index < state.lighting.src_num; ++light_index) {
|
for (u32 light_index = 0; light_index < state.lighting.src_num; ++light_index) {
|
||||||
@ -110,8 +121,8 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
const auto& light = regs.lighting.light[num];
|
const auto& light = regs.lighting.light[num];
|
||||||
state.lighting.light[light_index].num.Assign(num);
|
state.lighting.light[light_index].num.Assign(num);
|
||||||
state.lighting.light[light_index].directional.Assign(light.config.directional != 0);
|
state.lighting.light[light_index].directional.Assign(light.config.directional != 0);
|
||||||
state.lighting.light[light_index].two_sided_diffuse.Assign(light.config.two_sided_diffuse !=
|
state.lighting.light[light_index].two_sided_diffuse.Assign(
|
||||||
0);
|
light.config.two_sided_diffuse != 0);
|
||||||
state.lighting.light[light_index].geometric_factor_0.Assign(
|
state.lighting.light[light_index].geometric_factor_0.Assign(
|
||||||
light.config.geometric_factor_0 != 0);
|
light.config.geometric_factor_0 != 0);
|
||||||
state.lighting.light[light_index].geometric_factor_1.Assign(
|
state.lighting.light[light_index].geometric_factor_1.Assign(
|
||||||
@ -125,14 +136,20 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.lighting.lut_d0.enable.Assign(regs.lighting.config1.disable_lut_d0 == 0);
|
state.lighting.lut_d0.enable.Assign(regs.lighting.config1.disable_lut_d0 == 0);
|
||||||
|
if (state.lighting.lut_d0.enable) {
|
||||||
state.lighting.lut_d0.abs_input.Assign(regs.lighting.abs_lut_input.disable_d0 == 0);
|
state.lighting.lut_d0.abs_input.Assign(regs.lighting.abs_lut_input.disable_d0 == 0);
|
||||||
state.lighting.lut_d0.type.Assign(regs.lighting.lut_input.d0.Value());
|
state.lighting.lut_d0.type.Assign(regs.lighting.lut_input.d0.Value());
|
||||||
state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
|
state.lighting.lut_d0.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_d1.enable.Assign(regs.lighting.config1.disable_lut_d1 == 0);
|
state.lighting.lut_d1.enable.Assign(regs.lighting.config1.disable_lut_d1 == 0);
|
||||||
|
if (state.lighting.lut_d1.enable) {
|
||||||
state.lighting.lut_d1.abs_input.Assign(regs.lighting.abs_lut_input.disable_d1 == 0);
|
state.lighting.lut_d1.abs_input.Assign(regs.lighting.abs_lut_input.disable_d1 == 0);
|
||||||
state.lighting.lut_d1.type.Assign(regs.lighting.lut_input.d1.Value());
|
state.lighting.lut_d1.type.Assign(regs.lighting.lut_input.d1.Value());
|
||||||
state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
|
state.lighting.lut_d1.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
|
||||||
|
}
|
||||||
|
|
||||||
// this is a dummy field due to lack of the corresponding register
|
// this is a dummy field due to lack of the corresponding register
|
||||||
state.lighting.lut_sp.enable.Assign(1);
|
state.lighting.lut_sp.enable.Assign(1);
|
||||||
@ -141,24 +158,36 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp);
|
state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp);
|
||||||
|
|
||||||
state.lighting.lut_fr.enable.Assign(regs.lighting.config1.disable_lut_fr == 0);
|
state.lighting.lut_fr.enable.Assign(regs.lighting.config1.disable_lut_fr == 0);
|
||||||
|
if (state.lighting.lut_fr.enable) {
|
||||||
state.lighting.lut_fr.abs_input.Assign(regs.lighting.abs_lut_input.disable_fr == 0);
|
state.lighting.lut_fr.abs_input.Assign(regs.lighting.abs_lut_input.disable_fr == 0);
|
||||||
state.lighting.lut_fr.type.Assign(regs.lighting.lut_input.fr.Value());
|
state.lighting.lut_fr.type.Assign(regs.lighting.lut_input.fr.Value());
|
||||||
state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
|
state.lighting.lut_fr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rr.enable.Assign(regs.lighting.config1.disable_lut_rr == 0);
|
state.lighting.lut_rr.enable.Assign(regs.lighting.config1.disable_lut_rr == 0);
|
||||||
|
if (state.lighting.lut_rr.enable) {
|
||||||
state.lighting.lut_rr.abs_input.Assign(regs.lighting.abs_lut_input.disable_rr == 0);
|
state.lighting.lut_rr.abs_input.Assign(regs.lighting.abs_lut_input.disable_rr == 0);
|
||||||
state.lighting.lut_rr.type.Assign(regs.lighting.lut_input.rr.Value());
|
state.lighting.lut_rr.type.Assign(regs.lighting.lut_input.rr.Value());
|
||||||
state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
|
state.lighting.lut_rr.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rg.enable.Assign(regs.lighting.config1.disable_lut_rg == 0);
|
state.lighting.lut_rg.enable.Assign(regs.lighting.config1.disable_lut_rg == 0);
|
||||||
|
if (state.lighting.lut_rg.enable) {
|
||||||
state.lighting.lut_rg.abs_input.Assign(regs.lighting.abs_lut_input.disable_rg == 0);
|
state.lighting.lut_rg.abs_input.Assign(regs.lighting.abs_lut_input.disable_rg == 0);
|
||||||
state.lighting.lut_rg.type.Assign(regs.lighting.lut_input.rg.Value());
|
state.lighting.lut_rg.type.Assign(regs.lighting.lut_input.rg.Value());
|
||||||
state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
|
state.lighting.lut_rg.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.lut_rb.enable.Assign(regs.lighting.config1.disable_lut_rb == 0);
|
state.lighting.lut_rb.enable.Assign(regs.lighting.config1.disable_lut_rb == 0);
|
||||||
|
if (state.lighting.lut_rb.enable) {
|
||||||
state.lighting.lut_rb.abs_input.Assign(regs.lighting.abs_lut_input.disable_rb == 0);
|
state.lighting.lut_rb.abs_input.Assign(regs.lighting.abs_lut_input.disable_rb == 0);
|
||||||
state.lighting.lut_rb.type.Assign(regs.lighting.lut_input.rb.Value());
|
state.lighting.lut_rb.type.Assign(regs.lighting.lut_input.rb.Value());
|
||||||
state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
|
state.lighting.lut_rb.scale =
|
||||||
|
regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
|
||||||
|
}
|
||||||
|
|
||||||
state.lighting.config.Assign(regs.lighting.config0.config);
|
state.lighting.config.Assign(regs.lighting.config0.config);
|
||||||
state.lighting.enable_primary_alpha.Assign(regs.lighting.config0.enable_primary_alpha);
|
state.lighting.enable_primary_alpha.Assign(regs.lighting.config0.enable_primary_alpha);
|
||||||
@ -169,11 +198,14 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
|||||||
state.lighting.clamp_highlights.Assign(regs.lighting.config0.clamp_highlights != 0);
|
state.lighting.clamp_highlights.Assign(regs.lighting.config0.clamp_highlights != 0);
|
||||||
|
|
||||||
state.lighting.enable_shadow.Assign(regs.lighting.config0.enable_shadow != 0);
|
state.lighting.enable_shadow.Assign(regs.lighting.config0.enable_shadow != 0);
|
||||||
|
if (state.lighting.enable_shadow) {
|
||||||
state.lighting.shadow_primary.Assign(regs.lighting.config0.shadow_primary != 0);
|
state.lighting.shadow_primary.Assign(regs.lighting.config0.shadow_primary != 0);
|
||||||
state.lighting.shadow_secondary.Assign(regs.lighting.config0.shadow_secondary != 0);
|
state.lighting.shadow_secondary.Assign(regs.lighting.config0.shadow_secondary != 0);
|
||||||
state.lighting.shadow_invert.Assign(regs.lighting.config0.shadow_invert != 0);
|
state.lighting.shadow_invert.Assign(regs.lighting.config0.shadow_invert != 0);
|
||||||
state.lighting.shadow_alpha.Assign(regs.lighting.config0.shadow_alpha != 0);
|
state.lighting.shadow_alpha.Assign(regs.lighting.config0.shadow_alpha != 0);
|
||||||
state.lighting.shadow_selector.Assign(regs.lighting.config0.shadow_selector);
|
state.lighting.shadow_selector.Assign(regs.lighting.config0.shadow_selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.proctex.enable.Assign(regs.texturing.main_config.texture3_enable);
|
state.proctex.enable.Assign(regs.texturing.main_config.texture3_enable);
|
||||||
if (state.proctex.enable) {
|
if (state.proctex.enable) {
|
||||||
@ -284,54 +316,6 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
|||||||
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_unit) {
|
|
||||||
const auto& state = config.state;
|
|
||||||
switch (texture_unit) {
|
|
||||||
case 0:
|
|
||||||
// Only unit 0 respects the texturing type
|
|
||||||
switch (state.texture0_type) {
|
|
||||||
case TexturingRegs::TextureConfig::Texture2D:
|
|
||||||
return "textureLod(tex0, texcoord0, getLod(texcoord0 * "
|
|
||||||
"vec2(textureSize(tex0, 0))) + tex_lod_bias[0])";
|
|
||||||
case TexturingRegs::TextureConfig::Projection2D:
|
|
||||||
// TODO (wwylele): find the exact LOD formula for projection texture
|
|
||||||
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
|
|
||||||
case TexturingRegs::TextureConfig::TextureCube:
|
|
||||||
return "texture(tex_cube, vec3(texcoord0, texcoord0_w))";
|
|
||||||
case TexturingRegs::TextureConfig::Shadow2D:
|
|
||||||
return "shadowTexture(texcoord0, texcoord0_w)";
|
|
||||||
case TexturingRegs::TextureConfig::ShadowCube:
|
|
||||||
return "shadowTextureCube(texcoord0, texcoord0_w)";
|
|
||||||
case TexturingRegs::TextureConfig::Disabled:
|
|
||||||
return "vec4(0.0)";
|
|
||||||
default:
|
|
||||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {:x}", state.texture0_type);
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return "texture(tex0, texcoord0)";
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return "textureLod(tex1, texcoord1, getLod(texcoord1 * "
|
|
||||||
"vec2(textureSize(tex1, 0))) + tex_lod_bias[1])";
|
|
||||||
case 2:
|
|
||||||
if (state.texture2_use_coord1)
|
|
||||||
return "textureLod(tex2, texcoord1, getLod(texcoord1 * "
|
|
||||||
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
|
|
||||||
else
|
|
||||||
return "textureLod(tex2, texcoord2, getLod(texcoord2 * "
|
|
||||||
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
|
|
||||||
case 3:
|
|
||||||
if (state.proctex.enable) {
|
|
||||||
return "ProcTex()";
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG(Render_OpenGL, "Using Texture3 without enabling it");
|
|
||||||
return "vec4(0.0)";
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the specified TEV stage source component(s)
|
/// Writes the specified TEV stage source component(s)
|
||||||
static void AppendSource(std::string& out, const PicaFSConfig& config,
|
static void AppendSource(std::string& out, const PicaFSConfig& config,
|
||||||
TevStageConfig::Source source, std::string_view index_name) {
|
TevStageConfig::Source source, std::string_view index_name) {
|
||||||
@ -347,16 +331,16 @@ static void AppendSource(std::string& out, const PicaFSConfig& config,
|
|||||||
out += "secondary_fragment_color";
|
out += "secondary_fragment_color";
|
||||||
break;
|
break;
|
||||||
case Source::Texture0:
|
case Source::Texture0:
|
||||||
out += SampleTexture(config, 0);
|
out += "sampleTexUnit0()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture1:
|
case Source::Texture1:
|
||||||
out += SampleTexture(config, 1);
|
out += "sampleTexUnit1()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture2:
|
case Source::Texture2:
|
||||||
out += SampleTexture(config, 2);
|
out += "sampleTexUnit2()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture3:
|
case Source::Texture3:
|
||||||
out += SampleTexture(config, 3);
|
out += "sampleTexUnit3()";
|
||||||
break;
|
break;
|
||||||
case Source::PreviousBuffer:
|
case Source::PreviousBuffer:
|
||||||
out += "combiner_buffer";
|
out += "combiner_buffer";
|
||||||
@ -656,7 +640,7 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) {
|
|||||||
|
|
||||||
// Compute fragment normals and tangents
|
// Compute fragment normals and tangents
|
||||||
const auto perturbation = [&] {
|
const auto perturbation = [&] {
|
||||||
return fmt::format("2.0 * ({}).rgb - 1.0", SampleTexture(config, lighting.bump_selector));
|
return fmt::format("2.0 * (sampleTexUnit{}()).rgb - 1.0", lighting.bump_selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (lighting.bump_mode) {
|
switch (lighting.bump_mode) {
|
||||||
@ -700,7 +684,7 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) {
|
|||||||
"vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n";
|
"vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n";
|
||||||
|
|
||||||
if (lighting.enable_shadow) {
|
if (lighting.enable_shadow) {
|
||||||
std::string shadow_texture = SampleTexture(config, lighting.shadow_selector);
|
std::string shadow_texture = fmt::format("sampleTexUnit{}()", lighting.shadow_selector);
|
||||||
if (lighting.shadow_invert) {
|
if (lighting.shadow_invert) {
|
||||||
out += fmt::format("vec4 shadow = vec4(1.0) - {};\n", shadow_texture);
|
out += fmt::format("vec4 shadow = vec4(1.0) - {};\n", shadow_texture);
|
||||||
} else {
|
} else {
|
||||||
@ -1247,13 +1231,13 @@ float LookupLightingLUT(int lut_index, int index, float delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float LookupLightingLUTUnsigned(int lut_index, float pos) {
|
float LookupLightingLUTUnsigned(int lut_index, float pos) {
|
||||||
int index = clamp(int(pos * 256.0), 0, 255);
|
int index = int(clamp(floor(pos * 256.0), 0.f, 255.f));
|
||||||
float delta = pos * 256.0 - float(index);
|
float delta = pos * 256.0 - float(index);
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
float LookupLightingLUTSigned(int lut_index, float pos) {
|
float LookupLightingLUTSigned(int lut_index, float pos) {
|
||||||
int index = clamp(int(pos * 128.0), -128, 127);
|
int index = int(clamp(floor(pos * 128.0), -128.f, 127.f));
|
||||||
float delta = pos * 128.0 - float(index);
|
float delta = pos * 128.0 - float(index);
|
||||||
if (index < 0) index += 256;
|
if (index < 0) index += 256;
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
@ -1310,6 +1294,7 @@ float mix2(vec4 s, vec2 a) {
|
|||||||
|
|
||||||
vec4 shadowTexture(vec2 uv, float w) {
|
vec4 shadowTexture(vec2 uv, float w) {
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if (!config.state.shadow_texture_orthographic) {
|
if (!config.state.shadow_texture_orthographic) {
|
||||||
out += "uv /= w;";
|
out += "uv /= w;";
|
||||||
}
|
}
|
||||||
@ -1344,9 +1329,7 @@ vec4 shadowTextureCube(vec2 uv, float w) {
|
|||||||
uv = -c.xy;
|
uv = -c.xy;
|
||||||
if (c.z > 0.0) uv.x = -uv.x;
|
if (c.z > 0.0) uv.x = -uv.x;
|
||||||
}
|
}
|
||||||
)";
|
uint z = uint(max(0, int(min(w, 1.0) * float(0xFFFFFF)) - shadow_texture_bias));
|
||||||
out += "uint z = uint(max(0, int(min(w, 1.0) * float(0xFFFFFF)) - shadow_texture_bias));";
|
|
||||||
out += R"(
|
|
||||||
vec2 coord = vec2(size) * (uv / w * vec2(0.5) + vec2(0.5)) - vec2(0.5);
|
vec2 coord = vec2(size) * (uv / w * vec2(0.5) + vec2(0.5)) - vec2(0.5);
|
||||||
vec2 coord_floor = floor(coord);
|
vec2 coord_floor = floor(coord);
|
||||||
vec2 f = coord - coord_floor;
|
vec2 f = coord - coord_floor;
|
||||||
@ -1411,8 +1394,90 @@ vec4 shadowTextureCube(vec2 uv, float w) {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if (config.state.proctex.enable)
|
if (config.state.proctex.enable) {
|
||||||
AppendProcTexSampler(out, config);
|
AppendProcTexSampler(out, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 texture_unit = 0; texture_unit < 4; texture_unit++) {
|
||||||
|
out += fmt::format("vec4 sampleTexUnit{}() {{", texture_unit);
|
||||||
|
if (texture_unit == 0 && state.texture0_type == TexturingRegs::TextureConfig::Disabled) {
|
||||||
|
out += "return vec4(0.0);}";
|
||||||
|
continue;
|
||||||
|
} else if (texture_unit == 3) {
|
||||||
|
if (state.proctex.enable) {
|
||||||
|
out += "return ProcTex();}";
|
||||||
|
} else {
|
||||||
|
out += "return vec4(0.0);}";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 texcoord_num = texture_unit == 2 && state.texture2_use_coord1 ? 1 : texture_unit;
|
||||||
|
if (config.state.texture_border_color[texture_unit].enable_s) {
|
||||||
|
out += fmt::format(R"(
|
||||||
|
if (texcoord{}.x < 0 || texcoord{}.x > 1) {{
|
||||||
|
return tex_border_color[{}];
|
||||||
|
}}
|
||||||
|
)",
|
||||||
|
texcoord_num, texcoord_num, texture_unit);
|
||||||
|
}
|
||||||
|
if (config.state.texture_border_color[texture_unit].enable_t) {
|
||||||
|
out += fmt::format(R"(
|
||||||
|
if (texcoord{}.y < 0 || texcoord{}.y > 1) {{
|
||||||
|
return tex_border_color[{}];
|
||||||
|
}}
|
||||||
|
)",
|
||||||
|
texcoord_num, texcoord_num, texture_unit);
|
||||||
|
}
|
||||||
|
// TODO: 3D border?
|
||||||
|
|
||||||
|
switch (texture_unit) {
|
||||||
|
case 0:
|
||||||
|
// Only unit 0 respects the texturing type
|
||||||
|
switch (state.texture0_type) {
|
||||||
|
case TexturingRegs::TextureConfig::Texture2D:
|
||||||
|
out += "return textureLod(tex0, texcoord0, getLod(texcoord0 * "
|
||||||
|
"vec2(textureSize(tex0, 0))) + tex_lod_bias[0]);";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::Projection2D:
|
||||||
|
// TODO (wwylele): find the exact LOD formula for projection texture
|
||||||
|
out += "return textureProj(tex0, vec3(texcoord0, texcoord0_w));";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::TextureCube:
|
||||||
|
out += "return texture(tex_cube, vec3(texcoord0, texcoord0_w));";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::Shadow2D:
|
||||||
|
out += "return shadowTexture(texcoord0, texcoord0_w);";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::ShadowCube:
|
||||||
|
out += "return shadowTextureCube(texcoord0, texcoord0_w);";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(HW_GPU, "Unhandled texture type {:x}", state.texture0_type);
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
out += "return texture(tex0, texcoord0);";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
out += "return textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, "
|
||||||
|
"0))) + tex_lod_bias[1]);";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (state.texture2_use_coord1) {
|
||||||
|
out += "return textureLod(tex2, texcoord1, getLod(texcoord1 * "
|
||||||
|
"vec2(textureSize(tex2, 0))) + tex_lod_bias[1]);";
|
||||||
|
} else {
|
||||||
|
out += "return textureLod(tex2, texcoord2, getLod(texcoord2 * "
|
||||||
|
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2]);";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out += "}";
|
||||||
|
}
|
||||||
|
|
||||||
// We round the interpolated primary color to the nearest 1/255th
|
// We round the interpolated primary color to the nearest 1/255th
|
||||||
// This maintains the PICA's 8 bits of precision
|
// This maintains the PICA's 8 bits of precision
|
||||||
|
@ -57,6 +57,11 @@ struct PicaFSConfigState {
|
|||||||
BitField<28, 1, u32> shadow_texture_orthographic;
|
BitField<28, 1, u32> shadow_texture_orthographic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union {
|
||||||
|
BitField<0, 1, u32> enable_s;
|
||||||
|
BitField<1, 1, u32> enable_t;
|
||||||
|
} texture_border_color[3];
|
||||||
|
|
||||||
std::array<TevStageConfigRaw, 6> tev_stages;
|
std::array<TevStageConfigRaw, 6> tev_stages;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -21,8 +21,8 @@ FragmentModule::FragmentModule(Core::TelemetrySession& telemetry_, const PicaFSC
|
|||||||
DefineArithmeticTypes();
|
DefineArithmeticTypes();
|
||||||
DefineUniformStructs();
|
DefineUniformStructs();
|
||||||
DefineInterface();
|
DefineInterface();
|
||||||
if (config.state.proctex.enable) {
|
for (u32 i = 0; i < NUM_TEX_UNITS; i++) {
|
||||||
DefineProcTexSampler();
|
DefineTexSampler(i);
|
||||||
}
|
}
|
||||||
DefineEntryPoint();
|
DefineEntryPoint();
|
||||||
}
|
}
|
||||||
@ -225,7 +225,8 @@ void FragmentModule::WriteLighting() {
|
|||||||
|
|
||||||
// Compute fragment normals and tangents
|
// Compute fragment normals and tangents
|
||||||
const auto perturbation = [&]() -> Id {
|
const auto perturbation = [&]() -> Id {
|
||||||
const Id texel{SampleTexture(lighting.bump_selector)};
|
const Id texel{
|
||||||
|
OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[lighting.bump_selector])};
|
||||||
const Id texel_rgb{OpVectorShuffle(vec_ids.Get(3), texel, texel, 0, 1, 2)};
|
const Id texel_rgb{OpVectorShuffle(vec_ids.Get(3), texel, texel, 0, 1, 2)};
|
||||||
const Id rgb_mul_two{OpVectorTimesScalar(vec_ids.Get(3), texel_rgb, ConstF32(2.f))};
|
const Id rgb_mul_two{OpVectorTimesScalar(vec_ids.Get(3), texel_rgb, ConstF32(2.f))};
|
||||||
return OpFSub(vec_ids.Get(3), rgb_mul_two, ConstF32(1.f, 1.f, 1.f));
|
return OpFSub(vec_ids.Get(3), rgb_mul_two, ConstF32(1.f, 1.f, 1.f));
|
||||||
@ -284,23 +285,25 @@ void FragmentModule::WriteLighting() {
|
|||||||
|
|
||||||
Id shadow{ConstF32(1.f, 1.f, 1.f, 1.f)};
|
Id shadow{ConstF32(1.f, 1.f, 1.f, 1.f)};
|
||||||
if (lighting.enable_shadow) {
|
if (lighting.enable_shadow) {
|
||||||
shadow = SampleTexture(lighting.shadow_selector);
|
shadow = OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[lighting.shadow_selector]);
|
||||||
if (lighting.shadow_invert) {
|
if (lighting.shadow_invert) {
|
||||||
shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow);
|
shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto lookup_lighting_lut_unsigned = [this](Id lut_index, Id pos) -> Id {
|
const auto lookup_lighting_lut_unsigned = [this](Id lut_index, Id pos) -> Id {
|
||||||
const Id pos_int{OpConvertFToS(i32_id, OpFMul(f32_id, pos, ConstF32(256.f)))};
|
const Id pos_floor{OpFloor(f32_id, OpFMul(f32_id, pos, ConstF32(256.f)))};
|
||||||
const Id index{OpSClamp(i32_id, pos_int, ConstS32(0), ConstS32(255))};
|
const Id index_float{OpFClamp(f32_id, pos_floor, ConstF32(0.f), ConstF32(255.f))};
|
||||||
|
const Id index{OpConvertFToS(i32_id, index_float)};
|
||||||
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
||||||
const Id delta{OpFma(f32_id, pos, ConstF32(256.f), neg_index)};
|
const Id delta{OpFma(f32_id, pos, ConstF32(256.f), neg_index)};
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto lookup_lighting_lut_signed = [this](Id lut_index, Id pos) -> Id {
|
const auto lookup_lighting_lut_signed = [this](Id lut_index, Id pos) -> Id {
|
||||||
const Id pos_int{OpConvertFToS(i32_id, OpFMul(f32_id, pos, ConstF32(128.f)))};
|
const Id pos_floor{OpFloor(f32_id, OpFMul(f32_id, pos, ConstF32(128.f)))};
|
||||||
const Id index{OpSClamp(i32_id, pos_int, ConstS32(-128), ConstS32(127))};
|
const Id index_float{OpFClamp(f32_id, pos_floor, ConstF32(-128.f), ConstF32(127.f))};
|
||||||
|
const Id index{OpConvertFToS(i32_id, index_float)};
|
||||||
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
||||||
const Id delta{OpFma(f32_id, pos, ConstF32(128.f), neg_index)};
|
const Id delta{OpFma(f32_id, pos, ConstF32(128.f), neg_index)};
|
||||||
const Id increment{
|
const Id increment{
|
||||||
@ -708,89 +711,6 @@ void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::SampleTexture(u32 texture_unit) {
|
|
||||||
const PicaFSConfigState& state = config.state;
|
|
||||||
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
|
||||||
|
|
||||||
// PICA's LOD formula for 2D textures.
|
|
||||||
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
|
||||||
// f(x, y) >= max{m_u, m_v, m_w}
|
|
||||||
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
|
|
||||||
const auto sample_lod = [this, texture_unit](Id tex_id, Id texcoord_id) {
|
|
||||||
const Id sampled_image{OpLoad(TypeSampledImage(image2d_id), tex_id)};
|
|
||||||
const Id tex_image{OpImage(image2d_id, sampled_image)};
|
|
||||||
const Id tex_size{OpImageQuerySizeLod(ivec_ids.Get(2), tex_image, ConstS32(0))};
|
|
||||||
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id)};
|
|
||||||
const Id coord{OpFMul(vec_ids.Get(2), texcoord, OpConvertSToF(vec_ids.Get(2), tex_size))};
|
|
||||||
const Id abs_dfdx_coord{OpFAbs(vec_ids.Get(2), OpDPdx(vec_ids.Get(2), coord))};
|
|
||||||
const Id abs_dfdy_coord{OpFAbs(vec_ids.Get(2), OpDPdy(vec_ids.Get(2), coord))};
|
|
||||||
const Id d{OpFMax(vec_ids.Get(2), abs_dfdx_coord, abs_dfdy_coord)};
|
|
||||||
const Id dx_dy_max{
|
|
||||||
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
|
|
||||||
const Id lod{OpLog2(f32_id, dx_dy_max)};
|
|
||||||
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))};
|
|
||||||
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
|
||||||
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
|
||||||
spv::ImageOperandsMask::Lod, biased_lod);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto sample = [this](Id tex_id, bool projection) {
|
|
||||||
const Id image_type = tex_id.value == tex_cube_id.value ? image_cube_id : image2d_id;
|
|
||||||
const Id sampled_image{OpLoad(TypeSampledImage(image_type), tex_id)};
|
|
||||||
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord0_id)};
|
|
||||||
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
|
||||||
const Id coord{OpCompositeConstruct(vec_ids.Get(3),
|
|
||||||
OpCompositeExtract(f32_id, texcoord0, 0),
|
|
||||||
OpCompositeExtract(f32_id, texcoord0, 1), texcoord0_w)};
|
|
||||||
if (projection) {
|
|
||||||
return OpImageSampleProjImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
|
||||||
} else {
|
|
||||||
return OpImageSampleImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (texture_unit) {
|
|
||||||
case 0:
|
|
||||||
// Only unit 0 respects the texturing type
|
|
||||||
switch (state.texture0_type) {
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Texture2D:
|
|
||||||
return sample_lod(tex0_id, texcoord0_id);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Projection2D:
|
|
||||||
return sample(tex0_id, true);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::TextureCube:
|
|
||||||
return sample(tex_cube_id, false);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Shadow2D:
|
|
||||||
return SampleShadow();
|
|
||||||
// case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
|
||||||
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Disabled:
|
|
||||||
return zero_vec;
|
|
||||||
default:
|
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return zero_vec;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return sample_lod(tex1_id, texcoord1_id);
|
|
||||||
case 2:
|
|
||||||
if (state.texture2_use_coord1) {
|
|
||||||
return sample_lod(tex2_id, texcoord1_id);
|
|
||||||
} else {
|
|
||||||
return sample_lod(tex2_id, texcoord2_id);
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if (state.proctex.enable) {
|
|
||||||
return OpFunctionCall(vec_ids.Get(4), proctex_func);
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG(Render_Vulkan, "Using Texture3 without enabling it");
|
|
||||||
return zero_vec;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
return void_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
||||||
const Id pixel_d24{OpShiftRightLogical(u32_id, pixel, ConstS32(8))};
|
const Id pixel_d24{OpShiftRightLogical(u32_id, pixel, ConstS32(8))};
|
||||||
const Id pixel_s8{OpConvertUToF(f32_id, OpBitwiseAnd(u32_id, pixel, ConstU32(255u)))};
|
const Id pixel_s8{OpConvertUToF(f32_id, OpBitwiseAnd(u32_id, pixel, ConstU32(255u)))};
|
||||||
@ -800,7 +720,7 @@ Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::SampleShadow() {
|
Id FragmentModule::SampleShadow() {
|
||||||
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord0_id)};
|
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord_id[0])};
|
||||||
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
||||||
const Id abs_min_w{OpFMul(f32_id, OpFMin(f32_id, OpFAbs(f32_id, texcoord0_w), ConstF32(1.f)),
|
const Id abs_min_w{OpFMul(f32_id, OpFMin(f32_id, OpFAbs(f32_id, texcoord0_w), ConstF32(1.f)),
|
||||||
ConstF32(16777215.f))};
|
ConstF32(16777215.f))};
|
||||||
@ -939,11 +859,145 @@ Id FragmentModule::AppendProcTexCombineAndMap(ProcTexCombiner combiner, Id u, Id
|
|||||||
return ProcTexLookupLUT(offset, combined);
|
return ProcTexLookupLUT(offset, combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FragmentModule::DefineProcTexSampler() {
|
void FragmentModule::DefineTexSampler(u32 texture_unit) {
|
||||||
|
const PicaFSConfigState& state = config.state;
|
||||||
|
|
||||||
const Id func_type{TypeFunction(vec_ids.Get(4))};
|
const Id func_type{TypeFunction(vec_ids.Get(4))};
|
||||||
proctex_func = OpFunction(vec_ids.Get(4), spv::FunctionControlMask::MaskNone, func_type);
|
sample_tex_unit_func[texture_unit] =
|
||||||
|
OpFunction(vec_ids.Get(4), spv::FunctionControlMask::MaskNone, func_type);
|
||||||
AddLabel(OpLabel());
|
AddLabel(OpLabel());
|
||||||
|
|
||||||
|
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
||||||
|
|
||||||
|
if (texture_unit == 0 && state.texture0_type == TexturingRegs::TextureConfig::Disabled) {
|
||||||
|
OpReturnValue(zero_vec);
|
||||||
|
OpFunctionEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture_unit == 3) {
|
||||||
|
if (state.proctex.enable) {
|
||||||
|
OpReturnValue(ProcTexSampler());
|
||||||
|
} else {
|
||||||
|
OpReturnValue(zero_vec);
|
||||||
|
}
|
||||||
|
OpFunctionEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Id border_label{OpLabel()};
|
||||||
|
const Id not_border_label{OpLabel()};
|
||||||
|
|
||||||
|
u32 texcoord_num = texture_unit == 2 && state.texture2_use_coord1 ? 1 : texture_unit;
|
||||||
|
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[texcoord_num])};
|
||||||
|
|
||||||
|
auto& texture_border_color = state.texture_border_color[texture_unit];
|
||||||
|
if (texture_border_color.enable_s || texture_border_color.enable_t) {
|
||||||
|
const Id texcoord_s{OpCompositeExtract(f32_id, texcoord, 0)};
|
||||||
|
const Id texcoord_t{OpCompositeExtract(f32_id, texcoord, 1)};
|
||||||
|
|
||||||
|
const Id s_lt_zero{OpFOrdLessThan(bool_id, texcoord_s, ConstF32(0.0f))};
|
||||||
|
const Id s_gt_one{OpFOrdGreaterThan(bool_id, texcoord_s, ConstF32(1.0f))};
|
||||||
|
const Id t_lt_zero{OpFOrdLessThan(bool_id, texcoord_t, ConstF32(0.0f))};
|
||||||
|
const Id t_gt_one{OpFOrdGreaterThan(bool_id, texcoord_t, ConstF32(1.0f))};
|
||||||
|
|
||||||
|
Id cond{};
|
||||||
|
if (texture_border_color.enable_s && texture_border_color.enable_t) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(4), s_lt_zero, s_gt_one,
|
||||||
|
t_lt_zero, t_gt_one));
|
||||||
|
} else if (texture_border_color.enable_s) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(2), s_lt_zero, s_gt_one));
|
||||||
|
} else if (texture_border_color.enable_t) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(2), t_lt_zero, t_gt_one));
|
||||||
|
}
|
||||||
|
|
||||||
|
OpSelectionMerge(not_border_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
OpBranchConditional(cond, border_label, not_border_label);
|
||||||
|
|
||||||
|
AddLabel(border_label);
|
||||||
|
const Id border_color{
|
||||||
|
GetShaderDataMember(vec_ids.Get(4), ConstS32(29), ConstU32(texture_unit))};
|
||||||
|
OpReturnValue(border_color);
|
||||||
|
|
||||||
|
AddLabel(not_border_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PICA's LOD formula for 2D textures.
|
||||||
|
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
||||||
|
// f(x, y) >= max{m_u, m_v, m_w}
|
||||||
|
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
|
||||||
|
const auto sample_lod = [&](Id tex_id) {
|
||||||
|
const Id sampled_image{OpLoad(TypeSampledImage(image2d_id), tex_id)};
|
||||||
|
const Id tex_image{OpImage(image2d_id, sampled_image)};
|
||||||
|
const Id tex_size{OpImageQuerySizeLod(ivec_ids.Get(2), tex_image, ConstS32(0))};
|
||||||
|
const Id coord{OpFMul(vec_ids.Get(2), texcoord, OpConvertSToF(vec_ids.Get(2), tex_size))};
|
||||||
|
const Id abs_dfdx_coord{OpFAbs(vec_ids.Get(2), OpDPdx(vec_ids.Get(2), coord))};
|
||||||
|
const Id abs_dfdy_coord{OpFAbs(vec_ids.Get(2), OpDPdy(vec_ids.Get(2), coord))};
|
||||||
|
const Id d{OpFMax(vec_ids.Get(2), abs_dfdx_coord, abs_dfdy_coord)};
|
||||||
|
const Id dx_dy_max{
|
||||||
|
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
|
||||||
|
const Id lod{OpLog2(f32_id, dx_dy_max)};
|
||||||
|
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))};
|
||||||
|
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
||||||
|
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
||||||
|
spv::ImageOperandsMask::Lod, biased_lod);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto sample_3d = [&](Id tex_id, bool projection) {
|
||||||
|
const Id image_type = tex_id.value == tex_cube_id.value ? image_cube_id : image2d_id;
|
||||||
|
const Id sampled_image{OpLoad(TypeSampledImage(image_type), tex_id)};
|
||||||
|
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
||||||
|
const Id coord{OpCompositeConstruct(vec_ids.Get(3), OpCompositeExtract(f32_id, texcoord, 0),
|
||||||
|
OpCompositeExtract(f32_id, texcoord, 1), texcoord0_w)};
|
||||||
|
if (projection) {
|
||||||
|
return OpImageSampleProjImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
||||||
|
} else {
|
||||||
|
return OpImageSampleImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Id ret_val{void_id};
|
||||||
|
switch (texture_unit) {
|
||||||
|
case 0:
|
||||||
|
// Only unit 0 respects the texturing type
|
||||||
|
switch (state.texture0_type) {
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Texture2D:
|
||||||
|
ret_val = sample_lod(tex0_id);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Projection2D:
|
||||||
|
ret_val = sample_3d(tex0_id, true);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::TextureCube:
|
||||||
|
ret_val = sample_3d(tex_cube_id, false);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Shadow2D:
|
||||||
|
ret_val = SampleShadow();
|
||||||
|
// case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
||||||
|
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
ret_val = zero_vec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ret_val = sample_lod(tex1_id);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ret_val = sample_lod(tex2_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpReturnValue(ret_val);
|
||||||
|
OpFunctionEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
Id FragmentModule::ProcTexSampler() {
|
||||||
// Define noise tables at the beginning of the function
|
// Define noise tables at the beginning of the function
|
||||||
if (config.state.proctex.noise_enable) {
|
if (config.state.proctex.noise_enable) {
|
||||||
noise1d_table =
|
noise1d_table =
|
||||||
@ -955,24 +1009,11 @@ void FragmentModule::DefineProcTexSampler() {
|
|||||||
|
|
||||||
Id uv{};
|
Id uv{};
|
||||||
if (config.state.proctex.coord < 3) {
|
if (config.state.proctex.coord < 3) {
|
||||||
Id texcoord_id{};
|
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])};
|
||||||
switch (config.state.proctex.coord.Value()) {
|
|
||||||
case 0:
|
|
||||||
texcoord_id = texcoord0_id;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
texcoord_id = texcoord1_id;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
texcoord_id = texcoord2_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id)};
|
|
||||||
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
||||||
} else {
|
} else {
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3");
|
LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3");
|
||||||
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord0_id));
|
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This LOD formula is the same as the LOD upper limit defined in OpenGL.
|
// This LOD formula is the same as the LOD upper limit defined in OpenGL.
|
||||||
@ -1056,8 +1097,7 @@ void FragmentModule::DefineProcTexSampler() {
|
|||||||
final_color = OpCompositeInsert(vec_ids.Get(4), final_alpha, final_color, 3);
|
final_color = OpCompositeInsert(vec_ids.Get(4), final_alpha, final_color, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpReturnValue(final_color);
|
return final_color;
|
||||||
OpFunctionEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::Byteround(Id variable_id, u32 size) {
|
Id FragmentModule::Byteround(Id variable_id, u32 size) {
|
||||||
@ -1224,13 +1264,13 @@ Id FragmentModule::AppendSource(TevStageConfig::Source source, s32 index) {
|
|||||||
case Source::SecondaryFragmentColor:
|
case Source::SecondaryFragmentColor:
|
||||||
return secondary_fragment_color;
|
return secondary_fragment_color;
|
||||||
case Source::Texture0:
|
case Source::Texture0:
|
||||||
return SampleTexture(0);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[0]);
|
||||||
case Source::Texture1:
|
case Source::Texture1:
|
||||||
return SampleTexture(1);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[1]);
|
||||||
case Source::Texture2:
|
case Source::Texture2:
|
||||||
return SampleTexture(2);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[2]);
|
||||||
case Source::Texture3:
|
case Source::Texture3:
|
||||||
return SampleTexture(3);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[3]);
|
||||||
case Source::PreviousBuffer:
|
case Source::PreviousBuffer:
|
||||||
return combiner_buffer;
|
return combiner_buffer;
|
||||||
case Source::Constant:
|
case Source::Constant:
|
||||||
@ -1426,9 +1466,9 @@ void FragmentModule::DefineEntryPoint() {
|
|||||||
|
|
||||||
const Id main_type{TypeFunction(TypeVoid())};
|
const Id main_type{TypeFunction(TypeVoid())};
|
||||||
const Id main_func{OpFunction(TypeVoid(), spv::FunctionControlMask::MaskNone, main_type)};
|
const Id main_func{OpFunction(TypeVoid(), spv::FunctionControlMask::MaskNone, main_type)};
|
||||||
AddEntryPoint(spv::ExecutionModel::Fragment, main_func, "main", primary_color_id, texcoord0_id,
|
AddEntryPoint(spv::ExecutionModel::Fragment, main_func, "main", primary_color_id,
|
||||||
texcoord1_id, texcoord2_id, texcoord0_w_id, normquat_id, view_id, color_id,
|
texcoord_id[0], texcoord_id[1], texcoord_id[2], texcoord0_w_id, normquat_id,
|
||||||
gl_frag_coord_id, gl_frag_depth_id);
|
view_id, color_id, gl_frag_coord_id, gl_frag_depth_id);
|
||||||
AddExecutionMode(main_func, spv::ExecutionMode::OriginUpperLeft);
|
AddExecutionMode(main_func, spv::ExecutionMode::OriginUpperLeft);
|
||||||
AddExecutionMode(main_func, spv::ExecutionMode::DepthReplacing);
|
AddExecutionMode(main_func, spv::ExecutionMode::DepthReplacing);
|
||||||
}
|
}
|
||||||
@ -1441,21 +1481,25 @@ void FragmentModule::DefineUniformStructs() {
|
|||||||
const Id light_src_array_id{TypeArray(light_src_struct_id, ConstU32(NUM_LIGHTS))};
|
const Id light_src_array_id{TypeArray(light_src_struct_id, ConstU32(NUM_LIGHTS))};
|
||||||
const Id lighting_lut_array_id{TypeArray(ivec_ids.Get(4), ConstU32(NUM_LIGHTING_SAMPLERS / 4))};
|
const Id lighting_lut_array_id{TypeArray(ivec_ids.Get(4), ConstU32(NUM_LIGHTING_SAMPLERS / 4))};
|
||||||
const Id const_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_TEV_STAGES))};
|
const Id const_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_TEV_STAGES))};
|
||||||
|
const Id border_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_NON_PROC_TEX_UNITS))};
|
||||||
|
|
||||||
const Id shader_data_struct_id{TypeStruct(
|
const Id shader_data_struct_id{
|
||||||
i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, i32_id,
|
TypeStruct(i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id,
|
||||||
i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id, lighting_lut_array_id,
|
i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id,
|
||||||
vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(3),
|
lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2),
|
||||||
light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(3), vec_ids.Get(4))};
|
vec_ids.Get(2), vec_ids.Get(3), light_src_array_id, const_color_array_id,
|
||||||
|
vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))};
|
||||||
|
|
||||||
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
|
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
|
||||||
constexpr std::array shader_data_offsets{
|
constexpr std::array shader_data_offsets{0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u,
|
||||||
0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u, 32u, 36u, 40u, 44u, 48u, 52u, 56u,
|
32u, 36u, 40u, 44u, 48u, 52u, 56u, 60u,
|
||||||
60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u};
|
64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u,
|
||||||
|
224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u};
|
||||||
|
|
||||||
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
||||||
Decorate(const_color_array_id, spv::Decoration::ArrayStride, 16u);
|
Decorate(const_color_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
|
Decorate(border_color_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
for (u32 i = 0; i < static_cast<u32>(light_src_offsets.size()); i++) {
|
for (u32 i = 0; i < static_cast<u32>(light_src_offsets.size()); i++) {
|
||||||
MemberDecorate(light_src_struct_id, i, spv::Decoration::Offset, light_src_offsets[i]);
|
MemberDecorate(light_src_struct_id, i, spv::Decoration::Offset, light_src_offsets[i]);
|
||||||
}
|
}
|
||||||
@ -1473,9 +1517,9 @@ void FragmentModule::DefineUniformStructs() {
|
|||||||
void FragmentModule::DefineInterface() {
|
void FragmentModule::DefineInterface() {
|
||||||
// Define interface block
|
// Define interface block
|
||||||
primary_color_id = DefineInput(vec_ids.Get(4), 1);
|
primary_color_id = DefineInput(vec_ids.Get(4), 1);
|
||||||
texcoord0_id = DefineInput(vec_ids.Get(2), 2);
|
texcoord_id[0] = DefineInput(vec_ids.Get(2), 2);
|
||||||
texcoord1_id = DefineInput(vec_ids.Get(2), 3);
|
texcoord_id[1] = DefineInput(vec_ids.Get(2), 3);
|
||||||
texcoord2_id = DefineInput(vec_ids.Get(2), 4);
|
texcoord_id[2] = DefineInput(vec_ids.Get(2), 4);
|
||||||
texcoord0_w_id = DefineInput(f32_id, 5);
|
texcoord0_w_id = DefineInput(f32_id, 5);
|
||||||
normquat_id = DefineInput(vec_ids.Get(4), 6);
|
normquat_id = DefineInput(vec_ids.Get(4), 6);
|
||||||
view_id = DefineInput(vec_ids.Get(3), 7);
|
view_id = DefineInput(vec_ids.Get(3), 7);
|
||||||
|
@ -30,6 +30,8 @@ class FragmentModule : public Sirit::Module {
|
|||||||
static constexpr u32 NUM_TEV_STAGES = 6;
|
static constexpr u32 NUM_TEV_STAGES = 6;
|
||||||
static constexpr u32 NUM_LIGHTS = 8;
|
static constexpr u32 NUM_LIGHTS = 8;
|
||||||
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
||||||
|
static constexpr u32 NUM_TEX_UNITS = 4;
|
||||||
|
static constexpr u32 NUM_NON_PROC_TEX_UNITS = 3;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FragmentModule(Core::TelemetrySession& telemetry, const PicaFSConfig& config);
|
explicit FragmentModule(Core::TelemetrySession& telemetry, const PicaFSConfig& config);
|
||||||
@ -57,15 +59,15 @@ private:
|
|||||||
/// Writes the code to emulate the specified TEV stage
|
/// Writes the code to emulate the specified TEV stage
|
||||||
void WriteTevStage(s32 index);
|
void WriteTevStage(s32 index);
|
||||||
|
|
||||||
/// Defines the tex3 proctex sampling function
|
/// Defines the basic texture sampling functions for a unit
|
||||||
void DefineProcTexSampler();
|
void DefineTexSampler(u32 texture_unit);
|
||||||
|
|
||||||
|
/// Function for sampling the procedurally generated texture unit.
|
||||||
|
Id ProcTexSampler();
|
||||||
|
|
||||||
/// Writes the if-statement condition used to evaluate alpha testing.
|
/// Writes the if-statement condition used to evaluate alpha testing.
|
||||||
void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
||||||
|
|
||||||
/// Samples the current fragment texel from the provided texture unit
|
|
||||||
[[nodiscard]] Id SampleTexture(u32 texture_unit);
|
|
||||||
|
|
||||||
/// Samples the current fragment texel from shadow plane
|
/// Samples the current fragment texel from shadow plane
|
||||||
[[nodiscard]] Id SampleShadow();
|
[[nodiscard]] Id SampleShadow();
|
||||||
|
|
||||||
@ -237,9 +239,7 @@ private:
|
|||||||
Id shader_data_id{};
|
Id shader_data_id{};
|
||||||
|
|
||||||
Id primary_color_id{};
|
Id primary_color_id{};
|
||||||
Id texcoord0_id{};
|
Id texcoord_id[NUM_NON_PROC_TEX_UNITS]{};
|
||||||
Id texcoord1_id{};
|
|
||||||
Id texcoord2_id{};
|
|
||||||
Id texcoord0_w_id{};
|
Id texcoord0_w_id{};
|
||||||
Id normquat_id{};
|
Id normquat_id{};
|
||||||
Id view_id{};
|
Id view_id{};
|
||||||
@ -276,7 +276,7 @@ private:
|
|||||||
Id alpha_results_2{};
|
Id alpha_results_2{};
|
||||||
Id alpha_results_3{};
|
Id alpha_results_3{};
|
||||||
|
|
||||||
Id proctex_func{};
|
Id sample_tex_unit_func[NUM_TEX_UNITS]{};
|
||||||
Id noise1d_table{};
|
Id noise1d_table{};
|
||||||
Id noise2d_table{};
|
Id noise2d_table{};
|
||||||
Id lut_offsets{};
|
Id lut_offsets{};
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
#include <nihstro/shader_bytecode.h>
|
#include <nihstro/shader_bytecode.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@ -26,32 +27,64 @@ using nihstro::SwizzlePattern;
|
|||||||
|
|
||||||
namespace Pica::Shader {
|
namespace Pica::Shader {
|
||||||
|
|
||||||
|
struct IfStackElement {
|
||||||
|
u32 else_address;
|
||||||
|
u32 end_address;
|
||||||
|
};
|
||||||
|
|
||||||
struct CallStackElement {
|
struct CallStackElement {
|
||||||
u32 final_address; // Address upon which we jump to return_address
|
u32 end_address;
|
||||||
u32 return_address; // Where to jump when leaving scope
|
u32 return_address;
|
||||||
u8 repeat_counter; // How often to repeat until this call stack element is removed
|
};
|
||||||
u8 loop_increment; // Which value to add to the loop counter after an iteration
|
|
||||||
// TODO: Should this be a signed value? Does it even matter?
|
struct LoopStackElement {
|
||||||
u32 loop_address; // The address where we'll return to after each loop iteration
|
u32 entry_address;
|
||||||
|
u32 end_address;
|
||||||
|
u8 loop_downcounter;
|
||||||
|
u8 address_increment;
|
||||||
|
u8 previous_aL;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool Debug>
|
template <bool Debug>
|
||||||
static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data,
|
static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data,
|
||||||
unsigned offset) {
|
unsigned entry_point) {
|
||||||
// TODO: Is there a maximal size for this?
|
boost::circular_buffer<IfStackElement> if_stack(8);
|
||||||
boost::container::static_vector<CallStackElement, 16> call_stack;
|
boost::circular_buffer<CallStackElement> call_stack(4);
|
||||||
u32 program_counter = offset;
|
boost::circular_buffer<LoopStackElement> loop_stack(4);
|
||||||
|
u32 program_counter = entry_point;
|
||||||
|
|
||||||
state.conditional_code[0] = false;
|
state.conditional_code[0] = false;
|
||||||
state.conditional_code[1] = false;
|
state.conditional_code[1] = false;
|
||||||
|
|
||||||
auto call = [&program_counter, &call_stack](u32 offset, u32 num_instructions, u32 return_offset,
|
const auto do_if = [&](Instruction instr, bool condition) {
|
||||||
u8 repeat_count, u8 loop_increment) {
|
if (condition) {
|
||||||
// -1 to make sure when incrementing the PC we end up at the correct offset
|
if_stack.push_back({
|
||||||
program_counter = offset - 1;
|
.else_address = instr.flow_control.dest_offset,
|
||||||
ASSERT(call_stack.size() < call_stack.capacity());
|
.end_address = instr.flow_control.dest_offset + instr.flow_control.num_instructions,
|
||||||
call_stack.push_back(
|
});
|
||||||
{offset + num_instructions, return_offset, repeat_count, loop_increment, offset});
|
} else {
|
||||||
|
program_counter = instr.flow_control.dest_offset - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto do_call = [&](Instruction instr) {
|
||||||
|
call_stack.push_back({
|
||||||
|
.end_address = instr.flow_control.dest_offset + instr.flow_control.num_instructions,
|
||||||
|
.return_address = program_counter + 1,
|
||||||
|
});
|
||||||
|
program_counter = instr.flow_control.dest_offset - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto do_loop = [&](Instruction instr, const Common::Vec4<u8>& loop_param) {
|
||||||
|
const u8 previous_aL = static_cast<u8>(state.address_registers[2]);
|
||||||
|
loop_stack.push_back({
|
||||||
|
.entry_address = program_counter + 1,
|
||||||
|
.end_address = instr.flow_control.dest_offset + 1,
|
||||||
|
.loop_downcounter = loop_param.x,
|
||||||
|
.address_increment = loop_param.z,
|
||||||
|
.previous_aL = previous_aL,
|
||||||
|
});
|
||||||
|
state.address_registers[2] = loop_param.y;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto evaluate_condition = [&state](Instruction::FlowControlType flow_control) {
|
auto evaluate_condition = [&state](Instruction::FlowControlType flow_control) {
|
||||||
@ -82,25 +115,11 @@ static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData
|
|||||||
// Placeholder for invalid inputs
|
// Placeholder for invalid inputs
|
||||||
static f24 dummy_vec4_float24[4];
|
static f24 dummy_vec4_float24[4];
|
||||||
|
|
||||||
unsigned iteration = 0;
|
u32 iteration = 0;
|
||||||
bool exit_loop = false;
|
bool should_stop = false;
|
||||||
while (!exit_loop) {
|
while (!should_stop) {
|
||||||
if (!call_stack.empty()) {
|
bool is_break = false;
|
||||||
auto& top = call_stack.back();
|
const u32 old_program_counter = program_counter;
|
||||||
if (program_counter == top.final_address) {
|
|
||||||
state.address_registers[2] += top.loop_increment;
|
|
||||||
|
|
||||||
if (top.repeat_counter-- == 0) {
|
|
||||||
program_counter = top.return_address;
|
|
||||||
call_stack.pop_back();
|
|
||||||
} else {
|
|
||||||
program_counter = top.loop_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Is "trying again" accurate to hardware?
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Instruction instr = {program_code[program_counter]};
|
const Instruction instr = {program_code[program_counter]};
|
||||||
const SwizzlePattern swizzle = {swizzle_data[instr.common.operand_desc_id]};
|
const SwizzlePattern swizzle = {swizzle_data[instr.common.operand_desc_id]};
|
||||||
@ -538,7 +557,7 @@ static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData
|
|||||||
// Handle each instruction on its own
|
// Handle each instruction on its own
|
||||||
switch (instr.opcode.Value()) {
|
switch (instr.opcode.Value()) {
|
||||||
case OpCode::Id::END:
|
case OpCode::Id::END:
|
||||||
exit_loop = true;
|
should_stop = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::JMPC:
|
case OpCode::Id::JMPC:
|
||||||
@ -559,72 +578,68 @@ static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::CALL:
|
case OpCode::Id::CALL:
|
||||||
call(instr.flow_control.dest_offset, instr.flow_control.num_instructions,
|
do_call(instr);
|
||||||
program_counter + 1, 0, 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::CALLU:
|
case OpCode::Id::CALLU:
|
||||||
Record<DebugDataRecord::COND_BOOL_IN>(
|
Record<DebugDataRecord::COND_BOOL_IN>(
|
||||||
debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
|
debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
|
||||||
if (uniforms.b[instr.flow_control.bool_uniform_id]) {
|
if (uniforms.b[instr.flow_control.bool_uniform_id]) {
|
||||||
call(instr.flow_control.dest_offset, instr.flow_control.num_instructions,
|
do_call(instr);
|
||||||
program_counter + 1, 0, 0);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::CALLC:
|
case OpCode::Id::CALLC:
|
||||||
Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code);
|
Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code);
|
||||||
if (evaluate_condition(instr.flow_control)) {
|
if (evaluate_condition(instr.flow_control)) {
|
||||||
call(instr.flow_control.dest_offset, instr.flow_control.num_instructions,
|
do_call(instr);
|
||||||
program_counter + 1, 0, 0);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::NOP:
|
case OpCode::Id::NOP:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpCode::Id::IFU:
|
case OpCode::Id::IFU: {
|
||||||
Record<DebugDataRecord::COND_BOOL_IN>(
|
Record<DebugDataRecord::COND_BOOL_IN>(
|
||||||
debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
|
debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
|
||||||
if (uniforms.b[instr.flow_control.bool_uniform_id]) {
|
const bool cond = uniforms.b[instr.flow_control.bool_uniform_id];
|
||||||
call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1,
|
do_if(instr, cond);
|
||||||
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0,
|
|
||||||
0);
|
|
||||||
} else {
|
|
||||||
call(instr.flow_control.dest_offset, instr.flow_control.num_instructions,
|
|
||||||
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OpCode::Id::IFC: {
|
case OpCode::Id::IFC: {
|
||||||
// TODO: Do we need to consider swizzlers here?
|
// TODO: Do we need to consider swizzlers here?
|
||||||
|
|
||||||
Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code);
|
Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code);
|
||||||
if (evaluate_condition(instr.flow_control)) {
|
const bool cond = evaluate_condition(instr.flow_control);
|
||||||
call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1,
|
do_if(instr, cond);
|
||||||
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0,
|
|
||||||
0);
|
|
||||||
} else {
|
|
||||||
call(instr.flow_control.dest_offset, instr.flow_control.num_instructions,
|
|
||||||
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OpCode::Id::LOOP: {
|
case OpCode::Id::LOOP: {
|
||||||
Common::Vec4<u8> loop_param(uniforms.i[instr.flow_control.int_uniform_id].x,
|
const Common::Vec4<u8>& loop_param = uniforms.i[instr.flow_control.int_uniform_id];
|
||||||
uniforms.i[instr.flow_control.int_uniform_id].y,
|
|
||||||
uniforms.i[instr.flow_control.int_uniform_id].z,
|
|
||||||
uniforms.i[instr.flow_control.int_uniform_id].w);
|
|
||||||
state.address_registers[2] = loop_param.y;
|
state.address_registers[2] = loop_param.y;
|
||||||
|
|
||||||
Record<DebugDataRecord::LOOP_INT_IN>(debug_data, iteration, loop_param);
|
Record<DebugDataRecord::LOOP_INT_IN>(debug_data, iteration, loop_param);
|
||||||
call(program_counter + 1, instr.flow_control.dest_offset - program_counter,
|
do_loop(instr, loop_param);
|
||||||
instr.flow_control.dest_offset + 1, loop_param.x, loop_param.z);
|
Record<DebugDataRecord::ADDR_REG_OUT>(debug_data, iteration,
|
||||||
|
state.address_registers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpCode::Id::BREAK: {
|
||||||
|
is_break = true;
|
||||||
|
Record<DebugDataRecord::ADDR_REG_OUT>(debug_data, iteration,
|
||||||
|
state.address_registers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OpCode::Id::BREAKC: {
|
||||||
|
Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code);
|
||||||
|
if (evaluate_condition(instr.flow_control)) {
|
||||||
|
is_break = true;
|
||||||
|
}
|
||||||
|
Record<DebugDataRecord::ADDR_REG_OUT>(debug_data, iteration,
|
||||||
|
state.address_registers);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,6 +672,47 @@ static void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData
|
|||||||
|
|
||||||
++program_counter;
|
++program_counter;
|
||||||
++iteration;
|
++iteration;
|
||||||
|
|
||||||
|
// Stacks are checked in the order CALL -> IF -> LOOP. The CALL stack
|
||||||
|
// can be popped multiple times per instruction. A JMP at the end of a
|
||||||
|
// scope is never taken, this is why we compare against
|
||||||
|
// old_program_counter + 1 here.
|
||||||
|
u32 next_program_counter = old_program_counter + 1;
|
||||||
|
for (u32 i = 0; i < 4; i++) {
|
||||||
|
if (call_stack.empty() || call_stack.back().end_address != next_program_counter)
|
||||||
|
break;
|
||||||
|
// Hardware bug: when popping four CALL scopes at once, the last
|
||||||
|
// one doesn't update the program counter
|
||||||
|
if (i < 3) {
|
||||||
|
program_counter = call_stack.back().return_address;
|
||||||
|
next_program_counter = program_counter;
|
||||||
|
}
|
||||||
|
call_stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The other two stacks can only pop one entry per instruction. They
|
||||||
|
// are checked against the original program counter before any CALL
|
||||||
|
// scopes were closed and they overwrite any previous program counter
|
||||||
|
// updates.
|
||||||
|
if (!if_stack.empty() && if_stack.back().else_address == old_program_counter + 1) {
|
||||||
|
program_counter = if_stack.back().end_address;
|
||||||
|
if_stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loop_stack.empty() &&
|
||||||
|
(loop_stack.back().end_address == old_program_counter + 1 || is_break)) {
|
||||||
|
auto& loop = loop_stack.back();
|
||||||
|
state.address_registers[2] += loop.address_increment;
|
||||||
|
if (!is_break && loop.loop_downcounter--) {
|
||||||
|
program_counter = loop.entry_address;
|
||||||
|
} else {
|
||||||
|
program_counter = loop.end_address;
|
||||||
|
// Only restore previous value if there is a surrounding LOOP scope.
|
||||||
|
if (loop_stack.size() > 1)
|
||||||
|
state.address_registers[2] = loop.previous_aL;
|
||||||
|
loop_stack.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ layout ({}std140) uniform shader_data {{
|
|||||||
vec4 const_color[NUM_TEV_STAGES];
|
vec4 const_color[NUM_TEV_STAGES];
|
||||||
vec4 tev_combiner_buffer_color;
|
vec4 tev_combiner_buffer_color;
|
||||||
vec3 tex_lod_bias;
|
vec3 tex_lod_bias;
|
||||||
|
vec4 tex_border_color[3];
|
||||||
vec4 clip_coef;
|
vec4 clip_coef;
|
||||||
}};
|
}};
|
||||||
)";
|
)";
|
||||||
|
@ -64,10 +64,11 @@ struct UniformData {
|
|||||||
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
|
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
|
||||||
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
||||||
alignas(16) Common::Vec3f tex_lod_bias;
|
alignas(16) Common::Vec3f tex_lod_bias;
|
||||||
|
alignas(16) Common::Vec4f tex_border_color[3];
|
||||||
alignas(16) Common::Vec4f clip_coef;
|
alignas(16) Common::Vec4f clip_coef;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(UniformData) == 0x500,
|
static_assert(sizeof(UniformData) == 0x530,
|
||||||
"The size of the UniformData does not match the structure in the shader");
|
"The size of the UniformData does not match the structure in the shader");
|
||||||
static_assert(sizeof(UniformData) < 16384,
|
static_assert(sizeof(UniformData) < 16384,
|
||||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
add_library(web_service STATIC
|
add_library(web_service STATIC
|
||||||
announce_room_json.cpp
|
announce_room_json.cpp
|
||||||
announce_room_json.h
|
announce_room_json.h
|
||||||
nus_download.cpp
|
|
||||||
nus_download.h
|
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
telemetry_json.cpp
|
telemetry_json.cpp
|
||||||
telemetry_json.h
|
telemetry_json.h
|
||||||
@ -16,7 +14,7 @@ add_library(web_service STATIC
|
|||||||
|
|
||||||
create_target_directory_groups(web_service)
|
create_target_directory_groups(web_service)
|
||||||
|
|
||||||
target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_compile_definitions(web_service PUBLIC -DENABLE_WEB_SERVICE)
|
||||||
target_link_libraries(web_service PRIVATE citra_common network json-headers httplib cpp-jwt)
|
target_link_libraries(web_service PRIVATE citra_common network json-headers httplib cpp-jwt)
|
||||||
target_link_libraries(web_service PUBLIC ${OPENSSL_LIBS})
|
target_link_libraries(web_service PUBLIC ${OPENSSL_LIBS})
|
||||||
set_target_properties(web_service PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
set_target_properties(web_service PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
|
||||||
|
Loading…
Reference in New Issue
Block a user