Compare commits

..

56 Commits

Author SHA1 Message Date
Lioncash
f00a54f508 bcat/backend: Make formatting of passphrase consistent in NullBackend::SetPassphrase()
Aligns the '=' to be consistent with the rest of the logs within this
source file.
2020-02-12 01:18:29 -05:00
Lioncash
eefd97e80d bcat/backend: Prevent fmt exception in debug log within NullBackend::Clear()
A formatting specifier within Clear wasn't being used, which will cause
fmt to throw an exception. This fixes that.
2020-02-12 01:14:47 -05:00
bunnei
37f1cf8cbd Merge pull request #3376 from ReinUsesLisp/point-sprite
gl_rasterizer: Implement GL_POINT_SPRITE
2020-02-11 08:26:07 -05:00
bunnei
84ea9c2b42 Merge pull request #3372 from ReinUsesLisp/fix-back-stencil
maxwell_3d: Fix stencil back mask
2020-02-09 22:29:28 -05:00
Zach Hilman
21c3f48279 Merge pull request #3391 from Morph1984/remove-unknown
Remove option "Show files with type 'Unknown'"
2020-02-09 12:08:01 -05:00
Morph
fcf3425b1b Remove option "Show files with type 'Unknown'" 2020-02-09 11:30:02 -05:00
bunnei
a952fbc5b3 Merge pull request #3388 from bunnei/service-shared-ptr
hle: services: Use std::shared_ptr instead of copy by value.
- This is a prerequisite to adding a mutex to `ServiceFramework`, which cannot be copied.
- This will be used for threaded services.
2020-02-08 21:35:30 -05:00
bunnei
e210835dd0 Merge pull request #3387 from bunnei/gpu-mpscqueue
gpu_thread: Use MPSCQueue for GPU commands.
2020-02-08 21:15:48 -05:00
bunnei
6536cc9741 Merge pull request #3386 from bunnei/gpu-mem-interface
video_core: memory_manager: Use GPU interface for cache functions.
2020-02-08 21:15:27 -05:00
bunnei
7b07e521ca hle: services: Use std::shared_ptr instead of copy by value. 2020-02-07 23:02:26 -05:00
bunnei
b5c13ee0eb gpu_thread: Use MPSCQueue for GPU commands.
- Necessary for multiple service threads.
2020-02-07 23:01:23 -05:00
bunnei
7cacb08cdf video_core: memory_manager: Use GPU interface for cache functions. 2020-02-07 22:59:35 -05:00
bunnei
90bda66028 Merge pull request #3378 from ReinUsesLisp/uscaled
maxwell_to_gl: Implement R8G8_USCALED
2020-02-07 22:55:52 -05:00
bunnei
90df4b8e2b Merge pull request #3369 from ReinUsesLisp/shf
shader/shift: Implement SHF
2020-02-07 22:06:57 -05:00
bunnei
aa3f9b9606 Merge pull request #3381 from bunnei/ipc-fix
hle: services: Fix prepo IPC, and add better error checking.
2020-02-07 16:25:42 -05:00
bunnei
09d766d357 Merge pull request #3362 from ReinUsesLisp/fix-instanced
gl_rasterizer: Fix instanced draw arrays
2020-02-06 21:39:59 -05:00
bunnei
1b01c3036d Merge pull request #3366 from bunnei/swkbd-fixes
applets: Fixes for software keyboard and transfer memory.
2020-02-05 23:26:32 -05:00
bunnei
ba53543da6 kernel: transfer_memory: Properly reserve and reset memory region. 2020-02-05 23:06:54 -05:00
Zach Hilman
7a547b9342 wait_object: Make wait behavior only require one object to signal.
- This was holdover from citra.
2020-02-05 23:06:53 -05:00
bunnei
3a0c1e79f8 am: Correct IPC object count mismatch. 2020-02-05 23:06:53 -05:00
bunnei
77da74e17a services: am: Clear events on PopOutData and PopInteractiveOutData. 2020-02-05 23:06:52 -05:00
bunnei
84e895cdd6 am: Refactor IStorage interface. 2020-02-05 23:06:52 -05:00
bunnei
3557fa25d0 applets: software_keyboard: Signal state change on end of interactive session. 2020-02-05 23:06:51 -05:00
bunnei
be5fcffb89 applets: software_keyboard: Minor cleanup. 2020-02-05 23:06:50 -05:00
bunnei
2245c24e21 services: prepo: Fix IPC interface with SaveReport/SaveReportWithUser. 2020-02-05 22:52:35 -05:00
bunnei
9751ccc5e0 hle_ipc: Add error checking to read/write buffer access. 2020-02-05 22:52:35 -05:00
ReinUsesLisp
8bb9eef97b maxwell_to_gl: Implement R8G8_USCALED 2020-02-04 21:32:36 -03:00
ReinUsesLisp
c81c361e82 maxwell_to_gl: Reduce unimplemented formats to LOG_ERROR 2020-02-04 21:32:08 -03:00
bunnei
a0b4be4262 Merge pull request #3377 from ReinUsesLisp/bitset-vk
vk_rasterizer: Use noexcept methods of std::bitset
2020-02-04 16:56:57 -05:00
ReinUsesLisp
0eb36c90f4 vk_rasterizer: Use noexcept variants of std::bitset
Removes bounds checking from "texceptions" instances.
2020-02-04 18:04:24 -03:00
bunnei
08c508b1c4 Merge pull request #3357 from ReinUsesLisp/bfi-rc
shader/bfi: Implement register-constant buffer variant
2020-02-04 15:14:13 -05:00
ReinUsesLisp
7da52673d0 gl_rasterizer: Implement GL_POINT_SPRITE
OpenGL core defaults to GL_POINT_SPRITE, meanwhile on OpenGL
compatibility we have to explicitly enable it. This fixes
gl_PointCoord's behaviour.
2020-02-04 15:19:45 -03:00
bunnei
bf21aacc74 Merge pull request #3356 from ReinUsesLisp/fcmp
shader/arithmetic: Implement FCMP
2020-02-04 11:36:59 -05:00
bunnei
5733287822 Merge pull request #3360 from CJBok/statusbar-buttons
GUI: Togglable graphics settings buttons in status bar
2020-02-03 16:57:18 -05:00
bunnei
c31ec00d67 Merge pull request #3337 from ReinUsesLisp/vulkan-staged
yuzu: Implement Vulkan frontend
2020-02-03 16:56:25 -05:00
bunnei
2cd51fc9fd Merge pull request #3374 from lioncash/udp
input_common/udp: Minor changes
2020-02-03 11:41:04 -05:00
ReinUsesLisp
4eed744277 maxwell_3d: Fix stencil back mask 2020-02-02 17:50:46 -03:00
ReinUsesLisp
729ca120e3 shader/shift: Implement SHIFT_RIGHT_{IMM,R}
Shifts a pair of registers to the right and returns the low register.
2020-02-01 21:20:02 -03:00
ReinUsesLisp
017474c3f8 shader/shift: Implement SHF_LEFT_{IMM,R}
Shifts a pair of registers to the left and returns the high register.
2020-02-01 21:19:44 -03:00
ReinUsesLisp
b69321650e gl_rasterizer: Fix instanced draw arrays
glDrawArrays was being used when the draw had a base instance specified.
This commit removes the draw parameters abstraction and fixes the
mentioned issue.
2020-01-30 02:22:00 -03:00
ReinUsesLisp
d027850f33 ci: Disable Vulkan for Windows MinGW builds 2020-01-29 19:44:00 -03:00
ReinUsesLisp
a7beabb68f yuzu/bootmanager: Define Vulkan widget only when enabled 2020-01-29 19:20:12 -03:00
ReinUsesLisp
252415a163 ci: Disable Vulkan for Linux builds 2020-01-29 18:06:16 -03:00
ReinUsesLisp
c29584a090 yuzu_cmd: Fix memcpy on Vulkan handlers 2020-01-29 17:53:11 -03:00
ReinUsesLisp
f92cbc5501 yuzu: Implement Vulkan frontend
Adds a Qt and SDL2 frontend for Vulkan. It also finishes the missing
bits on Vulkan initialization.
2020-01-29 17:53:11 -03:00
ReinUsesLisp
8299f1ceef web_service/telemetry_json: Report USER_CONFIG 2020-01-29 17:53:11 -03:00
ReinUsesLisp
788d57d723 settings: Add settings for graphics backend 2020-01-29 17:53:11 -03:00
ReinUsesLisp
e651e54b85 core: Only wait for idle on gpu_core when it was initialized
This fixes crashes when a Vulkan device fails to initialize.
2020-01-29 17:53:11 -03:00
ReinUsesLisp
9f0162e4b5 shader/other: Fix skips for SYNC and BRK 2020-01-29 17:53:11 -03:00
ReinUsesLisp
270177f38a shader/other: Stub S2R LaneId 2020-01-29 17:53:11 -03:00
ReinUsesLisp
b35449c85d buffer_cache: Delay buffer destructions
Delay buffer destruction some extra frames to avoid destroying buffers
that are still being used from older frames. This happens on Nvidia's
driver with mailbox.
2020-01-29 17:53:11 -03:00
CJBok
8d6b4e836c clang 2020-01-29 05:43:55 +01:00
CJBok
6e87111f91 minor corrections 2020-01-29 00:02:28 +01:00
CJBok
4bc4fdf5ff GUI: Togglable graphics settings buttons in status bar 2020-01-28 23:59:30 +01:00
ReinUsesLisp
137a8aa55c shader/bfi: Implement register-constant buffer variant
It's the same as the variant that was implemented, but it takes the
operands from another source.
2020-01-27 01:20:38 -03:00
ReinUsesLisp
e3fc3459c8 shader/arithmetic: Implement FCMP
Compares the third operand with zero, then selects between the first and
second.
2020-01-27 01:15:44 -03:00
79 changed files with 1867 additions and 552 deletions

View File

@@ -5,7 +5,7 @@ cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_VULKAN=No
ninja

View File

@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build || true && cd build
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_VULKAN=No
ninja
# Clean up the dirty hacks

View File

@@ -10,6 +10,6 @@
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="colorful">
<file>style.qss</file>
<file alias="style.qss">../default/style.qss</file>
</qresource>
</RCC>

View File

@@ -1,4 +0,0 @@
/*
This file is intentionally left blank.
We do not want to apply any stylesheet for colorful, only icons.
*/

View File

@@ -1,25 +1,18 @@
<RCC>
<qresource prefix="icons/default">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="default">
<file>style.qss</file>
</qresource>
</RCC>

35
dist/qt_themes/default/style.qss vendored Normal file
View File

@@ -0,0 +1,35 @@
QPushButton#TogglableStatusBarButton {
color: #959595;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#TogglableStatusBarButton:checked {
color: #000000;
}
QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton {
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#RendererStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
QPushButton#RendererStatusBarButton:!checked{
color: #0066ff;
}

