mirror of
https://github.com/azahar-emu/azahar
synced 2025-11-06 15:09:58 +01: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:
parent
a15af9b550
commit
df3c0c18e4
@ -17,7 +17,7 @@ puts 'done'
|
|||||||
print 'Checking files...'
|
print 'Checking files...'
|
||||||
issue_files = []
|
issue_files = []
|
||||||
branch_changed_files.each do |file_name|
|
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')
|
file_content = File.read(file_name, mode: 'r:bom|utf-8')
|
||||||
if not file_content.start_with?(license_header)
|
if not file_content.start_with?(license_header)
|
||||||
issue_files.push(file_name)
|
issue_files.push(file_name)
|
||||||
|
|||||||
@ -18,7 +18,11 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
|||||||
include(DownloadExternals)
|
include(DownloadExternals)
|
||||||
include(CMakeDependentOption)
|
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.
|
# 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.
|
# Make sure we default to Release build type always, unless the generator has custom types.
|
||||||
@ -125,6 +129,9 @@ endif()
|
|||||||
if (ENABLE_ROOM)
|
if (ENABLE_ROOM)
|
||||||
add_definitions(-DENABLE_ROOM)
|
add_definitions(-DENABLE_ROOM)
|
||||||
endif()
|
endif()
|
||||||
|
if (ENABLE_SDL2)
|
||||||
|
add_definitions(-DENABLE_SDL2)
|
||||||
|
endif()
|
||||||
if (ENABLE_SDL2_FRONTEND)
|
if (ENABLE_SDL2_FRONTEND)
|
||||||
add_definitions(-DENABLE_SDL2_FRONTEND)
|
add_definitions(-DENABLE_SDL2_FRONTEND)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -165,10 +165,12 @@ if (UNIX AND NOT APPLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_sources(citra_common PUBLIC
|
target_sources(citra_common PUBLIC
|
||||||
apple_authorization.h
|
apple_authorization.h
|
||||||
apple_authorization.cpp
|
apple_authorization.cpp
|
||||||
)
|
apple_utils.h
|
||||||
|
apple_utils.mm
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
@ -211,4 +213,4 @@ endif()
|
|||||||
if (SSE42_COMPILE_OPTION)
|
if (SSE42_COMPILE_OPTION)
|
||||||
target_compile_definitions(citra_common PRIVATE CITRA_HAS_SSE42)
|
target_compile_definitions(citra_common PRIVATE CITRA_HAS_SSE42)
|
||||||
target_compile_options(citra_common PRIVATE ${SSE42_COMPILE_OPTION})
|
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)
|
target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_SDL2)
|
||||||
|
target_link_libraries(video_core PRIVATE SDL2::SDL2)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_dependencies(video_core host_shaders)
|
add_dependencies(video_core host_shaders)
|
||||||
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,14 @@
|
|||||||
|
|
||||||
#include <vk_mem_alloc.h>
|
#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));
|
MICROPROFILE_DEFINE(Vulkan_RenderFrame, "Vulkan", "Render Frame", MP_RGB(128, 128, 64));
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
@ -50,11 +58,48 @@ constexpr static std::array<vk::DescriptorSetLayoutBinding, 1> PRESENT_BINDINGS
|
|||||||
{0, vk::DescriptorType::eCombinedImageSampler, 3, vk::ShaderStageFlagBits::eFragment},
|
{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_,
|
RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_,
|
||||||
Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window)
|
Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window)
|
||||||
: RendererBase{system, window, secondary_window}, memory{system.Memory()}, pica{pica_},
|
: RendererBase{system, window, secondary_window}, memory{system.Memory()}, pica{pica_},
|
||||||
instance{window, Settings::values.physical_device.GetValue()}, scheduler{instance},
|
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{instance, scheduler, vk::BufferUsageFlagBits::eVertexBuffer,
|
||||||
VERTEX_BUFFER_SIZE},
|
VERTEX_BUFFER_SIZE},
|
||||||
update_queue{instance},
|
update_queue{instance},
|
||||||
@ -66,7 +111,8 @@ RendererVulkan::RendererVulkan(Core::System& system, Pica::PicaCore& pica_,
|
|||||||
BuildLayouts();
|
BuildLayouts();
|
||||||
BuildPipelines();
|
BuildPipelines();
|
||||||
if (secondary_window) {
|
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);
|
ASSERT(secondary_window);
|
||||||
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
|
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
|
||||||
if (!second_window) {
|
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);
|
RenderToWindow(*second_window, secondary_layout, false);
|
||||||
secondary_window->PollEvents();
|
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
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -98,11 +98,12 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, vk::Format fo
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& instance_,
|
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_},
|
: emu_window{emu_window_}, instance{instance_}, scheduler{scheduler_},
|
||||||
|
low_refresh_rate{low_refresh_rate_},
|
||||||
surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface},
|
surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface},
|
||||||
swapchain{instance, emu_window.GetFramebufferLayout().width,
|
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()},
|
graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()},
|
||||||
vsync_enabled{Settings::values.use_vsync_new.GetValue()},
|
vsync_enabled{Settings::values.use_vsync_new.GetValue()},
|
||||||
blit_supported{
|
blit_supported{
|
||||||
@ -355,7 +356,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
|
|||||||
#endif
|
#endif
|
||||||
std::scoped_lock submit_lock{scheduler.submit_mutex};
|
std::scoped_lock submit_lock{scheduler.submit_mutex};
|
||||||
graphics_queue.waitIdle();
|
graphics_queue.waitIdle();
|
||||||
swapchain.Create(frame->width, frame->height, surface);
|
swapchain.Create(frame->width, frame->height, surface, low_refresh_rate);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef ANDROID
|
#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
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ struct Frame {
|
|||||||
class PresentWindow final {
|
class PresentWindow final {
|
||||||
public:
|
public:
|
||||||
explicit PresentWindow(Frontend::EmuWindow& emu_window, const Instance& instance,
|
explicit PresentWindow(Frontend::EmuWindow& emu_window, const Instance& instance,
|
||||||
Scheduler& scheduler);
|
Scheduler& scheduler, bool low_refresh_rate);
|
||||||
~PresentWindow();
|
~PresentWindow();
|
||||||
|
|
||||||
/// Waits for all queued frames to finish presenting.
|
/// Waits for all queued frames to finish presenting.
|
||||||
@ -74,6 +74,7 @@ private:
|
|||||||
Frontend::EmuWindow& emu_window;
|
Frontend::EmuWindow& emu_window;
|
||||||
const Instance& instance;
|
const Instance& instance;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
bool low_refresh_rate;
|
||||||
vk::SurfaceKHR surface;
|
vk::SurfaceKHR surface;
|
||||||
vk::SurfaceKHR next_surface{};
|
vk::SurfaceKHR next_surface{};
|
||||||
Swapchain swapchain;
|
Swapchain swapchain;
|
||||||
|
|||||||
@ -15,11 +15,12 @@ MICROPROFILE_DEFINE(Vulkan_Present, "Vulkan", "Swapchain Present", MP_RGB(66, 18
|
|||||||
|
|
||||||
namespace Vulkan {
|
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_} {
|
: instance{instance_}, surface{surface_} {
|
||||||
FindPresentFormat();
|
FindPresentFormat();
|
||||||
SetPresentMode();
|
SetPresentMode();
|
||||||
Create(width, height, surface);
|
Create(width, height, surface, low_refresh_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
Swapchain::~Swapchain() {
|
Swapchain::~Swapchain() {
|
||||||
@ -27,10 +28,11 @@ Swapchain::~Swapchain() {
|
|||||||
instance.GetInstance().destroySurfaceKHR(surface);
|
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_;
|
width = width_;
|
||||||
height = height_;
|
height = height_;
|
||||||
surface = surface_;
|
surface = surface_;
|
||||||
|
low_refresh_rate = low_refresh_rate_;
|
||||||
needs_recreation = false;
|
needs_recreation = false;
|
||||||
|
|
||||||
Destroy();
|
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
|
// 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.
|
// 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;
|
present_mode = has_mailbox ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eImmediate;
|
||||||
if (!has_mailbox) {
|
if (!has_mailbox) {
|
||||||
LOG_WARNING(
|
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
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -16,11 +16,12 @@ class Scheduler;
|
|||||||
|
|
||||||
class Swapchain {
|
class Swapchain {
|
||||||
public:
|
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();
|
~Swapchain();
|
||||||
|
|
||||||
/// Creates (or recreates) the swapchain with a given size.
|
/// 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.
|
/// Acquires the next image in the swapchain.
|
||||||
bool AcquireNextImage();
|
bool AcquireNextImage();
|
||||||
@ -105,6 +106,7 @@ private:
|
|||||||
u32 image_index = 0;
|
u32 image_index = 0;
|
||||||
u32 frame_index = 0;
|
u32 frame_index = 0;
|
||||||
bool needs_recreation = true;
|
bool needs_recreation = true;
|
||||||
|
bool low_refresh_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user