Compare commits

..

4 Commits

Author SHA1 Message Date
lat9nq
3862511a9a settings: Use std::clamp where possible
Addresses PR review

Co-authored-by: PixelyIon <pixelyion@protonmail.com>
2021-07-31 17:20:12 -04:00
lat9nq
e9cf08c241 settings: Remove unnecessary std::move usages
Addresses review feedback.

Co-authored-by: Mai M. <mathew1800@gmail.com>
2021-07-30 18:44:50 -04:00
lat9nq
7737bdfd1a settings: Fix function virtualization
Fixes a theoretical scenario where a Setting is using the BasicSetting's
GetValue function. In practice this probably only happens on yuzu-cmd,
where there is no need for a Setting's additional features. Need to fix
regardless.
2021-07-30 13:33:35 -04:00
lat9nq
a1f19b61f8 settings: Implement setting ranges
Clamps the setting's values against the specified minimum and maximum
values.
2021-07-30 13:33:21 -04:00
87 changed files with 1102 additions and 1521 deletions

View File

@@ -18,8 +18,7 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_DISCORD_PRESENCE=ON \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"}
make -j$(nproc)

View File

@@ -25,7 +25,7 @@ option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@@ -583,32 +583,8 @@ if (YUZU_USE_BUNDLED_FFMPEG)
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}"
CACHE PATH "Path to FFmpeg headers" FORCE)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()
if(LIBVA_FOUND)
pkg_check_modules(LIBDRM libdrm REQUIRED)
find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
set(FFmpeg_LIBVA_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi
--enable-hwaccel=vp9_vaapi
--enable-libdrm)
message(STATUS "VA-API found")
else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
# `--disable-{vaapi,vdpau}` is needed to avoid linking issues
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
@@ -624,16 +600,13 @@ if (YUZU_USE_BUNDLED_FFMPEG)
--disable-network
--disable-postproc
--disable-swresample
--disable-vaapi
--disable-vdpau
--enable-decoder=h264
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
@@ -643,10 +616,9 @@ if (YUZU_USE_BUNDLED_FFMPEG)
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
${FFmpeg_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
@@ -656,12 +628,7 @@ if (YUZU_USE_BUNDLED_FFMPEG)
# ALL makes this custom target build every time
# but it won't actually build if the DEPENDS parameter is up to date
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
set(FFmpeg_LIBRARIES ${FFmpeg_LIBVA_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
unset(FFmpeg_BUILD_LIBRARIES)
unset(FFmpeg_LIBVA_LIBRARIES)
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_LIBRARIES} ffmpeg-configure)
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")

View File

@@ -51,11 +51,11 @@ QPushButton#GPUStatusBarButton:hover {
}
QPushButton#GPUStatusBarButton:checked {
color: #b06020;
color: #ff8040;
}
QPushButton#GPUStatusBarButton:!checked {
color: #109010;
color: #40dd40;
}
QPushButton#buttonRefreshDevices {

View File

@@ -67,8 +67,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
"${LIBUSB_MAKEFILE}"
COMMAND
env
CC="${CMAKE_C_COMPILER}"
CXX="${CMAKE_CXX_COMPILER}"
CFLAGS="${LIBUSB_CFLAGS}"
sh "${LIBUSB_CONFIGURE}"
${LIBUSB_CONFIGURE_ARGS}

View File

@@ -52,12 +52,8 @@ assert_noinline_call(const Fn& fn) {
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
#else // not debug
#define DEBUG_ASSERT(_a_) \
do { \
} while (0)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) \
do { \
} while (0)
#define DEBUG_ASSERT(_a_)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
#endif
#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")

View File

@@ -61,7 +61,7 @@ template <typename ContiguousContainer>
return out;
}
[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[33]) {
[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
return HexStringToArray<16>(data);
}

View File

@@ -59,6 +59,7 @@ void LogSettings() {
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -142,6 +143,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_caches_gc.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
@@ -81,7 +82,7 @@ public:
*
* @returns A reference to the setting
*/
[[nodiscard]] const Type& GetValue() const {
[[nodiscard]] virtual const Type& GetValue() const {
return global;
}
@@ -90,7 +91,7 @@ public:
*
* @param value The desired value
*/
void SetValue(const Type& value) {
virtual void SetValue(const Type& value) {
Type temp{value};
std::swap(global, temp);
}
@@ -120,7 +121,7 @@ public:
*
* @returns A reference to the setting
*/
const Type& operator=(const Type& value) {
virtual const Type& operator=(const Type& value) {
Type temp{value};
std::swap(global, temp);
return global;
@@ -131,7 +132,7 @@ public:
*
* @returns A reference to the setting
*/
explicit operator const Type&() const {
explicit virtual operator const Type&() const {
return global;
}
@@ -141,6 +142,51 @@ protected:
const std::string label{}; ///< The setting's label
};
/**
* BasicRangedSetting class is intended for use with quantifiable settings that need a more
* restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
* simply used to sanitize SetValue and the assignment overload.
*/
template <typename Type>
class BasicRangedSetting : virtual public BasicSetting<Type> {
public:
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param default_val Intial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
*/
explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
const std::string& name)
: BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
~BasicRangedSetting() = default;
/**
* Like BasicSetting's SetValue, except value is clamped to the range of the setting.
*
* @param value The desired value
*/
void SetValue(const Type& value) override {
this->global = std::clamp(value, minimum, maximum);
}
/**
* Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
*
* @param value The desired value
* @returns A reference to the setting's value
*/
const Type& operator=(const Type& value) override {
this->global = std::clamp(value, minimum, maximum);
return this->global;
}
const Type minimum; ///< Minimum allowed value of the setting
const Type maximum; ///< Maximum allowed value of the setting
};
/**
* The Setting class is a slightly more complex version of the BasicSetting class. This adds a
* custom setting to switch to when a guest application specifically requires it. The effect is that
@@ -152,7 +198,7 @@ protected:
* Like the BasicSetting, this requires setting a default value and label to use.
*/
template <typename Type>
class Setting final : public BasicSetting<Type> {
class Setting : virtual public BasicSetting<Type> {
public:
/**
* Sets a default value, label, and setting value.
@@ -191,7 +237,13 @@ public:
*
* @returns The required value of the setting
*/
[[nodiscard]] const Type& GetValue(bool need_global = false) const {
[[nodiscard]] const Type& GetValue() const override {
if (use_global) {
return this->global;
}
return custom;
}
[[nodiscard]] const Type& GetValue(bool need_global) const {
if (use_global || need_global) {
return this->global;
}
@@ -203,7 +255,7 @@ public:
*
* @param value The new value
*/
void SetValue(const Type& value) {
void SetValue(const Type& value) override {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
@@ -219,7 +271,7 @@ public:
*
* @returns A reference to the current setting value
*/
const Type& operator=(const Type& value) {
const Type& operator=(const Type& value) override {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
@@ -234,18 +286,72 @@ public:
*
* @returns A reference to the current setting value
*/
explicit operator const Type&() const {
explicit operator const Type&() const override {
if (use_global) {
return this->global;
}
return custom;
}
private:
protected:
bool use_global{true}; ///< The setting's global state
Type custom{}; ///< The custom value of the setting
};
/**
* RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
* for use with quantifiable settings.
*/
template <typename Type>
class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
public:
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param default_val Intial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
*/
explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
const std::string& name)
: BasicSetting<Type>{default_val, name},
BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
name} {}
~RangedSetting() = default;
/**
* Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
* appropriate value depending on the global state.
*
* @param value The desired value
*/
void SetValue(const Type& value) override {
const Type temp = std::clamp(value, this->minimum, this->maximum);
if (this->use_global) {
this->global = temp;
}
this->custom = temp;
}
/**
* Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
* Uses the appropriate value depending on the global state.
*
* @param value The desired value
* @returns A reference to the setting's value
*/
const Type& operator=(const Type& value) override {
const Type temp = std::clamp(value, this->minimum, this->maximum);
if (this->use_global) {
this->global = temp;
return this->global;
}
this->custom = temp;
return this->custom;
}
};
/**
* The InputSetting class allows for getting a reference to either the global or custom members.
* This is required as we cannot easily modify the values of user-defined types within containers
@@ -289,13 +395,14 @@ struct Values {
BasicSetting<std::string> sink_id{"auto", "output_engine"};
BasicSetting<bool> audio_muted{false, "audio_muted"};
Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
Setting<u8> volume{100, "volume"};
RangedSetting<u8> volume{100, 0, 100, "volume"};
// Core
Setting<bool> use_multi_core{true, "use_multi_core"};
// Cpu
Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"};
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
CPUAccuracy::Unsafe, "cpu_accuracy"};
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -317,9 +424,9 @@ struct Values {
Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
// Renderer
Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
RangedSetting<RendererBackend> renderer_backend{
RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
BasicSetting<bool> renderer_debug{false, "debug"};
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
BasicSetting<bool> disable_shader_loop_safety_checks{false,
"disable_shader_loop_safety_checks"};
@@ -328,28 +435,31 @@ struct Values {
Setting<u16> resolution_factor{1, "resolution_factor"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
Setting<FullscreenMode> fullscreen_mode{
RangedSetting<FullscreenMode> fullscreen_mode{
#ifdef _WIN32
FullscreenMode::Borderless,
#else
FullscreenMode::Exclusive,
#endif
"fullscreen_mode"};
Setting<int> aspect_ratio{0, "aspect_ratio"};
Setting<int> max_anisotropy{0, "max_anisotropy"};
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
Setting<bool> use_speed_limit{true, "use_speed_limit"};
Setting<u16> speed_limit{100, "speed_limit"};
RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
GPUAccuracy::Extreme, "gpu_accuracy"};
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
Setting<bool> accelerate_astc{true, "accelerate_astc"};
Setting<bool> use_vsync{true, "use_vsync"};
BasicSetting<u16> fps_cap{1000, "fps_cap"};
BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"};
RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
Setting<bool> use_caches_gc{false, "use_caches_gc"};
Setting<u8> bg_red{0, "bg_red"};
Setting<u8> bg_green{0, "bg_green"};
@@ -363,10 +473,10 @@ struct Values {
std::chrono::seconds custom_rtc_differential;
BasicSetting<s32> current_user{0, "current_user"};
Setting<s32> language_index{1, "language_index"};
Setting<s32> region_index{1, "region_index"};
Setting<s32> time_zone_index{0, "time_zone_index"};
Setting<s32> sound_index{1, "sound_index"};
RangedSetting<s32> language_index{1, 0, 16, "language_index"};
RangedSetting<s32> region_index{1, 0, 6, "region_index"};
RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
@@ -383,7 +493,7 @@ struct Values {
"udp_input_servers"};
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"};
BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
std::string mouse_device;
MouseButtonsRaw mouse_buttons;

View File

@@ -6,64 +6,10 @@
#include <fmt/format.h>
#include "common/assert.h"
#include "common/uuid.h"
namespace Common {
namespace {
bool IsHexDigit(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
u8 HexCharToByte(char c) {
if (c >= '0' && c <= '9') {
return static_cast<u8>(c - '0');
}
if (c >= 'a' && c <= 'f') {
return static_cast<u8>(c - 'a' + 10);
}
if (c >= 'A' && c <= 'F') {
return static_cast<u8>(c - 'A' + 10);
}
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return u8{0};
}
} // Anonymous namespace
u128 HexStringToU128(std::string_view hex_string) {
const size_t length = hex_string.length();
// Detect "0x" prefix.
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
const size_t offset = has_0x_prefix ? 2 : 0;
// Check length.
if (length > 32 + offset) {
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
return INVALID_UUID;
}
u64 lo = 0;
u64 hi = 0;
for (size_t i = 0; i < length - offset; ++i) {
const char c = hex_string[length - 1 - i];
if (!IsHexDigit(c)) {
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
return INVALID_UUID;
}
if (i < 16) {
lo |= u64{HexCharToByte(c)} << (i * 4);
}
if (i >= 16) {
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
}
}
return u128{lo, hi};
}
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());

View File

@@ -5,7 +5,6 @@
#pragma once
#include <string>
#include <string_view>
#include "common/common_types.h"
@@ -13,30 +12,12 @@ namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
/**
* Converts a hex string to a 128-bit unsigned integer.
*
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
*
* This function will assert and return INVALID_UUID under the following conditions:
* - If the hex string is more than 32 characters long
* - If the hex string contains non-hexadecimal characters
*
* @param hex_string Hexadecimal string
*
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
*/
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid;
UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
explicit UUID(std::string_view hex_string) {
uuid = HexStringToU128(hex_string);
}
[[nodiscard]] constexpr explicit operator bool() const {
return uuid != INVALID_UUID;
@@ -69,14 +50,3 @@ struct UUID {
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
} // namespace Common
namespace std {
template <>
struct hash<Common::UUID> {
size_t operator()(const Common::UUID& uuid) const noexcept {
return uuid.uuid[1] ^ uuid.uuid[0];
}
};
} // namespace std

View File

@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
// Horizon OS version constants.
constexpr u8 HOS_VERSION_MAJOR = 12;
constexpr u8 HOS_VERSION_MINOR = 1;
constexpr u8 HOS_VERSION_MICRO = 0;
constexpr u8 HOS_VERSION_MAJOR = 11;
constexpr u8 HOS_VERSION_MINOR = 0;
constexpr u8 HOS_VERSION_MICRO = 1;
// NintendoSDK version constants.
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
constexpr char DISPLAY_VERSION[] = "12.1.0";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
constexpr char DISPLAY_VERSION[] = "11.0.1";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
constexpr u32 GetTargetFirmware() {
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |

View File

@@ -377,8 +377,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
if (swkbd_config_common.use_utf8) {
std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
// Include the null terminator in the buffer size.
const u64 buffer_size = utf8_submitted_text.size() + 1;
const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
utf8_submitted_text);
@@ -387,8 +386,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
utf8_submitted_text.size());
} else {
// Include the null terminator in the buffer size.
const u64 buffer_size = (current_text.size() + 1) * sizeof(char16_t);
const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
Common::UTF16ToUTF8(current_text));

View File

@@ -339,7 +339,6 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
case Set::LanguageCode::FR_CA:
return ApplicationLanguage::CanadianFrench;
case Set::LanguageCode::PT:
case Set::LanguageCode::PT_BR:
return ApplicationLanguage::Portuguese;
case Set::LanguageCode::RU:
return ApplicationLanguage::Russian;

View File

@@ -166,6 +166,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
} else {
cmd_buffer.map_address = object->dma_map_addr;
AddBufferMap(object->dma_map_addr, object->size, object->addr,
object->status == nvmap::Object::Status::Allocated);
}
}
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
@@ -176,11 +178,30 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
}
NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
// This is intntionally stubbed.
// Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame
// addresses, and risk invalidating data before the async GPU thread is done with it
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
auto& gpu = system.GPU();
for (auto& cmd_buffer : cmd_buffer_handles) {
const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
if (!object) {
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
std::memcpy(output.data(), &params, output.size());
return NvResult::InvalidState;
}
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
} else {
// This occurs quite frequently, however does not seem to impact functionality
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
object->dma_map_addr);
}
object->dma_map_addr = 0;
}
std::memset(output.data(), 0, output.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
return NvResult::Success;
}
@@ -191,4 +212,33 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
return NvResult::Success;
}
std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
GPUVAddr gpu_addr) const {
const auto it = std::find_if(
buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
});
ASSERT(it != buffer_mappings.end());
return it->second;
}
void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
bool is_allocated) {
buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
}
std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
const auto iter{buffer_mappings.find(gpu_addr)};
if (iter == buffer_mappings.end()) {
return std::nullopt;
}
std::size_t size = 0;
if (iter->second.IsAllocated()) {
size = iter->second.Size();
}
buffer_mappings.erase(iter);
return size;
}
} // namespace Service::Nvidia::Devices

