Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bdb70ba692 | |||
| f9cfc2e5c6 | |||
| 413a0c2c03 | |||
| 944d2e4154 | |||
| b22892a306 | |||
| 4f0695024c | |||
| 9307ef73e6 | |||
| 8eac3c8fe4 | |||
| 43467dc141 | |||
| 388e1171d2 | |||
| aba560df7e | |||
| 2f8fb0f96d | |||
| b6b18d2a6b | |||
| b146b19b5e | |||
| d7c90f0c2a | |||
| 1c15c70fcd | |||
| 025af28583 | |||
| 33b925b2af | |||
| a69aad10c1 | |||
| e64875e15b | |||
| d1636b81f5 | |||
| da4faa90e8 | |||
| 7d88ae02e6 | |||
| 3769e1a66f | |||
| 5f0bb935c4 | |||
| 661d20f2e7 | |||
| 730abf80f4 | |||
| a471e87912 | |||
| f143d8cd37 | |||
| f39e11a183 | |||
| b20bc73334 | |||
| 5c5b6d8e04 | |||
| f5a27784c6 | |||
| f118d3ba48 | |||
| 3b26a03b27 | |||
| d725d957bb | |||
| d3986f1ed8 | |||
| b63f1faae0 | |||
| da0dae3d05 | |||
| 13b7ad86dc | |||
| 0fc8dbf6d2 | |||
| b0aca1e8ed | |||
| 25d139a4e1 | |||
| 7370b0c463 | |||
| 3692a01574 | |||
| a12b94569a | |||
| 82cdc93e25 | |||
| 462db514df | |||
| d17f6cceb7 | |||
| 2b22a91dee | |||
| d0b2e59b27 |
Regular → Executable
+1
-1
@@ -10,7 +10,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
|
||||
fi
|
||||
|
||||
# Default clang-format points to default 3.5 version one
|
||||
CLANG_FORMAT=clang-format-12
|
||||
CLANG_FORMAT=${CLANG_FORMAT:-clang-format-12}
|
||||
$CLANG_FORMAT --version
|
||||
|
||||
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
|
||||
|
||||
+24
-16
@@ -3,13 +3,8 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
# Dynarmic has cmake_minimum_required(3.12) and we may want to override
|
||||
# some of its variables, which is only possible in 3.13+
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/find-modules")
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
@@ -22,6 +17,8 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
|
||||
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
|
||||
|
||||
option(ENABLE_LIBUSB "Enable the use of LibUSB" ON)
|
||||
|
||||
option(ENABLE_OPENGL "Enable OpenGL" ON)
|
||||
mark_as_advanced(FORCE ENABLE_OPENGL)
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
@@ -35,6 +32,8 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
|
||||
|
||||
option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON)
|
||||
|
||||
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
@@ -47,6 +46,8 @@ option(YUZU_TESTS "Compile tests" ON)
|
||||
|
||||
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||
|
||||
option(YUZU_ROOM "Compile LDN room server" ON)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
||||
@@ -201,36 +202,43 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# System imported libraries
|
||||
# =======================================================================
|
||||
|
||||
find_package(enet 1.3)
|
||||
# Enforce the search mode of non-required packages for better and shorter failure messages
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(inih)
|
||||
find_package(libusb 1.0.24)
|
||||
find_package(inih MODULE)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3)
|
||||
find_package(Vulkan 1.3.238)
|
||||
find_package(Opus 1.3 MODULE)
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(Vulkan 1.3.238 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
find_package(libusb 1.0.24 MODULE)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
find_package(xbyak 6)
|
||||
find_package(xbyak 6 CONFIG)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
find_package(dynarmic 6.4.0)
|
||||
find_package(dynarmic 6.4.0 CONFIG)
|
||||
endif()
|
||||
|
||||
if (ENABLE_CUBEB)
|
||||
find_package(cubeb)
|
||||
find_package(cubeb CONFIG)
|
||||
endif()
|
||||
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
find_package(DiscordRPC)
|
||||
find_package(DiscordRPC MODULE)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
find_package(cpp-jwt 1.4)
|
||||
find_package(httplib 0.11)
|
||||
find_package(cpp-jwt 1.4 CONFIG)
|
||||
find_package(httplib 0.11 MODULE)
|
||||
endif()
|
||||
|
||||
if (YUZU_TESTS)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package(httplib QUIET CONFIG)
|
||||
if (httplib_FOUND)
|
||||
if (httplib_CONSIDERED_CONFIGS)
|
||||
find_package_handle_standard_args(httplib CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
@@ -4,7 +4,7 @@
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package(lz4 QUIET CONFIG)
|
||||
if (lz4_FOUND)
|
||||
if (lz4_CONSIDERED_CONFIGS)
|
||||
find_package_handle_standard_args(lz4 CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
@@ -4,7 +4,7 @@
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package(zstd QUIET CONFIG)
|
||||
if (zstd_FOUND)
|
||||
if (zstd_CONSIDERED_CONFIGS)
|
||||
find_package_handle_standard_args(zstd CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+733
-599
File diff suppressed because it is too large
Load Diff
Vendored
+757
-623
File diff suppressed because it is too large
Load Diff
Vendored
+733
-599
File diff suppressed because it is too large
Load Diff
Vendored
+736
-602
File diff suppressed because it is too large
Load Diff
Vendored
+740
-603
File diff suppressed because it is too large
Load Diff
Vendored
+733
-599
File diff suppressed because it is too large
Load Diff
Vendored
+782
-649
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+734
-601
File diff suppressed because it is too large
Load Diff
Vendored
+759
-625
File diff suppressed because it is too large
Load Diff
Vendored
+741
-607
File diff suppressed because it is too large
Load Diff
Vendored
+819
-682
File diff suppressed because it is too large
Load Diff
Vendored
+734
-600
File diff suppressed because it is too large
Load Diff
Vendored
+742
-604
File diff suppressed because it is too large
Load Diff
Vendored
+806
-669
File diff suppressed because it is too large
Load Diff
Vendored
+733
-599
File diff suppressed because it is too large
Load Diff
Vendored
+733
-599
File diff suppressed because it is too large
Load Diff
Vendored
+737
-600
File diff suppressed because it is too large
Load Diff
Vendored
+739
-602
File diff suppressed because it is too large
Load Diff
Vendored
+11
-12
@@ -1,9 +1,9 @@
|
||||
# SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules")
|
||||
include(DownloadExternals)
|
||||
# Dynarmic has cmake_minimum_required(3.12) and we may want to override
|
||||
# some of its variables, which is only possible in 3.13+
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
# xbyak
|
||||
if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
|
||||
@@ -12,8 +12,7 @@ endif()
|
||||
|
||||
# Dynarmic
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
|
||||
set(DYNARMIC_IGNORE_ASSERTS ON)
|
||||
add_subdirectory(dynarmic EXCLUDE_FROM_ALL)
|
||||
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
||||
endif()
|
||||
@@ -45,7 +44,7 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
if (NOT TARGET libusb::usb)
|
||||
if (ENABLE_LIBUSB AND NOT TARGET libusb::usb)
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
@@ -60,10 +59,10 @@ if (YUZU_USE_EXTERNAL_SDL2)
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
set(SDL_${_OPT} OFF)
|
||||
endforeach()
|
||||
|
||||
option(HIDAPI "" ON)
|
||||
set(HIDAPI ON)
|
||||
endif()
|
||||
set(SDL_STATIC ON)
|
||||
set(SDL_SHARED OFF)
|
||||
@@ -83,7 +82,7 @@ endif()
|
||||
|
||||
# Cubeb
|
||||
if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
set(BUILD_TESTS OFF)
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
add_library(cubeb::cubeb ALIAS cubeb)
|
||||
endif()
|
||||
@@ -98,6 +97,7 @@ endif()
|
||||
# Sirit
|
||||
add_subdirectory(sirit EXCLUDE_FROM_ALL)
|
||||
|
||||
# httplib
|
||||
if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
if (NOT WIN32)
|
||||
find_package(OpenSSL 1.1)
|
||||
@@ -108,7 +108,7 @@ if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
|
||||
if (WIN32 OR NOT OPENSSL_FOUND)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(LIBRESSL_SKIP_INSTALL ON)
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||
@@ -118,7 +118,6 @@ if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
||||
DEFINITION OPENSSL_LIBS)
|
||||
endif()
|
||||
|
||||
# httplib
|
||||
add_library(httplib INTERFACE)
|
||||
target_include_directories(httplib INTERFACE ./cpp-httplib)
|
||||
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
@@ -152,6 +151,6 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
endif()
|
||||
|
||||
# Vulkan-Headers
|
||||
if (NOT TARGET Vulkan::Headers)
|
||||
if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
+4
-1
@@ -161,7 +161,10 @@ add_subdirectory(video_core)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
add_subdirectory(dedicated_room)
|
||||
|
||||
if (YUZU_ROOM)
|
||||
add_subdirectory(dedicated_room)
|
||||
endif()
|
||||
|
||||
if (YUZU_TESTS)
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -187,11 +187,7 @@ add_library(audio_core STATIC
|
||||
renderer/voice/voice_info.cpp
|
||||
renderer/voice/voice_info.h
|
||||
renderer/voice/voice_state.h
|
||||
sink/cubeb_sink.cpp
|
||||
sink/cubeb_sink.h
|
||||
sink/null_sink.h
|
||||
sink/sdl2_sink.cpp
|
||||
sink/sdl2_sink.h
|
||||
sink/sink.h
|
||||
sink/sink_details.cpp
|
||||
sink/sink_details.h
|
||||
@@ -222,11 +218,22 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
|
||||
endif()
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
if (ENABLE_CUBEB)
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/cubeb_sink.cpp
|
||||
sink/cubeb_sink.h
|
||||
)
|
||||
|
||||
target_link_libraries(audio_core PRIVATE cubeb::cubeb)
|
||||
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
|
||||
endif()
|
||||
if(ENABLE_SDL2)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
target_sources(audio_core PRIVATE
|
||||
sink/sdl2_sink.cpp
|
||||
sink/sdl2_sink.h
|
||||
)
|
||||
|
||||
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
@@ -97,6 +97,7 @@ add_library(common STATIC
|
||||
point.h
|
||||
precompiled_headers.h
|
||||
quaternion.h
|
||||
range_map.h
|
||||
reader_writer_queue.h
|
||||
ring_buffer.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
||||
|
||||
@@ -393,12 +393,27 @@ public:
|
||||
}
|
||||
|
||||
// Virtual memory initialization
|
||||
virtual_base = static_cast<u8*>(
|
||||
mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
#if defined(__FreeBSD__)
|
||||
virtual_base =
|
||||
static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0));
|
||||
if (virtual_base == MAP_FAILED) {
|
||||
virtual_base = static_cast<u8*>(
|
||||
mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
if (virtual_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
#else
|
||||
virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
|
||||
if (virtual_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
||||
#endif
|
||||
|
||||
good = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename KeyTBase, typename ValueT>
|
||||
class RangeMap {
|
||||
private:
|
||||
using KeyT =
|
||||
std::conditional_t<std::is_signed_v<KeyTBase>, KeyTBase, std::make_signed_t<KeyTBase>>;
|
||||
|
||||
public:
|
||||
explicit RangeMap(ValueT null_value_) : null_value{null_value_} {
|
||||
container.emplace(std::numeric_limits<KeyT>::min(), null_value);
|
||||
};
|
||||
~RangeMap() = default;
|
||||
|
||||
void Map(KeyTBase address, KeyTBase address_end, ValueT value) {
|
||||
KeyT new_address = static_cast<KeyT>(address);
|
||||
KeyT new_address_end = static_cast<KeyT>(address_end);
|
||||
if (new_address < 0) {
|
||||
new_address = 0;
|
||||
}
|
||||
if (new_address_end < 0) {
|
||||
new_address_end = 0;
|
||||
}
|
||||
InternalMap(new_address, new_address_end, value);
|
||||
}
|
||||
|
||||
void Unmap(KeyTBase address, KeyTBase address_end) {
|
||||
Map(address, address_end, null_value);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const {
|
||||
const KeyT new_address = static_cast<KeyT>(address);
|
||||
if (new_address < 0) {
|
||||
return 0;
|
||||
}
|
||||
return ContinousSizeInternal(new_address);
|
||||
}
|
||||
|
||||
[[nodiscard]] ValueT GetValueAt(KeyT address) const {
|
||||
const KeyT new_address = static_cast<KeyT>(address);
|
||||
if (new_address < 0) {
|
||||
return null_value;
|
||||
}
|
||||
return GetValueInternal(new_address);
|
||||
}
|
||||
|
||||
private:
|
||||
using MapType = std::map<KeyT, ValueT>;
|
||||
using IteratorType = typename MapType::iterator;
|
||||
using ConstIteratorType = typename MapType::const_iterator;
|
||||
|
||||
size_t ContinousSizeInternal(KeyT address) const {
|
||||
const auto it = GetFirstElementBeforeOrOn(address);
|
||||
if (it == container.end() || it->second == null_value) {
|
||||
return 0;
|
||||
}
|
||||
const auto it_end = std::next(it);
|
||||
if (it_end == container.end()) {
|
||||
return std::numeric_limits<KeyT>::max() - address;
|
||||
}
|
||||
return it_end->first - address;
|
||||
}
|
||||
|
||||
ValueT GetValueInternal(KeyT address) const {
|
||||
const auto it = GetFirstElementBeforeOrOn(address);
|
||||
if (it == container.end()) {
|
||||
return null_value;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ConstIteratorType GetFirstElementBeforeOrOn(KeyT address) const {
|
||||
auto it = container.lower_bound(address);
|
||||
if (it == container.begin()) {
|
||||
return it;
|
||||
}
|
||||
if (it != container.end() && (it->first == address)) {
|
||||
return it;
|
||||
}
|
||||
--it;
|
||||
return it;
|
||||
}
|
||||
|
||||
ValueT GetFirstValueWithin(KeyT address) {
|
||||
auto it = container.lower_bound(address);
|
||||
if (it == container.begin()) {
|
||||
return it->second;
|
||||
}
|
||||
if (it == container.end()) [[unlikely]] { // this would be a bug
|
||||
return null_value;
|
||||
}
|
||||
--it;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ValueT GetLastValueWithin(KeyT address) {
|
||||
auto it = container.upper_bound(address);
|
||||
if (it == container.end()) {
|
||||
return null_value;
|
||||
}
|
||||
if (it == container.begin()) [[unlikely]] { // this would be a bug
|
||||
return it->second;
|
||||
}
|
||||
--it;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void InternalMap(KeyT address, KeyT address_end, ValueT value) {
|
||||
const bool must_add_start = GetFirstValueWithin(address) != value;
|
||||
const ValueT last_value = GetLastValueWithin(address_end);
|
||||
const bool must_add_end = last_value != value;
|
||||
auto it = container.lower_bound(address);
|
||||
const auto it_end = container.upper_bound(address_end);
|
||||
while (it != it_end) {
|
||||
it = container.erase(it);
|
||||
}
|
||||
if (must_add_start) {
|
||||
container.emplace(address, value);
|
||||
}
|
||||
if (must_add_end) {
|
||||
container.emplace(address_end, last_value);
|
||||
}
|
||||
}
|
||||
|
||||
ValueT null_value;
|
||||
MapType container;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -531,6 +531,7 @@ struct Values {
|
||||
Setting<bool> reporting_services{false, "reporting_services"};
|
||||
Setting<bool> quest_flag{false, "quest_flag"};
|
||||
Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
|
||||
Setting<bool> disable_macro_hle{false, "disable_macro_hle"};
|
||||
Setting<bool> extended_logging{false, "extended_logging"};
|
||||
Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
|
||||
Setting<bool> use_auto_stub{false, "use_auto_stub"};
|
||||
|
||||
@@ -226,7 +226,6 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_buffer.h
|
||||
hle/kernel/k_page_heap.cpp
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_group.cpp
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
|
||||
@@ -194,9 +194,9 @@ std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset)
|
||||
|
||||
std::string VfsFile::GetFullPath() const {
|
||||
if (GetContainingDirectory() == nullptr)
|
||||
return "/" + GetName();
|
||||
return '/' + GetName();
|
||||
|
||||
return GetContainingDirectory()->GetFullPath() + "/" + GetName();
|
||||
return GetContainingDirectory()->GetFullPath() + '/' + GetName();
|
||||
}
|
||||
|
||||
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
@@ -435,7 +435,7 @@ std::string VfsDirectory::GetFullPath() const {
|
||||
if (IsRoot())
|
||||
return GetName();
|
||||
|
||||
return GetParentDirectory()->GetFullPath() + "/" + GetName();
|
||||
return GetParentDirectory()->GetFullPath() + '/' + GetName();
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::IsWritable() const {
|
||||
|
||||
@@ -40,6 +40,11 @@ void EmulatedConsole::SetTouchParams() {
|
||||
touch_params[index++] = std::move(touchscreen_param);
|
||||
}
|
||||
|
||||
if (Settings::values.touch_from_button_maps.empty()) {
|
||||
LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
namespace Core::HID {
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
constexpr s32 HID_TRIGGER_MAX = 0x7fff;
|
||||
// Use a common UUID for TAS and Virtual Gamepad
|
||||
constexpr Common::UUID TAS_UUID =
|
||||
Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
constexpr Common::UUID VIRTUAL_UUID =
|
||||
Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
|
||||
EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
|
||||
|
||||
@@ -348,10 +353,6 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
}
|
||||
|
||||
// Use a common UUID for TAS
|
||||
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
|
||||
// Register TAS devices. No need to force update
|
||||
for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
|
||||
if (!tas_button_devices[index]) {
|
||||
@@ -377,10 +378,6 @@ void EmulatedController::ReloadInput() {
|
||||
});
|
||||
}
|
||||
|
||||
// Use a common UUID for Virtual Gamepad
|
||||
static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{
|
||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
|
||||
// Register virtual devices. No need to force update
|
||||
for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
|
||||
if (!virtual_button_devices[index]) {
|
||||
@@ -780,7 +777,12 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
|
||||
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
||||
if (controller.stick_values[index].uuid != uuid) {
|
||||
if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
|
||||
const bool is_tas = uuid == TAS_UUID;
|
||||
if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
|
||||
return;
|
||||
}
|
||||
if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
|
||||
!stick_value.right) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
|
||||
auto& page_table = m_owner->PageTable();
|
||||
|
||||
// Construct the page group.
|
||||
m_page_group.emplace(kernel, page_table.GetBlockInfoManager());
|
||||
m_page_group = {};
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size))
|
||||
R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
|
||||
|
||||
// Clear the memory.
|
||||
for (const auto& block : *m_page_group) {
|
||||
for (const auto& block : m_page_group.Nodes()) {
|
||||
std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
|
||||
}
|
||||
|
||||
@@ -51,13 +51,12 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
|
||||
void KCodeMemory::Finalize() {
|
||||
// Unlock.
|
||||
if (!m_is_mapped && !m_is_owner_mapped) {
|
||||
const size_t size = m_page_group->GetNumPages() * PageSize;
|
||||
m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group);
|
||||
const size_t size = m_page_group.GetNumPages() * PageSize;
|
||||
m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
|
||||
}
|
||||
|
||||
// Close the page group.
|
||||
m_page_group->Close();
|
||||
m_page_group->Finalize();
|
||||
m_page_group = {};
|
||||
|
||||
// Close our reference to our owner.
|
||||
m_owner->Close();
|
||||
@@ -65,7 +64,7 @@ void KCodeMemory::Finalize() {
|
||||
|
||||
Result KCodeMemory::Map(VAddr address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
@@ -75,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
|
||||
|
||||
// Map the memory.
|
||||
R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
|
||||
address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
|
||||
address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
|
||||
|
||||
// Mark ourselves as mapped.
|
||||
m_is_mapped = true;
|
||||
@@ -85,13 +84,13 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
|
||||
|
||||
Result KCodeMemory::Unmap(VAddr address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Unmap the memory.
|
||||
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group,
|
||||
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
|
||||
KMemoryState::CodeOut));
|
||||
|
||||
// Mark ourselves as unmapped.
|
||||
@@ -102,7 +101,7 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
|
||||
|
||||
Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
@@ -126,7 +125,7 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
|
||||
|
||||
// Map the memory.
|
||||
R_TRY(
|
||||
m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm));
|
||||
m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
|
||||
|
||||
// Mark ourselves as mapped.
|
||||
m_is_owner_mapped = true;
|
||||
@@ -136,13 +135,13 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
|
||||
|
||||
Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Unmap the memory.
|
||||
R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode));
|
||||
R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
|
||||
|
||||
// Mark ourselves as unmapped.
|
||||
m_is_owner_mapped = false;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
@@ -51,11 +49,11 @@ public:
|
||||
return m_address;
|
||||
}
|
||||
size_t GetSize() const {
|
||||
return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
|
||||
return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<KPageGroup> m_page_group{};
|
||||
KPageGroup m_page_group{};
|
||||
KProcess* m_owner{};
|
||||
VAddr m_address{};
|
||||
KLightLock m_lock;
|
||||
|
||||
@@ -223,7 +223,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
|
||||
|
||||
// Ensure that we don't leave anything un-freed.
|
||||
ON_RESULT_FAILURE {
|
||||
for (const auto& it : *out) {
|
||||
for (const auto& it : out->Nodes()) {
|
||||
auto& manager = this->GetManager(it.GetAddress());
|
||||
const size_t node_num_pages = std::min<u64>(
|
||||
it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
|
||||
@@ -285,7 +285,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
|
||||
m_has_optimized_process[static_cast<size_t>(pool)], true));
|
||||
|
||||
// Open the first reference to the pages.
|
||||
for (const auto& block : *out) {
|
||||
for (const auto& block : out->Nodes()) {
|
||||
PAddr cur_address = block.GetAddress();
|
||||
size_t remaining_pages = block.GetNumPages();
|
||||
while (remaining_pages > 0) {
|
||||
@@ -335,7 +335,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
|
||||
// Perform optimized memory tracking, if we should.
|
||||
if (optimized) {
|
||||
// Iterate over the allocated blocks.
|
||||
for (const auto& block : *out) {
|
||||
for (const auto& block : out->Nodes()) {
|
||||
// Get the block extents.
|
||||
const PAddr block_address = block.GetAddress();
|
||||
const size_t block_pages = block.GetNumPages();
|
||||
@@ -391,7 +391,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
|
||||
}
|
||||
} else {
|
||||
// Set all the allocated memory.
|
||||
for (const auto& block : *out) {
|
||||
for (const auto& block : out->Nodes()) {
|
||||
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
|
||||
block.GetSize());
|
||||
}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_dynamic_resource_manager.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_group.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KPageGroup::Finalize() {
|
||||
KBlockInfo* cur = m_first_block;
|
||||
while (cur != nullptr) {
|
||||
KBlockInfo* next = cur->GetNext();
|
||||
m_manager->Free(cur);
|
||||
cur = next;
|
||||
}
|
||||
|
||||
m_first_block = nullptr;
|
||||
m_last_block = nullptr;
|
||||
}
|
||||
|
||||
void KPageGroup::CloseAndReset() {
|
||||
auto& mm = m_kernel.MemoryManager();
|
||||
|
||||
KBlockInfo* cur = m_first_block;
|
||||
while (cur != nullptr) {
|
||||
KBlockInfo* next = cur->GetNext();
|
||||
mm.Close(cur->GetAddress(), cur->GetNumPages());
|
||||
m_manager->Free(cur);
|
||||
cur = next;
|
||||
}
|
||||
|
||||
m_first_block = nullptr;
|
||||
m_last_block = nullptr;
|
||||
}
|
||||
|
||||
size_t KPageGroup::GetNumPages() const {
|
||||
size_t num_pages = 0;
|
||||
|
||||
for (const auto& it : *this) {
|
||||
num_pages += it.GetNumPages();
|
||||
}
|
||||
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) {
|
||||
// Succeed immediately if we're adding no pages.
|
||||
R_SUCCEED_IF(num_pages == 0);
|
||||
|
||||
// Check for overflow.
|
||||
ASSERT(addr < addr + num_pages * PageSize);
|
||||
|
||||
// Try to just append to the last block.
|
||||
if (m_last_block != nullptr) {
|
||||
R_SUCCEED_IF(m_last_block->TryConcatenate(addr, num_pages));
|
||||
}
|
||||
|
||||
// Allocate a new block.
|
||||
KBlockInfo* new_block = m_manager->Allocate();
|
||||
R_UNLESS(new_block != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the block.
|
||||
new_block->Initialize(addr, num_pages);
|
||||
|
||||
// Add the block to our list.
|
||||
if (m_last_block != nullptr) {
|
||||
m_last_block->SetNext(new_block);
|
||||
} else {
|
||||
m_first_block = new_block;
|
||||
}
|
||||
m_last_block = new_block;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KPageGroup::Open() const {
|
||||
auto& mm = m_kernel.MemoryManager();
|
||||
|
||||
for (const auto& it : *this) {
|
||||
mm.Open(it.GetAddress(), it.GetNumPages());
|
||||
}
|
||||
}
|
||||
|
||||
void KPageGroup::OpenFirst() const {
|
||||
auto& mm = m_kernel.MemoryManager();
|
||||
|
||||
for (const auto& it : *this) {
|
||||
mm.OpenFirst(it.GetAddress(), it.GetNumPages());
|
||||
}
|
||||
}
|
||||
|
||||
void KPageGroup::Close() const {
|
||||
auto& mm = m_kernel.MemoryManager();
|
||||
|
||||
for (const auto& it : *this) {
|
||||
mm.Close(it.GetAddress(), it.GetNumPages());
|
||||
}
|
||||
}
|
||||
|
||||
bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const {
|
||||
auto lit = this->begin();
|
||||
auto rit = rhs.begin();
|
||||
auto lend = this->end();
|
||||
auto rend = rhs.end();
|
||||
|
||||
while (lit != lend && rit != rend) {
|
||||
if (*lit != *rit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++lit;
|
||||
++rit;
|
||||
}
|
||||
|
||||
return lit == lend && rit == rend;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -13,23 +13,24 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KBlockInfoManager;
|
||||
class KernelCore;
|
||||
class KPageGroup;
|
||||
|
||||
class KBlockInfo {
|
||||
public:
|
||||
constexpr explicit KBlockInfo() : m_next(nullptr) {}
|
||||
private:
|
||||
friend class KPageGroup;
|
||||
|
||||
constexpr void Initialize(KPhysicalAddress addr, size_t np) {
|
||||
public:
|
||||
constexpr KBlockInfo() = default;
|
||||
|
||||
constexpr void Initialize(PAddr addr, size_t np) {
|
||||
ASSERT(Common::IsAligned(addr, PageSize));
|
||||
ASSERT(static_cast<u32>(np) == np);
|
||||
|
||||
m_page_index = static_cast<u32>(addr / PageSize);
|
||||
m_page_index = static_cast<u32>(addr) / PageSize;
|
||||
m_num_pages = static_cast<u32>(np);
|
||||
}
|
||||
|
||||
constexpr KPhysicalAddress GetAddress() const {
|
||||
constexpr PAddr GetAddress() const {
|
||||
return m_page_index * PageSize;
|
||||
}
|
||||
constexpr size_t GetNumPages() const {
|
||||
@@ -38,10 +39,10 @@ public:
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetNumPages() * PageSize;
|
||||
}
|
||||
constexpr KPhysicalAddress GetEndAddress() const {
|
||||
constexpr PAddr GetEndAddress() const {
|
||||
return (m_page_index + m_num_pages) * PageSize;
|
||||
}
|
||||
constexpr KPhysicalAddress GetLastAddress() const {
|
||||
constexpr PAddr GetLastAddress() const {
|
||||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
@@ -61,8 +62,8 @@ public:
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr bool IsStrictlyBefore(KPhysicalAddress addr) const {
|
||||
const KPhysicalAddress end = this->GetEndAddress();
|
||||
constexpr bool IsStrictlyBefore(PAddr addr) const {
|
||||
const PAddr end = this->GetEndAddress();
|
||||
|
||||
if (m_page_index != 0 && end == 0) {
|
||||
return false;
|
||||
@@ -71,11 +72,11 @@ public:
|
||||
return end < addr;
|
||||
}
|
||||
|
||||
constexpr bool operator<(KPhysicalAddress addr) const {
|
||||
constexpr bool operator<(PAddr addr) const {
|
||||
return this->IsStrictlyBefore(addr);
|
||||
}
|
||||
|
||||
constexpr bool TryConcatenate(KPhysicalAddress addr, size_t np) {
|
||||
constexpr bool TryConcatenate(PAddr addr, size_t np) {
|
||||
if (addr != 0 && addr == this->GetEndAddress()) {
|
||||
m_num_pages += static_cast<u32>(np);
|
||||
return true;
|
||||
@@ -89,118 +90,96 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class KPageGroup;
|
||||
|
||||
KBlockInfo* m_next{};
|
||||
u32 m_page_index{};
|
||||
u32 m_num_pages{};
|
||||
};
|
||||
static_assert(sizeof(KBlockInfo) <= 0x10);
|
||||
|
||||
class KPageGroup {
|
||||
class KPageGroup final {
|
||||
public:
|
||||
class Iterator {
|
||||
class Node final {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const KBlockInfo;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {}
|
||||
|
||||
constexpr explicit Iterator(pointer n) : m_node(n) {}
|
||||
|
||||
constexpr bool operator==(const Iterator& rhs) const {
|
||||
return m_node == rhs.m_node;
|
||||
}
|
||||
constexpr bool operator!=(const Iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
constexpr u64 GetAddress() const {
|
||||
return addr;
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const {
|
||||
return m_node;
|
||||
}
|
||||
constexpr reference operator*() const {
|
||||
return *m_node;
|
||||
constexpr std::size_t GetNumPages() const {
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
constexpr Iterator& operator++() {
|
||||
m_node = m_node->GetNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++(*this);
|
||||
return it;
|
||||
constexpr std::size_t GetSize() const {
|
||||
return GetNumPages() * PageSize;
|
||||
}
|
||||
|
||||
private:
|
||||
pointer m_node{};
|
||||
u64 addr{};
|
||||
std::size_t num_pages{};
|
||||
};
|
||||
|
||||
explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m)
|
||||
: m_kernel{kernel}, m_manager{m} {}
|
||||
~KPageGroup() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
void CloseAndReset();
|
||||
void Finalize();
|
||||
|
||||
Iterator begin() const {
|
||||
return Iterator{m_first_block};
|
||||
}
|
||||
Iterator end() const {
|
||||
return Iterator{nullptr};
|
||||
}
|
||||
bool empty() const {
|
||||
return m_first_block == nullptr;
|
||||
}
|
||||
|
||||
Result AddBlock(KPhysicalAddress addr, size_t num_pages);
|
||||
void Open() const;
|
||||
void OpenFirst() const;
|
||||
void Close() const;
|
||||
|
||||
size_t GetNumPages() const;
|
||||
|
||||
bool IsEquivalentTo(const KPageGroup& rhs) const;
|
||||
|
||||
bool operator==(const KPageGroup& rhs) const {
|
||||
return this->IsEquivalentTo(rhs);
|
||||
}
|
||||
|
||||
bool operator!=(const KPageGroup& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
KernelCore& m_kernel;
|
||||
KBlockInfo* m_first_block{};
|
||||
KBlockInfo* m_last_block{};
|
||||
KBlockInfoManager* m_manager{};
|
||||
};
|
||||
|
||||
class KScopedPageGroup {
|
||||
public:
|
||||
explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
|
||||
if (m_pg) {
|
||||
m_pg->Open();
|
||||
}
|
||||
}
|
||||
explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
|
||||
~KScopedPageGroup() {
|
||||
if (m_pg) {
|
||||
m_pg->Close();
|
||||
}
|
||||
KPageGroup() = default;
|
||||
KPageGroup(u64 address, u64 num_pages) {
|
||||
ASSERT(AddBlock(address, num_pages).IsSuccess());
|
||||
}
|
||||
|
||||
void CancelClose() {
|
||||
m_pg = nullptr;
|
||||
constexpr std::list<Node>& Nodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
constexpr const std::list<Node>& Nodes() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
std::size_t GetNumPages() const {
|
||||
std::size_t num_pages = 0;
|
||||
for (const Node& node : nodes) {
|
||||
num_pages += node.GetNumPages();
|
||||
}
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
bool IsEqual(KPageGroup& other) const {
|
||||
auto this_node = nodes.begin();
|
||||
auto other_node = other.nodes.begin();
|
||||
while (this_node != nodes.end() && other_node != other.nodes.end()) {
|
||||
if (this_node->GetAddress() != other_node->GetAddress() ||
|
||||
this_node->GetNumPages() != other_node->GetNumPages()) {
|
||||
return false;
|
||||
}
|
||||
this_node = std::next(this_node);
|
||||
other_node = std::next(other_node);
|
||||
}
|
||||
|
||||
return this_node == nodes.end() && other_node == other.nodes.end();
|
||||
}
|
||||
|
||||
Result AddBlock(u64 address, u64 num_pages) {
|
||||
if (!num_pages) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (!nodes.empty()) {
|
||||
const auto node = nodes.back();
|
||||
if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
|
||||
address = node.GetAddress();
|
||||
num_pages += node.GetNumPages();
|
||||
nodes.pop_back();
|
||||
}
|
||||
}
|
||||
nodes.push_back({address, num_pages});
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return nodes.empty();
|
||||
}
|
||||
|
||||
void Finalize() {}
|
||||
|
||||
private:
|
||||
const KPageGroup* m_pg{};
|
||||
std::list<Node> nodes;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -100,7 +100,7 @@ constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType a
|
||||
|
||||
KPageTable::KPageTable(Core::System& system_)
|
||||
: m_general_lock{system_.Kernel()},
|
||||
m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
|
||||
m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {}
|
||||
|
||||
KPageTable::~KPageTable() = default;
|
||||
|
||||
@@ -373,7 +373,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta
|
||||
m_memory_block_slab_manager);
|
||||
|
||||
// Allocate and open.
|
||||
KPageGroup pg{m_kernel, m_block_info_manager};
|
||||
KPageGroup pg;
|
||||
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
|
||||
&pg, num_pages,
|
||||
KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
|
||||
@@ -432,7 +432,7 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Create page groups for the memory being mapped.
|
||||
KPageGroup pg{m_kernel, m_block_info_manager};
|
||||
KPageGroup pg;
|
||||
AddRegionToPages(src_address, num_pages, pg);
|
||||
|
||||
// Reprotect the source as kernel-read/not mapped.
|
||||
@@ -593,7 +593,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
|
||||
const size_t size = num_pages * PageSize;
|
||||
|
||||
// We're making a new group, not adding to an existing one.
|
||||
R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
|
||||
|
||||
// Begin traversal.
|
||||
Common::PageTable::TraversalContext context;
|
||||
@@ -640,10 +640,11 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) {
|
||||
bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
const size_t size = num_pages * PageSize;
|
||||
const auto& pg = pg_ll.Nodes();
|
||||
const auto& memory_layout = m_system.Kernel().MemoryLayout();
|
||||
|
||||
// Empty groups are necessarily invalid.
|
||||
@@ -941,6 +942,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
|
||||
|
||||
ON_RESULT_FAILURE {
|
||||
if (cur_mapped_addr != dst_addr) {
|
||||
// HACK: Manually close the pages.
|
||||
HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize);
|
||||
|
||||
ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
|
||||
KMemoryPermission::None, OperationType::Unmap)
|
||||
.IsSuccess());
|
||||
@@ -1016,6 +1020,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
|
||||
// Map the page.
|
||||
R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
|
||||
|
||||
// HACK: Manually open the pages.
|
||||
HACK_OpenPages(start_partial_page, 1);
|
||||
|
||||
// Update tracking extents.
|
||||
cur_mapped_addr += PageSize;
|
||||
cur_block_addr += PageSize;
|
||||
@@ -1044,6 +1051,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
|
||||
R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
|
||||
cur_block_addr));
|
||||
|
||||
// HACK: Manually open the pages.
|
||||
HACK_OpenPages(cur_block_addr, cur_block_size / PageSize);
|
||||
|
||||
// Update tracking extents.
|
||||
cur_mapped_addr += cur_block_size;
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -1063,6 +1073,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
|
||||
R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
|
||||
cur_block_addr));
|
||||
|
||||
// HACK: Manually open the pages.
|
||||
HACK_OpenPages(cur_block_addr, last_block_size / PageSize);
|
||||
|
||||
// Update tracking extents.
|
||||
cur_mapped_addr += last_block_size;
|
||||
cur_block_addr += last_block_size;
|
||||
@@ -1094,6 +1107,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
|
||||
|
||||
// Map the page.
|
||||
R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
|
||||
|
||||
// HACK: Manually open the pages.
|
||||
HACK_OpenPages(end_partial_page, 1);
|
||||
}
|
||||
|
||||
// Update memory blocks to reflect our changes
|
||||
@@ -1195,6 +1211,9 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
|
||||
const size_t aligned_size = aligned_end - aligned_start;
|
||||
const size_t aligned_num_pages = aligned_size / PageSize;
|
||||
|
||||
// HACK: Manually close the pages.
|
||||
HACK_ClosePages(aligned_start, aligned_num_pages);
|
||||
|
||||
// Unmap the pages.
|
||||
R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||
|
||||
@@ -1482,6 +1501,17 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi
|
||||
}
|
||||
}
|
||||
|
||||
void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) {
|
||||
m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages);
|
||||
}
|
||||
|
||||
void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) {
|
||||
for (size_t index = 0; index < num_pages; ++index) {
|
||||
const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize));
|
||||
m_system.Kernel().MemoryManager().Close(paddr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
// Lock the physical memory lock.
|
||||
KScopedLightLock phys_lk(m_map_physical_memory_lock);
|
||||
@@ -1542,7 +1572,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate pages for the new memory.
|
||||
KPageGroup pg{m_kernel, m_block_info_manager};
|
||||
KPageGroup pg;
|
||||
R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
|
||||
&pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
|
||||
|
||||
@@ -1620,7 +1650,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
KScopedPageTableUpdater updater(this);
|
||||
|
||||
// Prepare to iterate over the memory.
|
||||
auto pg_it = pg.begin();
|
||||
auto pg_it = pg.Nodes().begin();
|
||||
PAddr pg_phys_addr = pg_it->GetAddress();
|
||||
size_t pg_pages = pg_it->GetNumPages();
|
||||
|
||||
@@ -1650,6 +1680,9 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
last_unmap_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// HACK: Manually close the pages.
|
||||
HACK_ClosePages(cur_address, cur_pages);
|
||||
|
||||
// Unmap.
|
||||
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap)
|
||||
@@ -1670,7 +1703,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
// Release any remaining unmapped memory.
|
||||
m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
|
||||
m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
|
||||
for (++pg_it; pg_it != pg.end(); ++pg_it) {
|
||||
for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) {
|
||||
m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
|
||||
pg_it->GetNumPages());
|
||||
m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
|
||||
@@ -1698,7 +1731,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
// Check if we're at the end of the physical block.
|
||||
if (pg_pages == 0) {
|
||||
// Ensure there are more pages to map.
|
||||
ASSERT(pg_it != pg.end());
|
||||
ASSERT(pg_it != pg.Nodes().end());
|
||||
|
||||
// Advance our physical block.
|
||||
++pg_it;
|
||||
@@ -1709,7 +1742,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
|
||||
// Map whatever we can.
|
||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
||||
OperationType::MapFirst, pg_phys_addr));
|
||||
OperationType::Map, pg_phys_addr));
|
||||
|
||||
// HACK: Manually open the pages.
|
||||
HACK_OpenPages(pg_phys_addr, cur_pages);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
@@ -1852,6 +1888,9 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
|
||||
last_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// HACK: Manually close the pages.
|
||||
HACK_ClosePages(cur_address, cur_pages);
|
||||
|
||||
// Unmap.
|
||||
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
|
||||
.IsSuccess());
|
||||
@@ -1916,7 +1955,7 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
|
||||
R_TRY(dst_allocator_result);
|
||||
|
||||
// Map the memory.
|
||||
KPageGroup page_linked_list{m_kernel, m_block_info_manager};
|
||||
KPageGroup page_linked_list;
|
||||
const size_t num_pages{size / PageSize};
|
||||
const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
|
||||
KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
|
||||
@@ -1983,14 +2022,14 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
|
||||
num_dst_allocator_blocks);
|
||||
R_TRY(dst_allocator_result);
|
||||
|
||||
KPageGroup src_pages{m_kernel, m_block_info_manager};
|
||||
KPageGroup dst_pages{m_kernel, m_block_info_manager};
|
||||
KPageGroup src_pages;
|
||||
KPageGroup dst_pages;
|
||||
const size_t num_pages{size / PageSize};
|
||||
|
||||
AddRegionToPages(src_address, num_pages, src_pages);
|
||||
AddRegionToPages(dst_address, num_pages, dst_pages);
|
||||
|
||||
R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion);
|
||||
R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion);
|
||||
|
||||
{
|
||||
auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); });
|
||||
@@ -2021,7 +2060,7 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
|
||||
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list) {
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
if (const auto result{
|
||||
Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
|
||||
result.IsError()) {
|
||||
@@ -2121,7 +2160,7 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
|
||||
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list) {
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
|
||||
OperationType::Unmap)};
|
||||
result.IsError()) {
|
||||
@@ -2488,13 +2527,13 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate pages for the heap extension.
|
||||
KPageGroup pg{m_kernel, m_block_info_manager};
|
||||
KPageGroup pg;
|
||||
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
|
||||
&pg, allocation_size / PageSize,
|
||||
KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (const auto& it : pg) {
|
||||
for (const auto& it : pg.Nodes()) {
|
||||
std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
|
||||
it.GetSize());
|
||||
}
|
||||
@@ -2571,23 +2610,11 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_
|
||||
if (is_map_only) {
|
||||
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
|
||||
} else {
|
||||
// Create a page group tohold the pages we allocate.
|
||||
KPageGroup pg{m_kernel, m_block_info_manager};
|
||||
|
||||
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
|
||||
&pg, needed_num_pages,
|
||||
KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
|
||||
|
||||
// Ensure that the page group is closed when we're done working with it.
|
||||
SCOPE_EXIT({ pg.Close(); });
|
||||
|
||||
// Clear all pages.
|
||||
for (const auto& it : pg) {
|
||||
std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()),
|
||||
m_heap_fill_value, it.GetSize());
|
||||
}
|
||||
|
||||
R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup));
|
||||
KPageGroup page_group;
|
||||
R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
|
||||
&page_group, needed_num_pages,
|
||||
KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
|
||||
R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
|
||||
}
|
||||
|
||||
// Update the blocks.
|
||||
@@ -2768,28 +2795,19 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
|
||||
ASSERT(num_pages > 0);
|
||||
ASSERT(num_pages == page_group.GetNumPages());
|
||||
|
||||
switch (operation) {
|
||||
case OperationType::MapGroup: {
|
||||
// We want to maintain a new reference to every page in the group.
|
||||
KScopedPageGroup spg(page_group);
|
||||
for (const auto& node : page_group.Nodes()) {
|
||||
const size_t size{node.GetNumPages() * PageSize};
|
||||
|
||||
for (const auto& node : page_group) {
|
||||
const size_t size{node.GetNumPages() * PageSize};
|
||||
|
||||
// Map the pages.
|
||||
switch (operation) {
|
||||
case OperationType::MapGroup:
|
||||
m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
|
||||
|
||||
addr += size;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// We succeeded! We want to persist the reference to the pages.
|
||||
spg.CancelClose();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
addr += size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -2804,29 +2822,13 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
|
||||
ASSERT(ContainsPages(addr, num_pages));
|
||||
|
||||
switch (operation) {
|
||||
case OperationType::Unmap: {
|
||||
// Ensure that any pages we track close on exit.
|
||||
KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
|
||||
SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
|
||||
|
||||
this->AddRegionToPages(addr, num_pages, pages_to_close);
|
||||
case OperationType::Unmap:
|
||||
m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
||||
break;
|
||||
}
|
||||
case OperationType::MapFirst:
|
||||
case OperationType::Map: {
|
||||
ASSERT(map_addr);
|
||||
ASSERT(Common::IsAligned(map_addr, PageSize));
|
||||
m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
|
||||
|
||||
// Open references to pages, if we should.
|
||||
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
||||
if (operation == OperationType::MapFirst) {
|
||||
m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
|
||||
} else {
|
||||
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationType::Separate: {
|
||||
|
||||
@@ -107,10 +107,6 @@ public:
|
||||
return *m_page_table_impl;
|
||||
}
|
||||
|
||||
KBlockInfoManager* GetBlockInfoManager() {
|
||||
return m_block_info_manager;
|
||||
}
|
||||
|
||||
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
|
||||
|
||||
protected:
|
||||
@@ -265,6 +261,10 @@ private:
|
||||
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
|
||||
size_t size, KMemoryPermission prot_perm);
|
||||
|
||||
// HACK: These will be removed once we automatically manage page reference counts.
|
||||
void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
|
||||
void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
|
||||
|
||||
mutable KLightLock m_general_lock;
|
||||
mutable KLightLock m_map_physical_memory_lock;
|
||||
|
||||
@@ -488,7 +488,6 @@ private:
|
||||
std::unique_ptr<Common::PageTable> m_page_table_impl;
|
||||
|
||||
Core::System& m_system;
|
||||
KernelCore& m_kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
namespace Kernel {
|
||||
|
||||
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
KSharedMemory::~KSharedMemory() = default;
|
||||
|
||||
KSharedMemory::~KSharedMemory() {
|
||||
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
|
||||
}
|
||||
|
||||
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
|
||||
Svc::MemoryPermission owner_permission_,
|
||||
@@ -46,8 +49,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
|
||||
R_UNLESS(physical_address != 0, ResultOutOfMemory);
|
||||
|
||||
//! Insert the result into our page group.
|
||||
page_group.emplace(kernel, &kernel.GetSystemSystemResource().GetBlockInfoManager());
|
||||
page_group->AddBlock(physical_address, num_pages);
|
||||
page_group.emplace(physical_address, num_pages);
|
||||
|
||||
// Commit our reservation.
|
||||
memory_reservation.Commit();
|
||||
@@ -60,7 +62,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
|
||||
is_initialized = true;
|
||||
|
||||
// Clear all pages in the memory.
|
||||
for (const auto& block : *page_group) {
|
||||
for (const auto& block : page_group->Nodes()) {
|
||||
std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize());
|
||||
}
|
||||
|
||||
@@ -69,8 +71,13 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
|
||||
|
||||
void KSharedMemory::Finalize() {
|
||||
// Close and finalize the page group.
|
||||
page_group->Close();
|
||||
page_group->Finalize();
|
||||
// page_group->Close();
|
||||
// page_group->Finalize();
|
||||
|
||||
//! HACK: Manually close.
|
||||
for (const auto& block : page_group->Nodes()) {
|
||||
kernel.MemoryManager().Close(block.GetAddress(), block.GetNumPages());
|
||||
}
|
||||
|
||||
// Release the memory reservation.
|
||||
resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
|
||||
|
||||
@@ -14,7 +14,4 @@ constexpr std::size_t PageSize{1 << PageBits};
|
||||
|
||||
using Page = std::array<u8, PageSize>;
|
||||
|
||||
using KPhysicalAddress = PAddr;
|
||||
using KProcessAddress = VAddr;
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1485,7 +1485,7 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p
|
||||
ResultInvalidMemoryRegion);
|
||||
|
||||
// Create a new page group.
|
||||
KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
|
||||
KPageGroup pg;
|
||||
R_TRY(src_pt.MakeAndOpenPageGroup(
|
||||
std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
|
||||
KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
|
||||
|
||||
@@ -22,15 +22,19 @@ namespace {
|
||||
|
||||
namespace Service::NIFM {
|
||||
|
||||
// This is nn::nifm::RequestState
|
||||
enum class RequestState : u32 {
|
||||
NotSubmitted = 1,
|
||||
Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
|
||||
Pending = 2,
|
||||
Connected = 3,
|
||||
Invalid = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
|
||||
OnHold = 2,
|
||||
Accepted = 3,
|
||||
Blocking = 4,
|
||||
};
|
||||
|
||||
enum class InternetConnectionType : u8 {
|
||||
WiFi = 1,
|
||||
// This is nn::nifm::NetworkInterfaceType
|
||||
enum class NetworkInterfaceType : u32 {
|
||||
Invalid = 0,
|
||||
WiFi_Ieee80211 = 1,
|
||||
Ethernet = 2,
|
||||
};
|
||||
|
||||
@@ -42,14 +46,23 @@ enum class InternetConnectionStatus : u8 {
|
||||
Connected,
|
||||
};
|
||||
|
||||
// This is nn::nifm::NetworkProfileType
|
||||
enum class NetworkProfileType : u32 {
|
||||
User,
|
||||
SsidList,
|
||||
Temporary,
|
||||
};
|
||||
|
||||
// This is nn::nifm::IpAddressSetting
|
||||
struct IpAddressSetting {
|
||||
bool is_automatic{};
|
||||
Network::IPv4Address current_address{};
|
||||
Network::IPv4Address ip_address{};
|
||||
Network::IPv4Address subnet_mask{};
|
||||
Network::IPv4Address gateway{};
|
||||
Network::IPv4Address default_gateway{};
|
||||
};
|
||||
static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size.");
|
||||
|
||||
// This is nn::nifm::DnsSetting
|
||||
struct DnsSetting {
|
||||
bool is_automatic{};
|
||||
Network::IPv4Address primary_dns{};
|
||||
@@ -57,18 +70,26 @@ struct DnsSetting {
|
||||
};
|
||||
static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size.");
|
||||
|
||||
// This is nn::nifm::AuthenticationSetting
|
||||
struct AuthenticationSetting {
|
||||
bool is_enabled{};
|
||||
std::array<char, 0x20> user{};
|
||||
std::array<char, 0x20> password{};
|
||||
};
|
||||
static_assert(sizeof(AuthenticationSetting) == 0x41, "AuthenticationSetting has incorrect size.");
|
||||
|
||||
// This is nn::nifm::ProxySetting
|
||||
struct ProxySetting {
|
||||
bool enabled{};
|
||||
bool is_enabled{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u16 port{};
|
||||
std::array<char, 0x64> proxy_server{};
|
||||
bool automatic_auth_enabled{};
|
||||
std::array<char, 0x20> user{};
|
||||
std::array<char, 0x20> password{};
|
||||
AuthenticationSetting authentication{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size.");
|
||||
|
||||
// This is nn::nifm::IpSettingData
|
||||
struct IpSettingData {
|
||||
IpAddressSetting ip_address_setting{};
|
||||
DnsSetting dns_setting{};
|
||||
@@ -101,6 +122,7 @@ static_assert(sizeof(NifmWirelessSettingData) == 0x70,
|
||||
"NifmWirelessSettingData has incorrect size.");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// This is nn::nifm::detail::sf::NetworkProfileData
|
||||
struct SfNetworkProfileData {
|
||||
IpSettingData ip_setting_data{};
|
||||
u128 uuid{};
|
||||
@@ -114,13 +136,14 @@ struct SfNetworkProfileData {
|
||||
};
|
||||
static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
|
||||
|
||||
// This is nn::nifm::NetworkProfileData
|
||||
struct NifmNetworkProfileData {
|
||||
u128 uuid{};
|
||||
std::array<char, 0x40> network_name{};
|
||||
u32 unknown_1{};
|
||||
u32 unknown_2{};
|
||||
u8 unknown_3{};
|
||||
u8 unknown_4{};
|
||||
NetworkProfileType network_profile_type{};
|
||||
NetworkInterfaceType network_interface_type{};
|
||||
bool is_auto_connect{};
|
||||
bool is_large_capacity{};
|
||||
INSERT_PADDING_BYTES(2);
|
||||
NifmWirelessSettingData wireless_setting_data{};
|
||||
IpSettingData ip_setting_data{};
|
||||
@@ -184,6 +207,7 @@ public:
|
||||
|
||||
event1 = CreateKEvent(service_context, "IRequest:Event1");
|
||||
event2 = CreateKEvent(service_context, "IRequest:Event2");
|
||||
state = RequestState::NotSubmitted;
|
||||
}
|
||||
|
||||
~IRequest() override {
|
||||
@@ -196,7 +220,7 @@ private:
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
if (state == RequestState::NotSubmitted) {
|
||||
UpdateState(RequestState::Pending);
|
||||
UpdateState(RequestState::OnHold);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -219,14 +243,14 @@ private:
|
||||
switch (state) {
|
||||
case RequestState::NotSubmitted:
|
||||
return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled;
|
||||
case RequestState::Pending:
|
||||
case RequestState::OnHold:
|
||||
if (has_connection) {
|
||||
UpdateState(RequestState::Connected);
|
||||
UpdateState(RequestState::Accepted);
|
||||
} else {
|
||||
UpdateState(RequestState::Error);
|
||||
UpdateState(RequestState::Invalid);
|
||||
}
|
||||
return ResultPendingConnection;
|
||||
case RequestState::Connected:
|
||||
case RequestState::Accepted:
|
||||
default:
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -338,9 +362,9 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
.ip_setting_data{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.ip_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
|
||||
.gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
.default_gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
@@ -348,12 +372,14 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
.secondary_dns{1, 0, 0, 1},
|
||||
},
|
||||
.proxy_setting{
|
||||
.enabled{false},
|
||||
.is_enabled{false},
|
||||
.port{},
|
||||
.proxy_server{},
|
||||
.automatic_auth_enabled{},
|
||||
.user{},
|
||||
.password{},
|
||||
.authentication{
|
||||
.is_enabled{},
|
||||
.user{},
|
||||
.password{},
|
||||
},
|
||||
},
|
||||
.mtu{1500},
|
||||
},
|
||||
@@ -370,7 +396,7 @@ void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
// When we're connected to a room, spoof the hosts IP address
|
||||
if (auto room_member = network.GetRoomMember().lock()) {
|
||||
if (room_member->IsConnected()) {
|
||||
network_profile_data.ip_setting_data.ip_address_setting.current_address =
|
||||
network_profile_data.ip_setting_data.ip_address_setting.ip_address =
|
||||
room_member->GetFakeIpAddress();
|
||||
}
|
||||
}
|
||||
@@ -444,9 +470,9 @@ void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
|
||||
return IpConfigInfo{
|
||||
.ip_address_setting{
|
||||
.is_automatic{true},
|
||||
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.ip_address{Network::TranslateIPv4(net_iface->ip_address)},
|
||||
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
|
||||
.gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
.default_gateway{Network::TranslateIPv4(net_iface->gateway)},
|
||||
},
|
||||
.dns_setting{
|
||||
.is_automatic{true},
|
||||
@@ -459,7 +485,7 @@ void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
|
||||
// When we're connected to a room, spoof the hosts IP address
|
||||
if (auto room_member = network.GetRoomMember().lock()) {
|
||||
if (room_member->IsConnected()) {
|
||||
ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
|
||||
ip_config_info.ip_address_setting.ip_address = room_member->GetFakeIpAddress();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +506,7 @@ void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
struct Output {
|
||||
InternetConnectionType type{InternetConnectionType::WiFi};
|
||||
u8 type{static_cast<u8>(NetworkInterfaceType::WiFi_Ieee80211)};
|
||||
u8 wifi_strength{3};
|
||||
InternetConnectionStatus state{InternetConnectionStatus::Connected};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
|
||||
|
||||
add_executable(yuzu-room
|
||||
precompiled_headers.h
|
||||
yuzu_room.cpp
|
||||
|
||||
@@ -4,14 +4,10 @@
|
||||
add_library(input_common STATIC
|
||||
drivers/camera.cpp
|
||||
drivers/camera.h
|
||||
drivers/gc_adapter.cpp
|
||||
drivers/gc_adapter.h
|
||||
drivers/keyboard.cpp
|
||||
drivers/keyboard.h
|
||||
drivers/mouse.cpp
|
||||
drivers/mouse.h
|
||||
drivers/sdl_driver.cpp
|
||||
drivers/sdl_driver.h
|
||||
drivers/tas_input.cpp
|
||||
drivers/tas_input.h
|
||||
drivers/touch_screen.cpp
|
||||
@@ -62,8 +58,17 @@ if (ENABLE_SDL2)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
target_sources(input_common PRIVATE
|
||||
drivers/gc_adapter.cpp
|
||||
drivers/gc_adapter.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE libusb::usb)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_LIBUSB)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
if (YUZU_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(input_common PRIVATE precompiled_headers.h)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/camera.h"
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
@@ -19,6 +18,10 @@
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
#include "input_common/drivers/sdl_driver.h"
|
||||
#endif
|
||||
@@ -45,7 +48,9 @@ struct InputSubsystem::Impl {
|
||||
RegisterEngine("keyboard", keyboard);
|
||||
RegisterEngine("mouse", mouse);
|
||||
RegisterEngine("touch", touch_screen);
|
||||
#ifdef HAVE_LIBUSB
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
#endif
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
RegisterEngine("tas", tas_input);
|
||||
RegisterEngine("camera", camera);
|
||||
@@ -72,7 +77,9 @@ struct InputSubsystem::Impl {
|
||||
UnregisterEngine(keyboard);
|
||||
UnregisterEngine(mouse);
|
||||
UnregisterEngine(touch_screen);
|
||||
#ifdef HAVE_LIBUSB
|
||||
UnregisterEngine(gcadapter);
|
||||
#endif
|
||||
UnregisterEngine(udp_client);
|
||||
UnregisterEngine(tas_input);
|
||||
UnregisterEngine(camera);
|
||||
@@ -95,8 +102,10 @@ struct InputSubsystem::Impl {
|
||||
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
|
||||
auto mouse_devices = mouse->GetInputDevices();
|
||||
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
|
||||
#ifdef HAVE_LIBUSB
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
#endif
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#ifdef HAVE_SDL2
|
||||
@@ -119,9 +128,11 @@ struct InputSubsystem::Impl {
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse;
|
||||
}
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter;
|
||||
}
|
||||
#endif
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client;
|
||||
}
|
||||
@@ -194,9 +205,11 @@ struct InputSubsystem::Impl {
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
@@ -217,7 +230,9 @@ struct InputSubsystem::Impl {
|
||||
void BeginConfiguration() {
|
||||
keyboard->BeginConfiguration();
|
||||
mouse->BeginConfiguration();
|
||||
#ifdef HAVE_LIBUSB
|
||||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
udp_client->BeginConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->BeginConfiguration();
|
||||
@@ -227,7 +242,9 @@ struct InputSubsystem::Impl {
|
||||
void EndConfiguration() {
|
||||
keyboard->EndConfiguration();
|
||||
mouse->EndConfiguration();
|
||||
#ifdef HAVE_LIBUSB
|
||||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
udp_client->EndConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->EndConfiguration();
|
||||
@@ -248,7 +265,6 @@ struct InputSubsystem::Impl {
|
||||
|
||||
std::shared_ptr<Keyboard> keyboard;
|
||||
std::shared_ptr<Mouse> mouse;
|
||||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
std::shared_ptr<TouchScreen> touch_screen;
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||
@@ -256,6 +272,10 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
std::shared_ptr<VirtualGamepad> virtual_gamepad;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
std::shared_ptr<SDLDriver> sdl;
|
||||
#endif
|
||||
|
||||
@@ -137,6 +137,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::BaseInstance:
|
||||
ctx.Add("MOV.F {}.x,{}.baseInstance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::BaseVertex:
|
||||
ctx.Add("MOV.F {}.x,{}.baseVertex;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::DrawID:
|
||||
ctx.Add("MOV.F {}.x,{}.draw.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
@@ -156,6 +165,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, S
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::BaseInstance:
|
||||
ctx.Add("MOV.S {}.x,{}.baseInstance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::BaseVertex:
|
||||
ctx.Add("MOV.S {}.x,{}.baseVertex;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::DrawID:
|
||||
ctx.Add("MOV.S {}.x,{}.draw.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
const std::string version{fmt::format("#version 460{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
|
||||
@@ -234,6 +234,15 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
|
||||
break;
|
||||
case IR::Attribute::BaseInstance:
|
||||
ctx.AddF32("{}=itof(gl_BaseInstance);", inst);
|
||||
break;
|
||||
case IR::Attribute::BaseVertex:
|
||||
ctx.AddF32("{}=itof(gl_BaseVertex);", inst);
|
||||
break;
|
||||
case IR::Attribute::DrawID:
|
||||
ctx.AddF32("{}=itof(gl_DrawID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
@@ -250,6 +259,15 @@ void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, s
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
case IR::Attribute::BaseInstance:
|
||||
ctx.AddU32("{}=uint(gl_BaseInstance);", inst);
|
||||
break;
|
||||
case IR::Attribute::BaseVertex:
|
||||
ctx.AddU32("{}=uint(gl_BaseVertex);", inst);
|
||||
break;
|
||||
case IR::Attribute::DrawID:
|
||||
ctx.AddU32("{}=uint(gl_DrawID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
|
||||
@@ -339,6 +339,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
|
||||
}
|
||||
case IR::Attribute::BaseInstance:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
|
||||
case IR::Attribute::BaseVertex:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_vertex));
|
||||
case IR::Attribute::DrawID:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.draw_index));
|
||||
case IR::Attribute::FrontFace:
|
||||
return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
|
||||
ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
|
||||
@@ -380,6 +386,12 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
case IR::Attribute::BaseInstance:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.base_instance);
|
||||
case IR::Attribute::BaseVertex:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.base_vertex);
|
||||
case IR::Attribute::DrawID:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.draw_index);
|
||||
default:
|
||||
throw NotImplementedException("Read U32 attribute {}", attr);
|
||||
}
|
||||
|
||||
@@ -1379,18 +1379,31 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (loads[IR::Attribute::InstanceId]) {
|
||||
if (profile.support_vertex_instance_id) {
|
||||
instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
|
||||
if (loads[IR::Attribute::BaseInstance]) {
|
||||
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
|
||||
}
|
||||
} else {
|
||||
instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
|
||||
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
|
||||
}
|
||||
} else if (loads[IR::Attribute::BaseInstance]) {
|
||||
base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
|
||||
}
|
||||
if (loads[IR::Attribute::VertexId]) {
|
||||
if (profile.support_vertex_instance_id) {
|
||||
vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
|
||||
if (loads[IR::Attribute::BaseVertex]) {
|
||||
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
|
||||
}
|
||||
} else {
|
||||
vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
|
||||
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
|
||||
}
|
||||
} else if (loads[IR::Attribute::BaseVertex]) {
|
||||
base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
|
||||
}
|
||||
if (loads[IR::Attribute::DrawID]) {
|
||||
draw_index = DefineInput(*this, U32[1], true, spv::BuiltIn::DrawIndex);
|
||||
}
|
||||
if (loads[IR::Attribute::FrontFace]) {
|
||||
front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
|
||||
|
||||
@@ -218,6 +218,7 @@ public:
|
||||
Id base_instance{};
|
||||
Id vertex_id{};
|
||||
Id vertex_index{};
|
||||
Id draw_index{};
|
||||
Id base_vertex{};
|
||||
Id front_face{};
|
||||
Id point_coord{};
|
||||
|
||||
@@ -34,6 +34,11 @@ public:
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool HasHLEMacroState() const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::optional<ReplaceConstant> GetReplaceConstBuffer(u32 bank,
|
||||
u32 offset) = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
@@ -52,11 +57,16 @@ public:
|
||||
return start_address;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPropietaryDriver() const noexcept {
|
||||
return is_propietary_driver;
|
||||
}
|
||||
|
||||
protected:
|
||||
ProgramHeader sph{};
|
||||
std::array<u32, 8> gp_passthrough_mask{};
|
||||
Stage stage{};
|
||||
u32 start_address{};
|
||||
bool is_propietary_driver{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -446,6 +446,12 @@ std::string NameOf(Attribute attribute) {
|
||||
return "ViewportMask";
|
||||
case Attribute::FrontFace:
|
||||
return "FrontFace";
|
||||
case Attribute::BaseInstance:
|
||||
return "BaseInstance";
|
||||
case Attribute::BaseVertex:
|
||||
return "BaseVertex";
|
||||
case Attribute::DrawID:
|
||||
return "DrawID";
|
||||
}
|
||||
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
|
||||
}
|
||||
|
||||
@@ -219,6 +219,11 @@ enum class Attribute : u64 {
|
||||
FixedFncTexture9Q = 231,
|
||||
ViewportMask = 232,
|
||||
FrontFace = 255,
|
||||
|
||||
// Implementation attributes
|
||||
BaseInstance = 256,
|
||||
BaseVertex = 257,
|
||||
DrawID = 258,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
@@ -294,6 +294,14 @@ F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) {
|
||||
return Inst<F32>(Opcode::GetAttribute, attribute, vertex);
|
||||
}
|
||||
|
||||
U32 IREmitter::GetAttributeU32(IR::Attribute attribute) {
|
||||
return GetAttributeU32(attribute, Imm32(0));
|
||||
}
|
||||
|
||||
U32 IREmitter::GetAttributeU32(IR::Attribute attribute, const U32& vertex) {
|
||||
return Inst<U32>(Opcode::GetAttributeU32, attribute, vertex);
|
||||
}
|
||||
|
||||
void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) {
|
||||
Inst(Opcode::SetAttribute, attribute, value, vertex);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
|
||||
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute);
|
||||
[[nodiscard]] U32 GetAttributeU32(IR::Attribute attribute, const U32& vertex);
|
||||
void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
|
||||
|
||||
@@ -219,7 +219,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
}
|
||||
Optimization::SsaRewritePass(program);
|
||||
|
||||
Optimization::ConstantPropagationPass(program);
|
||||
Optimization::ConstantPropagationPass(env, program);
|
||||
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
@@ -515,6 +516,9 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
case IR::Attribute::InstanceId:
|
||||
case IR::Attribute::VertexId:
|
||||
case IR::Attribute::BaseVertex:
|
||||
case IR::Attribute::BaseInstance:
|
||||
case IR::Attribute::DrawID:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -644,7 +648,63 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
||||
void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
const IR::Value bank{inst.Arg(0)};
|
||||
const IR::Value offset{inst.Arg(1)};
|
||||
if (!bank.IsImmediate() || !offset.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
const auto bank_value = bank.U32();
|
||||
const auto offset_value = offset.U32();
|
||||
auto replacement = env.GetReplaceConstBuffer(bank_value, offset_value);
|
||||
if (!replacement) {
|
||||
return;
|
||||
}
|
||||
const auto new_attribute = [replacement]() {
|
||||
switch (*replacement) {
|
||||
case ReplaceConstant::BaseInstance:
|
||||
return IR::Attribute::BaseInstance;
|
||||
case ReplaceConstant::BaseVertex:
|
||||
return IR::Attribute::BaseVertex;
|
||||
case ReplaceConstant::DrawID:
|
||||
return IR::Attribute::DrawID;
|
||||
default:
|
||||
throw NotImplementedException("Not implemented replacement variable {}", *replacement);
|
||||
}
|
||||
}();
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
|
||||
inst.ReplaceUsesWith(ir.GetAttributeU32(new_attribute));
|
||||
} else {
|
||||
inst.ReplaceUsesWith(ir.GetAttribute(new_attribute));
|
||||
}
|
||||
}
|
||||
|
||||
void FoldDriverConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst, u32 which_bank,
|
||||
u32 offset_start = 0, u32 offset_end = std::numeric_limits<u16>::max()) {
|
||||
const IR::Value bank{inst.Arg(0)};
|
||||
const IR::Value offset{inst.Arg(1)};
|
||||
if (!bank.IsImmediate() || !offset.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
const auto bank_value = bank.U32();
|
||||
if (bank_value != which_bank) {
|
||||
return;
|
||||
}
|
||||
const auto offset_value = offset.U32();
|
||||
if (offset_value < offset_start || offset_value >= offset_end) {
|
||||
return;
|
||||
}
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
if (inst.GetOpcode() == IR::Opcode::GetCbufU32) {
|
||||
inst.ReplaceUsesWith(IR::Value{env.ReadCbufValue(bank_value, offset_value)});
|
||||
} else {
|
||||
inst.ReplaceUsesWith(
|
||||
IR::Value{Common::BitCast<f32>(env.ReadCbufValue(bank_value, offset_value))});
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::GetRegister:
|
||||
return FoldGetRegister(inst);
|
||||
@@ -789,18 +849,28 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
||||
IR::Opcode::CompositeInsertF16x4);
|
||||
case IR::Opcode::FSwizzleAdd:
|
||||
return FoldFSwizzleAdd(block, inst);
|
||||
case IR::Opcode::GetCbufF32:
|
||||
case IR::Opcode::GetCbufU32:
|
||||
if (env.HasHLEMacroState()) {
|
||||
FoldConstBuffer(env, block, inst);
|
||||
}
|
||||
if (env.IsPropietaryDriver()) {
|
||||
FoldDriverConstBuffer(env, block, inst, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void ConstantPropagationPass(IR::Program& program) {
|
||||
void ConstantPropagationPass(Environment& env, IR::Program& program) {
|
||||
const auto end{program.post_order_blocks.rend()};
|
||||
for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
|
||||
IR::Block* const block{*it};
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
ConstantPropagation(*block, inst);
|
||||
ConstantPropagation(env, *block, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ struct HostTranslateInfo;
|
||||
namespace Shader::Optimization {
|
||||
|
||||
void CollectShaderInfoPass(Environment& env, IR::Program& program);
|
||||
void ConstantPropagationPass(IR::Program& program);
|
||||
void ConstantPropagationPass(Environment& env, IR::Program& program);
|
||||
void DeadCodeEliminationPass(IR::Program& program);
|
||||
void GlobalMemoryToStorageBufferPass(IR::Program& program);
|
||||
void IdentityRemovalPass(IR::Program& program);
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
namespace Shader {
|
||||
|
||||
enum class ReplaceConstant : u32 {
|
||||
BaseInstance,
|
||||
BaseVertex,
|
||||
DrawID,
|
||||
};
|
||||
|
||||
enum class TextureType : u32 {
|
||||
Color1D,
|
||||
ColorArray1D,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace Shader {
|
||||
|
||||
struct VaryingState {
|
||||
std::bitset<256> mask{};
|
||||
std::bitset<512> mask{};
|
||||
|
||||
void Set(IR::Attribute attribute, bool state = true) {
|
||||
mask[static_cast<size_t>(attribute)] = state;
|
||||
|
||||
@@ -7,6 +7,7 @@ add_executable(tests
|
||||
common/fibers.cpp
|
||||
common/host_memory.cpp
|
||||
common/param_package.cpp
|
||||
common/range_map.cpp
|
||||
common/ring_buffer.cpp
|
||||
common/scratch_buffer.cpp
|
||||
common/unique_function.cpp
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "common/range_map.h"
|
||||
|
||||
enum class MappedEnum : u32 {
|
||||
Invalid = 0,
|
||||
Valid_1 = 1,
|
||||
Valid_2 = 2,
|
||||
Valid_3 = 3,
|
||||
};
|
||||
|
||||
TEST_CASE("Range Map: Setup", "[video_core]") {
|
||||
Common::RangeMap<u64, MappedEnum> my_map(MappedEnum::Invalid);
|
||||
my_map.Map(3000, 3500, MappedEnum::Valid_1);
|
||||
my_map.Unmap(3200, 3600);
|
||||
my_map.Map(4000, 4500, MappedEnum::Valid_2);
|
||||
my_map.Map(4200, 4400, MappedEnum::Valid_2);
|
||||
my_map.Map(4200, 4400, MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
|
||||
|
||||
REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
|
||||
REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(3000) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(3200) == MappedEnum::Invalid);
|
||||
|
||||
REQUIRE(my_map.GetValueAt(4199) == MappedEnum::Valid_2);
|
||||
REQUIRE(my_map.GetValueAt(4200) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(4400) == MappedEnum::Valid_2);
|
||||
REQUIRE(my_map.GetValueAt(4500) == MappedEnum::Invalid);
|
||||
REQUIRE(my_map.GetValueAt(4600) == MappedEnum::Invalid);
|
||||
|
||||
my_map.Unmap(0, 6000);
|
||||
for (u64 address = 0; address < 10000; address += 1000) {
|
||||
REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
|
||||
}
|
||||
|
||||
my_map.Map(1000, 3000, MappedEnum::Valid_1);
|
||||
my_map.Map(4000, 5000, MappedEnum::Valid_1);
|
||||
my_map.Map(2500, 4100, MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
|
||||
|
||||
my_map.Map(1000, 3000, MappedEnum::Valid_1);
|
||||
my_map.Map(4000, 5000, MappedEnum::Valid_2);
|
||||
my_map.Map(2500, 4100, MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
|
||||
REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
|
||||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetValueAt(4100) == MappedEnum::Valid_2);
|
||||
REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
|
||||
|
||||
my_map.Map(2000, 6000, MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
|
||||
REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
|
||||
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
|
||||
REQUIRE(my_map.GetValueAt(2001) == MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetValueAt(5999) == MappedEnum::Valid_3);
|
||||
REQUIRE(my_map.GetValueAt(6000) == MappedEnum::Invalid);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ add_library(video_core STATIC
|
||||
buffer_cache/buffer_base.h
|
||||
buffer_cache/buffer_cache.cpp
|
||||
buffer_cache/buffer_cache.h
|
||||
cache_types.h
|
||||
cdma_pusher.cpp
|
||||
cdma_pusher.h
|
||||
compatible_formats.cpp
|
||||
@@ -98,6 +99,8 @@ add_library(video_core STATIC
|
||||
renderer_null/null_rasterizer.h
|
||||
renderer_null/renderer_null.cpp
|
||||
renderer_null/renderer_null.h
|
||||
renderer_opengl/blit_image.cpp
|
||||
renderer_opengl/blit_image.h
|
||||
renderer_opengl/gl_buffer_cache.cpp
|
||||
renderer_opengl/gl_buffer_cache.h
|
||||
renderer_opengl/gl_compute_pipeline.cpp
|
||||
|
||||
@@ -200,7 +200,16 @@ public:
|
||||
/// Return true when a CPU region is modified from the CPU
|
||||
[[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
|
||||
|
||||
std::mutex mutex;
|
||||
void SetDrawIndirect(
|
||||
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
|
||||
current_draw_indirect = current_draw_indirect_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
Runtime& runtime;
|
||||
|
||||
private:
|
||||
@@ -272,6 +281,8 @@ private:
|
||||
|
||||
void BindHostVertexBuffers();
|
||||
|
||||
void BindHostDrawIndirectBuffers();
|
||||
|
||||
void BindHostGraphicsUniformBuffers(size_t stage);
|
||||
|
||||
void BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind);
|
||||
@@ -298,6 +309,8 @@ private:
|
||||
|
||||
void UpdateVertexBuffer(u32 index);
|
||||
|
||||
void UpdateDrawIndirect();
|
||||
|
||||
void UpdateUniformBuffers(size_t stage);
|
||||
|
||||
void UpdateStorageBuffers(size_t stage);
|
||||
@@ -372,6 +385,8 @@ private:
|
||||
SlotVector<Buffer> slot_buffers;
|
||||
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
|
||||
|
||||
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
|
||||
|
||||
u32 last_index_count = 0;
|
||||
|
||||
Binding index_buffer;
|
||||
@@ -380,6 +395,8 @@ private:
|
||||
std::array<std::array<Binding, NUM_STORAGE_BUFFERS>, NUM_STAGES> storage_buffers;
|
||||
std::array<std::array<TextureBufferBinding, NUM_TEXTURE_BUFFERS>, NUM_STAGES> texture_buffers;
|
||||
std::array<Binding, NUM_TRANSFORM_FEEDBACK_BUFFERS> transform_feedback_buffers;
|
||||
Binding count_buffer_binding;
|
||||
Binding indirect_buffer_binding;
|
||||
|
||||
std::array<Binding, NUM_COMPUTE_UNIFORM_BUFFERS> compute_uniform_buffers;
|
||||
std::array<Binding, NUM_STORAGE_BUFFERS> compute_storage_buffers;
|
||||
@@ -674,6 +691,9 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
|
||||
}
|
||||
BindHostVertexBuffers();
|
||||
BindHostTransformFeedbackBuffers();
|
||||
if (current_draw_indirect) {
|
||||
BindHostDrawIndirectBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -823,6 +843,7 @@ bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
AccumulateFlushes();
|
||||
|
||||
if (committed_ranges.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -869,7 +890,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
buffer_id,
|
||||
});
|
||||
// Align up to avoid cache conflicts
|
||||
constexpr u64 align = 256ULL;
|
||||
constexpr u64 align = 8ULL;
|
||||
constexpr u64 mask = ~(align - 1ULL);
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = std::max(largest_copy, new_size);
|
||||
@@ -1041,6 +1062,19 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostDrawIndirectBuffers() {
|
||||
const auto bind_buffer = [this](const Binding& binding) {
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
|
||||
};
|
||||
if (current_draw_indirect->include_count) {
|
||||
bind_buffer(count_buffer_binding);
|
||||
}
|
||||
bind_buffer(indirect_buffer_binding);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
|
||||
u32 dirty = ~0U;
|
||||
@@ -1272,6 +1306,9 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
|
||||
UpdateStorageBuffers(stage);
|
||||
UpdateTextureBuffers(stage);
|
||||
}
|
||||
if (current_draw_indirect) {
|
||||
UpdateDrawIndirect();
|
||||
}
|
||||
} while (has_deleted_buffers);
|
||||
}
|
||||
|
||||
@@ -1289,7 +1326,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
const auto& index_array = draw_state.index_buffer;
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) {
|
||||
if (!flags[Dirty::IndexBuffer]) {
|
||||
return;
|
||||
}
|
||||
flags[Dirty::IndexBuffer] = false;
|
||||
@@ -1361,6 +1398,27 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
||||
};
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateDrawIndirect() {
|
||||
const auto update = [this](GPUVAddr gpu_addr, size_t size, Binding& binding) {
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
if (!cpu_addr) {
|
||||
binding = NULL_BINDING;
|
||||
return;
|
||||
}
|
||||
binding = Binding{
|
||||
.cpu_addr = *cpu_addr,
|
||||
.size = static_cast<u32>(size),
|
||||
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
|
||||
};
|
||||
};
|
||||
if (current_draw_indirect->include_count) {
|
||||
update(current_draw_indirect->count_start_address, sizeof(u32), count_buffer_binding);
|
||||
}
|
||||
update(current_draw_indirect->indirect_start_address, current_draw_indirect->buffer_size,
|
||||
indirect_buffer_binding);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
|
||||
ForEachEnabledBit(enabled_uniform_buffer_masks[stage], [&](u32 index) {
|
||||
@@ -1941,4 +1999,16 @@ bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index)
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectCount() {
|
||||
auto& buffer = slot_buffers[count_buffer_binding.buffer_id];
|
||||
return std::make_pair(&buffer, buffer.Offset(count_buffer_binding.cpu_addr));
|
||||
}
|
||||
|
||||
template <class P>
|
||||
std::pair<typename BufferCache<P>::Buffer*, u32> BufferCache<P>::GetDrawIndirectBuffer() {
|
||||
auto& buffer = slot_buffers[indirect_buffer_binding.buffer_id];
|
||||
return std::make_pair(&buffer, buffer.Offset(indirect_buffer_binding.cpu_addr));
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
enum class CacheType : u32 {
|
||||
None = 0,
|
||||
TextureCache = 1 << 0,
|
||||
QueryCache = 1 << 1,
|
||||
BufferCache = 1 << 2,
|
||||
ShaderCache = 1 << 3,
|
||||
NoTextureCache = QueryCache | BufferCache | ShaderCache,
|
||||
NoBufferCache = TextureCache | QueryCache | ShaderCache,
|
||||
NoQueryCache = TextureCache | BufferCache | ShaderCache,
|
||||
All = TextureCache | QueryCache | BufferCache | ShaderCache,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(CacheType)
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -61,7 +61,7 @@ bool DmaPusher::Step() {
|
||||
} else {
|
||||
const CommandListHeader command_list_header{
|
||||
command_list.command_lists[dma_pushbuffer_subindex++]};
|
||||
const GPUVAddr dma_get = command_list_header.addr;
|
||||
dma_state.dma_get = command_list_header.addr;
|
||||
|
||||
if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
|
||||
// We've gone through the current list, remove it from the queue
|
||||
@@ -75,12 +75,22 @@ bool DmaPusher::Step() {
|
||||
|
||||
// Push buffer non-empty, read a word
|
||||
command_headers.resize_destructive(command_list_header.size);
|
||||
if (Settings::IsGPULevelHigh()) {
|
||||
memory_manager.ReadBlock(dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
if (dma_state.method < MacroRegistersStart) {
|
||||
if (Settings::IsGPULevelHigh()) {
|
||||
memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
}
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(),
|
||||
command_list_header.size * sizeof(u32));
|
||||
const size_t copy_size = command_list_header.size * sizeof(u32);
|
||||
if (subchannels[dma_state.subchannel]) {
|
||||
subchannels[dma_state.subchannel]->current_dirty =
|
||||
memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size);
|
||||
}
|
||||
memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
|
||||
}
|
||||
ProcessCommands(command_headers);
|
||||
}
|
||||
@@ -94,6 +104,7 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
|
||||
|
||||
if (dma_state.method_count) {
|
||||
// Data word of methods command
|
||||
dma_state.dma_word_offset = static_cast<u32>(index * sizeof(u32));
|
||||
if (dma_state.non_incrementing) {
|
||||
const u32 max_write = static_cast<u32>(
|
||||
std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
|
||||
@@ -132,6 +143,8 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
|
||||
case SubmissionMode::Inline:
|
||||
dma_state.method = command_header.method;
|
||||
dma_state.subchannel = command_header.subchannel;
|
||||
dma_state.dma_word_offset = static_cast<u64>(
|
||||
-static_cast<s64>(dma_state.dma_get)); // negate to set address as 0
|
||||
CallMethod(command_header.arg_count);
|
||||
dma_state.non_incrementing = true;
|
||||
dma_increment_once = false;
|
||||
@@ -164,8 +177,14 @@ void DmaPusher::CallMethod(u32 argument) const {
|
||||
dma_state.method_count,
|
||||
});
|
||||
} else {
|
||||
subchannels[dma_state.subchannel]->CallMethod(dma_state.method, argument,
|
||||
dma_state.is_last_call);
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
if (!subchannel->execution_mask[dma_state.method]) [[likely]] {
|
||||
subchannel->method_sink.emplace_back(dma_state.method, argument);
|
||||
return;
|
||||
}
|
||||
subchannel->ConsumeSink();
|
||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||
subchannel->CallMethod(dma_state.method, argument, dma_state.is_last_call);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,8 +193,11 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
|
||||
puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
|
||||
dma_state.method_count);
|
||||
} else {
|
||||
subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start,
|
||||
num_methods, dma_state.method_count);
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
subchannel->ConsumeSink();
|
||||
subchannel->current_dma_segment = dma_state.dma_get + dma_state.dma_word_offset;
|
||||
subchannel->CallMultiMethod(dma_state.method, base_start, num_methods,
|
||||
dma_state.method_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,8 @@ private:
|
||||
u32 subchannel; ///< Current subchannel
|
||||
u32 method_count; ///< Current method count
|
||||
u32 length_pending; ///< Large NI command length pending
|
||||
GPUVAddr dma_get; ///< Currently read segment
|
||||
u64 dma_word_offset; ///< Current word ofset from address
|
||||
bool non_incrementing; ///< Current command's NI flag
|
||||
bool is_last_call;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,10 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
|
||||
LOG_WARNING(HW_GPU, "(STUBBED) called");
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
|
||||
DrawTexture();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -91,6 +95,23 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind
|
||||
ProcessDraw(true, num_instances);
|
||||
}
|
||||
|
||||
void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) {
|
||||
draw_state.topology = topology;
|
||||
|
||||
ProcessDrawIndirect();
|
||||
}
|
||||
|
||||
void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first,
|
||||
u32 index_count) {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
draw_state.topology = topology;
|
||||
draw_state.index_buffer = regs.index_buffer;
|
||||
draw_state.index_buffer.first = index_first;
|
||||
draw_state.index_buffer.count = index_count;
|
||||
|
||||
ProcessDrawIndirect();
|
||||
}
|
||||
|
||||
void DrawManager::SetInlineIndexBuffer(u32 index) {
|
||||
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
|
||||
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
|
||||
@@ -162,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) {
|
||||
ProcessDraw(true, 1);
|
||||
}
|
||||
|
||||
void DrawManager::DrawTexture() {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f;
|
||||
draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f;
|
||||
const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f;
|
||||
const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f;
|
||||
const bool lower_left{regs.window_origin.mode !=
|
||||
Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
|
||||
if (lower_left) {
|
||||
draw_texture_state.dst_y0 -= dst_height;
|
||||
}
|
||||
draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width;
|
||||
draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height;
|
||||
draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
|
||||
draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
|
||||
draw_texture_state.src_x1 =
|
||||
(static_cast<float>(regs.draw_texture.dx_du) / 4294967295.f) * dst_width +
|
||||
draw_texture_state.src_x0;
|
||||
draw_texture_state.src_y1 =
|
||||
(static_cast<float>(regs.draw_texture.dy_dv) / 4294967295.f) * dst_height +
|
||||
draw_texture_state.src_y0;
|
||||
draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
|
||||
draw_texture_state.src_texture = regs.draw_texture.src_texture;
|
||||
|
||||
maxwell3d->rasterizer->DrawTexture();
|
||||
}
|
||||
|
||||
void DrawManager::UpdateTopology() {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
switch (regs.primitive_topology_control) {
|
||||
@@ -198,4 +246,18 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
|
||||
maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawManager::ProcessDrawIndirect() {
|
||||
LOG_TRACE(
|
||||
HW_GPU,
|
||||
"called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}",
|
||||
draw_state.topology, indirect_state.is_indexed, indirect_state.include_count,
|
||||
indirect_state.buffer_size, indirect_state.max_draw_counts);
|
||||
|
||||
UpdateTopology();
|
||||
|
||||
if (maxwell3d->ShouldExecute()) {
|
||||
maxwell3d->rasterizer->DrawIndirect();
|
||||
}
|
||||
}
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -32,6 +32,29 @@ public:
|
||||
std::vector<u8> inline_index_draw_indexes;
|
||||
};
|
||||
|
||||
struct DrawTextureState {
|
||||
f32 dst_x0;
|
||||
f32 dst_y0;
|
||||
f32 dst_x1;
|
||||
f32 dst_y1;
|
||||
f32 src_x0;
|
||||
f32 src_y0;
|
||||
f32 src_x1;
|
||||
f32 src_y1;
|
||||
u32 src_sampler;
|
||||
u32 src_texture;
|
||||
};
|
||||
|
||||
struct IndirectParams {
|
||||
bool is_indexed;
|
||||
bool include_count;
|
||||
GPUVAddr count_start_address;
|
||||
GPUVAddr indirect_start_address;
|
||||
size_t buffer_size;
|
||||
size_t max_draw_counts;
|
||||
size_t stride;
|
||||
};
|
||||
|
||||
explicit DrawManager(Maxwell3D* maxwell_3d);
|
||||
|
||||
void ProcessMethodCall(u32 method, u32 argument);
|
||||
@@ -46,10 +69,26 @@ public:
|
||||
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
|
||||
u32 base_instance, u32 num_instances);
|
||||
|
||||
void DrawArrayIndirect(PrimitiveTopology topology);
|
||||
|
||||
void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
|
||||
|
||||
const State& GetDrawState() const {
|
||||
return draw_state;
|
||||
}
|
||||
|
||||
const DrawTextureState& GetDrawTextureState() const {
|
||||
return draw_texture_state;
|
||||
}
|
||||
|
||||
IndirectParams& GetIndirectParams() {
|
||||
return indirect_state;
|
||||
}
|
||||
|
||||
const IndirectParams& GetIndirectParams() const {
|
||||
return indirect_state;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetInlineIndexBuffer(u32 index);
|
||||
|
||||
@@ -59,11 +98,17 @@ private:
|
||||
|
||||
void DrawIndexSmall(u32 argument);
|
||||
|
||||
void DrawTexture();
|
||||
|
||||
void UpdateTopology();
|
||||
|
||||
void ProcessDraw(bool draw_indexed, u32 instance_count);
|
||||
|
||||
void ProcessDrawIndirect();
|
||||
|
||||
Maxwell3D* maxwell3d{};
|
||||
State draw_state{};
|
||||
DrawTextureState draw_texture_state{};
|
||||
IndirectParams indirect_state{};
|
||||
};
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
@@ -17,6 +21,26 @@ public:
|
||||
/// Write multiple values to the register identified by method.
|
||||
virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) = 0;
|
||||
|
||||
void ConsumeSink() {
|
||||
if (method_sink.empty()) {
|
||||
return;
|
||||
}
|
||||
ConsumeSinkImpl();
|
||||
}
|
||||
|
||||
std::bitset<std::numeric_limits<u16>::max()> execution_mask{};
|
||||
std::vector<std::pair<u32, u32>> method_sink{};
|
||||
bool current_dirty{};
|
||||
GPUVAddr current_dma_segment;
|
||||
|
||||
protected:
|
||||
virtual void ConsumeSinkImpl() {
|
||||
for (auto [method, value] : method_sink) {
|
||||
CallMethod(method, value, true);
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -25,6 +25,9 @@ Fermi2D::Fermi2D(MemoryManager& memory_manager_) {
|
||||
// Nvidia's OpenGL driver seems to assume these values
|
||||
regs.src.depth = 1;
|
||||
regs.dst.depth = 1;
|
||||
|
||||
execution_mask.reset();
|
||||
execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
|
||||
}
|
||||
|
||||
Fermi2D::~Fermi2D() = default;
|
||||
@@ -49,6 +52,13 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
|
||||
}
|
||||
}
|
||||
|
||||
void Fermi2D::ConsumeSinkImpl() {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void Fermi2D::Blit() {
|
||||
MICROPROFILE_SCOPE(GPU_BlitEngine);
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
|
||||
|
||||
@@ -309,6 +309,8 @@ private:
|
||||
/// Performs the copy from the source surface to the destination surface as configured in the
|
||||
/// registers.
|
||||
void Blit();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
namespace Tegra::Engines {
|
||||
|
||||
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
|
||||
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
|
||||
execution_mask.reset();
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
|
||||
}
|
||||
|
||||
KeplerCompute::~KeplerCompute() = default;
|
||||
|
||||
@@ -23,6 +28,13 @@ void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
|
||||
upload_state.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
void KeplerCompute::ConsumeSinkImpl() {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid KeplerCompute register, increase the size of the Regs structure");
|
||||
|
||||
@@ -204,6 +204,8 @@ public:
|
||||
private:
|
||||
void ProcessLaunch();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||
|
||||
|
||||
@@ -18,6 +18,17 @@ KeplerMemory::~KeplerMemory() = default;
|
||||
|
||||
void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||
upload_state.BindRasterizer(rasterizer_);
|
||||
|
||||
execution_mask.reset();
|
||||
execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
|
||||
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
||||
}
|
||||
|
||||
void KeplerMemory::ConsumeSinkImpl() {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
Upload::State upload_state;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
@@ -28,6 +30,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
|
||||
regs.upload} {
|
||||
dirty.flags.flip();
|
||||
InitializeRegisterDefaults();
|
||||
execution_mask.reset();
|
||||
for (size_t i = 0; i < execution_mask.size(); i++) {
|
||||
execution_mask[i] = IsMethodExecutable(static_cast<u32>(i));
|
||||
}
|
||||
}
|
||||
|
||||
Maxwell3D::~Maxwell3D() = default;
|
||||
@@ -121,6 +127,71 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
shadow_state = regs;
|
||||
}
|
||||
|
||||
bool Maxwell3D::IsMethodExecutable(u32 method) {
|
||||
if (method >= MacroRegistersStart) {
|
||||
return true;
|
||||
}
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(draw.end):
|
||||
case MAXWELL3D_REG_INDEX(draw.begin):
|
||||
case MAXWELL3D_REG_INDEX(vertex_buffer.first):
|
||||
case MAXWELL3D_REG_INDEX(vertex_buffer.count):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer.first):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer.count):
|
||||
case MAXWELL3D_REG_INDEX(draw_inline_index):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer32_subsequent):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer16_subsequent):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer8_subsequent):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer32_first):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer16_first):
|
||||
case MAXWELL3D_REG_INDEX(index_buffer8_first):
|
||||
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
|
||||
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
|
||||
case MAXWELL3D_REG_INDEX(wait_for_idle):
|
||||
case MAXWELL3D_REG_INDEX(shadow_ram_control):
|
||||
case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
|
||||
case MAXWELL3D_REG_INDEX(load_mme.instruction):
|
||||
case MAXWELL3D_REG_INDEX(load_mme.start_address):
|
||||
case MAXWELL3D_REG_INDEX(falcon[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 1:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 2:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 3:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 4:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 5:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 6:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 7:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 8:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 9:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 10:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 11:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 12:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 13:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 14:
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[0].raw_config):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[1].raw_config):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[2].raw_config):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[3].raw_config):
|
||||
case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
|
||||
case MAXWELL3D_REG_INDEX(topology_override):
|
||||
case MAXWELL3D_REG_INDEX(clear_surface):
|
||||
case MAXWELL3D_REG_INDEX(report_semaphore.query):
|
||||
case MAXWELL3D_REG_INDEX(render_enable.mode):
|
||||
case MAXWELL3D_REG_INDEX(clear_report_value):
|
||||
case MAXWELL3D_REG_INDEX(sync_info):
|
||||
case MAXWELL3D_REG_INDEX(launch_dma):
|
||||
case MAXWELL3D_REG_INDEX(inline_data):
|
||||
case MAXWELL3D_REG_INDEX(fragment_barrier):
|
||||
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||
if (executing_macro == 0) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
@@ -130,14 +201,72 @@ void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool
|
||||
}
|
||||
|
||||
macro_params.insert(macro_params.end(), base_start, base_start + amount);
|
||||
for (size_t i = 0; i < amount; i++) {
|
||||
macro_addresses.push_back(current_dma_segment + i * sizeof(u32));
|
||||
}
|
||||
macro_segments.emplace_back(current_dma_segment, amount);
|
||||
current_macro_dirty |= current_dirty;
|
||||
current_dirty = false;
|
||||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (is_last_call) {
|
||||
ConsumeSink();
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
macro_params.clear();
|
||||
macro_addresses.clear();
|
||||
macro_segments.clear();
|
||||
current_macro_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::RefreshParametersImpl() {
|
||||
size_t current_index = 0;
|
||||
for (auto& segment : macro_segments) {
|
||||
if (segment.first == 0) {
|
||||
current_index += segment.second;
|
||||
continue;
|
||||
}
|
||||
memory_manager.ReadBlock(segment.first, ¯o_params[current_index],
|
||||
sizeof(u32) * segment.second);
|
||||
current_index += segment.second;
|
||||
}
|
||||
}
|
||||
|
||||
u32 Maxwell3D::GetMaxCurrentVertices() {
|
||||
u32 num_vertices = 0;
|
||||
for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
|
||||
const auto& array = regs.vertex_streams[index];
|
||||
if (array.enable == 0) {
|
||||
continue;
|
||||
}
|
||||
const auto& attribute = regs.vertex_attrib_format[index];
|
||||
if (attribute.constant) {
|
||||
num_vertices = std::max(num_vertices, 1U);
|
||||
continue;
|
||||
}
|
||||
const auto& limit = regs.vertex_stream_limits[index];
|
||||
const GPUVAddr gpu_addr_begin = array.Address();
|
||||
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||
num_vertices = std::max(
|
||||
num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value()));
|
||||
}
|
||||
return num_vertices;
|
||||
}
|
||||
|
||||
size_t Maxwell3D::EstimateIndexBufferSize() {
|
||||
GPUVAddr start_address = regs.index_buffer.StartAddress();
|
||||
GPUVAddr end_address = regs.index_buffer.EndAddress();
|
||||
constexpr std::array<size_t, 4> max_sizes = {
|
||||
std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
|
||||
std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
|
||||
const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
|
||||
return std::min<size_t>(
|
||||
memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[byte_size]) /
|
||||
byte_size,
|
||||
static_cast<size_t>(end_address - start_address));
|
||||
}
|
||||
|
||||
u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
||||
// Keep track of the register value in shadow_state when requested.
|
||||
const auto control = shadow_state.shadow_ram_control;
|
||||
@@ -152,6 +281,29 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
||||
return argument;
|
||||
}
|
||||
|
||||
void Maxwell3D::ConsumeSinkImpl() {
|
||||
SCOPE_EXIT({ method_sink.clear(); });
|
||||
const auto control = shadow_state.shadow_ram_control;
|
||||
if (control == Regs::ShadowRamControl::Track ||
|
||||
control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
|
||||
for (auto [method, value] : method_sink) {
|
||||
shadow_state.reg_array[method] = value;
|
||||
ProcessDirtyRegisters(method, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (control == Regs::ShadowRamControl::Replay) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (auto [method, value] : method_sink) {
|
||||
ProcessDirtyRegisters(method, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
|
||||
if (regs.reg_array[method] == argument) {
|
||||
return;
|
||||
@@ -263,7 +415,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
|
||||
const u32 argument = ProcessShadowRam(method, method_argument);
|
||||
ProcessDirtyRegisters(method, argument);
|
||||
|
||||
ProcessMethodCall(method, argument, method_argument, is_last_call);
|
||||
}
|
||||
|
||||
@@ -294,9 +445,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.buffer) + 15:
|
||||
ProcessCBMultiData(base_start, amount);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(inline_data):
|
||||
case MAXWELL3D_REG_INDEX(inline_data): {
|
||||
ASSERT(methods_pending == amount);
|
||||
upload_state.ProcessData(base_start, amount);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
for (u32 i = 0; i < amount; i++) {
|
||||
CallMethod(method, base_start[i], methods_pending - i <= 1);
|
||||
@@ -389,7 +542,11 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
case Regs::RenderEnable::Override::NeverRender:
|
||||
execute_on = false;
|
||||
break;
|
||||
case Regs::RenderEnable::Override::UseRenderEnable:
|
||||
case Regs::RenderEnable::Override::UseRenderEnable: {
|
||||
if (rasterizer->AccelerateConditionalRendering()) {
|
||||
execute_on = true;
|
||||
return;
|
||||
}
|
||||
switch (regs.render_enable.mode) {
|
||||
case Regs::RenderEnable::Mode::True: {
|
||||
execute_on = true;
|
||||
@@ -427,6 +584,7 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCounterReset() {
|
||||
@@ -463,7 +621,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(size_t stage_index) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader
|
||||
// stage.
|
||||
const auto& bind_data = regs.bind_groups[stage_index];
|
||||
auto& buffer = state.shader_stages[stage_index].const_buffers[bind_data.shader_slot];
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
@@ -524,4 +683,10 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
|
||||
return regs.reg_array[method];
|
||||
}
|
||||
|
||||
void Maxwell3D::SetHLEReplacementAttributeType(u32 bank, u32 offset,
|
||||
HLEReplacementAttributeType name) {
|
||||
const u64 key = (static_cast<u64>(bank) << 32) | offset;
|
||||
replace_table.emplace(key, name);
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -272,6 +272,7 @@ public:
|
||||
};
|
||||
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, Mode> mode;
|
||||
BitField<4, 8, u32> pad;
|
||||
};
|
||||
@@ -1217,10 +1218,12 @@ public:
|
||||
|
||||
struct Window {
|
||||
union {
|
||||
u32 raw_x;
|
||||
BitField<0, 16, u32> x_min;
|
||||
BitField<16, 16, u32> x_max;
|
||||
};
|
||||
union {
|
||||
u32 raw_y;
|
||||
BitField<0, 16, u32> y_min;
|
||||
BitField<16, 16, u32> y_max;
|
||||
};
|
||||
@@ -1596,6 +1599,20 @@ public:
|
||||
};
|
||||
static_assert(sizeof(TIRModulationCoeff) == 0x4);
|
||||
|
||||
struct DrawTexture {
|
||||
s32 dst_x0;
|
||||
s32 dst_y0;
|
||||
s32 dst_width;
|
||||
s32 dst_height;
|
||||
s64 dx_du;
|
||||
s64 dy_dv;
|
||||
u32 src_sampler;
|
||||
u32 src_texture;
|
||||
s32 src_x0;
|
||||
s32 src_y0;
|
||||
};
|
||||
static_assert(sizeof(DrawTexture) == 0x30);
|
||||
|
||||
struct ReduceColorThreshold {
|
||||
union {
|
||||
BitField<0, 8, u32> all_hit_once;
|
||||
@@ -2708,7 +2725,7 @@ public:
|
||||
u32 post_z_pixel_imask; ///< 0x0F1C
|
||||
INSERT_PADDING_BYTES_NOINIT(0x20);
|
||||
ConstantColorRendering const_color_rendering; ///< 0x0F40
|
||||
s32 stencil_back_ref; ///< 0x0F54
|
||||
u32 stencil_back_ref; ///< 0x0F54
|
||||
u32 stencil_back_mask; ///< 0x0F58
|
||||
u32 stencil_back_func_mask; ///< 0x0F5C
|
||||
INSERT_PADDING_BYTES_NOINIT(0x14);
|
||||
@@ -2748,7 +2765,7 @@ public:
|
||||
u32 reserved_sw_method2; ///< 0x102C
|
||||
std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030
|
||||
std::array<u32, 15> spare_nop; ///< 0x1044
|
||||
INSERT_PADDING_BYTES_NOINIT(0x30);
|
||||
DrawTexture draw_texture; ///< 0x1080
|
||||
std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0
|
||||
ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC
|
||||
std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0
|
||||
@@ -2832,9 +2849,9 @@ public:
|
||||
Blend blend; ///< 0x133C
|
||||
u32 stencil_enable; ///< 0x1380
|
||||
StencilOp stencil_front_op; ///< 0x1384
|
||||
s32 stencil_front_ref; ///< 0x1394
|
||||
s32 stencil_front_func_mask; ///< 0x1398
|
||||
s32 stencil_front_mask; ///< 0x139C
|
||||
u32 stencil_front_ref; ///< 0x1394
|
||||
u32 stencil_front_func_mask; ///< 0x1398
|
||||
u32 stencil_front_mask; ///< 0x139C
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
u32 draw_auto_start_byte_count; ///< 0x13A4
|
||||
PsSaturate frag_color_clamp; ///< 0x13A8
|
||||
@@ -3020,6 +3037,24 @@ public:
|
||||
/// Store temporary hw register values, used by some calls to restore state after a operation
|
||||
Regs shadow_state;
|
||||
|
||||
// None Engine
|
||||
enum class EngineHint : u32 {
|
||||
None = 0x0,
|
||||
OnHLEMacro = 0x1,
|
||||
};
|
||||
|
||||
EngineHint engine_state{EngineHint::None};
|
||||
|
||||
enum class HLEReplacementAttributeType : u32 {
|
||||
BaseVertex = 0x0,
|
||||
BaseInstance = 0x1,
|
||||
DrawID = 0x2,
|
||||
};
|
||||
|
||||
void SetHLEReplacementAttributeType(u32 bank, u32 offset, HLEReplacementAttributeType name);
|
||||
|
||||
std::unordered_map<u64, HLEReplacementAttributeType> replace_table;
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
|
||||
|
||||
@@ -3067,6 +3102,35 @@ public:
|
||||
std::unique_ptr<DrawManager> draw_manager;
|
||||
friend class DrawManager;
|
||||
|
||||
GPUVAddr GetMacroAddress(size_t index) const {
|
||||
return macro_addresses[index];
|
||||
}
|
||||
|
||||
void RefreshParameters() {
|
||||
if (!current_macro_dirty) {
|
||||
return;
|
||||
}
|
||||
RefreshParametersImpl();
|
||||
}
|
||||
|
||||
bool AnyParametersDirty() const {
|
||||
return current_macro_dirty;
|
||||
}
|
||||
|
||||
u32 GetMaxCurrentVertices();
|
||||
|
||||
size_t EstimateIndexBufferSize();
|
||||
|
||||
/// Handles a write to the CLEAR_BUFFERS register.
|
||||
void ProcessClearBuffers(u32 layer_count);
|
||||
|
||||
/// Handles a write to the CB_BIND register.
|
||||
void ProcessCBBind(size_t stage_index);
|
||||
|
||||
/// Handles a write to the CB_DATA[i] register.
|
||||
void ProcessCBData(u32 value);
|
||||
void ProcessCBMultiData(const u32* start_base, u32 amount);
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
@@ -3076,6 +3140,8 @@ private:
|
||||
|
||||
void ProcessDirtyRegisters(u32 method, u32 argument);
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
@@ -3116,16 +3182,13 @@ private:
|
||||
/// Handles writes to syncing register.
|
||||
void ProcessSyncPoint();
|
||||
|
||||
/// Handles a write to the CB_DATA[i] register.
|
||||
void ProcessCBData(u32 value);
|
||||
void ProcessCBMultiData(const u32* start_base, u32 amount);
|
||||
|
||||
/// Handles a write to the CB_BIND register.
|
||||
void ProcessCBBind(size_t stage_index);
|
||||
|
||||
/// Returns a query's value or an empty object if the value will be deferred through a cache.
|
||||
std::optional<u64> GetQueryResult();
|
||||
|
||||
void RefreshParametersImpl();
|
||||
|
||||
bool IsMethodExecutable(u32 method);
|
||||
|
||||
Core::System& system;
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
@@ -3145,6 +3208,10 @@ private:
|
||||
Upload::State upload_state;
|
||||
|
||||
bool execute_on{true};
|
||||
|
||||
std::vector<std::pair<GPUVAddr, size_t>> macro_segments;
|
||||
std::vector<GPUVAddr> macro_addresses;
|
||||
bool current_macro_dirty{};
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
|
||||
@@ -21,7 +21,10 @@ namespace Tegra::Engines {
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
|
||||
: system{system_}, memory_manager{memory_manager_} {}
|
||||
: system{system_}, memory_manager{memory_manager_} {
|
||||
execution_mask.reset();
|
||||
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
||||
}
|
||||
|
||||
MaxwellDMA::~MaxwellDMA() = default;
|
||||
|
||||
@@ -29,6 +32,13 @@ void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
void MaxwellDMA::ConsumeSinkImpl() {
|
||||
for (auto [method, value] : method_sink) {
|
||||
regs.reg_array[method] = value;
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||
|
||||
|
||||
@@ -231,6 +231,8 @@ private:
|
||||
|
||||
void ReleaseSemaphore();
|
||||
|
||||
void ConsumeSinkImpl() override;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
@@ -11,6 +11,7 @@ set(GLSL_INCLUDES
|
||||
|
||||
set(SHADER_FILES
|
||||
astc_decoder.comp
|
||||
blit_color_float.frag
|
||||
block_linear_unswizzle_2d.comp
|
||||
block_linear_unswizzle_3d.comp
|
||||
convert_abgr8_to_d24s8.frag
|
||||
@@ -36,7 +37,6 @@ set(SHADER_FILES
|
||||
smaa_blending_weight_calculation.frag
|
||||
smaa_neighborhood_blending.vert
|
||||
smaa_neighborhood_blending.frag
|
||||
vulkan_blit_color_float.frag
|
||||
vulkan_blit_depth_stencil.frag
|
||||
vulkan_fidelityfx_fsr_easu_fp16.comp
|
||||
vulkan_fidelityfx_fsr_easu_fp32.comp
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
#version 450
|
||||
|
||||
#ifdef VULKAN
|
||||
#define VERTEX_ID gl_VertexIndex
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define FLIPY 1
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
#define VERTEX_ID gl_VertexID
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define FLIPY -1
|
||||
#define UNIFORM(n) layout (location = n) uniform
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
#endif
|
||||
|
||||
BEGIN_PUSH_CONSTANTS
|
||||
@@ -21,8 +28,8 @@ END_PUSH_CONSTANTS
|
||||
layout(location = 0) out vec2 texcoord;
|
||||
|
||||
void main() {
|
||||
float x = float((gl_VertexIndex & 1) << 2);
|
||||
float y = float((gl_VertexIndex & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
|
||||
float x = float((VERTEX_ID & 1) << 2);
|
||||
float y = float((VERTEX_ID & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0);
|
||||
texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user