From 64f52777892bf39cadcdb77c1a252fa3a889616c Mon Sep 17 00:00:00 2001
From: Briar <205427297+icy-briar@users.noreply.github.com>
Date: Mon, 7 Apr 2025 19:18:29 +0200
Subject: [PATCH] renderer_vulkan: Add second screen opacity support
& Update bottom screen opacity label in UI
---
.../settings/ui/SettingsFragmentPresenter.kt | 3 +-
src/android/app/src/main/jni/default_ini.h | 2 +-
.../app/src/main/res/values/strings.xml | 2 +-
.../configuration/configure_layout.ui | 2 +-
.../renderer_vulkan/renderer_vulkan.cpp | 34 +++++++++++++++++--
.../renderer_vulkan/renderer_vulkan.h | 4 +++
6 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
index ba95c6aee..b8bfb4645 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -1185,8 +1185,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
"%",
FloatSetting.SECOND_SCREEN_OPACITY.key,
FloatSetting.SECOND_SCREEN_OPACITY.defaultValue,
- // TODO: Remove graphics API check when #895 is merged
- isEnabled = IntSetting.SCREEN_LAYOUT.int == 5 && IntSetting.GRAPHICS_API.int == 1
+ isEnabled = IntSetting.SCREEN_LAYOUT.int == 5
)
)
add(HeaderSetting(R.string.bg_color, R.string.bg_color_description))
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
index 481e8d927..5bedfa319 100644
--- a/src/android/app/src/main/jni/default_ini.h
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -170,7 +170,7 @@ bg_red =
bg_blue =
bg_green =
-# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer. OpenGL only.
+# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer.
custom_second_layer_opacity =
# Whether and how Stereoscopic 3D should be rendered
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 044a8b25f..c0c5d8ed3 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -455,7 +455,7 @@
Red
Green
Blue
- Custom Layout Second Screen Opacity (OpenGL Only)
+ Custom Layout Second Screen Opacity
The opacity of the second 3DS screen when using a custom screen layout. Useful if the second screen is to be positioned on top of the first screen.
Small Screen Position
Where should the small screen appear relative to the large one in Large Screen Layout?
diff --git a/src/citra_qt/configuration/configure_layout.ui b/src/citra_qt/configuration/configure_layout.ui
index e43dcbad3..1125ab2fb 100644
--- a/src/citra_qt/configuration/configure_layout.ui
+++ b/src/citra_qt/configuration/configure_layout.ui
@@ -531,7 +531,7 @@
-
- <html><head/><body><p>Bottom Screen Opacity % (OpenGL Only)</p></body></html>
+ <html><head/><body><p>Bottom Screen Opacity %</p></body></html>
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ee8b1910a..d4a63dd9b 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -376,7 +376,13 @@ void RendererVulkan::BuildPipelines() {
};
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
- .blendEnable = false,
+ .blendEnable = true,
+ .srcColorBlendFactor = vk::BlendFactor::eConstantAlpha,
+ .dstColorBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
+ .colorBlendOp = vk::BlendOp::eAdd,
+ .srcAlphaBlendFactor = vk::BlendFactor::eConstantAlpha,
+ .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
+ .alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
@@ -385,7 +391,6 @@ void RendererVulkan::BuildPipelines() {
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorblend_attachment,
- .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
};
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
@@ -398,6 +403,7 @@ void RendererVulkan::BuildPipelines() {
};
const std::array dynamic_states = {
+ vk::DynamicState::eBlendConstants,
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
@@ -729,6 +735,13 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
});
}
+void RendererVulkan::ApplySecondLayerOpacity(float alpha) {
+ scheduler.Record([alpha](vk::CommandBuffer cmdbuf) {
+ const std::array blend_constants = {0.0f, 0.0f, 0.0f, alpha};
+ cmdbuf.setBlendConstants(blend_constants.data());
+ });
+}
+
void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& top_screen) {
if (!layout.top_screen_enabled) {
@@ -867,13 +880,30 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height);
draw_info.layer = 0;
+
+ // Apply the initial default opacity value; Needed to avoid flickering
+ ApplySecondLayerOpacity(1.0f);
+
+ bool use_custom_opacity =
+ Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
+ Settings::values.custom_second_layer_opacity.GetValue() < 100;
+ float second_alpha = use_custom_opacity
+ ? Settings::values.custom_second_layer_opacity.GetValue() / 100.0f
+ : 1.0f;
+
if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
+ if (use_custom_opacity) {
+ ApplySecondLayerOpacity(second_alpha);
+ }
DrawBottomScreen(layout, bottom_screen);
} else {
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
+ if (use_custom_opacity) {
+ ApplySecondLayerOpacity(second_alpha);
+ }
DrawTopScreen(layout, top_screen);
}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 8486915e4..15807a4f3 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -98,12 +98,16 @@ private:
void DrawScreens(Frame* frame, const Layout::FramebufferLayout& layout, bool flipped);
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& bottom_screen);
+
void DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& top_screen);
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h,
Layout::DisplayOrientation orientation);
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
float h, Layout::DisplayOrientation orientation);
+
+ void ApplySecondLayerOpacity(float alpha);
+
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
bool right_eye);
void FillScreen(Common::Vec3 color, const TextureInfo& texture);