View File

@@ -1236,3 +1236,41 @@ QToolButton:disabled,
QPlainTextEdit:disabled {
background-color: #2b2e31;
}
QPushButton#TogglableStatusBarButton {
min-width: 0px;
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#TogglableStatusBarButton:checked {
color: #ffffff;
}
QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton {
min-width: 0px;
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#RendererStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
QPushButton#RendererStatusBarButton:!checked{
color: #00ccdd;
}

View File

@@ -268,7 +268,9 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
gpu_core->WaitIdle();
if (gpu_core) {
gpu_core->WaitIdle();
}
// Shutdown emulation session
renderer.reset();

View File

@@ -75,6 +75,13 @@ public:
return nullptr;
}
/// Returns if window is shown (not minimized)
virtual bool IsShown() const = 0;
/// Retrieves Vulkan specific handlers from the window
virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const = 0;
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed

View File

@@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
std::vector<u8> buffer;
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_a) {
ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
"BufferDescriptorA invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorA()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
} else {
ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
"BufferDescriptorX invalid buffer_index {}", buffer_index);
buffer.resize(BufferDescriptorX()[buffer_index].Size());
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
}
@@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
return 0;
}
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
if (size > buffer_size) {
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
@@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_b) {
ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
"BufferDescriptorB invalid buffer_index {}", buffer_index);
ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,
"BufferDescriptorB buffer_index {} is not large enough", buffer_index);
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
} else {
ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
"BufferDescriptorC invalid buffer_index {}", buffer_index);
ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,
"BufferDescriptorC buffer_index {} is not large enough", buffer_index);
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
}
@@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
}
std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
return is_buffer_a ? BufferDescriptorA()[buffer_index].Size()
: BufferDescriptorX()[buffer_index].Size();
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
"BufferDescriptorA invalid buffer_index {}", buffer_index);
ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
"BufferDescriptorA buffer_index {} is empty", buffer_index);
return BufferDescriptorA()[buffer_index].Size();
} else {
ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
"BufferDescriptorX invalid buffer_index {}", buffer_index);
ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
"BufferDescriptorX buffer_index {} is empty", buffer_index);
return BufferDescriptorX()[buffer_index].Size();
}
}
std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
return is_buffer_b ? BufferDescriptorB()[buffer_index].Size()
: BufferDescriptorC()[buffer_index].Size();
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
"BufferDescriptorB invalid buffer_index {}", buffer_index);
return BufferDescriptorB()[buffer_index].Size();
} else {
ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
"BufferDescriptorC invalid buffer_index {}", buffer_index);
return BufferDescriptorC()[buffer_index].Size();
}
}
std::string HLERequestContext::Description() const {

View File

@@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
}
auto& kernel = system.Kernel();
auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
return reserve_result;
}
auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
const auto result = handle_table.Create(std::move(transfer_mem_handle));
const auto result{handle_table.Create(std::move(transfer_mem_handle))};
if (result.Failed()) {
return result.Code();
}

View File

@@ -8,15 +8,23 @@
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
TransferMemory::~TransferMemory() = default;
TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory)
: Object{kernel}, memory{memory} {}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address,
u64 size, MemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)};
TransferMemory::~TransferMemory() {
// Release memory region when transfer memory is destroyed
Reset();
}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory,
VAddr base_address, u64 size,
MemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{
std::make_shared<TransferMemory>(kernel, memory)};
transfer_memory->base_address = base_address;
transfer_memory->memory_size = size;
@@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr
}
const u8* TransferMemory::GetPointer() const {
return backing_block.get()->data();
return memory.GetPointer(base_address);
}
u64 TransferMemory::GetSize() const {
@@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return RESULT_SUCCESS;
}
ResultCode TransferMemory::Reserve() {
auto& vm_manager{owner_process->VMManager()};
const auto check_range_result{vm_manager.CheckRangeState(
base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All,
VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None,
MemoryAttribute::IpcAndDeviceMapped)};
if (check_range_result.Failed()) {
return check_range_result.Code();
}
auto [state_, permissions_, attribute] = *check_range_result;
if (const auto result{vm_manager.ReprotectRange(
base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))};
result.IsError()) {
return result;
}
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
attribute | MemoryAttribute::Locked);
}
ResultCode TransferMemory::Reset() {
auto& vm_manager{owner_process->VMManager()};
if (const auto result{vm_manager.CheckRangeState(
base_address, memory_size,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None,
VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked,
MemoryAttribute::IpcAndDeviceMapped)};
result.Failed()) {
return result.Code();
}
if (const auto result{
vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)};
result.IsError()) {
return result;
}
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
MemoryAttribute::None);
}
ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
if (memory_size != size) {
return ERR_INVALID_SIZE;

View File

@@ -11,6 +11,10 @@
union ResultCode;
namespace Memory {
class Memory;
}
namespace Kernel {
class KernelCore;
@@ -26,12 +30,13 @@ enum class MemoryPermission : u32;
///
class TransferMemory final : public Object {
public:
explicit TransferMemory(KernelCore& kernel);
explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);
~TransferMemory() override;
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory,
VAddr base_address, u64 size,
MemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
@@ -80,6 +85,14 @@ public:
///
ResultCode UnmapMemory(VAddr address, u64 size);
/// Reserves the region to be used for the transfer memory, called after the transfer memory is
/// created.
ResultCode Reserve();
/// Resets the region previously used for the transfer memory, called after the transfer memory
/// is closed.
ResultCode Reset();
private:
/// Memory block backing this instance.
std::shared_ptr<PhysicalMemory> backing_block;
@@ -98,6 +111,8 @@ private:
/// Whether or not this transfer memory instance has mapped memory.
bool is_mapped = false;
Memory::Memory& memory;
};
} // namespace Kernel

View File

@@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
MemoryAttribute attribute) {
constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped;
constexpr auto ignore_mask =
MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;
constexpr auto attribute_mask = ~ignore_mask;
const auto result = CheckRangeState(

View File

@@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 {
DeviceMapped = 4,
/// Uncached memory
Uncached = 8,
IpcAndDeviceMapped = LockedForIPC | DeviceMapped,
};
constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
@@ -654,6 +656,35 @@ public:
/// is scheduled.
Common::PageTable page_table{Memory::PAGE_BITS};
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
/// Checks if an address range adheres to the specified states provided.
///
/// @param address The starting address of the address range.
/// @param size The size of the address range.
/// @param state_mask The memory state mask.
/// @param state The state to compare the individual VMA states against,
/// which is done in the form of: (vma.state & state_mask) != state.
/// @param permission_mask The memory permissions mask.
/// @param permissions The permission to compare the individual VMA permissions against,
/// which is done in the form of:
/// (vma.permission & permission_mask) != permission.
/// @param attribute_mask The memory attribute mask.
/// @param attribute The memory attributes to compare the individual VMA attributes
/// against, which is done in the form of:
/// (vma.attributes & attribute_mask) != attribute.
/// @param ignore_mask The memory attributes to ignore during the check.
///
/// @returns If successful, returns a tuple containing the memory attributes
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
/// memory state across the memory range.
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
///
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
VMAPermission permission_mask, VMAPermission permissions,
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
private:
using VMAIter = VMAMap::iterator;
@@ -707,35 +738,6 @@ private:
/// Clears out the page table
void ClearPageTable();
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
/// Checks if an address range adheres to the specified states provided.
///
/// @param address The starting address of the address range.
/// @param size The size of the address range.
/// @param state_mask The memory state mask.
/// @param state The state to compare the individual VMA states against,
/// which is done in the form of: (vma.state & state_mask) != state.
/// @param permission_mask The memory permissions mask.
/// @param permissions The permission to compare the individual VMA permissions against,
/// which is done in the form of:
/// (vma.permission & permission_mask) != permission.
/// @param attribute_mask The memory attribute mask.
/// @param attribute The memory attributes to compare the individual VMA attributes
/// against, which is done in the form of:
/// (vma.attributes & attribute_mask) != attribute.
/// @param ignore_mask The memory attributes to ignore during the check.
///
/// @returns If successful, returns a tuple containing the memory attributes
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
/// memory state across the memory range.
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
///
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
VMAPermission permission_mask, VMAPermission permissions,
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
/// Gets the amount of memory currently mapped (state != Unmapped) in a range.
ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;

View File

@@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
if (ShouldWait(thread.get()))
continue;
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
// and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
if (thread_status == ThreadStatus::WaitSynch) {
ready_to_run = thread->AllWaitObjectsReady();
}
if (ready_to_run) {
candidate = thread.get();
candidate_priority = thread->GetPriority();
}
candidate = thread.get();
candidate_priority = thread->GetPriority();
}
return SharedFrom(candidate);

View File

@@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
IStorage::IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
IStorageImpl::~IStorageImpl() = default;
class StorageDataImpl final : public IStorageImpl {
public:
explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {}
std::vector<u8>& GetData() override {
return buffer;
}
const std::vector<u8>& GetData() const override {
return buffer;
}
std::size_t GetSize() const override {
return buffer.size();
}
private:
std::vector<u8> buffer;
};
IStorage::IStorage(std::vector<u8>&& buffer)
: ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} {
Register();
}
void IStorage::Register() {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
@@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer)
IStorage::~IStorage() = default;
const std::vector<u8>& IStorage::GetData() const {
return buffer;
void IStorage::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorageAccessor>(*this);
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
@@ -816,7 +847,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -825,26 +856,25 @@ private:
void PopOutData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
const auto storage = applet->GetBroker().PopNormalDataToGame();
if (storage == nullptr) {
LOG_ERROR(Service_AM,
"storage is a nullptr. There is no data in the current normal channel");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NO_DATA_IN_CHANNEL);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(*storage));
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
ASSERT(applet->IsInitialized());
applet->ExecuteInteractive();
@@ -857,19 +887,18 @@ private:
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
if (storage == nullptr) {
LOG_ERROR(Service_AM,
"storage is a nullptr. There is no data in the current interactive channel");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NO_DATA_IN_CHANNEL);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(*storage));
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
@@ -891,15 +920,6 @@ private:
std::shared_ptr<Applets::Applet> applet;
};
void IStorage::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorageAccessor>(*this);
}
IStorageAccessor::IStorageAccessor(IStorage& storage)
: ServiceFramework("IStorageAccessor"), backing(storage) {
// clang-format off
@@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(backing.buffer.size()));
rb.Push(static_cast<u64>(backing.GetSize()));
}
void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
@@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
if (data.size() > backing.buffer.size() - offset) {
if (data.size() > backing.GetSize() - offset) {
LOG_ERROR(Service_AM,
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
backing.buffer.size(), data.size(), offset);
backing.GetSize(), data.size(), offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
std::memcpy(backing.GetData().data() + offset, data.data(), data.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
if (size > backing.buffer.size() - offset) {
if (size > backing.GetSize() - offset) {
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
backing.buffer.size(), size, offset);
backing.GetSize(), size, offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
return;
}
ctx.WriteBuffer(backing.buffer.data() + offset, size);
ctx.WriteBuffer(backing.GetData().data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
rp.SetCurrentOffset(3);
const auto handle{rp.Pop<Kernel::Handle>()};
const auto transfer_mem =
auto transfer_mem =
system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
if (transfer_mem == nullptr) {
@@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
rb.PushIpcInterface<IStorage>(std::move(memory));
}
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
@@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
u64 build_id{};
std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
const auto data =
backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
if (data.has_value()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(*data);
rb.PushIpcInterface<IStorage>(std::move(*data));
launch_popped_application_specific = true;
return;
}
@@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
std::memcpy(buffer.data(), &params, buffer.size());
rb.PushIpcInterface<AM::IStorage>(buffer);
rb.PushIpcInterface<IStorage>(std::move(buffer));
launch_popped_account_preselect = true;
return;
}

View File

@@ -12,7 +12,8 @@
namespace Kernel {
class KernelCore;
}
class TransferMemory;
} // namespace Kernel
namespace Service::NVFlinger {
class NVFlinger;
@@ -188,19 +189,36 @@ private:
std::shared_ptr<AppletMessageQueue> msg_queue;
};
class IStorageImpl {
public:
virtual ~IStorageImpl();
virtual std::vector<u8>& GetData() = 0;
virtual const std::vector<u8>& GetData() const = 0;
virtual std::size_t GetSize() const = 0;
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer);
explicit IStorage(std::vector<u8>&& buffer);
~IStorage() override;
const std::vector<u8>& GetData() const;
std::vector<u8>& GetData() {
return impl->GetData();
}
const std::vector<u8>& GetData() const {
return impl->GetData();
}
std::size_t GetSize() const {
return impl->GetSize();
}
private:
void Register();
void Open(Kernel::HLERequestContext& ctx);
std::vector<u8> buffer;
friend class IStorageAccessor;
std::shared_ptr<IStorageImpl> impl;
};
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {

View File

@@ -50,16 +50,17 @@ AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() co
return {std::move(out_normal), std::move(out_interactive)};
}
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
if (out_channel.empty())
return nullptr;
auto out = std::move(out_channel.front());
out_channel.pop_front();
pop_out_data_event.writable->Clear();
return out;
}
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
if (in_channel.empty())
return nullptr;
@@ -68,16 +69,17 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
return out;
}
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
if (out_interactive_channel.empty())
return nullptr;
auto out = std::move(out_interactive_channel.front());
out_interactive_channel.pop_front();
pop_interactive_out_data_event.writable->Clear();
return out;
}
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
if (in_interactive_channel.empty())
return nullptr;
@@ -86,21 +88,21 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
return out;
}
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
in_channel.push_back(std::make_unique<IStorage>(storage));
void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) {
in_channel.emplace_back(std::move(storage));
}
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
out_channel.push_back(std::make_unique<IStorage>(storage));
void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
out_channel.emplace_back(std::move(storage));
pop_out_data_event.writable->Signal();
}
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
in_interactive_channel.emplace_back(std::move(storage));
}
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
out_interactive_channel.emplace_back(std::move(storage));
pop_interactive_out_data_event.writable->Signal();
}

