Compare commits

..

85 Commits

Author SHA1 Message Date
Lioncash
d72e675df5 common/logging: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
4f4691502b common/zstd_compression: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
1c23c2ec5e common/uint128: Apply nodiscard and noexcept where applicable 2019-04-15 15:46:17 -04:00
Lioncash
1c13186ec5 common/timer: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
5ef9259e64 common/threadsafe_queue: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
737fafef56 common/thread_queue_list: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
5cbc3dcfe6 common/thread: Apply nodiscard where applicable 2019-04-15 15:46:17 -04:00
Lioncash
1a40a58fd5 common/telemetry: Apply nodiscard where applicable 2019-04-15 15:46:05 -04:00
Lioncash
3519abc87a common/string_util: Apply nodiscard where applicable 2019-04-15 13:18:24 -04:00
Lioncash
f811a4fef0 common/ring_buffer: Apply nodiscard where applicable 2019-04-15 13:16:06 -04:00
Lioncash
52d845f59c common/quaternion: Apply nodiscard and noexcept where applicable 2019-04-15 13:13:11 -04:00
Lioncash
406b04c7de common/param_package: Apply nodiscard where applicable 2019-04-15 13:11:26 -04:00
Lioncash
76eb6c89ed common/page_table: Apply nodiscard where applicable 2019-04-15 13:10:24 -04:00
Lioncash
30f35cbd5b common/multi_level_queue: Apply nodiscard where applicable 2019-04-15 13:09:09 -04:00
Lioncash
329dc99635 common/memory_hook: Apply nodiscard where applicable 2019-04-15 13:06:35 -04:00
Lioncash
07fd12f353 common/math_util: Apply nodiscard and noexcept where applicable
Applies the nodiscard attribute where it would be a bug/logic error to
ignore the return value.

Also marks inline member functions as noexcept where applicable.
2019-04-15 13:02:46 -04:00
Lioncash
a88b2e8c88 common/lz4_compression: Apply nodiscard where applicable
Applies nodiscard where it would 100% be a bug if the return value is
ignored.
2019-04-15 12:58:51 -04:00
Lioncash
4ddf95aba0 common/hex_util: Apply nodiscard where applicable
Applies nodiscard to functions where ignoring the return value would be
a bug.
2019-04-15 12:57:09 -04:00
Lioncash
016a647623 common/file_util: Apply nodiscard where applicable
Applies nodiscard to functions where it would be guaranteed to be a bug
if the return value is ignored.
2019-04-15 12:54:15 -04:00
Lioncash
6d266f4374 common/common_funcs: Apply nodiscard and noexcept where applicable
In all cases, it would be a bug to ignore the return value.
2019-04-15 12:45:58 -04:00
Lioncash
50e452217e common/bit_util: Apply nodiscard and noexcept where applicable
In all scenarios here, it would be a bug to ignore the return values of
these functions.
2019-04-15 12:43:40 -04:00
Lioncash
d0ea421f2d common/bit_field: Apply nodiscard and noexcept where applicable
Marks functions where it would be a bug to ignore the return value with
nodiscard.
2019-04-15 12:43:20 -04:00
Lioncash
10cfa87eb6 common/alignment: Apply nodiscard and noexcept to utility functions
In all scenarios, ignoring the return value of these functions is a bug.
While we're at it, we can also make them noexcept.
2019-04-15 12:36:16 -04:00
bunnei
1f4dfb3998 Merge pull request #2378 from lioncash/ro
ldr: Minor amendments to IPC-related parameters
2019-04-13 22:16:10 -04:00
bunnei
c9454c8422 Merge pull request #2373 from FernandoS27/z32
Set Pixel Format to Z32 if its R32F and depth compare enabled, and Implement format ZF32_X24S8
2019-04-13 22:14:51 -04:00
bunnei
6088898b02 Merge pull request #2357 from zarroboogs/force-30fps-mode
Add a toggle to force 30FPS mode
2019-04-13 22:14:04 -04:00
bunnei
a788c861bd Merge pull request #2381 from lioncash/fs
fsp_srv: Minor cleanup related changes
2019-04-13 22:09:58 -04:00
bunnei
ee2206a1b7 Merge pull request #2386 from ReinUsesLisp/shader-manager
gl_shader_manager: Move code to source file and minor clean up
2019-04-13 22:09:27 -04:00
bunnei
065f83c6c3 Merge pull request #2017 from jroweboy/glwidget
Frontend: Migrate to QOpenGLWindow and support shared contexts
2019-04-13 22:08:40 -04:00
bunnei
ee3f576495 Merge pull request #2389 from FreddyFunk/rename-gamedir
ui_settings: Rename game directory variables
2019-04-13 22:06:51 -04:00
bunnei
b42595fa6b Merge pull request #2391 from lioncash/scope
common/scope_exit: Replace std::move with std::forward in ScopeExit()
2019-04-12 21:52:35 -04:00
bunnei
0faf7b17a1 Merge pull request #2392 from lioncash/swap
common/swap: Minor cleanup and improvements to byte swapping functions
2019-04-12 21:52:16 -04:00
FreddyFunk
382722b9c4 Fix Clang Format 2019-04-12 16:40:35 +02:00
Lioncash
0d8ef2d3b9 common/swap: Improve codegen of the default swap fallbacks
Uses arithmetic that can be identified more trivially by compilers for
optimizations. e.g. Rather than shifting the halves of the value and
then swapping and combining them, we can swap them in place.

e.g. for the original swap32 code on x86-64, clang 8.0 would generate:

    mov     ecx, edi
    rol     cx, 8
    shl     ecx, 16
    shr     edi, 16
    rol     di, 8
    movzx   eax, di
    or      eax, ecx
    ret

while GCC 8.3 would generate the ideal:

    mov     eax, edi
    bswap   eax
    ret

now both generate the same optimal output.

MSVC used to generate the following with the old code:

    mov     eax, ecx
    rol     cx, 8
    shr     eax, 16
    rol     ax, 8
    movzx   ecx, cx
    movzx   eax, ax
    shl     ecx, 16
    or      eax, ecx
    ret     0

Now MSVC also generates a similar, but equally optimal result as clang/GCC:

    bswap   ecx
    mov     eax, ecx
    ret     0

====

In the swap64 case, for the original code, clang 8.0 would generate:

    mov     eax, edi
    bswap   eax
    shl     rax, 32
    shr     rdi, 32
    bswap   edi
    or      rax, rdi
    ret

(almost there, but still missing the mark)

while, again, GCC 8.3 would generate the more ideal:

    mov     rax, rdi
    bswap   rax
    ret

now clang also generates the optimal sequence for this fallback as well.

This is a case where MSVC unfortunately falls short, despite the new
code, this one still generates a doozy of an output.

    mov     r8, rcx
    mov     r9, rcx
    mov     rax, 71776119061217280
    mov     rdx, r8
    and     r9, rax
    and     edx, 65280
    mov     rax, rcx
    shr     rax, 16
    or      r9, rax
    mov     rax, rcx
    shr     r9, 16
    mov     rcx, 280375465082880
    and     rax, rcx
    mov     rcx, 1095216660480
    or      r9, rax
    mov     rax, r8
    and     rax, rcx
    shr     r9, 16
    or      r9, rax
    mov     rcx, r8
    mov     rax, r8
    shr     r9, 8
    shl     rax, 16
    and     ecx, 16711680
    or      rdx, rax
    mov     eax, -16777216
    and     rax, r8
    shl     rdx, 16
    or      rdx, rcx
    shl     rdx, 16
    or      rax, rdx
    shl     rax, 8
    or      rax, r9
    ret     0

which is pretty unfortunate.
2019-04-12 00:07:39 -04:00
bunnei
ea80e2bc57 Merge pull request #2235 from ReinUsesLisp/spirv-decompiler
vk_shader_decompiler: Implement a SPIR-V decompiler
2019-04-11 21:54:23 -04:00
bunnei
83a2fb3c3a Merge pull request #2360 from lioncash/svc-global
kernel/svc: Deglobalize the supervisor call handlers
2019-04-11 21:50:05 -04:00
bunnei
e2f2155dab Merge pull request #2388 from lioncash/constexpr
kernel: Make handle type declarations constexpr
2019-04-11 21:49:45 -04:00
bunnei
c0b2b7020d Merge pull request #2387 from FernandoS27/fast-copy-relax
gl_rasterizer_cache: Relax restrictions on FastCopySurface
2019-04-11 21:49:21 -04:00
Lioncash
66b73fd399 common/swap: Mark byte swapping free functions with [[nodiscard]] and noexcept
Allows the compiler to inform when the result of a swap function is
being ignored (which is 100% a bug in all usage scenarios). We also mark
them noexcept to allow other functions using them to be able to be
marked as noexcept and play nicely with things that potentially inspect
"nothrowability".
2019-04-11 20:42:44 -04:00
Lioncash
9cb4b7be40 common/swap: Simplify swap function ifdefs
Including every OS' own built-in byte swapping functions is kind of
undesirable, since it adds yet another build path to ensure compilation
succeeds on.

Given we only support clang, GCC, and MSVC for the time being, we can
utilize their built-in functions directly instead of going through the
OS's API functions.

This shrinks the overall code down to just

if (msvc)
  use msvc's functions
else if (clang or gcc)
  use clang/gcc's builtins
else
  use the slow path
2019-04-11 20:36:19 -04:00
Lioncash
598954436f common/swap: Remove 32-bit ARM path
We don't plan to support host 32-bit ARM execution environments, so this
is essentially dead code.
2019-04-11 20:15:47 -04:00
Lioncash
b569641098 common/scope_exit: Replace std::move with std::forward in ScopeExit()
The template type here is actually a forwarding reference, not an rvalue
reference in this case, so it's more appropriate to use std::forward to
preserve the value category of the type being moved.
2019-04-11 20:01:33 -04:00
Lioncash
6300ccbc3c kernel: Make handle type declarations constexpr
Some objects declare their handle type as const, while others declare it
as constexpr. This makes the const ones constexpr for consistency, and
prevent unexpected compilation errors if these happen to be attempted to be
used within a constexpr context.
2019-04-11 16:34:53 -04:00
FreddyFunk
dffa1a872a ui_settings: Rename game directory variables 2019-04-11 19:55:56 +02:00
Fernando Sahmkow
c9305959d3 gl_rasterizer_cache: Relax restrictions on FastCopySurface and FastLayeredCopySurface 2019-04-11 13:14:28 -04:00
bunnei
6951741a94 Merge pull request #2278 from ReinUsesLisp/vc-texture-cache
video_core: Implement API agnostic view based texture cache
2019-04-10 21:17:35 -04:00
bunnei
0371650bd7 Merge pull request #2372 from FernandoS27/fermi-fix
Correct Fermi Copy on Linear Textures.
2019-04-10 21:17:03 -04:00
ReinUsesLisp
93af663683 gl_shader_manager: Move code to source file and minor clean up 2019-04-10 19:29:15 -03:00
Lioncash
dae2449880 ldr: Mark IsValidNROHash() as a const member function
This doesn't modify instance state, so it can be made const.
2019-04-10 15:57:02 -04:00
Lioncash
0032cf3818 ldr: Amend parameters for LoadNro/UnloadNro LoadNrr/UnloadNrr
The initial two words indicate a process ID. Also UnloadNro only
specifies one address, not two.
2019-04-10 15:56:43 -04:00
ReinUsesLisp
75d23a3679 vk_shader_decompiler: Implement flow primitives 2019-04-10 14:20:25 -03:00
ReinUsesLisp
58ad8dfac6 vk_shader_decompiler: Implement most common texture primitives 2019-04-10 14:20:25 -03:00
ReinUsesLisp
4667ed8e22 vk_shader_decompiler: Implement texture decompilation helper functions 2019-04-10 14:20:25 -03:00
ReinUsesLisp
676172e20d vk_shader_decompiler: Implement Assign and LogicalAssign 2019-04-10 14:20:25 -03:00
ReinUsesLisp
d316d248ab vk_shader_decompiler: Implement non-OperationCode visits 2019-04-10 14:20:25 -03:00
ReinUsesLisp
b758c861b0 vk_shader_decompiler: Implement OperationCode decompilation interface 2019-04-10 14:20:25 -03:00
ReinUsesLisp
fec4eb9776 vk_shader_decompiler: Implement Visit 2019-04-10 14:20:25 -03:00
ReinUsesLisp
ca51f99840 vk_shader_decompiler: Implement labels tree and flow 2019-04-10 14:20:25 -03:00
ReinUsesLisp
13aa664f3f vk_shader_decompiler: Implement declarations 2019-04-10 14:20:25 -03:00
ReinUsesLisp
ad53b233c5 vk_shader_decompiler: Declare and stub interface for a SPIR-V decompiler 2019-04-10 14:20:25 -03:00
ReinUsesLisp
970d9e57c8 video_core: Add sirit as optional dependency with Vulkan
sirit is a runtime assembler for SPIR-V
2019-04-10 14:20:25 -03:00
Lioncash
8676832064 fsp_srv: Remove unnecessary parameter popping in IDirectory's Read()
IDirectory's Read() function doesn't take any input parameters. It only
uses the output parameters that we already provide.
2019-04-10 13:04:08 -04:00
Lioncash
fc436bb09b fsp_srv: Log out option values in IFile's Read and Write functions
These indicate options that alter how a read/write is performed.

Currently we don't need to handle these, as the only one that seems to
be used is for writes, but all the custom options ever seem to do is
immediate flushing, which we already do by default.
2019-04-10 13:01:52 -04:00
bunnei
97648f4841 Merge pull request #2345 from ReinUsesLisp/multibind
gl_rasterizer: Use ARB_multi_bind to update buffers with a single call per drawcall
2019-04-10 11:23:19 -04:00
bunnei
1312cf15d6 Merge pull request #2377 from lioncash/todo
kernel/server_session: Remove obsolete TODOs
2019-04-10 10:29:24 -04:00
Lioncash
08d507a196 kernel/server_session: Remove obsolete TODOs
These are holdovers from Citra.
2019-04-09 23:34:49 -04:00
bunnei
ed9dba89d3 Merge pull request #2375 from FernandoS27/fix-ldc
Remove unnecessary bounding in LD_C
2019-04-09 21:23:24 -04:00
bunnei
f46c3164e7 Merge pull request #2353 from lioncash/surface
yuzu/debugger: Remove graphics surface viewer
2019-04-09 21:20:02 -04:00
Fernando Sahmkow
c9f35d96be Remove bounding in LD_C 2019-04-09 20:02:11 -04:00
Fernando Sahmkow
cd91e98dab Correct Fermi Copy on Linear Textures. 2019-04-09 14:13:58 -04:00
Fernando Sahmkow
7c458311d3 Implement Texture Format ZF32_X24S8. 2019-04-09 12:33:46 -04:00
Fernando Sahmkow
b0aa8ad736 Correct depth compare with color formats for R32F 2019-04-09 12:06:59 -04:00
zarroboogs
be6466d5c0 added a toggle to force 30fps mode 2019-04-09 02:14:03 +03:00
Lioncash
b117ca5fce kernel/svc: Deglobalize the supervisor call handlers
Adjusts the interface of the wrappers to take a system reference, which
allows accessing a system instance without using the global accessors.

This also allows getting rid of all global accessors within the
supervisor call handling code. While this does make the wrappers
themselves slightly more noisy, this will be further cleaned up in a
follow-up. This eliminates the global system accessors in the current
code while preserving the existing interface.
2019-04-07 20:30:05 -04:00
Lioncash
218ae888f3 yuzu/debugger: Remove graphics surface viewer
This doesn't actually work anymore, and given how long it's been left in
that state, it's unlikely anyone actually seriously used it.