View File

@@ -23,6 +23,45 @@ public:
~nvhost_nvdec_common() override;
protected:
class BufferMap final {
public:
constexpr BufferMap() = default;
constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_)
: start_addr{start_addr_}, end_addr{start_addr_ + size_} {}
constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
bool is_allocated_)
: start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
is_allocated{is_allocated_} {}
constexpr VAddr StartAddr() const {
return start_addr;
}
constexpr VAddr EndAddr() const {
return end_addr;
}
constexpr std::size_t Size() const {
return end_addr - start_addr;
}
constexpr VAddr CpuAddr() const {
return cpu_addr;
}
constexpr bool IsAllocated() const {
return is_allocated;
}
private:
GPUVAddr start_addr{};
GPUVAddr end_addr{};
VAddr cpu_addr{};
bool is_allocated{};
};
struct IoctlSetNvmapFD {
s32_le nvmap_fd{};
};
@@ -115,11 +154,17 @@ protected:
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
SyncpointManager& syncpoint_manager;
std::array<u32, MaxSyncPoints> device_syncpoints{};
// This is expected to be ordered, therefore we must use a map, not unordered_map
std::map<GPUVAddr, BufferMap> buffer_mappings;
};
}; // namespace Devices
} // namespace Service::Nvidia

View File

@@ -12,7 +12,7 @@
namespace Service::Set {
namespace {
constexpr std::array<LanguageCode, 18> available_language_codes = {{
constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
@@ -30,7 +30,6 @@ constexpr std::array<LanguageCode, 18> available_language_codes = {{
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
LanguageCode::PT_BR,
}};
enum class KeyboardLayout : u64 {
@@ -51,7 +50,7 @@ enum class KeyboardLayout : u64 {
ChineseTraditional = 14,
};
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
{LanguageCode::JA, KeyboardLayout::Japanese},
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
{LanguageCode::FR, KeyboardLayout::French},
@@ -69,11 +68,10 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_la
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
{LanguageCode::PT_BR, KeyboardLayout::Portuguese},
}};
constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
constexpr std::size_t pre4_0_0_max_entries = 15;
constexpr std::size_t post4_0_0_max_entries = 17;
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
@@ -83,10 +81,9 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
rb.Push(static_cast<u32>(num_language_codes));
}
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
const std::size_t max_amount = std::min(requested_amount, max_entries);
const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
const std::size_t copy_amount = std::min(requested_amount, max_size);
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
ctx.WriteBuffer(available_language_codes.data(), copy_size);
@@ -121,7 +118,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
}
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -143,19 +140,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
}
void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {

View File

@@ -31,7 +31,6 @@ enum class LanguageCode : u64 {
ES_419 = 0x00003931342D7365,
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
PT_BR = 0x00000052422D7470,
};
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);

View File

@@ -570,7 +570,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
ASSERT(flags == 0);
const sockaddr* to = nullptr;
const int tolen = addr ? sizeof(sockaddr) : 0;
const int tolen = addr ? 0 : sizeof(sockaddr);
sockaddr host_addr_in;
if (addr) {

View File

@@ -304,10 +304,10 @@ std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
}
std::string GenerateKeyboardParam(int key_code) {
Common::ParamPackage param;
param.Set("engine", "keyboard");
param.Set("code", key_code);
param.Set("toggle", false);
Common::ParamPackage param{
{"engine", "keyboard"},
{"code", std::to_string(key_code)},
};
return param.Serialize();
}

View File

@@ -57,7 +57,6 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
if (pad.button != MouseInput::MouseButton::Undefined) {
params.Set("engine", "mouse");
params.Set("button", static_cast<u16>(pad.button));
params.Set("toggle", false);
return params;
}
}

View File

@@ -82,12 +82,6 @@ public:
state.buttons.insert_or_assign(button, value);
}
void PreSetButton(int button) {
if (!state.buttons.contains(button)) {
SetButton(button, false);
}
}
void SetMotion(SDL_ControllerSensorEvent event) {
constexpr float gravity_constant = 9.80665f;
std::lock_guard lock{mutex};
@@ -161,16 +155,9 @@ public:
state.axes.insert_or_assign(axis, value);
}
void PreSetAxis(int axis) {
if (!state.axes.contains(axis)) {
SetAxis(axis, 0);
}
}
float GetAxis(int axis, float range, float offset) const {
float GetAxis(int axis, float range) const {
std::lock_guard lock{mutex};
const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
return (value + offset) / range;
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
}
bool RumblePlay(u16 amp_low, u16 amp_high) {
@@ -187,10 +174,9 @@ public:
return false;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
float offset_y) const {
float x = GetAxis(axis_x, range, offset_x);
float y = GetAxis(axis_y, range, offset_y);
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
float x = GetAxis(axis_x, range);
float y = GetAxis(axis_y, range);
y = -y; // 3DS uses an y-axis inverse from SDL
// Make sure the coordinates are in the unit circle,
@@ -497,7 +483,7 @@ public:
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
const float axis_value = joystick->GetAxis(axis, 1.0f);
if (trigger_if_greater) {
return axis_value > threshold;
}
@@ -514,14 +500,12 @@ private:
class SDLAnalog final : public Input::AnalogDevice {
public:
explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
bool invert_x_, bool invert_y_, float deadzone_, float range_,
float offset_x_, float offset_y_)
bool invert_x_, bool invert_y_, float deadzone_, float range_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
offset_y(offset_y_) {}
invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
std::tuple<float, float> GetStatus() const override {
auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
const float r = std::sqrt((x * x) + (y * y));
if (invert_x) {
x = -x;
@@ -538,8 +522,8 @@ public:
}
std::tuple<float, float> GetRawStatus() const override {
const float x = joystick->GetAxis(axis_x, range, offset_x);
const float y = joystick->GetAxis(axis_y, range, offset_y);
const float x = joystick->GetAxis(axis_x, range);
const float y = joystick->GetAxis(axis_y, range);
return {x, -y};
}
@@ -571,8 +555,6 @@ private:
const bool invert_y;
const float deadzone;
const float range;
const float offset_x;
const float offset_y;
};
class SDLVibration final : public Input::VibrationDevice {
@@ -639,7 +621,7 @@ public:
trigger_if_greater(trigger_if_greater_) {}
Input::MotionStatus GetStatus() const override {
const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
const float axis_value = joystick->GetAxis(axis, 1.0f);
bool trigger = axis_value < threshold;
if (trigger_if_greater) {
trigger = axis_value > threshold;
@@ -738,13 +720,13 @@ public:
LOG_ERROR(Input, "Unknown direction {}", direction_name);
}
// This is necessary so accessing GetAxis with axis won't crash
joystick->PreSetAxis(axis);
joystick->SetAxis(axis, 0);
return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
}
const int button = params.Get("button", 0);
// This is necessary so accessing GetButton with button won't crash
joystick->PreSetButton(button);
joystick->SetButton(button, false);
return std::make_unique<SDLButton>(joystick, button, toggle);
}
@@ -775,15 +757,13 @@ public:
const std::string invert_y_value = params.Get("invert_y", "+");
const bool invert_x = invert_x_value == "-";
const bool invert_y = invert_y_value == "-";
const float offset_x = params.Get("offset_x", 0.0f);
const float offset_y = params.Get("offset_y", 0.0f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
joystick->PreSetAxis(axis_x);
joystick->PreSetAxis(axis_y);
joystick->SetAxis(axis_x, 0);
joystick->SetAxis(axis_y, 0);
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
range, offset_x, offset_y);
range);
}
private:
@@ -864,13 +844,13 @@ public:
LOG_ERROR(Input, "Unknown direction {}", direction_name);
}
// This is necessary so accessing GetAxis with axis won't crash
joystick->PreSetAxis(axis);
joystick->SetAxis(axis, 0);
return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
}
const int button = params.Get("button", 0);
// This is necessary so accessing GetButton with button won't crash
joystick->PreSetButton(button);
joystick->SetButton(button, false);
return std::make_unique<SDLButtonMotion>(joystick, button);
}
@@ -1015,7 +995,6 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
params.Set("port", port);
params.Set("guid", std::move(guid));
params.Set("button", button);
params.Set("toggle", false);
return params;
}
@@ -1155,15 +1134,13 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu
}
Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
int axis_y, float offset_x, float offset_y) {
int axis_y) {
Common::ParamPackage params;
params.Set("engine", "sdl");
params.Set("port", port);
params.Set("guid", guid);
params.Set("axis_x", axis_x);
params.Set("axis_y", axis_y);
params.Set("offset_x", offset_x);
params.Set("offset_y", offset_y);
params.Set("invert_x", "+");
params.Set("invert_y", "+");
return params;
@@ -1365,39 +1342,24 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
const auto& binding_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
if (params.Has("guid2")) {
joystick2->PreSetAxis(binding_left_x.value.axis);
joystick2->PreSetAxis(binding_left_y.value.axis);
const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
mapping.insert_or_assign(
Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
binding_left_x.value.axis, binding_left_y.value.axis,
left_offset_x, left_offset_y));
binding_left_x.value.axis, binding_left_y.value.axis));
} else {
joystick->PreSetAxis(binding_left_x.value.axis);
joystick->PreSetAxis(binding_left_y.value.axis);
const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
mapping.insert_or_assign(
Settings::NativeAnalog::LStick,
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
binding_left_x.value.axis, binding_left_y.value.axis,
left_offset_x, left_offset_y));
binding_left_x.value.axis, binding_left_y.value.axis));
}
const auto& binding_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
const auto& binding_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
joystick->PreSetAxis(binding_right_x.value.axis);
joystick->PreSetAxis(binding_right_y.value.axis);
const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
mapping.insert_or_assign(Settings::NativeAnalog::RStick,
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
binding_right_x.value.axis,
binding_right_y.value.axis, right_offset_x,
right_offset_y));
binding_right_y.value.axis));
return mapping;
}
@@ -1601,9 +1563,8 @@ public:
}
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
// Set offset to zero since the joystick is not on center
auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
first_axis, axis, 0, 0);
first_axis, axis);
first_axis = -1;
return params;
}

View File

@@ -1,10 +1,5 @@
add_subdirectory(host_shaders)
if(LIBVA_FOUND)
set_source_files_properties(command_classes/codecs/codec.cpp
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
endif()
add_library(video_core STATIC
buffer_cache/buffer_base.h
buffer_cache/buffer_cache.cpp
@@ -111,8 +106,6 @@ add_library(video_core STATIC
renderer_vulkan/maxwell_to_vk.cpp
renderer_vulkan/maxwell_to_vk.h
renderer_vulkan/pipeline_helper.h
renderer_vulkan/pipeline_statistics.cpp
renderer_vulkan/pipeline_statistics.h
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/renderer_vulkan.cpp
renderer_vulkan/vk_blit_screen.cpp

View File

@@ -485,7 +485,7 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
if (total_used_memory >= EXPECTED_MEMORY) {
if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) {
RunGarbageCollector();
}
++frame_tick;

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <fstream>
#include <vector>
#include "common/assert.h"
@@ -16,47 +17,10 @@ extern "C" {
}
namespace Tegra {
#if defined(LIBVA_FOUND)
// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
namespace {
constexpr std::array<const char*, 2> VAAPI_DRIVERS = {
"i915",
"amdgpu",
};
AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) {
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
if (*p == AV_PIX_FMT_VAAPI) {
return AV_PIX_FMT_VAAPI;
}
}
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
return *pix_fmts;
}
bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) {
AVDictionary* hwdevice_options = nullptr;
av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
for (const auto& driver : VAAPI_DRIVERS) {
av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI,
nullptr, hwdevice_options, 0);
if (hwdevice_error >= 0) {
LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
av_dict_free(&hwdevice_options);
return true;
}
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
}
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
av_dict_free(&hwdevice_options);
return false;
}
} // namespace
#endif
void AVFrameDeleter(AVFrame* ptr) {
av_frame_free(&ptr);
av_frame_unref(ptr);
av_free(ptr);
}
Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
@@ -68,31 +32,19 @@ Codec::~Codec() {
return;
}
// Free libav memory
AVFrame* av_frame{nullptr};
avcodec_send_packet(av_codec_ctx, nullptr);
AVFrame* av_frame = av_frame_alloc();
av_frame = av_frame_alloc();
avcodec_receive_frame(av_codec_ctx, av_frame);
avcodec_flush_buffers(av_codec_ctx);
av_frame_free(&av_frame);
avcodec_close(av_codec_ctx);
av_buffer_unref(&av_hw_device);
}
void Codec::InitializeHwdec() {
// Prioritize integrated GPU to mitigate bandwidth bottlenecks
#if defined(LIBVA_FOUND)
if (CreateVaapiHwdevice(&av_hw_device)) {
const auto hw_device_ctx = av_buffer_ref(av_hw_device);
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
av_codec_ctx->hw_device_ctx = hw_device_ctx;
av_codec_ctx->get_format = GetHwFormat;
return;
}
#endif
// TODO more GPU accelerated decoders
av_frame_unref(av_frame);
av_free(av_frame);
avcodec_close(av_codec_ctx);
}
void Codec::Initialize() {
AVCodecID codec;
AVCodecID codec{AV_CODEC_ID_NONE};
switch (current_codec) {
case NvdecCommon::VideoCodec::H264:
codec = AV_CODEC_ID_H264;
@@ -101,24 +53,22 @@ void Codec::Initialize() {
codec = AV_CODEC_ID_VP9;
break;
default:
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
return;
}
av_codec = avcodec_find_decoder(codec);
av_codec_ctx = avcodec_alloc_context3(av_codec);
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
InitializeHwdec();
if (!av_codec_ctx->hw_device_ctx) {
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
}
// TODO(ameerj): libavcodec gpu hw acceleration
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
if (av_error < 0) {
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
avcodec_close(av_codec_ctx);
av_buffer_unref(&av_hw_device);
return;
}
initialized = true;
return;
}
void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
@@ -130,64 +80,36 @@ void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
void Codec::Decode() {
const bool is_first_frame = !initialized;
if (is_first_frame) {
if (!initialized) {
Initialize();
}
bool vp9_hidden_frame = false;
AVPacket packet{};
av_init_packet(&packet);
std::vector<u8> frame_data;
if (current_codec == NvdecCommon::VideoCodec::H264) {
frame_data = h264_decoder->ComposeFrameHeader(state, is_first_frame);
} else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
frame_data = vp9_decoder->ComposeFrameHeader(state);
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
}
AVPacket packet{};
av_init_packet(&packet);
packet.data = frame_data.data();
packet.size = static_cast<s32>(frame_data.size());
if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) {
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret);
return;
}
// Only receive/store visible frames
if (vp9_hidden_frame) {
return;
}
AVFrame* hw_frame = av_frame_alloc();
AVFrame* sw_frame = hw_frame;
ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed");
if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) {
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
av_frame_free(&hw_frame);
return;
}
if (!hw_frame->width || !hw_frame->height) {
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
av_frame_free(&hw_frame);
return;
}
#if defined(LIBVA_FOUND)
// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license
if (hw_frame->format == AV_PIX_FMT_VAAPI) {
sw_frame = av_frame_alloc();
ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
// because Intel drivers crash unless using AV_PIX_FMT_NV12
sw_frame->format = AV_PIX_FMT_NV12;
const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0);
ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret);
av_frame_free(&hw_frame);
}
#endif
if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) {
UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
av_frame_free(&sw_frame);
return;
}
av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter});
if (av_frames.size() > 10) {
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
av_frames.pop();
avcodec_send_packet(av_codec_ctx, &packet);
if (!vp9_hidden_frame) {
// Only receive/store visible frames
AVFramePtr frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
avcodec_receive_frame(av_codec_ctx, frame.get());
av_frames.push(std::move(frame));
// Limit queue to 10 frames. Workaround for ZLA decode and queue spam
if (av_frames.size() > 10) {
av_frames.pop();
}
}
}
@@ -197,6 +119,7 @@ AVFramePtr Codec::GetCurrentFrame() {
if (av_frames.empty()) {
return AVFramePtr{nullptr, AVFrameDeleter};
}
AVFramePtr frame = std::move(av_frames.front());
av_frames.pop();
return frame;
@@ -221,5 +144,6 @@ std::string_view Codec::GetCurrentCodecName() const {
default:
return "Unknown";
}
}
};
} // namespace Tegra

