Compare commits

..

6 Commits

Author SHA1 Message Date
lizzie dc93b250e5 [dynarmic, jit] add address mapping checks for jit service
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-27 09:33:26 +02:00
lizzie d8a8169eb2 [hle/bsd] do not use rust-result wannabe Expected in functions (#4075)
rust has Result<T,E> but we don't really need that in c++, also the header just sucks, objectively

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4075
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-27 08:50:24 +02:00
lizzie 81c6e56713 [core/am] ban y2k domain to make Civ7 boot web-appletless (#3868)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3868
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-27 08:46:18 +02:00
MaranBr b4b41ee62c [buffer_cache] Add option to control GPU buffer readback (#4126)
Added an option to control the GPU buffer readback, as it causes issues if the hardware cannot keep up with the additional workload.

Some games require this to render certain effects properly.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4126
2026-06-27 08:38:04 +02:00
simply0001 0d6a2158f0 [maxwell_3d] append inline index draw streams in bulk (#4083)
Inline index draws arrive as a batch but were processed one word at a time, so each index ran through the full per-method path and got appended a byte at a time.
Handle the whole batch in one pass instead, updating state once and appending all the indices together. Replay shadow control still goes word by word since it has to reload each one.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4083
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-27 02:52:59 +02:00
xbzk 09b6b3b71e xbzk/gpu-logging_qt-controls_android-fix (#4018)
5af7771f83-Bugfix: Made gpu_log_level global-only (was per-game switchable). Fixed Android non-determinism where a per-game profile silently overrode the global to Off and trapped GPULogger::Initialize() in a dead state, making shader dumps fail invisibly. Android per-game UI now hides the whole GPU logging block; Qt UI is untouched (global-only anyway).

bf4aabe8ab-Refactor/Cleanup: Removed gpu_logging_enabled master toggle as redundant with gpu_log_level == Off. Introduced GPU::Logging::IsActive() helper, replaced 14 call sites across vk_*.cpp. Refactored LogShaderCompilation() to be text-only and extracted SPIR-V dumping into a standalone GPU::Logging::DumpSpirvShader() free function. No singleton dependency, gated only by gpu_log_shader_dumps. Now gpu_log_level and gpu_log_shader_dumps are fully orthogonal. Cleaned up Android (BooleanSetting, SettingsItem, presenter, 7 locale string files).

865a1c5027-Refactor: Renamed dump_shaders → dump_guest_shaders to disambiguate from gpu_log_shader_dumps. Updated Qt label to "Dump Guest (Maxwell) Shaders" and rewrote the tooltip to mention .ash, the DumpDir/shaders/ location, and nvdisasm.

7cab456fdf-Feature: Added Qt UI control for GPU log level in the Logging session. Added gpu_log_shader_dumps checkbox to the Graphics column right below dump_guest_shaders.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4018
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-27 02:52:13 +02:00
55 changed files with 551 additions and 1361 deletions
@@ -83,9 +83,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
ENABLE_OVERLAY("enable_overlay"),
// GPU Logging
GPU_LOGGING_ENABLED("gpu_logging_enabled"),
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
DUMP_GUEST_SHADERS("dump_guest_shaders"),
DUMP_MACROS("dump_macros"),
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
@@ -931,13 +931,6 @@ abstract class SettingsItem(
)
// GPU Logging settings
put(
SwitchSetting(
BooleanSetting.GPU_LOGGING_ENABLED,
titleId = R.string.gpu_logging_enabled,
descriptionId = R.string.gpu_logging_enabled_description
)
)
put(
SingleChoiceSetting(
ByteSetting.GPU_LOG_LEVEL,
@@ -954,6 +947,13 @@ abstract class SettingsItem(
descriptionId = R.string.gpu_log_vulkan_calls_description
)
)
put(
SwitchSetting(
BooleanSetting.DUMP_GUEST_SHADERS,
titleId = R.string.dump_guest_shaders,
descriptionId = R.string.dump_guest_shaders_description
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_SHADER_DUMPS,
@@ -961,6 +961,13 @@ abstract class SettingsItem(
descriptionId = R.string.gpu_log_shader_dumps_description
)
)
put(
SwitchSetting(
BooleanSetting.DUMP_MACROS,
titleId = R.string.dump_macros,
descriptionId = R.string.dump_macros_description
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_MEMORY_TRACKING,
@@ -1288,14 +1288,17 @@ class SettingsFragmentPresenter(
add(ShortSetting.DEBUG_KNOBS.key)
add(StringSetting.PROGRAM_ARGS.key)
add(HeaderSetting(R.string.gpu_logging_header))
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
add(ByteSetting.GPU_LOG_LEVEL.key)
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
if (!NativeConfig.isPerGameConfigLoaded()) {
add(HeaderSetting(R.string.gpu_logging_header))
add(ByteSetting.GPU_LOG_LEVEL.key)
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
add(BooleanSetting.DUMP_MACROS.key)
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
}
}
}
@@ -569,8 +569,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">تسجيل وحدة معالجة الرسومات</string>
<string name="gpu_logging_enabled">تمكين تسجيل وحدة معالجة الرسومات</string>
<string name="gpu_logging_enabled_description">تسجيل عمليات وحدة معالجة الرسومات في ملف eden_gpu.log لتصحيح أخطاء برامج تشغيل Adreno</string>
<string name="gpu_log_level">مستوى السجل</string>
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
@@ -563,8 +563,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">Registros de la GPU</string>
<string name="gpu_logging_enabled">Activar los registros de la GPU</string>
<string name="gpu_logging_enabled_description">Registra las operaciones de la GPU en eden_gpu.log para la depuración de los controladores de Adreno</string>
<string name="gpu_log_level">Nivel de registros</string>
<string name="gpu_log_level_description">Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga)</string>
<string name="gpu_log_vulkan_calls">Registros de llamadas del API de Vulkan</string>
@@ -525,7 +525,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">Journalisation GPU</string>
<string name="gpu_logging_enabled">Activer la journalisation GPU</string>
<string name="gpu_log_level">Niveau de journalisation</string>
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
@@ -562,8 +562,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">Ведение журнала ГПУ</string>
<string name="gpu_logging_enabled">Включить ведение журнала ГПУ</string>
<string name="gpu_logging_enabled_description">Записывать операции ГПУ в файл eden_gpu.log для отладки драйверов Adreno</string>
<string name="gpu_log_level">Уровень журналирования</string>
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
@@ -565,8 +565,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">Журналювання ГП</string>
<string name="gpu_logging_enabled">Увімкнути журналювання ГП</string>
<string name="gpu_logging_enabled_description">Журналювати операції ГП до eden_gpu.log для зневадження драйверів Adreno</string>
<string name="gpu_log_level">Рівень журналювання</string>
<string name="gpu_log_level_description">Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію)</string>
<string name="gpu_log_vulkan_calls">Записувати виклики API Vulkan</string>
@@ -559,8 +559,6 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_logging_enabled">启用 GPU 日志</string>
<string name="gpu_logging_enabled_description">将 GPU 操作记录至 eden_gpu.log 以供调试 Adreno 驱动</string>
<string name="gpu_log_level">日志等级</string>
<string name="gpu_log_level_description">GPU 日志的详细级别(数值越高 = 细节越多,开销越大)</string>
<string name="gpu_log_vulkan_calls">记录 Vulkan API 调用</string>
@@ -575,14 +575,16 @@
<!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU Logging</string>
<string name="gpu_logging_enabled">Enable GPU Logging</string>
<string name="gpu_logging_enabled_description">Log GPU operations to eden_gpu.log for debugging Adreno drivers</string>
<string name="gpu_log_level">Log Level</string>
<string name="gpu_log_level_description">Detail level for GPU logs (higher = more detail, more overhead)</string>
<string name="gpu_log_vulkan_calls">Log Vulkan API Calls</string>
<string name="gpu_log_vulkan_calls_description">Track all Vulkan API calls in ring buffer</string>
<string name="gpu_log_shader_dumps">Dump Shaders</string>
<string name="gpu_log_shader_dumps_description">Save compiled shader SPIR-V to files</string>
<string name="gpu_log_shader_dumps">Dump SPIR-V Shaders</string>
<string name="gpu_log_shader_dumps_description">Save recompiled SPIR-V binaries (.spv) to dump folder. Inspect with spirv-dis/spirv-cross/spirv-val.</string>
<string name="dump_guest_shaders">Dump Guest (Maxwell) Shaders</string>
<string name="dump_guest_shaders_description">Save Maxwell guest shader bytecode files (*.ash) to dump folder. Inspect with nvdisasm.</string>
<string name="dump_macros">Dump Maxwell Macros</string>
<string name="dump_macros_description">Save Maxwell macro program files (*.macro) to dump folder. Inspect with envydis.</string>
<string name="gpu_log_memory_tracking">Track GPU Memory</string>
<string name="gpu_log_memory_tracking_description">Monitor GPU memory allocations and deallocations</string>
<string name="gpu_log_driver_debug">Driver Debug Info</string>
-1
View File
@@ -50,7 +50,6 @@ add_library(
elf.h
error.cpp
error.h
expected.h
fiber.cpp
fiber.h
fixed_point.h
-986
View File
@@ -1,986 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// This is based on the proposed implementation of std::expected (P0323)
// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
#pragma once
#include <type_traits>
#include <utility>
namespace Common {
template <typename T, typename E>
class Expected;
template <typename E>
class Unexpected {
public:
Unexpected() = delete;
constexpr explicit Unexpected(const E& e) : m_val{e} {}
constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {}
constexpr E& value() & {
return m_val;
}
constexpr const E& value() const& {
return m_val;
}
constexpr E&& value() && {
return std::move(m_val);
}
constexpr const E&& value() const&& {
return std::move(m_val);
}
private:
E m_val;
};
template <typename E>
constexpr auto operator<=>(const Unexpected<E>& lhs, const Unexpected<E>& rhs) {
return lhs.value() <=> rhs.value();
}
struct unexpect_t {
constexpr explicit unexpect_t() = default;
};
namespace detail {
struct no_init_t {
constexpr explicit no_init_t() = default;
};
/**
* This specialization is for when T is not trivially destructible,
* so the destructor must be called on destruction of `expected'
* Additionally, this requires E to be trivially destructible
*/
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
requires std::is_trivially_destructible_v<E>
struct expected_storage_base {
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
constexpr expected_storage_base(std::in_place_t, Args&&... args)
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
Args&&... args)
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
~expected_storage_base() {
if (m_has_val) {
m_val.~T();
}
}
union {
T m_val;
Unexpected<E> m_unexpect;
};
bool m_has_val;
};
/**
* This specialization is for when T is trivially destructible,
* so the destructor of `expected` can be trivial
* Additionally, this requires E to be trivially destructible
*/
template <typename T, typename E>
requires std::is_trivially_destructible_v<E>
struct expected_storage_base<T, E, true> {
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
constexpr expected_storage_base(no_init_t) : m_has_val{false} {}
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
constexpr expected_storage_base(std::in_place_t, Args&&... args)
: m_val{std::forward<Args>(args)...}, m_has_val{true} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args)
: m_val{il, std::forward<Args>(args)...}, m_has_val{true} {}
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
: m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il,
Args&&... args)
: m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {}
~expected_storage_base() = default;
union {
T m_val;
Unexpected<E> m_unexpect;
};
bool m_has_val;
};
template <typename T, typename E>
struct expected_operations_base : expected_storage_base<T, E> {
using expected_storage_base<T, E>::expected_storage_base;
template <typename... Args>
void construct(Args&&... args) noexcept {
new (std::addressof(this->m_val)) T{std::forward<Args>(args)...};
this->m_has_val = true;
}
template <typename Rhs>
void construct_with(Rhs&& rhs) noexcept {
new (std::addressof(this->m_val)) T{std::forward<Rhs>(rhs).get()};
this->m_has_val = true;
}
template <typename... Args>
void construct_error(Args&&... args) noexcept {
new (std::addressof(this->m_unexpect)) Unexpected<E>{std::forward<Args>(args)...};
this->m_has_val = false;
}
void assign(const expected_operations_base& rhs) noexcept {
if (!this->m_has_val && rhs.m_has_val) {
geterr().~Unexpected<E>();
construct(rhs.get());
} else {
assign_common(rhs);
}
}
void assign(expected_operations_base&& rhs) noexcept {
if (!this->m_has_val && rhs.m_has_val) {
geterr().~Unexpected<E>();
construct(std::move(rhs).get());
} else {
assign_common(rhs);
}
}
template <typename Rhs>
void assign_common(Rhs&& rhs) {
if (this->m_has_val) {
if (rhs.m_has_val) {
get() = std::forward<Rhs>(rhs).get();
} else {
destroy_val();
construct_error(std::forward<Rhs>(rhs).geterr());
}
} else {
if (!rhs.m_has_val) {
geterr() = std::forward<Rhs>(rhs).geterr();
}
}
}
bool has_value() const {
return this->m_has_val;
}
constexpr T& get() & {
return this->m_val;
}
constexpr const T& get() const& {
return this->m_val;
}
constexpr T&& get() && {
return std::move(this->m_val);
}
constexpr const T&& get() const&& {
return std::move(this->m_val);
}
constexpr Unexpected<E>& geterr() & {
return this->m_unexpect;
}
constexpr const Unexpected<E>& geterr() const& {
return this->m_unexpect;
}
constexpr Unexpected<E>&& geterr() && {
return std::move(this->m_unexpect);
}
constexpr const Unexpected<E>&& geterr() const&& {
return std::move(this->m_unexpect);
}
constexpr void destroy_val() {
get().~T();
}
};
/**
* This manages conditionally having a trivial copy constructor
* This specialization is for when T is trivially copy constructible
* Additionally, this requires E to be trivially copy constructible
*/
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
requires std::is_trivially_copy_constructible_v<E>
struct expected_copy_base : expected_operations_base<T, E> {
using expected_operations_base<T, E>::expected_operations_base;
};
/**
* This specialization is for when T is not trivially copy constructible
* Additionally, this requires E to be trivially copy constructible
*/
template <typename T, typename E>
requires std::is_trivially_copy_constructible_v<E>
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
using expected_operations_base<T, E>::expected_operations_base;
expected_copy_base() = default;
expected_copy_base(const expected_copy_base& rhs)
: expected_operations_base<T, E>{no_init_t{}} {
if (rhs.has_value()) {
this->construct_with(rhs);
} else {
this->construct_error(rhs.geterr());
}
}
expected_copy_base(expected_copy_base&&) = default;
expected_copy_base& operator=(const expected_copy_base&) = default;
expected_copy_base& operator=(expected_copy_base&&) = default;
};
/**
* This manages conditionally having a trivial move constructor
* This specialization is for when T is trivially move constructible
* Additionally, this requires E to be trivially move constructible
*/
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
requires std::is_trivially_move_constructible_v<E>
struct expected_move_base : expected_copy_base<T, E> {
using expected_copy_base<T, E>::expected_copy_base;
};
/**
* This specialization is for when T is not trivially move constructible
* Additionally, this requires E to be trivially move constructible
*/
template <typename T, typename E>
requires std::is_trivially_move_constructible_v<E>
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
using expected_copy_base<T, E>::expected_copy_base;
expected_move_base() = default;
expected_move_base(const expected_move_base&) = default;
expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v<T>)
: expected_copy_base<T, E>{no_init_t{}} {
if (rhs.has_value()) {
this->construct_with(std::move(rhs));
} else {
this->construct_error(std::move(rhs.geterr()));
}
}
expected_move_base& operator=(const expected_move_base&) = default;
expected_move_base& operator=(expected_move_base&&) = default;
};
/**
* This manages conditionally having a trivial copy assignment operator
* This specialization is for when T is trivially copy assignable
* Additionally, this requires E to be trivially copy assignable
*/
template <typename T, typename E,
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
std::is_trivially_copy_constructible<T>,
std::is_trivially_destructible<T>>>
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
std::is_trivially_copy_constructible<E>,
std::is_trivially_destructible<E>>
struct expected_copy_assign_base : expected_move_base<T, E> {
using expected_move_base<T, E>::expected_move_base;
};
/**
* This specialization is for when T is not trivially copy assignable
* Additionally, this requires E to be trivially copy assignable
*/
template <typename T, typename E>
requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
std::is_trivially_copy_constructible<E>,
std::is_trivially_destructible<E>>
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
using expected_move_base<T, E>::expected_move_base;
expected_copy_assign_base() = default;
expected_copy_assign_base(const expected_copy_assign_base&) = default;
expected_copy_assign_base(expected_copy_assign_base&&) = default;
expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) {
this->assign(rhs);
return *this;
}
expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default;
};
/**
* This manages conditionally having a trivial move assignment operator
* This specialization is for when T is trivially move assignable
* Additionally, this requires E to be trivially move assignable
*/
template <typename T, typename E,
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
std::is_trivially_move_constructible<T>,
std::is_trivially_destructible<T>>>
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
std::is_trivially_move_constructible<E>,
std::is_trivially_destructible<E>>
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
};
/**
* This specialization is for when T is not trivially move assignable
* Additionally, this requires E to be trivially move assignable
*/
template <typename T, typename E>
requires std::conjunction_v<std::is_trivially_move_assignable<E>,
std::is_trivially_move_constructible<E>,
std::is_trivially_destructible<E>>
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
expected_move_assign_base() = default;
expected_move_assign_base(const expected_move_assign_base&) = default;
expected_move_assign_base(expected_move_assign_base&&) = default;
expected_move_assign_base& operator=(const expected_move_assign_base&) = default;
expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept(
std::conjunction_v<std::is_nothrow_move_constructible<T>,
std::is_nothrow_move_assignable<T>>) {
this->assign(std::move(rhs));
return *this;
}
};
/**
* expected_delete_ctor_base will conditionally delete copy and move constructors
* depending on whether T is copy/move constructible
* Additionally, this requires E to be copy/move constructible
*/
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
bool EnableMove = std::is_move_constructible_v<T>>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, true, false> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, false, true> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default;
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, false, false> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete;
expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default;
expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default;
};
/**
* expected_delete_assign_base will conditionally delete copy and move assignment operators
* depending on whether T is copy/move constructible + assignable
* Additionally, this requires E to be copy/move constructible + assignable
*/
template <
typename T, typename E,
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, true, false> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default;
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, false, true> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default;
};
template <typename T, typename E>
requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, false, false> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default;
expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete;
expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete;
};
/**
* This is needed to be able to construct the expected_default_ctor_base which follows,
* while still conditionally deleting the default constructor.
*/
struct default_constructor_tag {
constexpr explicit default_constructor_tag() = default;
};
/**
* expected_default_ctor_base will ensure that expected
* has a deleted default constructor if T is not default constructible
* This specialization is for when T is default constructible
*/
template <typename T, typename E, bool Enable = std::is_default_constructible_v<T>>
struct expected_default_ctor_base {
constexpr expected_default_ctor_base() noexcept = default;
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
};
template <typename T, typename E>
struct expected_default_ctor_base<T, E, false> {
constexpr expected_default_ctor_base() noexcept = delete;
constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default;
constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default;
expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default;
expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default;
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
};
template <typename T, typename E, typename U>
using expected_enable_forward_value =
std::enable_if_t<std::is_constructible_v<T, U&&> &&
!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> &&
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
!std::is_same_v<Unexpected<E>, std::remove_cvref_t<U>>>;
template <typename T, typename E, typename U, typename G, typename UR, typename GR>
using expected_enable_from_other = std::enable_if_t<
std::is_constructible_v<T, UR> && std::is_constructible_v<E, GR> &&
!std::is_constructible_v<T, Expected<U, G>&> && !std::is_constructible_v<T, Expected<U, G>&&> &&
!std::is_constructible_v<T, const Expected<U, G>&> &&
!std::is_constructible_v<T, const Expected<U, G>&&> &&
!std::is_convertible_v<Expected<U, G>&, T> && !std::is_convertible_v<Expected<U, G>&&, T> &&
!std::is_convertible_v<const Expected<U, G>&, T> &&
!std::is_convertible_v<const Expected<U, G>&&, T>>;
} // namespace detail
template <typename T, typename E>
class Expected : private detail::expected_move_assign_base<T, E>,
private detail::expected_delete_ctor_base<T, E>,
private detail::expected_delete_assign_base<T, E>,
private detail::expected_default_ctor_base<T, E> {
public:
using value_type = T;
using error_type = E;
using unexpected_type = Unexpected<E>;
constexpr Expected() = default;
constexpr Expected(const Expected&) = default;
constexpr Expected(Expected&&) = default;
Expected& operator=(const Expected&) = default;
Expected& operator=(Expected&&) = default;
template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
constexpr Expected(std::in_place_t, Args&&... args)
: impl_base{std::in_place, std::forward<Args>(args)...},
ctor_base{detail::default_constructor_tag{}} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr Expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
: impl_base{std::in_place, il, std::forward<Args>(args)...},
ctor_base{detail::default_constructor_tag{}} {}
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr>
constexpr explicit Expected(const Unexpected<G>& e)
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr,
std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr>
constexpr Expected(Unexpected<G> const& e)
: impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {}
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
std::enable_if_t<!std::is_convertible_v<G&&, E>>* = nullptr>
constexpr explicit Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
detail::default_constructor_tag{}} {}
template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr,
std::enable_if_t<std::is_convertible_v<G&&, E>>* = nullptr>
constexpr Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>)
: impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{
detail::default_constructor_tag{}} {}
template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
constexpr explicit Expected(unexpect_t, Args&&... args)
: impl_base{unexpect_t{}, std::forward<Args>(args)...},
ctor_base{detail::default_constructor_tag{}} {}
template <typename U, typename... Args,
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* =
nullptr>
constexpr explicit Expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
: impl_base{unexpect_t{}, il, std::forward<Args>(args)...},
ctor_base{detail::default_constructor_tag{}} {}
template <typename U, typename G,
std::enable_if_t<!(std::is_convertible_v<U const&, T> &&
std::is_convertible_v<G const&, E>)>* = nullptr,
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
constexpr explicit Expected(const Expected<U, G>& rhs)
: ctor_base{detail::default_constructor_tag{}} {
if (rhs.has_value()) {
this->construct(*rhs);
} else {
this->construct_error(rhs.error());
}
}
template <typename U, typename G,
std::enable_if_t<(std::is_convertible_v<U const&, T> &&
std::is_convertible_v<G const&, E>)>* = nullptr,
detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr>
constexpr Expected(const Expected<U, G>& rhs) : ctor_base{detail::default_constructor_tag{}} {
if (rhs.has_value()) {
this->construct(*rhs);
} else {
this->construct_error(rhs.error());
}
}
template <typename U, typename G,
std::enable_if_t<!(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
nullptr,
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
constexpr explicit Expected(Expected<U, G>&& rhs)
: ctor_base{detail::default_constructor_tag{}} {
if (rhs.has_value()) {
this->construct(std::move(*rhs));
} else {
this->construct_error(std::move(rhs.error()));
}
}
template <typename U, typename G,
std::enable_if_t<(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* =
nullptr,
detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr>
constexpr Expected(Expected<U, G>&& rhs) : ctor_base{detail::default_constructor_tag{}} {
if (rhs.has_value()) {
this->construct(std::move(*rhs));
} else {
this->construct_error(std::move(rhs.error()));
}
}
template <typename U = T, std::enable_if_t<!std::is_convertible_v<U&&, T>>* = nullptr,
detail::expected_enable_forward_value<T, E, U>* = nullptr>
constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
template <typename U = T, std::enable_if_t<std::is_convertible_v<U&&, T>>* = nullptr,
detail::expected_enable_forward_value<T, E, U>* = nullptr>
constexpr Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {}
template <typename U = T, typename G = T,
std::enable_if_t<std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
std::enable_if_t<(
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
Expected& operator=(U&& v) {
if (has_value()) {
val() = std::forward<U>(v);
} else {
err().~Unexpected<E>();
new (valptr()) T{std::forward<U>(v)};
this->m_has_val = true;
}
return *this;
}
template <typename U = T, typename G = T,
std::enable_if_t<!std::is_nothrow_constructible_v<T, U&&>>* = nullptr,
std::enable_if_t<(
!std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> &&
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> &&
std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> &&
std::is_nothrow_move_constructible_v<E>)>* = nullptr>
Expected& operator=(U&& v) {
if (has_value()) {
val() = std::forward<U>(v);
} else {
auto tmp = std::move(err());
err().~Unexpected<E>();
new (valptr()) T{std::forward<U>(v)};
this->m_has_val = true;
}
return *this;
}
template <typename G = E, std::enable_if_t<std::is_nothrow_copy_constructible_v<G> &&
std::is_assignable_v<G&, G>>* = nullptr>
Expected& operator=(const Unexpected<G>& rhs) {
if (!has_value()) {
err() = rhs;
} else {
this->destroy_val();
new (errptr()) Unexpected<E>{rhs};
this->m_has_val = false;
}
return *this;
}
template <typename G = E, std::enable_if_t<std::is_nothrow_move_constructible_v<G> &&
std::is_move_assignable_v<G>>* = nullptr>
Expected& operator=(Unexpected<G>&& rhs) noexcept {
if (!has_value()) {
err() = std::move(rhs);
} else {
this->destroy_val();
new (errptr()) Unexpected<E>{std::move(rhs)};
this->m_has_val = false;
}
return *this;
}
template <typename... Args,
std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
void emplace(Args&&... args) {
if (has_value()) {
val() = T{std::forward<Args>(args)...};
} else {
err().~Unexpected<E>();
new (valptr()) T{std::forward<Args>(args)...};
this->m_has_val = true;
}
}
template <typename... Args,
std::enable_if_t<!std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr>
void emplace(Args&&... args) {
if (has_value()) {
val() = T{std::forward<Args>(args)...};
} else {
auto tmp = std::move(err());
err().~Unexpected<E>();
new (valptr()) T{std::forward<Args>(args)...};
this->m_has_val = true;
}
}
template <typename U, typename... Args,
std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
Args&&...>>* = nullptr>
void emplace(std::initializer_list<U> il, Args&&... args) {
if (has_value()) {
T t{il, std::forward<Args>(args)...};
val() = std::move(t);
} else {
err().~Unexpected<E>();
new (valptr()) T{il, std::forward<Args>(args)...};
this->m_has_val = true;
}
}
template <typename U, typename... Args,
std::enable_if_t<!std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
Args&&...>>* = nullptr>
void emplace(std::initializer_list<U> il, Args&&... args) {
if (has_value()) {
T t{il, std::forward<Args>(args)...};
val() = std::move(t);
} else {
auto tmp = std::move(err());
err().~Unexpected<E>();
new (valptr()) T{il, std::forward<Args>(args)...};
this->m_has_val = true;
}
}
constexpr T* operator->() {
return valptr();
}
constexpr const T* operator->() const {
return valptr();
}
template <typename U = T>
constexpr U& operator*() & {
return val();
}
template <typename U = T>
constexpr const U& operator*() const& {
return val();
}
template <typename U = T>
constexpr U&& operator*() && {
return std::move(val());
}
template <typename U = T>
constexpr const U&& operator*() const&& {
return std::move(val());
}
constexpr bool has_value() const noexcept {
return this->m_has_val;
}
constexpr explicit operator bool() const noexcept {
return this->m_has_val;
}
template <typename U = T>
constexpr U& value() & {
return val();
}
template <typename U = T>
constexpr const U& value() const& {
return val();
}
template <typename U = T>
constexpr U&& value() && {
return std::move(val());
}
template <typename U = T>
constexpr const U&& value() const&& {
return std::move(val());
}
constexpr E& error() & {
return err().value();
}
constexpr const E& error() const& {
return err().value();
}
constexpr E&& error() && {
return std::move(err().value());
}
constexpr const E&& error() const&& {
return std::move(err().value());
}
template <typename U>
constexpr T value_or(U&& v) const& {
static_assert(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>,
"T must be copy-constructible and convertible from U&&");
return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
}
template <typename U>
constexpr T value_or(U&& v) && {
static_assert(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>,
"T must be move-constructible and convertible from U&&");
return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
}
private:
static_assert(!std::is_reference_v<T>, "T must not be a reference");
static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
"T must not be std::in_place_t");
static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>, "T must not be unexpect_t");
static_assert(!std::is_same_v<T, std::remove_cv_t<Unexpected<E>>>,
"T must not be Unexpected<E>");
static_assert(!std::is_reference_v<E>, "E must not be a reference");
T* valptr() {
return std::addressof(this->m_val);
}
const T* valptr() const {
return std::addressof(this->m_val);
}
Unexpected<E>* errptr() {
return std::addressof(this->m_unexpect);
}
const Unexpected<E>* errptr() const {
return std::addressof(this->m_unexpect);
}
template <typename U = T>
constexpr U& val() {
return this->m_val;
}
template <typename U = T>
constexpr const U& val() const {
return this->m_val;
}
constexpr Unexpected<E>& err() {
return this->m_unexpect;
}
constexpr const Unexpected<E>& err() const {
return this->m_unexpect;
}
using impl_base = detail::expected_move_assign_base<T, E>;
using ctor_base = detail::expected_default_ctor_base<T, E>;
};
template <typename T, typename E, typename U, typename F>
constexpr bool operator==(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
return (lhs.has_value() != rhs.has_value())
? false
: (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
}
template <typename T, typename E, typename U, typename F>
constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<U, F>& rhs) {
return !operator==(lhs, rhs);
}
template <typename T, typename E, typename U>
constexpr bool operator==(const Expected<T, E>& x, const U& v) {
return x.has_value() ? *x == v : false;
}
template <typename T, typename E, typename U>
constexpr bool operator==(const U& v, const Expected<T, E>& x) {
return x.has_value() ? *x == v : false;
}
template <typename T, typename E, typename U>
constexpr bool operator!=(const Expected<T, E>& x, const U& v) {
return !operator==(x, v);
}
template <typename T, typename E, typename U>
constexpr bool operator!=(const U& v, const Expected<T, E>& x) {
return !operator==(v, x);
}
template <typename T, typename E>
constexpr bool operator==(const Expected<T, E>& x, const Unexpected<E>& e) {
return x.has_value() ? false : x.error() == e.value();
}
template <typename T, typename E>
constexpr bool operator==(const Unexpected<E>& e, const Expected<T, E>& x) {
return x.has_value() ? false : x.error() == e.value();
}
template <typename T, typename E>
constexpr bool operator!=(const Expected<T, E>& x, const Unexpected<E>& e) {
return !operator==(x, e);
}
template <typename T, typename E>
constexpr bool operator!=(const Unexpected<E>& e, const Expected<T, E>& x) {
return !operator==(e, x);
}
} // namespace Common
+10 -1
View File
@@ -54,7 +54,6 @@ SWITCHABLE(CpuBackend, true);
SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true);
SWITCHABLE(GpuLogLevel, true);
SWITCHABLE(Language, true);
SWITCHABLE(MemoryLayout, true);
SWITCHABLE(NvdecEmulation, false);
@@ -213,6 +212,16 @@ bool IsNceEnabled() {
return is_nce_enabled;
}
static u64 current_program_id = 0;
void SetCurrentProgramID(u64 program_id) {
current_program_id = program_id;
}
u64 GetCurrentProgramID() {
return current_program_id;
}
bool IsDockedMode() {
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
}
+14 -5
View File
@@ -573,6 +573,13 @@ struct Values {
false,
#endif
"rescale_hack", Category::RendererHacks};
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
false,
"enable_gpu_buffer_readback",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
@@ -788,8 +795,8 @@ struct Values {
false}; // runtime_modifiable_ — startup-only
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
Setting<bool> dump_shaders{
linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
Setting<bool> dump_guest_shaders{
linkage, false, "dump_guest_shaders", Category::DebuggingGraphics, Specialization::Default,
false};
Setting<bool> dump_macros{
linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
@@ -813,9 +820,8 @@ struct Values {
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
// GPU Logging
Setting<bool> gpu_logging_enabled{linkage, false, "gpu_logging_enabled", Category::Debugging};
SwitchableSetting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Standard, "gpu_log_level",
Category::Debugging};
Setting<GpuLogLevel> gpu_log_level{linkage, GpuLogLevel::Off, "gpu_log_level",
Category::Debugging};
Setting<bool> gpu_log_vulkan_calls{linkage, true, "gpu_log_vulkan_calls", Category::Debugging};
Setting<bool> gpu_log_shader_dumps{linkage, false, "gpu_log_shader_dumps", Category::Debugging};
Setting<bool> gpu_log_memory_tracking{linkage, true, "gpu_log_memory_tracking",
@@ -876,6 +882,9 @@ bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
void SetCurrentProgramID(u64 program_id);
u64 GetCurrentProgramID();
bool IsOpenGL();
bool IsDockedMode();
+1 -1
View File
@@ -224,7 +224,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
config.fastmem_pointer = page_table->fastmem_arena ?
std::optional<uintptr_t>{reinterpret_cast<uintptr_t>(page_table->fastmem_arena)} :
std::optional<uintptr_t>{uintptr_t(page_table->fastmem_arena)} :
std::nullopt;
config.fastmem_address_space_bits = std::uint32_t(address_space_bits);
config.silently_mirror_fastmem = false;
+3
View File
@@ -326,6 +326,9 @@ struct System::Impl {
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
// Expose program id to dump sites and other global readers.
Settings::SetCurrentProgramID(params.program_id);
// Track launch time for frontend launches
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
-1
View File
@@ -10,7 +10,6 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/expected.h"
// All the constants in this file come from <https://switchbrew.org/wiki/Error_codes>
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "applet_web_browser_types.h"
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
@@ -370,16 +371,13 @@ void WebBrowser::ExtractOfflineRomFS() {
void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) {
const bool use_tlv_output =
(web_arg_header.shim_kind == ShimKind::Share &&
web_applet_version >= WebAppletVersion::Version196608) ||
(web_arg_header.shim_kind == ShimKind::Web &&
web_applet_version >= WebAppletVersion::Version524288) ||
(web_arg_header.shim_kind == ShimKind::Lhub);
(web_arg_header.shim_kind == ShimKind::Share && web_applet_version >= WebAppletVersion::Version196608)
|| (web_arg_header.shim_kind == ShimKind::Web && web_applet_version >= WebAppletVersion::Version524288)
|| (web_arg_header.shim_kind == ShimKind::Lhub);
// https://switchbrew.org/wiki/Internet_Browser#TLVs
if (use_tlv_output) {
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}",
exit_reason, last_url, last_url.size());
LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", exit_reason, last_url, last_url.size());
// storage size for TLVs is 0x2000 bytes (as per switchbrew documentation)
constexpr size_t TLV_STORAGE_SIZE = 0x2000;
@@ -600,11 +598,14 @@ void WebBrowser::ExecuteShare() {
void WebBrowser::ExecuteWeb() {
LOG_INFO(Service_AM, "Opening external URL at {}", external_url);
frontend.OpenExternalWebPage(external_url,
[this](WebExitReason exit_reason, std::string last_url) {
WebBrowserExit(exit_reason, last_url);
});
frontend.OpenExternalWebPage(external_url, [this](WebExitReason exit_reason, std::string last_url) {
// Offline and web applets must be explicitly exited from because they respect exit state
// Unlike the other web stuffs
if (exit_reason == WebExitReason::ExitRequested || exit_reason == WebExitReason::EndButtonPressed)
exit_reason = (web_arg_header.shim_kind == ShimKind::Web || web_arg_header.shim_kind == ShimKind::Offline)
? WebExitReason::ExitRequested : WebExitReason::EndButtonPressed;
WebBrowserExit(exit_reason, last_url);
});
}
void WebBrowser::ExecuteWifi() {
+2
View File
@@ -60,6 +60,8 @@ public:
{}
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
return std::nullopt;
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
if (last_code_addr != aligned_vaddr) {
+17 -11
View File
@@ -24,9 +24,6 @@
#include "network/network.h"
#include <common/settings.h>
using Common::Expected;
using Common::Unexpected;
namespace Service::Sockets {
namespace {
@@ -464,13 +461,22 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>();
Expected<s32, Errno> res = DuplicateSocketImpl(input.fd);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(OutputParameters{
.ret = res.value_or(0),
.bsd_errno = res ? Errno::SUCCESS : res.error(),
});
auto const res_v = DuplicateSocketImpl(input.fd);
if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{
.ret = *res,
.bsd_errno = Errno::SUCCESS,
});
} else {
auto* err = std::get_if<Errno>(&res_v);
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = *err,
});
}
}
void BSD::EventFd(HLERequestContext& ctx) {
@@ -977,15 +983,15 @@ Errno BSD::CloseImpl(s32 fd) {
return bsd_errno;
}
Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Unexpected(Errno::BADF);
return Errno::BADF;
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return Unexpected(Errno::MFILE);
return Errno::MFILE;
}
file_descriptors[new_fd] = FileDescriptor{
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,9 +8,9 @@
#include <memory>
#include <span>
#include <vector>
#include <variant>
#include "common/common_types.h"
#include "common/expected.h"
#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
@@ -35,7 +35,7 @@ public:
// These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service.
Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd);
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
+19 -20
View File
@@ -88,11 +88,12 @@ static const constexpr std::array blockedDomains = {
"sun.hac.lp1.d4c.nintendo.net",
"phoenix-api.wbagora.com", //hogwarts legacy
"battle.net",
"microsoft.com", //minecraft dungeons + other games
"microsoft.com", // Minecraft dungeons + other games
"mojang.com",
"xboxlive.com",
"api.epicgames.dev", // marvel cosmic invasion +?
"minecraftservices.com"
"minecraftservices.com",
"508223012e5a5ff19f30a391b2bdadc0.my.2k.com", // Civilization 5
};
static bool IsBlockedHost(const std::string& host) {
@@ -207,16 +208,15 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
return {0, GetAddrInfoError::AGAIN};
}
auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
if (!res.has_value()) {
return {0, Translate(res.error())};
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
}
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
const u32 data_size = static_cast<u32>(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)};
}
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -332,16 +332,15 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
// Serialized hints are also passed in a buffer, but are ignored for now.
auto res = Network::GetAddressInfo(host, service);
if (!res.has_value()) {
return {0, Translate(res.error())};
auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
}
const std::vector<u8> data = SerializeAddrInfo(res.value(), host);
const u32 data_size = static_cast<u32>(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)};
}
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
+19 -22
View File
@@ -160,29 +160,26 @@ private:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
auto res = bsd->DuplicateSocketImpl(fd);
if (!res.has_value()) {
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
return ResultInvalidSocket;
auto const res_v = bsd->DuplicateSocketImpl(fd);
if (auto *res = std::get_if<s32>(&res_v)) {
const s32 duplicated_fd = *res;
if (do_not_close_socket) {
*out_fd = duplicated_fd;
} else {
*out_fd = -1;
fd_to_close = duplicated_fd;
}
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(socket);
return ResultSuccess;
}
const s32 duplicated_fd = *res;
if (do_not_close_socket) {
*out_fd = duplicated_fd;
} else {
*out_fd = -1;
fd_to_close = duplicated_fd;
}
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(socket);
return ResultSuccess;
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
return ResultInvalidSocket;
}
Result SetHostNameImpl(const std::string& hostname) {
+3 -5
View File
@@ -28,7 +28,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/expected.h"
#include "common/logging.h"
#include "common/settings.h"
#include "core/internal_network/network.h"
@@ -733,15 +732,14 @@ u32 IPv4AddressToInteger(IPv4Address ip_addr) {
static_cast<u32>(ip_addr[2]) << 8 | static_cast<u32>(ip_addr[3]);
}
Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
std::variant<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
const std::string& host, const std::optional<std::string>& service) {
addrinfo hints{};
hints.ai_family = AF_INET; // Switch only supports IPv4.
addrinfo* addrinfo;
s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr,
&hints, &addrinfo);
s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr, &hints, &addrinfo);
if (gai_err != 0) {
return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err));
return TranslateGetAddrInfoErrorFromNative(gai_err);
}
std::vector<AddrInfo> ret;
for (auto* current = addrinfo; current; current = current->ai_next) {
+2 -2
View File
@@ -9,6 +9,7 @@
#include <array>
#include <optional>
#include <vector>
#include <variant>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -124,7 +125,6 @@ std::string IPv4AddressToString(IPv4Address ip_addr);
u32 IPv4AddressToInteger(IPv4Address ip_addr);
// named to avoid name collision with Windows macro
Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
const std::string& host, const std::optional<std::string>& service);
std::variant<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(const std::string& host, const std::optional<std::string>& service);
} // namespace Network
@@ -229,6 +229,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but "
"may degrade performance."));
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
tr("Preserves GPU-modified buffer data by reading it back before uploads.\nSome games require this to render certain effects properly.\nMay cause issues if the hardware cannot handle the additional workload."));
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
tr("May reduce shader stutter."));
INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"),
+10 -8
View File
@@ -1634,15 +1634,17 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
if (total_size_bytes == 0) {
return true;
}
u64 min_offset = (std::numeric_limits<u64>::max)();
u64 max_offset = 0;
for (const auto& copy : upload_copies) {
min_offset = (std::min)(min_offset, copy.dst_offset);
max_offset = (std::max)(max_offset, copy.dst_offset + copy.size);
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
u64 min_offset = (std::numeric_limits<u64>::max)();
u64 max_offset = 0;
for (const auto& copy : upload_copies) {
min_offset = (std::min)(min_offset, copy.dst_offset);
max_offset = (std::max)(max_offset, copy.dst_offset + copy.size);
}
const DAddr sync_addr = buffer.CpuAddr() + min_offset;
const u64 sync_size = max_offset - min_offset;
DownloadBufferMemory(buffer, sync_addr, sync_size);
}
const DAddr sync_addr = buffer.CpuAddr() + min_offset;
const u64 sync_size = max_offset - min_offset;
DownloadBufferMemory(buffer, sync_addr, sync_size);
const std::span<BufferCopy> copies_span(upload_copies.data(), upload_copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
any_buffer_uploaded = true;
+40
View File
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/settings.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
@@ -130,6 +132,44 @@ void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 inde
draw_state.draw_mode = DrawMode::InlineIndex;
}
void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method,
const u32* base_start, u32 amount) {
auto& index_buffer = draw_state.inline_index_draw_indexes;
switch (method) {
case MAXWELL3D_REG_INDEX(draw_inline_index): {
const auto* const bytes = reinterpret_cast<const u8*>(base_start);
index_buffer.insert(index_buffer.end(), bytes, bytes + size_t(amount) * sizeof(u32));
break;
}
case MAXWELL3D_REG_INDEX(inline_index_2x16.even): {
const size_t offset = index_buffer.size();
index_buffer.resize(offset + size_t(amount) * 2 * sizeof(u32));
u8* dst = index_buffer.data() + offset;
for (u32 i = 0; i < amount; ++i) {
const u32 word = base_start[i];
const u32 indexes[2]{word & 0xFFFF, word >> 16};
std::memcpy(dst, indexes, sizeof(indexes));
dst += sizeof(indexes);
}
break;
}
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): {
const size_t offset = index_buffer.size();
index_buffer.resize(offset + size_t(amount) * 4 * sizeof(u32));
u8* dst = index_buffer.data() + offset;
for (u32 i = 0; i < amount; ++i) {
const u32 word = base_start[i];
const u32 indexes[4]{word & 0xFF, (word >> 8) & 0xFF, (word >> 16) & 0xFF,
word >> 24};
std::memcpy(dst, indexes, sizeof(indexes));
dst += sizeof(indexes);
}
break;
}
}
draw_state.draw_mode = DrawMode::InlineIndex;
}
void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) {
auto reset_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First;
auto increment_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent;
+17
View File
@@ -436,6 +436,14 @@ void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* bas
upload_state.ProcessData(base_start, amount);
return;
}
case MAXWELL3D_REG_INDEX(draw_inline_index):
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
if (shadow_state.shadow_ram_control != Regs::ShadowRamControl::Replay) {
ProcessInlineIndexMultiData(method, base_start, amount);
break;
}
[[fallthrough]];
default:
for (u32 i = 0; i < amount; i++) {
CallMethod(system, method, base_start[i], methods_pending - i <= 1);
@@ -622,6 +630,15 @@ void Maxwell3D::ProcessCBData(u32 value) {
ProcessCBMultiData(&value, 1);
}
void Maxwell3D::ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount) {
if (amount == 0) {
return;
}
const u32 argument = ProcessShadowRam(method, start_base[amount - 1]);
ProcessDirtyRegisters(method, argument);
draw_manager.SetInlineIndexBuffer(*this, method, start_base, amount);
}
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
tic_index * sizeof(Texture::TICEntry)};
+3
View File
@@ -3077,6 +3077,7 @@ public:
void DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology);
void DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count);
void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 index);
void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 method, const u32* base_start, u32 amount);
void DrawBegin(Maxwell3D& maxwell3d);
void DrawEnd(Maxwell3D& maxwell3d, u32 instance_count = 1, bool force_draw = false);
void DrawIndexSmall(Maxwell3D& maxwell3d, u32 argument);
@@ -3193,6 +3194,8 @@ public:
void ProcessCBData(u32 value);
void ProcessCBMultiData(const u32* start_base, u32 amount);
void ProcessInlineIndexMultiData(u32 method, const u32* start_base, u32 amount);
private:
void InitializeRegisterDefaults();
+29 -35
View File
@@ -4,6 +4,7 @@
#include "video_core/gpu_logging/gpu_logging.h"
#include <fmt/format.h>
#include <mutex>
#include <thread>
#include "common/fs/file.h"
@@ -280,13 +281,12 @@ void GPULogger::LogMemoryDeallocation(uintptr_t memory) {
}
void GPULogger::LogShaderCompilation(const std::string& shader_name,
const std::string& shader_info,
std::span<const u32> spirv_code) {
const std::string& shader_info) {
if (!initialized || current_level == LogLevel::Off) {
return;
}
if (!dump_shaders && current_level < LogLevel::Verbose) {
if (current_level < LogLevel::Verbose) {
return;
}
@@ -294,38 +294,36 @@ void GPULogger::LogShaderCompilation(const std::string& shader_name,
std::chrono::steady_clock::now().time_since_epoch());
const auto log_entry = fmt::format("[{}] [Shader] Compiled: {} ({})\n",
FormatTimestamp(timestamp), shader_name, shader_info);
FormatTimestamp(timestamp), shader_name, shader_info);
WriteToLog(log_entry);
}
// Dump SPIR-V binary if enabled and we have data
if (dump_shaders && !spirv_code.empty()) {
using namespace Common::FS;
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
const auto shaders_dir = log_dir / "shaders";
bool IsActive() noexcept {
return Settings::values.gpu_log_level.GetValue() != Settings::GpuLogLevel::Off;
}
// Create directory on first dump
if (!shader_dump_dir_created) {
[[maybe_unused]] const bool created = CreateDir(shaders_dir);
shader_dump_dir_created = true;
}
// Write SPIR-V binary file
const auto shader_path = shaders_dir / fmt::format("{}.spv", shader_name);
auto shader_file = std::make_unique<Common::FS::IOFile>(
shader_path, FileAccessMode::Write, FileType::BinaryFile);
if (shader_file->IsOpen()) {
const size_t bytes_to_write = spirv_code.size() * sizeof(u32);
static_cast<void>(shader_file->WriteSpan(spirv_code));
shader_file->Close();
const auto dump_log = fmt::format("[{}] [Shader] Dumped SPIR-V: {} ({} bytes)\n",
FormatTimestamp(timestamp), shader_path.string(), bytes_to_write);
WriteToLog(dump_log);
} else {
LOG_WARNING(Render_Vulkan, "[GPU Logging] Failed to dump shader: {}", shader_path.string());
}
void DumpSpirvShader(u64 shader_hash, std::span<const u32> spirv_code) {
if (spirv_code.empty()) {
return;
}
using namespace Common::FS;
const auto& dump_dir = GetEdenPath(EdenPath::DumpDir);
// Ensure DumpDir exists once. CreateDir is idempotent, so guarded to skip the syscall.
static std::once_flag dump_dir_flag;
std::call_once(dump_dir_flag, [&dump_dir]() {
[[maybe_unused]] const bool created = CreateDir(dump_dir);
});
const auto shader_path = dump_dir / fmt::format("{:016x}_{:016x}.spv",
Settings::GetCurrentProgramID(), shader_hash);
Common::FS::IOFile shader_file(shader_path, FileAccessMode::Write, FileType::BinaryFile);
if (!shader_file.IsOpen()) {
LOG_WARNING(Render_Vulkan, "[Shader Dump] Failed to open {}", shader_path.string());
return;
}
static_cast<void>(shader_file.WriteSpan(spirv_code));
}
void GPULogger::LogPipelineStateChange(const std::string& state_info) {
@@ -657,10 +655,6 @@ void GPULogger::EnableVulkanCallTracking(bool enabled) {
track_vulkan_calls = enabled;
}
void GPULogger::EnableShaderDumps(bool enabled) {
dump_shaders = enabled;
}
void GPULogger::EnableMemoryTracking(bool enabled) {
track_memory = enabled;
}
+5 -7
View File
@@ -87,8 +87,7 @@ public:
void LogVulkanCall(const std::string& call_name, const std::string& params, int result);
void LogMemoryAllocation(uintptr_t memory, u64 size, u32 memory_flags);
void LogMemoryDeallocation(uintptr_t memory);
void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info,
std::span<const u32> spirv_code = {});
void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info);
void LogPipelineStateChange(const std::string& state_info);
void LogDriverDebugInfo(const std::string& debug_info);
@@ -121,7 +120,6 @@ public:
// Settings
void SetLogLevel(LogLevel level);
void EnableVulkanCallTracking(bool enabled);
void EnableShaderDumps(bool enabled);
void EnableMemoryTracking(bool enabled);
void EnableDriverDebugInfo(bool enabled);
void SetRingBufferSize(size_t entries);
@@ -171,7 +169,6 @@ private:
// Feature flags
bool track_vulkan_calls = true;
bool dump_shaders = false;
bool track_memory = false;
bool capture_driver_debug = false;
@@ -179,15 +176,16 @@ private:
std::set<std::string> used_extensions;
mutable std::mutex extension_mutex;
// Shader dump directory (created on demand)
bool shader_dump_dir_created = false;
// Stored state for crash dumps
std::string stored_driver_debug_info;
std::string stored_pipeline_state;
mutable std::mutex state_mutex;
};
[[nodiscard]] bool IsActive() noexcept;
void DumpSpirvShader(u64 shader_hash, std::span<const u32> spirv_code);
// Helper to get stage name from index
inline const char* GetShaderStageName(size_t stage_index) {
static constexpr std::array<const char*, 5> stage_names{
+6 -14
View File
@@ -1328,22 +1328,14 @@ Macro::Opcode MacroJITx64Impl::GetOpCode() const {
#endif
static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
const auto base_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
const auto macro_dir{base_dir / "macros"};
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
if (!Common::FS::CreateDir(dump_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create dump directory");
return;
}
auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
if (decompiled) {
auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
if (Common::FS::Exists(name)) {
(void)Common::FS::RenameFile(name, new_name);
return;
}
name = new_name;
}
const char* const variant_suffix = decompiled ? "jit" : "raw";
const auto name{dump_dir / fmt::format("{:016x}_{:016x}_{}.macro",
Settings::GetCurrentProgramID(), hash, variant_suffix)};
std::fstream macro_file(name, std::ios::out | std::ios::binary);
if (!macro_file) {
@@ -481,7 +481,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
const u32 cfg_offset = u32(env.StartAddress() + sizeof(Shader::ProgramHeader));
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
@@ -578,7 +578,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
if (Settings::values.dump_shaders) {
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hash);
}
+34 -56
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include "video_core/renderer_vulkan/vk_texture_cache.h"
@@ -136,18 +135,15 @@ VkPipelineInputAssemblyStateCreateInfo GetPipelineInputAssemblyStateCreateInfo(c
.primitiveRestartEnable = device.IsMoltenVK() ? VK_TRUE : VK_FALSE,
};
}
VkPipelineViewportStateCreateInfo GetPipelineViewportStateCreateInfo(const Device& device) {
const u32 viewport_count = device.GetViewportCount();
return VkPipelineViewportStateCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.viewportCount = viewport_count,
.pViewports = nullptr,
.scissorCount = viewport_count,
.pScissors = nullptr,
};
}
constexpr VkPipelineViewportStateCreateInfo PIPELINE_VIEWPORT_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.viewportCount = 1,
.pViewports = nullptr,
.scissorCount = 1,
.pScissors = nullptr,
};
constexpr VkPipelineRasterizationStateCreateInfo PIPELINE_RASTERIZATION_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -365,7 +361,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
}
void BindBlitState(const Device& device, vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
const VkOffset2D offset{
.x = (std::min)(dst_region.start.x, dst_region.end.x),
.y = (std::min)(dst_region.start.y, dst_region.end.y),
@@ -387,17 +383,13 @@ void BindBlitState(const Device& device, vk::CommandBuffer cmdbuf, const Region2
.offset = offset,
.extent = extent,
};
const u32 viewport_count = device.GetViewportCount();
const std::array<VkViewport, 2> viewports{viewport, viewport};
const std::array<VkRect2D, 2> scissors{scissor, scissor};
cmdbuf.SetViewport(0, vk::Span<VkViewport>(viewports.data(), viewport_count));
cmdbuf.SetScissor(0, vk::Span<VkRect2D>(scissors.data(), viewport_count));
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
}
void BindBlitState(const Device& device, vk::CommandBuffer cmdbuf, VkPipelineLayout layout,
const Region2D& dst_region, const Region2D& src_region,
const Extent3D& src_size = {1, 1, 1}) {
BindBlitState(device, cmdbuf, dst_region);
void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
BindBlitState(cmdbuf, dst_region);
const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) /
static_cast<float>(src_size.width);
const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) /
@@ -567,7 +559,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(device, cmdbuf, layout, dst_region, src_region);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
@@ -593,7 +585,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(device, cmdbuf, layout, dst_region, src_region, src_size);
BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
});
@@ -629,7 +621,7 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
BindBlitState(device, cmdbuf, layout, dst_region, src_region);
BindBlitState(cmdbuf, layout, dst_region, src_region);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
@@ -710,13 +702,13 @@ void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_ma
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record(
[pipeline, layout, color_mask, clear_color, dst_region, this](vk::CommandBuffer cmdbuf) {
[pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) {
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
const std::array blend_color = {
(color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f,
(color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f};
cmdbuf.SetBlendConstants(blend_color.data());
BindBlitState(device, cmdbuf, dst_region);
BindBlitState(cmdbuf, dst_region);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color);
cmdbuf.Draw(3, 1, 0, 0);
});
@@ -736,11 +728,11 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, clear_depth, dst_region, this](vk::CommandBuffer cmdbuf) {
scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) {
constexpr std::array blend_constants{0.0f, 0.0f, 0.0f, 0.0f};
cmdbuf.SetBlendConstants(blend_constants.data());
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
BindBlitState(device, cmdbuf, dst_region);
BindBlitState(cmdbuf, dst_region);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth);
cmdbuf.Draw(3, 1, 0, 0);
});
@@ -783,11 +775,8 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const u32 viewport_count = device.GetViewportCount();
const std::array<VkViewport, 2> viewports{viewport, viewport};
const std::array<VkRect2D, 2> scissors{scissor, scissor};
cmdbuf.SetViewport(0, vk::Span<VkViewport>(viewports.data(), viewport_count));
cmdbuf.SetScissor(0, vk::Span<VkRect2D>(scissors.data(), viewport_count));
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
});
@@ -832,11 +821,8 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const u32 viewport_count = device.GetViewportCount();
const std::array<VkViewport, 2> viewports{viewport, viewport};
const std::array<VkRect2D, 2> scissors{scissor, scissor};
cmdbuf.SetViewport(0, vk::Span<VkViewport>(viewports.data(), viewport_count));
cmdbuf.SetScissor(0, vk::Span<VkRect2D>(scissors.data(), viewport_count));
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
});
@@ -874,7 +860,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -884,7 +869,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = nullptr,
@@ -907,7 +892,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
blit_depth_stencil_keys.push_back(key);
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -917,7 +901,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -961,7 +945,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -971,7 +954,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -1018,7 +1001,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.maxDepthBounds = 0.0f,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -1028,7 +1010,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &depth_stencil_ci,
@@ -1050,7 +1032,6 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
VkShaderModule frag_shader = *convert_float_to_depth_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -1060,7 +1041,7 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -1081,7 +1062,6 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
VkShaderModule frag_shader = *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -1091,7 +1071,7 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -1113,7 +1093,6 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
}
const std::array stages = MakeStages(*full_screen_vert, *module);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -1123,7 +1102,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
@@ -1156,7 +1135,6 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
const VkPipelineViewportStateCreateInfo viewport_ci = GetPipelineViewportStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -1166,7 +1144,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_ci,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
@@ -183,10 +183,10 @@ VkImageView FSR::Draw(const Device& device, Scheduler& scheduler, size_t image_i
UpdateDescriptorSets(device, source_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, &device](vk::CommandBuffer cmdbuf) {
scheduler.Record([=](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, renderpass, easu_framebuffer, extent);
BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
easu_descriptor_set, {});
@@ -196,7 +196,7 @@ VkImageView FSR::Draw(const Device& device, Scheduler& scheduler, size_t image_i
TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, renderpass, rcas_framebuffer, extent);
BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
rcas_descriptor_set, {});
@@ -129,10 +129,10 @@ void FXAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index,
UpdateDescriptorSets(device, *inout_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, &device](vk::CommandBuffer cmdbuf) {
scheduler.Record([=](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, renderpass, framebuffer, extent);
BeginRenderPass(cmdbuf, renderpass, framebuffer, extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {});
cmdbuf.Draw(3, 1, 0, 0);
@@ -125,10 +125,10 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
UpdateDescriptorSets(device, source_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, &device](vk::CommandBuffer cmdbuf) {
scheduler.Record([=](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, renderpass, framebuffer, extent);
BeginRenderPass(cmdbuf, renderpass, framebuffer, extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {});
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, viewport_con);
@@ -237,10 +237,10 @@ void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index,
UpdateDescriptorSets(device, *inout_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, this, &device](vk::CommandBuffer cmdbuf) {
scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer,
BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer,
m_extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -251,7 +251,7 @@ void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index,
TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, *m_renderpasses[BlendingWeightCalculation],
BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation],
blending_weight_calculation_framebuffer, m_extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
*m_pipelines[BlendingWeightCalculation]);
@@ -263,7 +263,7 @@ void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index,
TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(device, cmdbuf, *m_renderpasses[NeighborhoodBlending],
BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending],
neighborhood_blending_framebuffer, m_extent);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include <array>
#include <ranges>
#include <vulkan/vulkan_core.h>
#include "video_core/renderer_vulkan/present/util.h"
@@ -409,14 +408,14 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = device.IsMoltenVK() ? VK_TRUE : VK_FALSE,
};
const u32 viewport_count = device.GetViewportCount();
const VkPipelineViewportStateCreateInfo viewport_state_ci{
constexpr VkPipelineViewportStateCreateInfo viewport_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.viewportCount = viewport_count,
.viewportCount = 1,
.pViewports = nullptr,
.scissorCount = viewport_count,
.scissorCount = 1,
.pScissors = nullptr,
};
@@ -670,8 +669,8 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources);
}
void BeginRenderPass(const Device& device, vk::CommandBuffer& cmdbuf, VkRenderPass render_pass,
VkFramebuffer framebuffer, VkExtent2D extent) {
void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
VkExtent2D extent) {
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
@@ -698,11 +697,8 @@ void BeginRenderPass(const Device& device, vk::CommandBuffer& cmdbuf, VkRenderPa
.offset = {0, 0},
.extent = extent,
};
const u32 viewport_count = device.GetViewportCount();
const std::array<VkViewport, 2> viewports{viewport, viewport};
const std::array<VkRect2D, 2> scissors{scissor, scissor};
cmdbuf.SetViewport(0, vk::Span<VkViewport>(viewports.data(), viewport_count));
cmdbuf.SetScissor(0, vk::Span<VkRect2D>(scissors.data(), viewport_count));
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
}
} // namespace Vulkan
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -59,7 +59,7 @@ vk::Sampler CreateBilinearSampler(const Device& device);
vk::Sampler CreateNearestNeighborSampler(const Device& device);
vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights);
void BeginRenderPass(const Device& device, vk::CommandBuffer& cmdbuf, VkRenderPass render_pass,
VkFramebuffer framebuffer, VkExtent2D extent);
void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
VkExtent2D extent);
} // namespace Vulkan
@@ -69,7 +69,7 @@ void WindowAdaptPass::Draw(const Device& device, RasterizerVulkan& rasterizer, S
layer_it++;
}
scheduler.Record([=, &device](vk::CommandBuffer cmdbuf) {
scheduler.Record([=](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;
@@ -91,7 +91,7 @@ void WindowAdaptPass::Draw(const Device& device, RasterizerVulkan& rasterizer, S
.layerCount = 1,
};
BeginRenderPass(device, cmdbuf, renderpass, host_framebuffer, render_area);
BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
for (size_t i = 0; i < layer_count; i++) {
@@ -87,7 +87,7 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk
}, *pipeline_cache);
// Log compute pipeline creation
if (Settings::values.gpu_logging_enabled.GetValue()) {
if (GPU::Logging::IsActive()) {
GPU::Logging::GPULogger::GetInstance().LogPipelineStateChange(
"ComputePipeline created"
);
@@ -223,7 +223,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
}
// Log compute pipeline binding
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogPipelineBind(true, "compute pipeline");
}
@@ -532,7 +532,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
// Log graphics pipeline binding
if (bind_pipeline && Settings::values.gpu_logging_enabled.GetValue() &&
if (bind_pipeline && GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string pipeline_info = fmt::format("hash=0x{:016x}", key.Hash());
GPU::Logging::GPULogger::GetInstance().LogPipelineBind(false, pipeline_info);
@@ -986,7 +986,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
}, *pipeline_cache);
// Log graphics pipeline creation
if (Settings::values.gpu_logging_enabled.GetValue()) {
if (GPU::Logging::IsActive()) {
const std::string pipeline_info = fmt::format(
"GraphicsPipeline created: stages={}, attachments={}",
shader_stages.size(),
@@ -751,7 +751,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
if (Settings::values.dump_shaders) {
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
@@ -783,14 +783,22 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
// Log shader compilation to GPU logger (with SPIR-V binary dump if enabled)
if (Settings::values.gpu_logging_enabled.GetValue()) {
// Text log + .spv dump. Text log is gated by gpu_log_level != Off; .spv dump
// is independent and gated only by gpu_log_shader_dumps.
const bool should_log = GPU::Logging::IsActive();
const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue();
if (should_log || should_dump) {
static constexpr std::array stage_names{"vertex", "tess_control", "tess_eval", "geometry", "fragment"};
const std::string shader_name = fmt::format("shader_{:016x}_{}", key.unique_hashes[index], stage_names[stage_index]);
const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}",
code.size() * sizeof(u32), key.unique_hashes[index]);
GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info,
std::span<const u32>(code.data(), code.size()));
if (should_log) {
const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}",
code.size() * sizeof(u32), key.unique_hashes[index]);
GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info);
}
if (should_dump) {
GPU::Logging::DumpSpirvShader(key.unique_hashes[index],
std::span<const u32>(code.data(), code.size()));
}
}
if (device.HasDebuggingToolAttached()) {
@@ -879,7 +887,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
// Dump it before error.
if (Settings::values.dump_shaders) {
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hash);
}
@@ -901,13 +909,20 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)};
// Log compute shader compilation to GPU logger (with SPIR-V binary dump if enabled)
if (Settings::values.gpu_logging_enabled.GetValue()) {
// Text log + .spv dump. Same split as the graphics path.
const bool should_log = GPU::Logging::IsActive();
const bool should_dump = Settings::values.gpu_log_shader_dumps.GetValue();
if (should_log || should_dump) {
const std::string shader_name = fmt::format("shader_{:016x}_compute", key.unique_hash);
const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}",
code.size() * sizeof(u32), key.unique_hash);
GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info,
std::span<const u32>(code.data(), code.size()));
if (should_log) {
const std::string shader_info = fmt::format("SPIR-V size: {} bytes, hash: {:016x}",
code.size() * sizeof(u32), key.unique_hash);
GPU::Logging::GPULogger::GetInstance().LogShaderCompilation(shader_name, shader_info);
}
if (should_dump) {
GPU::Logging::DumpSpirvShader(key.unique_hash,
std::span<const u32>(code.data(), code.size()));
}
}
if (device.HasDebuggingToolAttached()) {
@@ -270,7 +270,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
});
// Log draw call
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string params = is_indexed ?
fmt::format("vertices={}, instances={}, firstIndex={}, baseVertex={}, baseInstance={}",
@@ -331,7 +331,7 @@ void RasterizerVulkan::DrawIndirect() {
});
// Log indirect draw call
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string log_params = fmt::format("drawCount={}, stride={}",
params.max_draw_counts, params.stride);
@@ -585,7 +585,7 @@ void RasterizerVulkan::DispatchCompute() {
scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); });
// Log compute dispatch
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string params = fmt::format("groupCountX={}, groupCountY={}, groupCountZ={}",
dim[0], dim[1], dim[2]);
@@ -1110,7 +1110,7 @@ void RasterizerVulkan::HandleTransformFeedback() {
regs.transform_feedback_enabled);
if (regs.transform_feedback_enabled != 0) {
// Log extension usage for transform feedback
if (Settings::values.gpu_logging_enabled.GetValue()) {
if (GPU::Logging::IsActive()) {
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
"VK_EXT_transform_feedback", "HandleTransformFeedback");
}
@@ -181,7 +181,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
state.render_area = render_area;
// Log render pass begin
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string render_pass_info = fmt::format(
"renderArea={}x{}, numImages={}",
@@ -292,7 +292,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
case VK_SUCCESS:
// Log successful queue submission
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogVulkanCall(
"vkQueueSubmit", "", VK_SUCCESS);
@@ -335,7 +335,7 @@ void Scheduler::EndRenderPass()
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
// Log render pass end
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd();
}
@@ -2332,7 +2332,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
if (has_custom_border_colors) {
pnext = &border_ci;
// Log extension usage for custom border color
if (Settings::values.gpu_logging_enabled.GetValue()) {
if (GPU::Logging::IsActive()) {
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
"VK_EXT_custom_border_color", "Sampler::Sampler");
}
+16 -15
View File
@@ -18,6 +18,7 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "common/settings.h"
#include <ranges>
#include "shader_recompiler/environment.h"
#include "video_core/engines/kepler_compute.h"
@@ -73,36 +74,36 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture
static std::string_view StageToPrefix(Shader::Stage stage) {
switch (stage) {
case Shader::Stage::VertexB:
return "VB";
return "vs";
case Shader::Stage::TessellationControl:
return "TC";
return "tc";
case Shader::Stage::TessellationEval:
return "TE";
return "te";
case Shader::Stage::Geometry:
return "GS";
return "gs";
case Shader::Stage::Fragment:
return "FS";
return "fs";
case Shader::Stage::Compute:
return "CS";
return "cs";
case Shader::Stage::VertexA:
return "VA";
return "va";
default:
return "UK";
return "uk";
}
}
static void DumpImpl(u64 pipeline_hash, u64 shader_hash, std::span<const u64> code,
static void DumpImpl(u64 /*pipeline_hash*/, u64 shader_hash, std::span<const u64> code,
[[maybe_unused]] u32 read_highest, [[maybe_unused]] u32 read_lowest,
u32 initial_offset, Shader::Stage stage) {
const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
const auto base_dir{shader_dir / "shaders"};
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
const auto dump_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::DumpDir)};
if (!Common::FS::CreateDir(dump_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create dump directory");
return;
}
const auto prefix = StageToPrefix(stage);
const auto name{base_dir /
fmt::format("{:016x}_{}_{:016x}.ash", pipeline_hash, prefix, shader_hash)};
const auto name{dump_dir /
fmt::format("{:016x}_{:016x}_{}.ash",
Settings::GetCurrentProgramID(), shader_hash, prefix)};
std::fstream shader_file(name, std::ios::out | std::ios::binary);
ASSERT(initial_offset % sizeof(u64) == 0);
const size_t jump_index = initial_offset / sizeof(u64);
@@ -76,7 +76,7 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
}
// Route to GPU logger for tracking Vulkan validation messages
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
// Convert severity to result code for logging (negative = error)
int result_code = 0;
+4 -13
View File
@@ -441,7 +441,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if (!is_suitable)
LOG_WARNING(Render_Vulkan, "Unsuitable driver - continuing anyways");
@@ -657,11 +656,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
properties.properties.limits.maxVertexInputBindings = 32;
}
if (is_arm && SupportsMultiViewport()) {
LOG_WARNING(Render_Vulkan, "ARM driver requires setting multiple viewports on command buffer reuse");
requires_setting_multi_viewports_on_cmdbuf_reuse = true;
}
const auto dyna_state = Settings::values.dyna_state.GetValue();
// Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp
@@ -1524,7 +1518,10 @@ std::vector<VkDeviceQueueCreateInfo> Device::GetDeviceQueueCreateInfos() const {
}
void Device::InitializeGPULogging() {
if (!Settings::values.gpu_logging_enabled.GetValue()) {
// Get log level from settings — Off is the disable.
const auto log_level = static_cast<GPU::Logging::LogLevel>(
static_cast<u32>(Settings::values.gpu_log_level.GetValue()));
if (log_level == GPU::Logging::LogLevel::Off) {
return;
}
@@ -1538,18 +1535,12 @@ void Device::InitializeGPULogging() {
detected_driver = GPU::Logging::DriverType::Qualcomm;
}
// Get log level from settings
const auto log_level = static_cast<GPU::Logging::LogLevel>(
static_cast<u32>(Settings::values.gpu_log_level.GetValue()));
// Initialize GPU logger
GPU::Logging::GPULogger::GetInstance().Initialize(log_level, detected_driver);
// Configure feature flags
GPU::Logging::GPULogger::GetInstance().EnableVulkanCallTracking(
Settings::values.gpu_log_vulkan_calls.GetValue());
GPU::Logging::GPULogger::GetInstance().EnableShaderDumps(
Settings::values.gpu_log_shader_dumps.GetValue());
GPU::Logging::GPULogger::GetInstance().EnableMemoryTracking(
Settings::values.gpu_log_memory_tracking.GetValue());
GPU::Logging::GPULogger::GetInstance().EnableDriverDebugInfo(
@@ -834,10 +834,6 @@ public:
return features2.features.multiViewport;
}
u32 GetViewportCount() const noexcept {
return requires_setting_multi_viewports_on_cmdbuf_reuse ? 2U : 1U;
}
/// Returns true if the device supports VK_KHR_maintenance1.
bool IsKhrMaintenance1Supported() const {
return extensions.maintenance1;
@@ -1050,7 +1046,6 @@ private:
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
bool requires_setting_multi_viewports_on_cmdbuf_reuse{}; ///< ARM workaround to always set 2+ viewports.
bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3.
bool dynamic_state3_depth_clamp_enable{};
@@ -111,7 +111,7 @@ namespace Vulkan {
: allocator{alloc}, allocation{a}, memory{info.deviceMemory},
offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} {
// Log GPU memory allocation
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_memory_tracking.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation(
reinterpret_cast<uintptr_t>(memory),
@@ -179,7 +179,7 @@ namespace Vulkan {
void MemoryCommit::Release() {
if (allocation && allocator) {
// Log GPU memory deallocation
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_memory_tracking.GetValue() &&
memory != VK_NULL_HANDLE) {
GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation(
@@ -243,7 +243,7 @@ namespace Vulkan {
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info));
// Log GPU memory allocation for images
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_memory_tracking.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation(
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory),
@@ -281,7 +281,7 @@ namespace Vulkan {
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
// Log GPU memory allocation for buffers
if (Settings::values.gpu_logging_enabled.GetValue() &&
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_memory_tracking.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation(
reinterpret_cast<uintptr_t>(alloc_info.deviceMemory),
+12 -3
View File
@@ -81,8 +81,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
ui->dump_shaders->setEnabled(runtime_lock);
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
ui->dump_guest_shaders->setEnabled(runtime_lock);
ui->dump_guest_shaders->setChecked(Settings::values.dump_guest_shaders.GetValue());
ui->dump_macros->setEnabled(runtime_lock);
ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
ui->disable_macro_jit->setEnabled(runtime_lock);
@@ -94,6 +94,12 @@ void ConfigureDebug::SetConfiguration() {
Settings::values.disable_shader_loop_safety_checks.GetValue());
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
ui->debug_knobs_spinbox->setValue(Settings::values.debug_knobs.GetValue());
ui->gpu_log_level->setEnabled(runtime_lock);
ui->gpu_log_level->setCurrentIndex(
static_cast<int>(Settings::values.gpu_log_level.GetValue()));
ui->gpu_log_shader_dumps->setEnabled(runtime_lock);
ui->gpu_log_shader_dumps->setChecked(Settings::values.gpu_log_shader_dumps.GetValue());
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
#else
@@ -123,7 +129,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
Settings::values.dump_guest_shaders = ui->dump_guest_shaders->isChecked();
Settings::values.dump_macros = ui->dump_macros->isChecked();
Settings::values.disable_shader_loop_safety_checks =
ui->disable_loop_safety_checks->isChecked();
@@ -135,6 +141,9 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.serial_battery = ui->serial_battery_edit->text().toUInt();
Settings::values.serial_unit = ui->serial_board_edit->text().toUInt();
Settings::values.debug_knobs = ui->debug_knobs_spinbox->value();
Settings::values.gpu_log_level =
static_cast<Settings::GpuLogLevel>(ui->gpu_log_level->currentIndex());
Settings::values.gpu_log_shader_dumps = ui->gpu_log_shader_dumps->isChecked();
Debugger::ToggleConsole();
Common::Log::Filter filter;
filter.ParseFilterString(Settings::values.log_filter.GetValue());
+162 -43
View File
@@ -157,7 +157,7 @@
<string>Logging</string>
</property>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0" colspan="2">
<item row="0" column="0" colspan="4">
<widget class="QWidget" name="logging_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@@ -197,7 +197,77 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QWidget" name="gpu_log_level_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="gpu_log_level_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="gpu_log_level_label">
<property name="text">
<string>GPU Logging/Level</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="gpu_log_level">
<property name="toolTip">
<string>Detail level for GPU logs. Off disables logging entirely.</string>
</property>
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>Errors</string>
</property>
</item>
<item>
<property name="text">
<string>Standard</string>
</property>
</item>
<item>
<property name="text">
<string>Verbose</string>
</property>
</item>
<item>
<property name="text">
<string>All</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="extended_logging">
<property name="enabled">
<bool>true</bool>
@@ -210,15 +280,20 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="1" column="3">
<widget class="QPushButton" name="open_log_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Open Log Location</string>
</property>
@@ -270,7 +345,7 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QCheckBox" name="dump_macros">
<property name="enabled">
<bool>true</bool>
@@ -294,15 +369,15 @@
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="dump_shaders">
<widget class="QCheckBox" name="dump_guest_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
<string>When checked, it will dump original Maxwell guest shader bytecode (the input to the recompiler) as .ash files under DumpDir/shaders/. Useful for inspection with nvdisasm.</string>
</property>
<property name="text">
<string>Dump Game Shaders</string>
<string>Dump Guest (Maxwell) Shaders</string>
</property>
</widget>
</item>
@@ -313,7 +388,7 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
@@ -326,7 +401,7 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
@@ -352,7 +427,7 @@
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
@@ -388,6 +463,16 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="gpu_log_shader_dumps">
<property name="toolTip">
<string>When checked, it will dump the recompiler's output SPIR-V binaries (.spv) under LogDir/shaders/. Inspect via SPIRV-Tools (spirv-dis / spirv-cross / spirv-val) or RenderDoc.</string>
</property>
<property name="text">
<string>Dump SPIR-V Shaders</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -514,31 +599,65 @@
</layout>
</widget>
</item>
<item row="8" column="0">
<widget class="QSpinBox" name="debug_knobs_spinbox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="toolTip">
<string>Bitmask for quick development toggles</string>
</property>
<property name="statusTip">
<string>Set debug knobs (bitmask)</string>
</property>
<property name="whatsThis">
<string>16-bit debug knob set for quick development toggles</string>
</property>
<property name="suffix">
<string> (bitmask)</string>
</property>
<property name="prefix">
<string>Debug Knobs: </string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QWidget" name="debug_knobs_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_debug_knobs">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_debug_knobs">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debug Knobs:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="debug_knobs_spinbox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="toolTip">
<string>Bitmask for quick development toggles</string>
</property>
<property name="statusTip">
<string>Set debug knobs (bitmask)</string>
</property>
<property name="whatsThis">
<string>16-bit debug knob set for quick development toggles</string>
</property>
<property name="suffix">
<string> (bitmask)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QWidget" name="serial_board_widget" native="true">
<property name="sizePolicy">
@@ -579,7 +698,7 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<item row="20" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>