Compare commits

...

46 Commits

Author SHA1 Message Date
ReinUsesLisp 51512d01d8 renderer_opengl: Avoid precompiled cache and force NV GL cache directory
Setting __GL_SHADER_DISK_CACHE_PATH we can force the cache directory to
be in yuzu's user directory to stop commonly distributed malware from
deleting our driver shader cache. And by setting
__GL_SHADER_DISK_CACHE_SKIP_CLEANUP we can have an unbounded shader
cache size.

This has only been implemented on Windows, mostly because previous tests
didn't seem to work on Linux.

Disable the precompiled cache on Nvidia's driver. There's no need to
hide information the driver already has in its own cache.
2021-01-21 00:41:03 -03:00
bunnei 4cd8b2f1f7 Merge pull request #5755 from FearlessTobi/port-5344
Port citra-emu/citra#5344: "game_list: Fix folder reordering"
2021-01-19 10:53:18 -08:00
Rodrigo Locatti 2ef4591e58 Merge pull request #5746 from lioncash/sign-compare
texture_cache/util: Resolve -Wsign-compare warning
2021-01-18 03:49:58 -03:00
LC f1b58f0cd9 Merge pull request #5754 from lat9nq/fix-disable-boxcat
configure_service: Only compile FormatEventStatusString when YUZU_ENABLE_BOXCAT is enabled
2021-01-17 23:52:47 -05:00
LC dd0679d710 Merge pull request #5757 from Morph1984/npad-handheld
npad: Add check for HANDHELD_INDEX in UpdateControllerAt()
2021-01-17 23:51:30 -05:00
Morph 4a67a5b917 npad: Add check for HANDHELD_INDEX in UpdateControllerAt() 2021-01-17 22:36:17 -05:00
FearlessTobi bf9f737c60 game_list: Fix folder reordering
The bug(s) happened because we swapped the contents on values.game_dirs, but the pointer each item had to their respective game_dir wasn't updated. This made it so that the item had the wrong game_dir associated with it after a "move up" or "move down" operation. It can be observed by choosing "open directory location" after such operation.

Changed from raw pointer to an index because it's equivalent but a bit clearer, but the change is not essential.

Co-Authored-By: Vitor K <29167336+vitor-k@users.noreply.github.com>
2021-01-18 01:22:54 +01:00
lat9nq fb796843df configure_service: Only compile FormatEventStatusString when YUZU_ENABLE_BOXCAT is enabled
The function is unused if YUZU_ENABLE_BOXCAT is disabled, causing a
-Wunused-funciton error when compiled.

Wrapping it with `#ifdef YUZU_ENABLE_BOXCAT` to prevent compiling the
function when the variable is disabled. Opting to not use [[maybe
unused]] in case the function is totally unused in the future.
2021-01-17 17:54:29 -05:00
bunnei e8401964b4 Merge pull request #5360 from ReinUsesLisp/enforce-memclass-access
core: Silence Wclass-memaccess warnings and enforce it
2021-01-17 00:55:10 -08:00
Rodrigo Locatti 132f2006af Merge pull request #5745 from lioncash/documentation
video_core: Resolve -Wdocumentation warnings
2021-01-17 05:37:17 -03:00
bunnei e1ecf64701 Merge pull request #5744 from lioncash/header-guard
vulkan_debug_callback: Add missing header guard
2021-01-17 00:16:12 -08:00
Lioncash 5f4e7c77bd texture_cache/util: Resolve -Wsign-compare warning
Resolves a -Wsign-compare warning on Clang.
2021-01-17 02:47:48 -05:00
Lioncash 40acc2c079 video_core: Resolve -Wdocumentation warnings
Silences some -Wdocumentation warnings on Clang.
2021-01-17 02:44:21 -05:00
Lioncash c61b973968 vulkan_debug_callback: Add missing header guard
Prevents inclusion issues from occurring.
2021-01-17 02:39:24 -05:00
Rodrigo Locatti 0e0fc07135 Merge pull request #5740 from lioncash/const-fn
input_interpreter: Mark two member functions as const
2021-01-16 20:02:02 -03:00
Rodrigo Locatti fd873fd369 Merge pull request #5262 from ReinUsesLisp/buffer-base
buffer_cache/buffer_base: Add a range tracking buffer container and tests
2021-01-16 19:48:26 -03:00
Lioncash ca9afa3293 input_interpreter: Mark two member functions as const
These aren't stateful functions, so we can make use of const.

While we're at, we can resolve some -Wdocumentation warnings.
2021-01-16 16:08:35 -05:00
bunnei ff2b7cc0d3 Merge pull request #5366 from Morph1984/button-press
input_interpreter: Add method to check for a button press state
2021-01-16 13:00:54 -08:00
Morph 3c8f936b31 input_interpreter: Add method to check for a button press state
This allows to check for continuous input for the duration of a button press/hold
2021-01-16 10:34:39 -05:00
bunnei a7fd61fcce Merge pull request #5275 from FernandoS27/fast-native-clock
X86/NativeClock: Improve performance of clock calculations on hot path.
2021-01-15 23:01:42 -08:00
bunnei 8def504d73 Merge pull request #5336 from lioncash/tree
common/tree: Convert defines over to templates
2021-01-15 21:46:25 -08:00
Rodrigo Locatti c17ee0da5d Merge pull request #5297 from ReinUsesLisp/vulkan-allocator-common
vulkan_memory_allocator: Improvements to the memory allocator
2021-01-15 21:50:05 -03:00
LC 8be9e5b48b Merge pull request #5358 from ReinUsesLisp/rename-insert-padding
common/common_funcs: Rename INSERT_UNION_PADDING_{BYTES,WORDS} to _NOINIT
2021-01-15 16:19:46 -05:00
ReinUsesLisp 5f517e3e16 core/cmake: Enforce Wclass-memaccess
Treat -Wclass-memaccess as an error.
2021-01-15 16:31:19 -03:00
ReinUsesLisp f8650a9580 core: Silence Wclass-memaccess warnings
This requires making several types trivial and properly initialize
them whenever they are called.
2021-01-15 16:31:19 -03:00
ReinUsesLisp 3ff978aa4f common/common_funcs: Rename INSERT_UNION_PADDING_{BYTES,WORDS} to _NOINIT
INSERT_PADDING_BYTES_NOINIT is more descriptive of the underlying behavior.
2021-01-15 16:27:28 -03:00
ReinUsesLisp 301e2b5b7a vulkan_memory_allocator: Remove unnecesary 'device' memory from commits 2021-01-15 16:19:40 -03:00
ReinUsesLisp 432f045dba vk_texture_cache: Use Download memory types for texture flushes
Use the Download memory type where it matters.
2021-01-15 16:19:40 -03:00
ReinUsesLisp 8f22f5470c vulkan_memory_allocator: Add allocation support for download types
Implements the allocator logic to handle download memory types. This
will try to use HOST_CACHED_BIT when available.
2021-01-15 16:19:39 -03:00
ReinUsesLisp 72541af3bc vulkan_memory_allocator: Add "download" memory usage hint
Allow users of the allocator to hint memory usage for downloads. This
removes the non-descriptive boolean passed for "host visible" or not
host visible memory commits, and uses an enum to hint device local,
upload and download usages.
2021-01-15 16:19:39 -03:00
ReinUsesLisp fade63b58e vulkan_common: Move allocator to the common directory
Allow using the abstraction from the OpenGL backend.
2021-01-15 16:19:39 -03:00
ReinUsesLisp c2b550987b renderer_vulkan: Rename Vulkan memory manager to memory allocator
"Memory manager" collides with the guest GPU memory manager, and a
memory allocator sounds closer to what the abstraction aims to be.
2021-01-15 16:19:39 -03:00
ReinUsesLisp e996f1ad09 vk_memory_manager: Improve memory manager and its API
Fix a bug where the memory allocator could leave gaps between commits.
To fix this the allocation algorithm was reworked, although it's still
short in number of lines of code.

Rework the allocation API to self-contained movable objects instead of
naively using an unique_ptr to do the job for us. Remove the VK prefix.
2021-01-15 16:19:36 -03:00
bunnei f728a504aa Merge pull request #5355 from lioncash/timer
common/timer: Remove
2021-01-15 09:42:33 -08:00
LC 9754a8145c Merge pull request #5357 from ReinUsesLisp/alignment-log2
common/alignment: Rename AlignBits to AlignUpLog2 and use constraints
2021-01-15 03:12:36 -05:00
Rodrigo Locatti 5b9aedfc21 Merge pull request #5356 from lioncash/clz
common/bit_util: Replace CLZ/CTZ operations with standardized ones
2021-01-15 04:48:58 -03:00
Lioncash 8620de6b20 common/bit_util: Replace CLZ/CTZ operations with standardized ones
Makes for less code that we need to maintain.
2021-01-15 02:15:32 -05:00
Lioncash 91084d9396 common/timer: Remove
This is a leftover from citra and dolphin that isn't used at all,
particularly given the <chrono> header exists.
2021-01-15 01:55:33 -05:00
LC c8bf0caca0 Merge pull request #5354 from ReinUsesLisp/remove-common-color
common/color: Remove
2021-01-15 01:54:22 -05:00
ReinUsesLisp 95fa57f007 common/color: Remove
This is a leftover from Citra we no longer use.
2021-01-15 03:47:43 -03:00
ReinUsesLisp 7bd603061c tests: Add unit tests for the GPU range tracking buffer container
Due to how error prone the container design is, this commit adds unit
tests for it.

Some tests taken from here are based on bugs from using this buffer
container in games, so if we ever break it in the future in a way that
might harm games, the tests should fail.
2021-01-13 04:31:40 -03:00
ReinUsesLisp a4bfae1b55 buffer_cache/buffer_base: Add a range tracking buffer container
It keeps track of the modified CPU and GPU ranges on a CPU page
granularity, notifying the given rasterizer about state changes
in the tracking behavior of the buffer.

Use a small vector optimization to store buffers smaller than 256 KiB
locally instead of using free store memory allocations.
2021-01-13 04:14:58 -03:00
Lioncash b15e1a3501 common/tree: Convert defines over to templates
Reworks the tree header to operate off of templates as opposed to a
series of defines.

This allows all tree facilities to obey namespacing rules, and also
allows this code to be used within modules once compiler support is in
place.