View File

@@ -22,6 +22,7 @@ extern "C" {
namespace Tegra {
class GPU;
struct VicRegisters;
void AVFrameDeleter(AVFrame* ptr);
using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
@@ -54,13 +55,10 @@ public:
[[nodiscard]] std::string_view GetCurrentCodecName() const;
private:
void InitializeHwdec();
bool initialized{};
NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
AVCodec* av_codec{nullptr};
AVBufferRef* av_hw_device{nullptr};
AVCodecContext* av_codec_ctx{nullptr};
GPU& gpu;

View File

@@ -11,9 +11,6 @@
namespace Tegra::Decoder {
namespace {
constexpr u32 diff_update_probability = 252;
constexpr u32 frame_sync_code = 0x498342;
// Default compressed header probabilities once frame context resets
constexpr Vp9EntropyProbs default_probs{
.y_mode_prob{
@@ -364,7 +361,8 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
// surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
// order: last, golden, altref, current.
// order: last, golden, altref, current. It may be worthwhile to track the updates done here
// to avoid buffering frame data needed for reference frame updating in the header composition.
std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4,
vp9_info.frame_offsets.begin());
@@ -386,18 +384,33 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state)
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(),
current_frame.info.bitstream_size);
}
if (!next_frame.bit_stream.empty()) {
// Buffer two frames, saving the last show frame info
if (!next_next_frame.bit_stream.empty()) {
Vp9FrameContainer temp{
.info = current_frame.info,
.bit_stream = std::move(current_frame.bit_stream),
};
next_frame.info.show_frame = current_frame.info.last_frame_shown;
current_frame.info = next_frame.info;
current_frame.bit_stream = std::move(next_frame.bit_stream);
next_frame = std::move(temp);
next_next_frame.info.show_frame = current_frame.info.last_frame_shown;
current_frame.info = next_next_frame.info;
current_frame.bit_stream = std::move(next_next_frame.bit_stream);
next_next_frame = std::move(temp);
if (!next_frame.bit_stream.empty()) {
Vp9FrameContainer temp2{
.info = current_frame.info,
.bit_stream = std::move(current_frame.bit_stream),
};
next_frame.info.show_frame = current_frame.info.last_frame_shown;
current_frame.info = next_frame.info;
current_frame.bit_stream = std::move(next_frame.bit_stream);
next_frame = std::move(temp2);
} else {
next_frame.info = current_frame.info;
next_frame.bit_stream = std::move(current_frame.bit_stream);
}
} else {
next_frame.info = current_frame.info;
next_frame.bit_stream = std::move(current_frame.bit_stream);
next_next_frame.info = current_frame.info;
next_next_frame.bit_stream = std::move(current_frame.bit_stream);
}
return current_frame;
}
@@ -600,64 +613,86 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
// Reset context
prev_frame_probs = default_probs;
swap_ref_indices = false;
swap_next_golden = false;
loop_filter_ref_deltas.fill(0);
loop_filter_mode_deltas.fill(0);
frame_ctxs.fill(default_probs);
// allow frames offsets to stabilize before checking for golden frames
grace_period = 4;
// On key frames, all frame slots are set to the current frame,
// so the value of the selected slot doesn't really matter.
frame_ctxs.fill({current_frame_number, false, default_probs});
// intra only, meaning the frame can be recreated with no other references
current_frame_info.intra_only = true;
} else {
if (!current_frame_info.show_frame) {
uncomp_writer.WriteBit(current_frame_info.intra_only);
if (!current_frame_info.last_frame_was_key) {
swap_next_golden = !swap_next_golden;
}
} else {
current_frame_info.intra_only = false;
}
if (!current_frame_info.error_resilient_mode) {
uncomp_writer.WriteU(0, 2); // Reset frame context.
}
const auto& curr_offsets = current_frame_info.frame_offsets;
const auto& next_offsets = next_frame.info.frame_offsets;
const bool ref_frames_different = curr_offsets[1] != curr_offsets[2];
const bool next_references_swap =
(next_offsets[1] == curr_offsets[2]) || (next_offsets[2] == curr_offsets[1]);
const bool needs_ref_swap = ref_frames_different && next_references_swap;
if (needs_ref_swap) {
swap_ref_indices = !swap_ref_indices;
}
union {
u32 raw;
BitField<0, 1, u32> refresh_last;
BitField<1, 2, u32> refresh_golden;
BitField<2, 1, u32> refresh_alt;
} refresh_frame_flags;
refresh_frame_flags.raw = 0;
for (u32 index = 0; index < 3; ++index) {
// Refresh indices that use the current frame as an index
if (curr_offsets[3] == next_offsets[index]) {
refresh_frame_flags.raw |= 1u << index;
// Last, Golden, Altref frames
std::array<s32, 3> ref_frame_index{0, 1, 2};
// Set when next frame is hidden
// altref and golden references are swapped
if (swap_next_golden) {
ref_frame_index = std::array<s32, 3>{0, 2, 1};
}
// update Last Frame
u64 refresh_frame_flags = 1;
// golden frame may refresh, determined if the next golden frame offset is changed
bool golden_refresh = false;
if (grace_period <= 0) {
for (s32 index = 1; index < 3; ++index) {
if (current_frame_info.frame_offsets[index] !=
next_frame.info.frame_offsets[index]) {
current_frame_info.refresh_frame[index] = true;
golden_refresh = true;
grace_period = 3;
}
}
}
if (swap_ref_indices) {
const u32 temp = refresh_frame_flags.refresh_golden;
refresh_frame_flags.refresh_golden.Assign(refresh_frame_flags.refresh_alt.Value());
refresh_frame_flags.refresh_alt.Assign(temp);
if (current_frame_info.show_frame &&
(!next_frame.info.show_frame || next_frame.info.is_key_frame)) {
// Update golden frame
refresh_frame_flags = swap_next_golden ? 2 : 4;
}
if (!current_frame_info.show_frame) {
// Update altref
refresh_frame_flags = swap_next_golden ? 2 : 4;
} else if (golden_refresh) {
refresh_frame_flags = 3;
}
if (current_frame_info.intra_only) {
uncomp_writer.WriteU(frame_sync_code, 24);
uncomp_writer.WriteU(refresh_frame_flags.raw, 8);
uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
uncomp_writer.WriteBit(false); // Render and frame size different.
} else {
const bool swap_indices = needs_ref_swap ^ swap_ref_indices;
const auto ref_frame_index = swap_indices ? std::array{0, 2, 1} : std::array{0, 1, 2};
uncomp_writer.WriteU(refresh_frame_flags.raw, 8);
for (size_t index = 1; index < 4; index++) {
uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
for (s32 index = 1; index < 4; index++) {
uncomp_writer.WriteU(ref_frame_index[index - 1], 3);
uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1);
}
uncomp_writer.WriteBit(true); // Frame size with refs.
uncomp_writer.WriteBit(false); // Render and frame size different.
uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv);
@@ -679,9 +714,10 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
frame_ctx_idx = 1;
}
uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index.
prev_frame_probs = frame_ctxs[frame_ctx_idx]; // reference probabilities for compressed header
frame_ctxs[frame_ctx_idx] = current_frame_info.entropy;
uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index.
prev_frame_probs =
frame_ctxs[frame_ctx_idx].probs; // reference probabilities for compressed header
frame_ctxs[frame_ctx_idx] = {current_frame_number, false, current_frame_info.entropy};
uncomp_writer.WriteU(current_frame_info.first_level, 6);
uncomp_writer.WriteU(current_frame_info.sharpness_level, 3);
@@ -776,6 +812,7 @@ const std::vector<u8>& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters
current_frame_info = curr_frame.info;
bitstream = std::move(curr_frame.bit_stream);
}
// The uncompressed header routine sets PrevProb parameters needed for the compressed header
auto uncomp_writer = ComposeUncompressedHeader();
std::vector<u8> compressed_header = ComposeCompressedHeader();
@@ -791,6 +828,13 @@ const std::vector<u8>& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters
frame.begin() + uncompressed_header.size());
std::copy(bitstream.begin(), bitstream.end(),
frame.begin() + uncompressed_header.size() + compressed_header.size());
// keep track of frame number
current_frame_number++;
grace_period--;
// don't display hidden frames
hidden = !current_frame_info.show_frame;
return frame;
}

View File

@@ -14,6 +14,7 @@
namespace Tegra {
class GPU;
enum class FrameType { KeyFrame = 0, InterFrame = 1 };
namespace Decoder {
/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
@@ -123,7 +124,7 @@ public:
/// Returns true if the most recent frame was a hidden frame.
[[nodiscard]] bool WasFrameHidden() const {
return !current_frame_info.show_frame;
return hidden;
}
private:
@@ -177,12 +178,19 @@ private:
std::array<s8, 4> loop_filter_ref_deltas{};
std::array<s8, 2> loop_filter_mode_deltas{};
bool hidden = false;
s64 current_frame_number = -2; // since we buffer 2 frames
s32 grace_period = 6; // frame offsets need to stabilize
std::array<FrameContexts, 4> frame_ctxs{};
Vp9FrameContainer next_frame{};
std::array<Vp9EntropyProbs, 4> frame_ctxs{};
bool swap_ref_indices{};
Vp9FrameContainer next_next_frame{};
bool swap_next_golden{};
Vp9PictureInfo current_frame_info{};
Vp9EntropyProbs prev_frame_probs{};
s32 diff_update_probability = 252;
s32 frame_sync_code = 0x498342;
};
} // namespace Decoder

View File

@@ -296,6 +296,12 @@ struct RefPoolElement {
bool refresh{};
};
struct FrameContexts {
s64 from;
bool adapted;
Vp9EntropyProbs probs;
};
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
"Field " #field_name " has invalid position")

View File

@@ -39,7 +39,7 @@ void Nvdec::Execute() {
codec->Decode();
break;
default:
UNIMPLEMENTED_MSG("Codec {}", codec->GetCurrentCodecName());
UNIMPLEMENTED_MSG("Unknown codec {}", static_cast<u32>(codec->GetCurrentCodec()));
break;
}
}

View File

@@ -46,8 +46,11 @@ void Vic::ProcessMethod(Method method, u32 argument) {
case Method::SetOutputSurfaceLumaOffset:
output_surface_luma_address = arg;
break;
case Method::SetOutputSurfaceChromaOffset:
output_surface_chroma_address = arg;
case Method::SetOutputSurfaceChromaUOffset:
output_surface_chroma_u_address = arg;
break;
case Method::SetOutputSurfaceChromaVOffset:
output_surface_chroma_v_address = arg;
break;
default:
break;
@@ -62,10 +65,11 @@ void Vic::Execute() {
const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
const AVFramePtr frame_ptr = nvdec_processor->GetFrame();
const auto* frame = frame_ptr.get();
if (!frame) {
if (!frame || frame->width == 0 || frame->height == 0) {
return;
}
const auto pixel_format = static_cast<VideoPixelFormat>(config.pixel_format.Value());
const VideoPixelFormat pixel_format =
static_cast<VideoPixelFormat>(config.pixel_format.Value());
switch (pixel_format) {
case VideoPixelFormat::BGRA8:
case VideoPixelFormat::RGBA8: {
@@ -79,18 +83,16 @@ void Vic::Execute() {
sws_freeContext(scaler_ctx);
scaler_ctx = nullptr;
// Frames are decoded into either YUV420 or NV12 formats. Convert to desired format
scaler_ctx = sws_getContext(frame->width, frame->height,
static_cast<AVPixelFormat>(frame->format), frame->width,
frame->height, target_format, 0, nullptr, nullptr, nullptr);
// FFmpeg returns all frames in YUV420, convert it into expected format
scaler_ctx =
sws_getContext(frame->width, frame->height, AV_PIX_FMT_YUV420P, frame->width,
frame->height, target_format, 0, nullptr, nullptr, nullptr);
scaler_width = frame->width;
scaler_height = frame->height;
}
// Get Converted frame
const u32 width = static_cast<u32>(frame->width);
const u32 height = static_cast<u32>(frame->height);
const std::size_t linear_size = width * height * 4;
const std::size_t linear_size = frame->width * frame->height * 4;
// Only allocate frame_buffer once per stream, as the size is not expected to change
if (!converted_frame_buffer) {
@@ -107,10 +109,11 @@ void Vic::Execute() {
if (blk_kind != 0) {
// swizzle pitch linear to block linear
const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
const auto size =
Tegra::Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1,
block_height, 0);
luma_buffer.resize(size);
Tegra::Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(),
Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4,
frame->width, 4, luma_buffer.data(),
converted_frame_buffer.get(), block_height, 0, 0);
gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
@@ -128,65 +131,41 @@ void Vic::Execute() {
const std::size_t surface_height = config.surface_height_minus1 + 1;
const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width));
const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height));
const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
const std::size_t half_width = frame_width / 2;
const std::size_t half_height = frame_height / 2;
const std::size_t aligned_width = (surface_width + 0xff) & ~0xff;
const auto* luma_ptr = frame->data[0];
const auto* chroma_b_ptr = frame->data[1];
const auto* chroma_r_ptr = frame->data[2];
const auto stride = static_cast<size_t>(frame->linesize[0]);
const auto half_stride = static_cast<size_t>(frame->linesize[1]);
luma_buffer.resize(aligned_width * surface_height);
chroma_buffer.resize(aligned_width * surface_height / 2);
// Populate luma buffer
const u8* luma_src = frame->data[0];
for (std::size_t y = 0; y < frame_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
for (std::size_t x = 0; x < frame_width; ++x) {
luma_buffer[dst + x] = luma_src[src + x];
luma_buffer[dst + x] = luma_ptr[src + x];
}
}
gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
luma_buffer.size());
// Chroma
const std::size_t half_height = frame_height / 2;
const auto half_stride = static_cast<size_t>(frame->linesize[1]);
// Populate chroma buffer from both channels with interleaving.
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * half_stride;
const std::size_t dst = y * aligned_width;
switch (frame->format) {
case AV_PIX_FMT_YUV420P: {
// Frame from FFmpeg software
// Populate chroma buffer from both channels with interleaving.
const std::size_t half_width = frame_width / 2;
const u8* chroma_b_src = frame->data[1];
const u8* chroma_r_src = frame->data[2];
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * half_stride;
const std::size_t dst = y * aligned_width;
for (std::size_t x = 0; x < half_width; ++x) {
chroma_buffer[dst + x * 2] = chroma_b_src[src + x];
chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x];
}
for (std::size_t x = 0; x < half_width; ++x) {
chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x];
chroma_buffer[dst + x * 2 + 1] = chroma_r_ptr[src + x];
}
break;
}
case AV_PIX_FMT_NV12: {
// Frame from VA-API hardware
// This is already interleaved so just copy
const u8* chroma_src = frame->data[1];
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
for (std::size_t x = 0; x < frame_width; ++x) {
chroma_buffer[dst + x] = chroma_src[src + x];
}
}
break;
}
default:
UNREACHABLE();
break;
}
gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(),
chroma_buffer.size());
break;
}