Generally it's preferable to use RenderDoc or Nsight to view surfaces.
2019-04-05 23:54:00 -04:00
ReinUsesLisp
34c3e2c786 renderer_opengl/utils: Skip empty binds 2019-04-05 19:19:49 -03:00
ReinUsesLisp
b631c09e72 gl_rasterizer: Use ARB_multi_bind to update SSBOs 2019-04-05 19:18:43 -03:00
ReinUsesLisp
2d1f054c61 gl_rasterizer: Use ARB_multi_bind to update UBOs across stages 2019-04-05 19:10:46 -03:00
ReinUsesLisp
9ebc27234d bootmanager: Bypass input focus issues 2019-03-25 17:10:34 -03:00
ReinUsesLisp
bbb396d7f1 bootmanager: Bypass resizing issue 2019-03-25 17:10:34 -03:00
ReinUsesLisp
9ff72ca9f2 bootmanager: Delete container to avoid crash on game restarting
While we are at it, remove nullptr checks for deletion, since the C++
standard defines that delete does it by its own
2019-03-25 17:10:34 -03:00
ReinUsesLisp
d708d03d20 video_core: Implement API agnostic view based texture cache
Implements an API agnostic texture view based texture cache. Classes
defined here are intended to be inherited by the API implementation and
used in API-specific code.

This implementation exposes protected virtual functions to be called
from the implementer.

Before executing any surface copies methods (defined in API-specific code)
it tries to detect if the overlapping surface is a superset and if it
is, it creates a view. Views are references of a subset of a surface, it
can be a superset view (the same as referencing the whole texture).
Current code manages 1D, 1D array, 2D, 2D array, cube maps and cube map
arrays with layer and mipmap level views. Texture 3D slices views are
not implemented.

If the view attempt fails, the fast path is invoked with the overlapping
textures (defined in the implementer). If that one fails (returning
nullptr) it will flush and reload the texture.
2019-03-22 13:34:04 -03:00
James Rowe
5f2d9f282a QT: Hide GLWidget immediately after showing.
With the loading screen merged, we don't want to actually show at this
point, but it still needs to be shown to actually create the context.
Turns out you can just show and hide it immediately and it'll work.
2019-01-21 16:21:44 -07:00
James Rowe
f2a2f818b6 SDL Frontend: Add shared context support 2019-01-21 16:00:01 -07:00
James Rowe
c6a0ab9792 QT Frontend: Migrate to QOpenGLWindow 2019-01-21 16:00:01 -07:00
87 changed files with 3665 additions and 1541 deletions

3
.gitmodules vendored
View File

@@ -43,3 +43,6 @@
[submodule "externals/zstd"]
path = externals/zstd
url = https://github.com/facebook/zstd
[submodule "sirit"]
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit

View File

@@ -72,6 +72,11 @@ if (USE_DISCORD_PRESENCE)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif()
# Sirit
if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")

1
externals/sirit vendored Submodule

Submodule externals/sirit added at f7c4b07a7e

View File

@@ -8,25 +8,25 @@
namespace Common {
template <typename T>
constexpr T AlignUp(T value, std::size_t size) {
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) noexcept {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value + (size - value % size) % size);
}
template <typename T>
constexpr T AlignDown(T value, std::size_t size) {
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) noexcept {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}
template <typename T>
constexpr bool Is4KBAligned(T value) {
[[nodiscard]] constexpr bool Is4KBAligned(T value) noexcept {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0xFFF) == 0;
}
template <typename T>
constexpr bool IsWordAligned(T value) {
[[nodiscard]] constexpr bool IsWordAligned(T value) noexcept {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0b11) == 0;
}

View File

