Compare commits
79 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c439fc9be9 | ||
|
|
5ab8053511 | ||
|
|
b2862e4772 | ||
|
|
a75d70fa90 | ||
|
|
5665d05547 | ||
|
|
15c0c213b1 | ||
|
|
7cf0958b06 | ||
|
|
9143fe5d3a | ||
|
|
47f13a9df4 | ||
|
|
2c7fdee7a7 | ||
|
|
7530594602 | ||
|
|
0334b9b776 | ||
|
|
335de3fdf5 | ||
|
|
ba3d230421 | ||
|
|
275db94bb8 | ||
|
|
6ca8ed9e58 | ||
|
|
21ff0a3d6e | ||
|
|
db07ca6c7f | ||
|
|
a98f14e9b0 | ||
|
|
8c9febe8f7 | ||
|
|
23b3333f72 | ||
|
|
5acf020389 | ||
|
|
124e3b4819 | ||
|
|
f771d92e44 | ||
|
|
d05e6003f0 | ||
|
|
5593a3716e | ||
|
|
d923ec5805 | ||
|
|
1bb46b7d64 | ||
|
|
3b006f4fe2 | ||
|
|
92887a65f0 | ||
|
|
6053f2e1fe | ||
|
|
353be2306c | ||
|
|
c4cd82fa7c | ||
|
|
ab206d6378 | ||
|
|
5da97c57cd | ||
|
|
2717e79c74 | ||
|
|
e2c42ec5e2 | ||
|
|
ef29ed75b0 | ||
|
|
3109d1c3db | ||
|
|
a24224e274 | ||
|
|
a6359fe9ae | ||
|
|
d7cd316a6c | ||
|
|
00e100de08 | ||
|
|
f8964dd89a | ||
|
|
8c82c594f0 | ||
|
|
ec56a17acd | ||
|
|
075a744e38 | ||
|
|
296728ec46 | ||
|
|
c27ddb44de | ||
|
|
e490ddf327 | ||
|
|
90f3678ada | ||
|
|
f5f04cce01 | ||
|
|
b9b48aee7d | ||
|
|
d6c799494c | ||
|
|
7511f65e31 | ||
|
|
1a94b3f452 | ||
|
|
75050c788c | ||
|
|
c6991fa900 | ||
|
|
6c432d5349 | ||
|
|
5f9a4817a5 | ||
|
|
c0f99558fb | ||
|
|
de0b89792c | ||
|
|
3d97f1e6cf | ||
|
|
7490117fa4 | ||
|
|
c05bbf375d | ||
|
|
b2b3fcdccd | ||
|
|
4afc2de129 | ||
|
|
771dcb2a56 | ||
|
|
27ed6e7c2b | ||
|
|
3e7813e49d | ||
|
|
c2915d9f2f | ||
|
|
06ca911621 | ||
|
|
0b67df1f7c | ||
|
|
89ad9df0e9 | ||
|
|
09d6cc9943 | ||
|
|
c80ae87b4e | ||
|
|
9dfbc9bdce | ||
|
|
d8b00fd863 | ||
|
|
b11c81cc13 |
@@ -496,7 +496,7 @@ endif()
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
|
||||
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
|
||||
else()
|
||||
find_package(LibUSB)
|
||||
|
||||
@@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
|
||||
std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
|
||||
|
||||
/**
|
||||
* Converts a buffer of bytes to a UTF8-encoded std::string.
|
||||
* This converts from the start of the buffer until the first encountered null-terminator.
|
||||
* If no null-terminator is found, this converts the entire buffer instead.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
@@ -357,7 +357,12 @@ public:
|
||||
});
|
||||
|
||||
// Backing memory initialization
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#else
|
||||
fd = memfd_create("HostMemory", 0);
|
||||
#endif
|
||||
if (fd == -1) {
|
||||
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
|
||||
@@ -48,8 +48,8 @@ void LogSettings() {
|
||||
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
||||
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
||||
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
|
||||
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
|
||||
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
||||
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||
@@ -132,8 +132,8 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
values.max_anisotropy.SetGlobal(true);
|
||||
values.use_frame_limit.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.use_speed_limit.SetGlobal(true);
|
||||
values.speed_limit.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
|
||||
@@ -42,6 +42,11 @@ enum class CPUAccuracy : u32 {
|
||||
Unsafe = 2,
|
||||
};
|
||||
|
||||
enum class FullscreenMode : u32 {
|
||||
Borderless = 0,
|
||||
Exclusive = 1,
|
||||
};
|
||||
|
||||
/** The BasicSetting class is a simple resource manager. It defines a label and default value
|
||||
* alongside the actual value of the setting for simpler and less-error prone use with frontend
|
||||
* configurations. Setting a default value and label is required, though subclasses may deviate from
|
||||
@@ -314,6 +319,7 @@ struct Values {
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
|
||||
BasicSetting<bool> disable_shader_loop_safety_checks{false,
|
||||
"disable_shader_loop_safety_checks"};
|
||||
@@ -322,17 +328,17 @@ struct Values {
|
||||
Setting<u16> resolution_factor{1, "resolution_factor"};
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
Setting<int> fullscreen_mode{
|
||||
Setting<FullscreenMode> fullscreen_mode{
|
||||
#ifdef _WIN32
|
||||
0,
|
||||
FullscreenMode::Borderless,
|
||||
#else
|
||||
1,
|
||||
FullscreenMode::Exclusive,
|
||||
#endif
|
||||
"fullscreen_mode"};
|
||||
Setting<int> aspect_ratio{0, "aspect_ratio"};
|
||||
Setting<int> max_anisotropy{0, "max_anisotropy"};
|
||||
Setting<bool> use_frame_limit{true, "use_frame_limit"};
|
||||
Setting<u16> frame_limit{100, "frame_limit"};
|
||||
Setting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||
Setting<u16> speed_limit{100, "speed_limit"};
|
||||
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||
Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
|
||||
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
||||
|
||||
@@ -18,7 +18,7 @@ UUID UUID::Generate() {
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
|
||||
@@ -411,7 +411,7 @@ struct System::Impl {
|
||||
std::string status_details = "";
|
||||
|
||||
std::unique_ptr<Core::PerfStats> perf_stats;
|
||||
Core::FrameLimiter frame_limiter;
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
|
||||
bool is_multicore{};
|
||||
bool is_async_gpu{};
|
||||
@@ -606,12 +606,12 @@ const Core::PerfStats& System::GetPerfStats() const {
|
||||
return *impl->perf_stats;
|
||||
}
|
||||
|
||||
Core::FrameLimiter& System::FrameLimiter() {
|
||||
return impl->frame_limiter;
|
||||
Core::SpeedLimiter& System::SpeedLimiter() {
|
||||
return impl->speed_limiter;
|
||||
}
|
||||
|
||||
const Core::FrameLimiter& System::FrameLimiter() const {
|
||||
return impl->frame_limiter;
|
||||
const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||
return impl->speed_limiter;
|
||||
}
|
||||
|
||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||
|
||||
@@ -94,7 +94,7 @@ class ARM_Interface;
|
||||
class CpuManager;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class FrameLimiter;
|
||||
class SpeedLimiter;
|
||||
class PerfStats;
|
||||
class Reporter;
|
||||
class TelemetrySession;
|
||||
@@ -292,11 +292,11 @@ public:
|
||||
/// Provides a constant reference to the internal PerfStats instance.
|
||||
[[nodiscard]] const Core::PerfStats& GetPerfStats() const;
|
||||
|
||||
/// Provides a reference to the frame limiter;
|
||||
[[nodiscard]] Core::FrameLimiter& FrameLimiter();
|
||||
/// Provides a reference to the speed limiter;
|
||||
[[nodiscard]] Core::SpeedLimiter& SpeedLimiter();
|
||||
|
||||
/// Provides a constant referent to the frame limiter
|
||||
[[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
|
||||
/// Provides a constant reference to the speed limiter
|
||||
[[nodiscard]] const Core::SpeedLimiter& SpeedLimiter() const;
|
||||
|
||||
/// Gets the name of the current game
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
|
||||
|
||||
// Horizon OS version constants.
|
||||
|
||||
constexpr u8 HOS_VERSION_MAJOR = 11;
|
||||
constexpr u8 HOS_VERSION_MINOR = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 1;
|
||||
constexpr u8 HOS_VERSION_MAJOR = 12;
|
||||
constexpr u8 HOS_VERSION_MINOR = 1;
|
||||
constexpr u8 HOS_VERSION_MICRO = 0;
|
||||
|
||||
// NintendoSDK version constants.
|
||||
|
||||
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
|
||||
constexpr u8 SDK_REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
|
||||
constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
|
||||
constexpr char DISPLAY_VERSION[] = "12.1.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
|
||||
|
||||
constexpr u32 GetTargetFirmware() {
|
||||
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
|
||||
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
|
||||
protected:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
ProfileData data{};
|
||||
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
|
||||
@@ -301,7 +301,7 @@ protected:
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
|
||||
user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
@@ -309,14 +309,14 @@ protected:
|
||||
}
|
||||
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
@@ -372,7 +372,7 @@ protected:
|
||||
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
@@ -405,7 +405,7 @@ protected:
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
const auto image_data = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
|
||||
|
||||
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
|
||||
// way of confirming things like the TID, we're going to assume a non zero value for the time
|
||||
@@ -858,7 +858,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
const auto tid = rp.Pop<u64_le>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +377,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
|
||||
|
||||
if (swkbd_config_common.use_utf8) {
|
||||
std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
|
||||
const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
|
||||
const u64 buffer_size = utf8_submitted_text.size();
|
||||
|
||||
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
|
||||
utf8_submitted_text);
|
||||
@@ -386,7 +386,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
|
||||
std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
|
||||
utf8_submitted_text.size());
|
||||
} else {
|
||||
const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
|
||||
const u64 buffer_size = current_text.size() * sizeof(char16_t);
|
||||
|
||||
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
|
||||
Common::UTF16ToUTF8(current_text));
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
const auto local_play = rp.Pop<bool>();
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play,
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
|
||||
uuid.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
|
||||
const auto pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
|
||||
uuid.Format(), pid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format());
|
||||
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -339,13 +339,16 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
case Set::LanguageCode::FR_CA:
|
||||
return ApplicationLanguage::CanadianFrench;
|
||||
case Set::LanguageCode::PT:
|
||||
case Set::LanguageCode::PT_BR:
|
||||
return ApplicationLanguage::Portuguese;
|
||||
case Set::LanguageCode::RU:
|
||||
return ApplicationLanguage::Russian;
|
||||
case Set::LanguageCode::KO:
|
||||
return ApplicationLanguage::Korean;
|
||||
case Set::LanguageCode::ZH_TW:
|
||||
case Set::LanguageCode::ZH_HANT:
|
||||
return ApplicationLanguage::TraditionalChinese;
|
||||
case Set::LanguageCode::ZH_CN:
|
||||
case Set::LanguageCode::ZH_HANS:
|
||||
return ApplicationLanguage::SimplifiedChinese;
|
||||
default:
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
Service::Set::LanguageCode language_code);
|
||||
std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -54,7 +54,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||
system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||
system.GetPerfStats().BeginSystemFrame();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Service::Set {
|
||||
namespace {
|
||||
constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
constexpr std::array<LanguageCode, 18> available_language_codes = {{
|
||||
LanguageCode::JA,
|
||||
LanguageCode::EN_US,
|
||||
LanguageCode::FR,
|
||||
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::ES_419,
|
||||
LanguageCode::ZH_HANS,
|
||||
LanguageCode::ZH_HANT,
|
||||
LanguageCode::PT_BR,
|
||||
}};
|
||||
|
||||
enum class KeyboardLayout : u64 {
|
||||
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
|
||||
ChineseTraditional = 14,
|
||||
};
|
||||
|
||||
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
|
||||
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
|
||||
{LanguageCode::JA, KeyboardLayout::Japanese},
|
||||
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
|
||||
{LanguageCode::FR, KeyboardLayout::French},
|
||||
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
|
||||
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
|
||||
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
|
||||
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
|
||||
{LanguageCode::PT_BR, KeyboardLayout::Portuguese},
|
||||
}};
|
||||
|
||||
constexpr std::size_t pre4_0_0_max_entries = 15;
|
||||
constexpr std::size_t post4_0_0_max_entries = 17;
|
||||
constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
|
||||
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
|
||||
|
||||
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
|
||||
|
||||
@@ -81,9 +83,9 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
|
||||
rb.Push(static_cast<u32>(num_language_codes));
|
||||
}
|
||||
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
|
||||
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
|
||||
const std::size_t copy_amount = std::min(requested_amount, max_size);
|
||||
const std::size_t copy_amount = std::min(requested_amount, max_entries);
|
||||
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
|
||||
|
||||
ctx.WriteBuffer(available_language_codes.data(), copy_size);
|
||||
@@ -118,7 +120,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
|
||||
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -140,19 +142,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
|
||||
PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,6 +31,7 @@ enum class LanguageCode : u64 {
|
||||
ES_419 = 0x00003931342D7365,
|
||||
ZH_HANS = 0x00736E61482D687A,
|
||||
ZH_HANT = 0x00746E61482D687A,
|
||||
PT_BR = 0x00000052422D7470,
|
||||
};
|
||||
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
|
||||
|
||||
|
||||
@@ -127,15 +127,15 @@ double PerfStats::GetLastFrameTimeScale() const {
|
||||
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
||||
void SpeedLimiter::DoSpeedLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_speed_limit.GetValue() ||
|
||||
Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
||||
const double sleep_scale = Settings::values.speed_limit.GetValue() / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
@@ -143,17 +143,17 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
// limiting
|
||||
const microseconds max_lag_time_us = duration_cast<microseconds>(
|
||||
std::chrono::duration<double, std::chrono::microseconds::period>(25ms / sleep_scale));
|
||||
frame_limiting_delta_err += duration_cast<microseconds>(
|
||||
speed_limiting_delta_err += duration_cast<microseconds>(
|
||||
std::chrono::duration<double, std::chrono::microseconds::period>(
|
||||
(current_system_time_us - previous_system_time_us) / sleep_scale));
|
||||
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
||||
frame_limiting_delta_err =
|
||||
std::clamp(frame_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
||||
speed_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
||||
speed_limiting_delta_err =
|
||||
std::clamp(speed_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
||||
|
||||
if (frame_limiting_delta_err > microseconds::zero()) {
|
||||
std::this_thread::sleep_for(frame_limiting_delta_err);
|
||||
if (speed_limiting_delta_err > microseconds::zero()) {
|
||||
std::this_thread::sleep_for(speed_limiting_delta_err);
|
||||
auto now_after_sleep = Clock::now();
|
||||
frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
||||
speed_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
||||
now = now_after_sleep;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ private:
|
||||
double previous_fps = 0;
|
||||
};
|
||||
|
||||
class FrameLimiter {
|
||||
class SpeedLimiter {
|
||||
public:
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
|
||||
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
|
||||
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
|
||||
|
||||
private:
|
||||
/// Emulated system time (in microseconds) at the last limiter invocation
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
Clock::time_point previous_walltime = Clock::now();
|
||||
|
||||
/// Accumulated difference between walltime and emulated time
|
||||
std::chrono::microseconds frame_limiting_delta_err{0};
|
||||
std::chrono::microseconds speed_limiting_delta_err{0};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -221,8 +221,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue());
|
||||
AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache.GetValue());
|
||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||
|
||||
@@ -350,7 +350,7 @@ std::string_view InputPrimitive(InputTopology topology) {
|
||||
case InputTopology::Lines:
|
||||
return "LINES";
|
||||
case InputTopology::LinesAdjacency:
|
||||
return "LINESS_ADJACENCY";
|
||||
return "LINES_ADJACENCY";
|
||||
case InputTopology::Triangles:
|
||||
return "TRIANGLES";
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -17,7 +17,7 @@ class Exception : public std::exception {
|
||||
public:
|
||||
explicit Exception(std::string message) noexcept : err_message{std::move(message)} {}
|
||||
|
||||
const char* what() const noexcept override {
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return err_message.c_str();
|
||||
}
|
||||
|
||||
@@ -36,21 +36,21 @@ private:
|
||||
class LogicError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
LogicError(const char* message, Args&&... args)
|
||||
explicit LogicError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class RuntimeError : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
RuntimeError(const char* message, Args&&... args)
|
||||
explicit RuntimeError(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
class NotImplementedException : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
NotImplementedException(const char* message, Args&&... args)
|
||||
explicit NotImplementedException(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {
|
||||
Append(" is not implemented");
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
class InvalidArgument : public Exception {
|
||||
public:
|
||||
template <typename... Args>
|
||||
InvalidArgument(const char* message, Args&&... args)
|
||||
explicit InvalidArgument(const char* message, Args&&... args)
|
||||
: Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {}
|
||||
};
|
||||
|
||||
|
||||
@@ -327,8 +327,8 @@ public:
|
||||
const Value& derivates, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
|
||||
[[nodiscard]] void ImageWrite(const Value& handle, const Value& coords, const Value& color,
|
||||
TextureInstInfo info);
|
||||
void ImageWrite(const Value& handle, const Value& coords, const Value& color,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
|
||||
@@ -198,8 +198,8 @@ public:
|
||||
}
|
||||
|
||||
template <typename FlagsType>
|
||||
requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
|
||||
[[nodiscard]] void SetFlags(FlagsType value) noexcept {
|
||||
requires(sizeof(FlagsType) <= sizeof(u32) &&
|
||||
std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept {
|
||||
std::memcpy(&flags, &value, sizeof(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ Token OpcodeToken(Opcode opcode) {
|
||||
return Token::PBK;
|
||||
case Opcode::PCNT:
|
||||
case Opcode::CONT:
|
||||
return Token::PBK;
|
||||
return Token::PCNT;
|
||||
case Opcode::PEXIT:
|
||||
case Opcode::EXIT:
|
||||
return Token::PEXIT;
|
||||
|
||||
@@ -111,6 +111,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::ConvertF16U16:
|
||||
case IR::Opcode::ConvertF16U32:
|
||||
case IR::Opcode::ConvertF16U64:
|
||||
case IR::Opcode::ConvertF16F32:
|
||||
case IR::Opcode::ConvertF32F16:
|
||||
case IR::Opcode::FPAbs16:
|
||||
case IR::Opcode::FPAdd16:
|
||||
case IR::Opcode::FPCeil16:
|
||||
|
||||
@@ -649,6 +649,10 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
||||
return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
|
||||
case IR::Opcode::UnpackHalf2x16:
|
||||
return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
|
||||
case IR::Opcode::PackFloat2x16:
|
||||
return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16);
|
||||
case IR::Opcode::UnpackFloat2x16:
|
||||
return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16);
|
||||
case IR::Opcode::SelectU1:
|
||||
case IR::Opcode::SelectU8:
|
||||
case IR::Opcode::SelectU16:
|
||||
|
||||
@@ -63,6 +63,7 @@ private:
|
||||
used_objects = std::exchange(rhs.used_objects, 0);
|
||||
num_objects = std::exchange(rhs.num_objects, 0);
|
||||
storage = std::move(rhs.storage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Chunk(Chunk&& rhs) noexcept
|
||||
|
||||
@@ -106,6 +106,8 @@ add_library(video_core STATIC
|
||||
renderer_vulkan/maxwell_to_vk.cpp
|
||||
renderer_vulkan/maxwell_to_vk.h
|
||||
renderer_vulkan/pipeline_helper.h
|
||||
renderer_vulkan/pipeline_statistics.cpp
|
||||
renderer_vulkan/pipeline_statistics.h
|
||||
renderer_vulkan/renderer_vulkan.h
|
||||
renderer_vulkan/renderer_vulkan.cpp
|
||||
renderer_vulkan/vk_blit_screen.cpp
|
||||
|
||||
@@ -817,7 +817,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
const std::size_t size = interval.upper() - interval.lower();
|
||||
const VAddr cpu_addr = interval.lower();
|
||||
ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
boost::container::small_vector<BufferCopy, 1> copies;
|
||||
buffer.ForEachDownloadRangeAndClear(
|
||||
cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
||||
const VAddr buffer_addr = buffer.CpuAddr();
|
||||
|
||||
@@ -299,7 +299,7 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
VideoCore::RasterizerInterface* rasterizer;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
/// Performs the copy from the source surface to the destination surface as configured in the
|
||||
/// registers.
|
||||
|
||||
@@ -227,7 +227,7 @@ private:
|
||||
Core::System& system;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
VideoCore::RasterizerInterface* rasterizer;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
std::vector<u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
@@ -10,33 +10,27 @@
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_ENC_BUFFER 1
|
||||
#define BINDING_SWIZZLE_BUFFER 2
|
||||
#define BINDING_OUTPUT_IMAGE 3
|
||||
#define BINDING_OUTPUT_IMAGE 1
|
||||
|
||||
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
|
||||
|
||||
#define BEGIN_PUSH_CONSTANTS
|
||||
#define END_PUSH_CONSTANTS
|
||||
#define UNIFORM(n) layout(location = n) uniform
|
||||
#define BINDING_SWIZZLE_BUFFER 0
|
||||
#define BINDING_INPUT_BUFFER 1
|
||||
#define BINDING_ENC_BUFFER 2
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_IMAGE 0
|
||||
|
||||
#endif
|
||||
|
||||
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
BEGIN_PUSH_CONSTANTS
|
||||
UNIFORM(1) uvec2 block_dims;
|
||||
|
||||
UNIFORM(2) uint bytes_per_block_log2;
|
||||
UNIFORM(3) uint layer_stride;
|
||||
UNIFORM(4) uint block_size;
|
||||
UNIFORM(5) uint x_shift;
|
||||
UNIFORM(6) uint block_height;
|
||||
UNIFORM(7) uint block_height_mask;
|
||||
UNIFORM(2) uint layer_stride;
|
||||
UNIFORM(3) uint block_size;
|
||||
UNIFORM(4) uint x_shift;
|
||||
UNIFORM(5) uint block_height;
|
||||
UNIFORM(6) uint block_height_mask;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
struct EncodingData {
|
||||
@@ -55,45 +49,35 @@ struct TexelWeightParams {
|
||||
bool void_extent_hdr;
|
||||
};
|
||||
|
||||
// Swizzle data
|
||||
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
|
||||
uint swizzle_table[];
|
||||
};
|
||||
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 {
|
||||
uint astc_data[];
|
||||
};
|
||||
|
||||
// ASTC Encodings data
|
||||
layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues {
|
||||
EncodingData encoding_values[];
|
||||
uvec4 astc_data[];
|
||||
};
|
||||
|
||||
layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
const uint GOB_SIZE_Z = 1;
|
||||
const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
const uint GOB_SIZE_Z_SHIFT = 0;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
|
||||
|
||||
const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
|
||||
const int BLOCK_INFO_ERROR = 0;
|
||||
const int BLOCK_INFO_VOID_EXTENT_HDR = 1;
|
||||
const int BLOCK_INFO_VOID_EXTENT_LDR = 2;
|
||||
const int BLOCK_INFO_NORMAL = 3;
|
||||
const uint BYTES_PER_BLOCK_LOG2 = 4;
|
||||
|
||||
const int JUST_BITS = 0;
|
||||
const int QUINT = 1;
|
||||
const int TRIT = 2;
|
||||
|
||||
// ASTC Encodings data, sorted in ascending order based on their BitLength value
|
||||
// (see GetBitLength() function)
|
||||
EncodingData encoding_values[22] = EncodingData[](
|
||||
EncodingData(JUST_BITS, 0, 0, 0), EncodingData(JUST_BITS, 1, 0, 0), EncodingData(TRIT, 0, 0, 0),
|
||||
EncodingData(JUST_BITS, 2, 0, 0), EncodingData(QUINT, 0, 0, 0), EncodingData(TRIT, 1, 0, 0),
|
||||
EncodingData(JUST_BITS, 3, 0, 0), EncodingData(QUINT, 1, 0, 0), EncodingData(TRIT, 2, 0, 0),
|
||||
EncodingData(JUST_BITS, 4, 0, 0), EncodingData(QUINT, 2, 0, 0), EncodingData(TRIT, 3, 0, 0),
|
||||
EncodingData(JUST_BITS, 5, 0, 0), EncodingData(QUINT, 3, 0, 0), EncodingData(TRIT, 4, 0, 0),
|
||||
EncodingData(JUST_BITS, 6, 0, 0), EncodingData(QUINT, 4, 0, 0), EncodingData(TRIT, 5, 0, 0),
|
||||
EncodingData(JUST_BITS, 7, 0, 0), EncodingData(QUINT, 5, 0, 0), EncodingData(TRIT, 6, 0, 0),
|
||||
EncodingData(JUST_BITS, 8, 0, 0)
|
||||
);
|
||||
|
||||
// The following constants are expanded variants of the Replicate()
|
||||
// function calls corresponding to the following arguments:
|
||||
// value: index into the generated table
|
||||
@@ -135,44 +119,37 @@ const uint REPLICATE_7_BIT_TO_8_TABLE[128] =
|
||||
// Input ASTC texture globals
|
||||
uint current_index = 0;
|
||||
int bitsread = 0;
|
||||
uint total_bitsread = 0;
|
||||
uint local_buff[16];
|
||||
int total_bitsread = 0;
|
||||
uvec4 local_buff;
|
||||
|
||||
// Color data globals
|
||||
uint color_endpoint_data[16];
|
||||
uvec4 color_endpoint_data;
|
||||
int color_bitsread = 0;
|
||||
uint total_color_bitsread = 0;
|
||||
int color_index = 0;
|
||||
|
||||
// Four values, two endpoints, four maximum paritions
|
||||
uint color_values[32];
|
||||
int colvals_index = 0;
|
||||
|
||||
// Weight data globals
|
||||
uint texel_weight_data[16];
|
||||
uvec4 texel_weight_data;
|
||||
int texel_bitsread = 0;
|
||||
uint total_texel_bitsread = 0;
|
||||
int texel_index = 0;
|
||||
|
||||
bool texel_flag = false;
|
||||
|
||||
// Global "vectors" to be pushed into when decoding
|
||||
EncodingData result_vector[100];
|
||||
EncodingData result_vector[144];
|
||||
int result_index = 0;
|
||||
|
||||
EncodingData texel_vector[100];
|
||||
EncodingData texel_vector[144];
|
||||
int texel_vector_index = 0;
|
||||
|
||||
uint unquantized_texel_weights[2][144];
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return swizzle_table[pos.y * 64 + pos.x];
|
||||
}
|
||||
|
||||
uint ReadTexel(uint offset) {
|
||||
// extract the 8-bit value from the 32-bit packed data.
|
||||
return bitfieldExtract(astc_data[offset / 4], int((offset * 8) & 24), 8);
|
||||
uint x = pos.x;
|
||||
uint y = pos.y;
|
||||
return ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
|
||||
(y % 2) * 16 + (x % 16);
|
||||
}
|
||||
|
||||
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
|
||||
@@ -278,14 +255,10 @@ uint Hash52(uint p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bool small_block) {
|
||||
if (partition_count == 1) {
|
||||
return 0;
|
||||
}
|
||||
uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) {
|
||||
if (small_block) {
|
||||
x <<= 1;
|
||||
y <<= 1;
|
||||
z <<= 1;
|
||||
}
|
||||
|
||||
seed += (partition_count - 1) * 1024;
|
||||
@@ -299,10 +272,6 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
|
||||
uint seed6 = uint((rnum >> 20) & 0xF);
|
||||
uint seed7 = uint((rnum >> 24) & 0xF);
|
||||
uint seed8 = uint((rnum >> 28) & 0xF);
|
||||
uint seed9 = uint((rnum >> 18) & 0xF);
|
||||
uint seed10 = uint((rnum >> 22) & 0xF);
|
||||
uint seed11 = uint((rnum >> 26) & 0xF);
|
||||
uint seed12 = uint(((rnum >> 30) | (rnum << 2)) & 0xF);
|
||||
|
||||
seed1 = (seed1 * seed1);
|
||||
seed2 = (seed2 * seed2);
|
||||
@@ -312,12 +281,8 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
|
||||
seed6 = (seed6 * seed6);
|
||||
seed7 = (seed7 * seed7);
|
||||
seed8 = (seed8 * seed8);
|
||||
seed9 = (seed9 * seed9);
|
||||
seed10 = (seed10 * seed10);
|
||||
seed11 = (seed11 * seed11);
|
||||
seed12 = (seed12 * seed12);
|
||||
|
||||
int sh1, sh2, sh3;
|
||||
uint sh1, sh2;
|
||||
if ((seed & 1) > 0) {
|
||||
sh1 = (seed & 2) > 0 ? 4 : 5;
|
||||
sh2 = (partition_count == 3) ? 6 : 5;
|
||||
@@ -325,25 +290,19 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
|
||||
sh1 = (partition_count == 3) ? 6 : 5;
|
||||
sh2 = (seed & 2) > 0 ? 4 : 5;
|
||||
}
|
||||
sh3 = (seed & 0x10) > 0 ? sh1 : sh2;
|
||||
seed1 >>= sh1;
|
||||
seed2 >>= sh2;
|
||||
seed3 >>= sh1;
|
||||
seed4 >>= sh2;
|
||||
seed5 >>= sh1;
|
||||
seed6 >>= sh2;
|
||||
seed7 >>= sh1;
|
||||
seed8 >>= sh2;
|
||||
|
||||
seed1 = (seed1 >> sh1);
|
||||
seed2 = (seed2 >> sh2);
|
||||
seed3 = (seed3 >> sh1);
|
||||
seed4 = (seed4 >> sh2);
|
||||
seed5 = (seed5 >> sh1);
|
||||
seed6 = (seed6 >> sh2);
|
||||
seed7 = (seed7 >> sh1);
|
||||
seed8 = (seed8 >> sh2);
|
||||
seed9 = (seed9 >> sh3);
|
||||
seed10 = (seed10 >> sh3);
|
||||
seed11 = (seed11 >> sh3);
|
||||
seed12 = (seed12 >> sh3);
|
||||
|
||||
uint a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
|
||||
uint b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
|
||||
uint c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
|
||||
uint d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
|
||||
uint a = seed1 * x + seed2 * y + (rnum >> 14);
|
||||
uint b = seed3 * x + seed4 * y + (rnum >> 10);
|
||||
uint c = seed5 * x + seed6 * y + (rnum >> 6);
|
||||
uint d = seed7 * x + seed8 * y + (rnum >> 2);
|
||||
|
||||
a &= 0x3F;
|
||||
b &= 0x3F;
|
||||
@@ -368,58 +327,37 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
|
||||
}
|
||||
}
|
||||
|
||||
uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) {
|
||||
return SelectPartition(seed, x, y, 0, partition_count, small_block);
|
||||
}
|
||||
|
||||
uint ReadBit() {
|
||||
if (current_index >= local_buff.length()) {
|
||||
uint ExtractBits(uvec4 payload, int offset, int bits) {
|
||||
if (bits <= 0) {
|
||||
return 0;
|
||||
}
|
||||
uint bit = bitfieldExtract(local_buff[current_index], bitsread, 1);
|
||||
++bitsread;
|
||||
++total_bitsread;
|
||||
if (bitsread == 8) {
|
||||
++current_index;
|
||||
bitsread = 0;
|
||||
int last_offset = offset + bits - 1;
|
||||
int shifted_offset = offset >> 5;
|
||||
if ((last_offset >> 5) == shifted_offset) {
|
||||
return bitfieldExtract(payload[shifted_offset], offset & 31, bits);
|
||||
}
|
||||
return bit;
|
||||
int first_bits = 32 - (offset & 31);
|
||||
int result_first = int(bitfieldExtract(payload[shifted_offset], offset & 31, first_bits));
|
||||
int result_second = int(bitfieldExtract(payload[shifted_offset + 1], 0, bits - first_bits));
|
||||
return result_first | (result_second << first_bits);
|
||||
}
|
||||
|
||||
uint StreamBits(uint num_bits) {
|
||||
uint ret = 0;
|
||||
for (uint i = 0; i < num_bits; i++) {
|
||||
ret |= ((ReadBit() & 1) << i);
|
||||
}
|
||||
int int_bits = int(num_bits);
|
||||
uint ret = ExtractBits(local_buff, total_bitsread, int_bits);
|
||||
total_bitsread += int_bits;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint ReadColorBit() {
|
||||
uint bit = 0;
|
||||
if (texel_flag) {
|
||||
bit = bitfieldExtract(texel_weight_data[texel_index], texel_bitsread, 1);
|
||||
++texel_bitsread;
|
||||
++total_texel_bitsread;
|
||||
if (texel_bitsread == 8) {
|
||||
++texel_index;
|
||||
texel_bitsread = 0;
|
||||
}
|
||||
} else {
|
||||
bit = bitfieldExtract(color_endpoint_data[color_index], color_bitsread, 1);
|
||||
++color_bitsread;
|
||||
++total_color_bitsread;
|
||||
if (color_bitsread == 8) {
|
||||
++color_index;
|
||||
color_bitsread = 0;
|
||||
}
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
uint StreamColorBits(uint num_bits) {
|
||||
uint ret = 0;
|
||||
for (uint i = 0; i < num_bits; i++) {
|
||||
ret |= ((ReadColorBit() & 1) << i);
|
||||
int int_bits = int(num_bits);
|
||||
if (texel_flag) {
|
||||
ret = ExtractBits(texel_weight_data, texel_bitsread, int_bits);
|
||||
texel_bitsread += int_bits;
|
||||
} else {
|
||||
ret = ExtractBits(color_endpoint_data, color_bitsread, int_bits);
|
||||
color_bitsread += int_bits;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -596,22 +534,16 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits) {
|
||||
for (uint i = 0; i < num_partitions; i++) {
|
||||
num_values += ((modes[i] >> 2) + 1) << 1;
|
||||
}
|
||||
int range = 256;
|
||||
while (--range > 0) {
|
||||
EncodingData val = encoding_values[range];
|
||||
// Find the largest encoding that's within color_data_bits
|
||||
// TODO(ameerj): profile with binary search
|
||||
int range = 0;
|
||||
while (++range < encoding_values.length()) {
|
||||
uint bit_length = GetBitLength(num_values, range);
|
||||
if (bit_length <= color_data_bits) {
|
||||
while (--range > 0) {
|
||||
EncodingData newval = encoding_values[range];
|
||||
if (newval.encoding != val.encoding && newval.num_bits != val.num_bits) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++range;
|
||||
if (bit_length > color_data_bits) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
DecodeIntegerSequence(range, num_values);
|
||||
DecodeIntegerSequence(range - 1, num_values);
|
||||
uint out_index = 0;
|
||||
for (int itr = 0; itr < result_index; ++itr) {
|
||||
if (out_index >= num_values) {
|
||||
@@ -1028,7 +960,7 @@ int FindLayout(uint mode) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
TexelWeightParams DecodeBlockInfo(uint block_index) {
|
||||
TexelWeightParams DecodeBlockInfo() {
|
||||
TexelWeightParams params = TexelWeightParams(uvec2(0), 0, false, false, false, false);
|
||||
uint mode = StreamBits(11);
|
||||
if ((mode & 0x1ff) == 0x1fc) {
|
||||
@@ -1110,10 +1042,10 @@ TexelWeightParams DecodeBlockInfo(uint block_index) {
|
||||
}
|
||||
weight_index -= 2;
|
||||
if ((mode_layout != 9) && ((mode & 0x200) != 0)) {
|
||||
const int max_weights[6] = int[6](9, 11, 15, 19, 23, 31);
|
||||
const int max_weights[6] = int[6](7, 8, 9, 10, 11, 12);
|
||||
params.max_weight = max_weights[weight_index];
|
||||
} else {
|
||||
const int max_weights[6] = int[6](1, 2, 3, 4, 5, 7);
|
||||
const int max_weights[6] = int[6](1, 2, 3, 4, 5, 6);
|
||||
params.max_weight = max_weights[weight_index];
|
||||
}
|
||||
return params;
|
||||
@@ -1144,8 +1076,8 @@ void FillVoidExtentLDR(ivec3 coord) {
|
||||
}
|
||||
}
|
||||
|
||||
void DecompressBlock(ivec3 coord, uint block_index) {
|
||||
TexelWeightParams params = DecodeBlockInfo(block_index);
|
||||
void DecompressBlock(ivec3 coord) {
|
||||
TexelWeightParams params = DecodeBlockInfo();
|
||||
if (params.error_state) {
|
||||
FillError(coord);
|
||||
return;
|
||||
@@ -1212,7 +1144,7 @@ void DecompressBlock(ivec3 coord, uint block_index) {
|
||||
// Read color data...
|
||||
uint color_data_bits = remaining_bits;
|
||||
while (remaining_bits > 0) {
|
||||
int nb = int(min(remaining_bits, 8U));
|
||||
int nb = int(min(remaining_bits, 32U));
|
||||
uint b = StreamBits(nb);
|
||||
color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb));
|
||||
++ced_pointer;
|
||||
@@ -1254,25 +1186,20 @@ void DecompressBlock(ivec3 coord, uint block_index) {
|
||||
ComputeEndpoints(endpoints[i][0], endpoints[i][1], color_endpoint_mode[i]);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
texel_weight_data[i] = local_buff[i];
|
||||
}
|
||||
for (uint i = 0; i < 8; i++) {
|
||||
#define REVERSE_BYTE(b) ((b * 0x0802U & 0x22110U) | (b * 0x8020U & 0x88440U)) * 0x10101U >> 16
|
||||
uint a = REVERSE_BYTE(texel_weight_data[i]);
|
||||
uint b = REVERSE_BYTE(texel_weight_data[15 - i]);
|
||||
#undef REVERSE_BYTE
|
||||
texel_weight_data[i] = uint(bitfieldExtract(b, 0, 8));
|
||||
texel_weight_data[15 - i] = uint(bitfieldExtract(a, 0, 8));
|
||||
}
|
||||
texel_weight_data = local_buff;
|
||||
texel_weight_data = bitfieldReverse(texel_weight_data).wzyx;
|
||||
uint clear_byte_start =
|
||||
(GetPackedBitSize(params.size, params.dual_plane, params.max_weight) >> 3) + 1;
|
||||
texel_weight_data[clear_byte_start - 1] =
|
||||
texel_weight_data[clear_byte_start - 1] &
|
||||
|
||||
uint byte_insert = ExtractBits(texel_weight_data, int(clear_byte_start - 1) * 8, 8) &
|
||||
uint(
|
||||
((1 << (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) % 8)) - 1));
|
||||
for (uint i = 0; i < 16 - clear_byte_start; i++) {
|
||||
texel_weight_data[clear_byte_start + i] = 0U;
|
||||
uint vec_index = (clear_byte_start - 1) >> 2;
|
||||
texel_weight_data[vec_index] =
|
||||
bitfieldInsert(texel_weight_data[vec_index], byte_insert, int((clear_byte_start - 1) % 4) * 8, 8);
|
||||
for (uint i = clear_byte_start; i < 16; ++i) {
|
||||
uint idx = i >> 2;
|
||||
texel_weight_data[idx] = bitfieldInsert(texel_weight_data[idx], 0, int(i % 4) * 8, 8);
|
||||
}
|
||||
texel_flag = true; // use texel "vector" and bit stream in integer decoding
|
||||
DecodeIntegerSequence(params.max_weight, GetNumWeightValues(params.size, params.dual_plane));
|
||||
@@ -1281,8 +1208,11 @@ void DecompressBlock(ivec3 coord, uint block_index) {
|
||||
|
||||
for (uint j = 0; j < block_dims.y; j++) {
|
||||
for (uint i = 0; i < block_dims.x; i++) {
|
||||
uint local_partition = Select2DPartition(partition_index, i, j, num_partitions,
|
||||
uint local_partition = 0;
|
||||
if (num_partitions > 1) {
|
||||
local_partition = Select2DPartition(partition_index, i, j, num_partitions,
|
||||
(block_dims.y * block_dims.x) < 32);
|
||||
}
|
||||
vec4 p;
|
||||
uvec4 C0 = ReplicateByteTo16(endpoints[local_partition][0]);
|
||||
uvec4 C1 = ReplicateByteTo16(endpoints[local_partition][1]);
|
||||
@@ -1303,7 +1233,7 @@ void DecompressBlock(ivec3 coord, uint block_index) {
|
||||
|
||||
void main() {
|
||||
uvec3 pos = gl_GlobalInvocationID;
|
||||
pos.x <<= bytes_per_block_log2;
|
||||
pos.x <<= BYTES_PER_BLOCK_LOG2;
|
||||
|
||||
// Read as soon as possible due to its latency
|
||||
const uint swizzle = SwizzleOffset(pos.xy);
|
||||
@@ -1321,13 +1251,8 @@ void main() {
|
||||
if (any(greaterThanEqual(coord, imageSize(dest_image)))) {
|
||||
return;
|
||||
}
|
||||
uint block_index =
|
||||
pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x;
|
||||
|
||||
current_index = 0;
|
||||
bitsread = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
local_buff[i] = ReadTexel(offset + i);
|
||||
}
|
||||
DecompressBlock(coord, block_index);
|
||||
local_buff = astc_data[offset / 16];
|
||||
DecompressBlock(coord);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ RendererBase::~RendererBase() = default;
|
||||
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
|
||||
renderer_settings.set_background_color = true;
|
||||
}
|
||||
|
||||
void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||
|
||||
@@ -21,9 +21,6 @@ class GraphicsContext;
|
||||
namespace VideoCore {
|
||||
|
||||
struct RendererSettings {
|
||||
std::atomic_bool use_framelimiter{false};
|
||||
std::atomic_bool set_background_color{false};
|
||||
|
||||
// Screenshot
|
||||
std::atomic<bool> screenshot_requested{false};
|
||||
void* screenshot_bits{};
|
||||
|
||||
@@ -441,7 +441,6 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
|
||||
std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{};
|
||||
|
||||
OGLProgram source_program;
|
||||
std::array<std::string, 5> sources;
|
||||
std::array<std::vector<u32>, 5> sources_spirv;
|
||||
Shader::Backend::Bindings binding;
|
||||
|
||||
@@ -328,12 +328,10 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
}
|
||||
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
|
||||
Settings::values.bg_green.GetValue() / 255.0f,
|
||||
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
|
||||
}
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
|
||||
Settings::values.bg_green.GetValue() / 255.0f,
|
||||
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
|
||||
|
||||
// Set projection matrix
|
||||
const std::array ortho_matrix =
|
||||
|
||||
@@ -60,19 +60,14 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
|
||||
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
|
||||
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
|
||||
swizzle_table_buffer.Create();
|
||||
astc_buffer.Create();
|
||||
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
|
||||
glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_ENCODINGS_VALUES), &ASTC_ENCODINGS_VALUES,
|
||||
0);
|
||||
}
|
||||
|
||||
UtilShaders::~UtilShaders() = default;
|
||||
|
||||
void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
|
||||
static constexpr GLuint BINDING_ENC_BUFFER = 2;
|
||||
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
|
||||
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
|
||||
|
||||
const Extent2D tile_size{
|
||||
@@ -80,34 +75,32 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
|
||||
.height = VideoCore::Surface::DefaultBlockHeight(image.info.format),
|
||||
};
|
||||
program_manager.BindComputeProgram(astc_decoder_program.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle);
|
||||
|
||||
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
|
||||
glUniform2ui(1, tile_size.width, tile_size.height);
|
||||
|
||||
// Ensure buffer data is valid before dispatching
|
||||
glFlush();
|
||||
for (const SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
|
||||
ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
|
||||
ASSERT(params.bytes_per_block_log2 == 4);
|
||||
|
||||
glUniform1ui(2, params.bytes_per_block_log2);
|
||||
glUniform1ui(3, params.layer_stride);
|
||||
glUniform1ui(4, params.block_size);
|
||||
glUniform1ui(5, params.x_shift);
|
||||
glUniform1ui(6, params.block_height);
|
||||
glUniform1ui(7, params.block_height_mask);
|
||||
glUniform1ui(2, params.layer_stride);
|
||||
glUniform1ui(3, params.block_size);
|
||||
glUniform1ui(4, params.x_shift);
|
||||
glUniform1ui(5, params.block_height);
|
||||
glUniform1ui(6, params.block_height_mask);
|
||||
|
||||
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
|
||||
GL_WRITE_ONLY, GL_RGBA8);
|
||||
// ASTC texture data
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
|
||||
GL_WRITE_ONLY, GL_RGBA8);
|
||||
|
||||
glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ private:
|
||||
ProgramManager& program_manager;
|
||||
|
||||
OGLBuffer swizzle_table_buffer;
|
||||
OGLBuffer astc_buffer;
|
||||
|
||||
OGLProgram astc_decoder_program;
|
||||
OGLProgram block_linear_unswizzle_2d_program;
|
||||
|
||||
100
src/video_core/renderer_vulkan/pipeline_statistics.cpp
Normal file
100
src/video_core/renderer_vulkan/pipeline_statistics.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_statistics.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
|
||||
switch (statistic.format) {
|
||||
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
|
||||
return static_cast<u64>(statistic.value.i64);
|
||||
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
|
||||
return statistic.value.u64;
|
||||
case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
|
||||
return static_cast<u64>(statistic.value.f64);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
|
||||
|
||||
void PipelineStatistics::Collect(VkPipeline pipeline) {
|
||||
const auto& dev{device.GetLogical()};
|
||||
const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
|
||||
const u32 num_executables{static_cast<u32>(properties.size())};
|
||||
for (u32 executable = 0; executable < num_executables; ++executable) {
|
||||
const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
|
||||
if (statistics.empty()) {
|
||||
continue;
|
||||
}
|
||||
Stats stage_stats;
|
||||
for (const auto& statistic : statistics) {
|
||||
const char* const name{statistic.name};
|
||||
if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
|
||||
stage_stats.code_size = GetUint64(statistic);
|
||||
} else if (name == "Register Count"sv) {
|
||||
stage_stats.register_count = GetUint64(statistic);
|
||||
} else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
|
||||
stage_stats.sgpr_count = GetUint64(statistic);
|
||||
} else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
|
||||
stage_stats.vgpr_count = GetUint64(statistic);
|
||||
} else if (name == "Branches"sv) {
|
||||
stage_stats.branches_count = GetUint64(statistic);
|
||||
} else if (name == "Basic Block Count"sv) {
|
||||
stage_stats.basic_block_count = GetUint64(statistic);
|
||||
}
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
collected_stats.push_back(stage_stats);
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineStatistics::Report() const {
|
||||
double num{};
|
||||
Stats total;
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
for (const Stats& stats : collected_stats) {
|
||||
total.code_size += stats.code_size;
|
||||
total.register_count += stats.register_count;
|
||||
total.sgpr_count += stats.sgpr_count;
|
||||
total.vgpr_count += stats.vgpr_count;
|
||||
total.branches_count += stats.branches_count;
|
||||
total.basic_block_count += stats.basic_block_count;
|
||||
}
|
||||
num = static_cast<double>(collected_stats.size());
|
||||
}
|
||||
std::string report;
|
||||
const auto add = [&](const char* fmt, u64 value) {
|
||||
if (value > 0) {
|
||||
report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
|
||||
}
|
||||
};
|
||||
add("Code size: {:9.03f}\n", total.code_size);
|
||||
add("Register count: {:9.03f}\n", total.register_count);
|
||||
add("SGPRs: {:9.03f}\n", total.sgpr_count);
|
||||
add("VGPRs: {:9.03f}\n", total.vgpr_count);
|
||||
add("Branches count: {:9.03f}\n", total.branches_count);
|
||||
add("Basic blocks: {:9.03f}\n", total.basic_block_count);
|
||||
|
||||
LOG_INFO(Render_Vulkan,
|
||||
"\nAverage pipeline statistics\n"
|
||||
"==========================================\n"
|
||||
"{}\n",
|
||||
report);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
40
src/video_core/renderer_vulkan/pipeline_statistics.h
Normal file
40
src/video_core/renderer_vulkan/pipeline_statistics.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
|
||||
class PipelineStatistics {
|
||||
public:
|
||||
explicit PipelineStatistics(const Device& device_);
|
||||
|
||||
void Collect(VkPipeline pipeline);
|
||||
|
||||
void Report() const;
|
||||
|
||||
private:
|
||||
struct Stats {
|
||||
u64 code_size{};
|
||||
u64 register_count{};
|
||||
u64 sgpr_count{};
|
||||
u64 vgpr_count{};
|
||||
u64 branches_count{};
|
||||
u64 basic_block_count{};
|
||||
};
|
||||
|
||||
const Device& device;
|
||||
mutable std::mutex mutex;
|
||||
std::vector<Stats> collected_stats;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -358,7 +358,7 @@ void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
if (null_buffer) {
|
||||
return;
|
||||
}
|
||||
null_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
|
||||
VkBufferCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
@@ -367,9 +367,13 @@ void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
});
|
||||
};
|
||||
if (device.IsExtTransformFeedbackSupported()) {
|
||||
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
|
||||
}
|
||||
null_buffer = device.GetLogical().CreateBuffer(create_info);
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
null_buffer.SetObjectNameEXT("Null index buffer");
|
||||
null_buffer.SetObjectNameEXT("Null buffer");
|
||||
}
|
||||
null_buffer_commit = memory_allocator.Commit(null_buffer, MemoryUsage::DeviceLocal);
|
||||
|
||||
|
||||
@@ -30,16 +30,12 @@
|
||||
namespace Vulkan {
|
||||
|
||||
using Tegra::Texture::SWIZZLE_TABLE;
|
||||
using Tegra::Texture::ASTC::ASTC_ENCODINGS_VALUES;
|
||||
using namespace Tegra::Texture::ASTC;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 ASTC_BINDING_ENC_BUFFER = 1;
|
||||
constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 2;
|
||||
constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 3;
|
||||
constexpr size_t ASTC_NUM_BINDINGS = 4;
|
||||
constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 1;
|
||||
constexpr size_t ASTC_NUM_BINDINGS = 2;
|
||||
|
||||
template <size_t size>
|
||||
inline constexpr VkPushConstantRange COMPUTE_PUSH_CONSTANT_RANGE{
|
||||
@@ -75,7 +71,7 @@ constexpr DescriptorBankInfo INPUT_OUTPUT_BANK_INFO{
|
||||
.score = 2,
|
||||
};
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDINGS{{
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> ASTC_DESCRIPTOR_SET_BINDINGS{{
|
||||
{
|
||||
.binding = ASTC_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
@@ -83,20 +79,6 @@ constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDIN
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = ASTC_BINDING_ENC_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = ASTC_BINDING_SWIZZLE_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = ASTC_BINDING_OUTPUT_IMAGE,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
@@ -108,12 +90,12 @@ constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDIN
|
||||
|
||||
constexpr DescriptorBankInfo ASTC_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 3,
|
||||
.storage_buffers = 1,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 1,
|
||||
.score = 4,
|
||||
.score = 2,
|
||||
};
|
||||
|
||||
constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{
|
||||
@@ -135,22 +117,6 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
|
||||
.offset = ASTC_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = ASTC_BINDING_ENC_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = ASTC_BINDING_ENC_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = ASTC_BINDING_SWIZZLE_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = ASTC_BINDING_SWIZZLE_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = ASTC_BINDING_OUTPUT_IMAGE,
|
||||
.dstArrayElement = 0,
|
||||
@@ -163,7 +129,6 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
|
||||
|
||||
struct AstcPushConstants {
|
||||
std::array<u32, 2> blocks_dims;
|
||||
u32 bytes_per_block_log2;
|
||||
u32 layer_stride;
|
||||
u32 block_size;
|
||||
u32 x_shift;
|
||||
@@ -258,10 +223,9 @@ std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer
|
||||
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
||||
update_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
|
||||
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
|
||||
const VkBuffer buffer{staging.buffer};
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, buffer, descriptor_data, num_vertices](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([this, descriptor_data, num_vertices](vk::CommandBuffer cmdbuf) {
|
||||
static constexpr u32 DISPATCH_SIZE = 1024;
|
||||
static constexpr VkMemoryBarrier WRITE_BARRIER{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
@@ -319,14 +283,14 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
|
||||
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, buffer = staging.buffer, descriptor_data, num_tri_vertices, base_vertex,
|
||||
scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex,
|
||||
index_shift](vk::CommandBuffer cmdbuf) {
|
||||
static constexpr u32 DISPATCH_SIZE = 1024;
|
||||
static constexpr VkMemoryBarrier WRITE_BARRIER{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
|
||||
.dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
|
||||
};
|
||||
const std::array push_constants{base_vertex, index_shift};
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
@@ -355,46 +319,6 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_,
|
||||
|
||||
ASTCDecoderPass::~ASTCDecoderPass() = default;
|
||||
|
||||
void ASTCDecoderPass::MakeDataBuffer() {
|
||||
constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_ENCODINGS_VALUES) + sizeof(SWIZZLE_TABLE);
|
||||
data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = TOTAL_BUFFER_SIZE,
|
||||
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
});
|
||||
data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload);
|
||||
|
||||
const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload);
|
||||
std::memcpy(staging_ref.mapped_span.data(), &ASTC_ENCODINGS_VALUES,
|
||||
sizeof(ASTC_ENCODINGS_VALUES));
|
||||
// Tack on the swizzle table at the end of the buffer
|
||||
std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_ENCODINGS_VALUES), &SWIZZLE_TABLE,
|
||||
sizeof(SWIZZLE_TABLE));
|
||||
|
||||
scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer,
|
||||
TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) {
|
||||
static constexpr VkMemoryBarrier write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
};
|
||||
const VkBufferCopy copy{
|
||||
.srcOffset = offset,
|
||||
.dstOffset = 0,
|
||||
.size = TOTAL_BUFFER_SIZE,
|
||||
};
|
||||
cmdbuf.CopyBuffer(src, dst, copy);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0, write_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
@@ -403,9 +327,6 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
VideoCore::Surface::DefaultBlockHeight(image.info.format),
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
if (!data_buffer) {
|
||||
MakeDataBuffer();
|
||||
}
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
@@ -437,16 +358,13 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
});
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||
const u32 num_dispatches_z = image.info.resources.layers;
|
||||
|
||||
update_descriptor_queue.Acquire();
|
||||
update_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
update_descriptor_queue.AddBuffer(*data_buffer, 0, sizeof(ASTC_ENCODINGS_VALUES));
|
||||
update_descriptor_queue.AddBuffer(*data_buffer, sizeof(ASTC_ENCODINGS_VALUES),
|
||||
sizeof(SWIZZLE_TABLE));
|
||||
update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{update_descriptor_queue.UpdateData()};
|
||||
|
||||
@@ -454,11 +372,11 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
|
||||
ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
|
||||
ASSERT(params.bytes_per_block_log2 == 4);
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims,
|
||||
params, descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const AstcPushConstants uniforms{
|
||||
.blocks_dims = block_dims,
|
||||
.bytes_per_block_log2 = params.bytes_per_block_log2,
|
||||
.layer_stride = params.layer_stride,
|
||||
.block_size = params.block_size,
|
||||
.x_shift = params.x_shift,
|
||||
|
||||
@@ -96,15 +96,10 @@ public:
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
void MakeDataBuffer();
|
||||
|
||||
VKScheduler& scheduler;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue;
|
||||
MemoryAllocator& memory_allocator;
|
||||
|
||||
vk::Buffer data_buffer;
|
||||
MemoryCommit data_buffer_commit;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "video_core/renderer_vulkan/pipeline_helper.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_statistics.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
@@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair;
|
||||
ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue_,
|
||||
Common::ThreadWorker* thread_worker,
|
||||
PipelineStatistics* pipeline_statistics,
|
||||
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
|
||||
vk::ShaderModule spv_module_)
|
||||
: device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
|
||||
@@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
|
||||
std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
|
||||
uniform_buffer_sizes.begin());
|
||||
|
||||
auto func{[this, &descriptor_pool, shader_notify] {
|
||||
auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
|
||||
DescriptorLayoutBuilder builder{device};
|
||||
builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
|
||||
@@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
|
||||
.pNext = nullptr,
|
||||
.requiredSubgroupSize = GuestWarpSize,
|
||||
};
|
||||
VkPipelineCreateFlags flags{};
|
||||
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
|
||||
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
|
||||
}
|
||||
pipeline = device.GetLogical().CreateComputePipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.flags = flags,
|
||||
.stage{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
|
||||
@@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
|
||||
.basePipelineHandle = 0,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
if (pipeline_statistics) {
|
||||
pipeline_statistics->Collect(*pipeline);
|
||||
}
|
||||
std::lock_guard lock{build_mutex};
|
||||
is_built = true;
|
||||
build_condvar.notify_one();
|
||||
|
||||
@@ -25,6 +25,7 @@ class ShaderNotify;
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
class PipelineStatistics;
|
||||
class VKScheduler;
|
||||
|
||||
class ComputePipeline {
|
||||
@@ -32,6 +33,7 @@ public:
|
||||
explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Common::ThreadWorker* thread_worker,
|
||||
PipelineStatistics* pipeline_statistics,
|
||||
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
|
||||
vk::ShaderModule spv_module);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_helper.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_statistics.h"
|
||||
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
|
||||
@@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline(
|
||||
VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
|
||||
VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
|
||||
RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_,
|
||||
std::array<vk::ShaderModule, NUM_STAGES> stages,
|
||||
PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
|
||||
const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
|
||||
const std::array<const Shader::Info*, NUM_STAGES>& infos)
|
||||
: key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
|
||||
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
|
||||
@@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline(
|
||||
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
|
||||
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
|
||||
}
|
||||
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] {
|
||||
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
|
||||
DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
|
||||
uses_push_descriptor = builder.CanUsePushDescriptor();
|
||||
descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
|
||||
@@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline(
|
||||
const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
|
||||
Validate();
|
||||
MakePipeline(render_pass);
|
||||
if (pipeline_statistics) {
|
||||
pipeline_statistics->Collect(*pipeline);
|
||||
}
|
||||
|
||||
std::lock_guard lock{build_mutex};
|
||||
is_built = true;
|
||||
@@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
}
|
||||
*/
|
||||
}
|
||||
VkPipelineCreateFlags flags{};
|
||||
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
|
||||
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
|
||||
}
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.flags = flags,
|
||||
.stageCount = static_cast<u32>(shader_stages.size()),
|
||||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input_ci,
|
||||
|
||||
@@ -60,6 +60,7 @@ struct hash<Vulkan::GraphicsPipelineCacheKey> {
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
class PipelineStatistics;
|
||||
class RenderPassCache;
|
||||
class VKScheduler;
|
||||
class VKUpdateDescriptorQueue;
|
||||
@@ -73,8 +74,9 @@ public:
|
||||
VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
|
||||
VideoCore::ShaderNotify* shader_notify, const Device& device,
|
||||
DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache,
|
||||
const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
|
||||
Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
|
||||
RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
|
||||
std::array<vk::ShaderModule, NUM_STAGES> stages,
|
||||
const std::array<const Shader::Info*, NUM_STAGES>& infos);
|
||||
|
||||
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_helper.h"
|
||||
#include "video_core/renderer_vulkan/pipeline_statistics.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
@@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
size_t total{};
|
||||
size_t built{};
|
||||
bool has_loaded{};
|
||||
std::unique_ptr<PipelineStatistics> statistics;
|
||||
} state;
|
||||
|
||||
if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
|
||||
state.statistics = std::make_unique<PipelineStatistics>(device);
|
||||
}
|
||||
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
|
||||
ComputePipelineCacheKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
|
||||
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
|
||||
ShaderPools pools;
|
||||
auto pipeline{CreateComputePipeline(pools, key, env, false)};
|
||||
auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
|
||||
std::lock_guard lock{state.mutex};
|
||||
if (pipeline) {
|
||||
compute_cache.emplace(key, std::move(pipeline));
|
||||
@@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
for (auto& env : envs) {
|
||||
env_ptrs.push_back(&env);
|
||||
}
|
||||
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)};
|
||||
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
|
||||
state.statistics.get(), false)};
|
||||
|
||||
std::lock_guard lock{state.mutex};
|
||||
graphics_cache.emplace(key, std::move(pipeline));
|
||||
@@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
lock.unlock();
|
||||
|
||||
workers.WaitForRequests();
|
||||
|
||||
if (state.statistics) {
|
||||
state.statistics->Report();
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
|
||||
@@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||
std::span<Shader::Environment* const> envs, bool build_in_parallel) try {
|
||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||
bool build_in_parallel) try {
|
||||
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
|
||||
size_t env_index{0};
|
||||
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
||||
@@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
|
||||
return std::make_unique<GraphicsPipeline>(
|
||||
maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
|
||||
descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key,
|
||||
descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
|
||||
std::move(modules), infos);
|
||||
|
||||
} catch (const Shader::Exception& exception) {
|
||||
@@ -553,7 +564,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
||||
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
|
||||
auto pipeline{
|
||||
CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
|
||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
}
|
||||
@@ -578,7 +590,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
env.SetCachedSize(shader->size_bytes);
|
||||
|
||||
main_pools.ReleaseContents();
|
||||
auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
|
||||
auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
|
||||
if (!pipeline || pipeline_cache_filename.empty()) {
|
||||
return pipeline;
|
||||
}
|
||||
@@ -591,7 +603,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
|
||||
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
|
||||
bool build_in_parallel) try {
|
||||
PipelineStatistics* statistics, bool build_in_parallel) try {
|
||||
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
@@ -605,8 +617,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
}
|
||||
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
|
||||
return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
|
||||
thread_worker, &shader_notify, program.info,
|
||||
std::move(spv_module));
|
||||
thread_worker, statistics, &shader_notify,
|
||||
program.info, std::move(spv_module));
|
||||
|
||||
} catch (const Shader::Exception& exception) {
|
||||
LOG_ERROR(Render_Vulkan, "{}", exception.what());
|
||||
|
||||
@@ -80,8 +80,9 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
|
||||
namespace Vulkan {
|
||||
|
||||
class ComputePipeline;
|
||||
class Device;
|
||||
class DescriptorPool;
|
||||
class Device;
|
||||
class PipelineStatistics;
|
||||
class RasterizerVulkan;
|
||||
class RenderPassCache;
|
||||
class VKScheduler;
|
||||
@@ -128,7 +129,8 @@ private:
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
|
||||
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
|
||||
std::span<Shader::Environment* const> envs, bool build_in_parallel);
|
||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||
bool build_in_parallel);
|
||||
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
|
||||
const ShaderInfo* shader);
|
||||
@@ -136,6 +138,7 @@ private:
|
||||
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
|
||||
const ComputePipelineCacheKey& key,
|
||||
Shader::Environment& env,
|
||||
PipelineStatistics* statistics,
|
||||
bool build_in_parallel);
|
||||
|
||||
const Device& device;
|
||||
|
||||
@@ -61,11 +61,15 @@ std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask) {
|
||||
// Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this
|
||||
std::optional<u32> type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS);
|
||||
if (type) {
|
||||
return *type;
|
||||
u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask,
|
||||
bool try_device_local) {
|
||||
std::optional<u32> type;
|
||||
if (try_device_local) {
|
||||
// Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this
|
||||
type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS);
|
||||
if (type) {
|
||||
return *type;
|
||||
}
|
||||
}
|
||||
// Otherwise try without the DEVICE_LOCAL_BIT
|
||||
type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS);
|
||||
@@ -115,12 +119,21 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
|
||||
.buffer = *stream_buffer,
|
||||
};
|
||||
const auto memory_properties = device.GetPhysical().GetMemoryProperties();
|
||||
stream_memory = dev.AllocateMemory(VkMemoryAllocateInfo{
|
||||
VkMemoryAllocateInfo stream_memory_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = make_dedicated ? &dedicated_info : nullptr,
|
||||
.allocationSize = requirements.size,
|
||||
.memoryTypeIndex = FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits),
|
||||
});
|
||||
.memoryTypeIndex =
|
||||
FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true),
|
||||
};
|
||||
stream_memory = dev.TryAllocateMemory(stream_memory_info);
|
||||
if (!stream_memory) {
|
||||
LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory");
|
||||
stream_memory_info.memoryTypeIndex =
|
||||
FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false);
|
||||
stream_memory = dev.AllocateMemory(stream_memory_info);
|
||||
}
|
||||
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
stream_memory.SetObjectNameEXT("Stream Buffer Memory");
|
||||
}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/literals.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr VkBufferUsageFlags BUFFER_USAGE =
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
|
||||
constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
|
||||
constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
|
||||
constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
|
||||
/// Find a memory type with the passed requirements
|
||||
std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
|
||||
VkMemoryPropertyFlags wanted,
|
||||
u32 filter = std::numeric_limits<u32>::max()) {
|
||||
for (u32 i = 0; i < properties.memoryTypeCount; ++i) {
|
||||
const auto flags = properties.memoryTypes[i].propertyFlags;
|
||||
if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Get the preferred host visible memory type.
|
||||
u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
|
||||
u32 filter = std::numeric_limits<u32>::max()) {
|
||||
// Prefer device local host visible allocations. Both AMD and Nvidia now provide one.
|
||||
// Otherwise search for a host visible allocation.
|
||||
static constexpr auto HOST_MEMORY =
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY);
|
||||
if (!preferred_type) {
|
||||
preferred_type = FindMemoryType(properties, HOST_MEMORY);
|
||||
ASSERT_MSG(preferred_type, "No host visible and coherent memory type found");
|
||||
}
|
||||
return preferred_type.value_or(0);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_)
|
||||
: device{device_}, scheduler{scheduler_} {
|
||||
CreateBuffers();
|
||||
ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
|
||||
ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
|
||||
}
|
||||
|
||||
VKStreamBuffer::~VKStreamBuffer() = default;
|
||||
|
||||
std::pair<u8*, u64> VKStreamBuffer::Map(u64 size, u64 alignment) {
|
||||
ASSERT(size <= stream_buffer_size);
|
||||
mapped_size = size;
|
||||
|
||||
if (alignment > 0) {
|
||||
offset = Common::AlignUp(offset, alignment);
|
||||
}
|
||||
|
||||
WaitPendingOperations(offset);
|
||||
|
||||
if (offset + size > stream_buffer_size) {
|
||||
// The buffer would overflow, save the amount of used watches and reset the state.
|
||||
invalidation_mark = current_watch_cursor;
|
||||
current_watch_cursor = 0;
|
||||
offset = 0;
|
||||
|
||||
// Swap watches and reset waiting cursors.
|
||||
std::swap(previous_watches, current_watches);
|
||||
wait_cursor = 0;
|
||||
wait_bound = 0;
|
||||
|
||||
// Ensure that we don't wait for uncommitted fences.
|
||||
scheduler.Flush();
|
||||
}
|
||||
|
||||
return std::make_pair(memory.Map(offset, size), offset);
|
||||
}
|
||||
|
||||
void VKStreamBuffer::Unmap(u64 size) {
|
||||
ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
|
||||
|
||||
memory.Unmap();
|
||||
|
||||
offset += size;
|
||||
|
||||
if (current_watch_cursor + 1 >= current_watches.size()) {
|
||||
// Ensure that there are enough watches.
|
||||
ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK);
|
||||
}
|
||||
auto& watch = current_watches[current_watch_cursor++];
|
||||
watch.upper_bound = offset;
|
||||
watch.tick = scheduler.CurrentTick();
|
||||
}
|
||||
|
||||
void VKStreamBuffer::CreateBuffers() {
|
||||
const auto memory_properties = device.GetPhysical().GetMemoryProperties();
|
||||
const u32 preferred_type = GetMemoryType(memory_properties);
|
||||
const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex;
|
||||
|
||||
// Substract from the preferred heap size some bytes to avoid getting out of memory.
|
||||
const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
|
||||
// As per DXVK's example, using `heap_size / 2`
|
||||
const VkDeviceSize allocable_size = heap_size / 2;
|
||||
buffer = device.GetLogical().CreateBuffer({
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size),
|
||||
.usage = BUFFER_USAGE,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
});
|
||||
|
||||
const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer);
|
||||
const u32 required_flags = requirements.memoryTypeBits;
|
||||
stream_buffer_size = static_cast<u64>(requirements.size);
|
||||
|
||||
memory = device.GetLogical().AllocateMemory({
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.allocationSize = requirements.size,
|
||||
.memoryTypeIndex = GetMemoryType(memory_properties, required_flags),
|
||||
});
|
||||
buffer.BindMemory(*memory, 0);
|
||||
}
|
||||
|
||||
void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) {
|
||||
watches.resize(watches.size() + grow_size);
|
||||
}
|
||||
|
||||
void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
|
||||
if (!invalidation_mark) {
|
||||
return;
|
||||
}
|
||||
while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
|
||||
auto& watch = previous_watches[wait_cursor];
|
||||
wait_bound = watch.upper_bound;
|
||||
scheduler.Wait(watch.tick);
|
||||
++wait_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
class VKFenceWatch;
|
||||
class VKScheduler;
|
||||
|
||||
class VKStreamBuffer final {
|
||||
public:
|
||||
explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler);
|
||||
~VKStreamBuffer();
|
||||
|
||||
/**
|
||||
* Reserves a region of memory from the stream buffer.
|
||||
* @param size Size to reserve.
|
||||
* @returns A pair of a raw memory pointer (with offset added), and the buffer offset
|
||||
*/
|
||||
std::pair<u8*, u64> Map(u64 size, u64 alignment);
|
||||
|
||||
/// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
|
||||
void Unmap(u64 size);
|
||||
|
||||
VkBuffer Handle() const noexcept {
|
||||
return *buffer;
|
||||
}
|
||||
|
||||
u64 Address() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Watch {
|
||||
u64 tick{};
|
||||
u64 upper_bound{};
|
||||
};
|
||||
|
||||
/// Creates Vulkan buffer handles committing the required the required memory.
|
||||
void CreateBuffers();
|
||||
|
||||
/// Increases the amount of watches available.
|
||||
void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size);
|
||||
|
||||
void WaitPendingOperations(u64 requested_upper_bound);
|
||||
|
||||
const Device& device; ///< Vulkan device manager.
|
||||
VKScheduler& scheduler; ///< Command scheduler.
|
||||
|
||||
vk::Buffer buffer; ///< Mapped buffer.
|
||||
vk::DeviceMemory memory; ///< Memory allocation.
|
||||
u64 stream_buffer_size{}; ///< Stream buffer size.
|
||||
|
||||
u64 offset{}; ///< Buffer iterator.
|
||||
u64 mapped_size{}; ///< Size reserved for the current copy.
|
||||
|
||||
std::vector<Watch> current_watches; ///< Watches recorded in the current iteration.
|
||||
std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation.
|
||||
std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle.
|
||||
|
||||
std::vector<Watch> previous_watches; ///< Watches used in the previous iteration.
|
||||
std::size_t wait_cursor{}; ///< Last watch being waited for completion.
|
||||
u64 wait_bound{}; ///< Highest offset being watched for completion.
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -24,10 +24,10 @@ struct RenderTargets {
|
||||
return std::ranges::any_of(color_buffer_ids, contains) || contains(depth_buffer_id);
|
||||
}
|
||||
|
||||
std::array<ImageViewId, NUM_RT> color_buffer_ids;
|
||||
ImageViewId depth_buffer_id;
|
||||
std::array<ImageViewId, NUM_RT> color_buffer_ids{};
|
||||
ImageViewId depth_buffer_id{};
|
||||
std::array<u8, NUM_RT> draw_buffers{};
|
||||
Extent2D size;
|
||||
Extent2D size{};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -151,6 +151,76 @@ private:
|
||||
const IntType& m_Bits;
|
||||
};
|
||||
|
||||
enum class IntegerEncoding { JustBits, Quint, Trit };
|
||||
|
||||
struct IntegerEncodedValue {
|
||||
constexpr IntegerEncodedValue() = default;
|
||||
|
||||
constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
|
||||
: encoding{encoding_}, num_bits{num_bits_} {}
|
||||
|
||||
constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
|
||||
return encoding == other.encoding && num_bits == other.num_bits;
|
||||
}
|
||||
|
||||
// Returns the number of bits required to encode num_vals values.
|
||||
u32 GetBitLength(u32 num_vals) const {
|
||||
u32 total_bits = num_bits * num_vals;
|
||||
if (encoding == IntegerEncoding::Trit) {
|
||||
total_bits += (num_vals * 8 + 4) / 5;
|
||||
} else if (encoding == IntegerEncoding::Quint) {
|
||||
total_bits += (num_vals * 7 + 2) / 3;
|
||||
}
|
||||
return total_bits;
|
||||
}
|
||||
|
||||
IntegerEncoding encoding{};
|
||||
u32 num_bits = 0;
|
||||
u32 bit_value = 0;
|
||||
union {
|
||||
u32 quint_value = 0;
|
||||
u32 trit_value;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a new instance of this struct that corresponds to the
|
||||
// can take no more than mav_value values
|
||||
static constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
|
||||
while (mav_value > 0) {
|
||||
u32 check = mav_value + 1;
|
||||
|
||||
// Is mav_value a power of two?
|
||||
if (!(check & (check - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
|
||||
}
|
||||
|
||||
// Is mav_value of the type 3*2^n - 1?
|
||||
if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
|
||||
}
|
||||
|
||||
// Is mav_value of the type 5*2^n - 1?
|
||||
if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
|
||||
}
|
||||
|
||||
// Apparently it can't be represented with a bounded integer sequence...
|
||||
// just iterate.
|
||||
mav_value--;
|
||||
}
|
||||
return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
|
||||
}
|
||||
|
||||
static constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
|
||||
std::array<IntegerEncodedValue, 256> encodings{};
|
||||
for (std::size_t i = 0; i < encodings.size(); ++i) {
|
||||
encodings[i] = CreateEncoding(static_cast<u32>(i));
|
||||
}
|
||||
return encodings;
|
||||
}
|
||||
|
||||
static constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
|
||||
|
||||
namespace Tegra::Texture::ASTC {
|
||||
using IntegerEncodedVector = boost::container::static_vector<
|
||||
IntegerEncodedValue, 256,
|
||||
@@ -521,35 +591,41 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
|
||||
return params;
|
||||
}
|
||||
|
||||
static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
|
||||
u32 blockHeight) {
|
||||
// Don't actually care about the void extent, just read the bits...
|
||||
for (s32 i = 0; i < 4; ++i) {
|
||||
strm.ReadBits<13>();
|
||||
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
|
||||
// is the same as [(num_bits - 1):0] and repeats all the way down.
|
||||
template <typename IntType>
|
||||
static constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
|
||||
if (num_bits == 0 || to_bit == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decode the RGBA components and renormalize them to the range [0, 255]
|
||||
u16 r = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 g = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 b = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 a = static_cast<u16>(strm.ReadBits<16>());
|
||||
|
||||
u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
|
||||
(static_cast<u32>(a) & 0xFF00) << 16;
|
||||
|
||||
for (u32 j = 0; j < blockHeight; j++) {
|
||||
for (u32 i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = rgba;
|
||||
const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
|
||||
IntType res = v;
|
||||
u32 reslen = num_bits;
|
||||
while (reslen < to_bit) {
|
||||
u32 comp = 0;
|
||||
if (num_bits > to_bit - reslen) {
|
||||
u32 newshift = to_bit - reslen;
|
||||
comp = num_bits - newshift;
|
||||
num_bits = newshift;
|
||||
}
|
||||
res = static_cast<IntType>(res << num_bits);
|
||||
res = static_cast<IntType>(res | (v >> comp));
|
||||
reslen += num_bits;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
|
||||
for (u32 j = 0; j < blockHeight; j++) {
|
||||
for (u32 i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = 0xFFFF00FF;
|
||||
}
|
||||
static constexpr std::size_t NumReplicateEntries(u32 num_bits) {
|
||||
return std::size_t(1) << num_bits;
|
||||
}
|
||||
|
||||
template <typename IntType, u32 num_bits, u32 to_bit>
|
||||
static constexpr auto MakeReplicateTable() {
|
||||
std::array<IntType, NumReplicateEntries(num_bits)> table{};
|
||||
for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
|
||||
table[value] = Replicate(value, num_bits, to_bit);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
|
||||
@@ -572,6 +648,9 @@ static constexpr auto REPLICATE_2_BIT_TO_8_TABLE = MakeReplicateTable<u32, 2, 8>
|
||||
static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>();
|
||||
static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>();
|
||||
static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>();
|
||||
static constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
|
||||
static constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
|
||||
static constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
|
||||
/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback
|
||||
/// to the runtime implementation
|
||||
static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) {
|
||||
@@ -1316,6 +1395,37 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
|
||||
#undef READ_INT_VALUES
|
||||
}
|
||||
|
||||
static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
|
||||
u32 blockHeight) {
|
||||
// Don't actually care about the void extent, just read the bits...
|
||||
for (s32 i = 0; i < 4; ++i) {
|
||||
strm.ReadBits<13>();
|
||||
}
|
||||
|
||||
// Decode the RGBA components and renormalize them to the range [0, 255]
|
||||
u16 r = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 g = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 b = static_cast<u16>(strm.ReadBits<16>());
|
||||
u16 a = static_cast<u16>(strm.ReadBits<16>());
|
||||
|
||||
u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
|
||||
(static_cast<u32>(a) & 0xFF00) << 16;
|
||||
|
||||
for (u32 j = 0; j < blockHeight; j++) {
|
||||
for (u32 i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = rgba;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
|
||||
for (u32 j = 0; j < blockHeight; j++) {
|
||||
for (u32 i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = 0xFFFF00FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
|
||||
const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
|
||||
InputBitStream strm(inBuf);
|
||||
|
||||
@@ -9,117 +9,6 @@
|
||||
|
||||
namespace Tegra::Texture::ASTC {
|
||||
|
||||
enum class IntegerEncoding { JustBits, Quint, Trit };
|
||||
|
||||
struct IntegerEncodedValue {
|
||||
constexpr IntegerEncodedValue() = default;
|
||||
|
||||
constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
|
||||
: encoding{encoding_}, num_bits{num_bits_} {}
|
||||
|
||||
constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
|
||||
return encoding == other.encoding && num_bits == other.num_bits;
|
||||
}
|
||||
|
||||
// Returns the number of bits required to encode num_vals values.
|
||||
u32 GetBitLength(u32 num_vals) const {
|
||||
u32 total_bits = num_bits * num_vals;
|
||||
if (encoding == IntegerEncoding::Trit) {
|
||||
total_bits += (num_vals * 8 + 4) / 5;
|
||||
} else if (encoding == IntegerEncoding::Quint) {
|
||||
total_bits += (num_vals * 7 + 2) / 3;
|
||||
}
|
||||
return total_bits;
|
||||
}
|
||||
|
||||
IntegerEncoding encoding{};
|
||||
u32 num_bits = 0;
|
||||
u32 bit_value = 0;
|
||||
union {
|
||||
u32 quint_value = 0;
|
||||
u32 trit_value;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a new instance of this struct that corresponds to the
|
||||
// can take no more than mav_value values
|
||||
constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
|
||||
while (mav_value > 0) {
|
||||
u32 check = mav_value + 1;
|
||||
|
||||
// Is mav_value a power of two?
|
||||
if (!(check & (check - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
|
||||
}
|
||||
|
||||
// Is mav_value of the type 3*2^n - 1?
|
||||
if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
|
||||
}
|
||||
|
||||
// Is mav_value of the type 5*2^n - 1?
|
||||
if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
|
||||
return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
|
||||
}
|
||||
|
||||
// Apparently it can't be represented with a bounded integer sequence...
|
||||
// just iterate.
|
||||
mav_value--;
|
||||
}
|
||||
return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
|
||||
}
|
||||
|
||||
constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
|
||||
std::array<IntegerEncodedValue, 256> encodings{};
|
||||
for (std::size_t i = 0; i < encodings.size(); ++i) {
|
||||
encodings[i] = CreateEncoding(static_cast<u32>(i));
|
||||
}
|
||||
return encodings;
|
||||
}
|
||||
|
||||
constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
|
||||
|
||||
// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
|
||||
// is the same as [(num_bits - 1):0] and repeats all the way down.
|
||||
template <typename IntType>
|
||||
constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
|
||||
if (num_bits == 0 || to_bit == 0) {
|
||||
return 0;
|
||||
}
|
||||
const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
|
||||
IntType res = v;
|
||||
u32 reslen = num_bits;
|
||||
while (reslen < to_bit) {
|
||||
u32 comp = 0;
|
||||
if (num_bits > to_bit - reslen) {
|
||||
u32 newshift = to_bit - reslen;
|
||||
comp = num_bits - newshift;
|
||||
num_bits = newshift;
|
||||
}
|
||||
res = static_cast<IntType>(res << num_bits);
|
||||
res = static_cast<IntType>(res | (v >> comp));
|
||||
reslen += num_bits;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr std::size_t NumReplicateEntries(u32 num_bits) {
|
||||
return std::size_t(1) << num_bits;
|
||||
}
|
||||
|
||||
template <typename IntType, u32 num_bits, u32 to_bit>
|
||||
constexpr auto MakeReplicateTable() {
|
||||
std::array<IntType, NumReplicateEntries(num_bits)> table{};
|
||||
for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
|
||||
table[value] = Replicate(value, num_bits, to_bit);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
|
||||
constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
|
||||
constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
|
||||
|
||||
void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t block_width, uint32_t block_height, std::span<uint8_t> output);
|
||||
|
||||
|
||||
@@ -526,6 +526,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
SetNext(next, workgroup_layout);
|
||||
}
|
||||
|
||||
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
|
||||
if (khr_pipeline_executable_properties) {
|
||||
LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
|
||||
executable_properties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.pipelineExecutableInfo = VK_TRUE,
|
||||
};
|
||||
SetNext(next, executable_properties);
|
||||
}
|
||||
|
||||
if (!ext_depth_range_unrestricted) {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
|
||||
}
|
||||
@@ -824,6 +835,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
|
||||
bool has_khr_shader_float16_int8{};
|
||||
bool has_khr_workgroup_memory_explicit_layout{};
|
||||
bool has_khr_pipeline_executable_properties{};
|
||||
bool has_ext_subgroup_size_control{};
|
||||
bool has_ext_transform_feedback{};
|
||||
bool has_ext_custom_border_color{};
|
||||
@@ -878,6 +890,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
|
||||
true);
|
||||
}
|
||||
if (Settings::values.renderer_shader_feedback) {
|
||||
test(has_khr_pipeline_executable_properties,
|
||||
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
|
||||
}
|
||||
}
|
||||
VkPhysicalDeviceFeatures2KHR features{};
|
||||
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
|
||||
@@ -1033,6 +1049,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
khr_workgroup_memory_explicit_layout = true;
|
||||
}
|
||||
}
|
||||
if (has_khr_pipeline_executable_properties) {
|
||||
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
|
||||
executable_properties.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
|
||||
executable_properties.pNext = nullptr;
|
||||
features.pNext = &executable_properties;
|
||||
physical.GetFeatures2KHR(features);
|
||||
|
||||
if (executable_properties.pipelineExecutableInfo) {
|
||||
extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||
khr_pipeline_executable_properties = true;
|
||||
}
|
||||
}
|
||||
if (khr_push_descriptor) {
|
||||
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
|
||||
push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
|
||||
|
||||
@@ -214,6 +214,11 @@ public:
|
||||
return khr_push_descriptor;
|
||||
}
|
||||
|
||||
/// Returns true if VK_KHR_pipeline_executable_properties is enabled.
|
||||
bool IsKhrPipelineEexecutablePropertiesEnabled() const {
|
||||
return khr_pipeline_executable_properties;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
|
||||
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
|
||||
return khr_workgroup_memory_explicit_layout;
|
||||
@@ -378,6 +383,7 @@ private:
|
||||
bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
|
||||
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
|
||||
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
|
||||
bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
|
||||
@@ -181,6 +181,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkGetMemoryWin32HandleKHR);
|
||||
#endif
|
||||
X(vkGetQueryPoolResults);
|
||||
X(vkGetPipelineExecutablePropertiesKHR);
|
||||
X(vkGetPipelineExecutableStatisticsKHR);
|
||||
X(vkGetSemaphoreCounterValueKHR);
|
||||
X(vkMapMemory);
|
||||
X(vkQueueSubmit);
|
||||
@@ -202,7 +204,7 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
|
||||
const VkDebugUtilsObjectNameInfoEXT name_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.objectType = VK_OBJECT_TYPE_IMAGE,
|
||||
.objectType = type,
|
||||
.objectHandle = reinterpret_cast<u64>(handle),
|
||||
.pObjectName = name,
|
||||
};
|
||||
@@ -809,6 +811,42 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe
|
||||
return requirements;
|
||||
}
|
||||
|
||||
std::vector<VkPipelineExecutablePropertiesKHR> Device::GetPipelineExecutablePropertiesKHR(
|
||||
VkPipeline pipeline) const {
|
||||
const VkPipelineInfoKHR info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.pipeline = pipeline,
|
||||
};
|
||||
u32 num{};
|
||||
dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr);
|
||||
std::vector<VkPipelineExecutablePropertiesKHR> properties(num);
|
||||
for (auto& property : properties) {
|
||||
property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR;
|
||||
}
|
||||
Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data()));
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::vector<VkPipelineExecutableStatisticKHR> Device::GetPipelineExecutableStatisticsKHR(
|
||||
VkPipeline pipeline, u32 executable_index) const {
|
||||
const VkPipelineExecutableInfoKHR executable_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.pipeline = pipeline,
|
||||
.executableIndex = executable_index,
|
||||
};
|
||||
u32 num{};
|
||||
dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr);
|
||||
std::vector<VkPipelineExecutableStatisticKHR> statistics(num);
|
||||
for (auto& statistic : statistics) {
|
||||
statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR;
|
||||
}
|
||||
Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num,
|
||||
statistics.data()));
|
||||
return statistics;
|
||||
}
|
||||
|
||||
void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
|
||||
Span<VkCopyDescriptorSet> copies) const noexcept {
|
||||
dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data());
|
||||
|
||||
@@ -295,6 +295,8 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
#ifdef _WIN32
|
||||
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
||||
#endif
|
||||
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
||||
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
|
||||
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
|
||||
PFN_vkMapMemory vkMapMemory{};
|
||||
@@ -879,6 +881,12 @@ public:
|
||||
|
||||
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
|
||||
|
||||
std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
|
||||
VkPipeline pipeline) const;
|
||||
|
||||
std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR(
|
||||
VkPipeline pipeline, u32 executable_index) const;
|
||||
|
||||
void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
|
||||
Span<VkCopyDescriptorSet> copies) const noexcept;
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url,
|
||||
is_local = true;
|
||||
|
||||
LoadExtractedFonts();
|
||||
FocusFirstLinkElement();
|
||||
SetUserAgent(UserAgent::WebApplet);
|
||||
SetFinished(false);
|
||||
SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
|
||||
@@ -121,6 +122,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url,
|
||||
const std::string& additional_args) {
|
||||
is_local = false;
|
||||
|
||||
FocusFirstLinkElement();
|
||||
SetUserAgent(UserAgent::WebApplet);
|
||||
SetFinished(false);
|
||||
SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
|
||||
@@ -208,7 +210,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
|
||||
if (input_interpreter->IsButtonPressedOnce(button)) {
|
||||
page()->runJavaScript(
|
||||
QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
|
||||
[&](const QVariant& variant) {
|
||||
[this, button](const QVariant& variant) {
|
||||
if (variant.toBool()) {
|
||||
switch (button) {
|
||||
case HIDButton::A:
|
||||
@@ -364,6 +366,17 @@ void QtNXWebEngineView::LoadExtractedFonts() {
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::FocusFirstLinkElement() {
|
||||
QWebEngineScript focus_link_element;
|
||||
|
||||
focus_link_element.setName(QStringLiteral("focus_link_element.js"));
|
||||
focus_link_element.setSourceCode(QString::fromStdString(FOCUS_LINK_ELEMENT_SCRIPT));
|
||||
focus_link_element.setWorldId(QWebEngineScript::MainWorld);
|
||||
focus_link_element.setInjectionPoint(QWebEngineScript::Deferred);
|
||||
focus_link_element.setRunsOnSubFrames(true);
|
||||
default_profile->scripts()->insert(focus_link_element);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
|
||||
|
||||
@@ -161,6 +161,9 @@ private:
|
||||
/// Loads the extracted fonts using JavaScript.
|
||||
void LoadExtractedFonts();
|
||||
|
||||
/// Brings focus to the first available link element.
|
||||
void FocusFirstLinkElement();
|
||||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
std::unique_ptr<UrlRequestInterceptor> url_interceptor;
|
||||
|
||||
@@ -73,6 +73,12 @@ constexpr char LOAD_NX_FONT[] = R"(
|
||||
})();
|
||||
)";
|
||||
|
||||
constexpr char FOCUS_LINK_ELEMENT_SCRIPT[] = R"(
|
||||
if (document.getElementsByTagName("a").length > 0) {
|
||||
document.getElementsByTagName("a")[0].focus();
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr char GAMEPAD_SCRIPT[] = R"(
|
||||
window.addEventListener("gamepadconnected", function(e) {
|
||||
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
|
||||
|
||||
@@ -806,8 +806,8 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||
ReadGlobalSetting(Settings::values.aspect_ratio);
|
||||
ReadGlobalSetting(Settings::values.max_anisotropy);
|
||||
ReadGlobalSetting(Settings::values.use_frame_limit);
|
||||
ReadGlobalSetting(Settings::values.frame_limit);
|
||||
ReadGlobalSetting(Settings::values.use_speed_limit);
|
||||
ReadGlobalSetting(Settings::values.speed_limit);
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
||||
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||
@@ -825,6 +825,7 @@ void Config::ReadRendererValues() {
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.fps_cap);
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadBasicSetting(Settings::values.renderer_shader_feedback);
|
||||
ReadBasicSetting(Settings::values.enable_nsight_aftermath);
|
||||
ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
|
||||
}
|
||||
@@ -1332,11 +1333,14 @@ void Config::SaveRendererValues() {
|
||||
static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
|
||||
Settings::values.renderer_backend.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.vulkan_device);
|
||||
WriteGlobalSetting(Settings::values.fullscreen_mode);
|
||||
WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
|
||||
static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.fullscreen_mode.GetDefault()),
|
||||
Settings::values.fullscreen_mode.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.aspect_ratio);
|
||||
WriteGlobalSetting(Settings::values.max_anisotropy);
|
||||
WriteGlobalSetting(Settings::values.use_frame_limit);
|
||||
WriteGlobalSetting(Settings::values.frame_limit);
|
||||
WriteGlobalSetting(Settings::values.use_speed_limit);
|
||||
WriteGlobalSetting(Settings::values.speed_limit);
|
||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
|
||||
static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
|
||||
@@ -1360,6 +1364,7 @@ void Config::SaveRendererValues() {
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.fps_cap);
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
WriteBasicSetting(Settings::values.renderer_shader_feedback);
|
||||
WriteBasicSetting(Settings::values.enable_nsight_aftermath);
|
||||
WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
|
||||
}
|
||||
|
||||
@@ -181,5 +181,6 @@ private:
|
||||
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
|
||||
Q_DECLARE_METATYPE(Settings::CPUAccuracy);
|
||||
Q_DECLARE_METATYPE(Settings::GPUAccuracy);
|
||||
Q_DECLARE_METATYPE(Settings::FullscreenMode);
|
||||
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||
Q_DECLARE_METATYPE(Settings::ShaderBackend);
|
||||
|
||||
@@ -25,20 +25,6 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
|
||||
const QComboBox* combobox) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
setting->SetValue(combobox->currentIndex());
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
||||
const Settings::Setting<bool>* setting) {
|
||||
if (setting->UsingGlobal()) {
|
||||
|
||||
@@ -28,7 +28,20 @@ enum class CheckState {
|
||||
// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
|
||||
void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox,
|
||||
const CheckState& tracker);
|
||||
void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
|
||||
template <typename Type>
|
||||
void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
setting->SetValue(static_cast<Type>(combobox->currentIndex()));
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(static_cast<Type>(combobox->currentIndex() -
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a Qt UI element given a Settings::Setting
|
||||
void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
|
||||
|
||||
@@ -65,6 +65,7 @@ void ConfigureCpu::UpdateGroup(int index) {
|
||||
}
|
||||
|
||||
void ConfigureCpu::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma,
|
||||
ui->cpuopt_unsafe_unfuse_fma,
|
||||
cpuopt_unsafe_unfuse_fma);
|
||||
@@ -80,22 +81,6 @@ void ConfigureCpu::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
|
||||
ui->cpuopt_unsafe_fastmem_check,
|
||||
cpuopt_unsafe_fastmem_check);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.cpu_accuracy.UsingGlobal()) {
|
||||
Settings::values.cpu_accuracy.SetValue(
|
||||
static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()));
|
||||
}
|
||||
} else {
|
||||
if (ui->accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.cpu_accuracy.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.cpu_accuracy.SetGlobal(false);
|
||||
Settings::values.cpu_accuracy.SetValue(static_cast<Settings::CPUAccuracy>(
|
||||
ui->accuracy->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureCpu::changeEvent(QEvent* event) {
|
||||
|
||||
@@ -43,6 +43,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
|
||||
ui->enable_graphics_debugging->setEnabled(runtime_lock);
|
||||
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
|
||||
ui->enable_shader_feedback->setEnabled(runtime_lock);
|
||||
ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
|
||||
ui->enable_cpu_debugging->setEnabled(runtime_lock);
|
||||
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
|
||||
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
|
||||
@@ -65,6 +67,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
|
||||
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
|
||||
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
|
||||
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
|
||||
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
|
||||
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
|
||||
Settings::values.disable_shader_loop_safety_checks =
|
||||
|
||||
@@ -111,8 +111,8 @@
|
||||
<property name="title">
|
||||
<string>Graphics</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="enable_graphics_debugging">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
@@ -125,7 +125,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="enable_nsight_aftermath">
|
||||
<property name="toolTip">
|
||||
<string>When checked, it enables Nsight Aftermath crash dumps</string>
|
||||
@@ -135,7 +135,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="disable_macro_jit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
@@ -148,7 +148,17 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="enable_shader_feedback">
|
||||
<property name="toolTip">
|
||||
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Shader Feedback</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="disable_loop_safety_checks">
|
||||
<property name="toolTip">
|
||||
<string>When checked, it executes shaders without loop logic changes</string>
|
||||
@@ -276,11 +286,14 @@
|
||||
<tabstop>open_log_button</tabstop>
|
||||
<tabstop>homebrew_args_edit</tabstop>
|
||||
<tabstop>enable_graphics_debugging</tabstop>
|
||||
<tabstop>enable_shader_feedback</tabstop>
|
||||
<tabstop>enable_nsight_aftermath</tabstop>
|
||||
<tabstop>disable_macro_jit</tabstop>
|
||||
<tabstop>disable_loop_safety_checks</tabstop>
|
||||
<tabstop>fs_access_log</tabstop>
|
||||
<tabstop>reporting_services</tabstop>
|
||||
<tabstop>quest_flag</tabstop>
|
||||
<tabstop>enable_cpu_debugging</tabstop>
|
||||
<tabstop>use_debug_asserts</tabstop>
|
||||
<tabstop>use_auto_stub</tabstop>
|
||||
</tabstops>
|
||||
|
||||
@@ -24,8 +24,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
SetConfiguration();
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
|
||||
[this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
|
||||
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit,
|
||||
[this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); });
|
||||
}
|
||||
|
||||
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
||||
@@ -45,18 +45,18 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
||||
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
|
||||
ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue());
|
||||
ui->speed_limit->setValue(Settings::values.speed_limit.GetValue());
|
||||
|
||||
ui->fps_cap->setValue(Settings::values.fps_cap.GetValue());
|
||||
|
||||
ui->button_reset_defaults->setEnabled(runtime_lock);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
|
||||
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue());
|
||||
} else {
|
||||
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
|
||||
use_frame_limit != ConfigurationShared::CheckState::Global);
|
||||
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
|
||||
use_speed_limit != ConfigurationShared::CheckState::Global);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,19 +92,19 @@ void ConfigureGeneral::ApplyConfiguration() {
|
||||
Settings::values.fps_cap.SetValue(ui->fps_cap->value());
|
||||
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.use_frame_limit.UsingGlobal()) {
|
||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
||||
if (Settings::values.use_speed_limit.UsingGlobal()) {
|
||||
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
|
||||
Qt::Checked);
|
||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
||||
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
|
||||
}
|
||||
} else {
|
||||
bool global_frame_limit = use_frame_limit == ConfigurationShared::CheckState::Global;
|
||||
Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
|
||||
Settings::values.frame_limit.SetGlobal(global_frame_limit);
|
||||
if (!global_frame_limit) {
|
||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
||||
bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global;
|
||||
Settings::values.use_speed_limit.SetGlobal(global_speed_limit);
|
||||
Settings::values.speed_limit.SetGlobal(global_speed_limit);
|
||||
if (!global_speed_limit) {
|
||||
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
|
||||
Qt::Checked);
|
||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
||||
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,8 +126,8 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||
// Disables each setting if:
|
||||
// - A game is running (thus settings in use), and
|
||||
// - A non-global setting is applied.
|
||||
ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
|
||||
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
|
||||
ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal());
|
||||
ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -139,13 +139,13 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||
|
||||
ui->button_reset_defaults->setVisible(false);
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
|
||||
Settings::values.use_frame_limit, use_frame_limit);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit,
|
||||
Settings::values.use_speed_limit, use_speed_limit);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
|
||||
use_multi_core);
|
||||
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
|
||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
|
||||
(use_frame_limit != ConfigurationShared::CheckState::Global));
|
||||
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
|
||||
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
|
||||
(use_speed_limit != ConfigurationShared::CheckState::Global));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,6 +43,6 @@ private:
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
|
||||
ConfigurationShared::CheckState use_frame_limit;
|
||||
ConfigurationShared::CheckState use_speed_limit;
|
||||
ConfigurationShared::CheckState use_multi_core;
|
||||
};
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
||||
<widget class="QCheckBox" name="toggle_speed_limit">
|
||||
<property name="text">
|
||||
<string>Limit Speed Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frame_limit">
|
||||
<widget class="QSpinBox" name="speed_limit">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
|
||||
@@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
}
|
||||
|
||||
ui->backend->addItem(QStringLiteral("GLSL"));
|
||||
ui->backend->addItem(tr("GLASM (NVIDIA Only)"));
|
||||
ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)"));
|
||||
ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)"));
|
||||
|
||||
SetupPerGameUI();
|
||||
@@ -98,7 +98,8 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
|
||||
ui->fullscreen_mode_combobox->setCurrentIndex(Settings::values.fullscreen_mode.GetValue());
|
||||
ui->fullscreen_mode_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
|
||||
@@ -310,8 +311,9 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
|
||||
ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
|
||||
Settings::values.aspect_ratio.GetValue(true));
|
||||
ConfigurationShared::SetColoredComboBox(ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
|
||||
Settings::values.fullscreen_mode.GetValue(true));
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
|
||||
static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||
}
|
||||
|
||||
@@ -48,11 +48,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
// Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
|
||||
const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
|
||||
ui->gpu_accuracy->currentIndex() -
|
||||
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
|
||||
ui->anisotropic_filtering_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
|
||||
@@ -63,20 +59,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
use_caches_gc);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
|
||||
ui->use_fast_gpu_time, use_fast_gpu_time);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
// Must guard in case of a during-game configuration when set to be game-specific.
|
||||
if (Settings::values.gpu_accuracy.UsingGlobal()) {
|
||||
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
|
||||
}
|
||||
} else {
|
||||
if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.gpu_accuracy.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.gpu_accuracy.SetGlobal(false);
|
||||
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||
|
||||
@@ -401,6 +401,11 @@
|
||||
<string>Traditional Chinese (正體中文)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Brazilian Portuguese (português do Brasil)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
|
||||
@@ -972,23 +972,23 @@ void GMainWindow::InitializeHotkeys() {
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
Settings::values.use_frame_limit.SetValue(
|
||||
!Settings::values.use_frame_limit.GetValue());
|
||||
Settings::values.use_speed_limit.SetValue(
|
||||
!Settings::values.use_speed_limit.GetValue());
|
||||
UpdateStatusBar();
|
||||
});
|
||||
constexpr u16 SPEED_LIMIT_STEP = 5;
|
||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
|
||||
Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP +
|
||||
Settings::values.frame_limit.GetValue());
|
||||
if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
|
||||
Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP +
|
||||
Settings::values.speed_limit.GetValue());
|
||||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
|
||||
&QShortcut::activated, this, [&] {
|
||||
if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
|
||||
Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
|
||||
if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) {
|
||||
Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() -
|
||||
SPEED_LIMIT_STEP);
|
||||
UpdateStatusBar();
|
||||
}
|
||||
@@ -2513,7 +2513,7 @@ void GMainWindow::ShowFullscreen() {
|
||||
ui.menubar->hide();
|
||||
statusBar()->hide();
|
||||
|
||||
if (Settings::values.fullscreen_mode.GetValue() == 1) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
showFullScreen();
|
||||
return;
|
||||
}
|
||||
@@ -2528,7 +2528,7 @@ void GMainWindow::ShowFullscreen() {
|
||||
} else {
|
||||
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
|
||||
|
||||
if (Settings::values.fullscreen_mode.GetValue() == 1) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
render_window->showFullScreen();
|
||||
return;
|
||||
}
|
||||
@@ -2545,7 +2545,7 @@ void GMainWindow::ShowFullscreen() {
|
||||
|
||||
void GMainWindow::HideFullscreen() {
|
||||
if (ui.action_Single_Window_Mode->isChecked()) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == 1) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
showNormal();
|
||||
restoreGeometry(UISettings::values.geometry);
|
||||
} else {
|
||||
@@ -2559,7 +2559,7 @@ void GMainWindow::HideFullscreen() {
|
||||
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
|
||||
ui.menubar->show();
|
||||
} else {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == 1) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
render_window->showNormal();
|
||||
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
|
||||
} else {
|
||||
@@ -2910,10 +2910,10 @@ void GMainWindow::UpdateStatusBar() {
|
||||
shader_building_label->setVisible(false);
|
||||
}
|
||||
|
||||
if (Settings::values.use_frame_limit.GetValue()) {
|
||||
if (Settings::values.use_speed_limit.GetValue()) {
|
||||
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
||||
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
||||
.arg(Settings::values.frame_limit.GetValue()));
|
||||
.arg(Settings::values.speed_limit.GetValue()));
|
||||
} else {
|
||||
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
||||
}
|
||||
|
||||
@@ -444,6 +444,7 @@ void Config::ReadValues() {
|
||||
// Renderer
|
||||
ReadSetting("Renderer", Settings::values.renderer_backend);
|
||||
ReadSetting("Renderer", Settings::values.renderer_debug);
|
||||
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
|
||||
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
|
||||
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
|
||||
ReadSetting("Renderer", Settings::values.vulkan_device);
|
||||
@@ -451,8 +452,8 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.fullscreen_mode);
|
||||
ReadSetting("Renderer", Settings::values.aspect_ratio);
|
||||
ReadSetting("Renderer", Settings::values.max_anisotropy);
|
||||
ReadSetting("Renderer", Settings::values.use_frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_speed_limit);
|
||||
ReadSetting("Renderer", Settings::values.speed_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.gpu_accuracy);
|
||||
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
|
||||
|
||||
@@ -221,6 +221,10 @@ backend =
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
debug =
|
||||
|
||||
# Enable shader feedback.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
renderer_shader_feedback =
|
||||
|
||||
# Enable Nsight Aftermath crash dumps
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
nsight_aftermath =
|
||||
@@ -265,13 +269,13 @@ use_nvdec_emulation =
|
||||
# 0: Off, 1 (default): On
|
||||
accelerate_astc =
|
||||
|
||||
# Turns on the frame limiter, which will limit frames output to the target game speed
|
||||
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
|
||||
# 0: Off, 1: On (default)
|
||||
use_frame_limit =
|
||||
use_speed_limit =
|
||||
|
||||
# Limits the speed of the game to run no faster than this value as a percentage of target speed
|
||||
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
|
||||
frame_limit =
|
||||
speed_limit =
|
||||
|
||||
# Whether to use disk based shader cache
|
||||
# 0: Off, 1 (default): On
|
||||
@@ -363,7 +367,7 @@ custom_rtc =
|
||||
# Sets the systems language index
|
||||
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
|
||||
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
|
||||
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
|
||||
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
|
||||
language_index =
|
||||
|
||||
# The system region that yuzu will use during emulation
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/yuzu_icon.h"
|
||||
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
|
||||
: input_subsystem{input_subsystem_} {
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
|
||||
: input_subsystem{input_subsystem_}, system{system_} {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
|
||||
exit(1);
|
||||
@@ -124,7 +124,7 @@ void EmuWindow_SDL2::OnResize() {
|
||||
|
||||
void EmuWindow_SDL2::Fullscreen() {
|
||||
switch (Settings::values.fullscreen_mode.GetValue()) {
|
||||
case 1: // Exclusive fullscreen
|
||||
case Settings::FullscreenMode::Exclusive:
|
||||
// Set window size to render size before entering fullscreen -- SDL does not resize to
|
||||
// display dimensions in this mode.
|
||||
// TODO: Multiply the window size by resolution_factor (for both docked modes)
|
||||
@@ -140,7 +140,7 @@ void EmuWindow_SDL2::Fullscreen() {
|
||||
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
|
||||
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
|
||||
[[fallthrough]];
|
||||
case 0: // Borderless window
|
||||
case Settings::FullscreenMode::Borderless:
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -218,7 +218,7 @@ void EmuWindow_SDL2::WaitEvent() {
|
||||
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||
const auto results = system.GetAndResetPerfStats();
|
||||
const auto title =
|
||||
fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps,
|
||||
|
||||
@@ -24,7 +24,7 @@ enum class MouseButton;
|
||||
|
||||
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
@@ -87,4 +87,7 @@ protected:
|
||||
|
||||
/// Input subsystem to use with this window.
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
/// yuzu core instance
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -76,8 +76,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
return unsupported_ext.empty();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
|
||||
: EmuWindow_SDL2{input_subsystem} {
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
|
||||
Core::System& system_, bool fullscreen)
|
||||
: EmuWindow_SDL2{input_subsystem, system_} {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
|
||||
@@ -8,13 +8,18 @@
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
|
||||
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
|
||||
bool fullscreen);
|
||||
~EmuWindow_SDL2_GL();
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
|
||||
: EmuWindow_SDL2{input_subsystem} {
|
||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
|
||||
Core::System& system_, bool fullscreen)
|
||||
: EmuWindow_SDL2{input_subsystem, system_} {
|
||||
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
|
||||
@@ -19,7 +19,8 @@ class InputSubsystem;
|
||||
|
||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
|
||||
explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
|
||||
bool fullscreen);
|
||||
~EmuWindow_SDL2_VK() override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
@@ -172,10 +172,10 @@ int main(int argc, char** argv) {
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window;
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user