View File

@@ -22,8 +22,8 @@ public:
SetControlParams = 0x1c1,
SetConfigStructOffset = 0x1c2,
SetOutputSurfaceLumaOffset = 0x1c8,
SetOutputSurfaceChromaOffset = 0x1c9,
SetOutputSurfaceChromaUnusedOffset = 0x1ca
SetOutputSurfaceChromaUOffset = 0x1c9,
SetOutputSurfaceChromaVOffset = 0x1ca
};
explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor);
@@ -64,7 +64,8 @@ private:
GPUVAddr config_struct_address{};
GPUVAddr output_surface_luma_address{};
GPUVAddr output_surface_chroma_address{};
GPUVAddr output_surface_chroma_u_address{};
GPUVAddr output_surface_chroma_v_address{};
SwsContext* scaler_ctx{};
s32 scaler_width{};

View File

@@ -10,27 +10,33 @@
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1
#define BINDING_ENC_BUFFER 1
#define BINDING_SWIZZLE_BUFFER 2
#define BINDING_OUTPUT_IMAGE 3
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_ENC_BUFFER 2
#define BINDING_OUTPUT_IMAGE 0
#endif
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
BEGIN_PUSH_CONSTANTS
UNIFORM(1) uvec2 block_dims;
UNIFORM(2) uint layer_stride;
UNIFORM(3) uint block_size;
UNIFORM(4) uint x_shift;
UNIFORM(5) uint block_height;
UNIFORM(6) uint block_height_mask;
UNIFORM(2) uint bytes_per_block_log2;
UNIFORM(3) uint layer_stride;
UNIFORM(4) uint block_size;
UNIFORM(5) uint x_shift;
UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask;
END_PUSH_CONSTANTS
struct EncodingData {
@@ -49,35 +55,45 @@ struct TexelWeightParams {
bool void_extent_hdr;
};
// Swizzle data
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 {
uvec4 astc_data[];
uint astc_data[];
};
// ASTC Encodings data
layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues {
EncodingData encoding_values[];
};
layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_Z = 1;
const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uint GOB_SIZE_Z_SHIFT = 0;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
const uint BYTES_PER_BLOCK_LOG2 = 4;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
const int BLOCK_SIZE_IN_BYTES = 16;
const int BLOCK_INFO_ERROR = 0;
const int BLOCK_INFO_VOID_EXTENT_HDR = 1;
const int BLOCK_INFO_VOID_EXTENT_LDR = 2;
const int BLOCK_INFO_NORMAL = 3;
const int JUST_BITS = 0;
const int QUINT = 1;
const int TRIT = 2;
// ASTC Encodings data, sorted in ascending order based on their BitLength value
// (see GetBitLength() function)
EncodingData encoding_values[22] = EncodingData[](
EncodingData(JUST_BITS, 0, 0, 0), EncodingData(JUST_BITS, 1, 0, 0), EncodingData(TRIT, 0, 0, 0),
EncodingData(JUST_BITS, 2, 0, 0), EncodingData(QUINT, 0, 0, 0), EncodingData(TRIT, 1, 0, 0),
EncodingData(JUST_BITS, 3, 0, 0), EncodingData(QUINT, 1, 0, 0), EncodingData(TRIT, 2, 0, 0),
EncodingData(JUST_BITS, 4, 0, 0), EncodingData(QUINT, 2, 0, 0), EncodingData(TRIT, 3, 0, 0),
EncodingData(JUST_BITS, 5, 0, 0), EncodingData(QUINT, 3, 0, 0), EncodingData(TRIT, 4, 0, 0),
EncodingData(JUST_BITS, 6, 0, 0), EncodingData(QUINT, 4, 0, 0), EncodingData(TRIT, 5, 0, 0),
EncodingData(JUST_BITS, 7, 0, 0), EncodingData(QUINT, 5, 0, 0), EncodingData(TRIT, 6, 0, 0),
EncodingData(JUST_BITS, 8, 0, 0)
);
// The following constants are expanded variants of the Replicate()
// function calls corresponding to the following arguments:
// value: index into the generated table
@@ -119,37 +135,44 @@ const uint REPLICATE_7_BIT_TO_8_TABLE[128] =
// Input ASTC texture globals
uint current_index = 0;
int bitsread = 0;
int total_bitsread = 0;
uvec4 local_buff;
uint total_bitsread = 0;
uint local_buff[16];
// Color data globals
uvec4 color_endpoint_data;
uint color_endpoint_data[16];
int color_bitsread = 0;
uint total_color_bitsread = 0;
int color_index = 0;
// Four values, two endpoints, four maximum paritions
uint color_values[32];
int colvals_index = 0;
// Weight data globals
uvec4 texel_weight_data;
uint texel_weight_data[16];
int texel_bitsread = 0;
uint total_texel_bitsread = 0;
int texel_index = 0;
bool texel_flag = false;
// Global "vectors" to be pushed into when decoding
EncodingData result_vector[144];
EncodingData result_vector[100];
int result_index = 0;
EncodingData texel_vector[144];
EncodingData texel_vector[100];
int texel_vector_index = 0;
uint unquantized_texel_weights[2][144];
uint SwizzleOffset(uvec2 pos) {
uint x = pos.x;
uint y = pos.y;
return ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
(y % 2) * 16 + (x % 16);
pos = pos & SWIZZLE_MASK;
return swizzle_table[pos.y * 64 + pos.x];
}
uint ReadTexel(uint offset) {
// extract the 8-bit value from the 32-bit packed data.
return bitfieldExtract(astc_data[offset / 4], int((offset * 8) & 24), 8);
}
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
@@ -255,10 +278,14 @@ uint Hash52(uint p) {
return p;
}
uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) {
uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bool small_block) {
if (partition_count == 1) {
return 0;
}
if (small_block) {
x <<= 1;
y <<= 1;
z <<= 1;
}
seed += (partition_count - 1) * 1024;
@@ -272,6 +299,10 @@ uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool sma
uint seed6 = uint((rnum >> 20) & 0xF);
uint seed7 = uint((rnum >> 24) & 0xF);
uint seed8 = uint((rnum >> 28) & 0xF);
uint seed9 = uint((rnum >> 18) & 0xF);
uint seed10 = uint((rnum >> 22) & 0xF);
uint seed11 = uint((rnum >> 26) & 0xF);
uint seed12 = uint(((rnum >> 30) | (rnum << 2)) & 0xF);
seed1 = (seed1 * seed1);
seed2 = (seed2 * seed2);
@@ -281,8 +312,12 @@ uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool sma
seed6 = (seed6 * seed6);
seed7 = (seed7 * seed7);
seed8 = (seed8 * seed8);
seed9 = (seed9 * seed9);
seed10 = (seed10 * seed10);
seed11 = (seed11 * seed11);
seed12 = (seed12 * seed12);
uint sh1, sh2;
int sh1, sh2, sh3;
if ((seed & 1) > 0) {
sh1 = (seed & 2) > 0 ? 4 : 5;
sh2 = (partition_count == 3) ? 6 : 5;
@@ -290,19 +325,25 @@ uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool sma
sh1 = (partition_count == 3) ? 6 : 5;
sh2 = (seed & 2) > 0 ? 4 : 5;
}
seed1 >>= sh1;
seed2 >>= sh2;
seed3 >>= sh1;
seed4 >>= sh2;
seed5 >>= sh1;
seed6 >>= sh2;
seed7 >>= sh1;
seed8 >>= sh2;
sh3 = (seed & 0x10) > 0 ? sh1 : sh2;
uint a = seed1 * x + seed2 * y + (rnum >> 14);
uint b = seed3 * x + seed4 * y + (rnum >> 10);
uint c = seed5 * x + seed6 * y + (rnum >> 6);
uint d = seed7 * x + seed8 * y + (rnum >> 2);
seed1 = (seed1 >> sh1);
seed2 = (seed2 >> sh2);
seed3 = (seed3 >> sh1);
seed4 = (seed4 >> sh2);
seed5 = (seed5 >> sh1);
seed6 = (seed6 >> sh2);
seed7 = (seed7 >> sh1);
seed8 = (seed8 >> sh2);
seed9 = (seed9 >> sh3);
seed10 = (seed10 >> sh3);
seed11 = (seed11 >> sh3);
seed12 = (seed12 >> sh3);
uint a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
uint b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
uint c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
uint d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
a &= 0x3F;
b &= 0x3F;
@@ -327,37 +368,58 @@ uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool sma
}
}
uint ExtractBits(uvec4 payload, int offset, int bits) {
if (bits <= 0) {
uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) {
return SelectPartition(seed, x, y, 0, partition_count, small_block);
}
uint ReadBit() {
if (current_index >= local_buff.length()) {
return 0;
}
int last_offset = offset + bits - 1;
int shifted_offset = offset >> 5;
if ((last_offset >> 5) == shifted_offset) {
return bitfieldExtract(payload[shifted_offset], offset & 31, bits);
uint bit = bitfieldExtract(local_buff[current_index], bitsread, 1);
++bitsread;
++total_bitsread;
if (bitsread == 8) {
++current_index;
bitsread = 0;
}
int first_bits = 32 - (offset & 31);
int result_first = int(bitfieldExtract(payload[shifted_offset], offset & 31, first_bits));
int result_second = int(bitfieldExtract(payload[shifted_offset + 1], 0, bits - first_bits));
return result_first | (result_second << first_bits);
return bit;
}
uint StreamBits(uint num_bits) {
int int_bits = int(num_bits);
uint ret = ExtractBits(local_buff, total_bitsread, int_bits);
total_bitsread += int_bits;
uint ret = 0;
for (uint i = 0; i < num_bits; i++) {
ret |= ((ReadBit() & 1) << i);
}
return ret;
}
uint ReadColorBit() {
uint bit = 0;
if (texel_flag) {
bit = bitfieldExtract(texel_weight_data[texel_index], texel_bitsread, 1);
++texel_bitsread;
++total_texel_bitsread;
if (texel_bitsread == 8) {
++texel_index;
texel_bitsread = 0;
}
} else {
bit = bitfieldExtract(color_endpoint_data[color_index], color_bitsread, 1);
++color_bitsread;
++total_color_bitsread;
if (color_bitsread == 8) {
++color_index;
color_bitsread = 0;
}
}
return bit;
}
uint StreamColorBits(uint num_bits) {
uint ret = 0;
int int_bits = int(num_bits);
if (texel_flag) {
ret = ExtractBits(texel_weight_data, texel_bitsread, int_bits);
texel_bitsread += int_bits;
} else {
ret = ExtractBits(color_endpoint_data, color_bitsread, int_bits);
color_bitsread += int_bits;
for (uint i = 0; i < num_bits; i++) {
ret |= ((ReadColorBit() & 1) << i);
}
return ret;
}
@@ -534,16 +596,22 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits) {
for (uint i = 0; i < num_partitions; i++) {
num_values += ((modes[i] >> 2) + 1) << 1;
}
// Find the largest encoding that's within color_data_bits
// TODO(ameerj): profile with binary search
int range = 0;
while (++range < encoding_values.length()) {
int range = 256;
while (--range > 0) {
EncodingData val = encoding_values[range];
uint bit_length = GetBitLength(num_values, range);
if (bit_length > color_data_bits) {
if (bit_length <= color_data_bits) {
while (--range > 0) {
EncodingData newval = encoding_values[range];
if (newval.encoding != val.encoding && newval.num_bits != val.num_bits) {
break;
}
}
++range;
break;
}
}
DecodeIntegerSequence(range - 1, num_values);
DecodeIntegerSequence(range, num_values);
uint out_index = 0;
for (int itr = 0; itr < result_index; ++itr) {
if (out_index >= num_values) {
@@ -960,7 +1028,7 @@ int FindLayout(uint mode) {
return 5;
}
TexelWeightParams DecodeBlockInfo() {
TexelWeightParams DecodeBlockInfo(uint block_index) {
TexelWeightParams params = TexelWeightParams(uvec2(0), 0, false, false, false, false);
uint mode = StreamBits(11);
if ((mode & 0x1ff) == 0x1fc) {
@@ -1042,10 +1110,10 @@ TexelWeightParams DecodeBlockInfo() {
}
weight_index -= 2;
if ((mode_layout != 9) && ((mode & 0x200) != 0)) {
const int max_weights[6] = int[6](7, 8, 9, 10, 11, 12);
const int max_weights[6] = int[6](9, 11, 15, 19, 23, 31);
params.max_weight = max_weights[weight_index];
} else {
const int max_weights[6] = int[6](1, 2, 3, 4, 5, 6);
const int max_weights[6] = int[6](1, 2, 3, 4, 5, 7);
params.max_weight = max_weights[weight_index];
}
return params;
@@ -1076,8 +1144,8 @@ void FillVoidExtentLDR(ivec3 coord) {
}
}
void DecompressBlock(ivec3 coord) {
TexelWeightParams params = DecodeBlockInfo();
void DecompressBlock(ivec3 coord, uint block_index) {
TexelWeightParams params = DecodeBlockInfo(block_index);
if (params.error_state) {
FillError(coord);
return;
@@ -1144,7 +1212,7 @@ void DecompressBlock(ivec3 coord) {
// Read color data...
uint color_data_bits = remaining_bits;
while (remaining_bits > 0) {
int nb = int(min(remaining_bits, 32U));
int nb = int(min(remaining_bits, 8U));
uint b = StreamBits(nb);
color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb));
++ced_pointer;
@@ -1186,20 +1254,25 @@ void DecompressBlock(ivec3 coord) {
ComputeEndpoints(endpoints[i][0], endpoints[i][1], color_endpoint_mode[i]);
}
texel_weight_data = local_buff;
texel_weight_data = bitfieldReverse(texel_weight_data).wzyx;
for (uint i = 0; i < 16; i++) {
texel_weight_data[i] = local_buff[i];
}
for (uint i = 0; i < 8; i++) {
#define REVERSE_BYTE(b) ((b * 0x0802U & 0x22110U) | (b * 0x8020U & 0x88440U)) * 0x10101U >> 16
uint a = REVERSE_BYTE(texel_weight_data[i]);
uint b = REVERSE_BYTE(texel_weight_data[15 - i]);
#undef REVERSE_BYTE
texel_weight_data[i] = uint(bitfieldExtract(b, 0, 8));
texel_weight_data[15 - i] = uint(bitfieldExtract(a, 0, 8));
}
uint clear_byte_start =
(GetPackedBitSize(params.size, params.dual_plane, params.max_weight) >> 3) + 1;
uint byte_insert = ExtractBits(texel_weight_data, int(clear_byte_start - 1) * 8, 8) &
texel_weight_data[clear_byte_start - 1] =
texel_weight_data[clear_byte_start - 1] &
uint(
((1 << (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) % 8)) - 1));
uint vec_index = (clear_byte_start - 1) >> 2;
texel_weight_data[vec_index] =
bitfieldInsert(texel_weight_data[vec_index], byte_insert, int((clear_byte_start - 1) % 4) * 8, 8);
for (uint i = clear_byte_start; i < 16; ++i) {
uint idx = i >> 2;
texel_weight_data[idx] = bitfieldInsert(texel_weight_data[idx], 0, int(i % 4) * 8, 8);
for (uint i = 0; i < 16 - clear_byte_start; i++) {
texel_weight_data[clear_byte_start + i] = 0U;
}
texel_flag = true; // use texel "vector" and bit stream in integer decoding
DecodeIntegerSequence(params.max_weight, GetNumWeightValues(params.size, params.dual_plane));
@@ -1208,11 +1281,8 @@ void DecompressBlock(ivec3 coord) {
for (uint j = 0; j < block_dims.y; j++) {
for (uint i = 0; i < block_dims.x; i++) {
uint local_partition = 0;
if (num_partitions > 1) {
local_partition = Select2DPartition(partition_index, i, j, num_partitions,
uint local_partition = Select2DPartition(partition_index, i, j, num_partitions,
(block_dims.y * block_dims.x) < 32);
}
vec4 p;
uvec4 C0 = ReplicateByteTo16(endpoints[local_partition][0]);
uvec4 C1 = ReplicateByteTo16(endpoints[local_partition][1]);
@@ -1233,7 +1303,7 @@ void DecompressBlock(ivec3 coord) {
void main() {
uvec3 pos = gl_GlobalInvocationID;
pos.x <<= BYTES_PER_BLOCK_LOG2;
pos.x <<= bytes_per_block_log2;
// Read as soon as possible due to its latency
const uint swizzle = SwizzleOffset(pos.xy);
@@ -1251,8 +1321,13 @@ void main() {
if (any(greaterThanEqual(coord, imageSize(dest_image)))) {
return;
}
uint block_index =
pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x;
current_index = 0;
bitsread = 0;
local_buff = astc_data[offset / 16];
DecompressBlock(coord);
for (int i = 0; i < 16; i++) {
local_buff[i] = ReadTexel(offset + i);
}
DecompressBlock(coord, block_index);
}

View File

@@ -27,7 +27,7 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
}
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
const Layout::FramebufferLayout& layout) {
if (renderer_settings.screenshot_requested) {
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");

View File

@@ -24,7 +24,7 @@ struct RendererSettings {
// Screenshot
std::atomic<bool> screenshot_requested{false};
void* screenshot_bits{};
std::function<void(bool)> screenshot_complete_callback;
std::function<void()> screenshot_complete_callback;
Layout::FramebufferLayout screenshot_framebuffer_layout;
};
@@ -80,7 +80,7 @@ public:
void RefreshBaseSettings();
/// Request a screenshot of the next frame
void RequestScreenshot(void* data, std::function<void(bool)> callback,
void RequestScreenshot(void* data, std::function<void()> callback,
const Layout::FramebufferLayout& layout);
protected:

View File

@@ -486,7 +486,7 @@ void RendererOpenGL::RenderScreenshot() {
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
renderer_settings.screenshot_complete_callback(true);
renderer_settings.screenshot_complete_callback();
renderer_settings.screenshot_requested = false;
}

View File

@@ -60,14 +60,19 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
astc_buffer.Create();
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_ENCODINGS_VALUES), &ASTC_ENCODINGS_VALUES,
0);
}
UtilShaders::~UtilShaders() = default;
void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
static constexpr GLuint BINDING_ENC_BUFFER = 2;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
const Extent2D tile_size{
@@ -75,32 +80,34 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
.height = VideoCore::Surface::DefaultBlockHeight(image.info.format),
};
program_manager.BindComputeProgram(astc_decoder_program.handle);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle);
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
glUniform2ui(1, tile_size.width, tile_size.height);
// Ensure buffer data is valid before dispatching
glFlush();
for (const SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
ASSERT(params.bytes_per_block_log2 == 4);
glUniform1ui(2, params.layer_stride);
glUniform1ui(3, params.block_size);
glUniform1ui(4, params.x_shift);
glUniform1ui(5, params.block_height);
glUniform1ui(6, params.block_height_mask);
glUniform1ui(2, params.bytes_per_block_log2);
glUniform1ui(3, params.layer_stride);
glUniform1ui(4, params.block_size);
glUniform1ui(5, params.x_shift);
glUniform1ui(6, params.block_height);
glUniform1ui(7, params.block_height_mask);
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
GL_WRITE_ONLY, GL_RGBA8);
// ASTC texture data
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset);
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
GL_WRITE_ONLY, GL_RGBA8);
glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
}

View File

@@ -62,6 +62,7 @@ private:
ProgramManager& program_manager;
OGLBuffer swizzle_table_buffer;
OGLBuffer astc_buffer;
OGLProgram astc_decoder_program;
OGLProgram block_linear_unswizzle_2d_program;

View File

@@ -1,100 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include <fmt/format.h>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
using namespace std::string_view_literals;
static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
switch (statistic.format) {
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
return static_cast<u64>(statistic.value.i64);
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
return statistic.value.u64;
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
return static_cast<u64>(statistic.value.f64);
default:
return 0;
}
}
PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
void PipelineStatistics::Collect(VkPipeline pipeline) {
const auto& dev{device.GetLogical()};
const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
const u32 num_executables{static_cast<u32>(properties.size())};
for (u32 executable = 0; executable < num_executables; ++executable) {
const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
if (statistics.empty()) {
continue;
}
Stats stage_stats;
for (const auto& statistic : statistics) {
const char* const name{statistic.name};
if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
stage_stats.code_size = GetUint64(statistic);
} else if (name == "Register Count"sv) {
stage_stats.register_count = GetUint64(statistic);
} else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
stage_stats.sgpr_count = GetUint64(statistic);
} else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
stage_stats.vgpr_count = GetUint64(statistic);
} else if (name == "Branches"sv) {
stage_stats.branches_count = GetUint64(statistic);
} else if (name == "Basic Block Count"sv) {
stage_stats.basic_block_count = GetUint64(statistic);
}
}
std::lock_guard lock{mutex};
collected_stats.push_back(stage_stats);
}
}
void PipelineStatistics::Report() const {
double num{};
Stats total;
{
std::lock_guard lock{mutex};
for (const Stats& stats : collected_stats) {
total.code_size += stats.code_size;
total.register_count += stats.register_count;
total.sgpr_count += stats.sgpr_count;
total.vgpr_count += stats.vgpr_count;
total.branches_count += stats.branches_count;
total.basic_block_count += stats.basic_block_count;
}
num = static_cast<double>(collected_stats.size());
}
std::string report;
const auto add = [&](const char* fmt, u64 value) {
if (value > 0) {
report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
}
};
add("Code size: {:9.03f}\n", total.code_size);
add("Register count: {:9.03f}\n", total.register_count);
add("SGPRs: {:9.03f}\n", total.sgpr_count);
add("VGPRs: {:9.03f}\n", total.vgpr_count);
add("Branches count: {:9.03f}\n", total.branches_count);
add("Basic blocks: {:9.03f}\n", total.basic_block_count);
LOG_INFO(Render_Vulkan,
"\nAverage pipeline statistics\n"
"==========================================\n"
"{}\n",
report);
}
} // namespace Vulkan

View File

@@ -1,40 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <mutex>
#include <vector>
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
class Device;
class PipelineStatistics {
public:
explicit PipelineStatistics(const Device& device_);
void Collect(VkPipeline pipeline);
void Report() const;
private:
struct Stats {
u64 code_size{};
u64 register_count{};
u64 sgpr_count{};
u64 vgpr_count{};
u64 branches_count{};
u64 basic_block_count{};
};
const Device& device;
mutable std::mutex mutex;
std::vector<Stats> collected_stats;
};
} // namespace Vulkan

View File

@@ -138,7 +138,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
const bool use_accelerated =
rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
const bool is_srgb = use_accelerated && screen_info.is_srgb;
RenderScreenshot(*framebuffer, use_accelerated);
bool has_been_recreated = false;
const auto recreate_swapchain = [&] {
@@ -163,7 +162,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
if (has_been_recreated) {
blit_screen.Recreate();
}
const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated);
scheduler.Flush(render_semaphore);
scheduler.WaitWorker();
swapchain.Present(render_semaphore);
@@ -194,153 +193,4 @@ void RendererVulkan::Report() const {
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
}
void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated) {
if (!renderer_settings.screenshot_requested) {
return;
}
const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
vk::Image staging_image = device.GetLogical().CreateImage(VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_B8G8R8A8_UNORM,
.extent =
{
.width = layout.width,
.height = layout.height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
});
const auto image_commit = memory_allocator.Commit(staging_image, MemoryUsage::DeviceLocal);
const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = *staging_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = screen_info.is_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM,
.components{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
});
const VkExtent2D render_area{.width = layout.width, .height = layout.height};
const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
// Since we're not rendering to the screen, ignore the render semaphore.
void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
const VkBufferCreateInfo dst_buffer_info{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = buffer_size,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_buffer_info);
MemoryCommit dst_buffer_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *staging_image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
const VkImageMemoryBarrier image_write_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *staging_image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
static constexpr VkMemoryBarrier memory_write_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
};
const VkBufferImageCopy copy{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset{.x = 0, .y = 0, .z = 0},
.imageExtent{
.width = layout.width,
.height = layout.height,
.depth = 1,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, read_barrier);
cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
copy);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0, memory_write_barrier, nullptr, image_write_barrier);
});
// Ensure the copy is fully completed before saving the screenshot
scheduler.Finish();
// Copy backing image data to the QImage screenshot buffer
const auto dst_memory_map = dst_buffer_memory.Map();
std::memcpy(renderer_settings.screenshot_bits, dst_memory_map.data(), dst_memory_map.size());
renderer_settings.screenshot_complete_callback(false);
renderer_settings.screenshot_requested = false;
}
} // namespace Vulkan

