mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9302db1077 | |||
| f9f3fd0f3e |
+2
-3
@@ -89,11 +89,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
||||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
||||||
|
|
||||||
|
ENABLE_FRAME_INTERPOLATION("enable_frame_interpolation"),
|
||||||
|
ENABLE_FRAME_SKIPPING("enable_frame_skipping"),
|
||||||
ENABLE_QUICK_SETTINGS("enable_quick_settings");
|
ENABLE_QUICK_SETTINGS("enable_quick_settings");
|
||||||
|
|
||||||
// external fun isFrameSkippingEnabled(): Boolean
|
|
||||||
external fun isFrameInterpolationEnabled(): Boolean
|
|
||||||
|
|
||||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||||
NativeConfig.getBoolean(key, needsGlobal)
|
NativeConfig.getBoolean(key, needsGlobal)
|
||||||
|
|
||||||
|
|||||||
+16
@@ -238,6 +238,22 @@ abstract class SettingsItem(
|
|||||||
override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
|
override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.ENABLE_FRAME_INTERPOLATION,
|
||||||
|
titleId = R.string.enable_frame_interpolation,
|
||||||
|
descriptionId = R.string.enable_frame_interpolation_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.ENABLE_FRAME_SKIPPING,
|
||||||
|
titleId = R.string.enable_frame_skipping,
|
||||||
|
descriptionId = R.string.enable_frame_skipping_description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
dockedModeSetting,
|
dockedModeSetting,
|
||||||
|
|||||||
+2
@@ -271,6 +271,8 @@ class SettingsFragmentPresenter(
|
|||||||
sl.apply {
|
sl.apply {
|
||||||
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||||
|
|
||||||
|
add(BooleanSetting.ENABLE_FRAME_INTERPOLATION.key)
|
||||||
|
add(BooleanSetting.ENABLE_FRAME_SKIPPING.key)
|
||||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||||
add(IntSetting.RENDERER_VSYNC.key)
|
add(IntSetting.RENDERER_VSYNC.key)
|
||||||
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||||
|
|||||||
@@ -1509,6 +1509,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
if (BooleanSetting.SHOW_FPS.getBoolean(needsGlobal)) {
|
if (BooleanSetting.SHOW_FPS.getBoolean(needsGlobal)) {
|
||||||
var fpsText = String.format("FPS: %.1f", actualFps)
|
var fpsText = String.format("FPS: %.1f", actualFps)
|
||||||
|
if (BooleanSetting.ENABLE_FRAME_INTERPOLATION.getBoolean(needsGlobal)) {
|
||||||
|
fpsText += String.format(" (Generated: %.1f)", actualFps * 2)
|
||||||
|
}
|
||||||
sb.append(fpsText)
|
sb.append(fpsText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -465,6 +465,10 @@
|
|||||||
<string name="network">Network</string>
|
<string name="network">Network</string>
|
||||||
|
|
||||||
<!-- Graphics settings strings -->
|
<!-- Graphics settings strings -->
|
||||||
|
<string name="enable_frame_skipping">Enable Frame Skipping</string>
|
||||||
|
<string name="enable_frame_skipping_description">Toggle frame skipping to improve performance by reducing the number of rendered frames.</string>
|
||||||
|
<string name="enable_frame_interpolation">Enable Frame Interpolation</string>
|
||||||
|
<string name="enable_frame_interpolation_description">Toggle frame interpolation to improve visual smoothness by generating intermediate frames.</string>
|
||||||
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
|
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
|
||||||
<string name="renderer_vsync">VSync mode</string>
|
<string name="renderer_vsync">VSync mode</string>
|
||||||
<string name="renderer_scaling_filter">Window adapting filter</string>
|
<string name="renderer_scaling_filter">Window adapting filter</string>
|
||||||
|
|||||||
@@ -337,6 +337,10 @@ struct Values {
|
|||||||
#endif
|
#endif
|
||||||
"backend", Category::Renderer};
|
"backend", Category::Renderer};
|
||||||
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList};
|
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList};
|
||||||
|
SwitchableSetting<bool> enable_frame_interpolation{linkage, true, "enable_frame_interpolation",
|
||||||
|
Category::Renderer};
|
||||||
|
SwitchableSetting<bool> enable_frame_skipping{linkage, true, "enable_frame_skipping",
|
||||||
|
Category::Renderer};
|
||||||
|
|
||||||
// Graphics Settings
|
// Graphics Settings
|
||||||
ResolutionScalingInfo resolution_info{};
|
ResolutionScalingInfo resolution_info{};
|
||||||
|
|||||||
@@ -38,9 +38,6 @@
|
|||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||||
#include "video_core/vulkan_common/vulkan_surface.h"
|
#include "video_core/vulkan_common/vulkan_surface.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -174,7 +171,144 @@ RendererVulkan::~RendererVulkan() {
|
|||||||
void(device.GetLogical().WaitIdle());
|
void(device.GetLogical().WaitIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RendererVulkan::InterpolateFrames(Frame* prev_frame, Frame* interpolated_frame) {
|
||||||
|
if (!prev_frame || !interpolated_frame || prev_frame == interpolated_frame ||
|
||||||
|
!prev_frame->image || !interpolated_frame->image || prev_frame->width == 0 ||
|
||||||
|
prev_frame->height == 0 || interpolated_frame->width == 0 ||
|
||||||
|
interpolated_frame->height == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
scheduler.Record([prev_frame, interpolated_frame](vk::CommandBuffer cmdbuf) {
|
||||||
|
constexpr VkImageSubresourceRange color_range{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
};
|
||||||
|
const std::array pre_barriers{
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = *prev_frame->image,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = *interpolated_frame->image,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const std::array post_barriers{
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = *prev_frame->image,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = *interpolated_frame->image,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const VkImageBlit blit{
|
||||||
|
.srcSubresource{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
.srcOffsets{
|
||||||
|
{0, 0, 0},
|
||||||
|
{static_cast<s32>(prev_frame->width), static_cast<s32>(prev_frame->height), 1},
|
||||||
|
},
|
||||||
|
.dstSubresource{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
.dstOffsets{
|
||||||
|
{0, 0, 0},
|
||||||
|
{static_cast<s32>(interpolated_frame->width),
|
||||||
|
static_cast<s32>(interpolated_frame->height), 1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, {}, {}, {}, pre_barriers);
|
||||||
|
cmdbuf.BlitImage(*prev_frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
*interpolated_frame->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
blit, VK_FILTER_LINEAR);
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, {}, {}, {},
|
||||||
|
post_barriers);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
if (framebuffers.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
static u64 frame_counter = 0;
|
||||||
|
const bool enable_frame_skipping = Settings::values.enable_frame_skipping.GetValue();
|
||||||
|
const bool enable_frame_interpolation = Settings::values.enable_frame_interpolation.GetValue();
|
||||||
|
|
||||||
|
if (enable_frame_skipping) {
|
||||||
|
++frame_counter;
|
||||||
|
if ((frame_counter % 2) != 0) {
|
||||||
|
if (enable_frame_interpolation && previous_frame) {
|
||||||
|
Frame* interpolated_frame = present_manager.GetRenderFrame();
|
||||||
|
if (!InterpolateFrames(previous_frame, interpolated_frame)) {
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
blit_swapchain.DrawToFrame(rasterizer, interpolated_frame, framebuffers,
|
||||||
|
render_window.GetFramebufferLayout(),
|
||||||
|
swapchain.GetImageCount(),
|
||||||
|
swapchain.GetImageViewFormat());
|
||||||
|
}
|
||||||
|
scheduler.Flush(*interpolated_frame->render_ready);
|
||||||
|
present_manager.Present(interpolated_frame);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frame_counter = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed();
|
||||||
};
|
};
|
||||||
@@ -198,6 +332,8 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
|||||||
|
|
||||||
gpu.RendererFrameEndNotify();
|
gpu.RendererFrameEndNotify();
|
||||||
rasterizer.TickFrame();
|
rasterizer.TickFrame();
|
||||||
|
|
||||||
|
previous_frame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererVulkan::Report() const {
|
void RendererVulkan::Report() const {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public:
|
|||||||
void InitializePlatformSpecific();
|
void InitializePlatformSpecific();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InterpolateFrames(Frame* prev_frame, Frame* curr_frame);
|
bool InterpolateFrames(Frame* prev_frame, Frame* curr_frame);
|
||||||
Frame* previous_frame = nullptr; // Store the previous frame for interpolation
|
Frame* previous_frame = nullptr; // Store the previous frame for interpolation
|
||||||
VkCommandBuffer BeginSingleTimeCommands();
|
VkCommandBuffer BeginSingleTimeCommands();
|
||||||
void EndSingleTimeCommands(VkCommandBuffer command_buffer);
|
void EndSingleTimeCommands(VkCommandBuffer command_buffer);
|
||||||
|
|||||||
@@ -202,7 +202,8 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
|
|||||||
.arrayLayers = 1,
|
.arrayLayers = 1,
|
||||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||||
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
|||||||
Reference in New Issue
Block a user