early-access version 1929
This commit is contained in:
parent
1d25a09b5f
commit
6ecaf46441
22 changed files with 188 additions and 95 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 1928.
|
This is the source code for early-access 1929.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,8 @@ void LogSettings() {
|
||||||
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
||||||
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
||||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||||
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
|
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
|
||||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
|
||||||
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
||||||
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
||||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||||
|
@ -132,8 +132,8 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||||
values.vulkan_device.SetGlobal(true);
|
values.vulkan_device.SetGlobal(true);
|
||||||
values.aspect_ratio.SetGlobal(true);
|
values.aspect_ratio.SetGlobal(true);
|
||||||
values.max_anisotropy.SetGlobal(true);
|
values.max_anisotropy.SetGlobal(true);
|
||||||
values.use_frame_limit.SetGlobal(true);
|
values.use_speed_limit.SetGlobal(true);
|
||||||
values.frame_limit.SetGlobal(true);
|
values.speed_limit.SetGlobal(true);
|
||||||
values.use_disk_shader_cache.SetGlobal(true);
|
values.use_disk_shader_cache.SetGlobal(true);
|
||||||
values.gpu_accuracy.SetGlobal(true);
|
values.gpu_accuracy.SetGlobal(true);
|
||||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||||
|
|
|
@ -331,8 +331,8 @@ struct Values {
|
||||||
"fullscreen_mode"};
|
"fullscreen_mode"};
|
||||||
Setting<int> aspect_ratio{0, "aspect_ratio"};
|
Setting<int> aspect_ratio{0, "aspect_ratio"};
|
||||||
Setting<int> max_anisotropy{0, "max_anisotropy"};
|
Setting<int> max_anisotropy{0, "max_anisotropy"};
|
||||||
Setting<bool> use_frame_limit{true, "use_frame_limit"};
|
Setting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||||
Setting<u16> frame_limit{100, "frame_limit"};
|
Setting<u16> speed_limit{100, "speed_limit"};
|
||||||
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||||
Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
|
Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
|
||||||
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
||||||
|
|
|
@ -411,7 +411,7 @@ struct System::Impl {
|
||||||
std::string status_details = "";
|
std::string status_details = "";
|
||||||
|
|
||||||
std::unique_ptr<Core::PerfStats> perf_stats;
|
std::unique_ptr<Core::PerfStats> perf_stats;
|
||||||
Core::FrameLimiter frame_limiter;
|
Core::SpeedLimiter speed_limiter;
|
||||||
|
|
||||||
bool is_multicore{};
|
bool is_multicore{};
|
||||||
bool is_async_gpu{};
|
bool is_async_gpu{};
|
||||||
|
@ -606,12 +606,12 @@ const Core::PerfStats& System::GetPerfStats() const {
|
||||||
return *impl->perf_stats;
|
return *impl->perf_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::FrameLimiter& System::FrameLimiter() {
|
Core::SpeedLimiter& System::SpeedLimiter() {
|
||||||
return impl->frame_limiter;
|
return impl->speed_limiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::FrameLimiter& System::FrameLimiter() const {
|
const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||||
return impl->frame_limiter;
|
return impl->speed_limiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||||
|
|
|
@ -94,7 +94,7 @@ class ARM_Interface;
|
||||||
class CpuManager;
|
class CpuManager;
|
||||||
class DeviceMemory;
|
class DeviceMemory;
|
||||||
class ExclusiveMonitor;
|
class ExclusiveMonitor;
|
||||||
class FrameLimiter;
|
class SpeedLimiter;
|
||||||
class PerfStats;
|
class PerfStats;
|
||||||
class Reporter;
|
class Reporter;
|
||||||
class TelemetrySession;
|
class TelemetrySession;
|
||||||
|
@ -292,11 +292,11 @@ public:
|
||||||
/// Provides a constant reference to the internal PerfStats instance.
|
/// Provides a constant reference to the internal PerfStats instance.
|
||||||
[[nodiscard]] const Core::PerfStats& GetPerfStats() const;
|
[[nodiscard]] const Core::PerfStats& GetPerfStats() const;
|
||||||
|
|
||||||
/// Provides a reference to the frame limiter;
|
/// Provides a reference to the speed limiter;
|
||||||
[[nodiscard]] Core::FrameLimiter& FrameLimiter();
|
[[nodiscard]] Core::SpeedLimiter& SpeedLimiter();
|
||||||
|
|
||||||
/// Provides a constant referent to the frame limiter
|
/// Provides a constant reference to the speed limiter
|
||||||
[[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
|
[[nodiscard]] const Core::SpeedLimiter& SpeedLimiter() const;
|
||||||
|
|
||||||
/// Gets the name of the current game
|
/// Gets the name of the current game
|
||||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||||
|
|
|
@ -54,7 +54,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||||
|
|
||||||
system.GetPerfStats().EndSystemFrame();
|
system.GetPerfStats().EndSystemFrame();
|
||||||
system.GPU().SwapBuffers(&framebuffer);
|
system.GPU().SwapBuffers(&framebuffer);
|
||||||
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
|
system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||||
system.GetPerfStats().BeginSystemFrame();
|
system.GetPerfStats().BeginSystemFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,15 +127,15 @@ double PerfStats::GetLastFrameTimeScale() const {
|
||||||
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
void SpeedLimiter::DoSpeedLimiting(microseconds current_system_time_us) {
|
||||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
if (!Settings::values.use_speed_limit.GetValue() ||
|
||||||
Settings::values.use_multi_core.GetValue()) {
|
Settings::values.use_multi_core.GetValue()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto now = Clock::now();
|
auto now = Clock::now();
|
||||||
|
|
||||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
const double sleep_scale = Settings::values.speed_limit.GetValue() / 100.0;
|
||||||
|
|
||||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||||
|
@ -143,17 +143,17 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||||
// limiting
|
// limiting
|
||||||
const microseconds max_lag_time_us = duration_cast<microseconds>(
|
const microseconds max_lag_time_us = duration_cast<microseconds>(
|
||||||
std::chrono::duration<double, std::chrono::microseconds::period>(25ms / sleep_scale));
|
std::chrono::duration<double, std::chrono::microseconds::period>(25ms / sleep_scale));
|
||||||
frame_limiting_delta_err += duration_cast<microseconds>(
|
speed_limiting_delta_err += duration_cast<microseconds>(
|
||||||
std::chrono::duration<double, std::chrono::microseconds::period>(
|
std::chrono::duration<double, std::chrono::microseconds::period>(
|
||||||
(current_system_time_us - previous_system_time_us) / sleep_scale));
|
(current_system_time_us - previous_system_time_us) / sleep_scale));
|
||||||
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
speed_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
||||||
frame_limiting_delta_err =
|
speed_limiting_delta_err =
|
||||||
std::clamp(frame_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
std::clamp(speed_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
||||||
|
|
||||||
if (frame_limiting_delta_err > microseconds::zero()) {
|
if (speed_limiting_delta_err > microseconds::zero()) {
|
||||||
std::this_thread::sleep_for(frame_limiting_delta_err);
|
std::this_thread::sleep_for(speed_limiting_delta_err);
|
||||||
auto now_after_sleep = Clock::now();
|
auto now_after_sleep = Clock::now();
|
||||||
frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
speed_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
||||||
now = now_after_sleep;
|
now = now_after_sleep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,11 +85,11 @@ private:
|
||||||
double previous_fps = 0;
|
double previous_fps = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrameLimiter {
|
class SpeedLimiter {
|
||||||
public:
|
public:
|
||||||
using Clock = std::chrono::high_resolution_clock;
|
using Clock = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
|
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Emulated system time (in microseconds) at the last limiter invocation
|
/// Emulated system time (in microseconds) at the last limiter invocation
|
||||||
|
@ -98,7 +98,7 @@ private:
|
||||||
Clock::time_point previous_walltime = Clock::now();
|
Clock::time_point previous_walltime = Clock::now();
|
||||||
|
|
||||||
/// Accumulated difference between walltime and emulated time
|
/// Accumulated difference between walltime and emulated time
|
||||||
std::chrono::microseconds frame_limiting_delta_err{0};
|
std::chrono::microseconds speed_limiting_delta_err{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -221,8 +221,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||||
AddField(field_type, "Renderer_ResolutionFactor",
|
AddField(field_type, "Renderer_ResolutionFactor",
|
||||||
Settings::values.resolution_factor.GetValue());
|
Settings::values.resolution_factor.GetValue());
|
||||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue());
|
||||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue());
|
||||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||||
Settings::values.use_disk_shader_cache.GetValue());
|
Settings::values.use_disk_shader_cache.GetValue());
|
||||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||||
|
|
|
@ -19,9 +19,6 @@ RendererBase::~RendererBase() = default;
|
||||||
|
|
||||||
void RendererBase::RefreshBaseSettings() {
|
void RendererBase::RefreshBaseSettings() {
|
||||||
UpdateCurrentFramebufferLayout();
|
UpdateCurrentFramebufferLayout();
|
||||||
|
|
||||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
|
|
||||||
renderer_settings.set_background_color = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererBase::UpdateCurrentFramebufferLayout() {
|
void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||||
|
@ -30,7 +27,7 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||||
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
|
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||||
const Layout::FramebufferLayout& layout) {
|
const Layout::FramebufferLayout& layout) {
|
||||||
if (renderer_settings.screenshot_requested) {
|
if (renderer_settings.screenshot_requested) {
|
||||||
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
||||||
|
|
|
@ -21,13 +21,10 @@ class GraphicsContext;
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
struct RendererSettings {
|
struct RendererSettings {
|
||||||
std::atomic_bool use_framelimiter{false};
|
|
||||||
std::atomic_bool set_background_color{false};
|
|
||||||
|
|
||||||
// Screenshot
|
// Screenshot
|
||||||
std::atomic<bool> screenshot_requested{false};
|
std::atomic<bool> screenshot_requested{false};
|
||||||
void* screenshot_bits{};
|
void* screenshot_bits{};
|
||||||
std::function<void()> screenshot_complete_callback;
|
std::function<void(bool)> screenshot_complete_callback;
|
||||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +80,7 @@ public:
|
||||||
void RefreshBaseSettings();
|
void RefreshBaseSettings();
|
||||||
|
|
||||||
/// Request a screenshot of the next frame
|
/// Request a screenshot of the next frame
|
||||||
void RequestScreenshot(void* data, std::function<void()> callback,
|
void RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||||
const Layout::FramebufferLayout& layout);
|
const Layout::FramebufferLayout& layout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -328,12 +328,10 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||||
if (renderer_settings.set_background_color) {
|
|
||||||
// Update background color before drawing
|
// Update background color before drawing
|
||||||
glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
|
glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
|
||||||
Settings::values.bg_green.GetValue() / 255.0f,
|
Settings::values.bg_green.GetValue() / 255.0f,
|
||||||
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
|
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
|
||||||
}
|
|
||||||
|
|
||||||
// Set projection matrix
|
// Set projection matrix
|
||||||
const std::array ortho_matrix =
|
const std::array ortho_matrix =
|
||||||
|
@ -488,7 +486,7 @@ void RendererOpenGL::RenderScreenshot() {
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||||
|
|
||||||
renderer_settings.screenshot_complete_callback();
|
renderer_settings.screenshot_complete_callback(true);
|
||||||
renderer_settings.screenshot_requested = false;
|
renderer_settings.screenshot_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,6 +165,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||||
const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated);
|
const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated);
|
||||||
scheduler.Flush(render_semaphore);
|
scheduler.Flush(render_semaphore);
|
||||||
scheduler.WaitWorker();
|
scheduler.WaitWorker();
|
||||||
|
RenderScreenshot();
|
||||||
swapchain.Present(render_semaphore);
|
swapchain.Present(render_semaphore);
|
||||||
|
|
||||||
gpu.RendererFrameEndNotify();
|
gpu.RendererFrameEndNotify();
|
||||||
|
@ -193,4 +194,102 @@ void RendererVulkan::Report() const {
|
||||||
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Vulkan::RendererVulkan::RenderScreenshot() {
|
||||||
|
if (!renderer_settings.screenshot_requested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
||||||
|
const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
|
||||||
|
const VkBufferCreateInfo dst_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.size = buffer_size,
|
||||||
|
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||||
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
.queueFamilyIndexCount = 0,
|
||||||
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
};
|
||||||
|
const VkImage src_image = swapchain.GetImageIndex(swapchain.GetImageIndex());
|
||||||
|
const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_info);
|
||||||
|
MemoryCommit dst_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download);
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
scheduler.Record([buffer = *dst_buffer, src_image, layout](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier read_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = src_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const VkImageMemoryBarrier image_write_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = 0,
|
||||||
|
.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = src_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static constexpr VkMemoryBarrier memory_write_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
|
};
|
||||||
|
const VkBufferImageCopy copy{
|
||||||
|
.bufferOffset = 0,
|
||||||
|
.bufferRowLength = layout.width,
|
||||||
|
.bufferImageHeight = layout.height,
|
||||||
|
.imageSubresource{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
.imageOffset{.x = 0, .y = 0, .z = 0},
|
||||||
|
.imageExtent{
|
||||||
|
.width = layout.width,
|
||||||
|
.height = layout.height,
|
||||||
|
.depth = 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
0, read_barrier);
|
||||||
|
cmdbuf.CopyImageToBuffer(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy);
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
|
0, memory_write_barrier, nullptr, image_write_barrier);
|
||||||
|
});
|
||||||
|
// Ensure the copy is fully completed before saving the screenshot
|
||||||
|
scheduler.Finish();
|
||||||
|
|
||||||
|
// Copy backing image data to the QImage screenshot buffer
|
||||||
|
std::memcpy(renderer_settings.screenshot_bits, dst_memory.Map().data(),
|
||||||
|
dst_memory.Map().size());
|
||||||
|
renderer_settings.screenshot_complete_callback(false);
|
||||||
|
renderer_settings.screenshot_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -54,6 +54,8 @@ public:
|
||||||
private:
|
private:
|
||||||
void Report() const;
|
void Report() const;
|
||||||
|
|
||||||
|
void RenderScreenshot();
|
||||||
|
|
||||||
Core::TelemetrySession& telemetry_session;
|
Core::TelemetrySession& telemetry_session;
|
||||||
Core::Memory::Memory& cpu_memory;
|
Core::Memory::Memory& cpu_memory;
|
||||||
Tegra::GPU& gpu;
|
Tegra::GPU& gpu;
|
||||||
|
|
|
@ -634,9 +634,9 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
|
||||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||||
renderer.RequestScreenshot(
|
renderer.RequestScreenshot(
|
||||||
screenshot_image.bits(),
|
screenshot_image.bits(),
|
||||||
[=, this] {
|
[=, this](bool invert_y) {
|
||||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
|
||||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
||||||
|
|
|
@ -821,8 +821,8 @@ void Config::ReadRendererValues() {
|
||||||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||||
ReadGlobalSetting(Settings::values.aspect_ratio);
|
ReadGlobalSetting(Settings::values.aspect_ratio);
|
||||||
ReadGlobalSetting(Settings::values.max_anisotropy);
|
ReadGlobalSetting(Settings::values.max_anisotropy);
|
||||||
ReadGlobalSetting(Settings::values.use_frame_limit);
|
ReadGlobalSetting(Settings::values.use_speed_limit);
|
||||||
ReadGlobalSetting(Settings::values.frame_limit);
|
ReadGlobalSetting(Settings::values.speed_limit);
|
||||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||||
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
||||||
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||||
|
@ -1359,8 +1359,8 @@ void Config::SaveRendererValues() {
|
||||||
WriteGlobalSetting(Settings::values.fullscreen_mode);
|
WriteGlobalSetting(Settings::values.fullscreen_mode);
|
||||||
WriteGlobalSetting(Settings::values.aspect_ratio);
|
WriteGlobalSetting(Settings::values.aspect_ratio);
|
||||||
WriteGlobalSetting(Settings::values.max_anisotropy);
|
WriteGlobalSetting(Settings::values.max_anisotropy);
|
||||||
WriteGlobalSetting(Settings::values.use_frame_limit);
|
WriteGlobalSetting(Settings::values.use_speed_limit);
|
||||||
WriteGlobalSetting(Settings::values.frame_limit);
|
WriteGlobalSetting(Settings::values.speed_limit);
|
||||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||||
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
|
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
|
||||||
static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
|
static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
|
||||||
|
|
|
@ -24,8 +24,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
|
|
||||||
if (Settings::IsConfiguringGlobal()) {
|
if (Settings::IsConfiguringGlobal()) {
|
||||||
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
|
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit,
|
||||||
[this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
|
[this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
||||||
|
@ -45,18 +45,18 @@ void ConfigureGeneral::SetConfiguration() {
|
||||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
|
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
|
||||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
||||||
|
|
||||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
|
ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue());
|
||||||
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
|
ui->speed_limit->setValue(Settings::values.speed_limit.GetValue());
|
||||||
|
|
||||||
ui->fps_cap->setValue(Settings::values.fps_cap.GetValue());
|
ui->fps_cap->setValue(Settings::values.fps_cap.GetValue());
|
||||||
|
|
||||||
ui->button_reset_defaults->setEnabled(runtime_lock);
|
ui->button_reset_defaults->setEnabled(runtime_lock);
|
||||||
|
|
||||||
if (Settings::IsConfiguringGlobal()) {
|
if (Settings::IsConfiguringGlobal()) {
|
||||||
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
|
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue());
|
||||||
} else {
|
} else {
|
||||||
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
|
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
|
||||||
use_frame_limit != ConfigurationShared::CheckState::Global);
|
use_speed_limit != ConfigurationShared::CheckState::Global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,19 +92,19 @@ void ConfigureGeneral::ApplyConfiguration() {
|
||||||
Settings::values.fps_cap.SetValue(ui->fps_cap->value());
|
Settings::values.fps_cap.SetValue(ui->fps_cap->value());
|
||||||
|
|
||||||
// Guard if during game and set to game-specific value
|
// Guard if during game and set to game-specific value
|
||||||
if (Settings::values.use_frame_limit.UsingGlobal()) {
|
if (Settings::values.use_speed_limit.UsingGlobal()) {
|
||||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
|
||||||
Qt::Checked);
|
Qt::Checked);
|
||||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool global_frame_limit = use_frame_limit == ConfigurationShared::CheckState::Global;
|
bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global;
|
||||||
Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
|
Settings::values.use_speed_limit.SetGlobal(global_speed_limit);
|
||||||
Settings::values.frame_limit.SetGlobal(global_frame_limit);
|
Settings::values.speed_limit.SetGlobal(global_speed_limit);
|
||||||
if (!global_frame_limit) {
|
if (!global_speed_limit) {
|
||||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
|
||||||
Qt::Checked);
|
Qt::Checked);
|
||||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,8 +126,8 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||||
// Disables each setting if:
|
// Disables each setting if:
|
||||||
// - A game is running (thus settings in use), and
|
// - A game is running (thus settings in use), and
|
||||||
// - A non-global setting is applied.
|
// - A non-global setting is applied.
|
||||||
ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
|
ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal());
|
||||||
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
|
ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -139,13 +139,13 @@ void ConfigureGeneral::SetupPerGameUI() {
|
||||||
|
|
||||||
ui->button_reset_defaults->setVisible(false);
|
ui->button_reset_defaults->setVisible(false);
|
||||||
|
|
||||||
ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit,
|
ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit,
|
||||||
Settings::values.use_frame_limit, use_frame_limit);
|
Settings::values.use_speed_limit, use_speed_limit);
|
||||||
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
|
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
|
||||||
use_multi_core);
|
use_multi_core);
|
||||||
|
|
||||||
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
|
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
|
||||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
|
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
|
||||||
(use_frame_limit != ConfigurationShared::CheckState::Global));
|
(use_speed_limit != ConfigurationShared::CheckState::Global));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,6 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||||
|
|
||||||
ConfigurationShared::CheckState use_frame_limit;
|
ConfigurationShared::CheckState use_speed_limit;
|
||||||
ConfigurationShared::CheckState use_multi_core;
|
ConfigurationShared::CheckState use_multi_core;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,14 +27,14 @@
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
<widget class="QCheckBox" name="toggle_speed_limit">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Limit Speed Percent</string>
|
<string>Limit Speed Percent</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="frame_limit">
|
<widget class="QSpinBox" name="speed_limit">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string>%</string>
|
<string>%</string>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -979,23 +979,23 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
});
|
});
|
||||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
|
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
|
||||||
&QShortcut::activated, this, [&] {
|
&QShortcut::activated, this, [&] {
|
||||||
Settings::values.use_frame_limit.SetValue(
|
Settings::values.use_speed_limit.SetValue(
|
||||||
!Settings::values.use_frame_limit.GetValue());
|
!Settings::values.use_speed_limit.GetValue());
|
||||||
UpdateStatusBar();
|
UpdateStatusBar();
|
||||||
});
|
});
|
||||||
constexpr u16 SPEED_LIMIT_STEP = 5;
|
constexpr u16 SPEED_LIMIT_STEP = 5;
|
||||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
|
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
|
||||||
&QShortcut::activated, this, [&] {
|
&QShortcut::activated, this, [&] {
|
||||||
if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
|
if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
|
||||||
Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP +
|
Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP +
|
||||||
Settings::values.frame_limit.GetValue());
|
Settings::values.speed_limit.GetValue());
|
||||||
UpdateStatusBar();
|
UpdateStatusBar();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
|
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
|
||||||
&QShortcut::activated, this, [&] {
|
&QShortcut::activated, this, [&] {
|
||||||
if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
|
if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) {
|
||||||
Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
|
Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() -
|
||||||
SPEED_LIMIT_STEP);
|
SPEED_LIMIT_STEP);
|
||||||
UpdateStatusBar();
|
UpdateStatusBar();
|
||||||
}
|
}
|
||||||
|
@ -2965,10 +2965,10 @@ void GMainWindow::UpdateStatusBar() {
|
||||||
shader_building_label->setVisible(false);
|
shader_building_label->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.use_frame_limit.GetValue()) {
|
if (Settings::values.use_speed_limit.GetValue()) {
|
||||||
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
emu_speed_label->setText(tr("Speed: %1% / %2%")
|
||||||
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
|
||||||
.arg(Settings::values.frame_limit.GetValue()));
|
.arg(Settings::values.speed_limit.GetValue()));
|
||||||
} else {
|
} else {
|
||||||
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -451,8 +451,8 @@ void Config::ReadValues() {
|
||||||
ReadSetting("Renderer", Settings::values.fullscreen_mode);
|
ReadSetting("Renderer", Settings::values.fullscreen_mode);
|
||||||
ReadSetting("Renderer", Settings::values.aspect_ratio);
|
ReadSetting("Renderer", Settings::values.aspect_ratio);
|
||||||
ReadSetting("Renderer", Settings::values.max_anisotropy);
|
ReadSetting("Renderer", Settings::values.max_anisotropy);
|
||||||
ReadSetting("Renderer", Settings::values.use_frame_limit);
|
ReadSetting("Renderer", Settings::values.use_speed_limit);
|
||||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
ReadSetting("Renderer", Settings::values.speed_limit);
|
||||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||||
ReadSetting("Renderer", Settings::values.gpu_accuracy);
|
ReadSetting("Renderer", Settings::values.gpu_accuracy);
|
||||||
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
|
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
|
||||||
|
|
|
@ -265,13 +265,13 @@ use_nvdec_emulation =
|
||||||
# 0: Off, 1 (default): On
|
# 0: Off, 1 (default): On
|
||||||
accelerate_astc =
|
accelerate_astc =
|
||||||
|
|
||||||
# Turns on the frame limiter, which will limit frames output to the target game speed
|
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
|
||||||
# 0: Off, 1: On (default)
|
# 0: Off, 1: On (default)
|
||||||
use_frame_limit =
|
use_speed_limit =
|
||||||
|
|
||||||
# Limits the speed of the game to run no faster than this value as a percentage of target speed
|
# Limits the speed of the game to run no faster than this value as a percentage of target speed
|
||||||
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
|
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
|
||||||
frame_limit =
|
speed_limit =
|
||||||
|
|
||||||
# Whether to use disk based shader cache
|
# Whether to use disk based shader cache
|
||||||
# 0: Off, 1 (default): On
|
# 0: Off, 1 (default): On
|
||||||
|
|
Loading…
Reference in a new issue