View File

@@ -54,8 +54,6 @@ public:
private:
void Report() const;
void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated);
Core::TelemetrySession& telemetry_session;
Core::Memory::Memory& cpu_memory;
Tegra::GPU& gpu;

View File

@@ -130,10 +130,7 @@ void VKBlitScreen::Recreate() {
CreateDynamicResources();
}
VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
const VkFramebuffer& host_framebuffer,
const Layout::FramebufferLayout layout, VkExtent2D render_area,
bool use_accelerated) {
VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) {
RefreshResources(framebuffer);
// Finish any pending renderpass
@@ -148,8 +145,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
use_accelerated ? screen_info.image_view : *raw_image_views[image_index]);
BufferData data;
SetUniformData(data, layout);
SetVertexData(data, framebuffer, layout);
SetUniformData(data, framebuffer);
SetVertexData(data, framebuffer);
const std::span<u8> mapped_span = buffer_commit.Map();
std::memcpy(mapped_span.data(), &data, sizeof(data));
@@ -223,73 +220,50 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
});
}
scheduler.Record(
[this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) {
const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
const VkClearValue clear_color{
.color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
};
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = *renderpass,
.framebuffer = host_framebuffer,
.renderArea =
{
.offset = {0, 0},
.extent = size,
},
.clearValueCount = 1,
.pClearValues = &clear_color,
};
const VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(size.width),
.height = static_cast<float>(size.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
const VkRect2D scissor{
.offset = {0, 0},
.extent = size,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
scheduler.Record([this, image_index, size = swapchain.GetSize()](vk::CommandBuffer cmdbuf) {
const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
const VkClearValue clear_color{
.color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
};
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = *renderpass,
.framebuffer = *framebuffers[image_index],
.renderArea =
{
.offset = {0, 0},
.extent = size,
},
.clearValueCount = 1,
.pClearValues = &clear_color,
};
const VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(size.width),
.height = static_cast<float>(size.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
const VkRect2D scissor{
.offset = {0, 0},
.extent = size,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
descriptor_sets[image_index], {});
cmdbuf.Draw(4, 1, 0, 0);
cmdbuf.EndRenderPass();
});
return *semaphores[image_index];
}
VkSemaphore VKBlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated) {
const std::size_t image_index = swapchain.GetImageIndex();
const VkExtent2D render_area = swapchain.GetSize();
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated);
}
vk::Framebuffer VKBlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = *renderpass,
.attachmentCount = 1,
.pAttachments = &image_view,
.width = extent.width,
.height = extent.height,
.layers = 1,
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
descriptor_sets[image_index], {});
cmdbuf.Draw(4, 1, 0, 0);
cmdbuf.EndRenderPass();
});
return *semaphores[image_index];
}
void VKBlitScreen::CreateStaticResources() {
@@ -635,9 +609,22 @@ void VKBlitScreen::CreateFramebuffers() {
const VkExtent2D size{swapchain.GetSize()};
framebuffers.resize(image_count);
VkFramebufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = *renderpass,
.attachmentCount = 1,
.pAttachments = nullptr,
.width = size.width,
.height = size.height,
.layers = 1,
};
for (std::size_t i = 0; i < image_count; ++i) {
const VkImageView image_view{swapchain.GetImageViewIndex(i)};
framebuffers[i] = CreateFramebuffer(image_view, size);
ci.pAttachments = &image_view;
framebuffers[i] = device.GetLogical().CreateFramebuffer(ci);
}
}
@@ -765,13 +752,15 @@ void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView imag
device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
}
void VKBlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
void VKBlitScreen::SetUniformData(BufferData& data,
const Tegra::FramebufferConfig& framebuffer) const {
const auto& layout = render_window.GetFramebufferLayout();
data.uniform.modelview_matrix =
MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
}
void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
const Layout::FramebufferLayout layout) const {
void VKBlitScreen::SetVertexData(BufferData& data,
const Tegra::FramebufferConfig& framebuffer) const {
const auto& framebuffer_transform_flags = framebuffer.transform_flags;
const auto& framebuffer_crop_rect = framebuffer.crop_rect;
@@ -809,7 +798,7 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
static_cast<f32>(screen_info.height);
}
const auto& screen = layout.screen;
const auto& screen = render_window.GetFramebufferLayout().screen;
const auto x = static_cast<f32>(screen.left);
const auto y = static_cast<f32>(screen.top);
const auto w = static_cast<f32>(screen.GetWidth());

View File

@@ -56,16 +56,8 @@ public:
void Recreate();
[[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
const VkFramebuffer& host_framebuffer,
const Layout::FramebufferLayout layout, VkExtent2D render_area,
bool use_accelerated);
[[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated);
[[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
VkExtent2D extent);
private:
struct BufferData;
@@ -89,9 +81,8 @@ private:
void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
const Layout::FramebufferLayout layout) const;
void SetUniformData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const;
void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const;
u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,

View File

@@ -30,12 +30,16 @@
namespace Vulkan {
using Tegra::Texture::SWIZZLE_TABLE;
using Tegra::Texture::ASTC::ASTC_ENCODINGS_VALUES;
using namespace Tegra::Texture::ASTC;
namespace {
constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0;
constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 1;
constexpr size_t ASTC_NUM_BINDINGS = 2;
constexpr u32 ASTC_BINDING_ENC_BUFFER = 1;
constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 2;
constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 3;
constexpr size_t ASTC_NUM_BINDINGS = 4;
template <size_t size>
inline constexpr VkPushConstantRange COMPUTE_PUSH_CONSTANT_RANGE{
@@ -71,7 +75,7 @@ constexpr DescriptorBankInfo INPUT_OUTPUT_BANK_INFO{
.score = 2,
};
constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> ASTC_DESCRIPTOR_SET_BINDINGS{{
constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDINGS{{
{
.binding = ASTC_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
@@ -79,6 +83,20 @@ constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> ASTC_DESCR
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = ASTC_BINDING_ENC_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = ASTC_BINDING_SWIZZLE_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = ASTC_BINDING_OUTPUT_IMAGE,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
@@ -90,12 +108,12 @@ constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> ASTC_DESCR
constexpr DescriptorBankInfo ASTC_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 1,
.storage_buffers = 3,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 1,
.score = 2,
.score = 4,
};
constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{
@@ -117,6 +135,22 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
.offset = ASTC_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = ASTC_BINDING_ENC_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = ASTC_BINDING_ENC_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = ASTC_BINDING_SWIZZLE_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = ASTC_BINDING_SWIZZLE_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = ASTC_BINDING_OUTPUT_IMAGE,
.dstArrayElement = 0,
@@ -129,6 +163,7 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
struct AstcPushConstants {
std::array<u32, 2> blocks_dims;
u32 bytes_per_block_log2;
u32 layer_stride;
u32 block_size;
u32 x_shift;
@@ -319,6 +354,46 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_,
ASTCDecoderPass::~ASTCDecoderPass() = default;
void ASTCDecoderPass::MakeDataBuffer() {
constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_ENCODINGS_VALUES) + sizeof(SWIZZLE_TABLE);
data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = TOTAL_BUFFER_SIZE,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
});
data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload);
const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload);
std::memcpy(staging_ref.mapped_span.data(), &ASTC_ENCODINGS_VALUES,
sizeof(ASTC_ENCODINGS_VALUES));
// Tack on the swizzle table at the end of the buffer
std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_ENCODINGS_VALUES), &SWIZZLE_TABLE,
sizeof(SWIZZLE_TABLE));
scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer,
TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier write_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
const VkBufferCopy copy{
.srcOffset = offset,
.dstOffset = 0,
.size = TOTAL_BUFFER_SIZE,
};
cmdbuf.CopyBuffer(src, dst, copy);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, write_barrier);
});
}
void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
using namespace VideoCommon::Accelerated;
@@ -327,6 +402,9 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
VideoCore::Surface::DefaultBlockHeight(image.info.format),
};
scheduler.RequestOutsideRenderPassOperationContext();
if (!data_buffer) {
MakeDataBuffer();
}
const VkPipeline vk_pipeline = *pipeline;
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
@@ -358,13 +436,16 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
});
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
const u32 num_dispatches_z = image.info.resources.layers;
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset);
update_descriptor_queue.AddBuffer(*data_buffer, 0, sizeof(ASTC_ENCODINGS_VALUES));
update_descriptor_queue.AddBuffer(*data_buffer, sizeof(ASTC_ENCODINGS_VALUES),
sizeof(SWIZZLE_TABLE));
update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
@@ -372,11 +453,11 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
ASSERT(params.bytes_per_block_log2 == 4);
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims,
params, descriptor_data](vk::CommandBuffer cmdbuf) {
const AstcPushConstants uniforms{
.blocks_dims = block_dims,
.bytes_per_block_log2 = params.bytes_per_block_log2,
.layer_stride = params.layer_stride,
.block_size = params.block_size,
.x_shift = params.x_shift,

View File

@@ -96,10 +96,15 @@ public:
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
void MakeDataBuffer();
VKScheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
VKUpdateDescriptorQueue& update_descriptor_queue;
MemoryAllocator& memory_allocator;
vk::Buffer data_buffer;
MemoryCommit data_buffer_commit;
};
} // namespace Vulkan