@@ -135,8 +135,8 @@ public:
* containing several bitfields can be assembled by formatting each of their values and ORing
* the results together.
*/
static constexpr FORCE_INLINE StorageType FormatValue(const T& value) {
return ((StorageType)value << position) & mask;
[[nodiscard]] static constexpr FORCE_INLINE StorageType FormatValue(const T& value) noexcept {
return (static_cast<StorageType>(value) << position) & mask;
}
/**
@@ -144,7 +144,8 @@ public:
* (such as Value() or operator T), but this can be used to extract a value from a bitfield
* union in a constexpr context.
*/
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
[[nodiscard]] static constexpr FORCE_INLINE T
ExtractValue(const StorageType& storage) noexcept {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -168,19 +169,19 @@ public:
constexpr BitField(BitField&&) noexcept = default;
constexpr BitField& operator=(BitField&&) noexcept = default;
constexpr FORCE_INLINE operator T() const {
[[nodiscard]] constexpr FORCE_INLINE operator T() const noexcept {
return Value();
}
constexpr FORCE_INLINE void Assign(const T& value) {
constexpr FORCE_INLINE void Assign(const T& value) noexcept {
storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
}
constexpr T Value() const {
[[nodiscard]] constexpr T Value() const noexcept {
return ExtractValue(storage);
}
constexpr explicit operator bool() const {
[[nodiscard]] constexpr explicit operator bool() const noexcept {
return Value() != 0;
}

View File

@@ -17,12 +17,12 @@ namespace Common {
/// Gets the size of a specified type T in bits.
template <typename T>
constexpr std::size_t BitSize() {
[[nodiscard]] constexpr std::size_t BitSize() noexcept {
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
inline u32 CountLeadingZeroes32(u32 value) {
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) noexcept {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
inline u32 CountLeadingZeroes64(u64 value) {
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) noexcept {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
return 64;
}
#else
inline u32 CountLeadingZeroes32(u32 value) {
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) noexcept {
if (value == 0) {
return 32;
}
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return static_cast<u32>(__builtin_clz(value));
}
inline u32 CountLeadingZeroes64(u64 value) {
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) noexcept {
if (value == 0) {
return 64;
}
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
#endif
#ifdef _MSC_VER
inline u32 CountTrailingZeroes32(u32 value) {
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) noexcept {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
inline u32 CountTrailingZeroes64(u64 value) {
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) noexcept {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
return 64;
}
#else
inline u32 CountTrailingZeroes32(u32 value) {
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) noexcept {
if (value == 0) {
return 32;
}
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return static_cast<u32>(__builtin_ctz(value));
}
inline u32 CountTrailingZeroes64(u64 value) {
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) noexcept {
if (value == 0) {
return 64;
}

View File

@@ -52,11 +52,11 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
std::string GetLastErrorMsg();
[[nodiscard]] std::string GetLastErrorMsg();
namespace Common {
constexpr u32 MakeMagic(char a, char b, char c, char d) {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) noexcept {
return a | b << 8 | c << 16 | d << 24;
}

View File

@@ -46,19 +46,19 @@ struct FSTEntry {
};
// Returns true if file filename exists
bool Exists(const std::string& filename);
[[nodiscard]] bool Exists(const std::string& filename);
// Returns true if filename is a directory
bool IsDirectory(const std::string& filename);
[[nodiscard]] bool IsDirectory(const std::string& filename);
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename);
[[nodiscard]] u64 GetSize(const std::string& filename);
// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd);
[[nodiscard]] u64 GetSize(int fd);
// Overloaded GetSize, accepts FILE*
u64 GetSize(FILE* f);
[[nodiscard]] u64 GetSize(FILE* f);
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& filename);
@@ -118,7 +118,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory
std::string GetCurrentDir();
[[nodiscard]] std::string GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -128,22 +128,22 @@ bool SetCurrentDir(const std::string& directory);
// Returns a pointer to a string with a yuzu data dir in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
[[nodiscard]] const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
std::string GetHactoolConfigurationPath();
[[nodiscard]] std::string GetHactoolConfigurationPath();
std::string GetNANDRegistrationDir(bool system = false);
[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
// Returns the path to where the sys file are
std::string GetSysDirectory();
[[nodiscard]] std::string GetSysDirectory();
#ifdef __APPLE__
std::string GetBundleDirectory();
[[nodiscard]] std::string GetBundleDirectory();
#endif
#ifdef _WIN32
const std::string& GetExeDirectory();
std::string AppDataRoamingDirectory();
[[nodiscard]] const std::string& GetExeDirectory();
[[nodiscard]] std::string AppDataRoamingDirectory();
#endif
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
@@ -162,26 +162,27 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
std::vector<std::string> SplitPathComponents(std::string_view filename);
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
// Gets all of the text up to the last '/' or '\' in the path.
std::string_view GetParentPath(std::string_view path);
[[nodiscard]] std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
std::string_view GetPathWithoutTop(std::string_view path);
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
std::string_view GetFilename(std::string_view path);
[[nodiscard]] std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
std::string_view GetExtensionFromFilename(std::string_view name);
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
// Removes the final '/' or '\' if one exists
std::string_view RemoveTrailingSlash(std::string_view path);
[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
std::size_t last) {
if (first >= last)
return {};
last = std::min<std::size_t>(last, vector.size());
@@ -192,8 +193,9 @@ enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
std::string SanitizePath(std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
[[nodiscard]] std::string SanitizePath(
std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
@@ -261,13 +263,13 @@ public:
return WriteArray(str.c_str(), str.length());
}
bool IsOpen() const {
[[nodiscard]] bool IsOpen() const {
return nullptr != m_file;
}
bool Seek(s64 off, int origin) const;
u64 Tell() const;
u64 GetSize() const;
[[nodiscard]] u64 Tell() const;
[[nodiscard]] u64 GetSize() const;
bool Resize(u64 size);
bool Flush();

View File

@@ -13,12 +13,12 @@
namespace Common {
u8 ToHexNibble(char c1);
[[nodiscard]] u8 ToHexNibble(char c1);
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
[[nodiscard]] std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
@@ -30,17 +30,17 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
[[nodiscard]] std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
[[nodiscard]] std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;
for (u8 c : array)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
[[nodiscard]] std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
[[nodiscard]] std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
} // namespace Common

View File

@@ -46,7 +46,7 @@ public:
virtual void SetFilter(const Filter& new_filter) {
filter = new_filter;
}
virtual const char* GetName() const = 0;
[[nodiscard]] virtual const char* GetName() const = 0;
virtual void Write(const Entry& entry) = 0;
private:
@@ -58,12 +58,14 @@ private:
*/
class ConsoleBackend : public Backend {
public:
static const char* Name() {
[[nodiscard]] static const char* Name() {
return "console";
}
const char* GetName() const override {
[[nodiscard]] const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
@@ -72,13 +74,14 @@ public:
*/
class ColorConsoleBackend : public Backend {
public:
static const char* Name() {
[[nodiscard]] static const char* Name() {
return "color_console";
}
const char* GetName() const override {
[[nodiscard]] const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
@@ -89,11 +92,11 @@ class FileBackend : public Backend {
public:
explicit FileBackend(const std::string& filename);
static const char* Name() {
[[nodiscard]] static const char* Name() {
return "file";
}
const char* GetName() const override {
[[nodiscard]] const char* GetName() const override {
return Name();
}
@@ -109,12 +112,14 @@ private:
*/
class DebuggerBackend : public Backend {
public:
static const char* Name() {
[[nodiscard]] static const char* Name() {
return "debugger";
}
const char* GetName() const override {
[[nodiscard]] const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
@@ -122,18 +127,18 @@ void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(std::string_view backend_name);
[[nodiscard]] Backend* GetBackend(std::string_view backend_name);
/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
* instead of underscores as in the enumeration.
*/
const char* GetLogClassName(Class log_class);
[[nodiscard]] const char* GetLogClassName(Class log_class);
/**
* Returns the name of the passed log level as a C-string.
*/
const char* GetLevelName(Level log_level);
[[nodiscard]] const char* GetLevelName(Level log_level);
/**
* The global filter will prevent any messages from even being processed if they are filtered. Each

View File

@@ -43,10 +43,10 @@ public:
void ParseFilterString(std::string_view filter_view);
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;
[[nodiscard]] bool CheckMessage(Class log_class, Level level) const;
/// Returns true if any logging classes are set to debug
bool IsDebug() const;
[[nodiscard]] bool IsDebug() const;
private:
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;

View File

@@ -12,7 +12,7 @@ namespace Log {
struct Entry;
/// Formats a log entry into the provided text buffer.
std::string FormatLogMessage(const Entry& entry);
[[nodiscard]] std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored according to the severity level.

View File

@@ -16,7 +16,7 @@ namespace Common::Compression {
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
@@ -30,7 +30,8 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
s32 compression_level);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
@@ -40,7 +41,7 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
@@ -50,6 +51,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
std::size_t uncompressed_size);
} // namespace Common::Compression

View File

@@ -18,24 +18,24 @@ struct Rectangle {
T right{};
T bottom{};
constexpr Rectangle() = default;
constexpr Rectangle() noexcept = default;
constexpr Rectangle(T left, T top, T right, T bottom)
constexpr Rectangle(T left, T top, T right, T bottom) noexcept
: left(left), top(top), right(right), bottom(bottom) {}
T GetWidth() const {
[[nodiscard]] T GetWidth() const noexcept {
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
}
T GetHeight() const {
[[nodiscard]] T GetHeight() const noexcept {
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
}
Rectangle<T> TranslateX(const T x) const {
[[nodiscard]] Rectangle<T> TranslateX(const T x) const noexcept {
return Rectangle{left + x, top, right + x, bottom};
}
Rectangle<T> TranslateY(const T y) const {
[[nodiscard]] Rectangle<T> TranslateY(const T y) const noexcept {
return Rectangle{left, top + y, right, bottom + y};
}
Rectangle<T> Scale(const float s) const {
[[nodiscard]] Rectangle<T> Scale(const float s) const noexcept {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}

View File

@@ -26,21 +26,22 @@ class MemoryHook {
public:
virtual ~MemoryHook();
virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
[[nodiscard]] virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
virtual std::optional<u8> Read8(VAddr addr) = 0;
virtual std::optional<u16> Read16(VAddr addr) = 0;
virtual std::optional<u32> Read32(VAddr addr) = 0;
virtual std::optional<u64> Read64(VAddr addr) = 0;
[[nodiscard]] virtual std::optional<u8> Read8(VAddr addr) = 0;
[[nodiscard]] virtual std::optional<u16> Read16(VAddr addr) = 0;
[[nodiscard]] virtual std::optional<u32> Read32(VAddr addr) = 0;
[[nodiscard]] virtual std::optional<u64> Read64(VAddr addr) = 0;
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
[[nodiscard]] virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
virtual bool Write8(VAddr addr, u8 data) = 0;
virtual bool Write16(VAddr addr, u16 data) = 0;
virtual bool Write32(VAddr addr, u32 data) = 0;
virtual bool Write64(VAddr addr, u64 data) = 0;
[[nodiscard]] virtual bool Write8(VAddr addr, u8 data) = 0;
[[nodiscard]] virtual bool Write16(VAddr addr, u16 data) = 0;
[[nodiscard]] virtual bool Write32(VAddr addr, u32 data) = 0;
[[nodiscard]] virtual bool Write64(VAddr addr, u64 data) = 0;
virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
[[nodiscard]] virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer,
std::size_t size) = 0;
};
using MemoryHookPointer = std::shared_ptr<MemoryHook>;

View File

@@ -43,21 +43,21 @@ public:
using reference = std::conditional_t<is_constant, const T&, T&>;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
[[nodiscard]] friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
if (lhs.IsEnd() && rhs.IsEnd())
return true;
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
}
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
[[nodiscard]] friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
return !operator==(lhs, rhs);
}
reference operator*() const {
[[nodiscard]] reference operator*() const {
return *it;
}
pointer operator->() const {
[[nodiscard]] pointer operator->() const {
return it.operator->();
}
@@ -143,15 +143,15 @@ public:
explicit iterator_impl(container_ref mlq, u32 current_priority)
: mlq(mlq), it(), current_priority(current_priority) {}
bool IsEnd() const {
[[nodiscard]] bool IsEnd() const {
return current_priority == mlq.depth();
}
list_iterator GetBeginItForPrio() const {
[[nodiscard]] list_iterator GetBeginItForPrio() const {
return mlq.levels[current_priority].begin();
}
list_iterator GetEndItForPrio() const {
[[nodiscard]] list_iterator GetEndItForPrio() const {
return mlq.levels[current_priority].end();
}
@@ -223,15 +223,15 @@ public:
ListShiftForward(levels[priority], n);
}
std::size_t depth() const {
[[nodiscard]] std::size_t depth() const {
return Depth;
}
std::size_t size(u32 priority) const {
[[nodiscard]] std::size_t size(u32 priority) const {
return levels[priority].size();
}
std::size_t size() const {
[[nodiscard]] std::size_t size() const {
u64 priorities = used_priorities;
std::size_t size = 0;
while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
return size;
}
bool empty() const {
[[nodiscard]] bool empty() const {
return used_priorities == 0;
}
bool empty(u32 priority) const {
[[nodiscard]] bool empty(u32 priority) const {
return (used_priorities & (1ULL << priority)) == 0;
}
u32 highest_priority_set(u32 max_priority = 0) const {
[[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
const u64 priorities =
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
}
u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
[[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
const u64 priorities = min_priority >= Depth - 1
? used_priorities
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
}
const_iterator cbegin(u32 max_prio = 0) const {
[[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? cend()
: const_iterator{*this, levels[priority].cbegin(), priority};
}
const_iterator begin(u32 max_prio = 0) const {
[[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
return cbegin(max_prio);
}
iterator begin(u32 max_prio = 0) {
[[nodiscard]] iterator begin(u32 max_prio = 0) {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
const_iterator cend(u32 min_prio = Depth - 1) const {
[[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
}
const_iterator end(u32 min_prio = Depth - 1) const {
[[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
return cend(min_prio);
}
iterator end(u32 min_prio = Depth - 1) {
[[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
}
T& front(u32 max_priority = 0) {
[[nodiscard]] T& front(u32 max_priority = 0) {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
const T& front(u32 max_priority = 0) const {
[[nodiscard]] const T& front(u32 max_priority = 0) const {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
T back(u32 min_priority = Depth - 1) {
[[nodiscard]] T back(u32 min_priority = Depth - 1) {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
const T& back(u32 min_priority = Depth - 1) const {
[[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
@@ -322,7 +322,8 @@ private:
in_list.splice(position, out_list, element);
}
static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
[[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
const T& element) {
auto it = list.cbegin();
while (it != list.cend() && *it != element) {
++it;

View File

@@ -33,11 +33,11 @@ struct SpecialRegion {
MemoryHookPointer handler;
bool operator<(const SpecialRegion& other) const {
[[nodiscard]] bool operator<(const SpecialRegion& other) const {
return std::tie(type, handler) < std::tie(other.type, other.handler);
}
bool operator==(const SpecialRegion& other) const {
[[nodiscard]] bool operator==(const SpecialRegion& other) const {
return std::tie(type, handler) == std::tie(other.type, other.handler);
}
};

View File

@@ -24,14 +24,14 @@ public:
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) = default;
std::string Serialize() const;
std::string Get(const std::string& key, const std::string& default_value) const;
int Get(const std::string& key, int default_value) const;
float Get(const std::string& key, float default_value) const;
[[nodiscard]] std::string Serialize() const;
[[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
[[nodiscard]] int Get(const std::string& key, int default_value) const;
[[nodiscard]] float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
bool Has(const std::string& key) const;
[[nodiscard]] bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();

View File

@@ -14,35 +14,39 @@ public:
Vec3<T> xyz;
T w{};
Quaternion<decltype(-T{})> Inverse() const {
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const noexcept {
return {-xyz, w};
}
Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const
noexcept {
return {xyz + other.xyz, w + other.w};
}
Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const
noexcept {
return {xyz - other.xyz, w - other.w};
}
Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const {
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
const Quaternion& other) const noexcept {
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
Quaternion<T> Normalized() const {
[[nodiscard]] Quaternion<T> Normalized() const noexcept {
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
};
template <typename T>
auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) noexcept {
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
}
inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis,
float angle) noexcept {
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
}

View File

@@ -64,7 +64,7 @@ public:
/// @param output Where to store the popped slots
/// @param max_slots Maximum number of slots to pop
/// @returns The number of slots actually popped
std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
[[nodiscard]] std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
const std::size_t read_index = m_read_index.load();
const std::size_t slots_filled = m_write_index.load() - read_index;
const std::size_t pop_count = std::min(slots_filled, max_slots);
@@ -83,7 +83,7 @@ public:
return pop_count;
}
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
[[nodiscard]] std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
std::vector<T> out(std::min(max_slots, capacity) * granularity);
const std::size_t count = Pop(out.data(), out.size() / granularity);
out.resize(count * granularity);
@@ -91,12 +91,12 @@ public:
}
/// @returns Number of slots used
std::size_t Size() const {
[[nodiscard]] std::size_t Size() const {
return m_write_index.load() - m_read_index.load();
}
/// @returns Maximum size of ring buffer
constexpr std::size_t Capacity() const {
[[nodiscard]] constexpr std::size_t Capacity() const {
return capacity;
}

View File

@@ -20,7 +20,7 @@ struct ScopeExitHelper {
template <typename Func>
ScopeExitHelper<Func> ScopeExit(Func&& func) {
return ScopeExitHelper<Func>(std::move(func));
return ScopeExitHelper<Func>(std::forward<Func>(func));
}
} // namespace detail

View File

@@ -12,19 +12,19 @@
namespace Common {
/// Make a string lowercase
std::string ToLower(std::string str);
[[nodiscard]] std::string ToLower(std::string str);
/// Make a string uppercase
std::string ToUpper(std::string str);
[[nodiscard]] std::string ToUpper(std::string str);
std::string StringFromBuffer(const std::vector<u8>& data);
[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
std::string StripSpaces(const std::string& s);
std::string StripQuotes(const std::string& s);
[[nodiscard]] std::string StripSpaces(const std::string& s);
[[nodiscard]] std::string StripQuotes(const std::string& s);
std::string StringFromBool(bool value);
[[nodiscard]] std::string StringFromBool(bool value);
std::string TabsToSpaces(int tab_size, std::string in);
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
@@ -34,15 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
const std::string& _Filename);
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
const std::string& dest);
std::string UTF16ToUTF8(const std::u16string& input);
std::u16string UTF8ToUTF16(const std::string& input);
[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
#ifdef _WIN32
std::string UTF16ToUTF8(const std::wstring& input);
std::wstring UTF8ToUTF16W(const std::string& str);
[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
#endif
/**
@@ -50,7 +50,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
* `other` for equality.
*/
template <typename InIt>
bool ComparePartialString(InIt begin, InIt end, const char* other) {
[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
for (; begin != end && *other != '\0'; ++begin, ++other) {
if (*begin != *other) {
return false;
@@ -64,15 +64,16 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* NUL-terminated then the string ends at max_len characters.
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
std::size_t max_len);
/**
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
* max_len_bytes.
*/
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len);
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
@@ -84,6 +85,6 @@ std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buff
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
[[nodiscard]] const char* TrimSourcePath(const char* path, const char* root = "src");
} // namespace Common

View File

@@ -21,11 +21,6 @@
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
#include <byteswap.h>
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
#endif
#include <cstring>
#include "common/common_types.h"
@@ -62,86 +57,49 @@
namespace Common {
#ifdef _MSC_VER
inline u16 swap16(u16 _data) {
return _byteswap_ushort(_data);
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
}
inline u32 swap32(u32 _data) {
return _byteswap_ulong(_data);
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return _byteswap_ulong(data);
}
inline u64 swap64(u64 _data) {
return _byteswap_uint64(_data);
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return _byteswap_uint64(data);
}
#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6)
inline u16 swap16(u16 _data) {
u32 data = _data;
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
return (u16)data;
}
inline u32 swap32(u32 _data) {
__asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
return _data;
}
inline u64 swap64(u64 _data) {
return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
}
#elif __linux__
inline u16 swap16(u16 _data) {
return bswap_16(_data);
}
inline u32 swap32(u32 _data) {
return bswap_32(_data);
}
inline u64 swap64(u64 _data) {
return bswap_64(_data);
}
#elif __APPLE__
inline __attribute__((always_inline)) u16 swap16(u16 _data) {
return (_data >> 8) | (_data << 8);
}
inline __attribute__((always_inline)) u32 swap32(u32 _data) {
return __builtin_bswap32(_data);
}
inline __attribute__((always_inline)) u64 swap64(u64 _data) {
return __builtin_bswap64(_data);
}
#elif defined(__Bitrig__) || defined(__OpenBSD__)
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
inline u16 swap16(u16 _data) {
return __swap16(_data);
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
}
inline u32 swap32(u32 _data) {
return __swap32(_data);
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return __builtin_bswap32(data);
}
inline u64 swap64(u64 _data) {
return __swap64(_data);
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
inline u16 swap16(u16 _data) {
return bswap16(_data);
}
inline u32 swap32(u32 _data) {
return bswap32(_data);
}
inline u64 swap64(u64 _data) {
return bswap64(_data);
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return __builtin_bswap64(data);
}
#else
// Slow generic implementation.
inline u16 swap16(u16 data) {
// Generic implementation.
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return (data >> 8) | (data << 8);
}
inline u32 swap32(u32 data) {
return (swap16(data) << 16) | swap16(data >> 16);
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
}
inline u64 swap64(u64 data) {
return ((u64)swap32(data) << 32) | swap32(data >> 32);
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
}
#endif
inline float swapf(float f) {
[[nodiscard]] inline float swapf(float f) noexcept {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
@@ -153,7 +111,7 @@ inline float swapf(float f) {
return f;
}
inline double swapd(double f) {
[[nodiscard]] inline double swapd(double f) noexcept {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;

View File

@@ -42,7 +42,7 @@ public:
* Gets the name of this field.
* @returns Name of this field as a string.
*/
virtual const std::string& GetName() const = 0;
[[nodiscard]] virtual const std::string& GetName() const = 0;
};
/**
@@ -63,29 +63,29 @@ public:
void Accept(VisitorInterface& visitor) const override;
const std::string& GetName() const override {
[[nodiscard]] const std::string& GetName() const override {
return name;
}
/**
* Returns the type of the field.
*/
FieldType GetType() const {
[[nodiscard]] FieldType GetType() const {
return type;
}
/**
* Returns the value of the field.
*/
const T& GetValue() const {
[[nodiscard]] const T& GetValue() const {
return value;
}
bool operator==(const Field& other) const {
[[nodiscard]] bool operator==(const Field& other) const {
return (type == other.type) && (name == other.name) && (value == other.value);
}
bool operator!=(const Field& other) const {
[[nodiscard]] bool operator!=(const Field& other) const {
return !(*this == other);
}

View File

@@ -29,7 +29,7 @@ public:
}
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
[[nodiscard]] bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
if (!condvar.wait_until(lk, time, [this] { return is_set; }))
return false;

View File

@@ -25,7 +25,7 @@ struct ThreadQueueList {
}
// Only for debugging, returns priority level.
Priority contains(const T& uid) const {
[[nodiscard]] Priority contains(const T& uid) const {
for (Priority i = 0; i < NUM_QUEUES; ++i) {
const Queue& cur = queues[i];
if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
return -1;
}
T get_first() const {
[[nodiscard]] T get_first() const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
}
template <typename UnaryPredicate>
T get_first_filter(UnaryPredicate filter) const {
[[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
first = nullptr;
}
bool empty(Priority priority) const {
[[nodiscard]] bool empty(Priority priority) const {
const Queue* cur = &queues[priority];
return cur->data.empty();
}

View File

@@ -25,15 +25,15 @@ public:
delete read_ptr;
}
std::size_t Size() const {
[[nodiscard]] std::size_t Size() const {
return size.load();
}
bool Empty() const {
[[nodiscard]] bool Empty() const {
return Size() == 0;
}
T& Front() const {
[[nodiscard]] T& Front() const {
return read_ptr->current;
}
@@ -123,15 +123,15 @@ private:
template <typename T>
class MPSCQueue {
public:
std::size_t Size() const {
[[nodiscard]] std::size_t Size() const {
return spsc_queue.Size();
}
bool Empty() const {
[[nodiscard]] bool Empty() const {
return spsc_queue.Empty();
}
T& Front() const {
[[nodiscard]] T& Front() const {
return spsc_queue.Front();
}

View File

@@ -19,18 +19,18 @@ public:
// The time difference is always returned in milliseconds, regardless of alternative internal
// representation
std::chrono::milliseconds GetTimeDifference();
[[nodiscard]] std::chrono::milliseconds GetTimeDifference();
void AddTimeDifference();
static std::chrono::seconds GetTimeSinceJan1970();
static std::chrono::seconds GetLocalTimeSinceJan1970();
static double GetDoubleTime();
[[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
[[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
[[nodiscard]] static double GetDoubleTime();
static std::string GetTimeFormatted();
std::string GetTimeElapsedFormatted() const;
std::chrono::milliseconds GetTimeElapsed();
[[nodiscard]] static std::string GetTimeFormatted();
[[nodiscard]] std::string GetTimeElapsedFormatted() const;
[[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
static std::chrono::milliseconds GetTimeMs();
[[nodiscard]] static std::chrono::milliseconds GetTimeMs();
private:
std::chrono::milliseconds m_LastTime;

View File

@@ -12,7 +12,7 @@
namespace Common {
u128 Multiply64Into128(u64 a, u64 b) {
u128 Multiply64Into128(u64 a, u64 b) noexcept {
u128 result;
#ifdef _MSC_VER
result[0] = _umul128(a, b, &result[1]);
@@ -24,7 +24,7 @@ u128 Multiply64Into128(u64 a, u64 b) {
return result;
}
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) noexcept {
u64 remainder = dividend[0] % divisor;
u64 accum = dividend[0] / divisor;
if (dividend[1] == 0)

View File

@@ -10,10 +10,10 @@
namespace Common {
// This function multiplies 2 u64 values and produces a u128 value;
u128 Multiply64Into128(u64 a, u64 b);
[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b) noexcept;
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) noexcept;
} // namespace Common

View File

@@ -17,7 +17,8 @@ namespace Common::Compression {
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
s32 compression_level);
/**
* Compresses a source memory region with Zstandard with the default compression level and returns
@@ -28,7 +29,7 @@ std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
@@ -37,6 +38,6 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
} // namespace Common::Compression

View File

@@ -14,6 +14,7 @@
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/vm_manager.h"
@@ -99,7 +100,7 @@ public:
}
void CallSVC(u32 swi) override {
Kernel::CallSVC(swi);
Kernel::CallSVC(parent.system, swi);
}
void AddTicks(u64 ticks) override {
@@ -112,14 +113,14 @@ public:
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
parent.core_timing.AddTicks(amortized_ticks);
parent.system.CoreTiming().AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return std::max(parent.core_timing.GetDowncount(), 0);
return std::max(parent.system.CoreTiming().GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
}
ARM_Dynarmic& parent;
@@ -129,7 +130,7 @@ public:
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto* current_process = Core::CurrentProcess();
auto* current_process = system.Kernel().CurrentProcess();
auto** const page_table = current_process->VMManager().page_table.pointers.data();
Dynarmic::A64::UserConfig config;
@@ -171,10 +172,10 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
core_index{core_index}, core_timing{core_timing},
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
core_index{core_index}, system{system},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);

View File

@@ -12,19 +12,15 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Core::Timing {
class CoreTiming;
}
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index);
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic() override;
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -63,7 +59,7 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
Timing::CoreTiming& core_timing;
System& system;
DynarmicExclusiveMonitor& exclusive_monitor;
};

View File

@@ -10,7 +10,6 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
@@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
}
}
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
auto ec = esr >> 26;
auto iss = esr & 0xFFFFFF;
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(iss);
break;
}
}
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
ARM_Interface::ThreadContext ctx{};
@@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
return {};
}
ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
auto fpv = 3 << 20;
@@ -177,7 +162,7 @@ void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
}
}
@@ -190,7 +175,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
core_timing.AddTicks(num_instructions);
system.CoreTiming().AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
@@ -273,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt_hit = true;
}
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
const auto ec = esr >> 26;
const auto iss = esr & 0xFFFFFF;
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(arm_instance->system, iss);
break;
}
}
} // namespace Core

View File

@@ -9,15 +9,13 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core::Timing {
class CoreTiming;
}
namespace Core {
class System;
class ARM_Unicorn final : public ARM_Interface {
public:
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
explicit ARM_Unicorn(System& system);
~ARM_Unicorn() override;
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -47,8 +45,10 @@ public:
void RecordBreak(GDBStub::BreakpointAddress bkpt);
private:
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
uc_engine* uc{};
Timing::CoreTiming& core_timing;
System& system;
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit = false;
};

View File

@@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
#else
arm_interface = std::make_unique<ARM_Unicorn>();
arm_interface = std::make_unique<ARM_Unicorn>(system);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
arm_interface = std::make_unique<ARM_Unicorn>(system);
}
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);

View File

@@ -12,6 +12,23 @@
namespace Core::Frontend {
/**
* Represents a graphics context that can be used for background computation or drawing. If the
* graphics backend doesn't require the context, then the implementation of these methods can be
* stubs
*/
class GraphicsContext {
public:
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0;
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
};
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -30,7 +47,7 @@ namespace Core::Frontend {
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
*/
class EmuWindow {
class EmuWindow : public GraphicsContext {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -40,17 +57,21 @@ public:
std::pair<unsigned, unsigned> min_client_area_size;
};
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
/// Polls window events
virtual void PollEvents() = 0;
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
virtual void DoneCurrent() = 0;
/**
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
* context can be used from other threads for background graphics computation. If the frontend
* is using a graphics backend that doesn't need anything specific to run on a different thread,
* then it can use a stubbed implemenation for GraphicsContext.
*
* If the return value is null, then the core should assume that the frontend cannot provide a
* Shared Context
*/
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
return nullptr;
}
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)

View File

@@ -25,7 +25,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ClientPort;
static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -29,7 +29,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ClientSession;
static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -85,7 +85,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::Process;
static constexpr HandleType HANDLE_TYPE = HandleType::Process;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -31,7 +31,7 @@ public:
return reset_type;
}
static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -41,7 +41,7 @@ public:
return GetTypeName();
}
static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -43,7 +43,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ServerPort;
static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -28,11 +28,9 @@ ServerSession::~ServerSession() {
// the emulated application.
// Decrease the port's connection count.
if (parent->port)
if (parent->port) {
parent->port->ConnectionClosed();
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.
}
parent->server = nullptr;
}
@@ -74,9 +72,6 @@ void ServerSession::ClientDisconnected() {
handler->ClientDisconnected(this);
}
// TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
// their WaitSynchronization result to 0xC920181A.
// Clean up the list of client threads with pending requests, they are unneeded now that the
// client endpoint is closed.
pending_requesting_threads.clear();

View File

@@ -46,7 +46,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ServerSession;
static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -76,7 +76,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,12 @@
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Kernel {
void CallSVC(u32 immediate);
void CallSVC(Core::System& system, u32 immediate);
} // namespace Kernel

View File

@@ -11,278 +11,312 @@
namespace Kernel {
static inline u64 Param(int n) {
return Core::CurrentArmInterface().GetReg(n);
static inline u64 Param(const Core::System& system, int n) {
return system.CurrentArmInterface().GetReg(n);
}
/**
* HLE a function return from the current ARM userland process
* @param res Result to return
* @param system System context
* @param result Result to return
*/
static inline void FuncReturn(u64 res) {
Core::CurrentArmInterface().SetReg(0, res);
static inline void FuncReturn(Core::System& system, u64 result) {
system.CurrentArmInterface().SetReg(0, result);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type ResultCode
template <ResultCode func(u64)>
void SvcWrap() {
FuncReturn(func(Param(0)).raw);
template <ResultCode func(Core::System&, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
template <ResultCode func(u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0))).raw);
template <ResultCode func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
template <ResultCode func(u32, u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
template <ResultCode func(Core::System&, u32, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(
system,
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
}
template <ResultCode func(u32*)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*)>
void SvcWrap(Core::System& system) {
u32 param = 0;
const u32 retval = func(&param).raw;
Core::CurrentArmInterface().SetReg(1, param);
FuncReturn(retval);
const u32 retval = func(system, &param).raw;
system.CurrentArmInterface().SetReg(1, param);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u32*)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u32*)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
const u32 retval = func(&param_1, &param_2).raw;
const u32 retval = func(system, &param_1, &param_2).raw;
auto& arm_interface = Core::CurrentArmInterface();
auto& arm_interface = system.CurrentArmInterface();
arm_interface.SetReg(1, param_1);
arm_interface.SetReg(2, param_2);
FuncReturn(retval);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u64)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u64)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u64, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u64, u32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(&param_1, Param(1), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval =
func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u64*, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u64*, u32)>
void SvcWrap(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u64, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
template <ResultCode func(Core::System&, u64, s32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
}
template <ResultCode func(u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
template <ResultCode func(Core::System&, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
}
template <ResultCode func(u64*, u64)>
void SvcWrap() {
template <ResultCode func(Core::System&, u64*, u64)>
void SvcWrap(Core::System& system) {
u64 param_1 = 0;
u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u64*, u32, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u64*, u32, u32)>
void SvcWrap(Core::System& system) {
u64 param_1 = 0;
u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
static_cast<u32>(Param(system, 2)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
template <ResultCode func(Core::System&, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
template <ResultCode func(Core::System&, u32, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2))
.raw);
}
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32, u32*, u64*)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u64 param_2 = 0;
ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval.raw);
}
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u64, u64, u32, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
static_cast<u32>(Param(system, 2)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
template <ResultCode func(Core::System&, u64, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
system,
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
}
template <ResultCode func(u64, u64, u32, u64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
template <ResultCode func(Core::System&, u32, u64, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), static_cast<u32>(Param(system, 3)))
.raw);
}
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u64, u64, u64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
}
template <ResultCode func(u64, u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(
func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
system,
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
ResultCode retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<s64>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
FuncReturn(
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
template <ResultCode func(Core::System&, u64, u64, u32, s64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
}
template <ResultCode func(u64*, u64, u64, u64)>
void SvcWrap() {
template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
void SvcWrap(Core::System& system) {
u64 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval =
func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
static_cast<s32>(Param(5)))
.raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
static_cast<u32>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<u32>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s64>(Param(3)))
.raw);
template <ResultCode func(Core::System&, u64, u32, s32, s64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s32>(Param(3)))
.raw);
template <ResultCode func(Core::System&, u64, u32, s32, s32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
.raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
template <u32 func()>
void SvcWrap() {
FuncReturn(func());
template <u32 func(Core::System&)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u64
template <u64 func()>
void SvcWrap() {
FuncReturn(func());
template <u64 func(Core::System&)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Function wrappers that return type void
template <void func()>
void SvcWrap() {
func();
template <void func(Core::System&)>
void SvcWrap(Core::System& system) {
func(system);
}
template <void func(s64)>
void SvcWrap() {
func(static_cast<s64>(Param(0)));
template <void func(Core::System&, s64)>
void SvcWrap(Core::System& system) {
func(system, static_cast<s64>(Param(system, 0)));
}
template <void func(u64, u64 len)>
void SvcWrap() {
func(Param(0), Param(1));
template <void func(Core::System&, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, Param(system, 0), Param(system, 1));
}
template <void func(u64, u64, u64)>
void SvcWrap() {
func(Param(0), Param(1), Param(2));
template <void func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, Param(system, 0), Param(system, 1), Param(system, 2));
}
template <void func(u32, u64, u64)>
void SvcWrap() {
func(static_cast<u32>(Param(0)), Param(1), Param(2));
template <void func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
}
} // namespace Kernel

View File

@@ -106,7 +106,7 @@ public:
return "Thread";
}
static const HandleType HANDLE_TYPE = HandleType::Thread;
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -37,7 +37,7 @@ public:
return name;
}
static const HandleType HANDLE_TYPE = HandleType::WritableEvent;
static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -115,11 +115,12 @@ private:
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
length);
// Error checking
if (length < 0) {
@@ -148,11 +149,12 @@ private:
void Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
length);
// Error checking
if (length < 0) {
@@ -250,10 +252,7 @@ private:
u64 next_entry_index = 0;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 unk = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);

View File

@@ -93,12 +93,18 @@ public:
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
struct Parameters {
u64_le process_id;
u64_le nrr_address;
u64_le nrr_size;
};
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const VAddr nrr_addr{rp.Pop<VAddr>()};
const u64 nrr_size{rp.Pop<u64>()};
LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr,
nrr_size);
const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR,
"called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
process_id, nrr_address, nrr_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -116,24 +122,26 @@ public:
}
// NRR Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nrr_addr)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
if (!Common::Is4KBAligned(nrr_address)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRR Size is zero or causes overflow
if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
!Common::Is4KBAligned(nrr_size)) {
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
nrr_addr, nrr_size);
nrr_address, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRR data from memory
std::vector<u8> nrr_data(nrr_size);
Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
NRRHeader header;
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
@@ -174,7 +182,7 @@ public:
hashes.emplace_back(hash);
}
nrr.insert_or_assign(nrr_addr, std::move(hashes));
nrr.insert_or_assign(nrr_address, std::move(hashes));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -188,23 +196,30 @@ public:
return;
}
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const auto nrr_addr{rp.Pop<VAddr>()};
LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr);
struct Parameters {
u64_le process_id;
u64_le nrr_address;
};
if (!Common::Is4KBAligned(nrr_addr)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
IPC::RequestParser rp{ctx};
const auto [process_id, nrr_address] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id,
nrr_address);
if (!Common::Is4KBAligned(nrr_address)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
const auto iter = nrr.find(nrr_addr);
const auto iter = nrr.find(nrr_address);
if (iter == nrr.end()) {
LOG_ERROR(Service_LDR,
"Attempting to unload NRR which has not been loaded! (addr={:016X})",
nrr_addr);
nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR_ADDRESS);
return;
@@ -216,16 +231,22 @@ public:
}
void LoadNro(Kernel::HLERequestContext& ctx) {
struct Parameters {
u64_le process_id;
u64_le image_address;
u64_le image_size;
u64_le bss_address;
u64_le bss_size;
};
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const VAddr nro_addr{rp.Pop<VAddr>()};
const u64 nro_size{rp.Pop<u64>()};
const VAddr bss_addr{rp.Pop<VAddr>()};
const u64 bss_size{rp.Pop<u64>()};
LOG_DEBUG(
Service_LDR,
"called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}",
nro_addr, nro_size, bss_addr, bss_size);
const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR,
"called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
"bss_size={:016X}",
process_id, nro_address, nro_size, bss_address, bss_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -243,8 +264,9 @@ public:
}
// NRO Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nro_addr)) {
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
if (!Common::Is4KBAligned(nro_address)) {
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
@@ -252,15 +274,15 @@ public:
// NRO Size or BSS Size is zero or causes overflow
const auto nro_size_valid =
nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
const auto bss_size_valid =
nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
const auto bss_size_valid = nro_size + bss_size >= nro_size &&
(bss_size == 0 || bss_address + bss_size > bss_address);
if (!nro_size_valid || !bss_size_valid) {
LOG_ERROR(Service_LDR,
"NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
"bss_address={:016X}, bss_size={:016X})",
nro_addr, nro_size, bss_addr, bss_size);
nro_address, nro_size, bss_address, bss_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
@@ -268,7 +290,7 @@ public:
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
SHA256Hash hash{};
mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
@@ -318,17 +340,18 @@ public:
return;
}
ASSERT(vm_manager
.MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
ASSERT(
vm_manager
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
if (bss_size > 0) {
ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
.MirrorMemory(*map_address + nro_size, bss_address, bss_size,
Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess());
}
vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -348,13 +371,6 @@ public:
}
void UnloadNro(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
const VAddr mapped_addr{rp.PopRaw<VAddr>()};
const VAddr heap_addr{rp.PopRaw<VAddr>()};
LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr,
heap_addr);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -362,22 +378,30 @@ public:
return;
}
if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
LOG_ERROR(Service_LDR,
"NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
"bss_addr={:016X})!",
mapped_addr, heap_addr);
struct Parameters {
u64_le process_id;
u64_le nro_address;
};
IPC::RequestParser rp{ctx};
const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
nro_address);
if (!Common::Is4KBAligned(nro_address)) {
LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
const auto iter = nro.find(mapped_addr);
const auto iter = nro.find(nro_address);
if (iter == nro.end()) {
LOG_ERROR(Service_LDR,
"The NRO attempting to unmap was not mapped or has an invalid address "
"(actual {:016X})!",
mapped_addr);
"The NRO attempting to be unmapped was not mapped or has an invalid address "
"(nro_address=0x{:016X})!",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO_ADDRESS);
return;
@@ -386,10 +410,7 @@ public:
auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto& nro_size = iter->second.size;
ASSERT(vm_manager
.MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
@@ -459,11 +480,10 @@ private:
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
bool IsValidNROHash(const SHA256Hash& hash) {
return std::any_of(
nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
});
bool IsValidNROHash(const SHA256Hash& hash) const {
return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
});
}
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {

View File

@@ -21,12 +21,13 @@
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "video_core/renderer_base.h"
namespace Service::NVFlinger {
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
displays.emplace_back(0, "Default");
@@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
displays.emplace_back(4, "Null");
// Schedule the screen composition events
composition_event =
core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
composition_event = core_timing.RegisterEvent(
"ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
Compose();
this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
});
core_timing.ScheduleEvent(frame_ticks, composition_event);
core_timing.ScheduleEvent(ticks, composition_event);
}
NVFlinger::~NVFlinger() {
@@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto itr =
std::find_if(displays.begin(), displays.end(),
[&](const VI::Display& display) { return display.GetName() == name; });
if (itr == displays.end()) {
return {};
}

View File

@@ -393,6 +393,7 @@ struct Values {
bool use_disk_shader_cache;
bool use_accurate_gpu_emulation;
bool use_asynchronous_gpu_emulation;
bool force_30fps_mode;
float bg_red;
float bg_green;

View File

@@ -106,6 +106,8 @@ add_library(video_core STATIC
textures/decoders.cpp
textures/decoders.h
textures/texture.h
texture_cache.cpp
texture_cache.h
video_core.cpp
video_core.h
)
@@ -127,12 +129,14 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_sampler_cache.h
renderer_vulkan/vk_scheduler.cpp
renderer_vulkan/vk_scheduler.h
renderer_vulkan/vk_shader_decompiler.cpp
renderer_vulkan/vk_shader_decompiler.h
renderer_vulkan/vk_stream_buffer.cpp
renderer_vulkan/vk_stream_buffer.h
renderer_vulkan/vk_swapchain.cpp
renderer_vulkan/vk_swapchain.h)
target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
endif()
@@ -140,3 +144,6 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad)
if (ENABLE_VULKAN)
target_link_libraries(video_core PRIVATE sirit)
endif()

View File

@@ -299,6 +299,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
BaseBindings base_bindings;
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
// Prepare packed bindings
bind_ubo_pushbuffer.Setup(base_bindings.cbuf);
bind_ssbo_pushbuffer.Setup(base_bindings.gmem);
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
const auto& shader_config = gpu.regs.shader_config[index];
const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)};
@@ -321,8 +325,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
&ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
// Bind the emulation info buffer
glBindBufferRange(GL_UNIFORM_BUFFER, base_bindings.cbuf, buffer_cache.GetHandle(), offset,
static_cast<GLsizeiptr>(sizeof(ubo)));
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset,
static_cast<GLsizeiptr>(sizeof(ubo)));
Shader shader{shader_cache.GetStageProgram(program)};
const auto [program_handle, next_bindings] =
@@ -366,6 +370,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
base_bindings = next_bindings;
}
bind_ubo_pushbuffer.Bind();
bind_ssbo_pushbuffer.Bind();
SyncClipEnabled(clip_distances);
gpu.dirty_flags.shaders = false;
@@ -900,23 +907,14 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
const auto& entries = shader->GetShaderEntries().const_buffers;
constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers;
std::array<GLuint, max_binds> bind_buffers;
std::array<GLintptr, max_binds> bind_offsets;
std::array<GLsizeiptr, max_binds> bind_sizes;
ASSERT_MSG(entries.size() <= max_binds, "Exceeded expected number of binding points.");
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& used_buffer = entries[bindpoint];
const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()];
if (!buffer.enabled) {
// With disabled buffers set values as zero to unbind them
bind_buffers[bindpoint] = 0;
bind_offsets[bindpoint] = 0;
bind_sizes[bindpoint] = 0;
// Set values to zero to unbind buffers
bind_ubo_pushbuffer.Push(0, 0, 0);
continue;
}
@@ -944,30 +942,19 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader
const GLintptr const_buffer_offset = buffer_cache.UploadMemory(
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
// Prepare values for multibind
bind_buffers[bindpoint] = buffer_cache.GetHandle();
bind_offsets[bindpoint] = const_buffer_offset;
bind_sizes[bindpoint] = size;
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size);
}
// The first binding is reserved for emulation values
const GLuint ubo_base_binding = base_bindings.cbuf + 1;
glBindBuffersRange(GL_UNIFORM_BUFFER, ubo_base_binding, static_cast<GLsizei>(entries.size()),
bind_buffers.data(), bind_offsets.data(), bind_sizes.data());
}
void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, GLenum primitive_mode,
BaseBindings base_bindings) {
// TODO(Rodrigo): Use ARB_multi_bind here
const auto& entries = shader->GetShaderEntries().global_memory_entries;
for (u32 bindpoint = 0; bindpoint < static_cast<u32>(entries.size()); ++bindpoint) {
const auto& entry = entries[bindpoint];
const u32 current_bindpoint = base_bindings.gmem + bindpoint;
const auto& region = global_cache.GetGlobalRegion(entry, stage);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, current_bindpoint, region->GetBufferHandle());
for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry{entries[bindpoint]};
const auto& region{global_cache.GetGlobalRegion(entry, stage)};
bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0,
static_cast<GLsizeiptr>(region->GetSizeInBytes()));
}
}

View File

@@ -28,6 +28,7 @@
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/utils.h"
namespace Core {
class System;
@@ -229,6 +230,9 @@ private:
PrimitiveAssembler primitive_assembler{buffer_cache};
GLint uniform_buffer_alignment;
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
std::size_t CalculateVertexArraysSize() const;
std::size_t CalculateIndexBufferSize() const;

View File

@@ -112,11 +112,26 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
params.srgb_conversion);
if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) {
if (config.tsc.depth_compare_enabled) {
// Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
// then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
// causes GetFormatType to properly return 'Depth' below).
params.pixel_format = PixelFormat::Z16;
if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) {
switch (params.pixel_format) {
case PixelFormat::R16S:
case PixelFormat::R16U:
case PixelFormat::R16F:
params.pixel_format = PixelFormat::Z16;
break;
case PixelFormat::R32F:
params.pixel_format = PixelFormat::Z32F;
break;
default:
LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}",
static_cast<u32>(params.pixel_format));
break;
}
}
}
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -266,6 +281,10 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
params.width = config.width;
if (!params.is_tiled) {
const u32 bpp = params.GetFormatBpp() / 8;
params.pitch = config.width * bpp;
}
params.height = config.height;
params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D;
@@ -1175,10 +1194,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
return new_surface;
}
const bool old_compressed =
GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed;
const bool new_compressed =
GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed;
const bool compatible_formats =
GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) &&
!(old_compressed || new_compressed);
// For compatible surfaces, we can just do fast glCopyImageSubData based copy
if (old_params.target == new_params.target && old_params.type == new_params.type &&
old_params.depth == new_params.depth && old_params.depth == 1 &&
GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
if (old_params.target == new_params.target && old_params.depth == new_params.depth &&
old_params.depth == 1 && compatible_formats) {
FastCopySurface(old_surface, new_surface);
return new_surface;
}
@@ -1193,7 +1218,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
if (old_params.pixel_format == new_params.pixel_format)
if (compatible_formats)
FastLayeredCopySurface(old_surface, new_surface);
else {
AccurateCopySurface(old_surface, new_surface);

View File

@@ -552,8 +552,7 @@ private:
} else if (std::holds_alternative<OperationNode>(*offset)) {
// Indirect access
const std::string final_offset = code.GenerateTemporary();
code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " +
std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';');
code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);");
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
final_offset, final_offset);

View File

@@ -2,12 +2,44 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
namespace OpenGL::GLShader {
using Tegra::Engines::Maxwell3D;
ProgramManager::ProgramManager() {
pipeline.Create();
}
ProgramManager::~ProgramManager() = default;
void ProgramManager::ApplyTo(OpenGLState& state) {
UpdatePipeline();
state.draw.shader_program = 0;
state.draw.program_pipeline = pipeline.handle;
}
void ProgramManager::UpdatePipeline() {
// Avoid updating the pipeline when values have no changed
if (old_state == current_state) {
return;
}
// Workaround for AMD bug
constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
GL_FRAGMENT_SHADER_BIT};
glUseProgramStages(pipeline.handle, all_used_stages, 0);
glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader);
glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader);
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader);
old_state = current_state;
}
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
const auto& regs = maxwell.regs;
const auto& state = maxwell.state;
@@ -16,7 +48,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
u32 func = static_cast<u32>(regs.alpha_test_func);
auto func{static_cast<u32>(regs.alpha_test_func)};
// Normalize the gl variants of opCompare to be the same as the normal variants
const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never);
if (func >= op_gl_variant_base) {

View File

@@ -4,6 +4,8 @@
#pragma once
#include <cstddef>
#include <glad/glad.h>
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -38,55 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384,
class ProgramManager {
public:
ProgramManager() {
pipeline.Create();
}
explicit ProgramManager();
~ProgramManager();
void ApplyTo(OpenGLState& state);
void UseProgrammableVertexShader(GLuint program) {
vs = program;
current_state.vertex_shader = program;
}
void UseProgrammableGeometryShader(GLuint program) {
gs = program;
current_state.geometry_shader = program;
}
void UseProgrammableFragmentShader(GLuint program) {
fs = program;
current_state.fragment_shader = program;
}
void UseTrivialGeometryShader() {
gs = 0;
}
void ApplyTo(OpenGLState& state) {
UpdatePipeline();
state.draw.shader_program = 0;
state.draw.program_pipeline = pipeline.handle;
current_state.geometry_shader = 0;
}
private:
void UpdatePipeline() {
// Avoid updating the pipeline when values have no changed
if (old_vs == vs && old_fs == fs && old_gs == gs)
return;
// Workaround for AMD bug
glUseProgramStages(pipeline.handle,
GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT,
0);
struct PipelineState {
bool operator==(const PipelineState& rhs) const {
return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader &&
geometry_shader == rhs.geometry_shader;
}
glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs);
glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs);
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
bool operator!=(const PipelineState& rhs) const {
return !operator==(rhs);
}
// Update the old values
old_vs = vs;
old_fs = fs;
old_gs = gs;
}
GLuint vertex_shader{};
GLuint fragment_shader{};
GLuint geometry_shader{};
};
void UpdatePipeline();
OGLPipeline pipeline;
GLuint vs{}, fs{}, gs{};
GLuint old_vs{}, old_fs{}, old_gs{};
PipelineState current_state;
PipelineState old_state;
};
} // namespace OpenGL::GLShader

View File

@@ -5,11 +5,39 @@
#include <string>
#include <fmt/format.h>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/renderer_opengl/utils.h"
namespace OpenGL {
BindBuffersRangePushBuffer::BindBuffersRangePushBuffer(GLenum target) : target{target} {}
BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default;
void BindBuffersRangePushBuffer::Setup(GLuint first_) {
first = first_;
buffers.clear();
offsets.clear();
sizes.clear();
}
void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) {
buffers.push_back(buffer);
offsets.push_back(offset);
sizes.push_back(size);
}
void BindBuffersRangePushBuffer::Bind() const {
const std::size_t count{buffers.size()};
DEBUG_ASSERT(count == offsets.size() && count == sizes.size());
if (count == 0) {
return;
}
glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(),
sizes.data());
}
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
if (!GLAD_GL_KHR_debug) {
return; // We don't need to throw an error as this is just for debugging

View File

@@ -5,11 +5,31 @@
#pragma once
#include <string>
#include <vector>
#include <glad/glad.h>
#include "common/common_types.h"
namespace OpenGL {
class BindBuffersRangePushBuffer {
public:
BindBuffersRangePushBuffer(GLenum target);
~BindBuffersRangePushBuffer();
void Setup(GLuint first_);
void Push(GLuint buffer, GLintptr offset, GLsizeiptr size);
void Bind() const;
private:
GLenum target;
GLuint first;
std::vector<GLuint> buffers;
std::vector<GLintptr> offsets;
std::vector<GLsizeiptr> sizes;
};
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
} // namespace OpenGL

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include <sirit/sirit.h>
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
class ShaderIR;
}
namespace Vulkan::VKShader {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using SamplerEntry = VideoCommon::Shader::Sampler;
constexpr u32 DESCRIPTOR_SET = 0;
class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
public:
explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index)
: VideoCommon::Shader::ConstBuffer{entry}, index{index} {}
constexpr u32 GetIndex() const {
return index;
}
private:
u32 index{};
};
class GlobalBufferEntry {
public:
explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset)
: cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {}
u32 GetCbufIndex() const {
return cbuf_index;
}
u32 GetCbufOffset() const {
return cbuf_offset;
}
private:
u32 cbuf_index{};
u32 cbuf_offset{};
};
struct ShaderEntries {
u32 const_buffers_base_binding{};
u32 global_buffers_base_binding{};
u32 samplers_base_binding{};
std::vector<ConstBufferEntry> const_buffers;
std::vector<GlobalBufferEntry> global_buffers;
std::vector<SamplerEntry> samplers;
std::set<u32> attributes;
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
std::size_t shader_length{};
Sirit::Id entry_function{};
std::vector<Sirit::Id> interfaces;
};
using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage);
} // namespace Vulkan::VKShader

View File

@@ -294,6 +294,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
return PixelFormat::Z16;
case Tegra::Texture::TextureFormat::Z24S8:
return PixelFormat::Z24S8;
case Tegra::Texture::TextureFormat::ZF32_X24S8:
return PixelFormat::Z32FS8;
case Tegra::Texture::TextureFormat::DXT1:
return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
case Tegra::Texture::TextureFormat::DXT23:

View File

@@ -0,0 +1,386 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/assert.h"
#include "common/cityhash.h"
#include "common/common_types.h"
#include "core/core.h"
#include "video_core/surface.h"
#include "video_core/texture_cache.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
namespace VideoCommon {
using VideoCore::Surface::SurfaceTarget;
using VideoCore::Surface::ComponentTypeFromDepthFormat;
using VideoCore::Surface::ComponentTypeFromRenderTarget;
using VideoCore::Surface::ComponentTypeFromTexture;
using VideoCore::Surface::PixelFormatFromDepthFormat;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
using VideoCore::Surface::PixelFormatFromTextureFormat;
using VideoCore::Surface::SurfaceTargetFromTextureType;
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
}
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
const Tegra::Texture::FullTextureInfo& config) {
SurfaceParams params;
params.is_tiled = config.tic.IsTiled();
params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
params.pixel_format =
PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false);
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.depth = config.tic.Depth();
if (params.target == SurfaceTarget::TextureCubemap ||
params.target == SurfaceTarget::TextureCubeArray) {
params.depth *= 6;
}
params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
params.unaligned_height = config.tic.Height();
params.num_levels = config.tic.max_mip_level + 1;
params.CalculateCachedValues();
return params;
}
SurfaceParams SurfaceParams::CreateForDepthBuffer(
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
u32 block_width, u32 block_height, u32 block_depth,
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
SurfaceParams params;
params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
params.block_width = 1 << std::min(block_width, 5U);
params.block_height = 1 << std::min(block_height, 5U);
params.block_depth = 1 << std::min(block_depth, 5U);
params.tile_width_spacing = 1;
params.pixel_format = PixelFormatFromDepthFormat(format);
params.component_type = ComponentTypeFromDepthFormat(format);
params.type = GetFormatType(params.pixel_format);
params.width = zeta_width;
params.height = zeta_height;
params.unaligned_height = zeta_height;
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
params.num_levels = 1;
params.CalculateCachedValues();
return params;
}
SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
SurfaceParams params;
params.is_tiled =
config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
params.block_width = 1 << config.memory_layout.block_width;
params.block_height = 1 << config.memory_layout.block_height;
params.block_depth = 1 << config.memory_layout.block_depth;
params.tile_width_spacing = 1;
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
if (params.is_tiled) {
params.width = config.width;
} else {
const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
params.pitch = config.width;
params.width = params.pitch / bpp;
}
params.height = config.height;
params.depth = 1;
params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D;
params.num_levels = 1;
params.CalculateCachedValues();
return params;
}
SurfaceParams SurfaceParams::CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
SurfaceParams params{};
params.is_tiled = !config.linear;
params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
params.tile_width_spacing = 1;
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
params.width = config.width;
params.height = config.height;
params.unaligned_height = config.height;
// TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
params.num_levels = 1;
params.CalculateCachedValues();
return params;
}
u32 SurfaceParams::GetMipWidth(u32 level) const {
return std::max(1U, width >> level);
}
u32 SurfaceParams::GetMipHeight(u32 level) const {
return std::max(1U, height >> level);
}
u32 SurfaceParams::GetMipDepth(u32 level) const {
return IsLayered() ? depth : std::max(1U, depth >> level);
}
bool SurfaceParams::IsLayered() const {
switch (target) {
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
case SurfaceTarget::TextureCubemap:
return true;
default:
return false;
}
}
u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
// Auto block resizing algorithm from:
// https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
if (level == 0) {
return block_height;
}
const u32 height{GetMipHeight(level)};
const u32 default_block_height{GetDefaultBlockHeight(pixel_format)};
const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
u32 block_height = 16;
while (block_height > 1 && blocks_in_y <= block_height * 4) {
block_height >>= 1;
}
return block_height;
}
u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
if (level == 0)
return block_depth;
if (target != SurfaceTarget::Texture3D)
return 1;
const u32 depth{GetMipDepth(level)};
u32 block_depth = 32;
while (block_depth > 1 && depth * 2 <= block_depth) {
block_depth >>= 1;
}
if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
return 16;
}
return block_depth;
}
std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
std::size_t offset = 0;
for (u32 i = 0; i < level; i++) {
offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false);
}
return offset;
}
std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
std::size_t offset = 0;
for (u32 i = 0; i < level; i++) {
offset += GetInnerMipmapMemorySize(i, true, false, false);
}
return offset;
}
std::size_t SurfaceParams::GetGuestLayerSize() const {
return GetInnerMemorySize(false, true, false);
}
std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
return GetInnerMipmapMemorySize(level, true, IsLayered(), false);
}
bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
view_params.component_type, view_params.type)) {
return false;
}
const SurfaceTarget view_target{view_params.target};
if (view_target == target) {
return true;
}
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
return false;
case SurfaceTarget::Texture1DArray:
return view_target == SurfaceTarget::Texture1D;
case SurfaceTarget::Texture2DArray:
return view_target == SurfaceTarget::Texture2D;
case SurfaceTarget::TextureCubemap:
return view_target == SurfaceTarget::Texture2D ||
view_target == SurfaceTarget::Texture2DArray;
case SurfaceTarget::TextureCubeArray:
return view_target == SurfaceTarget::Texture2D ||
view_target == SurfaceTarget::Texture2DArray ||
view_target == SurfaceTarget::TextureCubemap;
default:
UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
return false;
}
}
bool SurfaceParams::IsPixelFormatZeta() const {
return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
}
void SurfaceParams::CalculateCachedValues() {
guest_size_in_bytes = GetInnerMemorySize(false, false, false);
// ASTC is uncompressed in software, in emulated as RGBA8
if (IsPixelFormatASTC(pixel_format)) {
host_size_in_bytes = width * height * depth * 4;
} else {
host_size_in_bytes = GetInnerMemorySize(true, false, false);
}
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
num_layers = 1;
break;
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray:
num_layers = depth;
break;
default:
UNREACHABLE();
}
}
std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
bool uncompressed) const {
const bool tiled{as_host_size ? false : is_tiled};
const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)};
const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)};
const u32 depth{layer_only ? 1U : GetMipDepth(level)};
return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height,
depth, GetMipBlockHeight(level), GetMipBlockDepth(level));
}
std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
bool uncompressed) const {
std::size_t size = 0;
for (u32 level = 0; level < num_levels; ++level) {
size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed);
}
if (!as_host_size && is_tiled) {
size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
}
return size;
}
std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
std::map<u64, std::pair<u32, u32>> view_offset_map;
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D: {
constexpr u32 layer = 0;
for (u32 level = 0; level < num_levels; ++level) {
const std::size_t offset{GetGuestMipmapLevelOffset(level)};
view_offset_map.insert({offset, {layer, level}});
}
break;
}
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray: {
const std::size_t layer_size{GetGuestLayerSize()};
for (u32 level = 0; level < num_levels; ++level) {
const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
for (u32 layer = 0; layer < num_layers; ++layer) {
const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
const std::size_t offset{level_offset + layer_offset};
view_offset_map.insert({offset, {layer, level}});
}
}
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
}
return view_offset_map;
}
bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
IsInBounds(view_params, layer, level);
}
bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
}
bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
if (view_params.target != SurfaceTarget::Texture3D) {
return true;
}
return view_params.depth == GetMipDepth(level);
}
bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
return layer + view_params.num_layers <= num_layers &&
level + view_params.num_levels <= num_levels;
}
std::size_t HasheableSurfaceParams::Hash() const {
return static_cast<std::size_t>(
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
}
bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
height, depth, pitch, unaligned_height, num_levels, pixel_format,
component_type, type, target) ==
std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
rhs.type, rhs.target);
}
std::size_t ViewKey::Hash() const {
return static_cast<std::size_t>(
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
}
bool ViewKey::operator==(const ViewKey& rhs) const {
return std::tie(base_layer, num_layers, base_level, num_levels) ==
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
}
} // namespace VideoCommon

