mirror of
https://github.com/Lime3DS/Lime3DS
synced 2025-10-05 19:22:47 +02:00
renderer_vulkan: Disable FIFO when refresh rate is lower than ~60hz or Apple low power mode is enabled (#1193)
* renderer_vulkan: Disable FIFO when refresh rate is lower than ~60hz Also disables FIFO when Apple low power mode is enabled, as it can limit the application framerate to 30fps * renderer_vulkan.cpp: Put `IsLowRefreshRate` into anon namespace + make static
This commit is contained in:
@@ -17,7 +17,7 @@ puts 'done'
|
||||
print 'Checking files...'
|
||||
issue_files = []
|
||||
branch_changed_files.each do |file_name|
|
||||
if file_name.end_with?('.cpp', '.h', '.kt', '.kts') and File.file?(file_name)
|
||||
if file_name.end_with?('.cpp', '.h', '.kt', '.kts', '.m', '.mm') and File.file?(file_name)
|
||||
file_content = File.read(file_name, mode: 'r:bom|utf-8')
|
||||
if not file_content.start_with?(license_header)
|
||||
issue_files.push(file_name)
|
||||
|
@@ -18,7 +18,11 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
project(citra LANGUAGES C CXX ASM)
|
||||
if (APPLE)
|
||||
project(citra LANGUAGES C CXX OBJC OBJCXX ASM)
|
||||
else()
|
||||
project(citra LANGUAGES C CXX ASM)
|
||||
endif()
|
||||
|
||||
# Some submodules like to pick their own default build type if not specified.
|
||||
# Make sure we default to Release build type always, unless the generator has custom types.
|
||||
@@ -125,6 +129,9 @@ endif()
|
||||
if (ENABLE_ROOM)
|
||||
add_definitions(-DENABLE_ROOM)
|
||||
endif()
|
||||
if (ENABLE_SDL2)
|
||||
add_definitions(-DENABLE_SDL2)
|
||||
endif()
|
||||
if (ENABLE_SDL2_FRONTEND)
|
||||
add_definitions(-DENABLE_SDL2_FRONTEND)
|
||||
endif()
|
||||
|
@@ -165,10 +165,12 @@ if (UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
target_sources(citra_common PUBLIC
|
||||
apple_authorization.h
|
||||
apple_authorization.cpp
|
||||
)
|
||||
target_sources(citra_common PUBLIC
|
||||
apple_authorization.h
|
||||
apple_authorization.cpp
|
||||
apple_utils.h
|
||||
apple_utils.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
@@ -211,4 +213,4 @@ endif()
|
||||
if (SSE42_COMPILE_OPTION)
|
||||
target_compile_definitions(citra_common PRIVATE CITRA_HAS_SSE42)
|
||||
target_compile_options(citra_common PRIVATE ${SSE42_COMPILE_OPTION})
|
||||
endif()
|
||||
endif()
|
||||
|
9
src/common/apple_utils.h
Normal file
9
src/common/apple_utils.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
namespace AppleUtils {
|
||||
|
||||
int IsLowPowerModeEnabled();
|
||||
|
||||
}
|
13
src/common/apple_utils.mm
Normal file
13
src/common/apple_utils.mm
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
namespace AppleUtils {
|
||||
|
||||
int IsLowPowerModeEnabled() {
|
||||
return (int)[NSProcessInfo processInfo].lowPowerModeEnabled;
|
||||
}
|
||||
|
||||
} // namespace AppleUtils
|
@@ -203,6 +203,10 @@ if (ENABLE_VULKAN)
|
||||
target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
target_link_libraries(video_core PRIVATE SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
add_dependencies(video_core host_shaders)
|
||||
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||
|
||||
|
@@ -22,6 +22,14 @@
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "common/apple_utils.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SDL2
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
MICROPROFILE_DEFINE(Vulkan_RenderFrame, "Vulkan", "Render Frame", MP_RGB(128, 128, 64));
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -50,11 +58,48 @@ constexpr static std::array<vk::DescriptorSetLayoutBinding, 1> PRESENT_BINDINGS
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, 3, vk::ShaderStageFlagBits::eFragment},
|
||||
}};
|
||||
|
||||
namespace {
|
||||
static bool IsLowRefreshRate() {
|
||||
#ifdef ENABLE_SDL2
|
||||
const auto sdl_init_status = SDL_Init(SDL_INIT_VIDEO);
|
||||
if (sdl_init_status < 0) {
|
||||
LOG_ERROR(Render_Vulkan, "SDL failed to initialize, unable to check refresh rate");
|
||||
} else {
|
||||
SDL_DisplayMode cur_display_mode;
|
||||
SDL_GetCurrentDisplayMode(0, &cur_display_mode); // TODO: Multimonitor handling. -OS
|
||||
const auto cur_refresh_rate = cur_display_mode.refresh_rate;
|
||||
SDL_Quit();
|
||||
|
||||
if (cur_refresh_rate < SCREEN_REFRESH_RATE) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Detected refresh rate lower than the emulated 3DS screen: {}hz. FIFO will "
|
||||
"be disabled",
|
||||
cur_refresh_rate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Apple's low power mode sometimes limits applications to 30fps without changing the refresh
|
||||
// rate, meaning the above code doesn't catch it.
|
||||
if (AppleUtils::IsLowPowerModeEnabled()) {
|
||||
LOG_WARNING(Render_Vulkan, "Apple's low power mode is enabled, assuming low application "
|
||||
"framerate. FIFO will be disabled");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_,
|
||||
Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window)
|
||||
: RendererBase{system, window, secondary_window}, memory{system.Memory()}, pica{pica_},
|
||||
instance{window, Settings::values.physical_device.GetValue()}, scheduler{instance},
|
||||
renderpass_cache{instance, scheduler}, main_window{window, instance, scheduler},
|
||||
renderpass_cache{instance, scheduler},
|
||||
main_window{window, instance, scheduler, IsLowRefreshRate()},
|
||||
vertex_buffer{instance, scheduler, vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
VERTEX_BUFFER_SIZE},
|
||||
update_queue{instance},
|
||||
@@ -66,7 +111,8 @@ RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_,
|
||||
BuildLayouts();
|
||||
BuildPipelines();
|
||||
if (secondary_window) {
|
||||
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler);
|
||||
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler,
|
||||
IsLowRefreshRate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +887,8 @@ void RendererVulkan::SwapBuffers() {
|
||||
ASSERT(secondary_window);
|
||||
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
|
||||
if (!second_window) {
|
||||
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler);
|
||||
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler,
|
||||
IsLowRefreshRate());
|
||||
}
|
||||
RenderToWindow(*second_window, secondary_layout, false);
|
||||
secondary_window->PollEvents();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -98,11 +98,12 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, vk::Format fo
|
||||
} // Anonymous namespace
|
||||
|
||||
PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& instance_,
|
||||
Scheduler& scheduler_)
|
||||
Scheduler& scheduler_, bool low_refresh_rate_)
|
||||
: emu_window{emu_window_}, instance{instance_}, scheduler{scheduler_},
|
||||
low_refresh_rate{low_refresh_rate_},
|
||||
surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface},
|
||||
swapchain{instance, emu_window.GetFramebufferLayout().width,
|
||||
emu_window.GetFramebufferLayout().height, surface},
|
||||
emu_window.GetFramebufferLayout().height, surface, low_refresh_rate_},
|
||||
graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()},
|
||||
vsync_enabled{Settings::values.use_vsync_new.GetValue()},
|
||||
blit_supported{
|
||||
@@ -355,7 +356,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
|
||||
#endif
|
||||
std::scoped_lock submit_lock{scheduler.submit_mutex};
|
||||
graphics_queue.waitIdle();
|
||||
swapchain.Create(frame->width, frame->height, surface);
|
||||
swapchain.Create(frame->width, frame->height, surface, low_refresh_rate);
|
||||
};
|
||||
|
||||
#ifndef ANDROID
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -37,7 +37,7 @@ struct Frame {
|
||||
class PresentWindow final {
|
||||
public:
|
||||
explicit PresentWindow(Frontend::EmuWindow& emu_window, const Instance& instance,
|
||||
Scheduler& scheduler);
|
||||
Scheduler& scheduler, bool low_refresh_rate);
|
||||
~PresentWindow();
|
||||
|
||||
/// Waits for all queued frames to finish presenting.
|
||||
@@ -74,6 +74,7 @@ private:
|
||||
Frontend::EmuWindow& emu_window;
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
bool low_refresh_rate;
|
||||
vk::SurfaceKHR surface;
|
||||
vk::SurfaceKHR next_surface{};
|
||||
Swapchain swapchain;
|
||||
|
@@ -15,11 +15,12 @@ MICROPROFILE_DEFINE(Vulkan_Present, "Vulkan", "Swapchain Present", MP_RGB(66, 18
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
Swapchain::Swapchain(const Instance& instance_, u32 width, u32 height, vk::SurfaceKHR surface_)
|
||||
Swapchain::Swapchain(const Instance& instance_, u32 width, u32 height, vk::SurfaceKHR surface_,
|
||||
bool low_refresh_rate)
|
||||
: instance{instance_}, surface{surface_} {
|
||||
FindPresentFormat();
|
||||
SetPresentMode();
|
||||
Create(width, height, surface);
|
||||
Create(width, height, surface, low_refresh_rate);
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain() {
|
||||
@@ -27,10 +28,11 @@ Swapchain::~Swapchain() {
|
||||
instance.GetInstance().destroySurfaceKHR(surface);
|
||||
}
|
||||
|
||||
void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) {
|
||||
void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_, bool low_refresh_rate_) {
|
||||
width = width_;
|
||||
height = height_;
|
||||
surface = surface_;
|
||||
low_refresh_rate = low_refresh_rate_;
|
||||
needs_recreation = false;
|
||||
|
||||
Destroy();
|
||||
@@ -188,7 +190,8 @@ void Swapchain::SetPresentMode() {
|
||||
|
||||
// If vsync is enabled attempt to use mailbox mode in case the user wants to speedup/slowdown
|
||||
// the game. If mailbox is not available use immediate and warn about it.
|
||||
if (use_vsync && (frame_limit > 100 || frame_limit == 0)) { // 0 = unthrottled
|
||||
if (use_vsync &&
|
||||
(frame_limit > 100 || frame_limit == 0 || low_refresh_rate)) { // 0 = unthrottled
|
||||
present_mode = has_mailbox ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate;
|
||||
if (!has_mailbox) {
|
||||
LOG_WARNING(
|
||||
@@ -275,4 +278,4 @@ void Swapchain::SetupImages() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
} // namespace Vulkan
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -16,11 +16,12 @@ class Scheduler;
|
||||
|
||||
class Swapchain {
|
||||
public:
|
||||
explicit Swapchain(const Instance& instance, u32 width, u32 height, vk::SurfaceKHR surface);
|
||||
explicit Swapchain(const Instance& instance, u32 width, u32 height, vk::SurfaceKHR surface,
|
||||
bool low_refresh_rate);
|
||||
~Swapchain();
|
||||
|
||||
/// Creates (or recreates) the swapchain with a given size.
|
||||
void Create(u32 width, u32 height, vk::SurfaceKHR surface);
|
||||
void Create(u32 width, u32 height, vk::SurfaceKHR surface, bool low_refresh_rate);
|
||||
|
||||
/// Acquires the next image in the swapchain.
|
||||
bool AcquireNextImage();
|
||||
@@ -105,6 +106,7 @@ private:
|
||||
u32 image_index = 0;
|
||||
u32 frame_index = 0;
|
||||
bool needs_recreation = true;
|
||||
bool low_refresh_rate;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Reference in New Issue
Block a user