View File

@@ -8,7 +8,6 @@
#include <boost/container/small_vector.hpp>
#include "video_core/renderer_vulkan/pipeline_helper.h"
#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -27,7 +26,6 @@ using Tegra::Texture::TexturePair;
ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue_,
Common::ThreadWorker* thread_worker,
PipelineStatistics* pipeline_statistics,
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
vk::ShaderModule spv_module_)
: device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
@@ -38,7 +36,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
uniform_buffer_sizes.begin());
auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
auto func{[this, &descriptor_pool, shader_notify] {
DescriptorLayoutBuilder builder{device};
builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
@@ -52,14 +50,10 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
.pNext = nullptr,
.requiredSubgroupSize = GuestWarpSize,
};
VkPipelineCreateFlags flags{};
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
}
pipeline = device.GetLogical().CreateComputePipeline({
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = flags,
.flags = 0,
.stage{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
@@ -73,9 +67,6 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
.basePipelineHandle = 0,
.basePipelineIndex = 0,
});
if (pipeline_statistics) {
pipeline_statistics->Collect(*pipeline);
}
std::lock_guard lock{build_mutex};
is_built = true;
build_condvar.notify_one();

View File

@@ -25,7 +25,6 @@ class ShaderNotify;
namespace Vulkan {
class Device;
class PipelineStatistics;
class VKScheduler;
class ComputePipeline {
@@ -33,7 +32,6 @@ public:
explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue,
Common::ThreadWorker* thread_worker,
PipelineStatistics* pipeline_statistics,
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
vk::ShaderModule spv_module);

View File

@@ -11,7 +11,6 @@
#include "common/bit_field.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/pipeline_helper.h"
#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
@@ -218,8 +217,8 @@ GraphicsPipeline::GraphicsPipeline(
VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_,
std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos)
: key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
@@ -236,7 +235,7 @@ GraphicsPipeline::GraphicsPipeline(
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
}
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] {
DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
uses_push_descriptor = builder.CanUsePushDescriptor();
descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
@@ -251,9 +250,6 @@ GraphicsPipeline::GraphicsPipeline(
const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
Validate();
MakePipeline(render_pass);
if (pipeline_statistics) {
pipeline_statistics->Collect(*pipeline);
}
std::lock_guard lock{build_mutex};
is_built = true;
@@ -786,14 +782,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
}
*/
}
VkPipelineCreateFlags flags{};
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
}
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = flags,
.flags = 0,
.stageCount = static_cast<u32>(shader_stages.size()),
.pStages = shader_stages.data(),
.pVertexInputState = &vertex_input_ci,

View File

@@ -60,7 +60,6 @@ struct hash<Vulkan::GraphicsPipelineCacheKey> {
namespace Vulkan {
class Device;
class PipelineStatistics;
class RenderPassCache;
class VKScheduler;
class VKUpdateDescriptorQueue;
@@ -74,9 +73,8 @@ public:
VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
VideoCore::ShaderNotify* shader_notify, const Device& device,
DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
std::array<vk::ShaderModule, NUM_STAGES> stages,
Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache,
const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos);
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;

View File

@@ -29,7 +29,6 @@
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/pipeline_helper.h"
#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
@@ -390,19 +389,15 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
size_t total{};
size_t built{};
bool has_loaded{};
std::unique_ptr<PipelineStatistics> statistics;
} state;
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
state.statistics = std::make_unique<PipelineStatistics>(device);
}
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
ComputePipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key));
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
ShaderPools pools;
auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
auto pipeline{CreateComputePipeline(pools, key, env, false)};
std::lock_guard lock{state.mutex};
if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
@@ -430,8 +425,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
for (auto& env : envs) {
env_ptrs.push_back(&env);
}
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
state.statistics.get(), false)};
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)};
std::lock_guard lock{state.mutex};
graphics_cache.emplace(key, std::move(pipeline));
@@ -451,10 +445,6 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
lock.unlock();
workers.WaitForRequests();
if (state.statistics) {
state.statistics->Report();
}
}
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@@ -496,8 +486,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
bool build_in_parallel) try {
std::span<Shader::Environment* const> envs, bool build_in_parallel) try {
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -551,7 +540,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<GraphicsPipeline>(
maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key,
std::move(modules), infos);
} catch (const Shader::Exception& exception) {
@@ -564,8 +553,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
main_pools.ReleaseContents();
auto pipeline{
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
if (!pipeline || pipeline_cache_filename.empty()) {
return pipeline;
}
@@ -590,7 +578,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
env.SetCachedSize(shader->size_bytes);
main_pools.ReleaseContents();
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
if (!pipeline || pipeline_cache_filename.empty()) {
return pipeline;
}
@@ -603,7 +591,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
PipelineStatistics* statistics, bool build_in_parallel) try {
bool build_in_parallel) try {
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -617,8 +605,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
}
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
thread_worker, statistics, &shader_notify,
program.info, std::move(spv_module));
thread_worker, &shader_notify, program.info,
std::move(spv_module));
} catch (const Shader::Exception& exception) {
LOG_ERROR(Render_Vulkan, "{}", exception.what());

View File

@@ -80,9 +80,8 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
namespace Vulkan {
class ComputePipeline;
class DescriptorPool;
class Device;
class PipelineStatistics;
class DescriptorPool;
class RasterizerVulkan;
class RenderPassCache;
class VKScheduler;
@@ -129,8 +128,7 @@ private:
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
bool build_in_parallel);
std::span<Shader::Environment* const> envs, bool build_in_parallel);
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
const ShaderInfo* shader);
@@ -138,7 +136,6 @@ private:
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
const ComputePipelineCacheKey& key,
Shader::Environment& env,
PipelineStatistics* statistics,
bool build_in_parallel);
const Device& device;

View File

@@ -61,16 +61,11 @@ struct DrawParams {
VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) {
const auto& src = regs.viewport_transform[index];
const float width = src.scale_x * 2.0f;
float y = src.translate_y - src.scale_y;
float height = src.scale_y * 2.0f;
if (regs.screen_y_control.y_negate) {
y += height;
height = -height;
}
const float height = src.scale_y * 2.0f;
const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
VkViewport viewport{
.x = src.translate_x - src.scale_x,
.y = y,
.y = src.translate_y - src.scale_y,
.width = width != 0.0f ? width : 1.0f,
.height = height != 0.0f ? height : 1.0f,
.minDepth = src.translate_z - src.scale_z * reduce_z,

View File

@@ -23,6 +23,7 @@
#include "common/common_types.h"
#include "common/literals.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "video_core/compatible_formats.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h"
@@ -509,7 +510,7 @@ void TextureCache<P>::RunGarbageCollector() {
template <class P>
void TextureCache<P>::TickFrame() {
if (total_used_memory > minimum_memory) {
if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) {
RunGarbageCollector();
}
sentenced_images.Tick();

View File

@@ -151,76 +151,6 @@ private:
const IntType& m_Bits;
};
enum class IntegerEncoding { JustBits, Quint, Trit };
struct IntegerEncodedValue {
constexpr IntegerEncodedValue() = default;
constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
: encoding{encoding_}, num_bits{num_bits_} {}
constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
return encoding == other.encoding && num_bits == other.num_bits;
}
// Returns the number of bits required to encode num_vals values.
u32 GetBitLength(u32 num_vals) const {
u32 total_bits = num_bits * num_vals;
if (encoding == IntegerEncoding::Trit) {
total_bits += (num_vals * 8 + 4) / 5;
} else if (encoding == IntegerEncoding::Quint) {
total_bits += (num_vals * 7 + 2) / 3;
}
return total_bits;
}
IntegerEncoding encoding{};
u32 num_bits = 0;
u32 bit_value = 0;
union {
u32 quint_value = 0;
u32 trit_value;
};
};
// Returns a new instance of this struct that corresponds to the
// can take no more than mav_value values
static constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
while (mav_value > 0) {
u32 check = mav_value + 1;
// Is mav_value a power of two?
if (!(check & (check - 1))) {
return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
}
// Is mav_value of the type 3*2^n - 1?
if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
}
// Is mav_value of the type 5*2^n - 1?
if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
}
// Apparently it can't be represented with a bounded integer sequence...
// just iterate.
mav_value--;
}
return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
}
static constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
std::array<IntegerEncodedValue, 256> encodings{};
for (std::size_t i = 0; i < encodings.size(); ++i) {
encodings[i] = CreateEncoding(static_cast<u32>(i));
}
return encodings;
}
static constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
namespace Tegra::Texture::ASTC {
using IntegerEncodedVector = boost::container::static_vector<
IntegerEncodedValue, 256,
@@ -591,41 +521,35 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
return params;
}
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
// is the same as [(num_bits - 1):0] and repeats all the way down.
template <typename IntType>
static constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
if (num_bits == 0 || to_bit == 0) {
return 0;
static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
u32 blockHeight) {
// Don't actually care about the void extent, just read the bits...
for (s32 i = 0; i < 4; ++i) {
strm.ReadBits<13>();
}
const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
IntType res = v;
u32 reslen = num_bits;
while (reslen < to_bit) {
u32 comp = 0;
if (num_bits > to_bit - reslen) {
u32 newshift = to_bit - reslen;
comp = num_bits - newshift;
num_bits = newshift;
// Decode the RGBA components and renormalize them to the range [0, 255]
u16 r = static_cast<u16>(strm.ReadBits<16>());
u16 g = static_cast<u16>(strm.ReadBits<16>());
u16 b = static_cast<u16>(strm.ReadBits<16>());
u16 a = static_cast<u16>(strm.ReadBits<16>());
u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
(static_cast<u32>(a) & 0xFF00) << 16;
for (u32 j = 0; j < blockHeight; j++) {
for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = rgba;
}
res = static_cast<IntType>(res << num_bits);
res = static_cast<IntType>(res | (v >> comp));
reslen += num_bits;
}
return res;
}
static constexpr std::size_t NumReplicateEntries(u32 num_bits) {
return std::size_t(1) << num_bits;
}
template <typename IntType, u32 num_bits, u32 to_bit>
static constexpr auto MakeReplicateTable() {
std::array<IntType, NumReplicateEntries(num_bits)> table{};
for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
table[value] = Replicate(value, num_bits, to_bit);
static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
for (u32 j = 0; j < blockHeight; j++) {
for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = 0xFFFF00FF;
}
}
return table;
}
static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
@@ -648,9 +572,6 @@ static constexpr auto REPLICATE_2_BIT_TO_8_TABLE = MakeReplicateTable<u32, 2, 8>
static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>();
static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>();
static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>();
static constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
static constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
static constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback
/// to the runtime implementation
static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) {
@@ -1395,37 +1316,6 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
#undef READ_INT_VALUES
}
static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
u32 blockHeight) {
// Don't actually care about the void extent, just read the bits...
for (s32 i = 0; i < 4; ++i) {
strm.ReadBits<13>();
}
// Decode the RGBA components and renormalize them to the range [0, 255]
u16 r = static_cast<u16>(strm.ReadBits<16>());
u16 g = static_cast<u16>(strm.ReadBits<16>());
u16 b = static_cast<u16>(strm.ReadBits<16>());
u16 a = static_cast<u16>(strm.ReadBits<16>());
u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
(static_cast<u32>(a) & 0xFF00) << 16;
for (u32 j = 0; j < blockHeight; j++) {
for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = rgba;
}
}
}
static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
for (u32 j = 0; j < blockHeight; j++) {
for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = 0xFFFF00FF;
}
}
}
static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
InputBitStream strm(inBuf);

