early-access version 3938
This commit is contained in:
parent
4d49e282ae
commit
ec28817fce
36 changed files with 376 additions and 144 deletions
|
@ -357,6 +357,9 @@ function(set_yuzu_qt_components)
|
|||
if (ENABLE_QT_TRANSLATION)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||
endif()
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||
endif()
|
||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||
endfunction(set_yuzu_qt_components)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
yuzu emulator early access
|
||||
=============
|
||||
|
||||
This is the source code for early-access 3932.
|
||||
This is the source code for early-access 3938.
|
||||
|
||||
## Legal Notice
|
||||
|
||||
|
|
|
@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
|||
// paused and we'll desync, so just play silence.
|
||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||
if (system.IsShuttingDown()) {
|
||||
{
|
||||
std::scoped_lock lk{release_mutex};
|
||||
queued_buffers.store(0);
|
||||
}
|
||||
release_cv.notify_one();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true);
|
|||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
|
@ -61,6 +62,10 @@ SWITCHABLE(u32, false);
|
|||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.cpp
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
|
|
@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true);
|
|||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
|
@ -83,6 +84,10 @@ SWITCHABLE(u32, false);
|
|||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.h
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
|
|
@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
|||
|
||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||
|
||||
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
||||
|
||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||
|
||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||
|
|
|
@ -407,13 +407,13 @@ protected:
|
|||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
|
||||
const auto user_data = ctx.ReadBuffer(0);
|
||||
const auto image_data = ctx.ReadBuffer(1); // TODO: Check if this buffer is always provided
|
||||
const auto image_data = ctx.ReadBufferA(0);
|
||||
const auto user_data = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
|
||||
if (user_data.size() < sizeof(UserData)) {
|
||||
LOG_ERROR(Service_ACC, "UserData buffer too small!");
|
||||
|
|
|
@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_)
|
|||
{21, nullptr, "ClearAppletTransitionBuffer"},
|
||||
{22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
|
||||
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
||||
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
||||
|
@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
|
|||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(1U);
|
||||
rb.Push(0);
|
||||
}
|
||||
|
||||
void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
|
|
|
@ -124,6 +124,8 @@ public:
|
|||
private:
|
||||
void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
|
||||
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
|
||||
void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
};
|
||||
|
|
|
@ -23,6 +23,17 @@
|
|||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace {
|
||||
static thread_local std::array read_buffer_data_a{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_x{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Service {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
|
||||
|
@ -328,26 +339,57 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
|
|||
}
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_a{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_a[buffer_index];
|
||||
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
|
||||
BufferDescriptorA()[buffer_index].Size(),
|
||||
&read_buffer_data_a[buffer_index]);
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_x{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_x[buffer_index];
|
||||
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
|
||||
BufferDescriptorX()[buffer_index].Size(),
|
||||
&read_buffer_data_x[buffer_index]);
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_a{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_a{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
static thread_local std::array read_buffer_x{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_x{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
const bool is_buffer_x{BufferDescriptorX().size() > buffer_index &&
|
||||
BufferDescriptorX()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a && is_buffer_x) {
|
||||
LOG_WARNING(Input, "Both buffer descriptors are available a.size={}, x.size={}",
|
||||
BufferDescriptorA()[buffer_index].Size(),
|
||||
BufferDescriptorX()[buffer_index].Size());
|
||||
}
|
||||
|
||||
if (is_buffer_a) {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
|
|
|
@ -253,6 +253,12 @@ public:
|
|||
return domain_message_header.has_value();
|
||||
}
|
||||
|
||||
/// Helper function to get a span of a buffer using the buffer descriptor A
|
||||
[[nodiscard]] std::span<const u8> ReadBufferA(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get a span of a buffer using the buffer descriptor X
|
||||
[[nodiscard]] std::span<const u8> ReadBufferX(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get a span of a buffer using the appropriate buffer descriptor
|
||||
[[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
|
|
|
@ -58,14 +58,8 @@ private:
|
|||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
|
@ -85,14 +79,8 @@ private:
|
|||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
|
||||
|
@ -137,14 +125,8 @@ private:
|
|||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
title_id, data1.size(), data2.size());
|
||||
|
@ -161,14 +143,8 @@ private:
|
|||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
|
||||
|
|
|
@ -338,6 +338,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host,
|
|||
for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
|
||||
const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
|
||||
PreSetController(identifier);
|
||||
PreSetMotion(identifier, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ TEST_CASE("UniqueFunction", "[common]") {
|
|||
Noisy noisy;
|
||||
REQUIRE(noisy.state == "Default constructed");
|
||||
|
||||
Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
|
||||
REQUIRE(noisy.state == "Move constructed");
|
||||
Common::UniqueFunction<void> func = [noisy_inner = std::move(noisy)] {
|
||||
REQUIRE(noisy_inner.state == "Move constructed");
|
||||
};
|
||||
REQUIRE(noisy.state == "Moved away");
|
||||
func();
|
||||
|
@ -101,7 +101,7 @@ TEST_CASE("UniqueFunction", "[common]") {
|
|||
};
|
||||
Foo object{&num_destroyed};
|
||||
{
|
||||
Common::UniqueFunction<void> func = [object = std::move(object)] {};
|
||||
Common::UniqueFunction<void> func = [object_inner = std::move(object)] {};
|
||||
REQUIRE(num_destroyed == 0);
|
||||
}
|
||||
REQUIRE(num_destroyed == 1);
|
||||
|
|
|
@ -48,8 +48,14 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
|
|||
SetInlineIndexBuffer(regs.inline_index_4x8.index3);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
|
||||
DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(),
|
||||
regs.vertex_array_instance_first.start.Value(),
|
||||
regs.vertex_array_instance_first.count.Value(), false);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
|
||||
LOG_WARNING(HW_GPU, "(STUBBED) called");
|
||||
DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(),
|
||||
regs.vertex_array_instance_subsequent.start.Value(),
|
||||
regs.vertex_array_instance_subsequent.count.Value(), true);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
|
||||
|
@ -84,6 +90,22 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve
|
|||
ProcessDraw(false, num_instances);
|
||||
}
|
||||
|
||||
void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
bool subsequent) {
|
||||
draw_state.topology = topology;
|
||||
draw_state.vertex_buffer.first = vertex_first;
|
||||
draw_state.vertex_buffer.count = vertex_count;
|
||||
|
||||
if (!subsequent) {
|
||||
draw_state.instance_count = 1;
|
||||
}
|
||||
|
||||
draw_state.base_instance = draw_state.instance_count - 1;
|
||||
draw_state.draw_mode = DrawMode::Instance;
|
||||
draw_state.instance_count++;
|
||||
ProcessDraw(false, 1);
|
||||
}
|
||||
|
||||
void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
|
||||
u32 base_index, u32 base_instance, u32 num_instances) {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
u32 base_instance, u32 num_instances);
|
||||
void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
bool subsequent);
|
||||
|
||||
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
|
||||
u32 base_instance, u32 num_instances);
|
||||
|
|
|
@ -86,7 +86,10 @@ public:
|
|||
uncommitted_operations.emplace_back(std::move(func));
|
||||
}
|
||||
pending_operations.emplace_back(std::move(uncommitted_operations));
|
||||
QueueFence(new_fence);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
QueueFence(new_fence);
|
||||
}
|
||||
if (!delay_fence) {
|
||||
func();
|
||||
}
|
||||
|
|
|
@ -137,16 +137,6 @@ bool Codec::CreateGpuAvDevice() {
|
|||
break;
|
||||
}
|
||||
if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
|
||||
#if defined(__unix__)
|
||||
// Some linux decoding backends are reported to crash with this config method
|
||||
// TODO(ameerj): Properly support this method
|
||||
if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
|
||||
// skip zero-copy decoders, we don't currently support them
|
||||
LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
|
||||
av_hwdevice_get_type_name(type), config->methods);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
||||
av_codec_ctx->pix_fmt = config->pix_fmt;
|
||||
return true;
|
||||
|
|
|
@ -19,6 +19,7 @@ set(SHADER_FILES
|
|||
block_linear_unswizzle_2d.comp
|
||||
block_linear_unswizzle_3d.comp
|
||||
convert_abgr8_to_d24s8.frag
|
||||
convert_abgr8_to_d32f.frag
|
||||
convert_d32f_to_abgr8.frag
|
||||
convert_d24s8_to_abgr8.frag
|
||||
convert_depth_to_float.frag
|
||||
|
|
15
src/video_core/host_shaders/convert_abgr8_to_d32f.frag
Executable file
15
src/video_core/host_shaders/convert_abgr8_to_d32f.frag
Executable file
|
@ -0,0 +1,15 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#version 450
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
vec4 color = texelFetch(color_texture, coord, 0).abgr;
|
||||
|
||||
float value = color.a * (color.r + color.g + color.b) / 3.0f;
|
||||
|
||||
gl_FragDepth = value;
|
||||
}
|
|
@ -9,6 +9,6 @@ layout(location = 0) out vec4 color;
|
|||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
float depth = textureLod(depth_tex, coord, 0).r;
|
||||
float depth = texelFetch(depth_tex, coord, 0).r;
|
||||
color = vec4(depth, depth, depth, 1.0);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "common/settings.h"
|
||||
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d32f_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
||||
|
@ -434,6 +435,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
|||
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
|
||||
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
|
||||
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
|
||||
convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),
|
||||
convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)),
|
||||
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||
|
@ -559,6 +561,13 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
|||
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(),
|
||||
convert_abgr8_to_d32f_frag);
|
||||
Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
|
||||
|
|
|
@ -67,6 +67,8 @@ public:
|
|||
|
||||
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
|
||||
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
|
@ -130,6 +132,7 @@ private:
|
|||
vk::ShaderModule convert_depth_to_float_frag;
|
||||
vk::ShaderModule convert_float_to_depth_frag;
|
||||
vk::ShaderModule convert_abgr8_to_d24s8_frag;
|
||||
vk::ShaderModule convert_abgr8_to_d32f_frag;
|
||||
vk::ShaderModule convert_d32f_to_abgr8_frag;
|
||||
vk::ShaderModule convert_d24s8_to_abgr8_frag;
|
||||
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
||||
|
@ -149,6 +152,7 @@ private:
|
|||
vk::Pipeline convert_d16_to_r16_pipeline;
|
||||
vk::Pipeline convert_r16_to_d16_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d32f_pipeline;
|
||||
vk::Pipeline convert_d32f_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_s8d24_to_abgr8_pipeline;
|
||||
|
|
|
@ -132,12 +132,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
|||
const bool use_accelerated =
|
||||
rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
|
||||
const bool is_srgb = use_accelerated && screen_info.is_srgb;
|
||||
RenderScreenshot(*framebuffer, use_accelerated);
|
||||
|
||||
Frame* frame = present_manager.GetRenderFrame();
|
||||
blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
|
||||
scheduler.Flush(*frame->render_ready);
|
||||
present_manager.Present(frame);
|
||||
{
|
||||
std::scoped_lock lock{rasterizer.LockCaches()};
|
||||
RenderScreenshot(*framebuffer, use_accelerated);
|
||||
|
||||
Frame* frame = present_manager.GetRenderFrame();
|
||||
blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
|
||||
scheduler.Flush(*frame->render_ready);
|
||||
present_manager.Present(frame);
|
||||
}
|
||||
|
||||
gpu.RendererFrameEndNotify();
|
||||
rasterizer.TickFrame();
|
||||
|
|
|
@ -198,7 +198,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||
if (!pipeline) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
// update engine as channel may be different.
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
pipeline->Configure(is_indexed);
|
||||
|
@ -708,6 +708,7 @@ void RasterizerVulkan::TiledCacheBarrier() {
|
|||
}
|
||||
|
||||
void RasterizerVulkan::FlushCommands() {
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
if (draw_counter == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -805,6 +806,7 @@ void RasterizerVulkan::FlushWork() {
|
|||
if ((++draw_counter & 7) != 7) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
if (draw_counter < DRAWS_TO_DISPATCH) {
|
||||
// Send recorded tasks to the worker thread
|
||||
scheduler.DispatchWork();
|
||||
|
@ -1499,7 +1501,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
|
|||
void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
|
||||
CreateChannel(channel);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
texture_cache.CreateChannel(channel);
|
||||
buffer_cache.CreateChannel(channel);
|
||||
}
|
||||
|
@ -1512,7 +1514,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
|
|||
const s32 channel_id = channel.bind_id;
|
||||
BindToChannel(channel_id);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
texture_cache.BindToChannel(channel_id);
|
||||
buffer_cache.BindToChannel(channel_id);
|
||||
}
|
||||
|
@ -1525,7 +1527,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
|
|||
void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
|
||||
EraseChannel(channel_id);
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{LockCaches()};
|
||||
texture_cache.EraseChannel(channel_id);
|
||||
buffer_cache.EraseChannel(channel_id);
|
||||
}
|
||||
|
|
|
@ -133,6 +133,10 @@ public:
|
|||
|
||||
void ReleaseChannel(s32 channel_id) override;
|
||||
|
||||
std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() {
|
||||
return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_TEXTURES = 192;
|
||||
static constexpr size_t MAX_IMAGES = 48;
|
||||
|
|
|
@ -1194,6 +1194,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
|||
return blit_image_helper.ConvertD16ToR16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
|
||||
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
|
||||
|
@ -1205,6 +1210,16 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
|||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32ToR32(dst, src_view);
|
||||
|
@ -1222,6 +1237,12 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
|||
}
|
||||
break;
|
||||
case PixelFormat::D32_FLOAT:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
|
||||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
|
||||
return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
|
||||
}
|
||||
if (src_view.format == PixelFormat::R32_FLOAT) {
|
||||
return blit_image_helper.ConvertR32ToD32(dst, src_view);
|
||||
}
|
||||
|
@ -2034,7 +2055,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) {
|
|||
},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([barrier](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
|
||||
});
|
||||
|
|
|
@ -68,6 +68,7 @@ struct LevelInfo {
|
|||
Extent2D tile_size;
|
||||
u32 bpp_log2;
|
||||
u32 tile_width_spacing;
|
||||
u32 num_levels;
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr u32 AdjustTileSize(u32 shift, u32 unit_factor, u32 dimension) {
|
||||
|
@ -118,11 +119,11 @@ template <u32 GOB_EXTENT>
|
|||
}
|
||||
|
||||
[[nodiscard]] constexpr Extent3D AdjustMipBlockSize(Extent3D num_tiles, Extent3D block_size,
|
||||
u32 level) {
|
||||
u32 level, u32 num_levels) {
|
||||
return {
|
||||
.width = AdjustMipBlockSize<GOB_SIZE_X>(num_tiles.width, block_size.width, level),
|
||||
.height = AdjustMipBlockSize<GOB_SIZE_Y>(num_tiles.height, block_size.height, level),
|
||||
.depth = level == 0
|
||||
.depth = level == 0 && num_levels == 1
|
||||
? block_size.depth
|
||||
: AdjustMipBlockSize<GOB_SIZE_Z>(num_tiles.depth, block_size.depth, level),
|
||||
};
|
||||
|
@ -166,7 +167,7 @@ template <u32 GOB_EXTENT>
|
|||
}
|
||||
|
||||
[[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) {
|
||||
if (level == 0) {
|
||||
if (level == 0 && info.num_levels == 1) {
|
||||
return Extent3D{
|
||||
.width = info.block.width,
|
||||
.height = info.block.height,
|
||||
|
@ -257,7 +258,7 @@ template <u32 GOB_EXTENT>
|
|||
}
|
||||
|
||||
[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block,
|
||||
u32 tile_width_spacing) {
|
||||
u32 tile_width_spacing, u32 num_levels) {
|
||||
const u32 bytes_per_block = BytesPerBlock(format);
|
||||
return {
|
||||
.size =
|
||||
|
@ -270,16 +271,18 @@ template <u32 GOB_EXTENT>
|
|||
.tile_size = DefaultBlockSize(format),
|
||||
.bpp_log2 = BytesPerBlockLog2(bytes_per_block),
|
||||
.tile_width_spacing = tile_width_spacing,
|
||||
.num_levels = num_levels,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) {
|
||||
return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing);
|
||||
return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing,
|
||||
info.resources.levels);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block,
|
||||
u32 tile_width_spacing, u32 level) {
|
||||
const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing);
|
||||
const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing, level);
|
||||
u32 offset = 0;
|
||||
for (u32 current_level = 0; current_level < level; ++current_level) {
|
||||
offset += CalculateLevelSize(info, current_level);
|
||||
|
@ -466,7 +469,7 @@ template <u32 GOB_EXTENT>
|
|||
};
|
||||
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
|
||||
const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing);
|
||||
const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0);
|
||||
const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0, info.resources.levels);
|
||||
return Extent3D{
|
||||
.width = Common::AlignUpLog2(num_tiles.width, alignment),
|
||||
.height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height),
|
||||
|
@ -533,7 +536,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
|
|||
UNIMPLEMENTED_IF(copy.image_extent != level_size);
|
||||
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level);
|
||||
const Extent3D block =
|
||||
AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
|
||||
size_t host_offset = copy.buffer_offset;
|
||||
|
||||
|
@ -698,7 +702,7 @@ u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level) {
|
|||
const Extent2D tile_size = DefaultBlockSize(info.format);
|
||||
const Extent3D level_size = AdjustMipSize(info.size, level);
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level);
|
||||
const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level, info.resources.levels);
|
||||
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
|
||||
return StrideAlignment(num_tiles, block, bpp_log2, info.tile_width_spacing);
|
||||
}
|
||||
|
@ -887,7 +891,8 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
|||
.image_extent = level_size,
|
||||
};
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level);
|
||||
const Extent3D block =
|
||||
AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2);
|
||||
size_t guest_layer_offset = 0;
|
||||
|
||||
|
@ -1041,7 +1046,7 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) {
|
|||
const Extent2D tile_size = DefaultBlockSize(info.format);
|
||||
const Extent3D level_size = AdjustMipSize(info.size, level);
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
return AdjustMipBlockSize(num_tiles, level_info.block, level);
|
||||
return AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
}
|
||||
|
||||
boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) {
|
||||
|
@ -1063,7 +1068,8 @@ boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const I
|
|||
for (s32 level = 0; level < num_levels; ++level) {
|
||||
const Extent3D level_size = AdjustMipSize(size, level);
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level);
|
||||
const Extent3D block =
|
||||
AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
params[level] = SwizzleParameters{
|
||||
.num_tiles = num_tiles,
|
||||
.block = block,
|
||||
|
@ -1292,11 +1298,11 @@ u32 MapSizeBytes(const ImageBase& image) {
|
|||
}
|
||||
}
|
||||
|
||||
static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0}, 0) ==
|
||||
static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) ==
|
||||
0x7f8000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x40000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
|
||||
|
||||
static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0}, 0) == 0x40000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
|
||||
|
||||
static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) ==
|
||||
0x2afc00);
|
||||
|
|
|
@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE)
|
|||
discord_impl.cpp
|
||||
discord_impl.h
|
||||
)
|
||||
target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
|
||||
target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network)
|
||||
target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -128,8 +128,8 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
|
|||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
|
|
|
@ -115,17 +115,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
|||
for (std::size_t i = 0; i < player_tabs.size(); ++i) {
|
||||
player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
|
||||
player_tabs[i]->layout()->addWidget(player_controllers[i]);
|
||||
connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
|
||||
connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) {
|
||||
// Ensures that the controllers are always connected in sequential order
|
||||
if (is_connected) {
|
||||
for (std::size_t index = 0; index <= i; ++index) {
|
||||
player_connected[index]->setChecked(is_connected);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t index = i; index < player_tabs.size(); ++index) {
|
||||
player_connected[index]->setChecked(is_connected);
|
||||
}
|
||||
}
|
||||
this->propagateMouseClickOnPlayers(i, checked, true);
|
||||
});
|
||||
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
|
||||
&ConfigureInput::UpdateAllInputDevices);
|
||||
|
@ -183,6 +175,30 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
|||
LoadConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) {
|
||||
// Origin has already been toggled
|
||||
if (!origin) {
|
||||
player_connected[player_index]->setChecked(checked);
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
// Check all previous buttons when checked
|
||||
if (player_index > 0) {
|
||||
propagateMouseClickOnPlayers(player_index - 1, checked, false);
|
||||
}
|
||||
} else {
|
||||
// Unchecked all following buttons when unchecked
|
||||
if (player_index < player_tabs.size() - 1) {
|
||||
// Reconnect current player if it was the last one checked
|
||||
// (player number was reduced by more than one)
|
||||
if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) {
|
||||
player_connected[player_index]->setCheckState(Qt::Checked);
|
||||
}
|
||||
propagateMouseClickOnPlayers(player_index + 1, checked, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QWidget*> ConfigureInput::GetSubTabs() const {
|
||||
return {
|
||||
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
|
||||
|
|
|
@ -56,6 +56,7 @@ private:
|
|||
void UpdateDockedState(bool is_handheld);
|
||||
void UpdateAllInputDevices();
|
||||
void UpdateAllInputProfiles(std::size_t player_index);
|
||||
void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked);
|
||||
|
||||
/// Load configuration settings.
|
||||
void LoadConfiguration();
|
||||
|
|
|
@ -157,6 +157,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
|||
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
|
||||
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
|
||||
INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
|
||||
INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
|
||||
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
|
||||
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
|
||||
|
||||
|
@ -383,6 +384,13 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
|||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
|
||||
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
|
||||
{
|
||||
PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"),
|
||||
PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"),
|
||||
PAIR(ConfirmStop, Ask_Never, "Never ask"),
|
||||
}});
|
||||
|
||||
#undef PAIR
|
||||
#undef CTX_PAIR
|
||||
|
|
|
@ -211,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() {
|
|||
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
|
||||
"data is collected</a> to help improve yuzu. "
|
||||
"<br/><br/>Would you like to share your usage data with us?");
|
||||
if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) {
|
||||
if (!question(this, tr("Telemetry"), telemetry_message)) {
|
||||
Settings::values.enable_telemetry = false;
|
||||
system->ApplySettings();
|
||||
}
|
||||
|
@ -2420,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
|
|||
}
|
||||
}();
|
||||
|
||||
if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No) != QMessageBox::Yes) {
|
||||
if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2521,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
|
|||
}
|
||||
}();
|
||||
|
||||
if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No) != QMessageBox::Yes) {
|
||||
if (!GMainWindow::question(this, tr("Remove File"), question,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3409,10 +3408,13 @@ void GMainWindow::OnRestartGame() {
|
|||
if (!system->IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
// Make a copy since ShutdownGame edits game_path
|
||||
const auto current_game = QString(current_game_path);
|
||||
ShutdownGame();
|
||||
BootGame(current_game);
|
||||
|
||||
if (ConfirmShutdownGame()) {
|
||||
// Make a copy since ShutdownGame edits game_path
|
||||
const auto current_game = QString(current_game_path);
|
||||
ShutdownGame();
|
||||
BootGame(current_game);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnPauseGame() {
|
||||
|
@ -3434,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() {
|
|||
}
|
||||
|
||||
void GMainWindow::OnStopGame() {
|
||||
if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
|
||||
return;
|
||||
if (ConfirmShutdownGame()) {
|
||||
play_time_manager->Stop();
|
||||
// Update game list to show new play time
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
if (OnShutdownBegin()) {
|
||||
OnShutdownBeginDialog();
|
||||
} else {
|
||||
OnEmulationStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play_time_manager->Stop();
|
||||
// Update game list to show new play time
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
if (OnShutdownBegin()) {
|
||||
OnShutdownBeginDialog();
|
||||
bool GMainWindow::ConfirmShutdownGame() {
|
||||
if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) {
|
||||
if (system->GetExitLocked()) {
|
||||
if (!ConfirmForceLockedExit()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!ConfirmChangeGame()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OnEmulationStopped();
|
||||
if (UISettings::values.confirm_before_stopping.GetValue() ==
|
||||
ConfirmStop::Ask_Based_On_Game &&
|
||||
system->GetExitLocked()) {
|
||||
if (!ConfirmForceLockedExit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadComplete() {
|
||||
|
@ -3825,22 +3848,11 @@ void GMainWindow::OnTasRecord() {
|
|||
const bool is_recording = input_subsystem->GetTas()->Record();
|
||||
if (!is_recording) {
|
||||
is_tas_recording_dialog_active = true;
|
||||
ControllerNavigation* controller_navigation =
|
||||
new ControllerNavigation(system->HIDCore(), this);
|
||||
// Use QMessageBox instead of question so we can link controller navigation
|
||||
QMessageBox* box_dialog = new QMessageBox();
|
||||
box_dialog->setWindowTitle(tr("TAS Recording"));
|
||||
box_dialog->setText(tr("Overwrite file of player 1?"));
|
||||
box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
box_dialog->setDefaultButton(QMessageBox::Yes);
|
||||
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
|
||||
[box_dialog](Qt::Key key) {
|
||||
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(box_dialog, event);
|
||||
});
|
||||
int res = box_dialog->exec();
|
||||
controller_navigation->UnloadController();
|
||||
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
|
||||
|
||||
bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
input_subsystem->GetTas()->SaveRecording(answer);
|
||||
is_tas_recording_dialog_active = false;
|
||||
}
|
||||
OnTasStateChanged();
|
||||
|
@ -4081,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() {
|
|||
LoadAmiibo(filename);
|
||||
}
|
||||
|
||||
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton) {
|
||||
|
||||
QMessageBox* box_dialog = new QMessageBox(parent);
|
||||
box_dialog->setWindowTitle(title);
|
||||
box_dialog->setText(text);
|
||||
box_dialog->setStandardButtons(buttons);
|
||||
box_dialog->setDefaultButton(defaultButton);
|
||||
|
||||
ControllerNavigation* controller_navigation =
|
||||
new ControllerNavigation(system->HIDCore(), box_dialog);
|
||||
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
|
||||
[box_dialog](Qt::Key key) {
|
||||
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(box_dialog, event);
|
||||
});
|
||||
int res = box_dialog->exec();
|
||||
|
||||
controller_navigation->UnloadController();
|
||||
return res == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void GMainWindow::LoadAmiibo(const QString& filename) {
|
||||
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
|
||||
const QString title = tr("Error loading Amiibo data");
|
||||
|
@ -4814,8 +4849,7 @@ bool GMainWindow::ConfirmClose() {
|
|||
return true;
|
||||
}
|
||||
const auto text = tr("Are you sure you want to close yuzu?");
|
||||
const auto answer = QMessageBox::question(this, tr("yuzu"), text);
|
||||
return answer != QMessageBox::No;
|
||||
return question(this, tr("yuzu"), text);
|
||||
}
|
||||
|
||||
void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
|
@ -4908,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() {
|
|||
if (emu_thread == nullptr)
|
||||
return true;
|
||||
|
||||
const auto answer = QMessageBox::question(
|
||||
// Use custom question to link controller navigation
|
||||
return question(
|
||||
this, tr("yuzu"),
|
||||
tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
return answer != QMessageBox::No;
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmForceLockedExit() {
|
||||
|
@ -4922,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() {
|
|||
const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
|
||||
"Would you like to bypass this and exit anyway?");
|
||||
|
||||
const auto answer = QMessageBox::question(this, tr("yuzu"), text);
|
||||
return answer != QMessageBox::No;
|
||||
return question(this, tr("yuzu"), text);
|
||||
}
|
||||
|
||||
void GMainWindow::RequestGameExit() {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <optional>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
|
@ -15,6 +16,7 @@
|
|||
#include "input_common/drivers/tas_input.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include <QVariant>
|
||||
|
@ -424,6 +426,11 @@ private:
|
|||
bool CheckSystemArchiveDecryption();
|
||||
bool CheckFirmwarePresence();
|
||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||
/**
|
||||
* Open (or not) the right confirm dialog based on current setting and game exit lock
|
||||
* @returns true if the player confirmed or the settings do no require it
|
||||
*/
|
||||
bool ConfirmShutdownGame();
|
||||
|
||||
QString GetTasStateDescription() const;
|
||||
bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
|
||||
|
@ -431,6 +438,17 @@ private:
|
|||
const std::string& command, const std::string& arguments,
|
||||
const std::string& categories, const std::string& keywords);
|
||||
|
||||
/**
|
||||
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
|
||||
* The only difference is that it returns a boolean.
|
||||
*
|
||||
* @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button.
|
||||
*/
|
||||
bool question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons =
|
||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
std::unique_ptr<Core::System> system;
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#include "common/settings_enums.h"
|
||||
|
||||
using Settings::Category;
|
||||
using Settings::ConfirmStop;
|
||||
using Settings::Setting;
|
||||
using Settings::SwitchableSetting;
|
||||
|
||||
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
|
||||
namespace Settings {
|
||||
|
@ -94,6 +96,15 @@ struct Values {
|
|||
Setting<bool> confirm_before_closing{
|
||||
linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
|
||||
true, true};
|
||||
|
||||
SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
|
||||
ConfirmStop::Ask_Always,
|
||||
"confirmStop",
|
||||
Category::UiGeneral,
|
||||
Settings::Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
|
||||
Setting<bool> pause_when_in_background{linkage,
|
||||
false,
|
||||
|
|
Loading…
Reference in a new issue