View File

@@ -0,0 +1,586 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <list>
#include <memory>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <boost/icl/interval_map.hpp>
#include <boost/range/iterator_range.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/surface.h"
namespace Core {
class System;
}
namespace Tegra::Texture {
struct FullTextureInfo;
}
namespace VideoCore {
class RasterizerInterface;
}
namespace VideoCommon {
class HasheableSurfaceParams {
public:
std::size_t Hash() const;
bool operator==(const HasheableSurfaceParams& rhs) const;
protected:
// Avoid creation outside of a managed environment.
HasheableSurfaceParams() = default;
bool is_tiled;
u32 block_width;
u32 block_height;
u32 block_depth;
u32 tile_width_spacing;
u32 width;
u32 height;
u32 depth;
u32 pitch;
u32 unaligned_height;
u32 num_levels;
VideoCore::Surface::PixelFormat pixel_format;
VideoCore::Surface::ComponentType component_type;
VideoCore::Surface::SurfaceType type;
VideoCore::Surface::SurfaceTarget target;
};
class SurfaceParams final : public HasheableSurfaceParams {
public:
/// Creates SurfaceCachedParams from a texture configuration.
static SurfaceParams CreateForTexture(Core::System& system,
const Tegra::Texture::FullTextureInfo& config);
/// Creates SurfaceCachedParams for a depth buffer configuration.
static SurfaceParams CreateForDepthBuffer(
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
u32 block_width, u32 block_height, u32 block_depth,
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
/// Creates SurfaceCachedParams from a framebuffer configuration.
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
static SurfaceParams CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config);
bool IsTiled() const {
return is_tiled;
}
u32 GetBlockWidth() const {
return block_width;
}
u32 GetTileWidthSpacing() const {
return tile_width_spacing;
}
u32 GetWidth() const {
return width;
}
u32 GetHeight() const {
return height;
}
u32 GetDepth() const {
return depth;
}
u32 GetPitch() const {
return pitch;
}
u32 GetNumLevels() const {
return num_levels;
}
VideoCore::Surface::PixelFormat GetPixelFormat() const {
return pixel_format;
}
VideoCore::Surface::ComponentType GetComponentType() const {
return component_type;
}
VideoCore::Surface::SurfaceTarget GetTarget() const {
return target;
}
VideoCore::Surface::SurfaceType GetType() const {
return type;
}
std::size_t GetGuestSizeInBytes() const {
return guest_size_in_bytes;
}
std::size_t GetHostSizeInBytes() const {
return host_size_in_bytes;
}
u32 GetNumLayers() const {
return num_layers;
}
/// Returns the width of a given mipmap level.
u32 GetMipWidth(u32 level) const;
/// Returns the height of a given mipmap level.
u32 GetMipHeight(u32 level) const;
/// Returns the depth of a given mipmap level.
u32 GetMipDepth(u32 level) const;
/// Returns true if these parameters are from a layered surface.
bool IsLayered() const;
/// Returns the block height of a given mipmap level.
u32 GetMipBlockHeight(u32 level) const;
/// Returns the block depth of a given mipmap level.
u32 GetMipBlockDepth(u32 level) const;
/// Returns the offset in bytes in guest memory of a given mipmap level.
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
std::size_t GetHostMipmapLevelOffset(u32 level) const;
/// Returns the size of a layer in bytes in guest memory.
std::size_t GetGuestLayerSize() const;
/// Returns the size of a layer in bytes in host memory for a given mipmap level.
std::size_t GetHostLayerSize(u32 level) const;
/// Returns true if another surface can be familiar with this. This is a loosely defined term
/// that reflects the possibility of these two surface parameters potentially being part of a
/// bigger superset.
bool IsFamiliar(const SurfaceParams& view_params) const;
/// Returns true if the pixel format is a depth and/or stencil format.
bool IsPixelFormatZeta() const;
/// Creates a map that redirects an address difference to a layer and mipmap level.
std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
/// Returns true if the passed surface view parameters is equal or a valid subset of this.
bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
private:
/// Calculates values that can be deduced from HasheableSurfaceParams.
void CalculateCachedValues();
/// Returns the size of a given mipmap level.
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
bool uncompressed) const;
/// Returns the size of all mipmap levels and aligns as needed.
std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
/// Returns true if the passed view width and height match the size of this params in a given
/// mipmap level.
bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
/// Returns true if the passed view depth match the size of this params in a given mipmap level.
bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
/// Returns true if the passed view layers and mipmap levels are in bounds.
bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
std::size_t guest_size_in_bytes;
std::size_t host_size_in_bytes;
u32 num_layers;
};
struct ViewKey {
std::size_t Hash() const;
bool operator==(const ViewKey& rhs) const;
u32 base_layer{};
u32 num_layers{};
u32 base_level{};
u32 num_levels{};
};
} // namespace VideoCommon
namespace std {
template <>
struct hash<VideoCommon::SurfaceParams> {
std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
return k.Hash();
}
};
template <>
struct hash<VideoCommon::ViewKey> {
std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
return k.Hash();
}
};
} // namespace std
namespace VideoCommon {
template <typename TView, typename TExecutionContext>
class SurfaceBase {
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
public:
virtual void LoadBuffer() = 0;
virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0;
virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) {
if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) {
// It can't be a view if it's in a prior address.
return {};
}
const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)};
const auto it{view_offset_map.find(relative_offset)};
if (it == view_offset_map.end()) {
// Couldn't find an aligned view.
return {};
}
const auto [layer, level] = it->second;
if (!params.IsViewValid(view_params, layer, level)) {
return {};
}
return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
}
VAddr GetCpuAddr() const {
ASSERT(is_registered);
return cpu_addr;
}
u8* GetHostPtr() const {
ASSERT(is_registered);
return host_ptr;
}
CacheAddr GetCacheAddr() const {
ASSERT(is_registered);
return cache_addr;
}
std::size_t GetSizeInBytes() const {
return params.GetGuestSizeInBytes();
}
void MarkAsModified(bool is_modified_) {
is_modified = is_modified_;
}
const SurfaceParams& GetSurfaceParams() const {
return params;
}
TView* GetView(VAddr view_addr, const SurfaceParams& view_params) {
TView* view{TryGetView(view_addr, view_params)};
ASSERT(view != nullptr);
return view;
}
void Register(VAddr cpu_addr_, u8* host_ptr_) {
ASSERT(!is_registered);
is_registered = true;
cpu_addr = cpu_addr_;
host_ptr = host_ptr_;
cache_addr = ToCacheAddr(host_ptr_);
}
void Register(VAddr cpu_addr_) {
Register(cpu_addr_, Memory::GetPointer(cpu_addr_));
}
void Unregister() {
ASSERT(is_registered);
is_registered = false;
}
bool IsRegistered() const {
return is_registered;
}
protected:
explicit SurfaceBase(const SurfaceParams& params)
: params{params}, view_offset_map{params.CreateViewOffsetMap()} {}
~SurfaceBase() = default;
virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
bool IsModified() const {
return is_modified;
}
const SurfaceParams params;
private:
TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
const ViewKey key{base_layer, num_layers, base_level, num_levels};
const auto [entry, is_cache_miss] = views.try_emplace(key);
auto& view{entry->second};
if (is_cache_miss) {
view = CreateView(key);
}
return view.get();
}
const std::map<u64, std::pair<u32, u32>> view_offset_map;
VAddr cpu_addr{};
u8* host_ptr{};
CacheAddr cache_addr{};
bool is_modified{};
bool is_registered{};
std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
};
template <typename TSurface, typename TView, typename TExecutionContext>
class TextureCache {
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
using ResultType = std::tuple<TView*, TExecutionContext>;
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>;
using IntervalType = typename IntervalMap::interval_type;
public:
void InvalidateRegion(CacheAddr addr, std::size_t size) {
for (TSurface* surface : GetSurfacesInRegion(addr, size)) {
if (!surface->IsRegistered()) {
// Skip duplicates
continue;
}
Unregister(surface);
}
}
ResultType GetTextureSurface(TExecutionContext exctx,
const Tegra::Texture::FullTextureInfo& config) {
auto& memory_manager{system.GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())};
if (!cpu_addr) {
return {{}, exctx};
}
const auto params{SurfaceParams::CreateForTexture(system, config)};
return GetSurfaceView(exctx, *cpu_addr, params, true);
}
ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
const auto& regs{system.GPU().Maxwell3D().regs};
if (!regs.zeta.Address() || !regs.zeta_enable) {
return {{}, exctx};
}
auto& memory_manager{system.GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())};
if (!cpu_addr) {
return {{}, exctx};
}
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents);
}
ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
bool preserve_contents) {
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
const auto& regs{system.GPU().Maxwell3D().regs};
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
return {{}, exctx};
}
auto& memory_manager{system.GPU().MemoryManager()};
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
const auto cpu_addr{memory_manager.GpuToCpuAddress(
config.Address() + config.base_layer * config.layer_stride * sizeof(u32))};
if (!cpu_addr) {
return {{}, exctx};
}
return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
preserve_contents);
}
ResultType GetFermiSurface(TExecutionContext exctx,
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())};
ASSERT(cpu_addr);
return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config),
true);
}
TSurface* TryFindFramebufferSurface(const u8* host_ptr) const {
const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
}
protected:
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: system{system}, rasterizer{rasterizer} {}
~TextureCache() = default;
virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
const SurfaceParams& params, bool preserve_contents,
const std::vector<TSurface*>& overlaps) = 0;
virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) {
surface->Register(cpu_addr, host_ptr);
registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
}
void Unregister(TSurface* surface) {
registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
surface->Unregister();
}
TSurface* GetUncachedSurface(const SurfaceParams& params) {
if (TSurface* surface = TryGetReservedSurface(params); surface)
return surface;
// No reserved surface available, create a new one and reserve it
auto new_surface{CreateSurface(params)};
TSurface* surface{new_surface.get()};
ReserveSurface(params, std::move(new_surface));
return surface;
}
Core::System& system;
private:
ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params,
bool preserve_contents) {
const auto host_ptr{Memory::GetPointer(cpu_addr)};
const auto cache_addr{ToCacheAddr(host_ptr)};
const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
if (overlaps.empty()) {
return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
}
if (overlaps.size() == 1) {
if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view)
return {view, exctx};
}
TView* fast_view;
std::tie(fast_view, exctx) =
TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps);
for (TSurface* surface : overlaps) {
if (!fast_view) {
// Flush even when we don't care about the contents, to preserve memory not written
// by the new surface.
exctx = surface->FlushBuffer(exctx);
}
Unregister(surface);
}
if (fast_view) {
return {fast_view, exctx};
}
return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
}
ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
const SurfaceParams& params, bool preserve_contents) {
TSurface* new_surface{GetUncachedSurface(params)};
Register(new_surface, cpu_addr, host_ptr);
if (preserve_contents) {
exctx = LoadSurface(exctx, new_surface);
}
return {new_surface->GetView(cpu_addr, params), exctx};
}
TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) {
surface->LoadBuffer();
exctx = surface->UploadTexture(exctx);
surface->MarkAsModified(false);
return exctx;
}
std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
if (size == 0) {
return {};
}
const IntervalType interval{cache_addr, cache_addr + size};
std::vector<TSurface*> surfaces;
for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
surfaces.push_back(*pair.second.begin());
}
return surfaces;
}
void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) {
surface_reserve[params].push_back(std::move(surface));
}
TSurface* TryGetReservedSurface(const SurfaceParams& params) {
auto search{surface_reserve.find(params)};
if (search == surface_reserve.end()) {
return {};
}
for (auto& surface : search->second) {
if (!surface->IsRegistered()) {
return surface.get();
}
}
return {};
}
IntervalType GetSurfaceInterval(TSurface* surface) const {
return IntervalType::right_open(surface->GetCacheAddr(),
surface->GetCacheAddr() + surface->GetSizeInBytes());
}
VideoCore::RasterizerInterface& rasterizer;
IntervalMap registered_surfaces;
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
/// previously been used. This is to prevent surfaces from being constantly created and
/// destroyed when used with different surface parameters.
std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve;
};
} // namespace VideoCommon