View File

@@ -9,6 +9,117 @@
namespace Tegra::Texture::ASTC {
enum class IntegerEncoding { JustBits, Quint, Trit };
struct IntegerEncodedValue {
constexpr IntegerEncodedValue() = default;
constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
: encoding{encoding_}, num_bits{num_bits_} {}
constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
return encoding == other.encoding && num_bits == other.num_bits;
}
// Returns the number of bits required to encode num_vals values.
u32 GetBitLength(u32 num_vals) const {
u32 total_bits = num_bits * num_vals;
if (encoding == IntegerEncoding::Trit) {
total_bits += (num_vals * 8 + 4) / 5;
} else if (encoding == IntegerEncoding::Quint) {
total_bits += (num_vals * 7 + 2) / 3;
}
return total_bits;
}
IntegerEncoding encoding{};
u32 num_bits = 0;
u32 bit_value = 0;
union {
u32 quint_value = 0;
u32 trit_value;
};
};
// Returns a new instance of this struct that corresponds to the
// can take no more than mav_value values
constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
while (mav_value > 0) {
u32 check = mav_value + 1;
// Is mav_value a power of two?
if (!(check & (check - 1))) {
return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
}
// Is mav_value of the type 3*2^n - 1?
if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
}
// Is mav_value of the type 5*2^n - 1?
if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
}
// Apparently it can't be represented with a bounded integer sequence...
// just iterate.
mav_value--;
}
return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
}
constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
std::array<IntegerEncodedValue, 256> encodings{};
for (std::size_t i = 0; i < encodings.size(); ++i) {
encodings[i] = CreateEncoding(static_cast<u32>(i));
}
return encodings;
}
constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
// is the same as [(num_bits - 1):0] and repeats all the way down.
template <typename IntType>
constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
if (num_bits == 0 || to_bit == 0) {
return 0;
}
const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
IntType res = v;
u32 reslen = num_bits;
while (reslen < to_bit) {
u32 comp = 0;
if (num_bits > to_bit - reslen) {
u32 newshift = to_bit - reslen;
comp = num_bits - newshift;
num_bits = newshift;
}
res = static_cast<IntType>(res << num_bits);
res = static_cast<IntType>(res | (v >> comp));
reslen += num_bits;
}
return res;
}
constexpr std::size_t NumReplicateEntries(u32 num_bits) {
return std::size_t(1) << num_bits;
}
template <typename IntType, u32 num_bits, u32 to_bit>
constexpr auto MakeReplicateTable() {
std::array<IntType, NumReplicateEntries(num_bits)> table{};
for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
table[value] = Replicate(value, num_bits, to_bit);
}
return table;
}
constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
uint32_t block_width, uint32_t block_height, std::span<uint8_t> output);

View File

@@ -18,9 +18,9 @@
namespace Tegra::Texture {
namespace {
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth, u32 stride_alignment) {
template <bool TO_LINEAR>
void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
// The origin of the transformation can be configured here, leave it as zero as the current API
// doesn't expose it.
static constexpr u32 origin_x = 0;
@@ -28,9 +28,9 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
static constexpr u32 origin_z = 0;
// We can configure here a custom pitch
// As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
const u32 pitch = width * BYTES_PER_PIXEL;
const u32 stride = Common::AlignUpLog2(width, stride_alignment) * BYTES_PER_PIXEL;
// As it's not exposed 'width * bpp' will be the expected pitch.
const u32 pitch = width * bytes_per_pixel;
const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
@@ -54,14 +54,14 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
for (u32 column = 0; column < width; ++column) {
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 x = (column + origin_x) * bytes_per_pixel;
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
const u32 swizzled_offset = base_swizzled_offset + table[x % GOB_SIZE_X];
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
slice * pitch * height + line * pitch + column * bytes_per_pixel;
if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset);
offset >= input.size()) {
@@ -73,45 +73,11 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
std::memcpy(dst, src, BYTES_PER_PIXEL);
std::memcpy(dst, src, bytes_per_pixel);
}
}
}
}
template <bool TO_LINEAR>
void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
switch (bytes_per_pixel) {
case 1:
return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 2:
return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 3:
return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 4:
return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 6:
return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 8:
return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 12:
return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 16:
return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
default:
UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
} // Anonymous namespace
void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,

View File

@@ -526,17 +526,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
SetNext(next, workgroup_layout);
}
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
if (khr_pipeline_executable_properties) {
LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
executable_properties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
.pNext = nullptr,
.pipelineExecutableInfo = VK_TRUE,
};
SetNext(next, executable_properties);
}
if (!ext_depth_range_unrestricted) {
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
@@ -835,7 +824,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_khr_shader_float16_int8{};
bool has_khr_workgroup_memory_explicit_layout{};
bool has_khr_pipeline_executable_properties{};
bool has_ext_subgroup_size_control{};
bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{};
@@ -890,10 +878,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
true);
}
if (Settings::values.renderer_shader_feedback) {
test(has_khr_pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
}
}
VkPhysicalDeviceFeatures2KHR features{};
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
@@ -1049,19 +1033,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
khr_workgroup_memory_explicit_layout = true;
}
}
if (has_khr_pipeline_executable_properties) {
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
executable_properties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
executable_properties.pNext = nullptr;
features.pNext = &executable_properties;
physical.GetFeatures2KHR(features);
if (executable_properties.pipelineExecutableInfo) {
extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
khr_pipeline_executable_properties = true;
}
}
if (khr_push_descriptor) {
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;

View File

@@ -214,11 +214,6 @@ public:
return khr_push_descriptor;
}
/// Returns true if VK_KHR_pipeline_executable_properties is enabled.
bool IsKhrPipelineEexecutablePropertiesEnabled() const {
return khr_pipeline_executable_properties;
}
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
return khr_workgroup_memory_explicit_layout;
@@ -383,7 +378,6 @@ private:
bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.

View File

@@ -228,9 +228,7 @@ void MemoryCommit::Release() {
MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
: device{device_}, properties{device_.GetPhysical().GetMemoryProperties()},
export_allocations{export_allocations_},
buffer_image_granularity{
device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
export_allocations{export_allocations_} {}
MemoryAllocator::~MemoryAllocator() = default;
@@ -260,9 +258,7 @@ MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage
}
MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image);
requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity);
auto commit = Commit(requirements, usage);
auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage);
image.BindMemory(commit.Memory(), commit.Offset());
return commit;
}

View File

@@ -123,8 +123,6 @@ private:
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
const bool export_allocations; ///< True when memory allocations have to be exported.
std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
// and optimal images
};
/// Returns true when a memory usage is guaranteed to be host visible.

View File

@@ -181,8 +181,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkGetMemoryWin32HandleKHR);
#endif
X(vkGetQueryPoolResults);
X(vkGetPipelineExecutablePropertiesKHR);
X(vkGetPipelineExecutableStatisticsKHR);
X(vkGetSemaphoreCounterValueKHR);
X(vkMapMemory);
X(vkQueueSubmit);
@@ -811,42 +809,6 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe
return requirements;
}
std::vector<VkPipelineExecutablePropertiesKHR> Device::GetPipelineExecutablePropertiesKHR(
VkPipeline pipeline) const {
const VkPipelineInfoKHR info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR,
.pNext = nullptr,
.pipeline = pipeline,
};
u32 num{};
dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr);
std::vector<VkPipelineExecutablePropertiesKHR> properties(num);
for (auto& property : properties) {
property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR;
}
Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data()));
return properties;
}
std::vector<VkPipelineExecutableStatisticKHR> Device::GetPipelineExecutableStatisticsKHR(
VkPipeline pipeline, u32 executable_index) const {
const VkPipelineExecutableInfoKHR executable_info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR,
.pNext = nullptr,
.pipeline = pipeline,
.executableIndex = executable_index,
};
u32 num{};
dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr);
std::vector<VkPipelineExecutableStatisticKHR> statistics(num);
for (auto& statistic : statistics) {
statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR;
}
Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num,
statistics.data()));
return statistics;
}
void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept {
dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data());

View File

@@ -295,8 +295,6 @@ struct DeviceDispatch : InstanceDispatch {
#ifdef _WIN32
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
#endif
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
PFN_vkMapMemory vkMapMemory{};
@@ -881,12 +879,6 @@ public:
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
VkPipeline pipeline) const;
std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR(
VkPipeline pipeline, u32 executable_index) const;
void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept;

View File

@@ -632,9 +632,9 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
renderer.RequestScreenshot(
screenshot_image.bits(),
[=, this](bool invert_y) {
[=, this] {
const std::string std_screenshot_path = screenshot_path.toStdString();
if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
} else {
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);

View File

@@ -817,6 +817,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.shader_backend);
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
ReadGlobalSetting(Settings::values.use_caches_gc);
ReadGlobalSetting(Settings::values.bg_red);
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
@@ -824,7 +825,6 @@ void Config::ReadRendererValues() {
if (global) {
ReadBasicSetting(Settings::values.fps_cap);
ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback);
ReadBasicSetting(Settings::values.enable_nsight_aftermath);
ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
}
@@ -872,6 +872,10 @@ void Config::ReadShortcutValues() {
void Config::ReadSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
ReadBasicSetting(Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
ReadGlobalSetting(Settings::values.language_index);
ReadGlobalSetting(Settings::values.region_index);
@@ -892,10 +896,6 @@ void Config::ReadSystemValues() {
}
if (global) {
ReadBasicSetting(Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
const auto custom_rtc_enabled =
ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
if (custom_rtc_enabled) {
@@ -945,8 +945,7 @@ void Config::ReadUIGamelistValues() {
qt_config->beginGroup(QStringLiteral("UIGameList"));
ReadBasicSetting(UISettings::values.show_add_ons);
ReadBasicSetting(UISettings::values.game_icon_size);
ReadBasicSetting(UISettings::values.folder_icon_size);
ReadBasicSetting(UISettings::values.icon_size);
ReadBasicSetting(UISettings::values.row_1_text_id);
ReadBasicSetting(UISettings::values.row_2_text_id);
ReadBasicSetting(UISettings::values.cache_game_list);
@@ -1356,6 +1355,7 @@ void Config::SaveRendererValues() {
Settings::values.shader_backend.UsingGlobal());
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
WriteGlobalSetting(Settings::values.use_caches_gc);
WriteGlobalSetting(Settings::values.bg_red);
WriteGlobalSetting(Settings::values.bg_green);
WriteGlobalSetting(Settings::values.bg_blue);
@@ -1363,7 +1363,6 @@ void Config::SaveRendererValues() {
if (global) {
WriteBasicSetting(Settings::values.fps_cap);
WriteBasicSetting(Settings::values.renderer_debug);
WriteBasicSetting(Settings::values.renderer_shader_feedback);
WriteBasicSetting(Settings::values.enable_nsight_aftermath);
WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
}
@@ -1404,6 +1403,7 @@ void Config::SaveShortcutValues() {
void Config::SaveSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
WriteBasicSetting(Settings::values.current_user);
WriteGlobalSetting(Settings::values.language_index);
WriteGlobalSetting(Settings::values.region_index);
WriteGlobalSetting(Settings::values.time_zone_index);
@@ -1415,8 +1415,6 @@ void Config::SaveSystemValues() {
0, Settings::values.rng_seed.UsingGlobal());
if (global) {
WriteBasicSetting(Settings::values.current_user);
WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
false);
WriteSetting(QStringLiteral("custom_rtc"),
@@ -1463,8 +1461,7 @@ void Config::SaveUIGamelistValues() {
qt_config->beginGroup(QStringLiteral("UIGameList"));
WriteBasicSetting(UISettings::values.show_add_ons);
WriteBasicSetting(UISettings::values.game_icon_size);
WriteBasicSetting(UISettings::values.folder_icon_size);
WriteBasicSetting(UISettings::values.icon_size);
WriteBasicSetting(UISettings::values.row_1_text_id);
WriteBasicSetting(UISettings::values.row_2_text_id);
WriteBasicSetting(UISettings::values.cache_game_list);

View File

@@ -43,8 +43,6 @@ void ConfigureDebug::SetConfiguration() {
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock);
ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
ui->enable_cpu_debugging->setEnabled(runtime_lock);
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
@@ -67,7 +65,6 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
Settings::values.disable_shader_loop_safety_checks =

View File

@@ -111,8 +111,8 @@
<property name="title">
<string>Graphics</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
@@ -125,7 +125,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item>
<widget class="QCheckBox" name="enable_nsight_aftermath">
<property name="toolTip">
<string>When checked, it enables Nsight Aftermath crash dumps</string>
@@ -135,7 +135,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item>
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
@@ -148,17 +148,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_shader_feedback">
<property name="toolTip">
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
</property>
<property name="text">
<string>Enable Shader Feedback</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item>
<widget class="QCheckBox" name="disable_loop_safety_checks">
<property name="toolTip">
<string>When checked, it executes shaders without loop logic changes</string>
@@ -286,14 +276,11 @@
<tabstop>open_log_button</tabstop>
<tabstop>homebrew_args_edit</tabstop>
<tabstop>enable_graphics_debugging</tabstop>
<tabstop>enable_shader_feedback</tabstop>
<tabstop>enable_nsight_aftermath</tabstop>
<tabstop>disable_macro_jit</tabstop>
<tabstop>disable_loop_safety_checks</tabstop>
<tabstop>fs_access_log</tabstop>
<tabstop>reporting_services</tabstop>
<tabstop>quest_flag</tabstop>
<tabstop>enable_cpu_debugging</tabstop>
<tabstop>use_debug_asserts</tabstop>
<tabstop>use_auto_stub</tabstop>
</tabstops>

View File

@@ -28,6 +28,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
if (Settings::IsConfiguringGlobal()) {
@@ -54,6 +55,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
ui->use_asynchronous_shaders,
use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc,
use_caches_gc);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time, use_fast_gpu_time);
}
@@ -78,6 +81,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
@@ -90,6 +94,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
use_asynchronous_shaders);
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc,
use_caches_gc);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));

View File

@@ -37,4 +37,5 @@ private:
ConfigurationShared::CheckState use_vsync;
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
ConfigurationShared::CheckState use_caches_gc;
};

View File

@@ -82,17 +82,24 @@
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
</property>
<property name="text">
<string>Use asynchronous shader building (hack)</string>
<string>Use asynchronous shader building</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_fast_gpu_time">
<property name="text">
<string>Use Fast GPU Time</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_caches_gc">
<property name="toolTip">
<string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
<string>Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4 GB by flushing the least used textures/buffers. May cause issues in a few games.</string>
</property>
<property name="text">
<string>Use Fast GPU Time (hack)</string>
<string>Enable GPU cache garbage collection (experimental)</string>
</property>
</widget>
</item>

View File

@@ -309,14 +309,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
buttons_param[button_id].Clear();
button_map[button_id]->setText(tr("[not set]"));
});
if (buttons_param[button_id].Has("toggle")) {
context_menu.addAction(tr("Toggle button"), [&] {
const bool toggle_value =
!buttons_param[button_id].Get("toggle", false);
buttons_param[button_id].Set("toggle", toggle_value);
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
});
}
context_menu.addAction(tr("Toggle button"), [&] {
const bool toggle_value = !buttons_param[button_id].Get("toggle", false);
buttons_param[button_id].Set("toggle", toggle_value);
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
});
if (buttons_param[button_id].Has("threshold")) {
context_menu.addAction(tr("Set threshold"), [&] {
const int button_threshold = static_cast<int>(

View File

@@ -122,7 +122,6 @@ void PlayerControlPreview::UpdateColors() {
colors.slider_arrow = QColor(14, 15, 18);
colors.font2 = QColor(255, 255, 255);
colors.indicator = QColor(170, 238, 255);
colors.indicator2 = QColor(100, 255, 100);
colors.deadzone = QColor(204, 136, 136);
colors.slider_button = colors.button;
}
@@ -140,7 +139,6 @@ void PlayerControlPreview::UpdateColors() {
colors.slider_arrow = QColor(65, 68, 73);
colors.font2 = QColor(0, 0, 0);
colors.indicator = QColor(0, 0, 200);
colors.indicator2 = QColor(0, 150, 0);
colors.deadzone = QColor(170, 0, 0);
colors.slider_button = QColor(153, 149, 149);
}
@@ -319,7 +317,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
using namespace Settings::NativeAnalog;
DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f,
button_values[Settings::NativeButton::LStick]);
DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
DrawRawJoystick(p, center + QPointF(-140, 90), axis_values[LStick].raw_value,
axis_values[LStick].properties);
}
using namespace Settings::NativeButton;
@@ -433,7 +432,8 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
using namespace Settings::NativeAnalog;
DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f,
button_values[Settings::NativeButton::RStick]);
DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
DrawRawJoystick(p, center + QPointF(140, 90), axis_values[RStick].raw_value,
axis_values[RStick].properties);
}
using namespace Settings::NativeButton;
@@ -547,7 +547,8 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button);
DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button);
DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
DrawRawJoystick(p, center + QPointF(-180, 90), l_stick.raw_value, l_stick.properties);
DrawRawJoystick(p, center + QPointF(180, 90), r_stick.raw_value, r_stick.properties);
}
using namespace Settings::NativeButton;
@@ -633,7 +634,8 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button);
DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button);
DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
DrawRawJoystick(p, center + QPointF(-50, 0), l_stick.raw_value, l_stick.properties);
DrawRawJoystick(p, center + QPointF(50, 0), r_stick.raw_value, r_stick.properties);
}
using namespace Settings::NativeButton;
@@ -726,7 +728,10 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
button_values[Settings::NativeButton::LStick]);
DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11,
button_values[Settings::NativeButton::RStick]);
DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
DrawRawJoystick(p, center + QPointF(-50, 105), axis_values[LStick].raw_value,
axis_values[LStick].properties);
DrawRawJoystick(p, center + QPointF(50, 105), axis_values[RStick].raw_value,
axis_values[RStick].properties);
}
using namespace Settings::NativeButton;
@@ -816,7 +821,10 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
p.setBrush(colors.font);
DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C,
1.0f);
DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
DrawRawJoystick(p, center + QPointF(-198, -125), axis_values[LStick].raw_value,
axis_values[LStick].properties);
DrawRawJoystick(p, center + QPointF(198, -125), axis_values[RStick].raw_value,
axis_values[RStick].properties);
}
using namespace Settings::NativeButton;
@@ -2350,33 +2358,8 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo
DrawCircle(p, center, 7.5f);
}
void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
using namespace Settings::NativeAnalog;
if (controller_type != Settings::ControllerType::LeftJoycon) {
DrawJoystickProperties(p, center_right, axis_values[RStick].properties);
p.setPen(colors.indicator);
p.setBrush(colors.indicator);
DrawJoystickDot(p, center_right, axis_values[RStick].raw_value,
axis_values[RStick].properties);
p.setPen(colors.indicator2);
p.setBrush(colors.indicator2);
DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties);
}
if (controller_type != Settings::ControllerType::RightJoycon) {
DrawJoystickProperties(p, center_left, axis_values[LStick].properties);
p.setPen(colors.indicator);
p.setBrush(colors.indicator);
DrawJoystickDot(p, center_left, axis_values[LStick].raw_value,
axis_values[LStick].properties);
p.setPen(colors.indicator2);
p.setBrush(colors.indicator2);
DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties);
}
}
void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center,
const Input::AnalogProperties& properties) {
void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, const QPointF value,
const Input::AnalogProperties& properties) {
constexpr float size = 45.0f;
const float range = size * properties.range;
const float deadzone = size * properties.deadzone;
@@ -2393,14 +2376,10 @@ void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF cen
pen.setColor(colors.deadzone);
p.setPen(pen);
DrawCircle(p, center, deadzone);
}
void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value,
const Input::AnalogProperties& properties) {
constexpr float size = 45.0f;
const float range = size * properties.range;
// Dot pointer
p.setPen(colors.indicator);
p.setBrush(colors.indicator);
DrawCircle(p, center + (value * range), 2);
}