This also gets rid to use a macro to define functions and structs for
necessary data types. With templates, these will be generated when
they're actually used, eliminating the need for the separate
declaration.
2021-01-12 16:46:36 -05:00
Lioncash 197b5d19bc common/tree: Remove unused splay tree defines
Makes for less code to take care of.
2021-01-12 02:32:41 -05:00
Fernando Sahmkow 53d92318b8 X86/NativeClock: Reimplement RTDSC access to be lock free. 2021-01-02 04:00:27 +01:00
Fernando Sahmkow d4f871cb6a X86/NativeClock: Improve performance of clock calculations on hot path. 2021-01-02 00:43:47 +01:00
83 changed files with 2883 additions and 2434 deletions
+3 -3
View File
@@ -40,17 +40,17 @@ public:
SinkSampleFormat sample_format;
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
bool in_use;
INSERT_UNION_PADDING_BYTES(5);
INSERT_PADDING_BYTES_NOINIT(5);
};
static_assert(sizeof(CircularBufferIn) == 0x28,
"SinkInfo::CircularBufferIn is in invalid size");
struct DeviceIn {
std::array<u8, 255> device_name;
INSERT_UNION_PADDING_BYTES(1);
INSERT_PADDING_BYTES_NOINIT(1);
s32_le input_count;
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
INSERT_UNION_PADDING_BYTES(1);
INSERT_PADDING_BYTES_NOINIT(1);
bool down_matrix_enabled;
DownmixCoefficients down_matrix_coef;
};
+18 -18
View File
@@ -86,28 +86,28 @@ struct BehaviorFlags {
static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
struct ADPCMContext {
u16 header{};
s16 yn1{};
s16 yn2{};
u16 header;
s16 yn1;
s16 yn2;
};
static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
struct VoiceState {
s64 played_sample_count{};
s32 offset{};
s32 wave_buffer_index{};
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
s32 wave_buffer_consumed{};
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
s32 fraction{};
VAddr context_address{};
Codec::ADPCM_Coeff coeff{};
ADPCMContext context{};
std::array<s64, 2> biquad_filter_state{};
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
u32 external_context_size{};
bool is_external_context_used{};
bool voice_dropped{};
s64 played_sample_count;
s32 offset;
s32 wave_buffer_index;
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid;
s32 wave_buffer_consumed;
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history;
s32 fraction;
VAddr context_address;
Codec::ADPCM_Coeff coeff;
ADPCMContext context;
std::array<s64, 2> biquad_filter_state;
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples;
u32 external_context_size;
bool is_external_context_used;
bool voice_dropped;
};
class VoiceChannelResource {
+2 -4
View File
@@ -98,7 +98,6 @@ add_library(common STATIC
algorithm.h
alignment.h
assert.h
atomic_ops.cpp
atomic_ops.h
detached_tasks.cpp
detached_tasks.h
@@ -108,7 +107,6 @@ add_library(common STATIC
bit_util.h
cityhash.cpp
cityhash.h
color.h
common_funcs.h
common_paths.h
common_types.h
@@ -140,6 +138,8 @@ add_library(common STATIC
microprofile.h
microprofileui.h
misc.cpp
nvidia_flags.cpp
nvidia_flags.h
page_table.cpp
page_table.h
param_package.cpp
@@ -167,8 +167,6 @@ add_library(common STATIC
threadsafe_queue.h
time_zone.cpp
time_zone.h
timer.cpp
timer.h
tree.h
uint128.cpp
uint128.h
-75
View File
@@ -1,75 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/atomic_ops.h"
#if _MSC_VER
#include <intrin.h>
#endif
namespace Common {
#if _MSC_VER
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0],
reinterpret_cast<__int64*>(expected.data())) != 0;
}
#else
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
#endif
} // namespace Common
+66 -5
View File
@@ -4,14 +4,75 @@
#pragma once
#include <cstring>
#include <memory>
#include "common/common_types.h"
#if _MSC_VER
#include <intrin.h>
#endif
namespace Common {
[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
#if _MSC_VER
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0],
reinterpret_cast<__int64*>(expected.data())) != 0;
}
#else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
#endif
} // namespace Common
-76
View File
@@ -21,82 +21,6 @@ template <typename T>
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
return 31 - leading_zero;
}
return 32;
}
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
return 63 - leading_zero;
}
return 64;
}
#else
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return static_cast<u32>(__builtin_clz(value));
}
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_clzll(value));
}
#endif
#ifdef _MSC_VER
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 32;
}
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 64;
}
#else
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return static_cast<u32>(__builtin_ctz(value));
}
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_ctzll(value));
}
#endif
#ifdef _MSC_VER
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
-271
View File
@@ -1,271 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "common/vector_math.h"
namespace Common::Color {
/// Convert a 1-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
/// Convert a 8-bit color component to 1 bit
[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
return value >> 7;
}
/// Convert a 8-bit color component to 4 bit
[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
return value >> 4;
}
/// Convert a 8-bit color component to 5 bit
[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
return value >> 3;
}
/// Convert a 8-bit color component to 6 bit
[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
return value >> 2;
}
/**
* Decode a color stored in RGBA8 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
/**
* Decode a color stored in RGB8 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
/**
* Decode a color stored in RG8 (aka HILO8) format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
/**
* Decode a color stored in RGB565 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255};
}
/**
* Decode a color stored in RGB5A1 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
}
/**
* Decode a color stored in RGBA4 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
}
/**
* Decode a depth value stored in D16 format
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
}
/**
* Decode a depth value stored in D24 format
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
}
/**
* Decode a depth value and a stencil value stored in D24S8 format
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Common::Vec2
*/
[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
/**
* Encode a color as RGBA8 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[3] = color.r();
bytes[2] = color.g();
bytes[1] = color.b();
bytes[0] = color.a();
}
/**
* Encode a color as RGB8 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[2] = color.r();
bytes[1] = color.g();
bytes[0] = color.b();
}
/**
* Encode a color as RG8 (aka HILO8) format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[1] = color.r();
bytes[0] = color.g();
}
/**
* Encode a color as RGB565 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) {
const u16_le data =
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a color as RGB5A1 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) {
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a color as RGBA4 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a 16 bit depth value as D16 format
* @param value 16 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD16(u32 value, u8* bytes) {
const u16_le data = static_cast<u16>(value);
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a 24 bit depth value as D24 format
* @param value 24 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD24(u32 value, u8* bytes) {
bytes[0] = value & 0xFF;
bytes[1] = (value >> 8) & 0xFF;
bytes[2] = (value >> 16) & 0xFF;
}
/**
* Encode a 24 bit depth and 8 bit stencil values as D24S8 format
* @param depth 24 bit source depth value to encode
* @param stencil 8 bit source stencil value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) {
bytes[0] = depth & 0xFF;
bytes[1] = (depth >> 8) & 0xFF;
bytes[2] = (depth >> 16) & 0xFF;
bytes[3] = stencil;
}
/**
* Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused)
* @param depth 24 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
* @note unused bits will not be modified
*/
inline void EncodeD24X8(u32 depth, u8* bytes) {
bytes[0] = depth & 0xFF;
bytes[1] = (depth >> 8) & 0xFF;
bytes[2] = (depth >> 16) & 0xFF;
}
/**
* Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused)
* @param stencil 8 bit source stencil value to encode
* @param bytes Pointer where to store the encoded value
* @note unused bits will not be modified
*/
inline void EncodeX24S8(u8 stencil, u8* bytes) {
bytes[3] = stencil;
}
} // namespace Common::Color
+4 -4
View File
@@ -24,10 +24,10 @@
#define INSERT_PADDING_WORDS(num_words) \
std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
/// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is
/// because unions can only be initialized by one member.
#define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
#define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
/// This keeps the structure trivial to construct.
#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
#ifndef _MSC_VER
+37 -62
View File
@@ -16,17 +16,30 @@ class IntrusiveRedBlackTreeImpl;
}
struct IntrusiveRedBlackTreeNode {
public:
using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
constexpr IntrusiveRedBlackTreeNode() = default;
void SetEntry(const EntryType& new_entry) {
entry = new_entry;
}
[[nodiscard]] EntryType& GetEntry() {
return entry;
}
[[nodiscard]] const EntryType& GetEntry() const {
return entry;
}
private:
RB_ENTRY(IntrusiveRedBlackTreeNode) entry{};
EntryType entry{};
friend class impl::IntrusiveRedBlackTreeImpl;
template <class, class, class>
friend class IntrusiveRedBlackTree;
public:
constexpr IntrusiveRedBlackTreeNode() = default;
};
template <class T, class Traits, class Comparator>
@@ -35,17 +48,12 @@ class IntrusiveRedBlackTree;
namespace impl {
class IntrusiveRedBlackTreeImpl {
private:
template <class, class, class>
friend class ::Common::IntrusiveRedBlackTree;
private:
RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode);
using RootType = IntrusiveRedBlackTreeRoot;
private:
IntrusiveRedBlackTreeRoot root;
using RootType = RBHead<IntrusiveRedBlackTreeNode>;
RootType root;
public:
template <bool Const>
@@ -121,57 +129,45 @@ public:
}
};
protected:
// Generate static implementations for non-comparison operations for IntrusiveRedBlackTreeRoot.
RB_GENERATE_WITHOUT_COMPARE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry);
private:
// Define accessors using RB_* functions.
constexpr void InitializeImpl() {
RB_INIT(&this->root);
}
bool EmptyImpl() const {
return RB_EMPTY(&this->root);
return root.IsEmpty();
}
IntrusiveRedBlackTreeNode* GetMinImpl() const {
return RB_MIN(IntrusiveRedBlackTreeRoot,
const_cast<IntrusiveRedBlackTreeRoot*>(&this->root));
return RB_MIN(const_cast<RootType*>(&root));
}
IntrusiveRedBlackTreeNode* GetMaxImpl() const {
return RB_MAX(IntrusiveRedBlackTreeRoot,
const_cast<IntrusiveRedBlackTreeRoot*>(&this->root));
return RB_MAX(const_cast<RootType*>(&root));
}
IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node);
return RB_REMOVE(&root, node);
}
public:
static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node);
return RB_NEXT(node);
}
static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node);
return RB_PREV(node);
}
static IntrusiveRedBlackTreeNode const* GetNext(const IntrusiveRedBlackTreeNode* node) {
static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
static IntrusiveRedBlackTreeNode const* GetPrev(const IntrusiveRedBlackTreeNode* node) {
static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
public:
constexpr IntrusiveRedBlackTreeImpl() : root() {
this->InitializeImpl();
}
constexpr IntrusiveRedBlackTreeImpl() {}
// Iterator accessors.
iterator begin() {
@@ -269,8 +265,6 @@ private:
ImplType impl{};
public:
struct IntrusiveRedBlackTreeRootWithCompare : ImplType::IntrusiveRedBlackTreeRoot {};
template <bool Const>
class Iterator;
@@ -362,11 +356,6 @@ public:
}
};
private:
// Generate static implementations for comparison operations for IntrusiveRedBlackTreeRoot.
RB_GENERATE_WITH_COMPARE_STATIC(IntrusiveRedBlackTreeRootWithCompare, IntrusiveRedBlackTreeNode,
entry, CompareImpl, LightCompareImpl);
private:
static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
const IntrusiveRedBlackTreeNode* rhs) {
@@ -379,41 +368,27 @@ private:
// Define accessors using RB_* functions.
IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
return RB_INSERT(IntrusiveRedBlackTreeRootWithCompare,
static_cast<IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root),
node);
return RB_INSERT(&impl.root, node, CompareImpl);
}
IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_FIND(
IntrusiveRedBlackTreeRootWithCompare,
const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
const_cast<IntrusiveRedBlackTreeNode*>(node));
return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_NFIND(
IntrusiveRedBlackTreeRootWithCompare,
const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
const_cast<IntrusiveRedBlackTreeNode*>(node));
return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
return RB_FIND_LIGHT(
IntrusiveRedBlackTreeRootWithCompare,
const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
static_cast<const void*>(lelm));
return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
}
IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
return RB_NFIND_LIGHT(
IntrusiveRedBlackTreeRootWithCompare,
const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
static_cast<const void*>(lelm));
return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
}
public:
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <filesystem>
#include <stdlib.h>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/nvidia_flags.h"
namespace Common {
void ConfigureNvidiaEnvironmentFlags() {
#ifdef _WIN32
const std::string shader_path = Common::FS::SanitizePath(
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
const std::string windows_path =
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
void(Common::FS::CreateFullPath(shader_path + '/'));
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
#endif
}
} // namespace Common
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Common {
/// Configure platform specific flags for Nvidia's driver
void ConfigureNvidiaEnvironmentFlags();
} // namespace Common
-159
View File
@@ -1,159 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <ctime>
#include <fmt/format.h>
#include "common/common_types.h"
#include "common/string_util.h"
#include "common/timer.h"
namespace Common {
std::chrono::milliseconds Timer::GetTimeMs() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
// --------------------------------------------
// Initiate, Start, Stop, and Update the time
// --------------------------------------------
// Set initial values for the class
Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) {
Update();
}
// Write the starting time
void Timer::Start() {
m_StartTime = GetTimeMs();
m_Running = true;
}
// Stop the timer
void Timer::Stop() {
// Write the final time
m_LastTime = GetTimeMs();
m_Running = false;
}
// Update the last time variable
void Timer::Update() {
m_LastTime = GetTimeMs();
// TODO(ector) - QPF
}
// -------------------------------------
// Get time difference and elapsed time
// -------------------------------------
// Get the number of milliseconds since the last Update()
std::chrono::milliseconds Timer::GetTimeDifference() {
return GetTimeMs() - m_LastTime;
}
// Add the time difference since the last Update() to the starting time.
// This is used to compensate for a paused game.
void Timer::AddTimeDifference() {
m_StartTime += GetTimeDifference();
}
// Get the time elapsed since the Start()
std::chrono::milliseconds Timer::GetTimeElapsed() {
// If we have not started yet, return 1 (because then I don't
// have to change the FPS calculation in CoreRerecording.cpp .
if (m_StartTime.count() == 0)
return std::chrono::milliseconds(1);
// Return the final timer time if the timer is stopped
if (!m_Running)
return (m_LastTime - m_StartTime);
return (GetTimeMs() - m_StartTime);
}
// Get the formatted time elapsed since the Start()
std::string Timer::GetTimeElapsedFormatted() const {
// If we have not started yet, return zero
if (m_StartTime.count() == 0)
return "00:00:00:000";
// The number of milliseconds since the start.
// Use a different value if the timer is stopped.
std::chrono::milliseconds Milliseconds;
if (m_Running)
Milliseconds = GetTimeMs() - m_StartTime;
else
Milliseconds = m_LastTime - m_StartTime;
// Seconds
std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds);
// Minutes
std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds);
// Hours
std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds);
std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60,
Seconds.count() % 60, Milliseconds.count() % 1000);
return TmpStr;
}
// Get the number of seconds since January 1 1970
std::chrono::seconds Timer::GetTimeSinceJan1970() {
return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
}
std::chrono::seconds Timer::GetLocalTimeSinceJan1970() {
time_t sysTime, tzDiff, tzDST;
struct tm* gmTime;
time(&sysTime);
// Account for DST where needed
gmTime = localtime(&sysTime);
if (gmTime->tm_isdst == 1)
tzDST = 3600;
else
tzDST = 0;
// Lazy way to get local time in sec
gmTime = gmtime(&sysTime);
tzDiff = sysTime - mktime(gmTime);
return std::chrono::seconds(sysTime + tzDiff + tzDST);
}
// Return the current time formatted as Minutes:Seconds:Milliseconds
// in the form 00:00:000.
std::string Timer::GetTimeFormatted() {
time_t sysTime;
struct tm* gmTime;
char tmp[13];
time(&sysTime);
gmTime = localtime(&sysTime);
strftime(tmp, 6, "%M:%S", gmTime);
u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000;
return fmt::format("{}:{:03}", tmp, milliseconds);
}
// Returns a timestamp with decimals for precise time comparisons
// ----------------
double Timer::GetDoubleTime() {
// Get continuous timestamp
auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
// Remove a few years. We only really want enough seconds to make
// sure that we are detecting actual actions, perhaps 60 seconds is
// enough really, but I leave a year of seconds anyway, in case the
// user's clock is incorrect or something like that.
tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
// Make a smaller integer that fits in the double
const auto seconds = static_cast<u32>(tmp_seconds);
return seconds + ms;
}
} // Namespace Common
-41
View File
@@ -1,41 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <string>
#include "common/common_types.h"
namespace Common {
class Timer {
public:
Timer();
void Start();
void Stop();
void Update();
// The time difference is always returned in milliseconds, regardless of alternative internal
// representation
[[nodiscard]] std::chrono::milliseconds GetTimeDifference();
void AddTimeDifference();
[[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
[[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
[[nodiscard]] static double GetDoubleTime();
[[nodiscard]] static std::string GetTimeFormatted();
[[nodiscard]] std::string GetTimeElapsedFormatted() const;
[[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
[[nodiscard]] static std::chrono::milliseconds GetTimeMs();
private:
std::chrono::milliseconds m_LastTime;
std::chrono::milliseconds m_StartTime;
bool m_Running;
};
} // Namespace Common
+587 -735
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
u128 uuid;
UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
+91 -19
View File
@@ -2,19 +2,74 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <chrono>
#include <limits>
#include <mutex>
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(__umulh)
#pragma intrinsic(_udiv128)
#else
#include <x86intrin.h>
#endif
#include "common/atomic_ops.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
namespace {
[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
#ifdef __SIZEOF_INT128__
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
return static_cast<u64>(base / divisor);
#elif defined(_M_X64) || defined(_M_ARM64)
std::array<u64, 2> r = {0, numerator};
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], divisor, &remainder);
#else
return _udiv128(r[1], r[0], divisor, &remainder);
#endif
#else
// This one is bit more inaccurate.
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
#endif
}
[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
#ifdef __SIZEOF_INT128__
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
#elif defined(_M_X64) || defined(_M_ARM64)
return __umulh(a, b); // MSVC
#else
// Generic fallback
const u64 a_lo = u32(a);
const u64 a_hi = a >> 32;
const u64 b_lo = u32(b);
const u64 b_hi = b >> 32;
const u64 a_x_b_hi = a_hi * b_hi;
const u64 a_x_b_mid = a_hi * b_lo;
const u64 b_x_a_mid = b_hi * a_lo;
const u64 a_x_b_lo = a_lo * b_lo;
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
32;
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
return multhi;
#endif
}
} // namespace
namespace Common {
u64 EstimateRDTSCFrequency() {
@@ -48,54 +103,71 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
rtsc_frequency_} {
_mm_mfence();
last_measure = __rdtsc();
accumulated_ticks = 0U;
time_point.inner.last_measure = __rdtsc();
time_point.inner.accumulated_ticks = 0U;
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
}
u64 NativeClock::GetRTSC() {
std::scoped_lock scope{rtsc_serialize};
_mm_mfence();
const u64 current_measure = __rdtsc();
u64 diff = current_measure - last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
if (current_measure > last_measure) {
last_measure = current_measure;
}
accumulated_ticks += diff;
TimePoint new_time_point{};
TimePoint current_time_point{};
do {
current_time_point.pack = time_point.pack;
_mm_mfence();
const u64 current_measure = __rdtsc();
u64 diff = current_measure - current_time_point.inner.last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
? current_measure
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
/// The clock cannot be more precise than the guest timer, remove the lower bits
return accumulated_ticks & inaccuracy_mask;
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
}
void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
_mm_mfence();
last_measure = __rdtsc();
TimePoint current_time_point{};
TimePoint new_time_point{};
do {
current_time_point.pack = time_point.pack;
new_time_point.pack = current_time_point.pack;
_mm_mfence();
new_time_point.inner.last_measure = __rdtsc();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
}
}
std::chrono::nanoseconds NativeClock::GetTimeNS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
}
u64 NativeClock::GetClockCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
return MultiplyHigh(rtsc_value, clock_rtsc_factor);
}
u64 NativeClock::GetCPUCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
}
} // namespace X64
+17 -4
View File
@@ -6,7 +6,6 @@
#include <optional>
#include "common/spin_lock.h"
#include "common/wall_clock.h"
namespace Common {
@@ -32,14 +31,28 @@ public:
private:
u64 GetRTSC();
union alignas(16) TimePoint {
TimePoint() : pack{} {}
u128 pack{};
struct Inner {
u64 last_measure{};
u64 accumulated_ticks{};
} inner;
};
/// value used to reduce the native clocks accuracy as some apss rely on
/// undefined behavior where the level of accuracy in the clock shouldn't
/// be higher.
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
SpinLock rtsc_serialize{};
u64 last_measure{};
u64 accumulated_ticks{};
TimePoint time_point;
// factors
u64 clock_rtsc_factor{};
u64 cpu_rtsc_factor{};
u64 ns_rtsc_factor{};
u64 us_rtsc_factor{};
u64 ms_rtsc_factor{};
u64 rtsc_frequency;
};
} // namespace X64
+1
View File
@@ -645,6 +645,7 @@ else()
-Werror=implicit-fallthrough
-Werror=sign-compare
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
+12 -12
View File
@@ -43,17 +43,17 @@ static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
struct IVFCHeader {
u32_le magic;
u32_le magic_number;
INSERT_UNION_PADDING_BYTES(8);
INSERT_PADDING_BYTES_NOINIT(8);
std::array<IVFCLevel, 6> levels;
INSERT_UNION_PADDING_BYTES(64);
INSERT_PADDING_BYTES_NOINIT(64);
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
struct NCASectionHeaderBlock {
INSERT_UNION_PADDING_BYTES(3);
INSERT_PADDING_BYTES_NOINIT(3);
NCASectionFilesystemType filesystem_type;
NCASectionCryptoType crypto_type;
INSERT_UNION_PADDING_BYTES(3);
INSERT_PADDING_BYTES_NOINIT(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
@@ -61,7 +61,7 @@ struct NCASectionRaw {
NCASectionHeaderBlock header;
std::array<u8, 0x138> block_data;
std::array<u8, 0x8> section_ctr;
INSERT_UNION_PADDING_BYTES(0xB8);
INSERT_PADDING_BYTES_NOINIT(0xB8);
};
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
@@ -69,19 +69,19 @@ struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_UNION_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
u64_le hash_table_offset;
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_UNION_PADDING_BYTES(0x1B0);
INSERT_PADDING_BYTES_NOINIT(0x1B0);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_UNION_PADDING_BYTES(0x118);
INSERT_PADDING_BYTES_NOINIT(0x118);
};
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
@@ -89,19 +89,19 @@ struct BKTRHeader {
u64_le offset;
u64_le size;
u32_le magic;
INSERT_UNION_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES_NOINIT(0x4);
u32_le number_entries;
INSERT_UNION_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES_NOINIT(0x4);
};
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
struct BKTRSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_UNION_PADDING_BYTES(0x18);
INSERT_PADDING_BYTES_NOINIT(0x18);
BKTRHeader relocation;
BKTRHeader subsection;
INSERT_UNION_PADDING_BYTES(0xC0);
INSERT_PADDING_BYTES_NOINIT(0xC0);
};
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
+2 -2
View File
@@ -58,7 +58,7 @@ struct SaveDataAttribute {
SaveDataType type;
SaveDataRank rank;
u16 index;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
u64 zero_1;
u64 zero_2;
u64 zero_3;
@@ -72,7 +72,7 @@ struct SaveDataExtraData {
u64 owner_id;
s64 timestamp;
SaveDataFlags flags;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
s64 available_size;
s64 journal_size;
s64 commit_id;
+4
View File
@@ -25,6 +25,10 @@ void InputInterpreter::PollInput() {
button_states[current_index] = button_state;
}
bool InputInterpreter::IsButtonPressed(HIDButton button) const {
return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
}
bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
const bool current_press =
(button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
+25 -4
View File
@@ -66,6 +66,27 @@ public:
/// Gets a button state from HID and inserts it into the array of button states.
void PollInput();
/**
* Checks whether the button is pressed.
*
* @param button The button to check.
*
* @returns True when the button is pressed.
*/
[[nodiscard]] bool IsButtonPressed(HIDButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed.
*
* @tparam HIDButton The buttons to check.
*
* @returns True when at least one of the buttons is pressed.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressed() {
return (IsButtonPressed(T) || ...);
}
/**
* The specified button is considered to be pressed once
* if it is currently pressed and not pressed previously.
@@ -79,12 +100,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is pressed once.
*
* @tparam HIDButton The buttons to check.
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is pressed once.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() {
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...);
}
@@ -100,12 +121,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is held down.
*
* @tparam HIDButton The buttons to check.
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is held down.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonHeld() {
[[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...);
}
+4 -4
View File
@@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre
struct DataPayloadHeader {
u32_le magic;
INSERT_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
@@ -160,7 +160,7 @@ struct DomainMessageHeader {
// Used when responding to an IPC request, Server -> Client.
struct {
u32_le num_objects;
INSERT_UNION_PADDING_WORDS(3);
INSERT_PADDING_WORDS_NOINIT(3);
};
// Used when performing an IPC request, Client -> Server.
@@ -171,10 +171,10 @@ struct DomainMessageHeader {
BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_UNION_PADDING_WORDS(2);
INSERT_PADDING_WORDS_NOINIT(2);
};
std::array<u32, 4> raw{};
std::array<u32, 4> raw;
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
+2 -2
View File
@@ -8,11 +8,11 @@
#pragma once
#include <array>
#include <bit>
#include <concepts>
#include "common/assert.h"
#include "common/bit_set.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/concepts.h"
@@ -268,7 +268,7 @@ private:
}
constexpr s32 GetNextCore(u64& affinity) {
const s32 core = Common::CountTrailingZeroes64(affinity);
const s32 core = std::countr_zero(affinity);
ClearAffinityBit(affinity, core);
return core;
}
+5 -3
View File
@@ -5,6 +5,8 @@
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include <bit>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/fiber.h"
@@ -31,12 +33,12 @@ static void IncrementScheduledCount(Kernel::Thread* thread) {
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
Core::EmuThreadHandle global_thread) {
u32 current_core = global_thread.host_handle;
const u32 current_core = global_thread.host_handle;
bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
(current_core < Core::Hardware::NUM_CPU_CORES);
while (cores_pending_reschedule != 0) {
u32 core = Common::CountTrailingZeroes64(cores_pending_reschedule);
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
if (!must_context_switch || core != current_core) {
auto& phys_core = kernel.PhysicalCore(core);
@@ -109,7 +111,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
u32 core_id = Common::CountTrailingZeroes64(idle_cores);
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
+2 -2
View File
@@ -8,11 +8,11 @@
#pragma once
#include <array>
#include <bit>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
@@ -105,7 +105,7 @@ private:
ASSERT(depth == 0);
return -1;
}
offset = offset * 64 + Common::CountTrailingZeroes64(v);
offset = offset * 64 + static_cast<u32>(std::countr_zero(v));
++depth;
} while (depth < static_cast<s32>(used_depths));
+3 -1
View File
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <bit>
#include "common/bit_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
@@ -60,7 +62,7 @@ constexpr CapabilityType GetCapabilityType(u32 value) {
u32 GetFlagBitOffset(CapabilityType type) {
const auto value = static_cast<u32>(type);
return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
}
} // Anonymous namespace
+1 -1
View File
@@ -534,7 +534,7 @@ private:
rb.Push(RESULT_SUCCESS);
}
Common::UUID user_id;
Common::UUID user_id{Common::INVALID_UUID};
};
// 6.0.0+
+5 -5
View File
@@ -227,17 +227,17 @@ void ProfileManager::CloseUser(UUID uuid) {
/// Gets all valid user ids on the system
UserIDArray ProfileManager::GetAllUsers() const {
UserIDArray output;
std::transform(profiles.begin(), profiles.end(), output.begin(),
[](const ProfileInfo& p) { return p.user_uuid; });
UserIDArray output{};
std::ranges::transform(profiles, output.begin(),
[](const ProfileInfo& p) { return p.user_uuid; });
return output;
}
/// Get all the open users on the system and zero out the rest of the data. This is specifically
/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
UserIDArray ProfileManager::GetOpenUsers() const {
UserIDArray output;
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
UserIDArray output{};
std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
return UUID{Common::INVALID_UUID};
+9 -9
View File
@@ -23,12 +23,12 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
struct ProfileData {
INSERT_PADDING_WORDS(1);
u32 icon_id{};
u8 bg_color_id{};
INSERT_PADDING_BYTES(0x7);
INSERT_PADDING_BYTES(0x10);
INSERT_PADDING_BYTES(0x60);
INSERT_PADDING_WORDS_NOINIT(1);
u32 icon_id;
u8 bg_color_id;
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0x10);
INSERT_PADDING_BYTES_NOINIT(0x60);
};
static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
@@ -43,9 +43,9 @@ struct ProfileInfo {
};
struct ProfileBase {
Common::UUID user_uuid{Common::INVALID_UUID};
u64_le timestamp{};
ProfileUsername username{};
Common::UUID user_uuid;
u64_le timestamp;
ProfileUsername username;
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
+5 -5
View File
@@ -20,9 +20,9 @@ namespace Service::AM::Applets {
struct ShowError {
u8 mode;
bool jump;
INSERT_UNION_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
bool use_64bit_error_code;
INSERT_UNION_PADDING_BYTES(1);
INSERT_PADDING_BYTES_NOINIT(1);
u64 error_code_64;
u32 error_code_32;
};
@@ -32,7 +32,7 @@ static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
struct ShowErrorRecord {
u8 mode;
bool jump;
INSERT_UNION_PADDING_BYTES(6);
INSERT_PADDING_BYTES_NOINIT(6);
u64 error_code_64;
u64 posix_time;
};
@@ -41,7 +41,7 @@ static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect si
struct SystemErrorArg {
u8 mode;
bool jump;
INSERT_UNION_PADDING_BYTES(6);
INSERT_PADDING_BYTES_NOINIT(6);
u64 error_code_64;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
@@ -52,7 +52,7 @@ static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect si
struct ApplicationErrorArg {
u8 mode;
bool jump;
INSERT_UNION_PADDING_BYTES(6);
INSERT_PADDING_BYTES_NOINIT(6);
u32 error_code;
std::array<char, 8> language_code;
std::array<char, 0x800> main_text;
+1 -1
View File
@@ -29,7 +29,7 @@ constexpr int DefaultSampleRate{48000};
struct AudoutParams {
s32_le sample_rate;
u16_le channel_count;
INSERT_PADDING_BYTES(2);
INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
@@ -141,7 +141,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
device_handle.device_index < DeviceIndex::MaxDeviceIndex;
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {
latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
}
Controller_NPad::~Controller_NPad() {
OnRelease();
@@ -732,7 +734,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
// Send an empty vibration to stop any vibrations.
vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
// Then reset the vibration value to its default value.
latest_vibration_values[npad_index][device_index] = {};
latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE;
}
return false;
@@ -890,7 +892,7 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
return;
}
if (controller == NPadControllerType::Handheld) {
if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+15 -8
View File
@@ -97,10 +97,10 @@ public:
};
struct DeviceHandle {
NpadType npad_type{};
u8 npad_id{};
DeviceIndex device_index{};
INSERT_PADDING_BYTES(1);
NpadType npad_type;
u8 npad_id;
DeviceIndex device_index;
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
@@ -120,13 +120,20 @@ public:
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
struct VibrationValue {
f32 amp_low{0.0f};
f32 freq_low{160.0f};
f32 amp_high{0.0f};
f32 freq_high{320.0f};
f32 amp_low;
f32 freq_low;
f32 amp_high;
f32 freq_high;
};
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
.amp_low = 0.0f,
.freq_low = 160.0f,
.amp_high = 0.0f,
.freq_high = 320.0f,
};
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
+74 -74
View File
@@ -401,9 +401,9 @@ void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 basic_xpad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 basic_xpad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -431,9 +431,9 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -452,9 +452,9 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -473,9 +473,9 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -494,9 +494,9 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -515,10 +515,10 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enable_sixaxis_sensor_fusion{};
INSERT_PADDING_BYTES(3);
Controller_NPad::DeviceHandle sixaxis_handle{};
u64 applet_resource_user_id{};
bool enable_sixaxis_sensor_fusion;
INSERT_PADDING_BYTES_NOINIT(3);
Controller_NPad::DeviceHandle sixaxis_handle;
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -556,9 +556,9 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -577,9 +577,9 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -599,9 +599,9 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -620,9 +620,9 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 unknown{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 unknown;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -702,10 +702,10 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u64 unknown{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
u64 unknown;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -722,9 +722,9 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -756,9 +756,9 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
// Should have no effect with how our npad sets up the data
IPC::RequestParser rp{ctx};
struct Parameters {
u32 unknown{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 unknown;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -800,9 +800,9 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -821,10 +821,10 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u64 npad_joy_device_type{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
u64 npad_joy_device_type;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -844,9 +844,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -952,9 +952,9 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 npad_id{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
u32 npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -971,10 +971,10 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool unintended_home_button_input_protection{};
INSERT_PADDING_BYTES(3);
u32 npad_id{};
u64 applet_resource_user_id{};
bool unintended_home_button_input_protection;
INSERT_PADDING_BYTES_NOINIT(3);
u32 npad_id;
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1026,10 +1026,10 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle{};
Controller_NPad::VibrationValue vibration_value{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle vibration_device_handle;
Controller_NPad::VibrationValue vibration_value;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1050,9 +1050,9 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1147,9 +1147,9 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1180,9 +1180,9 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1200,9 +1200,9 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle sixaxis_handle{};
INSERT_PADDING_WORDS(1);
u64 applet_resource_user_id{};
Controller_NPad::DeviceHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
+1
View File
@@ -100,6 +100,7 @@ MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
.mole_scale = static_cast<u8>(bf.mole_scale.Value()),
.mole_x = static_cast<u8>(bf.mole_x.Value()),
.mole_y = static_cast<u8>(bf.mole_y.Value()),
.padding = 0,
};
}
+53 -53
View File
@@ -27,58 +27,58 @@ enum class SourceFlag : u32 {
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
struct MiiInfo {
Common::UUID uuid{Common::INVALID_UUID};
std::array<char16_t, 11> name{};
u8 font_region{};
u8 favorite_color{};
u8 gender{};
u8 height{};
u8 build{};
u8 type{};
u8 region_move{};
u8 faceline_type{};
u8 faceline_color{};
u8 faceline_wrinkle{};
u8 faceline_make{};
u8 hair_type{};
u8 hair_color{};
u8 hair_flip{};
u8 eye_type{};
u8 eye_color{};
u8 eye_scale{};
u8 eye_aspect{};
u8 eye_rotate{};
u8 eye_x{};
u8 eye_y{};
u8 eyebrow_type{};
u8 eyebrow_color{};
u8 eyebrow_scale{};
u8 eyebrow_aspect{};
u8 eyebrow_rotate{};
u8 eyebrow_x{};
u8 eyebrow_y{};
u8 nose_type{};
u8 nose_scale{};
u8 nose_y{};
u8 mouth_type{};
u8 mouth_color{};
u8 mouth_scale{};
u8 mouth_aspect{};
u8 mouth_y{};
u8 beard_color{};
u8 beard_type{};
u8 mustache_type{};
u8 mustache_scale{};
u8 mustache_y{};
u8 glasses_type{};
u8 glasses_color{};
u8 glasses_scale{};
u8 glasses_y{};
u8 mole_type{};
u8 mole_scale{};
u8 mole_x{};
u8 mole_y{};
INSERT_PADDING_BYTES(1);
Common::UUID uuid;
std::array<char16_t, 11> name;
u8 font_region;
u8 favorite_color;
u8 gender;
u8 height;
u8 build;
u8 type;
u8 region_move;
u8 faceline_type;
u8 faceline_color;
u8 faceline_wrinkle;
u8 faceline_make;
u8 hair_type;
u8 hair_color;
u8 hair_flip;
u8 eye_type;
u8 eye_color;
u8 eye_scale;
u8 eye_aspect;
u8 eye_rotate;
u8 eye_x;
u8 eye_y;
u8 eyebrow_type;
u8 eyebrow_color;
u8 eyebrow_scale;
u8 eyebrow_aspect;
u8 eyebrow_rotate;
u8 eyebrow_x;
u8 eyebrow_y;
u8 nose_type;
u8 nose_scale;
u8 nose_y;
u8 mouth_type;
u8 mouth_color;
u8 mouth_scale;
u8 mouth_aspect;
u8 mouth_y;
u8 beard_color;
u8 beard_type;
u8 mustache_type;
u8 mustache_scale;
u8 mustache_y;
u8 glasses_type;
u8 glasses_color;
u8 glasses_scale;
u8 glasses_y;
u8 mole_type;
u8 mole_scale;
u8 mole_x;
u8 mole_y;
u8 padding;
std::u16string Name() const;
};
@@ -324,7 +324,7 @@ public:
ResultCode GetIndex(const MiiInfo& info, u32& index);
private:
const Common::UUID user_id;
const Common::UUID user_id{Common::INVALID_UUID};
u64 update_counter{};
};
+13 -13
View File
@@ -73,19 +73,19 @@ struct TimeSpanType {
static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
struct ClockSnapshot {
SystemClockContext user_context{};
SystemClockContext network_context{};
s64 user_time{};
s64 network_time{};
TimeZone::CalendarTime user_calendar_time{};
TimeZone::CalendarTime network_calendar_time{};
TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
SteadyClockTimePoint steady_clock_time_point{};
TimeZone::LocationName location_name{};
u8 is_automatic_correction_enabled{};
u8 type{};
INSERT_PADDING_BYTES(0x2);
SystemClockContext user_context;
SystemClockContext network_context;
s64 user_time;
s64 network_time;
TimeZone::CalendarTime user_calendar_time;
TimeZone::CalendarTime network_calendar_time;
TimeZone::CalendarAdditionalInfo user_calendar_additional_time;
TimeZone::CalendarAdditionalInfo network_calendar_additional_time;
SteadyClockTimePoint steady_clock_time_point;
TimeZone::LocationName location_name;
u8 is_automatic_correction_enabled;
u8 type;
INSERT_PADDING_BYTES_NOINIT(0x2);
static ResultCode GetCurrentTime(s64& current_time,
const SteadyClockTimePoint& steady_clock_time_point,
+11 -11
View File
@@ -45,23 +45,23 @@ static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
struct CalendarAdditionalInfo {
u32 day_of_week{};
u32 day_of_year{};
u32 day_of_week;
u32 day_of_year;
std::array<char, 8> timezone_name;
u32 is_dst{};
s32 gmt_offset{};
u32 is_dst;
s32 gmt_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
/// https://switchbrew.org/wiki/Glue_services#CalendarTime
struct CalendarTime {
s16 year{};
s8 month{};
s8 day{};
s8 hour{};
s8 minute{};
s8 second{};
INSERT_PADDING_BYTES(1);
s16 year;
s8 month;
s8 day;
s8 hour;
s8 minute;
s8 second;
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
+1 -1
View File
@@ -1,11 +1,11 @@
add_executable(tests
common/bit_field.cpp
common/bit_utils.cpp
common/fibers.cpp
common/param_package.cpp
common/ring_buffer.cpp
core/core_timing.cpp
tests.cpp
video_core/buffer_base.cpp
)
create_target_directory_groups(tests)
-23
View File
@@ -1,23 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include <math.h>
#include "common/bit_util.h"
namespace Common {
TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") {
REQUIRE(Common::CountTrailingZeroes32(0) == 32);
REQUIRE(Common::CountTrailingZeroes64(0) == 64);
REQUIRE(Common::CountTrailingZeroes32(9) == 0);
REQUIRE(Common::CountTrailingZeroes32(8) == 3);
REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12);
REQUIRE(Common::CountTrailingZeroes64(9) == 0);
REQUIRE(Common::CountTrailingZeroes64(8) == 3);
REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12);
REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36);
}
} // namespace Common
+473
View File
@@ -0,0 +1,473 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <stdexcept>
#include <unordered_map>
#include <catch2/catch.hpp>
#include "common/alignment.h"
#include "common/common_types.h"
#include "video_core/buffer_cache/buffer_base.h"
namespace {
using VideoCommon::BufferBase;
using Range = std::pair<u64, u64>;
constexpr u64 PAGE = 4096;
constexpr u64 WORD = 4096 * 64;
constexpr VAddr c = 0x1328914000;
class RasterizerInterface {
public:
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
const u64 page_start{addr >> Core::Memory::PAGE_BITS};
const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS};
for (u64 page = page_start; page < page_end; ++page) {
int& value = page_table[page];
value += delta;
if (value < 0) {
throw std::logic_error{"negative page"};
}
if (value == 0) {
page_table.erase(page);
}
}
}
[[nodiscard]] int Count(VAddr addr) const noexcept {
const auto it = page_table.find(addr >> Core::Memory::PAGE_BITS);
return it == page_table.end() ? 0 : it->second;
}
[[nodiscard]] unsigned Count() const noexcept {
unsigned count = 0;
for (const auto [index, value] : page_table) {
count += value;
}
return count;
}
private:
std::unordered_map<u64, int> page_table;
};
} // Anonymous namespace
TEST_CASE("BufferBase: Small buffer", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
REQUIRE(rasterizer.Count() == 0);
buffer.UnmarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == WORD / PAGE);
REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{0, 0});
buffer.MarkRegionAsCpuModified(c + PAGE, 1);
REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{PAGE * 1, PAGE * 2});
}
TEST_CASE("BufferBase: Large buffer", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 32);
buffer.UnmarkRegionAsCpuModified(c, WORD * 32);
buffer.MarkRegionAsCpuModified(c + 4096, WORD * 4);
REQUIRE(buffer.ModifiedCpuRegion(c, WORD + PAGE * 2) == Range{PAGE, WORD + PAGE * 2});
REQUIRE(buffer.ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) == Range{PAGE * 2, PAGE * 8});
REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 4 + PAGE});
REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 4, PAGE) == Range{WORD * 4, WORD * 4 + PAGE});
REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) ==
Range{WORD * 3 + PAGE * 63, WORD * 4});
buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE);
buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) ==
Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 9});
buffer.UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE);
REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) ==
Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 7});
buffer.MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63);
REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 32});
buffer.UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE);
buffer.UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE);
buffer.UnmarkRegionAsCpuModified(c, WORD * 32);
REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{0, 0});
}
TEST_CASE("BufferBase: Rasterizer counting", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, PAGE * 2);
REQUIRE(rasterizer.Count() == 0);
buffer.UnmarkRegionAsCpuModified(c, PAGE);
REQUIRE(rasterizer.Count() == 1);
buffer.MarkRegionAsCpuModified(c, PAGE * 2);
REQUIRE(rasterizer.Count() == 0);
buffer.UnmarkRegionAsCpuModified(c, PAGE);
buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE);
REQUIRE(rasterizer.Count() == 2);
buffer.MarkRegionAsCpuModified(c, PAGE * 2);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Basic range", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.MarkRegionAsCpuModified(c, PAGE);
int num = 0;
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == 0U);
REQUIRE(size == PAGE);
++num;
});
REQUIRE(num == 1U);
}
TEST_CASE("BufferBase: Border upload", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 2);
buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE * 2);
});
}
TEST_CASE("BufferBase: Border upload range", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 2);
buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE * 2);
});
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE);
});
buffer.ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) {
REQUIRE(offset == WORD);
REQUIRE(size == PAGE);
});
}
TEST_CASE("BufferBase: Border upload partial range", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 2);
buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE * 2);
});
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE);
});
buffer.ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) {
REQUIRE(offset == WORD);
REQUIRE(size == PAGE);
});
}
TEST_CASE("BufferBase: Partial word uploads", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, 0x9d000);
int num = 0;
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == 0U);
REQUIRE(size == WORD);
++num;
});
REQUIRE(num == 1);
buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == WORD);
REQUIRE(size == WORD);
++num;
});
REQUIRE(num == 2);
buffer.ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) {
REQUIRE(offset == WORD * 2);
REQUIRE(size == PAGE * 0x1d);
++num;
});
REQUIRE(num == 3);
}
TEST_CASE("BufferBase: Partial page upload", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
int num = 0;
buffer.MarkRegionAsCpuModified(c + PAGE * 2, PAGE);
buffer.MarkRegionAsCpuModified(c + PAGE * 9, PAGE);
buffer.ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 2);
REQUIRE(size == PAGE);
++num;
});
REQUIRE(num == 1);
buffer.ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 9);
REQUIRE(size == PAGE);
++num;
});
REQUIRE(num == 2);
}
TEST_CASE("BufferBase: Partial page upload with multiple words on the right") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 8);
buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
int num = 0;
buffer.ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 13);
REQUIRE(size == WORD * 7 - PAGE * 3);
++num;
});
REQUIRE(num == 1);
buffer.ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) {
REQUIRE(offset == WORD * 7 + PAGE * 10);
REQUIRE(size == PAGE * 3);
++num;
});
REQUIRE(num == 2);
}
TEST_CASE("BufferBase: Partial page upload with multiple words on the left", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 8);
buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7);
int num = 0;
buffer.ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 16);
REQUIRE(size == WORD * 7 - PAGE * 3);
++num;
});
REQUIRE(num == 1);
buffer.ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 13);
REQUIRE(size == PAGE * 3);
++num;
});
REQUIRE(num == 2);
}
TEST_CASE("BufferBase: Partial page upload with multiple words in the middle", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 8);
buffer.UnmarkRegionAsCpuModified(c, WORD * 8);
buffer.MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140);
int num = 0;
buffer.ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 16);
REQUIRE(size == WORD);
++num;
});
REQUIRE(num == 1);
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) {
REQUIRE(offset == PAGE * 13);
REQUIRE(size == PAGE * 3);
++num;
});
REQUIRE(num == 2);
buffer.ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) {
REQUIRE(offset == WORD + PAGE * 16);
REQUIRE(size == PAGE * 73);
++num;
});
REQUIRE(num == 3);
}
TEST_CASE("BufferBase: Empty right bits", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 2048);
buffer.UnmarkRegionAsCpuModified(c, WORD * 2048);
buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2);
buffer.ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) {
REQUIRE(offset == WORD - PAGE);
REQUIRE(size == PAGE * 2);
});
}
TEST_CASE("BufferBase: Out of bound ranges 1", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.MarkRegionAsCpuModified(c, PAGE);
int num = 0;
buffer.ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; });
buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; });
buffer.ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
buffer.ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 1);
buffer.MarkRegionAsCpuModified(c, WORD);
REQUIRE(rasterizer.Count() == 0);
}
TEST_CASE("BufferBase: Out of bound ranges 2", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, 0x22000);
REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x22000, PAGE));
REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x28000, PAGE));
REQUIRE(rasterizer.Count() == 0);
REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100));
REQUIRE(rasterizer.Count() == 1);
REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c - 0x1000, PAGE * 2));
buffer.UnmarkRegionAsCpuModified(c - 0x3000, PAGE * 2);
buffer.UnmarkRegionAsCpuModified(c - 0x2000, PAGE * 2);
REQUIRE(rasterizer.Count() == 2);
}
TEST_CASE("BufferBase: Out of bound ranges 3", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, 0x310720);
buffer.UnmarkRegionAsCpuModified(c, 0x310720);
REQUIRE(rasterizer.Count(c) == 1);
REQUIRE(rasterizer.Count(c + PAGE) == 1);
REQUIRE(rasterizer.Count(c + WORD) == 1);
REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1);
}
TEST_CASE("BufferBase: Sparse regions 1", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.MarkRegionAsCpuModified(c + PAGE * 1, PAGE);
buffer.MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4);
buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable {
static constexpr std::array<u64, 2> offsets{PAGE, PAGE * 3};
static constexpr std::array<u64, 2> sizes{PAGE, PAGE * 4};
REQUIRE(offset == offsets.at(i));
REQUIRE(size == sizes.at(i));
++i;
});
}
TEST_CASE("BufferBase: Sparse regions 2", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, 0x22000);
buffer.UnmarkRegionAsCpuModified(c, 0x22000);
REQUIRE(rasterizer.Count() == 0x22);
buffer.MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE);
buffer.MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE);
buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable {
static constexpr std::array<u64, 2> offsets{PAGE * 0x1B, PAGE * 0x21};
static constexpr std::array<u64, 2> sizes{PAGE, PAGE};
REQUIRE(offset == offsets.at(i));
REQUIRE(size == sizes.at(i));
++i;
});
}
TEST_CASE("BufferBase: Single page modified range", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, PAGE);
REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
buffer.UnmarkRegionAsCpuModified(c, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(c, PAGE));
}
TEST_CASE("BufferBase: Two page modified range", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, PAGE * 2);
REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c, PAGE * 2));
buffer.UnmarkRegionAsCpuModified(c, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(c, PAGE));
}
TEST_CASE("BufferBase: Multi word modified ranges", "[video_core]") {
for (int offset = 0; offset < 4; ++offset) {
const VAddr address = c + WORD * offset;
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, address, WORD * 4);
REQUIRE(buffer.IsRegionCpuModified(address, PAGE));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 48, PAGE));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 56, PAGE));
buffer.UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE);
REQUIRE(buffer.IsRegionCpuModified(address + PAGE, WORD));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE));
REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 33, PAGE));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE * 2));
REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
buffer.UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE);
REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2));
}
}
TEST_CASE("BufferBase: Single page in large buffer", "[video_core]") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 16);
buffer.UnmarkRegionAsCpuModified(c, WORD * 16);
REQUIRE(!buffer.IsRegionCpuModified(c, WORD * 16));
buffer.MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE);
REQUIRE(buffer.IsRegionCpuModified(c, WORD * 16));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 10, WORD * 2));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 11, WORD * 2));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12, WORD * 2));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8));
REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2));
}
TEST_CASE("BufferBase: Out of bounds region query") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 16);
REQUIRE(!buffer.IsRegionCpuModified(c - PAGE, PAGE));
REQUIRE(!buffer.IsRegionCpuModified(c - PAGE * 2, PAGE));
REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + WORD * 16 - PAGE, WORD * 64));
REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, WORD * 64));
}
TEST_CASE("BufferBase: Wrap word regions") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD * 2);
buffer.UnmarkRegionAsCpuModified(c, WORD * 2);
buffer.MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2);
REQUIRE(buffer.IsRegionCpuModified(c, WORD * 2));
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 62, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 64, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 2));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 8));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 60, PAGE * 8));
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16));
buffer.MarkRegionAsCpuModified(c + PAGE * 127, PAGE);
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, PAGE));
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 126, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 126, PAGE * 2));
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 128, WORD * 16));
}
TEST_CASE("BufferBase: Unaligned page region query") {
RasterizerInterface rasterizer;
BufferBase buffer(rasterizer, c, WORD);
buffer.UnmarkRegionAsCpuModified(c, WORD);
buffer.MarkRegionAsCpuModified(c + 4000, 1000);
REQUIRE(buffer.IsRegionCpuModified(c, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1000));
REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1));
}
+3 -2
View File
@@ -1,6 +1,7 @@
add_subdirectory(host_shaders)
add_library(video_core STATIC
buffer_cache/buffer_base.h
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.h
buffer_cache/map_interval.cpp
@@ -135,8 +136,6 @@ add_library(video_core STATIC
renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_master_semaphore.cpp
renderer_vulkan/vk_master_semaphore.h
renderer_vulkan/vk_memory_manager.cpp
renderer_vulkan/vk_memory_manager.h
renderer_vulkan/vk_pipeline_cache.cpp
renderer_vulkan/vk_pipeline_cache.h
renderer_vulkan/vk_query_cache.cpp
@@ -259,6 +258,8 @@ add_library(video_core STATIC
vulkan_common/vulkan_instance.h
vulkan_common/vulkan_library.cpp
vulkan_common/vulkan_library.h
vulkan_common/vulkan_memory_allocator.cpp
vulkan_common/vulkan_memory_allocator.h
vulkan_common/vulkan_surface.cpp
vulkan_common/vulkan_surface.h
vulkan_common/vulkan_wrapper.cpp
+495
View File
@@ -0,0 +1,495 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <bit>
#include <limits>
#include <utility>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "core/memory.h"
namespace VideoCommon {
enum class BufferFlagBits {
Picked = 1 << 0,
};
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
/// Tag for creating null buffers with no storage or size
struct NullBufferParams {};
/**
* Range tracking buffer container.
*
* It keeps track of the modified CPU and GPU ranges on a CPU page granularity, notifying the given
* rasterizer about state changes in the tracking behavior of the buffer.
*
* The buffer size and address is forcefully aligned to CPU page boundaries.
*/
template <class RasterizerInterface>
class BufferBase {
static constexpr u64 PAGES_PER_WORD = 64;
static constexpr u64 BYTES_PER_PAGE = Core::Memory::PAGE_SIZE;
static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
/// Vector tracking modified pages tightly packed with small vector optimization
union WrittenWords {
/// Returns the pointer to the words state
[[nodiscard]] const u64* Pointer(bool is_short) const noexcept {
return is_short ? &stack : heap;
}
/// Returns the pointer to the words state
[[nodiscard]] u64* Pointer(bool is_short) noexcept {
return is_short ? &stack : heap;
}
u64 stack = 0; ///< Small buffers storage
u64* heap; ///< Not-small buffers pointer to the storage
};
struct GpuCpuWords {
explicit GpuCpuWords() = default;
explicit GpuCpuWords(u64 size_bytes_) : size_bytes{size_bytes_} {
if (IsShort()) {
cpu.stack = ~u64{0};
gpu.stack = 0;
} else {
// Share allocation between CPU and GPU pages and set their default values
const size_t num_words = NumWords();
u64* const alloc = new u64[num_words * 2];
cpu.heap = alloc;
gpu.heap = alloc + num_words;
std::fill_n(cpu.heap, num_words, ~u64{0});
std::fill_n(gpu.heap, num_words, 0);
}
// Clean up tailing bits
const u64 last_local_page =
Common::DivCeil(size_bytes % BYTES_PER_WORD, BYTES_PER_PAGE);
const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD;
u64& last_word = cpu.Pointer(IsShort())[NumWords() - 1];
last_word = (last_word << shift) >> shift;
}
~GpuCpuWords() {
Release();
}
GpuCpuWords& operator=(GpuCpuWords&& rhs) noexcept {
Release();
size_bytes = rhs.size_bytes;
cpu = rhs.cpu;
gpu = rhs.gpu;
rhs.cpu.heap = nullptr;
return *this;
}
GpuCpuWords(GpuCpuWords&& rhs) noexcept
: size_bytes{rhs.size_bytes}, cpu{rhs.cpu}, gpu{rhs.gpu} {
rhs.cpu.heap = nullptr;
}
GpuCpuWords& operator=(const GpuCpuWords&) = delete;
GpuCpuWords(const GpuCpuWords&) = delete;
/// Returns true when the buffer fits in the small vector optimization
[[nodiscard]] bool IsShort() const noexcept {
return size_bytes <= BYTES_PER_WORD;
}
/// Returns the number of words of the buffer
[[nodiscard]] size_t NumWords() const noexcept {
return Common::DivCeil(size_bytes, BYTES_PER_WORD);
}
/// Release buffer resources
void Release() {
if (!IsShort()) {
// CPU written words is the base for the heap allocation
delete[] cpu.heap;
}
}
u64 size_bytes = 0;
WrittenWords cpu;
WrittenWords gpu;
};
public:
explicit BufferBase(RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes)
: rasterizer{&rasterizer_}, cpu_addr{Common::AlignDown(cpu_addr_, BYTES_PER_PAGE)},
words(Common::AlignUp(size_bytes + (cpu_addr_ - cpu_addr), BYTES_PER_PAGE)) {}
explicit BufferBase(NullBufferParams) {}
BufferBase& operator=(const BufferBase&) = delete;
BufferBase(const BufferBase&) = delete;
/// Returns the inclusive CPU modified range in a begin end pair
[[nodiscard]] std::pair<u64, u64> ModifiedCpuRegion(VAddr query_cpu_addr,
u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return ModifiedRegion<false>(offset, query_size);
}
/// Returns the inclusive GPU modified range in a begin end pair
[[nodiscard]] std::pair<u64, u64> ModifiedGpuRegion(VAddr query_cpu_addr,
u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return ModifiedRegion<true>(offset, query_size);
}
/// Returns true if a region has been modified from the CPU
[[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return IsRegionModified<false>(offset, query_size);
}
/// Returns true if a region has been modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept {
const u64 offset = query_cpu_addr - cpu_addr;
return IsRegionModified<true>(offset, query_size);
}
/// Mark region as CPU modified, notifying the rasterizer about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
ChangeRegionState<true, true>(words.cpu, dirty_cpu_addr, size);
}
/// Unmark region as CPU modified, notifying the rasterizer about this change
void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) {
ChangeRegionState<false, true>(words.cpu, dirty_cpu_addr, size);
}
/// Mark region as modified from the host GPU
void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
ChangeRegionState<true, false>(words.gpu, dirty_cpu_addr, size);
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept {
ChangeRegionState<false, false>(words.gpu, dirty_cpu_addr, size);
}
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
template <typename Func>
void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
ForEachModifiedRange<false, true>(query_cpu_range, size, func);
}
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
template <typename Func>
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) {
ForEachModifiedRange<true, false>(query_cpu_range, size, func);
}
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
template <typename Func>
void ForEachDownloadRange(Func&& func) {
ForEachModifiedRange<true, false>(cpu_addr, SizeBytes(), func);
}
/// Mark buffer as picked
void Pick() noexcept {
flags |= BufferFlagBits::Picked;
}
/// Unmark buffer as picked
void Unpick() noexcept {
flags &= ~BufferFlagBits::Picked;
}
/// Returns true when vaddr -> vaddr+size is fully contained in the buffer
[[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept {
return addr >= cpu_addr && addr + size <= cpu_addr + SizeBytes();
}
/// Returns true if the buffer has been marked as picked
[[nodiscard]] bool IsPicked() const noexcept {
return True(flags & BufferFlagBits::Picked);
}
/// Returns the base CPU address of the buffer
[[nodiscard]] VAddr CpuAddr() const noexcept {
return cpu_addr;
}
/// Returns the offset relative to the given CPU address
/// @pre IsInBounds returns true
[[nodiscard]] u32 Offset(VAddr other_cpu_addr) const noexcept {
return static_cast<u32>(other_cpu_addr - cpu_addr);
}
/// Returns the size in bytes of the buffer
[[nodiscard]] u64 SizeBytes() const noexcept {
return words.size_bytes;
}
private:
/**
* Change the state of a range of pages
*
* @param written_words Pages to be marked or unmarked as modified
* @param dirty_addr Base address to mark or unmark as modified
* @param size Size in bytes to mark or unmark as modified
*
* @tparam enable True when the bits will be set to one, false for zero
* @tparam notify_rasterizer True when the rasterizer has to be notified about the changes
*/
template <bool enable, bool notify_rasterizer>
void ChangeRegionState(WrittenWords& written_words, u64 dirty_addr,
s64 size) noexcept(!notify_rasterizer) {
const s64 difference = dirty_addr - cpu_addr;
const u64 offset = std::max<s64>(difference, 0);
size += std::min<s64>(difference, 0);
if (offset >= SizeBytes() || size < 0) {
return;
}
u64* const state_words = written_words.Pointer(IsShort());
const u64 offset_end = std::min(offset + size, SizeBytes());
const u64 begin_page_index = offset / BYTES_PER_PAGE;
const u64 begin_word_index = begin_page_index / PAGES_PER_WORD;
const u64 end_page_index = Common::DivCeil(offset_end, BYTES_PER_PAGE);
const u64 end_word_index = Common::DivCeil(end_page_index, PAGES_PER_WORD);
u64 page_index = begin_page_index % PAGES_PER_WORD;
u64 word_index = begin_word_index;
while (word_index < end_word_index) {
const u64 next_word_first_page = (word_index + 1) * PAGES_PER_WORD;
const u64 left_offset =
std::min(next_word_first_page - end_page_index, PAGES_PER_WORD) % PAGES_PER_WORD;
const u64 right_offset = page_index;
u64 bits = ~u64{0};
bits = (bits >> right_offset) << right_offset;
bits = (bits << left_offset) >> left_offset;
if constexpr (notify_rasterizer) {
NotifyRasterizer<!enable>(word_index, state_words[word_index], bits);
}
if constexpr (enable) {
state_words[word_index] |= bits;
} else {
state_words[word_index] &= ~bits;
}
page_index = 0;
++word_index;
}
}
/**
* Notify rasterizer about changes in the CPU tracking state of a word in the buffer
*
* @param word_index Index to the word to notify to the rasterizer
* @param current_bits Current state of the word
* @param new_bits New state of the word
*
* @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages
*/
template <bool add_to_rasterizer>
void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) {
u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits;
VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
while (changed_bits != 0) {
const int empty_bits = std::countr_zero(changed_bits);
addr += empty_bits * BYTES_PER_PAGE;
changed_bits >>= empty_bits;
const u32 continuous_bits = std::countr_one(changed_bits);
const u64 size = continuous_bits * BYTES_PER_PAGE;
const VAddr begin_addr = addr;
addr += size;
changed_bits = continuous_bits < PAGES_PER_WORD ? (changed_bits >> continuous_bits) : 0;
rasterizer->UpdatePagesCachedCount(begin_addr, size, add_to_rasterizer ? 1 : -1);
}
}
/**
* Loop over each page in the given range, turn off those bits and notify the rasterizer if
* needed. Call the given function on each turned off range.
*
* @param query_cpu_range Base CPU address to loop over
* @param size Size in bytes of the CPU range to loop over
* @param func Function to call for each turned off region
*
* @tparam gpu True for host GPU pages, false for CPU pages
* @tparam notify_rasterizer True when the rasterizer should be notified about state changes
*/
template <bool gpu, bool notify_rasterizer, typename Func>
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
const s64 difference = query_cpu_range - cpu_addr;
const u64 query_begin = std::max<s64>(difference, 0);
size += std::min<s64>(difference, 0);
if (query_begin >= SizeBytes() || size < 0) {
return;
}
const u64* const cpu_words = words.cpu.Pointer(IsShort());
const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
u64* const words_end = state_words + Common::DivCeil(query_end, BYTES_PER_WORD);
const auto modified = [](u64 word) { return word != 0; };
const auto first_modified_word = std::find_if(words_begin, words_end, modified);
if (first_modified_word == words_end) {
// Exit early when the buffer is not modified
return;
}
const auto last_modified_word = std::find_if_not(first_modified_word, words_end, modified);
const u64 word_index_begin = std::distance(state_words, first_modified_word);
const u64 word_index_end = std::distance(state_words, last_modified_word);
const unsigned local_page_begin = std::countr_zero(*first_modified_word);
const unsigned local_page_end = PAGES_PER_WORD - std::countl_zero(last_modified_word[-1]);
const u64 word_page_begin = word_index_begin * PAGES_PER_WORD;
const u64 word_page_end = (word_index_end - 1) * PAGES_PER_WORD;
const u64 query_page_begin = query_begin / BYTES_PER_PAGE;
const u64 query_page_end = Common::DivCeil(query_end, BYTES_PER_PAGE);
const u64 page_index_begin = std::max(word_page_begin + local_page_begin, query_page_begin);
const u64 page_index_end = std::min(word_page_end + local_page_end, query_page_end);
const u64 first_word_page_begin = page_index_begin % PAGES_PER_WORD;
const u64 last_word_page_end = (page_index_end - 1) % PAGES_PER_WORD + 1;
u64 page_begin = first_word_page_begin;
u64 current_base = 0;
u64 current_size = 0;
bool on_going = false;
for (u64 word_index = word_index_begin; word_index < word_index_end; ++word_index) {
const bool is_last_word = word_index + 1 == word_index_end;
const u64 page_end = is_last_word ? last_word_page_end : PAGES_PER_WORD;
const u64 right_offset = page_begin;
const u64 left_offset = PAGES_PER_WORD - page_end;
u64 bits = ~u64{0};
bits = (bits >> right_offset) << right_offset;
bits = (bits << left_offset) >> left_offset;
const u64 current_word = state_words[word_index] & bits;
state_words[word_index] &= ~bits;
// Exclude CPU modified pages when visiting GPU pages
const u64 word = current_word & ~(gpu ? cpu_words[word_index] : 0);
if constexpr (notify_rasterizer) {
NotifyRasterizer<true>(word_index, word, ~u64{0});
}
u64 page = page_begin;
page_begin = 0;
while (page < page_end) {
const int empty_bits = std::countr_zero(word >> page);
if (on_going && empty_bits != 0) {
InvokeModifiedRange(func, current_size, current_base);
current_size = 0;
on_going = false;
}
page += empty_bits;
const int continuous_bits = std::countr_one(word >> page);
if (!on_going && continuous_bits != 0) {
current_base = word_index * PAGES_PER_WORD + page;
on_going = true;
}
current_size += continuous_bits;
page += continuous_bits;
}
}
if (on_going && current_size > 0) {
InvokeModifiedRange(func, current_size, current_base);
}
}
template <typename Func>
void InvokeModifiedRange(Func&& func, u64 current_size, u64 current_base) {
const u64 current_size_bytes = current_size * BYTES_PER_PAGE;
const u64 offset_begin = current_base * BYTES_PER_PAGE;
const u64 offset_end = std::min(offset_begin + current_size_bytes, SizeBytes());
func(offset_begin, offset_end - offset_begin);
}
/**
* Returns true when a region has been modified
*
* @param offset Offset in bytes from the start of the buffer
* @param size Size in bytes of the region to query for modifications
*/
template <bool gpu>
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
const u64* const cpu_words = words.cpu.Pointer(IsShort());
const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
const u64 word_end = std::min(word_begin + num_query_words, NumWords());
const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0);
if (word == 0) {
continue;
}
const u64 page_end = std::min((word_index + 1) * PAGES_PER_WORD, page_limit);
const u64 local_page_end = page_end % PAGES_PER_WORD;
const u64 page_end_shift = (PAGES_PER_WORD - local_page_end) % PAGES_PER_WORD;
if (((word >> page_index) << page_index) << page_end_shift != 0) {
return true;
}
}
return false;
}
/**
* Returns a begin end pair with the inclusive modified region
*
* @param offset Offset in bytes from the start of the buffer
* @param size Size in bytes of the region to query for modifications
*
* @tparam gpu True to query GPU modified pages, false for CPU pages
*/
template <bool gpu>
[[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
const u64* const cpu_words = words.cpu.Pointer(IsShort());
const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort());
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
const u64 word_end = std::min(word_begin + num_query_words, NumWords());
const u64 page_base = offset / BYTES_PER_PAGE;
const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0);
if (word == 0) {
continue;
}
const u64 local_page_begin = std::countr_zero(word);
const u64 local_page_end = PAGES_PER_WORD - std::countl_zero(word);
const u64 page_index = word_index * PAGES_PER_WORD;
const u64 page_begin = std::max(page_index + local_page_begin, page_base);
const u64 page_end = std::min(page_index + local_page_end, page_limit);
begin = std::min(begin, page_begin);
end = std::max(end, page_end);
}
static constexpr std::pair<u64, u64> EMPTY{0, 0};
return begin < end ? std::make_pair(begin * BYTES_PER_PAGE, end * BYTES_PER_PAGE) : EMPTY;
}
/// Returns the number of words of the buffer
[[nodiscard]] size_t NumWords() const noexcept {
return words.NumWords();
}
/// Returns true when the buffer fits in the small vector optimization
[[nodiscard]] bool IsShort() const noexcept {
return words.IsShort();
}
RasterizerInterface* rasterizer = nullptr;
VAddr cpu_addr = 0;
GpuCpuWords words;
BufferFlagBits flags{};
};
} // namespace VideoCommon
+2 -2
View File
@@ -18,10 +18,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#include <bit>
#include "command_classes/host1x.h"
#include "command_classes/nvdec.h"
#include "command_classes/vic.h"
#include "common/bit_util.h"
#include "video_core/cdma_pusher.h"
#include "video_core/command_classes/nvdec_common.h"
#include "video_core/engines/maxwell_3d.h"
@@ -56,7 +56,7 @@ void CDmaPusher::Step() {
for (const u32 value : values) {
if (mask != 0) {
const u32 lbs = Common::CountTrailingZeroes32(mask);
const auto lbs = static_cast<u32>(std::countr_zero(mask));
mask &= ~(1U << lbs);
ExecuteCommand(static_cast<u32>(offset + lbs), value);
continue;
+1 -1
View File
@@ -126,7 +126,7 @@ private:
s32 count{};
s32 offset{};
s32 mask{};
u32 mask{};
bool incrementing{};
// Queue of command lists to be processed
@@ -19,7 +19,7 @@
//
#include <array>
#include "common/bit_util.h"
#include <bit>
#include "video_core/command_classes/codecs/h264.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -266,7 +266,7 @@ void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
}
void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
const s32 size = 32 - Common::CountLeadingZeroes32(static_cast<s32>(value + 1));
const s32 size = 32 - std::countl_zero(value + 1);
WriteBits(1, size);
value -= (1U << (size - 1)) - 1;
+15 -15
View File
@@ -171,30 +171,30 @@ public:
static constexpr std::size_t NUM_REGS = 0x258;
struct {
u32 object;
INSERT_UNION_PADDING_WORDS(0x3F);
INSERT_PADDING_WORDS_NOINIT(0x3F);
u32 no_operation;
NotifyType notify;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
u32 wait_for_idle;
INSERT_UNION_PADDING_WORDS(0xB);
INSERT_PADDING_WORDS_NOINIT(0xB);
u32 pm_trigger;
INSERT_UNION_PADDING_WORDS(0xF);
INSERT_PADDING_WORDS_NOINIT(0xF);
u32 context_dma_notify;
u32 dst_context_dma;
u32 src_context_dma;
u32 semaphore_context_dma;
INSERT_UNION_PADDING_WORDS(0x1C);
INSERT_PADDING_WORDS_NOINIT(0x1C);
Surface dst;
CpuIndexWrap pixels_from_cpu_index_wrap;
u32 kind2d_check_enable;
Surface src;
SectorPromotion pixels_from_memory_sector_promotion;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
NumTpcs num_tpcs;
u32 render_enable_addr_upper;
u32 render_enable_addr_lower;
RenderEnableMode render_enable_mode;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
u32 clip_x0;
u32 clip_y0;
u32 clip_width;
@@ -212,7 +212,7 @@ public:
BitField<8, 6, u32> y;
} pattern_offset;
BitField<0, 2, PatternSelect> pattern_select;
INSERT_UNION_PADDING_WORDS(0xC);
INSERT_PADDING_WORDS_NOINIT(0xC);
struct {
BitField<0, 3, MonochromePatternColorFormat> color_format;
BitField<0, 1, MonochromePatternFormat> format;
@@ -227,15 +227,15 @@ public:
std::array<u32, 0x20> X1R5G5B5;
std::array<u32, 0x10> Y8;
} color_pattern;
INSERT_UNION_PADDING_WORDS(0x10);
INSERT_PADDING_WORDS_NOINIT(0x10);
struct {
u32 prim_mode;
u32 prim_color_format;
u32 prim_color;
u32 line_tie_break_bits;
INSERT_UNION_PADDING_WORDS(0x14);
INSERT_PADDING_WORDS_NOINIT(0x14);
u32 prim_point_xy;
INSERT_UNION_PADDING_WORDS(0x7);
INSERT_PADDING_WORDS_NOINIT(0x7);
std::array<Point, 0x40> prim_point;
} render_solid;
struct {
@@ -247,7 +247,7 @@ public:
u32 color0;
u32 color1;
u32 mono_opacity;
INSERT_UNION_PADDING_WORDS(0x6);
INSERT_PADDING_WORDS_NOINIT(0x6);
u32 src_width;
u32 src_height;
u32 dx_du_frac;
@@ -260,9 +260,9 @@ public:
u32 dst_y0_int;
u32 data;
} pixels_from_cpu;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
u32 big_endian_control;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
struct {
BitField<0, 3, u32> block_shape;
BitField<0, 5, u32> corral_size;
@@ -271,7 +271,7 @@ public:
BitField<0, 1, Origin> origin;
BitField<4, 1, Filter> filter;
} sample_mode;
INSERT_UNION_PADDING_WORDS(0x8);
INSERT_PADDING_WORDS_NOINIT(0x8);
s32 dst_x0;
s32 dst_y0;
s32 dst_width;
+8 -8
View File
@@ -55,7 +55,7 @@ public:
union {
struct {
INSERT_UNION_PADDING_WORDS(0x60);
INSERT_PADDING_WORDS_NOINIT(0x60);
Upload::Registers upload;
@@ -67,7 +67,7 @@ public:
u32 data_upload;
INSERT_UNION_PADDING_WORDS(0x3F);
INSERT_PADDING_WORDS_NOINIT(0x3F);
struct {
u32 address;
@@ -76,11 +76,11 @@ public:
}
} launch_desc_loc;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 launch;
INSERT_UNION_PADDING_WORDS(0x4A7);
INSERT_PADDING_WORDS_NOINIT(0x4A7);
struct {
u32 address_high;
@@ -92,7 +92,7 @@ public:
}
} tsc;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
struct {
u32 address_high;
@@ -104,7 +104,7 @@ public:
}
} tic;
INSERT_UNION_PADDING_WORDS(0x22);
INSERT_PADDING_WORDS_NOINIT(0x22);
struct {
u32 address_high;
@@ -115,11 +115,11 @@ public:
}
} code_loc;
INSERT_UNION_PADDING_WORDS(0x3FE);
INSERT_PADDING_WORDS_NOINIT(0x3FE);
u32 tex_cb_index;
INSERT_UNION_PADDING_WORDS(0x374);
INSERT_PADDING_WORDS_NOINIT(0x374);
};
std::array<u32, NUM_REGS> reg_array;
};
+2 -2
View File
@@ -50,7 +50,7 @@ public:
union {
struct {
INSERT_UNION_PADDING_WORDS(0x60);
INSERT_PADDING_WORDS_NOINIT(0x60);
Upload::Registers upload;
@@ -62,7 +62,7 @@ public:
u32 data;
INSERT_UNION_PADDING_WORDS(0x11);
INSERT_PADDING_WORDS_NOINIT(0x11);
};
std::array<u32, NUM_REGS> reg_array;
};
+75 -75
View File
@@ -536,7 +536,7 @@ public:
Equation equation_a;
Factor factor_source_a;
Factor factor_dest_a;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
};
enum class TessellationPrimitive : u32 {
@@ -608,7 +608,7 @@ public:
};
u32 layer_stride;
u32 base_layer;
INSERT_UNION_PADDING_WORDS(7);
INSERT_PADDING_WORDS_NOINIT(7);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
@@ -640,7 +640,7 @@ public:
BitField<8, 3, ViewportSwizzle> z;
BitField<12, 3, ViewportSwizzle> w;
} swizzle;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
Common::Rectangle<f32> GetRect() const {
return {
@@ -700,7 +700,7 @@ public:
u32 address_low;
s32 buffer_size;
s32 buffer_offset;
INSERT_UNION_PADDING_WORDS(3);
INSERT_PADDING_WORDS_NOINIT(3);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
@@ -713,7 +713,7 @@ public:
u32 stream;
u32 varying_count;
u32 stride;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(TransformFeedbackLayout) == 16);
@@ -731,7 +731,7 @@ public:
union {
struct {
INSERT_UNION_PADDING_WORDS(0x44);
INSERT_PADDING_WORDS_NOINIT(0x44);
u32 wait_for_idle;
@@ -744,7 +744,7 @@ public:
ShadowRamControl shadow_ram_control;
INSERT_UNION_PADDING_WORDS(0x16);
INSERT_PADDING_WORDS_NOINIT(0x16);
Upload::Registers upload;
struct {
@@ -755,11 +755,11 @@ public:
u32 data_upload;
INSERT_UNION_PADDING_WORDS(0x16);
INSERT_PADDING_WORDS_NOINIT(0x16);
u32 force_early_fragment_tests;
INSERT_UNION_PADDING_WORDS(0x2D);
INSERT_PADDING_WORDS_NOINIT(0x2D);
struct {
union {
@@ -769,7 +769,7 @@ public:
};
} sync_info;
INSERT_UNION_PADDING_WORDS(0x15);
INSERT_PADDING_WORDS_NOINIT(0x15);
union {
BitField<0, 2, TessellationPrimitive> prim;
@@ -781,21 +781,21 @@ public:
std::array<f32, 4> tess_level_outer;
std::array<f32, 2> tess_level_inner;
INSERT_UNION_PADDING_WORDS(0x10);
INSERT_PADDING_WORDS_NOINIT(0x10);
u32 rasterize_enable;
std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
INSERT_UNION_PADDING_WORDS(0xC0);
INSERT_PADDING_WORDS_NOINIT(0xC0);
std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 tfb_enabled;
INSERT_UNION_PADDING_WORDS(0x2E);
INSERT_PADDING_WORDS_NOINIT(0x2E);
std::array<RenderTargetConfig, NumRenderTargets> rt;
@@ -803,7 +803,7 @@ public:
std::array<ViewPort, NumViewports> viewports;
INSERT_UNION_PADDING_WORDS(0x1D);
INSERT_PADDING_WORDS_NOINIT(0x1D);
struct {
u32 first;
@@ -815,16 +815,16 @@ public:
float clear_color[4];
float clear_depth;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
s32 clear_stencil;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
PolygonMode polygon_mode_front;
PolygonMode polygon_mode_back;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
u32 polygon_offset_point_enable;
u32 polygon_offset_line_enable;
@@ -832,47 +832,47 @@ public:
u32 patch_vertices;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
u32 fragment_barrier;
INSERT_UNION_PADDING_WORDS(0x7);
INSERT_PADDING_WORDS_NOINIT(0x7);
std::array<ScissorTest, NumViewports> scissor_test;
INSERT_UNION_PADDING_WORDS(0x15);
INSERT_PADDING_WORDS_NOINIT(0x15);
s32 stencil_back_func_ref;
u32 stencil_back_mask;
u32 stencil_back_func_mask;
INSERT_UNION_PADDING_WORDS(0x5);
INSERT_PADDING_WORDS_NOINIT(0x5);
u32 invalidate_texture_data_cache;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 tiled_cache_barrier;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
u32 color_mask_common;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
f32 depth_bounds[2];
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
u32 rt_separate_frag_data;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 multisample_raster_enable;
u32 multisample_raster_samples;
std::array<u32, 4> multisample_sample_mask;
INSERT_UNION_PADDING_WORDS(0x5);
INSERT_PADDING_WORDS_NOINIT(0x5);
struct {
u32 address_high;
@@ -898,7 +898,7 @@ public:
};
} render_area;
INSERT_UNION_PADDING_WORDS(0x3F);
INSERT_PADDING_WORDS_NOINIT(0x3F);
union {
BitField<0, 4, u32> stencil;
@@ -907,24 +907,24 @@ public:
BitField<12, 4, u32> viewport;
} clear_flags;
INSERT_UNION_PADDING_WORDS(0x10);
INSERT_PADDING_WORDS_NOINIT(0x10);
u32 fill_rectangle;
INSERT_UNION_PADDING_WORDS(0x8);
INSERT_PADDING_WORDS_NOINIT(0x8);
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
std::array<MsaaSampleLocation, 4> multisample_sample_locations;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
union {
BitField<0, 1, u32> enable;
BitField<4, 3, u32> target;
} multisample_coverage_to_color;
INSERT_UNION_PADDING_WORDS(0x8);
INSERT_PADDING_WORDS_NOINIT(0x8);
struct {
union {
@@ -947,7 +947,7 @@ public:
}
} rt_control;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
u32 zeta_width;
u32 zeta_height;
@@ -958,11 +958,11 @@ public:
SamplerIndex sampler_index;
INSERT_UNION_PADDING_WORDS(0x25);
INSERT_PADDING_WORDS_NOINIT(0x25);
u32 depth_test_enable;
INSERT_UNION_PADDING_WORDS(0x5);
INSERT_PADDING_WORDS_NOINIT(0x5);
u32 independent_blend_enable;
@@ -970,7 +970,7 @@ public:
u32 alpha_test_enabled;
INSERT_UNION_PADDING_WORDS(0x6);
INSERT_PADDING_WORDS_NOINIT(0x6);
u32 d3d_cull_mode;
@@ -985,7 +985,7 @@ public:
float a;
} blend_color;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
struct {
u32 separate_alpha;
@@ -994,7 +994,7 @@ public:
Blend::Factor factor_dest_rgb;
Blend::Equation equation_a;
Blend::Factor factor_source_a;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
Blend::Factor factor_dest_a;
u32 enable_common;
@@ -1010,7 +1010,7 @@ public:
u32 stencil_front_func_mask;
u32 stencil_front_mask;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
u32 frag_color_clamp;
@@ -1022,17 +1022,17 @@ public:
float line_width_smooth;
float line_width_aliased;
INSERT_UNION_PADDING_WORDS(0x1B);
INSERT_PADDING_WORDS_NOINIT(0x1B);
u32 invalidate_sampler_cache_no_wfi;
u32 invalidate_texture_header_cache_no_wfi;
INSERT_UNION_PADDING_WORDS(0x2);
INSERT_PADDING_WORDS_NOINIT(0x2);
u32 vb_element_base;
u32 vb_base_instance;
INSERT_UNION_PADDING_WORDS(0x35);
INSERT_PADDING_WORDS_NOINIT(0x35);
u32 clip_distance_enabled;
@@ -1040,11 +1040,11 @@ public:
float point_size;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 point_sprite_enable;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
CounterReset counter_reset;
@@ -1057,7 +1057,7 @@ public:
BitField<4, 1, u32> alpha_to_one;
} multisample_control;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
struct {
u32 address_high;
@@ -1081,7 +1081,7 @@ public:
}
} tsc;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
float polygon_offset_factor;
@@ -1098,7 +1098,7 @@ public:
}
} tic;
INSERT_UNION_PADDING_WORDS(0x5);
INSERT_PADDING_WORDS_NOINIT(0x5);
u32 stencil_two_side_enable;
StencilOp stencil_back_op_fail;
@@ -1106,17 +1106,17 @@ public:
StencilOp stencil_back_op_zpass;
ComparisonOp stencil_back_func_func;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
u32 framebuffer_srgb;
float polygon_offset_units;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
Tegra::Texture::MsaaMode multisample_mode;
INSERT_UNION_PADDING_WORDS(0xC);
INSERT_PADDING_WORDS_NOINIT(0xC);
union {
BitField<2, 1, u32> coord_origin;
@@ -1132,7 +1132,7 @@ public:
(static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low);
}
} code_address;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
struct {
u32 vertex_end_gl;
@@ -1144,14 +1144,14 @@ public:
};
} draw;
INSERT_UNION_PADDING_WORDS(0xA);
INSERT_PADDING_WORDS_NOINIT(0xA);
struct {
u32 enabled;
u32 index;
} primitive_restart;
INSERT_UNION_PADDING_WORDS(0x5F);
INSERT_PADDING_WORDS_NOINIT(0x5F);
struct {
u32 start_addr_high;
@@ -1192,9 +1192,9 @@ public:
}
} index_array;
INSERT_UNION_PADDING_WORDS(0x7);
INSERT_PADDING_WORDS_NOINIT(0x7);
INSERT_UNION_PADDING_WORDS(0x1F);
INSERT_PADDING_WORDS_NOINIT(0x1F);
float polygon_offset_clamp;
@@ -1208,14 +1208,14 @@ public:
}
} instanced_arrays;
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
union {
BitField<0, 1, u32> enable;
BitField<4, 8, u32> unk4;
} vp_point_size;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
u32 cull_test_enabled;
FrontFace front_face;
@@ -1223,11 +1223,11 @@ public:
u32 pixel_center_integer;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
u32 viewport_transform_enabled;
INSERT_UNION_PADDING_WORDS(0x3);
INSERT_PADDING_WORDS_NOINIT(0x3);
union {
BitField<0, 1, u32> depth_range_0_1;
@@ -1236,18 +1236,18 @@ public:
BitField<11, 1, u32> depth_clamp_disabled;
} view_volume_clip_control;
INSERT_UNION_PADDING_WORDS(0x1F);
INSERT_PADDING_WORDS_NOINIT(0x1F);
u32 depth_bounds_enable;
INSERT_UNION_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
struct {
u32 enable;
LogicOperation operation;
} logic_op;
INSERT_UNION_PADDING_WORDS(0x1);
INSERT_PADDING_WORDS_NOINIT(0x1);
union {
u32 raw;
@@ -1260,9 +1260,9 @@ public:
BitField<6, 4, u32> RT;
BitField<10, 11, u32> layer;
} clear_buffers;
INSERT_UNION_PADDING_WORDS(0xB);
INSERT_PADDING_WORDS_NOINIT(0xB);
std::array<ColorMask, NumRenderTargets> color_mask;
INSERT_UNION_PADDING_WORDS(0x38);
INSERT_PADDING_WORDS_NOINIT(0x38);
struct {
u32 query_address_high;
@@ -1284,7 +1284,7 @@ public:
}
} query;
INSERT_UNION_PADDING_WORDS(0x3C);
INSERT_PADDING_WORDS_NOINIT(0x3C);
struct {
union {
@@ -1325,10 +1325,10 @@ public:
BitField<4, 4, ShaderProgram> program;
};
u32 offset;
INSERT_UNION_PADDING_WORDS(14);
INSERT_PADDING_WORDS_NOINIT(14);
} shader_config[MaxShaderProgram];
INSERT_UNION_PADDING_WORDS(0x60);
INSERT_PADDING_WORDS_NOINIT(0x60);
u32 firmware[0x20];
@@ -1345,7 +1345,7 @@ public:
}
} const_buffer;
INSERT_UNION_PADDING_WORDS(0x10);
INSERT_PADDING_WORDS_NOINIT(0x10);
struct {
union {
@@ -1353,18 +1353,18 @@ public:
BitField<0, 1, u32> valid;
BitField<4, 5, u32> index;
};
INSERT_UNION_PADDING_WORDS(7);
INSERT_PADDING_WORDS_NOINIT(7);
} cb_bind[MaxShaderStage];
INSERT_UNION_PADDING_WORDS(0x56);
INSERT_PADDING_WORDS_NOINIT(0x56);
u32 tex_cb_index;
INSERT_UNION_PADDING_WORDS(0x7D);
INSERT_PADDING_WORDS_NOINIT(0x7D);
std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
INSERT_UNION_PADDING_WORDS(0x298);
INSERT_PADDING_WORDS_NOINIT(0x298);
struct {
/// Compressed address of a buffer that holds information about bound SSBOs.
@@ -1376,14 +1376,14 @@ public:
}
} ssbo_info;
INSERT_UNION_PADDING_WORDS(0x11);
INSERT_PADDING_WORDS_NOINIT(0x11);
struct {
u32 address[MaxShaderStage];
u32 size[MaxShaderStage];
} tex_info_buffers;
INSERT_UNION_PADDING_WORDS(0xCC);
INSERT_PADDING_WORDS_NOINIT(0xCC);
};
std::array<u32, NUM_REGS> reg_array;
};
+19 -19
View File
@@ -68,10 +68,10 @@ struct Header {
union {
struct {
INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB
INSERT_UNION_PADDING_BYTES(16); // ImapGenericVector[32]
INSERT_UNION_PADDING_BYTES(2); // ImapColor
INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA
INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB
INSERT_PADDING_BYTES_NOINIT(16); // ImapGenericVector[32]
INSERT_PADDING_BYTES_NOINIT(2); // ImapColor
union {
BitField<0, 8, u16> clip_distances;
BitField<8, 1, u16> point_sprite_s;
@@ -82,20 +82,20 @@ struct Header {
BitField<14, 1, u16> instance_id;
BitField<15, 1, u16> vertex_id;
};
INSERT_UNION_PADDING_BYTES(5); // ImapFixedFncTexture[10]
INSERT_UNION_PADDING_BYTES(1); // ImapReserved
INSERT_UNION_PADDING_BYTES(3); // OmapSystemValuesA
INSERT_UNION_PADDING_BYTES(1); // OmapSystemValuesB
INSERT_UNION_PADDING_BYTES(16); // OmapGenericVector[32]
INSERT_UNION_PADDING_BYTES(2); // OmapColor
INSERT_UNION_PADDING_BYTES(2); // OmapSystemValuesC
INSERT_UNION_PADDING_BYTES(5); // OmapFixedFncTexture[10]
INSERT_UNION_PADDING_BYTES(1); // OmapReserved
INSERT_PADDING_BYTES_NOINIT(5); // ImapFixedFncTexture[10]
INSERT_PADDING_BYTES_NOINIT(1); // ImapReserved
INSERT_PADDING_BYTES_NOINIT(3); // OmapSystemValuesA
INSERT_PADDING_BYTES_NOINIT(1); // OmapSystemValuesB
INSERT_PADDING_BYTES_NOINIT(16); // OmapGenericVector[32]
INSERT_PADDING_BYTES_NOINIT(2); // OmapColor
INSERT_PADDING_BYTES_NOINIT(2); // OmapSystemValuesC
INSERT_PADDING_BYTES_NOINIT(5); // OmapFixedFncTexture[10]
INSERT_PADDING_BYTES_NOINIT(1); // OmapReserved
} vtg;
struct {
INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB
INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA
INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB
union {
BitField<0, 2, PixelImap> x;
@@ -105,10 +105,10 @@ struct Header {
u8 raw;
} imap_generic_vector[32];
INSERT_UNION_PADDING_BYTES(2); // ImapColor
INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC
INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10]
INSERT_UNION_PADDING_BYTES(2); // ImapReserved
INSERT_PADDING_BYTES_NOINIT(2); // ImapColor
INSERT_PADDING_BYTES_NOINIT(2); // ImapSystemValuesC
INSERT_PADDING_BYTES_NOINIT(10); // ImapFixedFncTexture[10]
INSERT_PADDING_BYTES_NOINIT(2); // ImapReserved
struct {
u32 target;
+4 -4
View File
@@ -270,7 +270,7 @@ public:
union {
struct {
INSERT_UNION_PADDING_WORDS(0x4);
INSERT_PADDING_WORDS_NOINIT(0x4);
struct {
u32 address_high;
u32 address_low;
@@ -283,18 +283,18 @@ public:
u32 semaphore_sequence;
u32 semaphore_trigger;
INSERT_UNION_PADDING_WORDS(0xC);
INSERT_PADDING_WORDS_NOINIT(0xC);
// The pusher and the puller share the reference counter, the pusher only has read
// access
u32 reference_count;
INSERT_UNION_PADDING_WORDS(0x5);
INSERT_PADDING_WORDS_NOINIT(0x5);
u32 semaphore_acquire;
u32 semaphore_release;
u32 fence_value;
FenceAction fence_action;
INSERT_UNION_PADDING_WORDS(0xE2);
INSERT_PADDING_WORDS_NOINIT(0xE2);
// Puller state
u32 acquire_mode;
@@ -246,6 +246,7 @@ Device::Device()
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
use_driver_cache = is_nvidia;
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
@@ -120,6 +120,10 @@ public:
return use_asynchronous_shaders;
}
bool UseDriverCache() const {
return use_driver_cache;
}
private:
static bool TestVariableAoffi();
static bool TestPreciseBug();
@@ -147,6 +151,7 @@ private:
bool has_debugging_tool_attached{};
bool use_assembly_shaders{};
bool use_asynchronous_shaders{};
bool use_driver_cache{};
};
} // namespace OpenGL
@@ -159,6 +159,10 @@ std::unordered_set<GLenum> GetSupportedFormats() {
ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier,
const ShaderIR& ir, const Registry& registry, bool hint_retrievable) {
if (device.UseDriverCache()) {
// Ignore hint retrievable if we are using the driver cache
hint_retrievable = false;
}
const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
LOG_INFO(Render_OpenGL, "{}", shader_id);
@@ -336,7 +340,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
}
std::vector<ShaderDiskCachePrecompiled> gl_cache;
if (!device.UseAssemblyShaders()) {
if (!device.UseAssemblyShaders() && !device.UseDriverCache()) {
// Only load precompiled cache when we are not using assembly shaders
gl_cache = disk_cache.LoadPrecompiled();
}
@@ -356,8 +360,7 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
std::atomic_bool gl_cache_failed = false;
const auto find_precompiled = [&gl_cache](u64 id) {
return std::find_if(gl_cache.begin(), gl_cache.end(),
[id](const auto& entry) { return entry.unique_identifier == id; });
return std::ranges::find(gl_cache, id, &ShaderDiskCachePrecompiled::unique_identifier);
};
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
@@ -432,8 +435,8 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
return;
}
if (device.UseAssemblyShaders()) {
// Don't store precompiled binaries for assembly shaders.
if (device.UseAssemblyShaders() || device.UseDriverCache()) {
// Don't store precompiled binaries for assembly shaders or when using the driver cache
return;
}
@@ -23,7 +23,6 @@
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -32,6 +31,7 @@
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -137,7 +137,7 @@ bool RendererVulkan::Init() try {
InitializeDevice();
Report();
memory_manager = std::make_unique<VKMemoryManager>(*device);
memory_allocator = std::make_unique<MemoryAllocator>(*device);
state_tracker = std::make_unique<StateTracker>(gpu);
@@ -149,11 +149,11 @@ bool RendererVulkan::Init() try {
rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
cpu_memory, screen_info, *device,
*memory_manager, *state_tracker, *scheduler);
*memory_allocator, *state_tracker, *scheduler);
blit_screen =
std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
*memory_manager, *swapchain, *scheduler, screen_info);
*memory_allocator, *swapchain, *scheduler, screen_info);
return true;
} catch (const vk::Exception& exception) {
@@ -172,7 +172,7 @@ void RendererVulkan::ShutDown() {
blit_screen.reset();
scheduler.reset();
swapchain.reset();
memory_manager.reset();
memory_allocator.reset();
device.reset();
}
@@ -29,8 +29,8 @@ namespace Vulkan {
class Device;
class StateTracker;
class MemoryAllocator;
class VKBlitScreen;
class VKMemoryManager;
class VKSwapchain;
class VKScheduler;
@@ -75,7 +75,7 @@ private:
vk::DebugUtilsMessenger debug_callback;
std::unique_ptr<Device> device;
std::unique_ptr<VKMemoryManager> memory_manager;
std::unique_ptr<MemoryAllocator> memory_allocator;
std::unique_ptr<StateTracker> state_tracker;
std::unique_ptr<VKScheduler> scheduler;
std::unique_ptr<VKSwapchain> swapchain;
@@ -22,13 +22,13 @@
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -115,10 +115,10 @@ struct VKBlitScreen::BufferData {
VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
Core::Frontend::EmuWindow& render_window_,
VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
VKMemoryManager& memory_manager_, VKSwapchain& swapchain_,
MemoryAllocator& memory_allocator_, VKSwapchain& swapchain_,
VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
: cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_},
device{device_}, memory_allocator{memory_allocator_}, swapchain{swapchain_},
scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
resource_ticks.resize(image_count);
@@ -150,8 +150,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
SetUniformData(data, framebuffer);
SetVertexData(data, framebuffer);
auto map = buffer_commit->Map();
std::memcpy(map.Address(), &data, sizeof(data));
const std::span<u8> map = buffer_commit.Map();
std::memcpy(map.data(), &data, sizeof(data));
if (!use_accelerated) {
const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
@@ -165,8 +165,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
constexpr u32 block_height_log2 = 4;
const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
Tegra::Texture::UnswizzleTexture(
std::span(map.Address() + image_offset, size_bytes), std::span(host_ptr, size_bytes),
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
map.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), bytes_per_pixel,
framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
const VkBufferImageCopy copy{
.bufferOffset = image_offset,
@@ -224,8 +224,6 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
});
}
map.Release();
scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index],
descriptor_set = descriptor_sets[image_index], buffer = *buffer,
size = swapchain.GetSize(), pipeline = *pipeline,
@@ -642,7 +640,7 @@ void VKBlitScreen::ReleaseRawImages() {
raw_images.clear();
raw_buffer_commits.clear();
buffer.reset();
buffer_commit.reset();
buffer_commit = MemoryCommit{};
}
void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
@@ -659,7 +657,7 @@ void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuff
};
buffer = device.GetLogical().CreateBuffer(ci);
buffer_commit = memory_manager.Commit(buffer, true);
buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload);
}
void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
@@ -690,7 +688,7 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
});
raw_buffer_commits[i] = memory_manager.Commit(raw_images[i], false);
raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal);
raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
@@ -6,7 +6,7 @@
#include <memory>
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Core {
@@ -43,7 +43,7 @@ public:
explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
Core::Frontend::EmuWindow& render_window,
VideoCore::RasterizerInterface& rasterizer, const Device& device,
VKMemoryManager& memory_manager, VKSwapchain& swapchain,
MemoryAllocator& memory_allocator, VKSwapchain& swapchain,
VKScheduler& scheduler, const VKScreenInfo& screen_info);
~VKBlitScreen();
@@ -86,7 +86,7 @@ private:
Core::Frontend::EmuWindow& render_window;
VideoCore::RasterizerInterface& rasterizer;
const Device& device;
VKMemoryManager& memory_manager;
MemoryAllocator& memory_allocator;
VKSwapchain& swapchain;
VKScheduler& scheduler;
const std::size_t image_count;
@@ -104,14 +104,14 @@ private:
vk::Sampler sampler;
vk::Buffer buffer;
VKMemoryCommit buffer_commit;
MemoryCommit buffer_commit;
std::vector<u64> resource_ticks;
std::vector<vk::Semaphore> semaphores;
std::vector<vk::Image> raw_images;
std::vector<vk::ImageView> raw_image_views;
std::vector<VKMemoryCommit> raw_buffer_commits;
std::vector<MemoryCommit> raw_buffer_commits;
u32 raw_width = 0;
u32 raw_height = 0;
};
@@ -36,11 +36,11 @@ constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS =
} // Anonymous namespace
Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_,
VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_)
Buffer::Buffer(const Device& device_, MemoryAllocator& memory_allocator, VKScheduler& scheduler_,
StagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_)
: BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{
staging_pool_} {
const VkBufferCreateInfo ci{
buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -49,22 +49,20 @@ Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKSchedul
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
buffer.handle = device.GetLogical().CreateBuffer(ci);
buffer.commit = memory_manager.Commit(buffer.handle, false);
});
commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
}
Buffer::~Buffer() = default;
void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
const auto& staging = staging_pool.GetUnusedBuffer(data_size, true);
std::memcpy(staging.commit->Map(data_size), data, data_size);
const auto& staging = staging_pool.Request(data_size, MemoryUsage::Upload);
std::memcpy(staging.mapped_span.data(), data, data_size);
scheduler.RequestOutsideRenderPassOperationContext();
const VkBuffer handle = Handle();
scheduler.Record([staging = *staging.handle, handle, offset, data_size,
scheduler.Record([staging = staging.buffer, handle, offset, data_size,
&device = device](vk::CommandBuffer cmdbuf) {
const VkBufferMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@@ -100,12 +98,12 @@ void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
}
void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
const auto& staging = staging_pool.GetUnusedBuffer(data_size, true);
auto staging = staging_pool.Request(data_size, MemoryUsage::Download);
scheduler.RequestOutsideRenderPassOperationContext();
const VkBuffer handle = Handle();
scheduler.Record(
[staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
[staging = staging.buffer, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
const VkBufferMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
@@ -126,7 +124,7 @@ void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
});
scheduler.Finish();
std::memcpy(data, staging.commit->Map(data_size), data_size);
std::memcpy(data, staging.mapped_span.data(), data_size);
}
void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
@@ -164,29 +162,29 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_,
Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
const Device& device_, VKMemoryManager& memory_manager_,
const Device& device_, MemoryAllocator& memory_allocator_,
VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_,
VKStagingBufferPool& staging_pool_)
StagingBufferPool& staging_pool_)
: VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_,
cpu_memory_, stream_buffer_},
device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
staging_pool_} {}
device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
staging_pool{staging_pool_} {}
VKBufferCache::~VKBufferCache() = default;
std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr,
return std::make_shared<Buffer>(device, memory_allocator, scheduler, staging_pool, cpu_addr,
size);
}
VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) {
size = std::max(size, std::size_t(4));
const auto& empty = staging_pool.GetUnusedBuffer(size, false);
const auto& empty = staging_pool.Request(size, MemoryUsage::DeviceLocal);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) {
scheduler.Record([size, buffer = empty.buffer](vk::CommandBuffer cmdbuf) {
cmdbuf.FillBuffer(buffer, 0, size, 0);
});
return {*empty.handle, 0, 0};
return {empty.buffer, 0, 0};
}
} // namespace Vulkan
@@ -8,21 +8,20 @@
#include "common/common_types.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
class Device;
class VKMemoryManager;
class VKScheduler;
class Buffer final : public VideoCommon::BufferBlock {
public:
explicit Buffer(const Device& device, VKMemoryManager& memory_manager, VKScheduler& scheduler,
VKStagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_);
explicit Buffer(const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler,
StagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_);
~Buffer();
void Upload(std::size_t offset, std::size_t data_size, const u8* data);
@@ -33,7 +32,7 @@ public:
std::size_t copy_size);
VkBuffer Handle() const {
return *buffer.handle;
return *buffer;
}
u64 Address() const {
@@ -43,18 +42,19 @@ public:
private:
const Device& device;
VKScheduler& scheduler;
VKStagingBufferPool& staging_pool;
StagingBufferPool& staging_pool;
VKBuffer buffer;
vk::Buffer buffer;
MemoryCommit commit;
};
class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
public:
explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
const Device& device, VKMemoryManager& memory_manager,
const Device& device, MemoryAllocator& memory_allocator,
VKScheduler& scheduler, VKStreamBuffer& stream_buffer,
VKStagingBufferPool& staging_pool);
StagingBufferPool& staging_pool);
~VKBufferCache();
BufferInfo GetEmptyBuffer(std::size_t size) override;
@@ -64,9 +64,9 @@ protected:
private:
const Device& device;
VKMemoryManager& memory_manager;
MemoryAllocator& memory_allocator;
VKScheduler& scheduler;
VKStagingBufferPool& staging_pool;
StagingBufferPool& staging_pool;
};
} // namespace Vulkan
@@ -164,7 +164,7 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet(
QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool_,
VKStagingBufferPool& staging_buffer_pool_,
StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_)
: VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(),
BuildQuadArrayPassDescriptorUpdateTemplateEntry(),
@@ -177,18 +177,18 @@ QuadArrayPass::~QuadArrayPass() = default;
std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) {
const u32 num_triangle_vertices = (num_vertices / 4) * 6;
const std::size_t staging_size = num_triangle_vertices * sizeof(u32);
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
ASSERT(num_vertices % 4 == 0);
const u32 num_quads = num_vertices / 4;
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, num_quads,
first, set](vk::CommandBuffer cmdbuf) {
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer,
num_quads, first, set](vk::CommandBuffer cmdbuf) {
constexpr u32 dispatch_size = 1024;
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {});
@@ -208,11 +208,11 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {});
});
return {*buffer.handle, 0};
return {staging_ref.buffer, 0};
}
Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool_,
VKDescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_)
: VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(),
BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV),
@@ -224,15 +224,15 @@ Uint8Pass::~Uint8Pass() = default;
std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
u64 src_offset) {
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
num_vertices](vk::CommandBuffer cmdbuf) {
constexpr u32 dispatch_size = 1024;
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
@@ -252,12 +252,12 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
});
return {*buffer.handle, 0};
return {staging_ref.buffer, 0};
}
QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool_,
VKStagingBufferPool& staging_buffer_pool_,
StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_)
: VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(),
BuildInputOutputDescriptorUpdateTemplate(),
@@ -286,15 +286,15 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
const u32 num_tri_vertices = (num_vertices / 4) * 6;
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) {
static constexpr u32 dispatch_size = 1024;
const std::array push_constants = {base_vertex, index_shift};
@@ -317,7 +317,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
});
return {*buffer.handle, 0};
return {staging_ref.buffer, 0};
}
} // namespace Vulkan
@@ -16,8 +16,8 @@
namespace Vulkan {
class Device;
class StagingBufferPool;
class VKScheduler;
class VKStagingBufferPool;
class VKUpdateDescriptorQueue;
class VKComputePass {
@@ -45,7 +45,7 @@ class QuadArrayPass final : public VKComputePass {
public:
explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool_,
VKStagingBufferPool& staging_buffer_pool_,
StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_);
~QuadArrayPass();
@@ -53,15 +53,14 @@ public:
private:
VKScheduler& scheduler;
VKStagingBufferPool& staging_buffer_pool;
StagingBufferPool& staging_buffer_pool;
VKUpdateDescriptorQueue& update_descriptor_queue;
};
class Uint8Pass final : public VKComputePass {
public:
explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool_,
VKStagingBufferPool& staging_buffer_pool_,
VKDescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_);
~Uint8Pass();
@@ -69,7 +68,7 @@ public:
private:
VKScheduler& scheduler;
VKStagingBufferPool& staging_buffer_pool;
StagingBufferPool& staging_buffer_pool;
VKUpdateDescriptorQueue& update_descriptor_queue;
};
@@ -77,7 +76,7 @@ class QuadIndexedPass final : public VKComputePass {
public:
explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
VKDescriptorPool& descriptor_pool_,
VKStagingBufferPool& staging_buffer_pool_,
StagingBufferPool& staging_buffer_pool_,
VKUpdateDescriptorQueue& update_descriptor_queue_);
~QuadIndexedPass();
@@ -87,7 +86,7 @@ public:
private:
VKScheduler& scheduler;
VKStagingBufferPool& staging_buffer_pool;
StagingBufferPool& staging_buffer_pool;
VKUpdateDescriptorQueue& update_descriptor_queue;
};
@@ -1,230 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <optional>
#include <tuple>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
namespace {
u64 GetAllocationChunkSize(u64 required_size) {
static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20};
auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size);
return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20);
}
} // Anonymous namespace
class VKMemoryAllocation final {
public:
explicit VKMemoryAllocation(const Device& device_, vk::DeviceMemory memory_,
VkMemoryPropertyFlags properties_, u64 allocation_size_, u32 type_)
: device{device_}, memory{std::move(memory_)}, properties{properties_},
allocation_size{allocation_size_}, shifted_type{ShiftType(type_)} {}
VKMemoryCommit Commit(VkDeviceSize commit_size, VkDeviceSize alignment) {
auto found = TryFindFreeSection(free_iterator, allocation_size,
static_cast<u64>(commit_size), static_cast<u64>(alignment));
if (!found) {
found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size),
static_cast<u64>(alignment));
if (!found) {
// Signal out of memory, it'll try to do more allocations.
return nullptr;
}
}
auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found,
*found + commit_size);
commits.push_back(commit.get());
// Last commit's address is highly probable to be free.
free_iterator = *found + commit_size;
return commit;
}
void Free(const VKMemoryCommitImpl* commit) {
ASSERT(commit);
const auto it = std::find(std::begin(commits), std::end(commits), commit);
if (it == commits.end()) {
UNREACHABLE_MSG("Freeing unallocated commit!");
return;
}
commits.erase(it);
}
/// Returns whether this allocation is compatible with the arguments.
bool IsCompatible(VkMemoryPropertyFlags wanted_properties, u32 type_mask) const {
return (wanted_properties & properties) && (type_mask & shifted_type) != 0;
}
private:
static constexpr u32 ShiftType(u32 type) {
return 1U << type;
}
/// A memory allocator, it may return a free region between "start" and "end" with the solicited
/// requirements.
std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const {
u64 iterator = Common::AlignUp(start, alignment);
while (iterator + size <= end) {
const u64 try_left = iterator;
const u64 try_right = try_left + size;
bool overlap = false;
for (const auto& commit : commits) {
const auto [commit_left, commit_right] = commit->interval;
if (try_left < commit_right && commit_left < try_right) {
// There's an overlap, continue the search where the overlapping commit ends.
iterator = Common::AlignUp(commit_right, alignment);
overlap = true;
break;
}
}
if (!overlap) {
// A free address has been found.
return try_left;
}
}
// No free regions where found, return an empty optional.
return std::nullopt;
}
const Device& device; ///< Vulkan device.
const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
const VkMemoryPropertyFlags properties; ///< Vulkan properties.
const u64 allocation_size; ///< Size of this allocation.
const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted.
/// Hints where the next free region is likely going to be.
u64 free_iterator{};
/// Stores all commits done from this allocation.
std::vector<const VKMemoryCommitImpl*> commits;
};
VKMemoryManager::VKMemoryManager(const Device& device_)
: device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
VKMemoryManager::~VKMemoryManager() = default;
VKMemoryCommit VKMemoryManager::Commit(const VkMemoryRequirements& requirements,
bool host_visible) {
const u64 chunk_size = GetAllocationChunkSize(requirements.size);
// When a host visible commit is asked, search for host visible and coherent, otherwise search
// for a fast device local type.
const VkMemoryPropertyFlags wanted_properties =
host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
if (auto commit = TryAllocCommit(requirements, wanted_properties)) {
return commit;
}
// Commit has failed, allocate more memory.
if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) {
// TODO(Rodrigo): Handle these situations in some way like flushing to guest memory.
// Allocation has failed, panic.
UNREACHABLE_MSG("Ran out of VRAM!");
return {};
}
// Commit again, this time it won't fail since there's a fresh allocation above. If it does,
// there's a bug.
auto commit = TryAllocCommit(requirements, wanted_properties);
ASSERT(commit);
return commit;
}
VKMemoryCommit VKMemoryManager::Commit(const vk::Buffer& buffer, bool host_visible) {
auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), host_visible);
buffer.BindMemory(commit->GetMemory(), commit->GetOffset());
return commit;
}
VKMemoryCommit VKMemoryManager::Commit(const vk::Image& image, bool host_visible) {
auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), host_visible);
image.BindMemory(commit->GetMemory(), commit->GetOffset());
return commit;
}
bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask,
u64 size) {
const u32 type = [&] {
for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
const auto flags = properties.memoryTypes[type_index].propertyFlags;
if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) {
// The type matches in type and in the wanted properties.
return type_index;
}
}
UNREACHABLE_MSG("Couldn't find a compatible memory type!");
return 0U;
}();
// Try to allocate found type.
vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
.allocationSize = size,
.memoryTypeIndex = type,
});
if (!memory) {
LOG_CRITICAL(Render_Vulkan, "Device allocation failed!");
return false;
}
allocations.push_back(std::make_unique<VKMemoryAllocation>(device, std::move(memory),
wanted_properties, size, type));
return true;
}
VKMemoryCommit VKMemoryManager::TryAllocCommit(const VkMemoryRequirements& requirements,
VkMemoryPropertyFlags wanted_properties) {
for (auto& allocation : allocations) {
if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) {
continue;
}
if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
return commit;
}
}
return {};
}
VKMemoryCommitImpl::VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_,
const vk::DeviceMemory& memory_, u64 begin_, u64 end_)
: device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {}
VKMemoryCommitImpl::~VKMemoryCommitImpl() {
allocation->Free(this);
}
MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const {
return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size));
}
void VKMemoryCommitImpl::Unmap() const {
memory.Unmap();
}
MemoryMap VKMemoryCommitImpl::Map() const {
return Map(interval.second - interval.first);
}
} // namespace Vulkan
@@ -1,132 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <span>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
class Device;
class MemoryMap;
class VKMemoryAllocation;
class VKMemoryCommitImpl;
using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;
class VKMemoryManager final {
public:
explicit VKMemoryManager(const Device& device_);
VKMemoryManager(const VKMemoryManager&) = delete;
~VKMemoryManager();
/**
* Commits a memory with the specified requeriments.
* @param requirements Requirements returned from a Vulkan call.
* @param host_visible Signals the allocator that it *must* use host visible and coherent
* memory. When passing false, it will try to allocate device local memory.
* @returns A memory commit.
*/
VKMemoryCommit Commit(const VkMemoryRequirements& requirements, bool host_visible);
/// Commits memory required by the buffer and binds it.
VKMemoryCommit Commit(const vk::Buffer& buffer, bool host_visible);
/// Commits memory required by the image and binds it.
VKMemoryCommit Commit(const vk::Image& image, bool host_visible);
private:
/// Allocates a chunk of memory.
bool AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, u64 size);
/// Tries to allocate a memory commit.
VKMemoryCommit TryAllocCommit(const VkMemoryRequirements& requirements,
VkMemoryPropertyFlags wanted_properties);
const Device& device; ///< Device handler.
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations.
};
class VKMemoryCommitImpl final {
friend VKMemoryAllocation;
friend MemoryMap;
public:
explicit VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_,
const vk::DeviceMemory& memory_, u64 begin_, u64 end_);
~VKMemoryCommitImpl();
/// Maps a memory region and returns a pointer to it.
/// It's illegal to have more than one memory map at the same time.
MemoryMap Map(u64 size, u64 offset = 0) const;
/// Maps the whole commit and returns a pointer to it.
/// It's illegal to have more than one memory map at the same time.
MemoryMap Map() const;
/// Returns the Vulkan memory handler.
VkDeviceMemory GetMemory() const {
return *memory;
}
/// Returns the start position of the commit relative to the allocation.
VkDeviceSize GetOffset() const {
return static_cast<VkDeviceSize>(interval.first);
}
private:
/// Unmaps memory.
void Unmap() const;
const Device& device; ///< Vulkan device.
const vk::DeviceMemory& memory; ///< Vulkan device memory handler.
std::pair<u64, u64> interval{}; ///< Interval where the commit exists.
VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
};
/// Holds ownership of a memory map.
class MemoryMap final {
public:
explicit MemoryMap(const VKMemoryCommitImpl* commit_, std::span<u8> span_)
: commit{commit_}, span{span_} {}
~MemoryMap() {
if (commit) {
commit->Unmap();
}
}
/// Prematurely releases the memory map.
void Release() {
commit->Unmap();
commit = nullptr;
}
/// Returns a span to the memory map.
[[nodiscard]] std::span<u8> Span() const noexcept {
return span;
}
/// Returns the address of the memory map.
[[nodiscard]] u8* Address() const noexcept {
return span.data();
}
/// Returns the address of the memory map;
[[nodiscard]] operator u8*() const noexcept {
return span.data();
}
private:
const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit.
std::span<u8> span; ///< Address to the mapped memory.
};
} // namespace Vulkan
@@ -409,24 +409,24 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
Tegra::MemoryManager& gpu_memory_,
Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_,
const Device& device_, VKMemoryManager& memory_manager_,
const Device& device_, MemoryAllocator& memory_allocator_,
StateTracker& state_tracker_, VKScheduler& scheduler_)
: RasterizerAccelerated{cpu_memory_}, gpu{gpu_},
gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()},
screen_info{screen_info_}, device{device_}, memory_manager{memory_manager_},
screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_},
state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler),
staging_pool(device, memory_manager, scheduler), descriptor_pool(device, scheduler),
staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
update_descriptor_queue(device, scheduler),
blit_image(device, scheduler, state_tracker, descriptor_pool),
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
texture_cache_runtime{device, scheduler, memory_manager, staging_pool, blit_image},
texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image},
texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory),
pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
descriptor_pool, update_descriptor_queue),
buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_manager, scheduler, stream_buffer,
staging_pool),
buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_allocator, scheduler,
stream_buffer, staging_pool),
query_cache{*this, maxwell3d, gpu_memory, device, scheduler},
fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler),
wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) {
@@ -1445,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() {
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
});
default_buffer_commit = memory_manager.Commit(default_buffer, false);
default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) {
@@ -21,7 +21,6 @@
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_fence_manager.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -30,6 +29,7 @@
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/shader/async_shaders.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Core {
@@ -56,7 +56,7 @@ public:
explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
VKScreenInfo& screen_info_, const Device& device_,
VKMemoryManager& memory_manager_, StateTracker& state_tracker_,
MemoryAllocator& memory_allocator_, StateTracker& state_tracker_,
VKScheduler& scheduler_);
~RasterizerVulkan() override;
@@ -213,12 +213,12 @@ private:
VKScreenInfo& screen_info;
const Device& device;
VKMemoryManager& memory_manager;
MemoryAllocator& memory_allocator;
StateTracker& state_tracker;
VKScheduler& scheduler;
VKStreamBuffer stream_buffer;
VKStagingBufferPool staging_pool;
StagingBufferPool staging_pool;
VKDescriptorPool descriptor_pool;
VKUpdateDescriptorQueue update_descriptor_queue;
BlitImageHelper blit_image;
@@ -234,7 +234,7 @@ private:
VKFenceManager fence_manager;
vk::Buffer default_buffer;
VKMemoryCommit default_buffer_commit;
MemoryCommit default_buffer_commit;
vk::Event wfi_event;
VideoCommon::Shader::AsyncShaders async_shaders;
@@ -3,10 +3,12 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <unordered_map>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -16,45 +18,51 @@
namespace Vulkan {
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_)
: buffer{std::move(buffer_)} {}
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
VKScheduler& scheduler_)
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {}
VKStagingBufferPool::VKStagingBufferPool(const Device& device_, VKMemoryManager& memory_manager_,
VKScheduler& scheduler_)
: device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
StagingBufferPool::~StagingBufferPool() = default;
VKStagingBufferPool::~VKStagingBufferPool() = default;
VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) {
if (const auto buffer = TryGetReservedBuffer(size, host_visible)) {
return *buffer;
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) {
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) {
return *ref;
}
return CreateStagingBuffer(size, host_visible);
return CreateStagingBuffer(size, usage);
}
void VKStagingBufferPool::TickFrame() {
current_delete_level = (current_delete_level + 1) % NumLevels;
void StagingBufferPool::TickFrame() {
current_delete_level = (current_delete_level + 1) % NUM_LEVELS;
ReleaseCache(true);
ReleaseCache(false);
ReleaseCache(MemoryUsage::DeviceLocal);
ReleaseCache(MemoryUsage::Upload);
ReleaseCache(MemoryUsage::Download);
}
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
if (!scheduler.IsFree(entry.tick)) {
continue;
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
MemoryUsage usage) {
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
const auto is_free = [this](const StagingBuffer& entry) {
return scheduler.IsFree(entry.tick);
};
auto& entries = cache_level.entries;
const auto hint_it = entries.begin() + cache_level.iterate_index;
auto it = std::find_if(entries.begin() + cache_level.iterate_index, entries.end(), is_free);
if (it == entries.end()) {
it = std::find_if(entries.begin(), hint_it, is_free);
if (it == hint_it) {
return std::nullopt;
}
entry.tick = scheduler.CurrentTick();
return &*entry.buffer;
}
return nullptr;
cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
it->tick = scheduler.CurrentTick();
return it->Ref();
}
VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) {
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) {
const u32 log2 = Common::Log2Ceil64(size);
auto buffer = std::make_unique<VKBuffer>();
buffer->handle = device.GetLogical().CreateBuffer({
vk::Buffer buffer = device.GetLogical().CreateBuffer({
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -66,49 +74,63 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
});
buffer->commit = memory_manager.Commit(buffer->handle, host_visible);
if (device.HasDebuggingToolAttached()) {
++buffer_index;
buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
}
MemoryCommit commit = memory_allocator.Commit(buffer, usage);
const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{};
std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries;
StagingBuffer& entry = entries.emplace_back(std::move(buffer));
entry.tick = scheduler.CurrentTick();
return *entry.buffer;
StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
.buffer = std::move(buffer),
.commit = std::move(commit),
.mapped_span = mapped_span,
.tick = scheduler.CurrentTick(),
});
return entry.Ref();
}
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
return host_visible ? host_staging_buffers : device_staging_buffers;
}
void VKStagingBufferPool::ReleaseCache(bool host_visible) {
auto& cache = GetCache(host_visible);
const u64 size = ReleaseLevel(cache, current_delete_level);
if (size == 0) {
return;
StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) {
switch (usage) {
case MemoryUsage::DeviceLocal:
return device_local_cache;
case MemoryUsage::Upload:
return upload_cache;
case MemoryUsage::Download:
return download_cache;
default:
UNREACHABLE_MSG("Invalid memory usage={}", usage);
return upload_cache;
}
}
u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) {
static constexpr std::size_t deletions_per_tick = 16;
void StagingBufferPool::ReleaseCache(MemoryUsage usage) {
ReleaseLevel(GetCache(usage), current_delete_level);
}
void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) {
constexpr size_t deletions_per_tick = 16;
auto& staging = cache[log2];
auto& entries = staging.entries;
const std::size_t old_size = entries.size();
const size_t old_size = entries.size();
const auto is_deleteable = [this](const StagingBuffer& entry) {
return scheduler.IsFree(entry.tick);
};
const std::size_t begin_offset = staging.delete_index;
const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
const auto begin = std::begin(entries) + begin_offset;
const auto end = std::begin(entries) + end_offset;
const size_t begin_offset = staging.delete_index;
const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
const auto begin = entries.begin() + begin_offset;
const auto end = entries.begin() + end_offset;
entries.erase(std::remove_if(begin, end, is_deleteable), end);
const std::size_t new_size = entries.size();
const size_t new_size = entries.size();
staging.delete_index += deletions_per_tick;
if (staging.delete_index >= new_size) {
staging.delete_index = 0;
}
return (1ULL << log2) * (old_size - new_size);
if (staging.iterate_index > new_size) {
staging.iterate_index = 0;
}
}
} // namespace Vulkan
@@ -9,7 +9,7 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -17,55 +17,65 @@ namespace Vulkan {
class Device;
class VKScheduler;
struct VKBuffer final {
vk::Buffer handle;
VKMemoryCommit commit;
struct StagingBufferRef {
VkBuffer buffer;
std::span<u8> mapped_span;
};
class VKStagingBufferPool final {
class StagingBufferPool {
public:
explicit VKStagingBufferPool(const Device& device, VKMemoryManager& memory_manager,
VKScheduler& scheduler);
~VKStagingBufferPool();
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
VKScheduler& scheduler);
~StagingBufferPool();
VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible);
StagingBufferRef Request(size_t size, MemoryUsage usage);
void TickFrame();
private:
struct StagingBuffer final {
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer);
std::unique_ptr<VKBuffer> buffer;
struct StagingBuffer {
vk::Buffer buffer;
MemoryCommit commit;
std::span<u8> mapped_span;
u64 tick = 0;
StagingBufferRef Ref() const noexcept {
return {
.buffer = *buffer,
.mapped_span = mapped_span,
};
}
};
struct StagingBuffers final {
struct StagingBuffers {
std::vector<StagingBuffer> entries;
std::size_t delete_index = 0;
size_t delete_index = 0;
size_t iterate_index = 0;
};
static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT;
using StagingBuffersCache = std::array<StagingBuffers, NumLevels>;
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible);
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage);
VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible);
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage);
StagingBuffersCache& GetCache(bool host_visible);
StagingBuffersCache& GetCache(MemoryUsage usage);
void ReleaseCache(bool host_visible);
void ReleaseCache(MemoryUsage usage);
u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2);
void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
const Device& device;
VKMemoryManager& memory_manager;
MemoryAllocator& memory_allocator;
VKScheduler& scheduler;
StagingBuffersCache host_staging_buffers;
StagingBuffersCache device_staging_buffers;
StagingBuffersCache device_local_cache;
StagingBuffersCache upload_cache;
StagingBuffersCache download_cache;
std::size_t current_delete_level = 0;
size_t current_delete_level = 0;
u64 buffer_index = 0;
};
} // namespace Vulkan
@@ -10,12 +10,12 @@
#include "video_core/engines/fermi_2d.h"
#include "video_core/renderer_vulkan/blit_image.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -554,10 +554,18 @@ void TextureCacheRuntime::Finish() {
}
ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) {
const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true);
return ImageBufferMap{
.handle = *buffer.handle,
.map = buffer.commit->Map(size),
const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload);
return {
.handle = staging_ref.buffer,
.span = staging_ref.mapped_span,
};
}
ImageBufferMap TextureCacheRuntime::MapDownloadBuffer(size_t size) {
const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Download);
return {
.handle = staging_ref.buffer,
.span = staging_ref.mapped_span,
};
}
@@ -788,9 +796,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)),
aspect_mask(ImageAspectMask(info.format)) {
if (image) {
commit = runtime.memory_manager.Commit(image, false);
commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
} else {
commit = runtime.memory_manager.Commit(buffer, false);
commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
}
if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) {
flags |= VideoCommon::ImageFlagBits::Converted;
@@ -7,8 +7,8 @@
#include <compare>
#include <span>
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -19,14 +19,13 @@ using VideoCommon::Offset2D;
using VideoCommon::RenderTargets;
using VideoCore::Surface::PixelFormat;
class VKScheduler;
class VKStagingBufferPool;
class BlitImageHelper;
class Device;
class Image;
class ImageView;
class Framebuffer;
class StagingBufferPool;
class VKScheduler;
struct RenderPassKey {
constexpr auto operator<=>(const RenderPassKey&) const noexcept = default;
@@ -60,18 +59,18 @@ struct ImageBufferMap {
}
[[nodiscard]] std::span<u8> Span() const noexcept {
return map.Span();
return span;
}
VkBuffer handle;
MemoryMap map;
std::span<u8> span;
};
struct TextureCacheRuntime {
const Device& device;
VKScheduler& scheduler;
VKMemoryManager& memory_manager;
VKStagingBufferPool& staging_buffer_pool;
MemoryAllocator& memory_allocator;
StagingBufferPool& staging_buffer_pool;
BlitImageHelper& blit_image_helper;
std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache;
@@ -79,10 +78,7 @@ struct TextureCacheRuntime {
[[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size);
[[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size) {
// TODO: Have a special function for this
return MapUploadBuffer(size);
}
[[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size);
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
const std::array<Offset2D, 2>& dst_region,
@@ -141,7 +137,7 @@ private:
VKScheduler* scheduler;
vk::Image image;
vk::Buffer buffer;
VKMemoryCommit commit;
MemoryCommit commit;
VkImageAspectFlags aspect_mask = 0;
bool initialized = false;
};
+1 -1
View File
@@ -679,7 +679,7 @@ u32 CalculateLayerSize(const ImageInfo& info) noexcept {
}
std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
ASSERT(info.resources.levels <= MAX_MIP_LEVELS);
ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS));
const LevelInfo level_info = MakeLevelInfo(info);
std::array<u32, MAX_MIP_LEVELS> offsets{};
u32 offset = 0;
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -0,0 +1,268 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <bit>
#include <optional>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
namespace {
struct Range {
u64 begin;
u64 end;
[[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept {
return iterator < end && begin < iterator + size;
}
};
[[nodiscard]] u64 AllocationChunkSize(u64 required_size) {
static constexpr std::array sizes{
0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10,
0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10,
0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10,
};
static_assert(std::is_sorted(sizes.begin(), sizes.end()));
const auto it = std::ranges::lower_bound(sizes, required_size);
return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20);
}
[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
switch (usage) {
case MemoryUsage::DeviceLocal:
return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
case MemoryUsage::Upload:
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
case MemoryUsage::Download:
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
}
UNREACHABLE_MSG("Invalid memory usage={}", usage);
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
} // Anonymous namespace
class MemoryAllocation {
public:
explicit MemoryAllocation(const Device& device_, vk::DeviceMemory memory_,
VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type)
: device{device_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
property_flags{properties}, shifted_memory_type{1U << type} {}
[[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) {
const std::optional<u64> alloc = FindFreeRegion(size, alignment);
if (!alloc) {
// Signal out of memory, it'll try to do more allocations.
return std::nullopt;
}
const Range range{
.begin = *alloc,
.end = *alloc + size,
};
commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range);
return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size);
}
void Free(u64 begin) {
const auto it = std::ranges::find(commits, begin, &Range::begin);
ASSERT_MSG(it != commits.end(), "Invalid commit");
commits.erase(it);
}
[[nodiscard]] std::span<u8> Map() {
if (memory_mapped_span.empty()) {
u8* const raw_pointer = memory.Map(0, allocation_size);
memory_mapped_span = std::span<u8>(raw_pointer, allocation_size);
}
return memory_mapped_span;
}
/// Returns whether this allocation is compatible with the arguments.
[[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
return (flags & property_flags) && (type_mask & shifted_memory_type) != 0;
}
private:
[[nodiscard]] static constexpr u32 ShiftType(u32 type) {
return 1U << type;
}
[[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept {
ASSERT(std::has_single_bit(alignment));
const u64 alignment_log2 = std::countr_zero(alignment);
std::optional<u64> candidate;
u64 iterator = 0;
auto commit = commits.begin();
while (iterator + size <= allocation_size) {
candidate = candidate.value_or(iterator);
if (commit == commits.end()) {
break;
}
if (commit->Contains(*candidate, size)) {
candidate = std::nullopt;
}
iterator = Common::AlignUpLog2(commit->end, alignment_log2);
++commit;
}
return candidate;
}
const Device& device; ///< Vulkan device.
const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
const u64 allocation_size; ///< Size of this allocation.
const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags.
const u32 shifted_memory_type; ///< Shifted Vulkan memory type.
std::vector<Range> commits; ///< All commit ranges done from this allocation.
std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before.
};
MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
u64 end_) noexcept
: allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {}
MemoryCommit::~MemoryCommit() {
Release();
}
MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept {
Release();
allocation = std::exchange(rhs.allocation, nullptr);
memory = rhs.memory;
begin = rhs.begin;
end = rhs.end;
span = std::exchange(rhs.span, std::span<u8>{});
return *this;
}
MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept
: allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin},
end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {}
std::span<u8> MemoryCommit::Map() {
if (span.empty()) {
span = allocation->Map().subspan(begin, end - begin);
}
return span;
}
void MemoryCommit::Release() {
if (allocation) {
allocation->Free(begin);
}
}
MemoryAllocator::MemoryAllocator(const Device& device_)
: device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
MemoryAllocator::~MemoryAllocator() = default;
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
// Find the fastest memory flags we can afford with the current requirements
const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage);
if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
return std::move(*commit);
}
// Commit has failed, allocate more memory.
// TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size));
// Commit again, this time it won't fail since there's a fresh allocation above.
// If it does, there's a bug.
return TryCommit(requirements, flags).value();
}
MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) {
auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage);
buffer.BindMemory(commit.Memory(), commit.Offset());
return commit;
}
MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage);
image.BindMemory(commit.Memory(), commit.Offset());
return commit;
}
void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
const u32 type = FindType(flags, type_mask).value();
vk::DeviceMemory memory = device.GetLogical().AllocateMemory({
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
.allocationSize = size,
.memoryTypeIndex = type,
});
allocations.push_back(
std::make_unique<MemoryAllocation>(device, std::move(memory), flags, size, type));
}
std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
VkMemoryPropertyFlags flags) {
for (auto& allocation : allocations) {
if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) {
continue;
}
if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
return commit;
}
}
return std::nullopt;
}
VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
}
VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
VkMemoryPropertyFlags flags) const {
if (FindType(flags, type_mask)) {
// Found a memory type with those requirements
return flags;
}
if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
// Remove host cached bit in case it's not supported
return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
}
if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
// Remove device local, if it's not supported by the requested resource
return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
UNREACHABLE_MSG("No compatible memory types found");
return 0;
}
std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const {
for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags;
if ((type_mask & (1U << type_index)) && (type_flags & flags)) {
// The type matches in type and in the wanted properties.
return type_index;
}
}
// Failed to find index
return std::nullopt;
}
bool IsHostVisible(MemoryUsage usage) noexcept {
switch (usage) {
case MemoryUsage::DeviceLocal:
return false;
case MemoryUsage::Upload:
case MemoryUsage::Download:
return true;
}
UNREACHABLE_MSG("Invalid memory usage={}", usage);
return false;
}
} // namespace Vulkan
@@ -0,0 +1,117 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <span>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
class Device;
class MemoryMap;
class MemoryAllocation;
/// Hints and requirements for the backing memory type of a commit
enum class MemoryUsage {
DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU
Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads
Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks
};
/// Ownership handle of a memory commitment.
/// Points to a subregion of a memory allocation.
class MemoryCommit {
public:
explicit MemoryCommit() noexcept = default;
explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
u64 end_) noexcept;
~MemoryCommit();
MemoryCommit& operator=(MemoryCommit&&) noexcept;
MemoryCommit(MemoryCommit&&) noexcept;
MemoryCommit& operator=(const MemoryCommit&) = delete;
MemoryCommit(const MemoryCommit&) = delete;
/// Returns a host visible memory map.
/// It will map the backing allocation if it hasn't been mapped before.
std::span<u8> Map();
/// Returns the Vulkan memory handler.
VkDeviceMemory Memory() const {
return memory;
}
/// Returns the start position of the commit relative to the allocation.
VkDeviceSize Offset() const {
return static_cast<VkDeviceSize>(begin);
}
private:
void Release();
MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
VkDeviceMemory memory{}; ///< Vulkan device memory handler.
u64 begin{}; ///< Beginning offset in bytes to where the commit exists.
u64 end{}; ///< Offset in bytes where the commit ends.
std::span<u8> span; ///< Host visible memory span. Empty if not queried before.
};
/// Memory allocator container.
/// Allocates and releases memory allocations on demand.
class MemoryAllocator {
public:
explicit MemoryAllocator(const Device& device_);
~MemoryAllocator();
MemoryAllocator& operator=(const MemoryAllocator&) = delete;
MemoryAllocator(const MemoryAllocator&) = delete;
/**
* Commits a memory with the specified requirements.
*
* @param requirements Requirements returned from a Vulkan call.
* @param usage Indicates how the memory will be used.
*
* @returns A memory commit.
*/
MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage);
/// Commits memory required by the buffer and binds it.
MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage);
/// Commits memory required by the image and binds it.
MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
private:
/// Allocates a chunk of memory.
void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
/// Tries to allocate a memory commit.
std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
VkMemoryPropertyFlags flags);
/// Returns the fastest compatible memory property flags from a wanted usage.
VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const;
/// Returns the fastest compatible memory property flags from the wanted flags.
VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
/// Returns index to the fastest memory type compatible with the passed requirements.
std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
const Device& device; ///< Device handle.
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
};
/// Returns true when a memory usage is guaranteed to be host visible.
bool IsHostVisible(MemoryUsage usage) noexcept;
} // namespace Vulkan
+1 -1
View File
@@ -93,7 +93,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
const auto& profiles = profile_manager->GetAllUsers();
for (const auto& user : profiles) {
Service::Account::ProfileBase profile;
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(user, profile))
continue;
@@ -40,7 +40,7 @@ QString GetImagePath(Common::UUID uuid) {
}
QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) {
Service::Account::ProfileBase profile;
Service::Account::ProfileBase profile{};
if (!manager.GetProfileBase(uuid, profile)) {
return {};
}
@@ -147,7 +147,7 @@ void ConfigureProfileManager::SetConfiguration() {
void ConfigureProfileManager::PopulateUserList() {
const auto& profiles = profile_manager->GetAllUsers();
for (const auto& user : profiles) {
Service::Account::ProfileBase profile;
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(user, profile))
continue;
@@ -212,7 +212,7 @@ void ConfigureProfileManager::RenameUser() {
const auto uuid = profile_manager->GetUser(user);
ASSERT(uuid);
Service::Account::ProfileBase profile;
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(*uuid, profile))
return;
@@ -9,6 +9,7 @@
#include "ui_configure_service.h"
#include "yuzu/configuration/configure_service.h"
#ifdef YUZU_ENABLE_BOXCAT
namespace {
QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
QString out;
@@ -32,6 +33,7 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
return out;
}
} // Anonymous namespace
#endif
ConfigureService::ConfigureService(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) {
+31 -27
View File
@@ -173,8 +173,8 @@ void GameList::OnItemExpanded(const QModelIndex& item) {
return;
}
auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
game_dir->expanded = tree_view->isExpanded(item);
UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded =
tree_view->isExpanded(item);
}
// Event in order to filter the gamelist after editing the searchfield
@@ -262,9 +262,9 @@ void GameList::OnUpdateThemedIcons() {
Qt::DecorationRole);
break;
case GameListItemType::CustomDir: {
const UISettings::GameDir* game_dir =
child->data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
const QString icon_name = QFileInfo::exists(game_dir->path)
const UISettings::GameDir& game_dir =
UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
const QString icon_name = QFileInfo::exists(game_dir.path)
? QStringLiteral("folder")
: QStringLiteral("bad_folder");
child->setData(
@@ -366,7 +366,7 @@ void GameList::AddDirEntry(GameListDir* entry_items) {
item_model->invisibleRootItem()->appendRow(entry_items);
tree_view->setExpanded(
entry_items->index(),
entry_items->data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded);
UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()].expanded);
}
void GameList::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent) {
@@ -549,7 +549,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
UISettings::GameDir& game_dir =
*selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
UISettings::values.game_dirs[selected.data(GameListDir::GameDirRole).toInt()];
QAction* deep_scan = context_menu.addAction(tr("Scan Subfolders"));
QAction* delete_dir = context_menu.addAction(tr("Remove Game Directory"));
@@ -568,8 +568,7 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
}
void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
UISettings::GameDir& game_dir =
*selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
const int game_dir_index = selected.data(GameListDir::GameDirRole).toInt();
QAction* move_up = context_menu.addAction(tr("\u25B2 Move Up"));
QAction* move_down = context_menu.addAction(tr("\u25bc Move Down"));
@@ -580,34 +579,39 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
move_up->setEnabled(row > 0);
move_down->setEnabled(row < item_model->rowCount() - 2);
connect(move_up, &QAction::triggered, [this, selected, row, &game_dir] {
// find the indices of the items in settings and swap them
std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)],
UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(
*selected.sibling(row - 1, 0)
.data(GameListDir::GameDirRole)
.value<UISettings::GameDir*>())]);
connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] {
const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt();
// swap the items in the settings
std::swap(UISettings::values.game_dirs[game_dir_index],
UISettings::values.game_dirs[other_index]);
// swap the indexes held by the QVariants
item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole);
item_model->setData(selected.sibling(row - 1, 0), QVariant(game_dir_index),
GameListDir::GameDirRole);
// move the treeview items
QList<QStandardItem*> item = item_model->takeRow(row);
item_model->invisibleRootItem()->insertRow(row - 1, item);
tree_view->setExpanded(selected, game_dir.expanded);
tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded);
});
connect(move_down, &QAction::triggered, [this, selected, row, &game_dir] {
// find the indices of the items in settings and swap them
std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)],
UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(
*selected.sibling(row + 1, 0)
.data(GameListDir::GameDirRole)
.value<UISettings::GameDir*>())]);
connect(move_down, &QAction::triggered, [this, selected, row, game_dir_index] {
const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt();
// swap the items in the settings
std::swap(UISettings::values.game_dirs[game_dir_index],
UISettings::values.game_dirs[other_index]);
// swap the indexes held by the QVariants
item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole);
item_model->setData(selected.sibling(row + 1, 0), QVariant(game_dir_index),
GameListDir::GameDirRole);
// move the treeview items
const QList<QStandardItem*> item = item_model->takeRow(row);
item_model->invisibleRootItem()->insertRow(row + 1, item);
tree_view->setExpanded(selected, game_dir.expanded);
tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded);
});
connect(open_directory_location, &QAction::triggered,
[this, game_dir] { emit OpenDirectory(game_dir.path); });
connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path);
});
}
void GameList::LoadCompatibilityList() {
+1 -1
View File
@@ -230,7 +230,7 @@ public:
setData(type(), TypeRole);
UISettings::GameDir* game_dir = &directory;
setData(QVariant::fromValue(game_dir), GameDirRole);
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
switch (dir_type) {
+3
View File
@@ -16,6 +16,7 @@
#include "applets/profile_select.h"
#include "applets/software_keyboard.h"
#include "applets/web_browser.h"
#include "common/nvidia_flags.h"
#include "configuration/configure_input.h"
#include "configuration/configure_per_game.h"
#include "configuration/configure_vibration.h"
@@ -3023,6 +3024,8 @@ int main(int argc, char* argv[]) {
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
Common::ConfigureNvidiaEnvironmentFlags();
// Init settings params
QCoreApplication::setOrganizationName(QStringLiteral("yuzu team"));
QCoreApplication::setApplicationName(QStringLiteral("yuzu"));
+3
View File
@@ -17,6 +17,7 @@
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/nvidia_flags.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
@@ -152,6 +153,8 @@ int main(int argc, char** argv) {
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });
Common::ConfigureNvidiaEnvironmentFlags();
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return -1;