View File

@@ -72,17 +72,17 @@ public:
// Retrieves but does not pop the data sent to applet.
RawChannelData PeekDataToAppletForDebug() const;
std::unique_ptr<IStorage> PopNormalDataToGame();
std::unique_ptr<IStorage> PopNormalDataToApplet();
std::shared_ptr<IStorage> PopNormalDataToGame();
std::shared_ptr<IStorage> PopNormalDataToApplet();
std::unique_ptr<IStorage> PopInteractiveDataToGame();
std::unique_ptr<IStorage> PopInteractiveDataToApplet();
std::shared_ptr<IStorage> PopInteractiveDataToGame();
std::shared_ptr<IStorage> PopInteractiveDataToApplet();
void PushNormalDataFromGame(IStorage storage);
void PushNormalDataFromApplet(IStorage storage);
void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage);
void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage);
void PushInteractiveDataFromGame(IStorage storage);
void PushInteractiveDataFromApplet(IStorage storage);
void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage);
void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage);
void SignalStateChanged() const;
@@ -94,16 +94,16 @@ private:
// Queues are named from applet's perspective
// PopNormalDataToApplet and PushNormalDataFromGame
std::deque<std::unique_ptr<IStorage>> in_channel;
std::deque<std::shared_ptr<IStorage>> in_channel;
// PopNormalDataToGame and PushNormalDataFromApplet
std::deque<std::unique_ptr<IStorage>> out_channel;
std::deque<std::shared_ptr<IStorage>> out_channel;
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
std::deque<std::shared_ptr<IStorage>> in_interactive_channel;
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
Kernel::EventPair state_changed_event;

View File

@@ -186,7 +186,7 @@ void Error::Execute() {
void Error::DisplayCompleted() {
complete = true;
broker.PushNormalDataFromApplet(IStorage{{}});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
broker.SignalStateChanged();
}

View File

@@ -20,7 +20,7 @@ namespace Service::AM::Applets {
constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet();
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
@@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) {
std::vector<u8> out(sizeof(Return));
std::memcpy(out.data(), &return_, sizeof(Return));
broker.PushNormalDataFromApplet(IStorage{out});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out)));
broker.SignalStateChanged();
}
@@ -198,7 +198,7 @@ void PhotoViewer::Execute() {
}
void PhotoViewer::ViewFinished() {
broker.PushNormalDataFromApplet(IStorage{{}});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
broker.SignalStateChanged();
}
@@ -234,8 +234,8 @@ void StubApplet::ExecuteInteractive() {
LOG_WARNING(Service_AM, "called (STUBBED)");
LogCurrentStorage(broker, "ExecuteInteractive");
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
broker.SignalStateChanged();
}
@@ -243,8 +243,8 @@ void StubApplet::Execute() {
LOG_WARNING(Service_AM, "called (STUBBED)");
LogCurrentStorage(broker, "Execute");
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
broker.SignalStateChanged();
}

View File

@@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() {
void ProfileSelect::Execute() {
if (complete) {
broker.PushNormalDataFromApplet(IStorage{final_data});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
return;
}
@@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
std::memcpy(final_data.data(), &output, final_data.size());
broker.PushNormalDataFromApplet(IStorage{final_data});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
broker.SignalStateChanged();
}

View File

@@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() {
void SoftwareKeyboard::Execute() {
if (complete) {
broker.PushNormalDataFromApplet(IStorage{final_data});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
broker.SignalStateChanged();
return;
}
@@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
if (config.utf_8) {
const u64 size = text->size() + 8;
const u64 size = text->size() + sizeof(u64);
const auto new_text = Common::UTF16ToUTF8(*text);
std::memcpy(output_sub.data(), &size, sizeof(u64));
@@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
std::memcpy(output_main.data() + 4, new_text.data(),
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
} else {
const u64 size = text->size() * 2 + 8;
const u64 size = text->size() * 2 + sizeof(u64);
std::memcpy(output_sub.data(), &size, sizeof(u64));
std::memcpy(output_sub.data() + 8, text->data(),
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
@@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
final_data = output_main;
if (complete) {
broker.PushNormalDataFromApplet(IStorage{output_main});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
broker.SignalStateChanged();
} else {
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::move(output_sub)));
}
} else {
output_main[0] = 1;
complete = true;
broker.PushNormalDataFromApplet(IStorage{output_main});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
broker.SignalStateChanged();
}
}

View File

@@ -284,7 +284,7 @@ void WebBrowser::Finalize() {
std::vector<u8> data(sizeof(WebCommonReturnValue));
std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
broker.PushNormalDataFromApplet(IStorage{data});
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
broker.SignalStateChanged();
if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {

View File

@@ -117,13 +117,13 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
}
bool NullBackend::Clear(u64 title_id) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
return true;
}
void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
Common::HexToString(passphrase));
}

View File

@@ -420,7 +420,7 @@ public:
return;
}
IFile file(result.Unwrap());
auto file = std::make_shared<IFile>(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -445,7 +445,7 @@ public:
return;
}
IDirectory directory(result.Unwrap());
auto directory = std::make_shared<IDirectory>(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -794,8 +794,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
auto filesystem = std::make_shared<IFileSystem>(
fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -846,7 +846,8 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
id = FileSys::StorageId::NandSystem;
}
IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
auto filesystem =
std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -898,7 +899,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
IStorage storage(std::move(romfs.Unwrap()));
auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap()));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -937,7 +938,8 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
FileSys::PatchManager pm{title_id};
IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
auto storage = std::make_shared<IStorage>(
pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);

View File