View File

@@ -56,8 +56,6 @@ add_executable(yuzu
debugger/graphics/graphics_breakpoints.cpp
debugger/graphics/graphics_breakpoints.h
debugger/graphics/graphics_breakpoints_p.h
debugger/graphics/graphics_surface.cpp
debugger/graphics/graphics_surface.h
debugger/console.cpp
debugger/console.h
debugger/profiler.cpp

View File

@@ -1,6 +1,13 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QOffscreenSurface>
#include <QOpenGLWindow>
#include <QPainter>
#include <QScreen>
#include <QWindow>
#include <fmt/format.h>
@@ -82,13 +89,36 @@ void EmuThread::run() {
render_window->moveContext();
}
class GGLContext : public Core::Frontend::GraphicsContext {
public:
explicit GGLContext(QOpenGLContext* shared_context) : surface() {
context = std::make_unique<QOpenGLContext>(shared_context);
surface.setFormat(shared_context->format());
surface.create();
}
void MakeCurrent() override {
context->makeCurrent(&surface);
}
void DoneCurrent() override {
context->doneCurrent();
}
void SwapBuffers() override {}
private:
std::unique_ptr<QOpenGLContext> context;
QOffscreenSurface surface;
};
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
// context.
// The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget {
class GGLWidgetInternal : public QOpenGLWindow {
public:
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
: QGLWidget(fmt, parent), parent(parent) {}
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
: QOpenGLWindow(shared_context), parent(parent) {}
void paintEvent(QPaintEvent* ev) override {
if (do_painting) {
@@ -101,9 +131,51 @@ public:
parent->OnFramebufferSizeChanged();
}
void keyPressEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->PressKey(event->key());
}
void keyReleaseEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void mousePressEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
const auto pos{event->pos()};
if (event->button() == Qt::LeftButton) {
const auto [x, y] = parent->ScaleTouch(pos);
parent->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
}
void mouseMoveEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchUpdateEvent
const auto pos{event->pos()};
const auto [x, y] = parent->ScaleTouch(pos);
parent->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
}
void mouseReleaseEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
if (event->button() == Qt::LeftButton)
parent->TouchReleased();
else if (event->button() == Qt::RightButton)
InputCommon::GetMotionEmu()->EndTilt();
}
void DisablePainting() {
do_painting = false;
}
void EnablePainting() {
do_painting = true;
}
@@ -114,7 +186,7 @@ private:
};
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
: QWidget(parent), child(nullptr), emu_thread(emu_thread) {
: QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
@@ -137,19 +209,19 @@ void GRenderWindow::moveContext() {
auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
? emu_thread
: qApp->thread();
child->context()->moveToThread(thread);
context->moveToThread(thread);
}
void GRenderWindow::SwapBuffers() {
// In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`,
// In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`,
// since we never call `doneCurrent` in this thread.
// However:
// - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
// since the last time `swapBuffers` was executed;
// - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
child->makeCurrent();
context->makeCurrent(child);
child->swapBuffers();
context->swapBuffers(child);
if (!first_frame) {
emit FirstFrameDisplayed();
first_frame = true;
@@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() {
}
void GRenderWindow::MakeCurrent() {
child->makeCurrent();
context->makeCurrent(child);
}
void GRenderWindow::DoneCurrent() {
child->doneCurrent();
context->doneCurrent();
}
void GRenderWindow::PollEvents() {}
@@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {}
void GRenderWindow::OnFramebufferSizeChanged() {
// Screen changes potentially incur a change in screen DPI, hence we should update the
// framebuffer size
qreal pixelRatio = windowPixelRatio();
qreal pixelRatio = GetWindowPixelRatio();
unsigned width = child->QPaintDevice::width() * pixelRatio;
unsigned height = child->QPaintDevice::height() * pixelRatio;
UpdateCurrentFramebufferLayout(width, height);
}
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
if (child) {
child->keyPressEvent(event);
}
}
void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
if (child) {
child->keyReleaseEvent(event);
}
}
void GRenderWindow::BackupGeometry() {
geometry = ((QGLWidget*)this)->saveGeometry();
geometry = ((QWidget*)this)->saveGeometry();
}
void GRenderWindow::RestoreGeometry() {
@@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() {
// If we are a top-level widget, store the current geometry
// otherwise, store the last backup
if (parent() == nullptr)
return ((QGLWidget*)this)->saveGeometry();
return ((QWidget*)this)->saveGeometry();
else
return geometry;
}
qreal GRenderWindow::windowPixelRatio() const {
qreal GRenderWindow::GetWindowPixelRatio() const {
// windowHandle() might not be accessible until the window is displayed to screen.
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
}
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
const qreal pixel_ratio = windowPixelRatio();
const qreal pixel_ratio = GetWindowPixelRatio();
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
}
@@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event);
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
InputCommon::GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
auto pos = event->pos();
if (event->button() == Qt::LeftButton) {
const auto [x, y] = ScaleTouch(pos);
this->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
}
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchUpdateEvent
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
}
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
if (event->button() == Qt::LeftButton)
this->TouchReleased();
else if (event->button() == Qt::RightButton)
InputCommon::GetMotionEmu()->EndTilt();
}
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());
@@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
NotifyClientAreaSizeChanged(std::make_pair(width, height));
}
void GRenderWindow::InitRenderTarget() {
if (child) {
delete child;
}
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
return std::make_unique<GGLContext>(shared_context.get());
}
if (layout()) {
delete layout();
}
void GRenderWindow::InitRenderTarget() {
shared_context.reset();
context.reset();
delete child;
child = nullptr;
delete container;
container = nullptr;
delete layout();
first_frame = false;
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QGLFormat fmt;
QSurfaceFormat fmt;
fmt.setVersion(4, 3);
fmt.setProfile(QGLFormat::CoreProfile);
fmt.setProfile(QSurfaceFormat::CoreProfile);
// TODO: expose a setting for buffer value (ie default/single/double/triple)
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
shared_context = std::make_unique<QOpenGLContext>();
shared_context->setFormat(fmt);
shared_context->create();
context = std::make_unique<QOpenGLContext>();
context->setShareContext(shared_context.get());
context->setFormat(fmt);
context->create();
fmt.setSwapInterval(false);
// Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
fmt.setOption(QGL::NoDeprecatedFunctions);
child = new GGLWidgetInternal(this, shared_context.get());
container = QWidget::createWindowContainer(child, this);
child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this);
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout->addWidget(child);
layout->addWidget(container);
layout->setMargin(0);
setLayout(layout);
// Reset minimum size to avoid unwanted resizes when this function is called for a second time.
setMinimumSize(1, 1);
// Show causes the window to actually be created and the OpenGL context as well, but we don't
// want the widget to be shown yet, so immediately hide it.
show();
hide();
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
OnFramebufferSizeChanged();

