Compare commits
93 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bda177ef40 | ||
|
|
a175ba1089 | ||
|
|
1e9b1d439f | ||
|
|
2c4c7aea8a | ||
|
|
46dda01151 | ||
|
|
6ff2db181f | ||
|
|
a1335d3d51 | ||
|
|
ffbde909c8 | ||
|
|
d98b0f8f48 | ||
|
|
4cd8b2f1f7 | ||
|
|
2ef4591e58 | ||
|
|
f1b58f0cd9 | ||
|
|
dd0679d710 | ||
|
|
4a67a5b917 | ||
|
|
e7c1d7bf77 | ||
|
|
bf9f737c60 | ||
|
|
fb796843df | ||
|
|
0bd8cecc94 | ||
|
|
e8401964b4 | ||
|
|
132f2006af | ||
|
|
e1ecf64701 | ||
|
|
5f4e7c77bd | ||
|
|
40acc2c079 | ||
|
|
c61b973968 | ||
|
|
0e0fc07135 | ||
|
|
fd873fd369 | ||
|
|
ca9afa3293 | ||
|
|
ff2b7cc0d3 | ||
|
|
3c8f936b31 | ||
|
|
a7fd61fcce | ||
|
|
8def504d73 | ||
|
|
c17ee0da5d | ||
|
|
c3c7603076 | ||
|
|
8be9e5b48b | ||
|
|
5f517e3e16 | ||
|
|
f8650a9580 | ||
|
|
3ff978aa4f | ||
|
|
301e2b5b7a | ||
|
|
432f045dba | ||
|
|
8f22f5470c | ||
|
|
72541af3bc | ||
|
|
fade63b58e | ||
|
|
c2b550987b | ||
|
|
e996f1ad09 | ||
|
|
f728a504aa | ||
|
|
b483f2d010 | ||
|
|
8495e1bd83 | ||
|
|
d8df9a16bd | ||
|
|
390ee10eef | ||
|
|
d583e01f54 | ||
|
|
9754a8145c | ||
|
|
5b9aedfc21 | ||
|
|
8620de6b20 | ||
|
|
89c15dd115 | ||
|
|
fe494a0ccd | ||
|
|
91084d9396 | ||
|
|
c8bf0caca0 | ||
|
|
6676687694 | ||
|
|
95fa57f007 | ||
|
|
7f37822c74 | ||
|
|
fb99446f24 | ||
|
|
cc2c3e447f | ||
|
|
28e78d81b2 | ||
|
|
185388f341 | ||
|
|
76b465f3ef | ||
|
|
af540b0057 | ||
|
|
06e0506cb3 | ||
|
|
71264ce9a7 | ||
|
|
6dc1d48fd1 | ||
|
|
3e03391a49 | ||
|
|
be8fd5490e | ||
|
|
ba2ea7eeac | ||
|
|
22be115eb2 | ||
|
|
0ec71b78fb | ||
|
|
93f7719eed | ||
|
|
4038ca2e5d | ||
|
|
e11e1dcf2d | ||
|
|
f1e278c30f | ||
|
|
980973d83e | ||
|
|
45aee996c1 | ||
|
|
a2952ac213 | ||
|
|
5e35c69f35 | ||
|
|
2c2ef9252f | ||
|
|
06cf705501 | ||
|
|
0d7de7c2db | ||
|
|
baff865d7c | ||
|
|
7bd603061c | ||
|
|
a4bfae1b55 | ||
|
|
b15e1a3501 | ||
|
|
197b5d19bc | ||
|
|
0d24b1a31b | ||
|
|
53d92318b8 | ||
|
|
d4f871cb6a |
@@ -22,12 +22,10 @@ rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
# Download tools needed to build an AppImage
|
||||
wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
|
||||
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
|
||||
# Set executable bit
|
||||
chmod 755 \
|
||||
appimagetool-x86_64.AppImage \
|
||||
AppRun-patched-x86_64 \
|
||||
exec-x86_64.so \
|
||||
linuxdeploy-x86_64.AppImage \
|
||||
@@ -49,6 +47,3 @@ cp exec-x86_64.so AppDir/usr/optional/exec.so
|
||||
cp AppRun-patched-x86_64 AppDir/AppRun
|
||||
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
|
||||
|
||||
# Build the AppImage
|
||||
./appimagetool-x86_64.AppImage AppDir
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
APPIMAGE_NAME="yuzu-x86_64.AppImage"
|
||||
NEW_APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}-x86_64.AppImage"
|
||||
APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
|
||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
@@ -19,7 +18,24 @@ mkdir "$DIR_NAME"
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
|
||||
# Copy the AppImage to the artifacts directory and avoid compressing it
|
||||
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/${NEW_APPIMAGE_NAME}"
|
||||
# Build an AppImage
|
||||
cd build
|
||||
|
||||
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
chmod 755 appimagetool-x86_64.AppImage
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
# Generate update information if releasing to mainline
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
else
|
||||
./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# Copy the AppImage and update info to the artifacts directory and avoid compressing it
|
||||
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
|
||||
if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
|
||||
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
|
||||
fi
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -261,7 +261,7 @@ if(ENABLE_SDL2)
|
||||
find_package(SDL2)
|
||||
if (NOT SDL2_FOUND)
|
||||
# otherwise add this to the list of libraries to install
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ else()
|
||||
-Werror=reorder
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
-Werror=unused-function
|
||||
-Werror=unused-result
|
||||
-Werror=unused-variable
|
||||
-Wextra
|
||||
@@ -129,7 +130,6 @@ add_subdirectory(tests)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
add_subdirectory(yuzu_tester)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -167,8 +165,6 @@ add_library(common STATIC
|
||||
threadsafe_queue.h
|
||||
time_zone.cpp
|
||||
time_zone.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
tree.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
|
||||
@@ -9,50 +9,45 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
|
||||
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
return (value & 0b11) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned_t<T>;
|
||||
const U mask = static_cast<U>(alignment - 1);
|
||||
return (value & mask) == 0;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align = 16>
|
||||
template <typename T, size_t Align = 16>
|
||||
class AlignmentAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -21,124 +18,30 @@ 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]] constexpr u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(std::countl_zero(value));
|
||||
}
|
||||
|
||||
[[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]] constexpr u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(std::countl_zero(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) {
|
||||
unsigned long result;
|
||||
_BitScanReverse(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse64(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(__builtin_clz(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(__builtin_clzll(value));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) {
|
||||
return MostSignificantBit32(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + ((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) {
|
||||
return MostSignificantBit64(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
|
||||
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = Log2Floor64(value);
|
||||
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
1322
src/common/tree.h
1322
src/common/tree.h
File diff suppressed because it is too large
Load Diff
@@ -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}} {}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -643,10 +643,9 @@ else()
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<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>
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -21,21 +21,18 @@ public:
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
|
||||
|
||||
float touch_x = 0.0f; ///< Touchpad X-position
|
||||
float touch_y = 0.0f; ///< Touchpad Y-position
|
||||
Input::TouchStatus status;
|
||||
|
||||
private:
|
||||
class Device : public Input::TouchDevice {
|
||||
public:
|
||||
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
Input::TouchStatus GetStatus() const override {
|
||||
if (auto state = touch_state.lock()) {
|
||||
std::lock_guard guard{state->mutex};
|
||||
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
|
||||
return state->status;
|
||||
}
|
||||
return std::make_tuple(0.0f, 0.0f, false);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
|
||||
return std::make_tuple(new_x, new_y);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
|
||||
return;
|
||||
}
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_x =
|
||||
const float x =
|
||||
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
|
||||
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
|
||||
touch_state->touch_y =
|
||||
const float y =
|
||||
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
|
||||
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
|
||||
|
||||
touch_state->touch_pressed = true;
|
||||
touch_state->status[id] = std::make_tuple(x, y, true);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchReleased() {
|
||||
void EmuWindow::TouchReleased(std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_pressed = false;
|
||||
touch_state->touch_x = 0;
|
||||
touch_state->touch_y = 0;
|
||||
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!touch_state->touch_pressed)
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
if (!std::get<2>(touch_state->status[id]))
|
||||
return;
|
||||
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
|
||||
|
||||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
TouchPressed(framebuffer_x, framebuffer_y, id);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
|
||||
@@ -117,18 +117,23 @@ public:
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||
* @param framebuffer_y Framebuffer y-coordinate that was pressed
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/// Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
void TouchReleased();
|
||||
/**
|
||||
* Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchReleased(std::size_t id);
|
||||
|
||||
/**
|
||||
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
|
||||
* @param framebuffer_x Framebuffer x-coordinate
|
||||
* @param framebuffer_y Framebuffer y-coordinate
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/**
|
||||
* Returns currently active configuration.
|
||||
|
||||
@@ -163,10 +163,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
|
||||
using MotionDevice = InputDevice<MotionStatus>;
|
||||
|
||||
/**
|
||||
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
|
||||
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
|
||||
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
|
||||
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
|
||||
* pressed.
|
||||
*/
|
||||
using TouchStatus = std::tuple<float, float, bool>;
|
||||
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
|
||||
|
||||
/**
|
||||
* A touch device is an input device that returns a touch status object
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) || ...);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -534,7 +534,7 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Common::UUID user_id;
|
||||
Common::UUID user_id{Common::INVALID_UUID};
|
||||
};
|
||||
|
||||
// 6.0.0+
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -16,7 +17,13 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
void Controller_Touchscreen::OnInit() {
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
mouse_finger_id[id] = MAX_FINGERS;
|
||||
keyboard_finger_id[id] = MAX_FINGERS;
|
||||
udp_finger_id[id] = MAX_FINGERS;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
|
||||
@@ -40,38 +47,106 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
bool pressed = false;
|
||||
float x, y;
|
||||
std::tie(x, y, pressed) = touch_device->GetStatus();
|
||||
auto& touch_entry = cur_entry.states[0];
|
||||
touch_entry.attribute.raw = 0;
|
||||
if (!pressed && touch_btn_device) {
|
||||
std::tie(x, y, pressed) = touch_btn_device->GetStatus();
|
||||
}
|
||||
if (pressed && Settings::values.touchscreen.enabled) {
|
||||
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
const u64 tick = core_timing.GetCPUTicks();
|
||||
touch_entry.delta_time = tick - last_touch;
|
||||
last_touch = tick;
|
||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||
cur_entry.entry_count = 1;
|
||||
} else {
|
||||
cur_entry.entry_count = 0;
|
||||
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
|
||||
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
|
||||
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
|
||||
}
|
||||
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
keyboard_finger_id[id] =
|
||||
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<Finger, 16> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
[](const auto& finger) { return finger.pressed; });
|
||||
const auto active_fingers_count =
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
const u64 tick = core_timing.GetCPUTicks();
|
||||
cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
auto& touch_entry = cur_entry.states[id];
|
||||
if (id < active_fingers_count) {
|
||||
touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
touch_entry.delta_time = tick - active_fingers[id].last_touch;
|
||||
fingers[active_fingers[id].id].last_touch = tick;
|
||||
touch_entry.finger = active_fingers[id].id;
|
||||
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
|
||||
} else {
|
||||
// Clear touch entry
|
||||
touch_entry.attribute.raw = 0;
|
||||
touch_entry.x = 0;
|
||||
touch_entry.y = 0;
|
||||
touch_entry.diameter_x = 0;
|
||||
touch_entry.diameter_y = 0;
|
||||
touch_entry.rotation_angle = 0;
|
||||
touch_entry.delta_time = 0;
|
||||
touch_entry.finger = 0;
|
||||
}
|
||||
}
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnLoadInputDevices() {
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
} else {
|
||||
touch_btn_device.reset();
|
||||
}
|
||||
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
|
||||
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_FINGERS) {
|
||||
if (!fingers[first_free_id].pressed) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
|
||||
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
|
||||
const auto& [x, y, pressed] = touch_input;
|
||||
if (pressed) {
|
||||
Attributes attribute{};
|
||||
if (finger_id == MAX_FINGERS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id do nothing
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
finger_id = first_free_id.value();
|
||||
fingers[finger_id].pressed = true;
|
||||
fingers[finger_id].id = static_cast<u32_le>(finger_id);
|
||||
attribute.start_touch.Assign(1);
|
||||
}
|
||||
fingers[finger_id].x = x;
|
||||
fingers[finger_id].y = y;
|
||||
fingers[finger_id].attribute = attribute;
|
||||
return finger_id;
|
||||
}
|
||||
|
||||
if (finger_id != MAX_FINGERS) {
|
||||
if (!fingers[finger_id].attribute.end_touch) {
|
||||
fingers[finger_id].attribute.end_touch.Assign(1);
|
||||
fingers[finger_id].attribute.start_touch.Assign(0);
|
||||
return finger_id;
|
||||
}
|
||||
fingers[finger_id].pressed = false;
|
||||
}
|
||||
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -30,6 +30,18 @@ public:
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGERS = 16;
|
||||
|
||||
// Returns an unused finger id, if there is no fingers available std::nullopt will be returned
|
||||
std::optional<std::size_t> GetUnusedFingerID() const;
|
||||
|
||||
// If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
|
||||
// changes will be made. Updates the coordinates if the finger id it's already set. If the touch
|
||||
// ends delays the output by one frame to set the end_touch flag before finally freeing the
|
||||
// finger id
|
||||
std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
|
||||
std::size_t finger_id);
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32 raw{};
|
||||
@@ -55,7 +67,7 @@ private:
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
s32_le entry_count;
|
||||
std::array<TouchState, 16> states;
|
||||
std::array<TouchState, MAX_FINGERS> states;
|
||||
};
|
||||
static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
|
||||
|
||||
@@ -66,9 +78,23 @@ private:
|
||||
};
|
||||
static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
|
||||
"TouchScreenSharedMemory is an invalid size");
|
||||
|
||||
struct Finger {
|
||||
u64_le last_touch{};
|
||||
float x{};
|
||||
float y{};
|
||||
u32_le id{};
|
||||
bool pressed{};
|
||||
Attributes attribute;
|
||||
};
|
||||
|
||||
TouchScreenSharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_udp_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_btn_device;
|
||||
s64_le last_touch{};
|
||||
std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> udp_finger_id;
|
||||
std::array<Finger, MAX_FINGERS> fingers;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -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>()};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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{};
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -25,18 +25,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
for (const auto& m : map) {
|
||||
const bool state = std::get<0>(m)->GetStatus();
|
||||
Input::TouchStatus GetStatus() const override {
|
||||
Input::TouchStatus touch_status{};
|
||||
for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) {
|
||||
const bool state = std::get<0>(map[id])->GetStatus();
|
||||
if (state) {
|
||||
const float x = static_cast<float>(std::get<1>(m)) /
|
||||
const float x = static_cast<float>(std::get<1>(map[id])) /
|
||||
static_cast<int>(Layout::ScreenUndocked::Width);
|
||||
const float y = static_cast<float>(std::get<2>(m)) /
|
||||
const float y = static_cast<float>(std::get<2>(map[id])) /
|
||||
static_cast<int>(Layout::ScreenUndocked::Height);
|
||||
return {x, y, true};
|
||||
touch_status[id] = {x, y, true};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return touch_status;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -136,6 +136,7 @@ static void SocketLoop(Socket* socket) {
|
||||
|
||||
Client::Client() {
|
||||
LOG_INFO(Input, "Udp Initialization started");
|
||||
finger_id.fill(MAX_TOUCH_FINGERS);
|
||||
ReloadSockets();
|
||||
}
|
||||
|
||||
@@ -176,7 +177,7 @@ void Client::ReloadSockets() {
|
||||
std::string server_token;
|
||||
std::size_t client = 0;
|
||||
while (std::getline(servers_ss, server_token, ',')) {
|
||||
if (client == max_udp_clients) {
|
||||
if (client == MAX_UDP_CLIENTS) {
|
||||
break;
|
||||
}
|
||||
std::stringstream server_ss(server_token);
|
||||
@@ -194,7 +195,7 @@ void Client::ReloadSockets() {
|
||||
for (std::size_t pad = 0; pad < 4; ++pad) {
|
||||
const std::size_t client_number =
|
||||
GetClientNumber(udp_input_address, udp_input_port, pad);
|
||||
if (client_number != max_udp_clients) {
|
||||
if (client_number != MAX_UDP_CLIENTS) {
|
||||
LOG_ERROR(Input, "Duplicated UDP servers found");
|
||||
continue;
|
||||
}
|
||||
@@ -213,7 +214,7 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t
|
||||
return client;
|
||||
}
|
||||
}
|
||||
return max_udp_clients;
|
||||
return MAX_UDP_CLIENTS;
|
||||
}
|
||||
|
||||
void Client::OnVersion([[maybe_unused]] Response::Version data) {
|
||||
@@ -259,33 +260,14 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
|
||||
std::lock_guard guard(clients[client].status.update_mutex);
|
||||
clients[client].status.motion_status = clients[client].motion.GetMotion();
|
||||
|
||||
// TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
|
||||
// between a simple "tap" and a hard press that causes the touch screen to click.
|
||||
const bool is_active = data.touch_1.is_active != 0;
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
if (is_active && clients[client].status.touch_calibration) {
|
||||
const u16 min_x = clients[client].status.touch_calibration->min_x;
|
||||
const u16 max_x = clients[client].status.touch_calibration->max_x;
|
||||
const u16 min_y = clients[client].status.touch_calibration->min_y;
|
||||
const u16 max_y = clients[client].status.touch_calibration->max_y;
|
||||
|
||||
x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) -
|
||||
min_x) /
|
||||
static_cast<float>(max_x - min_x);
|
||||
y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) -
|
||||
min_y) /
|
||||
static_cast<float>(max_y - min_y);
|
||||
for (std::size_t id = 0; id < data.touch.size(); ++id) {
|
||||
UpdateTouchInput(data.touch[id], client, id);
|
||||
}
|
||||
|
||||
clients[client].status.touch_status = {x, y, is_active};
|
||||
|
||||
if (configuring) {
|
||||
const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
|
||||
const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
|
||||
UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
|
||||
UpdateYuzuSettings(client, accelerometer, gyroscope);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,21 +302,17 @@ void Client::Reset() {
|
||||
}
|
||||
|
||||
void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro, bool touch) {
|
||||
const Common::Vec3<float>& gyro) {
|
||||
if (gyro.Length() > 0.2f) {
|
||||
LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
|
||||
client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
|
||||
LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client,
|
||||
gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]);
|
||||
}
|
||||
UDPPadStatus pad{
|
||||
.host = clients[client].host,
|
||||
.port = clients[client].port,
|
||||
.pad_index = clients[client].pad_index,
|
||||
};
|
||||
if (touch) {
|
||||
pad.touch = PadTouch::Click;
|
||||
pad_queue.Push(pad);
|
||||
}
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
for (std::size_t i = 0; i < 3; ++i) {
|
||||
if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i);
|
||||
pad.motion_value = gyro[i];
|
||||
@@ -348,6 +326,50 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Client::GetUnusedFingerID() const {
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_TOUCH_FINGERS) {
|
||||
if (!std::get<2>(touch_status[first_free_id])) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
|
||||
// TODO: Use custom calibration per device
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device);
|
||||
const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
|
||||
const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
|
||||
const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
|
||||
const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
|
||||
const std::size_t touch_id = client * 2 + id;
|
||||
if (touch_pad.is_active) {
|
||||
if (finger_id[touch_id] == MAX_TOUCH_FINGERS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id skip to next input
|
||||
return;
|
||||
}
|
||||
finger_id[touch_id] = *first_free_id;
|
||||
}
|
||||
auto& [x, y, pressed] = touch_status[finger_id[touch_id]];
|
||||
x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
|
||||
static_cast<float>(max_x - min_x);
|
||||
y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
|
||||
static_cast<float>(max_y - min_y);
|
||||
pressed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (finger_id[touch_id] != MAX_TOUCH_FINGERS) {
|
||||
touch_status[finger_id[touch_id]] = {};
|
||||
finger_id[touch_id] = MAX_TOUCH_FINGERS;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::BeginConfiguration() {
|
||||
pad_queue.Clear();
|
||||
configuring = true;
|
||||
@@ -360,7 +382,7 @@ void Client::EndConfiguration() {
|
||||
|
||||
DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
|
||||
const std::size_t client_number = GetClientNumber(host, port, pad);
|
||||
if (client_number == max_udp_clients) {
|
||||
if (client_number == MAX_UDP_CLIENTS) {
|
||||
return clients[0].status;
|
||||
}
|
||||
return clients[client_number].status;
|
||||
@@ -368,12 +390,20 @@ DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t
|
||||
|
||||
const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
|
||||
const std::size_t client_number = GetClientNumber(host, port, pad);
|
||||
if (client_number == max_udp_clients) {
|
||||
if (client_number == MAX_UDP_CLIENTS) {
|
||||
return clients[0].status;
|
||||
}
|
||||
return clients[client_number].status;
|
||||
}
|
||||
|
||||
Input::TouchStatus& Client::GetTouchState() {
|
||||
return touch_status;
|
||||
}
|
||||
|
||||
const Input::TouchStatus& Client::GetTouchState() const {
|
||||
return touch_status;
|
||||
}
|
||||
|
||||
Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
@@ -426,24 +456,24 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch_1.is_active == 0) {
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
|
||||
data.touch_1.y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch_1.x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch_1.y));
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch_1.y - min_y > CALIBRATION_THRESHOLD) {
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch_1.x;
|
||||
max_y = data.touch_1.y;
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
@@ -28,6 +28,7 @@ class Socket;
|
||||
namespace Response {
|
||||
struct PadData;
|
||||
struct PortInfo;
|
||||
struct TouchPad;
|
||||
struct Version;
|
||||
} // namespace Response
|
||||
|
||||
@@ -50,7 +51,6 @@ struct UDPPadStatus {
|
||||
std::string host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
std::size_t pad_index{};
|
||||
PadTouch touch{PadTouch::Undefined};
|
||||
PadMotion motion{PadMotion::Undefined};
|
||||
f32 motion_value{0.0f};
|
||||
};
|
||||
@@ -93,6 +93,9 @@ public:
|
||||
DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
|
||||
const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
|
||||
|
||||
Input::TouchStatus& GetTouchState();
|
||||
const Input::TouchStatus& GetTouchState() const;
|
||||
|
||||
private:
|
||||
struct ClientData {
|
||||
std::string host{"127.0.0.1"};
|
||||
@@ -122,14 +125,25 @@ private:
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port,
|
||||
std::size_t pad_index, u32 client_id);
|
||||
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro, bool touch);
|
||||
const Common::Vec3<float>& gyro);
|
||||
|
||||
// Returns an unused finger id, if there is no fingers available std::nullopt will be
|
||||
// returned
|
||||
std::optional<std::size_t> GetUnusedFingerID() const;
|
||||
|
||||
// Merges and updates all touch inputs into the touch_status array
|
||||
void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id);
|
||||
|
||||
bool configuring = false;
|
||||
|
||||
// Allocate clients for 8 udp servers
|
||||
const std::size_t max_udp_clients = 32;
|
||||
std::array<ClientData, 4 * 8> clients;
|
||||
Common::SPSCQueue<UDPPadStatus> pad_queue;
|
||||
static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8;
|
||||
// Each client can have up 2 touch inputs
|
||||
static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
|
||||
std::array<ClientData, MAX_UDP_CLIENTS> clients{};
|
||||
Common::SPSCQueue<UDPPadStatus> pad_queue{};
|
||||
Input::TouchStatus touch_status{};
|
||||
std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
|
||||
};
|
||||
|
||||
/// An async job allowing configuration of the touchpad calibration.
|
||||
|
||||
@@ -140,6 +140,14 @@ static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong si
|
||||
static_assert(std::is_trivially_copyable_v<PortInfo>,
|
||||
"UDP Response PortInfo is not trivially copyable");
|
||||
|
||||
struct TouchPad {
|
||||
u8 is_active{};
|
||||
u8 id{};
|
||||
u16_le x{};
|
||||
u16_le y{};
|
||||
};
|
||||
static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PadData {
|
||||
PortInfo info{};
|
||||
@@ -190,12 +198,7 @@ struct PadData {
|
||||
u8 button_13{};
|
||||
} analog_button;
|
||||
|
||||
struct TouchPad {
|
||||
u8 is_active{};
|
||||
u8 id{};
|
||||
u16_le x{};
|
||||
u16_le y{};
|
||||
} touch_1, touch_2;
|
||||
std::array<TouchPad, 2> touch;
|
||||
|
||||
u64_le motion_timestamp;
|
||||
|
||||
@@ -222,7 +225,6 @@ static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
|
||||
|
||||
static_assert(sizeof(PadData::AnalogButton) == 12,
|
||||
"UDP Response AnalogButton struct has wrong size ");
|
||||
static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Accelerometer) == 12,
|
||||
"UDP Response Accelerometer struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
|
||||
|
||||
@@ -78,8 +78,8 @@ public:
|
||||
explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
|
||||
: ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
|
||||
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
return client->GetPadState(ip, port, pad).touch_status;
|
||||
Input::TouchStatus GetStatus() const override {
|
||||
return client->GetTouchState();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -107,32 +107,4 @@ std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamP
|
||||
return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
|
||||
}
|
||||
|
||||
void UDPTouchFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
client->BeginConfiguration();
|
||||
}
|
||||
|
||||
void UDPTouchFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
client->EndConfiguration();
|
||||
}
|
||||
|
||||
Common::ParamPackage UDPTouchFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
CemuhookUDP::UDPPadStatus pad;
|
||||
auto& queue = client->GetPadQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
|
||||
continue;
|
||||
}
|
||||
params.Set("engine", "cemuhookudp");
|
||||
params.Set("ip", pad.host);
|
||||
params.Set("port", static_cast<u16>(pad.port));
|
||||
params.Set("pad_index", static_cast<u16>(pad.pad_index));
|
||||
params.Set("touch", static_cast<u16>(pad.touch));
|
||||
return params;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
src/tests/video_core/buffer_base.cpp
Normal file
473
src/tests/video_core/buffer_base.cpp
Normal 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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -312,9 +313,7 @@ else()
|
||||
-Werror=pessimizing-move
|
||||
-Werror=redundant-move
|
||||
-Werror=shadow
|
||||
-Werror=switch
|
||||
-Werror=type-limits
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
|
||||
495
src/video_core/buffer_cache/buffer_base.h
Normal file
495
src/video_core/buffer_cache/buffer_base.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/memory/page_table.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -38,6 +39,12 @@ GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
|
||||
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
|
||||
if (it != map_ranges.end() && it->first == gpu_addr) {
|
||||
it->second = size;
|
||||
} else {
|
||||
map_ranges.insert(it, MapRange{gpu_addr, size});
|
||||
}
|
||||
return UpdateRange(gpu_addr, cpu_addr, size);
|
||||
}
|
||||
|
||||
@@ -52,10 +59,16 @@ GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
|
||||
}
|
||||
|
||||
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
|
||||
if (!size) {
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first);
|
||||
if (it != map_ranges.end()) {
|
||||
ASSERT(it->first == gpu_addr);
|
||||
map_ranges.erase(it);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
|
||||
}
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
@@ -218,6 +231,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
|
||||
return system.Memory().GetPointer(*address);
|
||||
}
|
||||
|
||||
size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
|
||||
auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first);
|
||||
--it;
|
||||
return it->second - (gpu_addr - it->first);
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{gpu_src_addr >> page_bits};
|
||||
|
||||
@@ -85,6 +85,9 @@ public:
|
||||
[[nodiscard]] u8* GetPointer(GPUVAddr addr);
|
||||
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
|
||||
|
||||
/// Returns the number of bytes until the end of the memory map containing the given GPU address
|
||||
[[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept;
|
||||
|
||||
/**
|
||||
* ReadBlock and WriteBlock are full read and write operations over virtual
|
||||
* GPU Memory. It's important to use these when GPU memory may not be continuous
|
||||
@@ -150,6 +153,9 @@ private:
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
std::vector<PageEntry> page_table;
|
||||
|
||||
using MapRange = std::pair<GPUVAddr, size_t>;
|
||||
std::vector<MapRange> map_ranges;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -34,17 +34,13 @@ constexpr VkAccessFlags UPLOAD_ACCESS_BARRIERS =
|
||||
constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS =
|
||||
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
|
||||
|
||||
std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const Device& device, VKScheduler& scheduler) {
|
||||
return std::make_unique<VKStreamBuffer>(device, scheduler);
|
||||
}
|
||||
|
||||
} // 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,
|
||||
@@ -53,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,
|
||||
@@ -104,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,
|
||||
@@ -130,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,
|
||||
@@ -168,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;
|
||||
|
||||
|
||||
@@ -1334,7 +1334,10 @@ private:
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
Name(OpUndef(t_void), comment->GetText());
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
// We should insert comments with OpString instead of using named variables
|
||||
Name(OpUndef(t_int), comment->GetText());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameter
|
||||
const Extent3D num_tiles = swizzle.num_tiles;
|
||||
const u32 bytes_per_block = BytesPerBlock(info.format);
|
||||
const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
|
||||
const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block;
|
||||
const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;
|
||||
const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
|
||||
return BlockLinearSwizzle2DParams{
|
||||
.origin{0, 0, 0},
|
||||
@@ -47,7 +47,7 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter
|
||||
const Extent3D num_tiles = swizzle.num_tiles;
|
||||
const u32 bytes_per_block = BytesPerBlock(info.format);
|
||||
const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
|
||||
const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block;
|
||||
const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;
|
||||
|
||||
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT;
|
||||
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth);
|
||||
|
||||
@@ -279,7 +279,7 @@ template <u32 GOB_EXTENT>
|
||||
const bool is_small = IsSmallerThanGobSize(blocks, gob, info.block.depth);
|
||||
const u32 alignment = is_small ? 0 : info.tile_width_spacing;
|
||||
return Extent2D{
|
||||
.width = Common::AlignBits(gobs.width, alignment),
|
||||
.width = Common::AlignUpLog2(gobs.width, alignment),
|
||||
.height = gobs.height,
|
||||
};
|
||||
}
|
||||
@@ -352,7 +352,7 @@ template <u32 GOB_EXTENT>
|
||||
// https://github.com/Ryujinx/Ryujinx/blob/1c9aba6de1520aea5480c032e0ff5664ac1bb36f/Ryujinx.Graphics.Texture/SizeCalculator.cs#L134
|
||||
if (tile_width_spacing > 0) {
|
||||
const u32 alignment_log2 = GOB_SIZE_SHIFT + tile_width_spacing + block.height + block.depth;
|
||||
return Common::AlignBits(size_bytes, alignment_log2);
|
||||
return Common::AlignUpLog2(size_bytes, alignment_log2);
|
||||
}
|
||||
const u32 aligned_height = Common::AlignUp(size.height, tile_size_y);
|
||||
while (block.height != 0 && aligned_height <= (1U << (block.height - 1)) * GOB_SIZE_Y) {
|
||||
@@ -528,9 +528,9 @@ template <u32 GOB_EXTENT>
|
||||
const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing);
|
||||
const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0);
|
||||
return Extent3D{
|
||||
.width = Common::AlignBits(num_tiles.width, alignment),
|
||||
.height = Common::AlignBits(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height),
|
||||
.depth = Common::AlignBits(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth),
|
||||
.width = Common::AlignUpLog2(num_tiles.width, alignment),
|
||||
.height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height),
|
||||
.depth = Common::AlignUpLog2(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,21 +42,24 @@ constexpr u32 Popcnt(u32 n) {
|
||||
|
||||
class InputBitStream {
|
||||
public:
|
||||
constexpr explicit InputBitStream(const u8* ptr, std::size_t start_offset = 0)
|
||||
: cur_byte{ptr}, next_bit{start_offset % 8} {}
|
||||
constexpr explicit InputBitStream(std::span<const u8> data, size_t start_offset = 0)
|
||||
: cur_byte{data.data()}, total_bits{data.size()}, next_bit{start_offset % 8} {}
|
||||
|
||||
constexpr std::size_t GetBitsRead() const {
|
||||
constexpr size_t GetBitsRead() const {
|
||||
return bits_read;
|
||||
}
|
||||
|
||||
constexpr bool ReadBit() {
|
||||
const bool bit = (*cur_byte >> next_bit++) & 1;
|
||||
if (bits_read >= total_bits * 8) {
|
||||
return 0;
|
||||
}
|
||||
const bool bit = ((*cur_byte >> next_bit) & 1) != 0;
|
||||
++next_bit;
|
||||
while (next_bit >= 8) {
|
||||
next_bit -= 8;
|
||||
cur_byte++;
|
||||
++cur_byte;
|
||||
}
|
||||
|
||||
bits_read++;
|
||||
++bits_read;
|
||||
return bit;
|
||||
}
|
||||
|
||||
@@ -79,8 +82,9 @@ public:
|
||||
|
||||
private:
|
||||
const u8* cur_byte;
|
||||
std::size_t next_bit = 0;
|
||||
std::size_t bits_read = 0;
|
||||
size_t total_bits = 0;
|
||||
size_t next_bit = 0;
|
||||
size_t bits_read = 0;
|
||||
};
|
||||
|
||||
class OutputBitStream {
|
||||
@@ -193,15 +197,15 @@ struct IntegerEncodedValue {
|
||||
};
|
||||
};
|
||||
using IntegerEncodedVector = boost::container::static_vector<
|
||||
IntegerEncodedValue, 64,
|
||||
IntegerEncodedValue, 256,
|
||||
boost::container::static_vector_options<
|
||||
boost::container::inplace_alignment<alignof(IntegerEncodedValue)>,
|
||||
boost::container::throw_on_overflow<false>>::type>;
|
||||
|
||||
static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) {
|
||||
// Implement the algorithm in section C.2.12
|
||||
u32 m[5];
|
||||
u32 t[5];
|
||||
std::array<u32, 5> m;
|
||||
std::array<u32, 5> t;
|
||||
u32 T;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
@@ -866,7 +870,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nPartitions,
|
||||
static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, const u32 nPartitions,
|
||||
const u32 nBitsForColorData) {
|
||||
// First figure out how many color values we have
|
||||
u32 nValues = 0;
|
||||
@@ -898,7 +902,7 @@ static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nP
|
||||
// We now have enough to decode our integer sequence.
|
||||
IntegerEncodedVector decodedColorValues;
|
||||
|
||||
InputBitStream colorStream(data);
|
||||
InputBitStream colorStream(data, 0);
|
||||
DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
|
||||
|
||||
// Once we have the decoded values, we need to dequantize them to the 0-255 range
|
||||
@@ -1441,7 +1445,7 @@ static void ComputeEndpos32s(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
|
||||
|
||||
static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
|
||||
const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
|
||||
InputBitStream strm(inBuf.data());
|
||||
InputBitStream strm(inBuf);
|
||||
TexelWeightParams weightParams = DecodeBlockInfo(strm);
|
||||
|
||||
// Was there an error?
|
||||
@@ -1619,15 +1623,16 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
|
||||
|
||||
// Make sure that higher non-texel bits are set to zero
|
||||
const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
|
||||
if (clearByteStart > 0) {
|
||||
if (clearByteStart > 0 && clearByteStart <= texelWeightData.size()) {
|
||||
texelWeightData[clearByteStart - 1] &=
|
||||
static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1);
|
||||
std::memset(texelWeightData.data() + clearByteStart, 0,
|
||||
std::min(16U - clearByteStart, 16U));
|
||||
}
|
||||
std::memset(texelWeightData.data() + clearByteStart, 0, std::min(16U - clearByteStart, 16U));
|
||||
|
||||
IntegerEncodedVector texelWeightValues;
|
||||
|
||||
InputBitStream weightStream(texelWeightData.data());
|
||||
InputBitStream weightStream(texelWeightData);
|
||||
|
||||
DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight,
|
||||
weightParams.GetNumWeightValues());
|
||||
|
||||
@@ -49,7 +49,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
|
||||
// We can configure here a custom pitch
|
||||
// As it's not exposed 'width * bpp' will be the expected pitch.
|
||||
const u32 pitch = width * bytes_per_pixel;
|
||||
const u32 stride = Common::AlignBits(width, stride_alignment) * bytes_per_pixel;
|
||||
const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
|
||||
|
||||
const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
|
||||
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
|
||||
@@ -217,9 +217,9 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
|
||||
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth) {
|
||||
if (tiled) {
|
||||
const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
|
||||
const u32 aligned_height = Common::AlignBits(height, GOB_SIZE_Y_SHIFT + block_height);
|
||||
const u32 aligned_depth = Common::AlignBits(depth, GOB_SIZE_Z_SHIFT + block_depth);
|
||||
const u32 aligned_width = Common::AlignUpLog2(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
|
||||
const u32 aligned_height = Common::AlignUpLog2(height, GOB_SIZE_Y_SHIFT + block_height);
|
||||
const u32 aligned_depth = Common::AlignUpLog2(depth, GOB_SIZE_Z_SHIFT + block_depth);
|
||||
return aligned_width * aligned_height * aligned_depth;
|
||||
} else {
|
||||
return width * height * depth * bytes_per_pixel;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -99,8 +99,7 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
|
||||
});
|
||||
}
|
||||
|
||||
std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
|
||||
vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
|
||||
std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{
|
||||
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
|
||||
VK_FORMAT_A8B8G8R8_UINT_PACK32,
|
||||
@@ -210,7 +209,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
|
||||
Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
|
||||
const vk::InstanceDispatch& dld_)
|
||||
: instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
|
||||
format_properties{GetFormatProperties(physical, dld)} {
|
||||
format_properties{GetFormatProperties(physical)} {
|
||||
CheckSuitability();
|
||||
SetupFamilies(surface);
|
||||
SetupFeatures();
|
||||
@@ -221,6 +220,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
VkPhysicalDeviceFeatures2 features2{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = nullptr,
|
||||
.features{},
|
||||
};
|
||||
const void* first_next = &features2;
|
||||
void** next = &features2.pNext;
|
||||
@@ -256,7 +256,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
.shaderTessellationAndGeometryPointSize = false,
|
||||
.shaderImageGatherExtended = true,
|
||||
.shaderStorageImageExtendedFormats = false,
|
||||
.shaderStorageImageMultisample = true,
|
||||
.shaderStorageImageMultisample = is_shader_storage_image_multisample,
|
||||
.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
|
||||
.shaderStorageImageWriteWithoutFormat = true,
|
||||
.shaderUniformBufferArrayDynamicIndexing = false,
|
||||
@@ -310,6 +310,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
|
||||
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.hostQueryReset = true,
|
||||
};
|
||||
SetNext(next, host_query_reset);
|
||||
@@ -803,6 +804,7 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
|
||||
void Device::SetupFeatures() {
|
||||
const auto supported_features{physical.GetFeatures()};
|
||||
is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
|
||||
is_shader_storage_image_multisample = supported_features.shaderStorageImageMultisample;
|
||||
is_blit_depth_stencil_supported = TestDepthStencilBlits();
|
||||
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features);
|
||||
}
|
||||
|
||||
@@ -272,23 +272,24 @@ private:
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool is_float16_supported{}; ///< Support for float16 arithmetics.
|
||||
bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
|
||||
bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
|
||||
bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
|
||||
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
|
||||
bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
|
||||
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
|
||||
bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
|
||||
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
|
||||
bool ext_robustness2{}; ///< Support for VK_EXT_robustness2.
|
||||
bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
|
||||
bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
|
||||
bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
|
||||
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
|
||||
bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
|
||||
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
|
||||
bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
|
||||
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
|
||||
bool ext_robustness2{}; ///< Support for VK_EXT_robustness2.
|
||||
bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
|
||||
// Asynchronous Graphics Pipeline setting
|
||||
bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
|
||||
|
||||
268
src/video_core/vulkan_common/vulkan_memory_allocator.cpp
Normal file
268
src/video_core/vulkan_common/vulkan_memory_allocator.cpp
Normal file
@@ -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
|
||||
117
src/video_core/vulkan_common/vulkan_memory_allocator.h
Normal file
117
src/video_core/vulkan_common/vulkan_memory_allocator.h
Normal file
@@ -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
|
||||
@@ -144,152 +144,152 @@ inline VkResult Filter(VkResult result) {
|
||||
|
||||
/// Table holding Vulkan instance function pointers.
|
||||
struct InstanceDispatch {
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
|
||||
|
||||
PFN_vkCreateInstance vkCreateInstance;
|
||||
PFN_vkDestroyInstance vkDestroyInstance;
|
||||
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
|
||||
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
|
||||
PFN_vkCreateInstance vkCreateInstance{};
|
||||
PFN_vkDestroyInstance vkDestroyInstance{};
|
||||
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties{};
|
||||
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
|
||||
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
|
||||
PFN_vkCreateDevice vkCreateDevice;
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
|
||||
PFN_vkDestroyDevice vkDestroyDevice;
|
||||
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
|
||||
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
|
||||
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
|
||||
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
|
||||
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
|
||||
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
||||
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
|
||||
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
|
||||
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
|
||||
PFN_vkQueuePresentKHR vkQueuePresentKHR;
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{};
|
||||
PFN_vkCreateDevice vkCreateDevice{};
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{};
|
||||
PFN_vkDestroyDevice vkDestroyDevice{};
|
||||
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{};
|
||||
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
|
||||
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{};
|
||||
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{};
|
||||
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
|
||||
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
|
||||
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
|
||||
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{};
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{};
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR{};
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR{};
|
||||
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR{};
|
||||
PFN_vkQueuePresentKHR vkQueuePresentKHR{};
|
||||
};
|
||||
|
||||
/// Table holding Vulkan device function pointers.
|
||||
struct DeviceDispatch : public InstanceDispatch {
|
||||
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
|
||||
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
|
||||
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||
PFN_vkAllocateMemory vkAllocateMemory;
|
||||
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
||||
PFN_vkBindBufferMemory vkBindBufferMemory;
|
||||
PFN_vkBindImageMemory vkBindImageMemory;
|
||||
PFN_vkCmdBeginQuery vkCmdBeginQuery;
|
||||
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
|
||||
PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT;
|
||||
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT;
|
||||
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
||||
PFN_vkCmdBindPipeline vkCmdBindPipeline;
|
||||
PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT;
|
||||
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
|
||||
PFN_vkCmdBlitImage vkCmdBlitImage;
|
||||
PFN_vkCmdClearAttachments vkCmdClearAttachments;
|
||||
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
|
||||
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
|
||||
PFN_vkCmdCopyImage vkCmdCopyImage;
|
||||
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
|
||||
PFN_vkCmdDispatch vkCmdDispatch;
|
||||
PFN_vkCmdDraw vkCmdDraw;
|
||||
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
|
||||
PFN_vkCmdEndQuery vkCmdEndQuery;
|
||||
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
|
||||
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT;
|
||||
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT;
|
||||
PFN_vkCmdFillBuffer vkCmdFillBuffer;
|
||||
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
|
||||
PFN_vkCmdPushConstants vkCmdPushConstants;
|
||||
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
|
||||
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
|
||||
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
|
||||
PFN_vkCmdSetEvent vkCmdSetEvent;
|
||||
PFN_vkCmdSetScissor vkCmdSetScissor;
|
||||
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
|
||||
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
|
||||
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
|
||||
PFN_vkCmdSetViewport vkCmdSetViewport;
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||
PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
|
||||
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
|
||||
PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
|
||||
PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
|
||||
PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
|
||||
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
|
||||
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
|
||||
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
|
||||
PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
|
||||
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
|
||||
PFN_vkCmdResolveImage vkCmdResolveImage;
|
||||
PFN_vkCreateBuffer vkCreateBuffer;
|
||||
PFN_vkCreateBufferView vkCreateBufferView;
|
||||
PFN_vkCreateCommandPool vkCreateCommandPool;
|
||||
PFN_vkCreateComputePipelines vkCreateComputePipelines;
|
||||
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
|
||||
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
||||
PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR;
|
||||
PFN_vkCreateEvent vkCreateEvent;
|
||||
PFN_vkCreateFence vkCreateFence;
|
||||
PFN_vkCreateFramebuffer vkCreateFramebuffer;
|
||||
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
|
||||
PFN_vkCreateImage vkCreateImage;
|
||||
PFN_vkCreateImageView vkCreateImageView;
|
||||
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
|
||||
PFN_vkCreateQueryPool vkCreateQueryPool;
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass;
|
||||
PFN_vkCreateSampler vkCreateSampler;
|
||||
PFN_vkCreateSemaphore vkCreateSemaphore;
|
||||
PFN_vkCreateShaderModule vkCreateShaderModule;
|
||||
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
|
||||
PFN_vkDestroyBuffer vkDestroyBuffer;
|
||||
PFN_vkDestroyBufferView vkDestroyBufferView;
|
||||
PFN_vkDestroyCommandPool vkDestroyCommandPool;
|
||||
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||
PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR;
|
||||
PFN_vkDestroyEvent vkDestroyEvent;
|
||||
PFN_vkDestroyFence vkDestroyFence;
|
||||
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
|
||||
PFN_vkDestroyImage vkDestroyImage;
|
||||
PFN_vkDestroyImageView vkDestroyImageView;
|
||||
PFN_vkDestroyPipeline vkDestroyPipeline;
|
||||
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
|
||||
PFN_vkDestroyQueryPool vkDestroyQueryPool;
|
||||
PFN_vkDestroyRenderPass vkDestroyRenderPass;
|
||||
PFN_vkDestroySampler vkDestroySampler;
|
||||
PFN_vkDestroySemaphore vkDestroySemaphore;
|
||||
PFN_vkDestroyShaderModule vkDestroyShaderModule;
|
||||
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
|
||||
PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
|
||||
PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
||||
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
|
||||
PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
|
||||
PFN_vkFreeMemory vkFreeMemory;
|
||||
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
|
||||
PFN_vkGetDeviceQueue vkGetDeviceQueue;
|
||||
PFN_vkGetEventStatus vkGetEventStatus;
|
||||
PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
||||
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR;
|
||||
PFN_vkMapMemory vkMapMemory;
|
||||
PFN_vkQueueSubmit vkQueueSubmit;
|
||||
PFN_vkResetFences vkResetFences;
|
||||
PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT;
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT;
|
||||
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT;
|
||||
PFN_vkUnmapMemory vkUnmapMemory;
|
||||
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
|
||||
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||
PFN_vkWaitForFences vkWaitForFences;
|
||||
PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR;
|
||||
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR{};
|
||||
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers{};
|
||||
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets{};
|
||||
PFN_vkAllocateMemory vkAllocateMemory{};
|
||||
PFN_vkBeginCommandBuffer vkBeginCommandBuffer{};
|
||||
PFN_vkBindBufferMemory vkBindBufferMemory{};
|
||||
PFN_vkBindImageMemory vkBindImageMemory{};
|
||||
PFN_vkCmdBeginQuery vkCmdBeginQuery{};
|
||||
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{};
|
||||
PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{};
|
||||
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{};
|
||||
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{};
|
||||
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{};
|
||||
PFN_vkCmdBindPipeline vkCmdBindPipeline{};
|
||||
PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{};
|
||||
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{};
|
||||
PFN_vkCmdBlitImage vkCmdBlitImage{};
|
||||
PFN_vkCmdClearAttachments vkCmdClearAttachments{};
|
||||
PFN_vkCmdCopyBuffer vkCmdCopyBuffer{};
|
||||
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage{};
|
||||
PFN_vkCmdCopyImage vkCmdCopyImage{};
|
||||
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{};
|
||||
PFN_vkCmdDispatch vkCmdDispatch{};
|
||||
PFN_vkCmdDraw vkCmdDraw{};
|
||||
PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
|
||||
PFN_vkCmdEndQuery vkCmdEndQuery{};
|
||||
PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
|
||||
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
|
||||
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
|
||||
PFN_vkCmdFillBuffer vkCmdFillBuffer{};
|
||||
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
|
||||
PFN_vkCmdPushConstants vkCmdPushConstants{};
|
||||
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{};
|
||||
PFN_vkCmdSetDepthBias vkCmdSetDepthBias{};
|
||||
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds{};
|
||||
PFN_vkCmdSetEvent vkCmdSetEvent{};
|
||||
PFN_vkCmdSetScissor vkCmdSetScissor{};
|
||||
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{};
|
||||
PFN_vkCmdSetStencilReference vkCmdSetStencilReference{};
|
||||
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
|
||||
PFN_vkCmdSetViewport vkCmdSetViewport{};
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents{};
|
||||
PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{};
|
||||
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{};
|
||||
PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT{};
|
||||
PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
|
||||
PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
|
||||
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
|
||||
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
|
||||
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
|
||||
PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{};
|
||||
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{};
|
||||
PFN_vkCmdResolveImage vkCmdResolveImage{};
|
||||
PFN_vkCreateBuffer vkCreateBuffer{};
|
||||
PFN_vkCreateBufferView vkCreateBufferView{};
|
||||
PFN_vkCreateCommandPool vkCreateCommandPool{};
|
||||
PFN_vkCreateComputePipelines vkCreateComputePipelines{};
|
||||
PFN_vkCreateDescriptorPool vkCreateDescriptorPool{};
|
||||
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{};
|
||||
PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{};
|
||||
PFN_vkCreateEvent vkCreateEvent{};
|
||||
PFN_vkCreateFence vkCreateFence{};
|
||||
PFN_vkCreateFramebuffer vkCreateFramebuffer{};
|
||||
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{};
|
||||
PFN_vkCreateImage vkCreateImage{};
|
||||
PFN_vkCreateImageView vkCreateImageView{};
|
||||
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
|
||||
PFN_vkCreateQueryPool vkCreateQueryPool{};
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass{};
|
||||
PFN_vkCreateSampler vkCreateSampler{};
|
||||
PFN_vkCreateSemaphore vkCreateSemaphore{};
|
||||
PFN_vkCreateShaderModule vkCreateShaderModule{};
|
||||
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR{};
|
||||
PFN_vkDestroyBuffer vkDestroyBuffer{};
|
||||
PFN_vkDestroyBufferView vkDestroyBufferView{};
|
||||
PFN_vkDestroyCommandPool vkDestroyCommandPool{};
|
||||
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{};
|
||||
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{};
|
||||
PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{};
|
||||
PFN_vkDestroyEvent vkDestroyEvent{};
|
||||
PFN_vkDestroyFence vkDestroyFence{};
|
||||
PFN_vkDestroyFramebuffer vkDestroyFramebuffer{};
|
||||
PFN_vkDestroyImage vkDestroyImage{};
|
||||
PFN_vkDestroyImageView vkDestroyImageView{};
|
||||
PFN_vkDestroyPipeline vkDestroyPipeline{};
|
||||
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{};
|
||||
PFN_vkDestroyQueryPool vkDestroyQueryPool{};
|
||||
PFN_vkDestroyRenderPass vkDestroyRenderPass{};
|
||||
PFN_vkDestroySampler vkDestroySampler{};
|
||||
PFN_vkDestroySemaphore vkDestroySemaphore{};
|
||||
PFN_vkDestroyShaderModule vkDestroyShaderModule{};
|
||||
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR{};
|
||||
PFN_vkDeviceWaitIdle vkDeviceWaitIdle{};
|
||||
PFN_vkEndCommandBuffer vkEndCommandBuffer{};
|
||||
PFN_vkFreeCommandBuffers vkFreeCommandBuffers{};
|
||||
PFN_vkFreeDescriptorSets vkFreeDescriptorSets{};
|
||||
PFN_vkFreeMemory vkFreeMemory{};
|
||||
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements{};
|
||||
PFN_vkGetDeviceQueue vkGetDeviceQueue{};
|
||||
PFN_vkGetEventStatus vkGetEventStatus{};
|
||||
PFN_vkGetFenceStatus vkGetFenceStatus{};
|
||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
|
||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
|
||||
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
|
||||
PFN_vkMapMemory vkMapMemory{};
|
||||
PFN_vkQueueSubmit vkQueueSubmit{};
|
||||
PFN_vkResetFences vkResetFences{};
|
||||
PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{};
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
|
||||
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{};
|
||||
PFN_vkUnmapMemory vkUnmapMemory{};
|
||||
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{};
|
||||
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{};
|
||||
PFN_vkWaitForFences vkWaitForFences{};
|
||||
PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{};
|
||||
};
|
||||
|
||||
/// Loads instance agnostic function pointers.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -290,8 +290,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
QString::fromUtf8(Common::g_scm_branch),
|
||||
QString::fromUtf8(Common::g_scm_desc)));
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
auto* layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
input_subsystem->Initialize();
|
||||
|
||||
@@ -394,7 +394,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
input_subsystem->GetMouse()->PressButton(x, y, event->button());
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchPressed(x, y);
|
||||
this->TouchPressed(x, y, 0);
|
||||
}
|
||||
|
||||
emit MouseActivity();
|
||||
@@ -409,7 +409,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
input_subsystem->GetMouse()->MouseMove(x, y);
|
||||
this->TouchMoved(x, y);
|
||||
this->TouchMoved(x, y, 0);
|
||||
|
||||
emit MouseActivity();
|
||||
}
|
||||
@@ -423,36 +423,72 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
input_subsystem->GetMouse()->ReleaseButton(event->button());
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchReleased();
|
||||
this->TouchReleased(0);
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
// TouchBegin always has exactly one touch point, so take the .first()
|
||||
const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
|
||||
this->TouchPressed(x, y);
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
|
||||
QPointF pos;
|
||||
int active_points = 0;
|
||||
|
||||
// average all active touch points
|
||||
for (const auto& tp : event->touchPoints()) {
|
||||
if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
|
||||
active_points++;
|
||||
pos += tp.pos();
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
}
|
||||
// Release all inactive points
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (!TouchExist(touch_ids[id], touch_points)) {
|
||||
touch_ids[id] = 0;
|
||||
this->TouchReleased(id + 1);
|
||||
}
|
||||
}
|
||||
|
||||
pos /= active_points;
|
||||
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
this->TouchMoved(x, y);
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchEndEvent() {
|
||||
this->TouchReleased();
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] != 0) {
|
||||
touch_ids[id] = 0;
|
||||
this->TouchReleased(id + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == 0) {
|
||||
touch_ids[id] = touch_point.id() + 1;
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
this->TouchPressed(x, y, id + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
this->TouchMoved(x, y, id + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchExist(std::size_t id,
|
||||
const QList<QTouchEvent::TouchPoint>& touch_points) const {
|
||||
return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
|
||||
return id == static_cast<std::size_t>(point.id() + 1);
|
||||
});
|
||||
}
|
||||
|
||||
bool GRenderWindow::event(QEvent* event) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <QImage>
|
||||
#include <QThread>
|
||||
#include <QTouchEvent>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
|
||||
@@ -21,7 +22,6 @@
|
||||
class GRenderWindow;
|
||||
class GMainWindow;
|
||||
class QKeyEvent;
|
||||
class QTouchEvent;
|
||||
class QStringList;
|
||||
|
||||
namespace InputCommon {
|
||||
@@ -191,6 +191,10 @@ private:
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
bool TouchStart(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
@@ -215,6 +219,8 @@ private:
|
||||
|
||||
bool first_frame = false;
|
||||
|
||||
std::array<std::size_t, 16> touch_ids{};
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
@@ -464,13 +464,7 @@ void Config::ReadMouseValues() {
|
||||
void Config::ReadTouchscreenValues() {
|
||||
Settings::values.touchscreen.enabled =
|
||||
ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
|
||||
Settings::values.touchscreen.device =
|
||||
ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
|
||||
Settings::values.touchscreen.finger =
|
||||
ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt();
|
||||
Settings::values.touchscreen.rotation_angle =
|
||||
ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt();
|
||||
Settings::values.touchscreen.diameter_x =
|
||||
@@ -563,7 +557,8 @@ void Config::ReadMotionTouchValues() {
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.touch_device =
|
||||
ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
|
||||
ReadSetting(QStringLiteral("touch_device"),
|
||||
QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.use_touch_from_button =
|
||||
@@ -1005,7 +1000,8 @@ void Config::SavePlayerValue(std::size_t player_index) {
|
||||
static_cast<u8>(Settings::ControllerType::ProController));
|
||||
|
||||
if (!player_prefix.isEmpty()) {
|
||||
WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
|
||||
WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected,
|
||||
player_index == 0);
|
||||
WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
|
||||
player.vibration_enabled, true);
|
||||
WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
|
||||
@@ -1087,10 +1083,7 @@ void Config::SaveTouchscreenValues() {
|
||||
const auto& touchscreen = Settings::values.touchscreen;
|
||||
|
||||
WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
|
||||
WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device),
|
||||
QStringLiteral("engine:emu_window"));
|
||||
|
||||
WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0);
|
||||
WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
|
||||
WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
|
||||
WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
|
||||
|
||||
@@ -117,31 +117,13 @@ void ConfigureDialog::UpdateVisibleTabs() {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::map<QWidget*, QString> widgets = {
|
||||
{ui->generalTab, tr("General")},
|
||||
{ui->systemTab, tr("System")},
|
||||
{ui->profileManagerTab, tr("Profiles")},
|
||||
{ui->inputTab, tr("Controls")},
|
||||
{ui->hotkeysTab, tr("Hotkeys")},
|
||||
{ui->cpuTab, tr("CPU")},
|
||||
{ui->cpuDebugTab, tr("Debug")},
|
||||
{ui->graphicsTab, tr("Graphics")},
|
||||
{ui->graphicsAdvancedTab, tr("Advanced")},
|
||||
{ui->audioTab, tr("Audio")},
|
||||
{ui->debugTab, tr("Debug")},
|
||||
{ui->webTab, tr("Web")},
|
||||
{ui->uiTab, tr("UI")},
|
||||
{ui->filesystemTab, tr("Filesystem")},
|
||||
{ui->serviceTab, tr("Services")},
|
||||
};
|
||||
|
||||
[[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
|
||||
|
||||
ui->tabWidget->clear();
|
||||
|
||||
const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
|
||||
const auto tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
|
||||
|
||||
for (const auto tab : tabs) {
|
||||
for (auto* const tab : tabs) {
|
||||
ui->tabWidget->addTab(tab, tab->accessibleName());
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user