@@ -50,16 +50,16 @@ private:
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = ctx.ReadBuffer(1);
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
if (Type == Core::Reporter::PlayReportType::New) {
data.emplace_back(ctx.ReadBuffer(1));
}
LOG_DEBUG(Service_PREPO,
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
static_cast<u8>(Type), process_id, data1.size(), data2.size());
LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
static_cast<u8>(Type), process_id, data[0].size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
process_id);
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -70,19 +70,19 @@ private:
IPC::RequestParser rp{ctx};
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = ctx.ReadBuffer(1);
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
if (Type == Core::Reporter::PlayReportType::New) {
data.emplace_back(ctx.ReadBuffer(1));
}
LOG_DEBUG(
Service_PREPO,
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, "
"data2_size={:016X}",
static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size());
const auto& reporter{system.GetReporter()};
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
process_id, user_id);
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -371,6 +371,11 @@ enum class SDMCSize : u64 {
S1TB = 0x10000000000ULL,
};
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
};
struct Values {
// System
bool use_docked_mode;
@@ -419,6 +424,10 @@ struct Values {
SDMCSize sdmc_size;
// Renderer
RendererBackend renderer_backend;
bool renderer_debug;
int vulkan_device;
float resolution_factor;
bool use_frame_limit;
u16 frame_limit;

View File

@@ -46,6 +46,16 @@ static u64 GenerateTelemetryId() {
return telemetry_id;
}
static const char* TranslateRenderer(Settings::RendererBackend backend) {
switch (backend) {
case Settings::RendererBackend::OpenGL:
return "OpenGL";
case Settings::RendererBackend::Vulkan:
return "Vulkan";
}
return "Unknown";
}
u64 GetTelemetryId() {
u64 telemetry_id{};
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
@@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
AddField(field_type, "Renderer_Backend", "OpenGL");
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);

View File

@@ -156,6 +156,7 @@ if (ENABLE_VULKAN)
renderer_vulkan/maxwell_to_vk.cpp
renderer_vulkan/maxwell_to_vk.h
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/renderer_vulkan.cpp
renderer_vulkan/vk_blit_screen.cpp
renderer_vulkan/vk_blit_screen.h
renderer_vulkan/vk_buffer_cache.cpp

View File

@@ -101,7 +101,10 @@ public:
void TickFrame() {
++epoch;
while (!pending_destruction.empty()) {
if (pending_destruction.front()->GetEpoch() + 1 > epoch) {
// Delay at least 4 frames before destruction.
// This is due to triple buffering happening on some drivers.
static constexpr u64 epochs_to_destroy = 5;
if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) {
break;
}
pending_destruction.pop_front();

View File

@@ -704,8 +704,8 @@ public:
INSERT_UNION_PADDING_WORDS(0x15);
s32 stencil_back_func_ref;
u32 stencil_back_func_mask;
u32 stencil_back_mask;
u32 stencil_back_func_mask;
INSERT_UNION_PADDING_WORDS(0xC);
@@ -862,7 +862,11 @@ public:
float point_size;
INSERT_UNION_PADDING_WORDS(0x7);
INSERT_UNION_PADDING_WORDS(0x1);
u32 point_sprite_enable;
INSERT_UNION_PADDING_WORDS(0x5);
u32 zeta_enable;
@@ -1458,8 +1462,8 @@ ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
ASSERT_REG_POSITION(patch_vertices, 0x373);
ASSERT_REG_POSITION(scissor_test, 0x380);
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D6);
ASSERT_REG_POSITION(stencil_back_mask, 0x3D7);
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
ASSERT_REG_POSITION(color_mask_common, 0x3E4);
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
ASSERT_REG_POSITION(depth_bounds, 0x3E7);
@@ -1494,6 +1498,7 @@ ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(vb_base_instance, 0x50E);
ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(point_sprite_enable, 0x548);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(multisample_control, 0x54F);
ASSERT_REG_POSITION(condition, 0x554);

View File

@@ -624,6 +624,19 @@ enum class ShuffleOperation : u64 {
Bfly = 3, // shuffleXorNV
};
enum class ShfType : u64 {
Bits32 = 0,
U64 = 2,
S64 = 3,
};
enum class ShfXmode : u64 {
None = 0,
HI = 1,
X = 2,
XHI = 3,
};
union Instruction {
constexpr Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -775,6 +788,13 @@ union Instruction {
BitField<39, 1, u64> wrap;
} shr;
union {
BitField<37, 2, ShfType> type;
BitField<48, 2, ShfXmode> xmode;
BitField<50, 1, u64> wrap;
BitField<20, 6, u64> immediate;
} shf;
union {
BitField<39, 5, u64> shift_amount;
BitField<48, 1, u64> negate_b;
@@ -1123,6 +1143,11 @@ union Instruction {
BitField<55, 1, u64> ftz;
} fset;
union {
BitField<47, 1, u64> ftz;
BitField<48, 4, PredCondition> cond;
} fcmp;
union {
BitField<49, 1, u64> bf;
BitField<35, 3, PredCondition> cond;
@@ -1703,6 +1728,7 @@ public:
BFE_C,
BFE_R,
BFE_IMM,
BFI_RC,
BFI_IMM_R,
BRA,
BRX,
@@ -1800,6 +1826,7 @@ public:
ICMP_R,
ICMP_CR,
ICMP_IMM,
FCMP_R,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
@@ -2104,6 +2131,7 @@ private:
INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"),
INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"),
INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"),
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -2128,6 +2156,7 @@ private:
INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
INST("0101001111110---", Id::BFI_RC, Type::Bfi, "BFI_RC"),
INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),

View File

@@ -23,7 +23,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
: system{system}, renderer{renderer}, is_async{is_async} {
auto& rasterizer{renderer.Rasterizer()};
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
memory_manager = std::make_unique<Tegra::MemoryManager>(system);
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);

View File

@@ -86,7 +86,7 @@ struct CommandDataContainer {
struct SynchState final {
std::atomic_bool is_running{true};
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
CommandQueue queue;
u64 last_fence{};
std::atomic<u64> signaled_fence{};

View File

@@ -9,13 +9,12 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra {
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: rasterizer{rasterizer}, system{system} {
MemoryManager::MemoryManager(Core::System& system) : system{system} {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
Common::PageType::Unmapped);
@@ -84,7 +83,8 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr);
rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size);
UnmapRange(gpu_addr, aligned_size);
ASSERT(system.CurrentProcess()
->VMManager()
@@ -242,7 +242,7 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
switch (page_table.attributes[page_index]) {
case Common::PageType::Memory: {
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
@@ -292,7 +292,7 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
switch (page_table.attributes[page_index]) {
case Common::PageType::Memory: {
u8* dest_ptr{page_table.pointers[page_index] + page_offset};
rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
std::memcpy(dest_ptr, src_buffer, copy_amount);
break;
}
@@ -340,7 +340,7 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::
switch (page_table.attributes[page_index]) {
case Common::PageType::Memory: {
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
WriteBlock(dest_addr, src_ptr, copy_amount);
break;
}

View File

@@ -10,10 +10,6 @@
#include "common/common_types.h"
#include "common/page_table.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Core {
class System;
}
@@ -51,7 +47,7 @@ struct VirtualMemoryArea {
class MemoryManager final {
public:
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
explicit MemoryManager(Core::System& system);
~MemoryManager();
GPUVAddr AllocateSpace(u64 size, u64 align);
@@ -176,7 +172,6 @@ private:
Common::PageTable page_table{page_bits};
VMAMap vma_map;
VideoCore::RasterizerInterface& rasterizer;
Core::System& system;
};

View File

@@ -248,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
}
GLintptr RasterizerOpenGL::SetupIndexBuffer() {
if (accelerate_draw != AccelDraw::Indexed) {
return 0;
}
MICROPROFILE_SCOPE(OpenGL_Index);
const auto& regs = system.GPU().Maxwell3D().regs;
const std::size_t size = CalculateIndexBufferSize();
@@ -546,7 +543,8 @@ void RasterizerOpenGL::Clear() {
}
}
void RasterizerOpenGL::DrawPrelude() {
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
auto& gpu = system.GPU().Maxwell3D();
SyncRasterizeEnable(state);
@@ -567,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() {
buffer_cache.Acquire();
// Draw the vertex batch
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
std::size_t buffer_size = CalculateVertexArraysSize();
// Add space for index buffer
@@ -596,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() {
// Upload vertex and index data.
SetupVertexBuffer(vao);
SetupVertexInstances(vao);
index_buffer_offset = SetupIndexBuffer();
GLintptr index_buffer_offset;
if (is_indexed) {
index_buffer_offset = SetupIndexBuffer();
}
// Prepare packed bindings.
bind_ubo_pushbuffer.Setup();
@@ -630,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() {
// As all cached buffers are invalidated, we need to recheck their state.
gpu.dirty.ResetVertexArrays();
}
gpu.dirty.memory_general = false;
shader_program_manager->ApplyTo(state);
state.Apply();
@@ -637,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() {
if (texture_cache.TextureBarrier()) {
glTextureBarrier();
}
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
const GLsizei num_instances =
static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
if (is_indexed) {
const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
glDrawElementsInstancedBaseVertexBaseInstance(
primitive_mode, num_vertices, index_format,
reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex,
base_instance);
} else {
const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances,
base_instance);
}
}
struct DrawParams {
bool is_indexed{};
bool is_instanced{};
GLenum primitive_mode{};
GLint count{};
GLint base_vertex{};
// Indexed settings
GLenum index_format{};
GLintptr index_buffer_offset{};
// Instanced setting
GLint num_instances{};
GLint base_instance{};
void DispatchDraw() {
if (is_indexed) {
const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
if (is_instanced) {
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
index_buffer_ptr, num_instances,
base_vertex, base_instance);
} else {
glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
base_vertex);
}
} else {
if (is_instanced) {
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
base_instance);
} else {
glDrawArrays(primitive_mode, base_vertex, count);
}
}
}
};
bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
MICROPROFILE_SCOPE(OpenGL_Drawing);
DrawPrelude();
auto& maxwell3d = system.GPU().Maxwell3D();
const auto& regs = maxwell3d.regs;
const auto current_instance = maxwell3d.state.current_instance;
DrawParams draw_call{};
draw_call.is_indexed = is_indexed;
draw_call.num_instances = static_cast<GLint>(1);
draw_call.base_instance = static_cast<GLint>(current_instance);
draw_call.is_instanced = current_instance > 0;
draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
if (draw_call.is_indexed) {
draw_call.count = static_cast<GLint>(regs.index_array.count);
draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
draw_call.index_buffer_offset = index_buffer_offset;
} else {
draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
}
draw_call.DispatchDraw();
maxwell3d.dirty.memory_general = false;
accelerate_draw = AccelDraw::Disabled;
Draw(is_indexed, false);
return true;
}
bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
MICROPROFILE_SCOPE(OpenGL_Drawing);
DrawPrelude();
auto& maxwell3d = system.GPU().Maxwell3D();
const auto& regs = maxwell3d.regs;
const auto& draw_setup = maxwell3d.mme_draw;
DrawParams draw_call{};
draw_call.is_indexed = is_indexed;
draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
draw_call.is_instanced = draw_setup.instance_count > 1;
draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
if (draw_call.is_indexed) {
draw_call.count = static_cast<GLint>(regs.index_array.count);
draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
draw_call.index_buffer_offset = index_buffer_offset;
} else {
draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
}
draw_call.DispatchDraw();
maxwell3d.dirty.memory_general = false;
accelerate_draw = AccelDraw::Disabled;
Draw(is_indexed, true);
return true;
}
@@ -1293,6 +1220,7 @@ void RasterizerOpenGL::SyncPointState() {
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
// in OpenGL).
state.point.program_control = regs.vp_point_size.enable != 0;
state.point.sprite = regs.point_sprite_enable != 0;
state.point.size = std::max(1.0f, regs.point_size);
}

View File

@@ -103,7 +103,7 @@ private:
std::size_t size);
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
void DrawPrelude();
void Draw(bool is_indexed, bool is_instanced);
/// Configures the current textures to use for the draw command.
void SetupDrawTextures(std::size_t stage_index, const Shader& shader);
@@ -220,12 +220,7 @@ private:
GLintptr SetupIndexBuffer();
GLintptr index_buffer_offset;
void SetupShaders(GLenum primitive_mode);
enum class AccelDraw { Disabled, Arrays, Indexed };
AccelDraw accelerate_draw = AccelDraw::Disabled;
};
} // namespace OpenGL

View File

@@ -128,6 +128,7 @@ void OpenGLState::ApplyClipDistances() {
void OpenGLState::ApplyPointSize() {
Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
if (UpdateValue(cur_state.point.size, point.size)) {
glPointSize(point.size);
}

View File

@@ -132,6 +132,7 @@ public:
struct {
bool program_control = false; // GL_PROGRAM_POINT_SIZE
bool sprite = false; // GL_POINT_SPRITE
GLfloat size = 1.0f; // GL_POINT_SIZE
} point;

View File

@@ -47,8 +47,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return GL_UNSIGNED_INT_2_10_10_10_REV;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
UNREACHABLE();
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
return {};
}
case Maxwell::VertexAttribute::Type::SignedInt:
@@ -72,8 +71,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return GL_INT_2_10_10_10_REV;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
UNREACHABLE();
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
return {};
}
case Maxwell::VertexAttribute::Type::Float:
@@ -89,13 +87,19 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
return GL_FLOAT;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
UNREACHABLE();
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
return {};
}
case Maxwell::VertexAttribute::Type::UnsignedScaled:
switch (attrib.size) {
case Maxwell::VertexAttribute::Size::Size_8_8:
return GL_UNSIGNED_BYTE;
default:
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
return {};
}
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
UNREACHABLE();
LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
return {};
}
}

View File

@@ -0,0 +1,265 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <optional>
#include <vector>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/telemetry.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "video_core/gpu.h"
#include "video_core/renderer_vulkan/declarations.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
namespace Vulkan {
namespace {
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
const char* message{data->pMessage};
if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
LOG_CRITICAL(Render_Vulkan, "{}", message);
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
LOG_WARNING(Render_Vulkan, "{}", message);
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) {
LOG_INFO(Render_Vulkan, "{}", message);
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) {
LOG_DEBUG(Render_Vulkan, "{}", message);
}
return VK_FALSE;
}
std::string GetReadableVersion(u32 version) {
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
VK_VERSION_PATCH(version));
}
std::string GetDriverVersion(const VKDevice& device) {
// Extracted from
// https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
const u32 version = device.GetDriverVersion();
if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) {
const u32 major = (version >> 22) & 0x3ff;
const u32 minor = (version >> 14) & 0x0ff;
const u32 secondary = (version >> 6) & 0x0ff;
const u32 tertiary = version & 0x003f;
return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
}
if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) {
const u32 major = version >> 14;
const u32 minor = version & 0x3fff;
return fmt::format("{}.{}", major, minor);
}
return GetReadableVersion(version);
}
std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) {
std::sort(std::begin(available_extensions), std::end(available_extensions));
static constexpr std::size_t AverageExtensionSize = 64;
std::string separated_extensions;
separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
const auto end = std::end(available_extensions);
for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
if (const bool is_last = extension + 1 == end; is_last) {
separated_extensions += *extension;
} else {
separated_extensions += fmt::format("{},", *extension);
}
}
return separated_extensions;
}
} // Anonymous namespace
RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system)
: RendererBase(window), system{system} {}
RendererVulkan::~RendererVulkan() {
ShutDown();
}
void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
const auto& layout = render_window.GetFramebufferLayout();
if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
const bool use_accelerated =
rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
const bool is_srgb = use_accelerated && screen_info.is_srgb;
if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) {
swapchain->Create(layout.width, layout.height, is_srgb);
blit_screen->Recreate();
}
scheduler->WaitWorker();
swapchain->AcquireNextImage();
const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
scheduler->Flush(false, render_semaphore);
if (swapchain->Present(render_semaphore, fence)) {
blit_screen->Recreate();
}
render_window.SwapBuffers();
rasterizer->TickFrame();
}
render_window.PollEvents();
}
bool RendererVulkan::Init() {
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
std::optional<vk::DebugUtilsMessengerEXT> callback;
if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) {
callback = CreateDebugCallback(dldi);
if (!callback) {
return false;
}
}
if (!PickDevices(dldi)) {
if (callback) {
instance.destroy(*callback, nullptr, dldi);
}
return false;
}
debug_callback = UniqueDebugUtilsMessengerEXT(
*callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
instance, nullptr, device->GetDispatchLoader()));
Report();
memory_manager = std::make_unique<VKMemoryManager>(*device);
resource_manager = std::make_unique<VKResourceManager>(*device);
const auto& framebuffer = render_window.GetFramebufferLayout();
swapchain = std::make_unique<VKSwapchain>(surface, *device);
swapchain->Create(framebuffer.width, framebuffer.height, false);
scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
*resource_manager, *memory_manager, *scheduler);
blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
*resource_manager, *memory_manager, *swapchain,
*scheduler, screen_info);
return true;
}
void RendererVulkan::ShutDown() {
if (!device) {
return;
}
const auto dev = device->GetLogical();
const auto& dld = device->GetDispatchLoader();
if (dev && dld.vkDeviceWaitIdle) {
dev.waitIdle(dld);
}
rasterizer.reset();
blit_screen.reset();
scheduler.reset();
swapchain.reset();
memory_manager.reset();
resource_manager.reset();
device.reset();
}
std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
const vk::DispatchLoaderDynamic& dldi) {
const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
{},
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
&DebugCallback, nullptr);
vk::DebugUtilsMessengerEXT callback;
if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
vk::Result::eSuccess) {
LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
return {};
}
return callback;
}
bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) {
const auto devices = instance.enumeratePhysicalDevices(dldi);
// TODO(Rodrigo): Choose device from config file
const s32 device_index = Settings::values.vulkan_device;
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
return false;
}
const vk::PhysicalDevice physical_device = devices[device_index];
if (!VKDevice::IsSuitable(dldi, physical_device, surface)) {
return false;
}
device = std::make_unique<VKDevice>(dldi, physical_device, surface);
return device->Create(dldi, instance);
}
void RendererVulkan::Report() const {
const std::string vendor_name{device->GetVendorName()};
const std::string model_name{device->GetModelName()};
const std::string driver_version = GetDriverVersion(*device);
const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
const std::string api_version = GetReadableVersion(device->GetApiVersion());
const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions());
LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
auto& telemetry_session = system.TelemetrySession();
constexpr auto field = Telemetry::FieldType::UserSystem;
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
telemetry_session.AddField(field, "GPU_Model", model_name);
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version);
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
}
} // namespace Vulkan