View File

@@ -7,9 +7,9 @@
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <QGLWidget>
#include <QImage>
#include <QThread>
#include <QWidget>
#include "common/thread.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
@@ -21,6 +21,8 @@ class QTouchEvent;
class GGLWidgetInternal;
class GMainWindow;
class GRenderWindow;
class QSurface;
class QOpenGLContext;
namespace VideoCore {
enum class LoadCallbackStage;
@@ -121,25 +123,21 @@ public:
void MakeCurrent() override;
void DoneCurrent() override;
void PollEvents() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
void ForwardKeyPressEvent(QKeyEvent* event);
void ForwardKeyReleaseEvent(QKeyEvent* event);
void BackupGeometry();
void RestoreGeometry();
void restoreGeometry(const QByteArray& geometry); // overridden
QByteArray saveGeometry(); // overridden
qreal windowPixelRatio() const;
qreal GetWindowPixelRatio() const;
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
void closeEvent(QCloseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
bool event(QEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
void OnClientAreaResized(unsigned width, unsigned height);
@@ -161,7 +159,6 @@ signals:
void FirstFrameDisplayed();
private:
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
@@ -169,11 +166,17 @@ private:
void OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) override;
GGLWidgetInternal* child;
QWidget* container = nullptr;
GGLWidgetInternal* child = nullptr;
QByteArray geometry;
EmuThread* emu_thread;
// Context that backs the GGLWidgetInternal (and will be used by core to render)
std::unique_ptr<QOpenGLContext> context;
// Context that will be shared between all newly created contexts. This should never be made
// current
std::unique_ptr<QOpenGLContext> shared_context;
/// Temporary storage of the screenshot taken
QImage screenshot_image;

View File

@@ -394,6 +394,7 @@ void Config::ReadValues() {
ReadSetting("use_accurate_gpu_emulation", false).toBool();
Settings::values.use_asynchronous_gpu_emulation =
ReadSetting("use_asynchronous_gpu_emulation", false).toBool();
Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool();
Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat();
Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat();
@@ -523,8 +524,8 @@ void Config::ReadValues() {
qt_config->beginGroup("Paths");
UISettings::values.roms_path = ReadSetting("romsPath").toString();
UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString();
UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool();
UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString();
UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool();
UISettings::values.recent_files = ReadSetting("recentFiles").toStringList();
qt_config->endGroup();
@@ -664,6 +665,7 @@ void Config::SaveValues() {
WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false);
WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation,
false);
WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false);
// Cast to double because Qt's written float values are not human-readable
WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0);
@@ -768,8 +770,8 @@ void Config::SaveValues() {
WriteSetting("romsPath", UISettings::values.roms_path);
WriteSetting("symbolsPath", UISettings::values.symbols_path);
WriteSetting("screenshotPath", UISettings::values.screenshot_path);
WriteSetting("gameListRootDir", UISettings::values.gamedir, ".");
WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false);
WriteSetting("gameListRootDir", UISettings::values.game_directory_path, ".");
WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false);
WriteSetting("recentFiles", UISettings::values.recent_files);
qt_config->endGroup();