View File

@@ -90,7 +90,6 @@ private:
QColor highlight2{};
QColor transparent{};
QColor indicator{};
QColor indicator2{};
QColor led_on{};
QColor led_off{};
QColor slider{};
@@ -140,10 +139,7 @@ private:
// Draw joystick functions
void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed);
void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed);
void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right);
void DrawJoystickProperties(QPainter& p, QPointF center,
const Input::AnalogProperties& properties);
void DrawJoystickDot(QPainter& p, QPointF center, QPointF value,
void DrawRawJoystick(QPainter& p, QPointF center, QPointF value,
const Input::AnalogProperties& properties);
void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed);
void DrawGCJoystick(QPainter& p, QPointF center, bool pressed);

View File

@@ -401,11 +401,6 @@
<string>Traditional Chinese (正體中文)</string>
</property>
</item>
<item>
<property name="text">
<string>Brazilian Portuguese (português do Brasil)</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">

View File

@@ -16,7 +16,7 @@
#include "yuzu/uisettings.h"
namespace {
constexpr std::array default_game_icon_sizes{
constexpr std::array default_icon_sizes{
std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
std::make_pair(32, QT_TRANSLATE_NOOP("ConfigureUI", "Small (32x32)")),
std::make_pair(64, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (64x64)")),
@@ -24,13 +24,6 @@ constexpr std::array default_game_icon_sizes{
std::make_pair(256, QT_TRANSLATE_NOOP("ConfigureUI", "Full Size (256x256)")),
};
constexpr std::array default_folder_icon_sizes{
std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
std::make_pair(24, QT_TRANSLATE_NOOP("ConfigureUI", "Small (24x24)")),
std::make_pair(48, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (48x48)")),
std::make_pair(72, QT_TRANSLATE_NOOP("ConfigureUI", "Large (72x72)")),
};
// clang-format off
constexpr std::array row_text_names{
QT_TRANSLATE_NOOP("ConfigureUI", "Filename"),
@@ -41,12 +34,8 @@ constexpr std::array row_text_names{
};
// clang-format on
QString GetTranslatedGameIconSize(size_t index) {
return QCoreApplication::translate("ConfigureUI", default_game_icon_sizes[index].second);
}
QString GetTranslatedFolderIconSize(size_t index) {
return QCoreApplication::translate("ConfigureUI", default_folder_icon_sizes[index].second);
QString GetTranslatedIconSize(size_t index) {
return QCoreApplication::translate("ConfigureUI", default_icon_sizes[index].second);
}
QString GetTranslatedRowTextName(size_t index) {
@@ -71,10 +60,8 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
// Force game list reload if any of the relevant settings are changed.
connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &ConfigureUi::RequestGameListUpdate);
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@@ -108,8 +95,7 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
@@ -128,10 +114,8 @@ void ConfigureUi::SetConfiguration() {
ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language));
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
ui->game_icon_size_combobox->setCurrentIndex(
ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
ui->folder_icon_size_combobox->setCurrentIndex(
ui->folder_icon_size_combobox->findData(UISettings::values.folder_icon_size.GetValue()));
ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size.GetValue()));
ui->enable_screenshot_save_as->setChecked(
UISettings::values.enable_screenshot_save_as.GetValue());
@@ -150,14 +134,8 @@ void ConfigureUi::changeEvent(QEvent* event) {
void ConfigureUi::RetranslateUI() {
ui->retranslateUi(this);
for (int i = 0; i < ui->game_icon_size_combobox->count(); i++) {
ui->game_icon_size_combobox->setItemText(i,
GetTranslatedGameIconSize(static_cast<size_t>(i)));
}
for (int i = 0; i < ui->folder_icon_size_combobox->count(); i++) {
ui->folder_icon_size_combobox->setItemText(
i, GetTranslatedFolderIconSize(static_cast<size_t>(i)));
for (int i = 0; i < ui->icon_size_combobox->count(); i++) {
ui->icon_size_combobox->setItemText(i, GetTranslatedIconSize(static_cast<size_t>(i)));
}
for (int i = 0; i < ui->row_1_text_combobox->count(); i++) {
@@ -188,13 +166,9 @@ void ConfigureUi::InitializeLanguageComboBox() {
}
void ConfigureUi::InitializeIconSizeComboBox() {
for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
const auto size = default_game_icon_sizes[i].first;
ui->game_icon_size_combobox->addItem(GetTranslatedGameIconSize(i), size);
}
for (size_t i = 0; i < default_folder_icon_sizes.size(); i++) {
const auto size = default_folder_icon_sizes[i].first;
ui->folder_icon_size_combobox->addItem(GetTranslatedFolderIconSize(i), size);
for (size_t i = 0; i < default_icon_sizes.size(); i++) {
const auto size = default_icon_sizes[i].first;
ui->icon_size_combobox->addItem(GetTranslatedIconSize(i), size);
}
}

View File

@@ -81,30 +81,16 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
<item>
<widget class="QLabel" name="game_icon_size_label">
<widget class="QLabel" name="icon_size_label">
<property name="text">
<string>Game Icon Size:</string>
<string>Icon Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="game_icon_size_combobox"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="folder_icon_size_qhbox_layout_2">
<item>
<widget class="QLabel" name="folder_icon_size_label">
<property name="text">
<string>Folder Icon Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="folder_icon_size_combobox"/>
<widget class="QComboBox" name="icon_size_combobox"/>
</item>
</layout>
</item>

View File

@@ -244,8 +244,8 @@ void GameList::OnUpdateThemedIcons() {
for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
QStandardItem* child = item_model->invisibleRootItem()->child(i);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
case GameListItemType::SdmcDir:
child->setData(

View File

@@ -80,7 +80,7 @@ public:
setData(qulonglong(program_id), ProgramIdRole);
setData(game_type, FileTypeRole);
const u32 size = UISettings::values.game_icon_size.GetValue();
const u32 size = UISettings::values.icon_size.GetValue();
QPixmap picture;
if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
@@ -233,7 +233,8 @@ public:
UISettings::GameDir* game_dir = &directory;
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
switch (dir_type) {
case GameListItemType::SdmcDir:
setData(
@@ -294,8 +295,8 @@ public:
explicit GameListAddDir() {
setData(type(), TypeRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
setData(QIcon::fromTheme(QStringLiteral("plus"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
@@ -317,8 +318,8 @@ public:
explicit GameListFavorites() {
setData(type(), TypeRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
setData(QIcon::fromTheme(QStringLiteral("star"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),

View File

@@ -91,8 +91,7 @@ struct Values {
// Game List
Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"};
Settings::BasicSetting<uint32_t> game_icon_size{64, "game_icon_size"};
Settings::BasicSetting<uint32_t> folder_icon_size{48, "folder_icon_size"};
Settings::BasicSetting<uint32_t> icon_size{64, "icon_size"};
Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"};
Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
std::atomic_bool is_game_list_reload_pending{false};

View File

@@ -278,9 +278,6 @@ void Config::ReadValues() {
if (Settings::values.players.GetValue()[p].analogs[i].empty())
Settings::values.players.GetValue()[p].analogs[i] = default_param;
}
Settings::values.players.GetValue()[p].connected =
sdl2_config->GetBoolean(group, "connected", false);
}
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
@@ -447,7 +444,6 @@ void Config::ReadValues() {
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.renderer_debug);
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
ReadSetting("Renderer", Settings::values.vulkan_device);
@@ -468,6 +464,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_nvdec_emulation);
ReadSetting("Renderer", Settings::values.accelerate_astc);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
ReadSetting("Renderer", Settings::values.use_caches_gc);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);

View File

@@ -221,10 +221,6 @@ backend =
# 0 (default): Disabled, 1: Enabled
debug =
# Enable shader feedback.
# 0 (default): Disabled, 1: Enabled
renderer_shader_feedback =
# Enable Nsight Aftermath crash dumps
# 0 (default): Disabled, 1: Enabled
nsight_aftermath =
@@ -367,7 +363,7 @@ custom_rtc =
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
language_index =
# The system region that yuzu will use during emulation

View File

@@ -16,8 +16,8 @@
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/yuzu_icon.h"
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
: input_subsystem{input_subsystem_}, system{system_} {
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
: input_subsystem{input_subsystem_} {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
@@ -122,10 +122,6 @@ void EmuWindow_SDL2::OnResize() {
UpdateCurrentFramebufferLayout(width, height);
}
void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE);
}
void EmuWindow_SDL2::Fullscreen() {
switch (Settings::values.fullscreen_mode.GetValue()) {
case Settings::FullscreenMode::Exclusive:
@@ -222,7 +218,7 @@ void EmuWindow_SDL2::WaitEvent() {
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = system.GetAndResetPerfStats();
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
const auto title =
fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps,

View File

@@ -24,7 +24,7 @@ enum class MouseButton;
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2();
/// Whether the window is still open, and a close request hasn't yet been sent
@@ -67,9 +67,6 @@ protected:
/// Called by WaitEvent when any event that may cause the window to be resized occurs
void OnResize();
/// Called when users want to hide the mouse cursor
void ShowCursor(bool show_cursor);
/// Called when user passes the fullscreen parameter flag
void Fullscreen();
@@ -90,7 +87,4 @@ protected:
/// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem;
/// yuzu core instance
Core::System& system;
};

View File

@@ -76,9 +76,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
return unsupported_ext.empty();
}
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
Core::System& system_, bool fullscreen)
: EmuWindow_SDL2{input_subsystem, system_} {
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
: EmuWindow_SDL2{input_subsystem} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -111,7 +110,6 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
if (fullscreen) {
Fullscreen();
ShowCursor(false);
}
window_context = SDL_GL_CreateContext(render_window);

View File

@@ -8,18 +8,13 @@
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
namespace Core {
class System;
}
namespace InputCommon {
class InputSubsystem;
}
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
bool fullscreen);
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
~EmuWindow_SDL2_GL();
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;

View File

@@ -24,9 +24,8 @@
#include <SDL.h>
#include <SDL_syswm.h>
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
Core::System& system_, bool fullscreen)
: EmuWindow_SDL2{input_subsystem, system_} {
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
: EmuWindow_SDL2{input_subsystem} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
@@ -45,7 +44,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
if (fullscreen) {
Fullscreen();
ShowCursor(false);
}
switch (wm.subsystem) {

View File

@@ -19,8 +19,7 @@ class InputSubsystem;
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
bool fullscreen);
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
~EmuWindow_SDL2_VK() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;

View File

@@ -172,10 +172,10 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
break;
case Settings::RendererBackend::Vulkan:
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen);
break;
}