View File

@@ -400,8 +400,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
false);
Test(extension, nv_device_diagnostic_checkpoints,
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
if (Settings::values.renderer_debug) {
Test(extension, nv_device_diagnostic_checkpoints,
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
}
}
if (khr_shader_float16_int8) {

View File

@@ -571,7 +571,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true);
}
if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) {
texceptions.set(rt);
texceptions[rt] = true;
}
}
@@ -579,7 +579,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
zeta_attachment = texture_cache.GetDepthBufferSurface(true);
}
if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) {
texceptions.set(ZETA_TEXCEPTION_INDEX);
texceptions[ZETA_TEXCEPTION_INDEX] = true;
}
texture_cache.GuardRenderTargets(false);
@@ -1122,11 +1122,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions)
for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) {
const auto& rendertarget = regs.rt[rt];
if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE)
if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) {
continue;
}
renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{
static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format),
texceptions.test(rt)});
texceptions[rt]});
}
renderpass_params.has_zeta = regs.zeta_enable;

View File

@@ -21,7 +21,7 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
Node op_a = GetRegister(instr.gpr8);
Node op_b = [&]() -> Node {
Node op_b = [&] {
if (instr.is_b_imm) {
return GetImmediate19(instr);
} else if (instr.is_b_gpr) {
@@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
SetRegister(bb, instr.gpr0, value);
break;
}
case OpCode::Id::FCMP_R: {
UNIMPLEMENTED_IF(instr.fcmp.ftz == 0);
Node op_c = GetRegister(instr.gpr39);
Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f));
SetRegister(
bb, instr.gpr0,
Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b)));
break;
}
case OpCode::Id::RRO_C:
case OpCode::Id::RRO_R:
case OpCode::Id::RRO_IMM: {

View File

@@ -17,10 +17,13 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr);
const auto [base, packed_shift] = [&]() -> std::tuple<Node, Node> {
const auto [packed_shift, base] = [&]() -> std::pair<Node, Node> {
switch (opcode->get().GetId()) {
case OpCode::Id::BFI_RC:
return {GetRegister(instr.gpr39),
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
case OpCode::Id::BFI_IMM_R:
return {GetRegister(instr.gpr39), Immediate(instr.alu.GetSignedImm20_20())};
return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
default:
UNREACHABLE();
return {Immediate(0), Immediate(0)};

View File

@@ -69,13 +69,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
case OpCode::Id::MOV_SYS: {
const Node value = [this, instr] {
switch (instr.sys20) {
case SystemVariable::LaneId:
LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete");
return Immediate(0U);
case SystemVariable::InvocationId:
return Operation(OperationCode::InvocationId);
case SystemVariable::Ydirection:
return Operation(OperationCode::YNegate);
case SystemVariable::InvocationInfo:
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
return Immediate(0u);
return Immediate(0U);
case SystemVariable::Tid: {
Node value = Immediate(0);
value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9);
@@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
static_cast<u32>(cc));
if (disable_flow_stack) {
if (decompiled) {
break;
}
@@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
static_cast<u32>(cc));
if (disable_flow_stack) {
if (decompiled) {
break;
}

View File

@@ -10,8 +10,80 @@
namespace VideoCommon::Shader {
using std::move;
using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
using Tegra::Shader::ShfType;
using Tegra::Shader::ShfXmode;
namespace {
Node IsFull(Node shift) {
return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32));
}
Node Shift(OperationCode opcode, Node value, Node shift) {
Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32));
Node shifted = Operation(opcode, move(value), shift);
return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted));
}
Node ClampShift(Node shift, s32 size = 32) {
shift = Operation(OperationCode::IMax, move(shift), Immediate(0));
return Operation(OperationCode::IMin, move(shift), Immediate(size));
}
Node WrapShift(Node shift, s32 size = 32) {
return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1));
}
Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) {
// These values are used when the shift value is less than 32
Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift);
Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift);
Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low));
if (type == ShfType::Bits32) {
// On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less));
}
// And these when it's larger than or 32
const bool is_signed = type == ShfType::S64;
const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed);
Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
Node greater = Shift(opcode, high, move(reduced));
Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
}
Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) {
// These values are used when the shift value is less than 32
Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift);
Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift);
Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high));
if (type == ShfType::Bits32) {
// On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less));
}
// And these when it's larger than or 32
Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced));
Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
}
} // Anonymous namespace
u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
@@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
}
}();
switch (opcode->get().GetId()) {
switch (const auto opid = opcode->get().GetId(); opid) {
case OpCode::Id::SHR_C:
case OpCode::Id::SHR_R:
case OpCode::Id::SHR_IMM: {
if (instr.shr.wrap) {
op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f));
} else {
op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0));
op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31));
}
op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b));
Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed,
std::move(op_a), std::move(op_b));
move(op_a), move(op_b));
SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
SetRegister(bb, instr.gpr0, std::move(value));
SetRegister(bb, instr.gpr0, move(value));
break;
}
case OpCode::Id::SHL_C:
case OpCode::Id::SHL_R:
case OpCode::Id::SHL_IMM: {
const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);
Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);
SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
SetRegister(bb, instr.gpr0, value);
SetRegister(bb, instr.gpr0, move(value));
break;
}
case OpCode::Id::SHF_RIGHT_R:
case OpCode::Id::SHF_RIGHT_IMM:
case OpCode::Id::SHF_LEFT_R:
case OpCode::Id::SHF_LEFT_IMM: {
UNIMPLEMENTED_IF(instr.generates_cc);
UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}",
static_cast<int>(instr.shf.xmode.Value()));
if (instr.is_b_imm) {
op_b = Immediate(static_cast<u32>(instr.shf.immediate));
}
const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64;
Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size);
Node negated_shift = Operation(OperationCode::INegate, shift);
Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32));
const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM;
Node value = (is_right ? ShiftRight : ShiftLeft)(
move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type);
SetRegister(bb, instr.gpr0, move(value));
break;
}
default:

View File

@@ -3,19 +3,32 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
#include "video_core/gpu_asynch.h"
#include "video_core/gpu_synch.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#ifdef HAS_VULKAN
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#endif
#include "video_core/video_core.h"
namespace VideoCore {
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system) {
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
#ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan:
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
#endif
default:
return nullptr;
}
}
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {

View File

@@ -117,6 +117,7 @@ bool TelemetryJson::SubmitTestcase() {
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
auto content = impl->TopSection().dump();
Client client(impl->host, impl->username, impl->token);

View File

@@ -200,3 +200,8 @@ if (MSVC)
copy_yuzu_SDL_deps(yuzu)
copy_yuzu_unicorn_deps(yuzu)
endif()
if (ENABLE_VULKAN)
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
endif()

View File

@@ -2,19 +2,30 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <glad/glad.h>
#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMessageBox>
#include <QOffscreenSurface>
#include <QOpenGLWindow>
#include <QPainter>
#include <QScreen>
#include <QStringList>
#include <QWindow>
#ifdef HAS_VULKAN
#include <QVulkanWindow>
#endif
#include <fmt/format.h>
#include "common/assert.h"
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/frontend/scope_acquire_window_context.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
@@ -114,19 +125,10 @@ private:
QOpenGLContext context;
};
// 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 QOpenGLWindow {
class GWidgetInternal : public QWindow {
public:
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
: QOpenGLWindow(shared_context), parent(parent) {}
void paintEvent(QPaintEvent* ev) override {
if (do_painting) {
QPainter painter(this);
}
}
GWidgetInternal(GRenderWindow* parent) : parent(parent) {}
virtual ~GWidgetInternal() = default;
void resizeEvent(QResizeEvent* ev) override {
parent->OnClientAreaResized(ev->size().width(), ev->size().height());
@@ -182,11 +184,47 @@ public:
do_painting = true;
}
std::pair<unsigned, unsigned> GetSize() const {
return std::make_pair(width(), height());
}
protected:
bool IsPaintingEnabled() const {
return do_painting;
}
private:
GRenderWindow* parent;
bool do_painting;
bool do_painting = false;
};
// 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 final : public GWidgetInternal, public QOpenGLWindow {
public:
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
: GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
~GGLWidgetInternal() override = default;
void paintEvent(QPaintEvent* ev) override {
if (IsPaintingEnabled()) {
QPainter painter(this);
}
}
};
#ifdef HAS_VULKAN
class GVKWidgetInternal final : public GWidgetInternal {
public:
GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) {
setSurfaceType(QSurface::SurfaceType::VulkanSurface);
setVulkanInstance(instance);
}
~GVKWidgetInternal() override = default;
};
#endif
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
: QWidget(parent), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
@@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown();
// Avoid an unordered destruction that generates a segfault
delete child;
}
void GRenderWindow::moveContext() {
if (!context) {
return;
}
DoneCurrent();
// If the thread started running, move the GL Context to the new thread. Otherwise, move it
@@ -215,8 +259,9 @@ void GRenderWindow::moveContext() {
}
void GRenderWindow::SwapBuffers() {
context->swapBuffers(child);
if (context) {
context->swapBuffers(child);
}
if (!first_frame) {
first_frame = true;
emit FirstFrameDisplayed();
@@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() {
}
void GRenderWindow::MakeCurrent() {
context->makeCurrent(child);
if (context) {
context->makeCurrent(child);
}
}
void GRenderWindow::DoneCurrent() {
context->doneCurrent();
if (context) {
context->doneCurrent();
}
}
void GRenderWindow::PollEvents() {}
bool GRenderWindow::IsShown() const {
return !isMinimized();
}
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
#ifdef HAS_VULKAN
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
const VkInstance instance_copy = vk_instance->vkInstance();
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child);
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
#else
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
#endif
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
//
// Older versions get the window size (density independent pixels),
@@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {}
void GRenderWindow::OnFramebufferSizeChanged() {
// Screen changes potentially incur a change in screen DPI, hence we should update the
// framebuffer size
const qreal pixel_ratio = GetWindowPixelRatio();
const u32 width = child->QPaintDevice::width() * pixel_ratio;
const u32 height = child->QPaintDevice::height() * pixel_ratio;
UpdateCurrentFramebufferLayout(width, height);
const qreal pixelRatio{GetWindowPixelRatio()};
const auto size{child->GetSize()};
UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio);
}
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
@@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
}
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
const qreal pixel_ratio = GetWindowPixelRatio();
const qreal pixel_ratio{GetWindowPixelRatio()};
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
}
@@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont
return std::make_unique<GGLContext>(context.get());
}
void GRenderWindow::InitRenderTarget() {
bool GRenderWindow::InitRenderTarget() {
shared_context.reset();
context.reset();
delete child;
child = nullptr;
delete container;
container = nullptr;
delete layout();
if (child) {
delete child;
}
if (container) {
delete container;
}
if (layout()) {
delete layout();
}
first_frame = false;
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QSurfaceFormat fmt;
fmt.setVersion(4, 3);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
// 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(0);
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
if (!InitializeOpenGL()) {
return false;
}
break;
case Settings::RendererBackend::Vulkan:
if (!InitializeVulkan()) {
return false;
}
break;
}
child = new GGLWidgetInternal(this, shared_context.get());
container = QWidget::createWindowContainer(child, this);
QBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(container);
layout->setMargin(0);
setLayout(layout);
// Reset minimum size to avoid unwanted resizes when this function is called for a second time.
// Reset minimum required size to avoid resizing issues on the main window after restarting.
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 causes the window to actually be created and the gl context as well, but we don't want
// the widget to be shown yet, so immediately hide it.
show();
hide();
@@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() {
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
OnFramebufferSizeChanged();
NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height()));
NotifyClientAreaSizeChanged(child->GetSize());
BackupGeometry();
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
if (!LoadOpenGL()) {
return false;
}
}
return true;
}
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
setMinimumSize(minimal_size.first, minimal_size.second);
}
bool GRenderWindow::InitializeOpenGL() {
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QSurfaceFormat fmt;
fmt.setVersion(4, 3);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
// 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);
child = new GGLWidgetInternal(this, shared_context.get());
return true;
}
bool GRenderWindow::InitializeVulkan() {
#ifdef HAS_VULKAN
vk_instance = std::make_unique<QVulkanInstance>();
vk_instance->setApiVersion(QVersionNumber(1, 1, 0));
vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
if (Settings::values.renderer_debug) {
const auto supported_layers{vk_instance->supportedLayers()};
const bool found =
std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) {
constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation";
return layer.name == searched_layer;
});
if (found) {
vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
}
if (!vk_instance->create()) {
QMessageBox::critical(
this, tr("Error while initializing Vulkan 1.1!"),
tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the "
"latest graphics drivers."));
return false;
}
child = new GVKWidgetInternal(this, vk_instance.get());
return true;
#else
QMessageBox::critical(this, tr("Vulkan not available!"),
tr("yuzu has not been compiled with Vulkan support."));
return false;
#endif
}
bool GRenderWindow::LoadOpenGL() {
Core::Frontend::ScopeAcquireWindowContext acquire_context{*this};
if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
"latest graphics driver."));
return false;
}
QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
if (!unsupported_gl_extensions.empty()) {
QMessageBox::critical(
this, tr("Error while initializing OpenGL!"),
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
"have the latest graphics driver.<br><br>Unsupported extensions:<br>") +
unsupported_gl_extensions.join(QStringLiteral("<br>")));
return false;
}
return true;
}
QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
QStringList unsupported_ext;
if (!GLAD_GL_ARB_buffer_storage)
unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
if (!GLAD_GL_ARB_direct_state_access)
unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
if (!GLAD_GL_ARB_multi_bind)
unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
if (!GLAD_GL_ARB_clip_control)
unsupported_ext.append(QStringLiteral("ARB_clip_control"));
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
if (!GLAD_GL_ARB_texture_compression_rgtc)
unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
for (const QString& ext : unsupported_ext)
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
return unsupported_ext;
}
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
this->emu_thread = emu_thread;
child->DisablePainting();

View File

@@ -7,17 +7,28 @@
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <QImage>
#include <QThread>
#include <QWidget>
#include "common/thread.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
class QKeyEvent;
class QScreen;
class QTouchEvent;
class QStringList;
class QSurface;
class QOpenGLContext;
#ifdef HAS_VULKAN
class QVulkanInstance;
#endif
class GWidgetInternal;
class GGLWidgetInternal;
class GVKWidgetInternal;
class GMainWindow;
class GRenderWindow;
class QSurface;
@@ -123,6 +134,9 @@ public:
void MakeCurrent() override;
void DoneCurrent() override;
void PollEvents() override;
bool IsShown() const override;
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
void ForwardKeyPressEvent(QKeyEvent* event);
@@ -142,7 +156,7 @@ public:
void OnClientAreaResized(u32 width, u32 height);
void InitRenderTarget();
bool InitRenderTarget();
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
@@ -165,10 +179,13 @@ private:
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
QWidget* container = nullptr;
GGLWidgetInternal* child = nullptr;
bool InitializeOpenGL();
bool InitializeVulkan();
bool LoadOpenGL();
QStringList GetUnsupportedGLExtensions() const;
QByteArray geometry;
QWidget* container = nullptr;
GWidgetInternal* child = nullptr;
EmuThread* emu_thread;
// Context that backs the GGLWidgetInternal (and will be used by core to render)
@@ -177,9 +194,14 @@ private:
// current
std::unique_ptr<QOpenGLContext> shared_context;
#ifdef HAS_VULKAN
std::unique_ptr<QVulkanInstance> vk_instance;
#endif
/// Temporary storage of the screenshot taken
QImage screenshot_image;
QByteArray geometry;
bool first_frame = false;
protected:

View File

@@ -624,6 +624,10 @@ void Config::ReadPathValues() {
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
Settings::values.renderer_backend =
static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
Settings::values.resolution_factor =
ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
Settings::values.use_frame_limit =
@@ -738,7 +742,6 @@ void Config::ReadUIValues() {
void Config::ReadUIGamelistValues() {
qt_config->beginGroup(QStringLiteral("UIGameList"));
UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool();
UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool();
UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt();
UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt();
@@ -1056,6 +1059,9 @@ void Config::SavePathValues() {
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
WriteSetting(QStringLiteral("resolution_factor"),
static_cast<double>(Settings::values.resolution_factor), 1.0);
WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
@@ -1152,7 +1158,6 @@ void Config::SaveUIValues() {
void Config::SaveUIGamelistValues() {
qt_config->beginGroup(QStringLiteral("UIGameList"));
WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true);
WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true);
WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64);
WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);

View File

@@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() {
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
ui->reporting_services->setChecked(Settings::values.reporting_services);
ui->quest_flag->setChecked(Settings::values.quest_flag);
ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
}
void ConfigureDebug::ApplyConfiguration() {
@@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Debugger::ToggleConsole();
Log::Filter filter;
filter.ParseFilterString(Settings::values.log_filter);

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>474</height>
<height>467</height>
</rect>
</property>
<property name="windowTitle">
@@ -103,44 +103,6 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="reporting_services">
<property name="text">
<string>Enable Verbose Reporting Services</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="quest_flag">
<property name="text">
<string>Kiosk (Quest) Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -167,6 +129,95 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Graphics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="whatsThis">
<string>When checked, the graphics API enters in a slower debugging mode</string>
</property>
<property name="text">
<string>Enable Graphics Debugging</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Dump</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="dump_decompressed_nso">
<property name="whatsThis">
<string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
</property>
<property name="text">
<string>Dump Decompressed NSOs</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="dump_exefs">
<property name="whatsThis">
<string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
</property>
<property name="text">
<string>Dump ExeFS</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="reporting_services">
<property name="text">
<string>Enable Verbose Reporting Services</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QCheckBox" name="quest_flag">
<property name="text">
<string>Kiosk (Quest) Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -185,6 +236,19 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>toggle_gdbstub</tabstop>
<tabstop>gdbport_spinbox</tabstop>
<tabstop>log_filter_edit</tabstop>
<tabstop>toggle_console</tabstop>
<tabstop>open_log_button</tabstop>
<tabstop>homebrew_args_edit</tabstop>
<tabstop>enable_graphics_debugging</tabstop>
<tabstop>dump_decompressed_nso</tabstop>
<tabstop>dump_exefs</tabstop>
<tabstop>reporting_services</tabstop>
<tabstop>quest_flag</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@@ -3,6 +3,13 @@
// Refer to the license.txt file included.
#include <QColorDialog>
#include <QComboBox>
#ifdef HAS_VULKAN
#include <QVulkanInstance>
#endif
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
@@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) {
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
vulkan_device = Settings::values.vulkan_device;
RetrieveVulkanDevices();
ui->setupUi(this);
SetConfiguration();
connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this] { UpdateDeviceComboBox(); });
connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
[this](int device) { UpdateDeviceSelection(device); });
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
const QColor new_bg_color = QColorDialog::getColor(bg_color);
if (!new_bg_color.isValid()) {
@@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
});
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
if (device == -1) {
return;
}
if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
vulkan_device = device;
}
}
ConfigureGraphics::~ConfigureGraphics() = default;
void ConfigureGraphics::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->api->setEnabled(runtime_lock);
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
ui->resolution_factor_combobox->setCurrentIndex(
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
ui->use_disk_shader_cache->setEnabled(runtime_lock);
@@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() {
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));
UpdateDeviceComboBox();
}
void ConfigureGraphics::ApplyConfiguration() {
Settings::values.renderer_backend = GetCurrentGraphicsBackend();
Settings::values.vulkan_device = vulkan_device;
Settings::values.resolution_factor =
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
@@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
}
void ConfigureGraphics::UpdateDeviceComboBox() {
ui->device->clear();
bool enabled = false;
switch (GetCurrentGraphicsBackend()) {
case Settings::RendererBackend::OpenGL:
ui->device->addItem(tr("OpenGL Graphics Device"));
enabled = false;
break;
case Settings::RendererBackend::Vulkan:
for (const auto device : vulkan_devices) {
ui->device->addItem(device);
}
ui->device->setCurrentIndex(vulkan_device);
enabled = !vulkan_devices.empty();
break;
}
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
}
void ConfigureGraphics::RetrieveVulkanDevices() {
#ifdef HAS_VULKAN
QVulkanInstance instance;
instance.setApiVersion(QVersionNumber(1, 1, 0));
if (!instance.create()) {
LOG_INFO(Frontend, "Vulkan 1.1 not available");
return;
}
const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))};
if (vkEnumeratePhysicalDevices == nullptr) {
LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices");
return;
}
u32 physical_device_count;
if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) !=
VK_SUCCESS) {
LOG_INFO(Frontend, "Failed to get physical devices count");
return;
}
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count,
physical_devices.data()) != VK_SUCCESS) {
LOG_INFO(Frontend, "Failed to get physical devices");
return;
}
const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))};
if (vkGetPhysicalDeviceProperties == nullptr) {
LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties");
return;
}
for (const auto physical_device : physical_devices) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physical_device, &properties);
vulkan_devices.push_back(QString::fromUtf8(properties.deviceName));
}
#endif
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
}

View File

@@ -5,7 +5,10 @@
#pragma once
#include <memory>
#include <vector>
#include <QString>
#include <QWidget>
#include "core/settings.h"
namespace Ui {
class ConfigureGraphics;
@@ -27,7 +30,16 @@ private:
void SetConfiguration();
void UpdateBackgroundColorButton(QColor color);
void UpdateDeviceComboBox();
void UpdateDeviceSelection(int device);
void RetrieveVulkanDevices();
Settings::RendererBackend GetCurrentGraphicsBackend() const;
std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color;
std::vector<QString> vulkan_devices;
u32 vulkan_device{};
};

View File

@@ -7,21 +7,69 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>API Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>API:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="api">
<item>
<property name="text">
<string notr="true">OpenGL</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Vulkan</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="device"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Graphics</string>
<string>Graphics Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="use_disk_shader_cache">
<property name="text">
@@ -29,13 +77,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
<property name="text">
<string>Use accurate GPU emulation (slow)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
<property name="text">
@@ -43,6 +84,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
<property name="text">
<string>Use accurate GPU emulation (slow)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="force_30fps_mode">
<property name="text">
@@ -51,11 +99,11 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Internal Resolution</string>
<string>Internal Resolution:</string>
</property>
</widget>
</item>
@@ -91,7 +139,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="bg_label">
<property name="text">

View File

@@ -40,7 +40,6 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
SetConfiguration();
// Force game list reload if any of the relevant settings are changed.
connect(ui->show_unknown, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureUi::RequestGameListUpdate);
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@@ -60,7 +59,6 @@ ConfigureUi::~ConfigureUi() = default;
void ConfigureUi::ApplyConfiguration() {
UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
UISettings::values.show_unknown = ui->show_unknown->isChecked();
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
@@ -74,7 +72,6 @@ void ConfigureUi::RequestGameListUpdate() {
void ConfigureUi::SetConfiguration() {
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->show_unknown->setChecked(UISettings::values.show_unknown);
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size));

View File

@@ -51,13 +51,6 @@
<layout class="QHBoxLayout" name="GameListHorizontalLayout">
<item>
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
<item>
<widget class="QCheckBox" name="show_unknown">
<property name="text">
<string>Show files with type 'Unknown'</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="show_add_ons">
<property name="text">

View File

@@ -298,8 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
}
const auto file_type = loader->GetFileType();
if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) &&
!UISettings::values.show_unknown) {
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return true;
}

View File