View File

@@ -12,7 +12,7 @@
#include "yuzu/hotkeys.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
: QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) {
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
this->setConfiguration();

View File

@@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan);
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
@@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() {
}
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.theme =

View File

@@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() {
ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
Settings::values.bg_blue));
}
@@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() {
Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
Settings::values.use_asynchronous_gpu_emulation =
ui->use_asynchronous_gpu_emulation->isChecked();
Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
Settings::values.bg_red = static_cast<float>(bg_color.redF());
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());

View File

@@ -70,6 +70,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="force_30fps_mode">
<property name="text">
<string>Force 30 FPS mode</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

View File

@@ -66,21 +66,20 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
}
void ConfigureHotkeys::Configure(QModelIndex index) {
if (!index.parent().isValid()) {
if (index.parent() == QModelIndex())
return;
}
index = index.sibling(index.row(), 1);
auto* const model = ui->hotkey_list->model();
const auto previous_key = model->data(index);
auto* model = ui->hotkey_list->model();
auto previous_key = model->data(index);
SequenceDialog hotkey_dialog{this};
auto* hotkey_dialog = new SequenceDialog;
int return_code = hotkey_dialog->exec();
const int return_code = hotkey_dialog.exec();
const auto key_sequence = hotkey_dialog.GetSequence();
if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
auto key_sequence = hotkey_dialog->GetSequence();
if (return_code == QDialog::Rejected || key_sequence.isEmpty())
return;
}
if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
QMessageBox::critical(this, tr("Error in inputted key"),
@@ -91,7 +90,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
}
}
bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) {
return GetUsedKeyList().contains(key_sequence);
}

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <QWidget>
#include "core/settings.h"
namespace Ui {
class ConfigureHotkeys;
@@ -38,7 +39,7 @@ signals:
private:
void Configure(QModelIndex index);
bool IsUsedKey(QKeySequence key_sequence) const;
bool IsUsedKey(QKeySequence key_sequence);
QList<QKeySequence> GetUsedKeyList() const;
std::unique_ptr<Ui::ConfigureHotkeys> ui;

View File

@@ -1,516 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QBoxLayout>
#include <QComboBox>
#include <QDebug>
#include <QFileDialog>
#include <QLabel>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPushButton>
#include <QScrollArea>
#include <QSpinBox>
#include "common/vector_math.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
#include "yuzu/debugger/graphics/graphics_surface.h"
#include "yuzu/util/spinbox.h"
static Tegra::Texture::TextureFormat ConvertToTextureFormat(
Tegra::RenderTargetFormat render_target_format) {
switch (render_target_format) {
case Tegra::RenderTargetFormat::RGBA8_UNORM:
return Tegra::Texture::TextureFormat::A8R8G8B8;
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
return Tegra::Texture::TextureFormat::A2B10G10R10;
default:
UNIMPLEMENTED_MSG("Unimplemented RT format");
return Tegra::Texture::TextureFormat::A8R8G8B8;
}
}
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
SurfacePicture::~SurfacePicture() = default;
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
// Only do something while the left mouse button is held down
if (!(event->buttons() & Qt::LeftButton))
return;
if (pixmap() == nullptr)
return;
if (surface_widget)
surface_widget->Pick(event->x() * pixmap()->width() / width(),
event->y() * pixmap()->height() / height());
}
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
// We also want to handle the event if the user moves the mouse while holding down the LMB
mousePressEvent(event);
}
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent),
surface_source(Source::RenderTarget0) {
setObjectName("MaxwellSurface");
surface_source_list = new QComboBox;
surface_source_list->addItem(tr("Render Target 0"));
surface_source_list->addItem(tr("Render Target 1"));
surface_source_list->addItem(tr("Render Target 2"));
surface_source_list->addItem(tr("Render Target 3"));
surface_source_list->addItem(tr("Render Target 4"));
surface_source_list->addItem(tr("Render Target 5"));
surface_source_list->addItem(tr("Render Target 6"));
surface_source_list->addItem(tr("Render Target 7"));
surface_source_list->addItem(tr("Z Buffer"));
surface_source_list->addItem(tr("Custom"));
surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
surface_address_control = new CSpinBox;
surface_address_control->SetBase(16);
surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF);
surface_address_control->SetPrefix("0x");
unsigned max_dimension = 16384; // TODO: Find actual maximum
surface_width_control = new QSpinBox;
surface_width_control->setRange(0, max_dimension);
surface_height_control = new QSpinBox;
surface_height_control->setRange(0, max_dimension);
surface_picker_x_control = new QSpinBox;
surface_picker_x_control->setRange(0, max_dimension - 1);
surface_picker_y_control = new QSpinBox;
surface_picker_y_control->setRange(0, max_dimension - 1);
// clang-format off
// Color formats sorted by Maxwell texture format index
const QStringList surface_formats{
tr("None"),
QStringLiteral("R32_G32_B32_A32"),
QStringLiteral("R32_G32_B32"),
QStringLiteral("R16_G16_B16_A16"),
QStringLiteral("R32_G32"),
QStringLiteral("R32_B24G8"),
QStringLiteral("ETC2_RGB"),
QStringLiteral("X8B8G8R8"),
QStringLiteral("A8R8G8B8"),
QStringLiteral("A2B10G10R10"),
QStringLiteral("ETC2_RGB_PTA"),
QStringLiteral("ETC2_RGBA"),
QStringLiteral("R16_G16"),
QStringLiteral("G8R24"),
QStringLiteral("G24R8"),
QStringLiteral("R32"),
QStringLiteral("BC6H_SF16"),
QStringLiteral("BC6H_UF16"),
QStringLiteral("A4B4G4R4"),
QStringLiteral("A5B5G5R1"),
QStringLiteral("A1B5G5R5"),
QStringLiteral("B5G6R5"),
QStringLiteral("B6G5R5"),
QStringLiteral("BC7U"),
QStringLiteral("G8R8"),
QStringLiteral("EAC"),
QStringLiteral("EACX2"),
QStringLiteral("R16"),
QStringLiteral("Y8_VIDEO"),
QStringLiteral("R8"),
QStringLiteral("G4R4"),
QStringLiteral("R1"),
QStringLiteral("E5B9G9R9_SHAREDEXP"),
QStringLiteral("BF10GF11RF11"),
QStringLiteral("G8B8G8R8"),
QStringLiteral("B8G8R8G8"),
QStringLiteral("DXT1"),
QStringLiteral("DXT23"),
QStringLiteral("DXT45"),
QStringLiteral("DXN1"),
QStringLiteral("DXN2"),
QStringLiteral("Z24S8"),
QStringLiteral("X8Z24"),
QStringLiteral("S8Z24"),
QStringLiteral("X4V4Z24__COV4R4V"),
QStringLiteral("X4V4Z24__COV8R8V"),
QStringLiteral("V8Z24__COV4R12V"),
QStringLiteral("ZF32"),
QStringLiteral("ZF32_X24S8"),
QStringLiteral("X8Z24_X20V4S8__COV4R4V"),
QStringLiteral("X8Z24_X20V4S8__COV8R8V"),
QStringLiteral("ZF32_X20V4X8__COV4R4V"),
QStringLiteral("ZF32_X20V4X8__COV8R8V"),
QStringLiteral("ZF32_X20V4S8__COV4R4V"),
QStringLiteral("ZF32_X20V4S8__COV8R8V"),
QStringLiteral("X8Z24_X16V8S8__COV4R12V"),
QStringLiteral("ZF32_X16V8X8__COV4R12V"),
QStringLiteral("ZF32_X16V8S8__COV4R12V"),
QStringLiteral("Z16"),
QStringLiteral("V8Z24__COV8R24V"),
QStringLiteral("X8Z24_X16V8S8__COV8R24V"),
QStringLiteral("ZF32_X16V8X8__COV8R24V"),
QStringLiteral("ZF32_X16V8S8__COV8R24V"),
QStringLiteral("ASTC_2D_4X4"),
QStringLiteral("ASTC_2D_5X5"),
QStringLiteral("ASTC_2D_6X6"),
QStringLiteral("ASTC_2D_8X8"),
QStringLiteral("ASTC_2D_10X10"),
QStringLiteral("ASTC_2D_12X12"),
QStringLiteral("ASTC_2D_5X4"),
QStringLiteral("ASTC_2D_6X5"),
QStringLiteral("ASTC_2D_8X6"),
QStringLiteral("ASTC_2D_10X8"),
QStringLiteral("ASTC_2D_12X10"),
QStringLiteral("ASTC_2D_8X5"),
QStringLiteral("ASTC_2D_10X5"),
QStringLiteral("ASTC_2D_10X6"),
};
// clang-format on
surface_format_control = new QComboBox;
surface_format_control->addItems(surface_formats);
surface_info_label = new QLabel();
surface_info_label->setWordWrap(true);
surface_picture_label = new SurfacePicture(0, this);
surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
surface_picture_label->setScaledContents(false);
auto scroll_area = new QScrollArea();
scroll_area->setBackgroundRole(QPalette::Dark);
scroll_area->setWidgetResizable(false);
scroll_area->setWidget(surface_picture_label);
save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
// Connections
connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this,
&GraphicsSurfaceWidget::OnSurfaceSourceChanged);
connect(surface_address_control, &CSpinBox::ValueChanged, this,
&GraphicsSurfaceWidget::OnSurfaceAddressChanged);
connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this,
&GraphicsSurfaceWidget::OnSurfaceWidthChanged);
connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this,
&GraphicsSurfaceWidget::OnSurfaceHeightChanged);
connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this,
&GraphicsSurfaceWidget::OnSurfaceFormatChanged);
connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this,
&GraphicsSurfaceWidget::OnSurfacePickerXChanged);
connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this,
&GraphicsSurfaceWidget::OnSurfacePickerYChanged);
connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
auto main_widget = new QWidget;
auto main_layout = new QVBoxLayout;
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("Source:")));
sub_layout->addWidget(surface_source_list);
main_layout->addLayout(sub_layout);
}
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("GPU Address:")));
sub_layout->addWidget(surface_address_control);
main_layout->addLayout(sub_layout);
}
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("Width:")));
sub_layout->addWidget(surface_width_control);
main_layout->addLayout(sub_layout);
}
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("Height:")));
sub_layout->addWidget(surface_height_control);
main_layout->addLayout(sub_layout);
}
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("Format:")));
sub_layout->addWidget(surface_format_control);
main_layout->addLayout(sub_layout);
}
main_layout->addWidget(scroll_area);
auto info_layout = new QHBoxLayout;
{
auto xy_layout = new QVBoxLayout;
{
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("X:")));
sub_layout->addWidget(surface_picker_x_control);
xy_layout->addLayout(sub_layout);
}
{
auto sub_layout = new QHBoxLayout;
sub_layout->addWidget(new QLabel(tr("Y:")));
sub_layout->addWidget(surface_picker_y_control);
xy_layout->addLayout(sub_layout);
}
}
info_layout->addLayout(xy_layout);
surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
info_layout->addWidget(surface_info_label);
}
main_layout->addLayout(info_layout);
main_layout->addWidget(save_surface);
main_widget->setLayout(main_layout);
setWidget(main_widget);
// Load current data - TODO: Make sure this works when emulation is not running
if (debug_context && debug_context->at_breakpoint) {
emit Update();
widget()->setEnabled(debug_context->at_breakpoint);
} else {
widget()->setEnabled(false);
}
}
void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
emit Update();
widget()->setEnabled(true);
}
void GraphicsSurfaceWidget::OnResumed() {
widget()->setEnabled(false);
}
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
surface_source = static_cast<Source>(new_value);
emit Update();
}
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
if (surface_address != new_value) {
surface_address = static_cast<GPUVAddr>(new_value);
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
emit Update();
}
}
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
if (surface_width != static_cast<unsigned>(new_value)) {
surface_width = static_cast<unsigned>(new_value);
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
emit Update();
}
}
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
if (surface_height != static_cast<unsigned>(new_value)) {
surface_height = static_cast<unsigned>(new_value);
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
emit Update();
}
}
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) {
surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value);
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
emit Update();
}
}
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
if (surface_picker_x != new_value) {
surface_picker_x = new_value;
Pick(surface_picker_x, surface_picker_y);
}
}
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
if (surface_picker_y != new_value) {
surface_picker_y = new_value;
Pick(surface_picker_x, surface_picker_y);
}
}
void GraphicsSurfaceWidget::Pick(int x, int y) {
surface_picker_x_control->setValue(x);
surface_picker_y_control->setValue(y);
if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
y >= static_cast<int>(surface_height)) {
surface_info_label->setText(tr("Pixel out of bounds"));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
return;
}
surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>"));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
}
void GraphicsSurfaceWidget::OnUpdate() {
auto& gpu = Core::System::GetInstance().GPU();
QPixmap pixmap;
switch (surface_source) {
case Source::RenderTarget0:
case Source::RenderTarget1:
case Source::RenderTarget2:
case Source::RenderTarget3:
case Source::RenderTarget4:
case Source::RenderTarget5:
case Source::RenderTarget6:
case Source::RenderTarget7: {
// TODO: Store a reference to the registers in the debug context instead of accessing them
// directly...
const auto& registers = gpu.Maxwell3D().regs;
const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) -
static_cast<std::size_t>(Source::RenderTarget0)];
surface_address = rt.Address();
surface_width = rt.width;
surface_height = rt.height;
if (rt.format != Tegra::RenderTargetFormat::NONE) {
surface_format = ConvertToTextureFormat(rt.format);
}
break;
}
case Source::Custom: {
// Keep user-specified values
break;
}
default:
qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
break;
}
surface_address_control->SetValue(surface_address);
surface_width_control->setValue(surface_width);
surface_height_control->setValue(surface_height);
surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
if (surface_address == 0) {
surface_picture_label->hide();
surface_info_label->setText(tr("(invalid surface address)"));
surface_info_label->setAlignment(Qt::AlignCenter);
surface_picker_x_control->setEnabled(false);
surface_picker_y_control->setEnabled(false);
save_surface->setEnabled(false);
return;
}
// TODO: Implement a good way to visualize alpha components!
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
gpu.MemoryManager().GetPointer(surface_address), 1, 1,
Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);
surface_picture_label->show();
for (unsigned int y = 0; y < surface_height; ++y) {
for (unsigned int x = 0; x < surface_width; ++x) {
Common::Vec4<u8> color;
color[0] = texture_data[x + y * surface_width + 0];
color[1] = texture_data[x + y * surface_width + 1];
color[2] = texture_data[x + y * surface_width + 2];
color[3] = texture_data[x + y * surface_width + 3];
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
}
}
pixmap = QPixmap::fromImage(decoded_image);
surface_picture_label->setPixmap(pixmap);
surface_picture_label->resize(pixmap.size());
// Update the info with pixel data
surface_picker_x_control->setEnabled(true);
surface_picker_y_control->setEnabled(true);
Pick(surface_picker_x, surface_picker_y);
// Enable saving the converted pixmap to file
save_surface->setEnabled(true);
}
void GraphicsSurfaceWidget::SaveSurface() {
const QString png_filter = tr("Portable Network Graphic (*.png)");
const QString bin_filter = tr("Binary data (*.bin)");
QString selected_filter;
const QString filename = QFileDialog::getSaveFileName(
this, tr("Save Surface"),
QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)),
QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter);
if (filename.isEmpty()) {
// If the user canceled the dialog, don't save anything.
return;
}
if (selected_filter == png_filter) {
const QPixmap* const pixmap = surface_picture_label->pixmap();
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
QFile file{filename};
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename));
return;
}
if (!pixmap->save(&file, "PNG")) {
QMessageBox::warning(this, tr("Error"),
tr("Failed to save surface data to file '%1'").arg(filename));
}
} else if (selected_filter == bin_filter) {
auto& gpu = Core::System::GetInstance().GPU();
const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
const u8* const buffer = Memory::GetPointer(*address);
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
QFile file{filename};
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename));
return;
}
const int size =
surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
const QByteArray data(reinterpret_cast<const char*>(buffer), size);
if (file.write(data) != data.size()) {
QMessageBox::warning(
this, tr("Error"),
tr("Failed to completely write surface data to file. The saved data will "
"likely be corrupt."));
}
} else {
UNREACHABLE_MSG("Unhandled filter selected");
}
}