@@ -454,7 +454,6 @@ void GMainWindow::InitializeWidgets() {
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
message_label->setVisible(false);
message_label->setFrameStyle(QFrame::NoFrame);
message_label->setContentsMargins(4, 0, 4, 0);
message_label->setAlignment(Qt::AlignLeft);
@@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() {
label->setVisible(false);
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
statusBar()->addPermanentWidget(label, 0);
statusBar()->addPermanentWidget(label);
}
// Setup Dock button
dock_status_button = new QPushButton();
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
dock_status_button->setFocusPolicy(Qt::NoFocus);
connect(dock_status_button, &QPushButton::clicked, [&] {
Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
dock_status_button->setChecked(Settings::values.use_docked_mode);
OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode);
});
dock_status_button->setText(tr("DOCK"));
dock_status_button->setCheckable(true);
dock_status_button->setChecked(Settings::values.use_docked_mode);
statusBar()->insertPermanentWidget(0, dock_status_button);
// Setup ASync button
async_status_button = new QPushButton();
async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
async_status_button->setFocusPolicy(Qt::NoFocus);
connect(async_status_button, &QPushButton::clicked, [&] {
if (emulation_running) {
return;
}
Settings::values.use_asynchronous_gpu_emulation =
!Settings::values.use_asynchronous_gpu_emulation;
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
Settings::Apply();
});
async_status_button->setText(tr("ASYNC"));
async_status_button->setCheckable(true);
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
statusBar()->insertPermanentWidget(0, async_status_button);
// Setup Renderer API button
renderer_status_button = new QPushButton();
renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
renderer_status_button->setCheckable(true);
renderer_status_button->setFocusPolicy(Qt::NoFocus);
connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) {
renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL"));
});
renderer_status_button->toggle();
#ifndef HAS_VULKAN
renderer_status_button->setChecked(false);
renderer_status_button->setCheckable(false);
renderer_status_button->setDisabled(true);
#else
renderer_status_button->setChecked(Settings::values.renderer_backend ==
Settings::RendererBackend::Vulkan);
connect(renderer_status_button, &QPushButton::clicked, [=] {
if (emulation_running) {
return;
}
if (renderer_status_button->isChecked()) {
Settings::values.renderer_backend = Settings::RendererBackend::Vulkan;
} else {
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
}
Settings::Apply();
});
#endif // HAS_VULKAN
statusBar()->insertPermanentWidget(0, renderer_status_button);
statusBar()->setVisible(true);
setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}"));
}
@@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() {
Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
OnDockedModeChanged(!Settings::values.use_docked_mode,
Settings::values.use_docked_mode);
dock_status_button->setChecked(Settings::values.use_docked_mode);
});
}
@@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() {
#endif
}
QStringList GMainWindow::GetUnsupportedGLExtensions() {
QStringList unsupported_ext;
if (!GLAD_GL_ARB_buffer_storage) {
unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
}
if (!GLAD_GL_ARB_direct_state_access) {
unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
}
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) {
unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
}
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
}
if (!GLAD_GL_ARB_multi_bind) {
unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
}
if (!GLAD_GL_ARB_clip_control) {
unsupported_ext.append(QStringLiteral("ARB_clip_control"));
}
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc) {
unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
}
if (!GLAD_GL_ARB_texture_compression_rgtc) {
unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
}
if (!GLAD_GL_ARB_depth_buffer_float) {
unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
}
for (const QString& ext : unsupported_ext) {
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
}
return unsupported_ext;
}
bool GMainWindow::LoadROM(const QString& filename) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
render_window->InitRenderTarget();
{
Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window};
if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
tr("Your GPU may not support OpenGL 4.3, or you do not "
"have the latest graphics driver."));
return false;
}
}
const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
if (!unsupported_gl_extensions.empty()) {
QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"),
tr("Your GPU may not support one or more required OpenGL"
"extensions. Please ensure you have the latest graphics "
"driver.<br><br>Unsupported extensions:<br>") +
unsupported_gl_extensions.join(QStringLiteral("<br>")));
if (!render_window->InitRenderTarget()) {
return false;
}
@@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) {
// Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>(render_window);
emit EmulationStarting(emu_thread.get());
render_window->moveContext();
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
render_window->moveContext();
}
emu_thread->start();
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
@@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) {
game_list_placeholder->hide();
}
status_bar_update_timer.start(2000);
async_status_button->setDisabled(true);
renderer_status_button->setDisabled(true);
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
@@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() {
// Disable status bar updates
status_bar_update_timer.stop();
message_label->setVisible(false);
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
async_status_button->setEnabled(true);
#ifdef HAS_VULKAN
renderer_status_button->setEnabled(true);
#endif
emulation_running = false;
@@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() {
}
config->Save();
dock_status_button->setChecked(Settings::values.use_docked_mode);
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
#ifdef HAS_VULKAN
renderer_status_button->setChecked(Settings::values.renderer_backend ==
Settings::RendererBackend::Vulkan);
#endif
}
void GMainWindow::OnLoadAmiibo() {
@@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
if (emu_thread) {
emu_thread->SetRunning(true);
message_label->setText(status_message);
message_label->setVisible(true);
}
}
}
@@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event);
}
void GMainWindow::keyPressEvent(QKeyEvent* event) {
if (render_window) {
render_window->ForwardKeyPressEvent(event);
}
}
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
if (render_window) {
render_window->ForwardKeyReleaseEvent(event);
}
}
static bool IsSingleFileDropEvent(QDropEvent* event) {
const QMimeData* mimeData = event->mimeData();
return mimeData->hasUrls() && mimeData->urls().length() == 1;
@@ -2227,18 +2259,6 @@ 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;
@@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() {
QStringList theme_paths(default_theme_paths);
if (is_default_theme || current_theme.isEmpty()) {
qApp->setStyleSheet({});
setStyleSheet({});
const QString theme_uri(QStringLiteral(":default/style.qss"));
QFile f(theme_uri);
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
setStyleSheet(ts.readAll());
} else {
qApp->setStyleSheet({});
setStyleSheet({});
}
theme_paths.append(default_icons);
QIcon::setThemeName(default_icons);
} else {

View File

@@ -27,6 +27,7 @@ class LoadingScreen;
class MicroProfileDialog;
class ProfilerWidget;
class QLabel;
class QPushButton;
class WaitTreeWidget;
enum class GameListOpenTarget;
class GameListPlaceholder;
@@ -130,7 +131,6 @@ private:
void PreventOSSleep();
void AllowOSSleep();
QStringList GetUnsupportedGLExtensions();
bool LoadROM(const QString& filename);
void BootGame(const QString& filename);
void ShutdownGame();
@@ -229,6 +229,9 @@ private:
QLabel* emu_speed_label = nullptr;
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;
QPushButton* async_status_button = nullptr;
QPushButton* renderer_status_button = nullptr;
QPushButton* dock_status_button = nullptr;
QTimer status_bar_update_timer;
std::unique_ptr<Config> config;

View File

@@ -89,7 +89,6 @@ struct Values {
int profile_index;
// Game List
bool show_unknown;
bool show_add_ons;
uint32_t icon_size;
uint8_t row_1_text_id;

View File

@@ -8,11 +8,22 @@ add_executable(yuzu-cmd
emu_window/emu_window_sdl2_gl.h
emu_window/emu_window_sdl2.cpp
emu_window/emu_window_sdl2.h
emu_window/emu_window_sdl2_gl.cpp
emu_window/emu_window_sdl2_gl.h
resource.h
yuzu.cpp
yuzu.rc
)
if (ENABLE_VULKAN)
target_sources(yuzu-cmd PRIVATE
emu_window/emu_window_sdl2_vk.cpp
emu_window/emu_window_sdl2_vk.h)
target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN)
endif()
create_target_directory_groups(yuzu-cmd)
target_link_libraries(yuzu-cmd PRIVATE common core input_common)

View File

@@ -371,6 +371,12 @@ void Config::ReadValues() {
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
// Renderer
const int renderer_backend = sdl2_config->GetInteger(
"Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend);
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0);
Settings::values.resolution_factor =
static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);

View File

@@ -98,6 +98,17 @@ udp_pad_index=
use_multi_core=
[Renderer]
# Which backend API to use.
# 0 (default): OpenGL, 1: Vulkan
backend =
# Enable graphics API debugging mode.
# 0 (default): Disabled, 1: Enabled
debug =
# Which Vulkan physical device to use (defaults to 0)
vulkan_device =
# Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware
use_hw_renderer =

View File

@@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const {
return is_open;
}
bool EmuWindow_SDL2::IsShown() const {
return is_shown;
}
void EmuWindow_SDL2::OnResize() {
int width, height;
SDL_GetWindowSize(render_window, &width, &height);

View File

@@ -21,6 +21,9 @@ public:
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;
/// Returns if window is shown (not minimized)
bool IsShown() const override;
protected:
/// Called by PollEvents when a key is pressed or released.
void OnKeyEvent(int key, u8 state);

View File

@@ -9,6 +9,7 @@
#include <SDL.h>
#include <fmt/format.h>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
@@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
// Should not have been called from OpenGL
UNREACHABLE();
}
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}

View File

@@ -22,6 +22,10 @@ public:
/// Releases the GL context from the caller thread
void DoneCurrent() override;
/// Ignored in OpenGL
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
private:

View File

@@ -0,0 +1,162 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <string>
#include <vector>
#include <SDL.h>
#include <SDL_vulkan.h>
#include <fmt/format.h>
#include <vulkan/vulkan.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) {
if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
exit(EXIT_FAILURE);
}
vkGetInstanceProcAddr =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
if (vkGetInstanceProcAddr == nullptr) {
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
exit(EXIT_FAILURE);
}
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN);
const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr);
u32 extra_ext_count{};
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
SDL_GetError());
exit(1);
}
auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
exit(1);
}
std::vector<const char*> enabled_extensions;
enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
extra_ext_names.get() + extra_ext_count);
std::vector<const char*> enabled_layers;
if (use_standard_layers) {
enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
}
VkApplicationInfo app_info{};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_API_VERSION_1_1;
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
app_info.pApplicationName = "yuzu-emu";
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
app_info.pEngineName = "yuzu-emu";
VkInstanceCreateInfo instance_ci{};
instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_ci.pApplicationInfo = &app_info;
instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size());
instance_ci.ppEnabledExtensionNames = enabled_extensions.data();
if (Settings::values.renderer_debug) {
instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size());
instance_ci.ppEnabledLayerNames = enabled_layers.data();
}
const auto vkCreateInstance =
reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));
if (vkCreateInstance == nullptr ||
vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) {
LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!");
exit(EXIT_FAILURE);
}
vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"));
if (vkDestroyInstance == nullptr) {
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
exit(EXIT_FAILURE);
}
if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) {
LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError());
exit(EXIT_FAILURE);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
vkDestroyInstance(vk_instance, nullptr);
}
void EmuWindow_SDL2_VK::SwapBuffers() {}
void EmuWindow_SDL2_VK::MakeCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::DoneCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
const auto instance_proc_addr = vkGetInstanceProcAddr;
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &vk_instance, sizeof(vk_instance));
std::memcpy(surface, &vk_surface, sizeof(vk_surface));
}
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
return nullptr;
}
bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const {
if (!Settings::values.renderer_debug) {
return false;
}
const auto vkEnumerateInstanceLayerProperties =
reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties"));
if (vkEnumerateInstanceLayerProperties == nullptr) {
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
return false;
}
u32 available_layers_count{};
if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) {
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
return false;
}
std::vector<VkLayerProperties> layers(available_layers_count);
if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) {
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
return false;
}
return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
}) != layers.end();
}

View File

@@ -0,0 +1,39 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vulkan/vulkan.h>
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_VK(bool fullscreen);
~EmuWindow_SDL2_VK();
/// Swap buffers to display the next frame
void SwapBuffers() override;
/// Makes the graphics context current for the caller thread
void MakeCurrent() override;
/// Releases the GL context from the caller thread
void DoneCurrent() override;
/// Retrieves Vulkan specific handlers from the window
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
private:
bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
VkInstance vk_instance{};
VkSurfaceKHR vk_surface{};
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
PFN_vkDestroyInstance vkDestroyInstance{};
};

View File

@@ -32,6 +32,9 @@
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
#ifdef HAS_VULKAN
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
#endif
#include "core/file_sys/registered_cache.h"
@@ -174,7 +177,20 @@ int main(int argc, char** argv) {
Settings::values.use_gdbstub = use_gdbstub;
Settings::Apply();
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)};
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen);
break;
case Settings::RendererBackend::Vulkan:
#ifdef HAS_VULKAN
emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen);
break;
#else
LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
return 1;
#endif
}
if (!Settings::values.use_multi_core) {
// Single core mode must acquire OpenGL context for entire emulation session

View File

@@ -5,10 +5,15 @@
#include <algorithm>
#include <cstdlib>
#include <string>
#include <fmt/format.h>
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include <fmt/format.h>
#include <glad/glad.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
@@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() {
void EmuWindow_SDL2_Hide::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
bool EmuWindow_SDL2_Hide::IsShown() const {
return false;
}
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
UNREACHABLE();
}

View File

@@ -25,6 +25,13 @@ public:
/// Releases the GL context from the caller thread
void DoneCurrent() override;
/// Whether the screen is being shown or not.
bool IsShown() const override;
/// Retrieves Vulkan specific handlers from the window
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;