View File

@@ -1,96 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QLabel>
#include <QPushButton>
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
class QComboBox;
class QSpinBox;
class CSpinBox;
class GraphicsSurfaceWidget;
class SurfacePicture : public QLabel {
Q_OBJECT
public:
explicit SurfacePicture(QWidget* parent = nullptr,
GraphicsSurfaceWidget* surface_widget = nullptr);
~SurfacePicture() override;
protected slots:
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
private:
GraphicsSurfaceWidget* surface_widget;
};
class GraphicsSurfaceWidget : public BreakPointObserverDock {
Q_OBJECT
using Event = Tegra::DebugContext::Event;
enum class Source {
RenderTarget0 = 0,
RenderTarget1 = 1,
RenderTarget2 = 2,
RenderTarget3 = 3,
RenderTarget4 = 4,
RenderTarget5 = 5,
RenderTarget6 = 6,
RenderTarget7 = 7,
ZBuffer = 8,
Custom = 9,
};
public:
explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
QWidget* parent = nullptr);
void Pick(int x, int y);
public slots:
void OnSurfaceSourceChanged(int new_value);
void OnSurfaceAddressChanged(qint64 new_value);
void OnSurfaceWidthChanged(int new_value);
void OnSurfaceHeightChanged(int new_value);
void OnSurfaceFormatChanged(int new_value);
void OnSurfacePickerXChanged(int new_value);
void OnSurfacePickerYChanged(int new_value);
void OnUpdate();
signals:
void Update();
private:
void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
void OnResumed() override;
void SaveSurface();
QComboBox* surface_source_list;
CSpinBox* surface_address_control;
QSpinBox* surface_width_control;
QSpinBox* surface_height_control;
QComboBox* surface_format_control;
SurfacePicture* surface_picture_label;
QSpinBox* surface_picker_x_control;
QSpinBox* surface_picker_y_control;
QLabel* surface_info_label;
QPushButton* save_surface;
Source surface_source;
GPUVAddr surface_address;
unsigned surface_width;
unsigned surface_height;
Tegra::Texture::TextureFormat surface_format;
int surface_picker_x = 0;
int surface_picker_y = 0;
};

View File

@@ -467,9 +467,10 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
void GameList::RefreshGameDirectory() {
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
search_field->clear();
PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
}

View File

@@ -90,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/configuration/configure_dialog.h"
#include "yuzu/debugger/console.h"
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
#include "yuzu/debugger/graphics/graphics_surface.h"
#include "yuzu/debugger/profiler.h"
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/discord.h"
@@ -214,7 +213,8 @@ GMainWindow::GMainWindow()
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
game_list->LoadCompatibilityList();
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
// Show one-time "callout" messages to the user
ShowTelemetryCallout();
@@ -483,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() {
graphicsBreakpointsWidget->hide();
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
graphicsSurfaceWidget->hide();
debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
waitTreeWidget = new WaitTreeWidget(this);
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide();
@@ -1284,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
game_list->PopulateAsync(UISettings::values.gamedir,
UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
config->Save();
@@ -1373,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() {
const auto success = [this]() {
QMessageBox::information(this, tr("Successfully Installed"),
tr("The file was successfully installed."));
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
};
const auto failed = [this]() {
@@ -1500,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() {
void GMainWindow::OnMenuSelectGameListRoot() {
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
if (!dir_path.isEmpty()) {
UISettings::values.gamedir = dir_path;
game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan);
UISettings::values.game_directory_path = dir_path;
game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan);
}
}
@@ -1523,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
: FileUtil::UserPath::NANDDir,
dir_path.toStdString());
Service::FileSystem::CreateFactories(*vfs);
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
}
@@ -1675,8 +1672,8 @@ void GMainWindow::OnConfigure() {
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
game_list->PopulateAsync(UISettings::values.gamedir,
UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
config->Save();
@@ -1926,7 +1923,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
Service::FileSystem::CreateFactories(*vfs);
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
game_list->PopulateAsync(UISettings::values.game_directory_path,
UISettings::values.game_directory_deepscan);
}
}
@@ -2033,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
event->acceptProposedAction();
}
void GMainWindow::keyPressEvent(QKeyEvent* event) {
if (render_window) {
render_window->ForwardKeyPressEvent(event);
}
}
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
if (render_window) {
render_window->ForwardKeyReleaseEvent(event);
}
}
bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
@@ -2100,7 +2110,8 @@ int main(int argc, char* argv[]) {
QCoreApplication::setOrganizationName("yuzu team");
QCoreApplication::setApplicationName("yuzu");
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
// Enables the core to make the qt created contexts current on std::threads
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when

View File

@@ -23,7 +23,6 @@ class EmuThread;
class GameList;
class GImageInfo;
class GraphicsBreakPointsWidget;
class GraphicsSurfaceWidget;
class GRenderWindow;
class LoadingScreen;
class MicroProfileDialog;
@@ -240,7 +239,6 @@ private:
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
GraphicsSurfaceWidget* graphicsSurfaceWidget;
WaitTreeWidget* waitTreeWidget;
QAction* actions_recent_files[max_recent_files_item];
@@ -254,4 +252,8 @@ protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
// Overrides used to forward signals to the render window when the focus moves out.
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
};

View File

@@ -55,8 +55,8 @@ struct Values {
QString roms_path;
QString symbols_path;
QString screenshot_path;
QString gamedir;
bool gamedir_deepscan;
QString game_directory_path;
bool game_directory_deepscan;
QStringList recent_files;
QString theme;

View File

@@ -19,6 +19,37 @@
#include "input_common/sdl/sdl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
class SDLGLContext : public Core::Frontend::GraphicsContext {
public:
explicit SDLGLContext() {
// create a hidden window to make the shared context against
window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
context = SDL_GL_CreateContext(window);
}
~SDLGLContext() {
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
}
void MakeCurrent() override {
SDL_GL_MakeCurrent(window, context);
}
void DoneCurrent() override {
SDL_GL_MakeCurrent(window, nullptr);
}
void SwapBuffers() override {}
private:
SDL_Window* window;
SDL_GLContext context;
};
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
InputCommon::GetMotionEmu()->Tilt(x, y);
@@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc);
@@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
if (fullscreen) {
Fullscreen();
}
gl_context = SDL_GL_CreateContext(render_window);
if (gl_context == nullptr) {
@@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}

View File

@@ -27,6 +27,8 @@ public:
/// Releases the GL context from the caller thread
void DoneCurrent() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;