Compare commits

...

6 commits

18 changed files with 374 additions and 89 deletions

View file

@ -28,6 +28,17 @@ QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#StatusBarButton {
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#StatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#button_reset_defaults {
min-width: 57px;
padding: 4px 8px;

View file

@ -43,6 +43,9 @@ object SettingKeys {
external fun frame_limit(): String
external fun turbo_limit(): String
external fun texture_filter(): String
external fun antialiasing_filter(): String
external fun fsr_sharpness(): String
external fun output_scaling(): String
external fun texture_sampling(): String
external fun delay_game_render_thread_us(): String
external fun layout_option(): String

View file

@ -48,6 +48,9 @@ enum class IntSetting(
PORTRAIT_BOTTOM_HEIGHT(SettingKeys.custom_portrait_bottom_height(),Settings.SECTION_LAYOUT,480),
AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0),
CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100),
ANTIALIASING_FILTER(SettingKeys.antialiasing_filter(), Settings.SECTION_RENDERER, 0),
FSR_SHARPNESS(SettingKeys.fsr_sharpness(), Settings.SECTION_RENDERER, 50),
OUTPUT_SCALING(SettingKeys.output_scaling(), Settings.SECTION_RENDERER, 2),
TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0),
TEXTURE_SAMPLING(SettingKeys.texture_sampling(), Settings.SECTION_RENDERER, 0),
USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, 1),

View file

@ -922,14 +922,48 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
)
add(
SwitchSetting(
BooleanSetting.LINEAR_FILTERING,
R.string.linear_filtering,
R.string.linear_filtering_description,
BooleanSetting.LINEAR_FILTERING.key,
BooleanSetting.LINEAR_FILTERING.defaultValue
SingleChoiceSetting(
IntSetting.OUTPUT_SCALING,
R.string.output_scaling_name,
R.string.output_scaling_description,
R.array.outputScalingNames,
R.array.outputScalingValues,
IntSetting.OUTPUT_SCALING.key,
IntSetting.OUTPUT_SCALING.defaultValue
)
)
add(
SliderSetting(
IntSetting.FSR_SHARPNESS,
R.string.fsr_sharpness_name,
R.string.fsr_sharpness_description,
0,
100,
"%",
IntSetting.FSR_SHARPNESS.key,
IntSetting.FSR_SHARPNESS.defaultValue.toFloat()
)
)
add(
SingleChoiceSetting(
IntSetting.ANTIALIASING_FILTER,
R.string.antialiasing_filter_name,
R.string.antialiasing_filter_description,
R.array.antialiasingFilterNames,
R.array.antialiasingFilterValues,
IntSetting.ANTIALIASING_FILTER.key,
IntSetting.ANTIALIASING_FILTER.defaultValue
)
)
// add(
// SwitchSetting(
// BooleanSetting.LINEAR_FILTERING,
// R.string.linear_filtering,
// R.string.linear_filtering_description,
// BooleanSetting.LINEAR_FILTERING.key,
// BooleanSetting.LINEAR_FILTERING.defaultValue
// )
// )
add(
SwitchSetting(
BooleanSetting.SHADERS_ACCURATE_MUL,

View file

@ -153,6 +153,9 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.use_vsync);
ReadSetting("Renderer", Settings::values.texture_filter);
ReadSetting("Renderer", Settings::values.output_scaling);
ReadSetting("Renderer", Settings::values.fsr_sharpness);
ReadSetting("Renderer", Settings::values.antialiasing_filter);
ReadSetting("Renderer", Settings::values.texture_sampling);
ReadSetting("Renderer", Settings::values.turbo_limit);
// Workaround to map Android setting for enabling the frame limiter to the format Citra expects

View file

@ -187,6 +187,17 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# Loaded from shaders/anaglyph
)") DECLARE_KEY(anaglyph_shader_name) BOOST_HANA_STRING(R"(
# Scaling mode for image output
# 0: Nearest, 1: Bilinear, 2 (default): Adaptive, 3: FSR 1, 4: Sharp Bilinear
)") DECLARE_KEY(output_scaling) BOOST_HANA_STRING(R"(
# Antialiasing filter to use
# 0 (default): None, 1: FXAA, 2: SMAA
)") DECLARE_KEY(antialiasing_filter) BOOST_HANA_STRING(R"(
# 0 - 100: Strength of FSR Sharpening. 50 (default)
)") DECLARE_KEY(fsr_sharpness) BOOST_HANA_STRING(R"(
# Whether to enable linear filtering or not
# This is required for some shaders to work correctly
# 0: Nearest, 1 (default): Linear

View file

@ -297,6 +297,34 @@
<item>5</item>
</integer-array>
<string-array name="outputScalingNames">
<item>@string/nearest</item>
<item>@string/bilinear</item>
<item>@string/adaptive</item>
<item>@string/fsr</item>
<item>@string/sharpbilinear</item>
</string-array>
<integer-array name="outputScalingValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</integer-array>
<string-array name="antialiasingFilterNames">
<item>@string/none</item>
<item>@string/fxaa</item>
<item>@string/smaa</item>
</string-array>
<integer-array name="antialiasingFilterValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="textureSamplingNames">
<item>@string/game_controlled</item>
<item>@string/nearest_neighbor</item>

View file

@ -262,6 +262,12 @@
<string name="use_integer_scaling_description">Scales the screens with an integer multiplier of the original 3DS screen. For layouts with two different screen sizes, the largest screen is integer-scaled.</string>
<string name="texture_filter_name">Texture Filter</string>
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
<string name="antialiasing_filter_name">Antialiasing Filter</string>
<string name="antialiasing_filter_description">Reduces the amount of aliasing, resulting in smoother edges. FXAA is extremely lightweight but more aggressive and lower quality. SMAA is heavier but is higher quality and more selective with the edges it smooths.</string>
<string name="output_scaling_name">Output Scaling</string>
<string name="output_scaling_description">The method by which the game output is scaled to the screen. Nearest doesn\'t smooth visuals but causes uneven pixels at non-integer ratios. Bilinear smooths visuals but may cause aliasing when downscaling. Adaptive uses bilinear for upscaling, but uses area sampling to downscale without creating aliasing. FSR 1 is an upscaler which allows sharpening and allows better visuals without increasing the internal resolution, but needs to be used with antialiasing and a minimum internal resolution of 2x Native. Sharp Bilinear looks similar to nearest but is slightly smoothed to prevent uneven pixels at non-integer ratios.</string>
<string name="fsr_sharpness_name">FSR Sharpness</string>
<string name="fsr_sharpness_description">Specifies the strength of the sharpening pass when using FSR</string>
<string name="delay_render_thread">Delay Game Render Thread</string>
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string>
<string name="advanced">Advanced</string>
@ -764,6 +770,18 @@
<string name="xbrz">xBRZ</string>
<string name="mmpx">MMPX</string>
<!-- Output scaling names -->
<string name="nearest">Nearest</string>
<string name="bilinear">Bilinear</string>
<string name="adaptive">Adaptive</string>
<string name="fsr">AMD FSR 1</string>
<string name="sharpbilinear">Sharp Bilinear</string>
<!-- Antialiasing names -->
<!-- None is already defined -->
<string name="fxaa">FXAA</string>
<string name="smaa">SMAA</string>
<!-- Texture Sampling names -->
<string name="game_controlled">Game Controlled</string>
<string name="nearest_neighbor">Nearest Neighbor</string>

View file

@ -628,6 +628,23 @@ void GMainWindow::InitializeWidgets() {
statusBar()->insertPermanentWidget(0, graphics_api_button);
// Setup Output Scaling button
output_scaling_button = new QPushButton();
output_scaling_button->setObjectName(QStringLiteral("StatusBarButton"));
output_scaling_button->setFocusPolicy(Qt::NoFocus);
UpdateOutputScalingIndicator();
connect(output_scaling_button, &QPushButton::clicked, this, [this] { UpdateOutputScalingIndicator(true); });
statusBar()->insertPermanentWidget(1, output_scaling_button);
// Setup Antialiasing Filter button
antialiasing_filter_button = new QPushButton();
antialiasing_filter_button->setObjectName(QStringLiteral("StatusBarButton"));
antialiasing_filter_button->setFocusPolicy(Qt::NoFocus);
UpdateAntialiasingFilterIndicator();
connect(antialiasing_filter_button, &QPushButton::clicked, this, [this] { UpdateAntialiasingFilterIndicator(true); });
statusBar()->insertPermanentWidget(2, antialiasing_filter_button);
// Setup Volume button and slider
volume_popup = new QWidget(this);
volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup);
volume_popup->setLayout(new QVBoxLayout());
@ -658,7 +675,7 @@ void GMainWindow::InitializeWidgets() {
bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height());
volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height())));
});
statusBar()->insertPermanentWidget(1, volume_button);
statusBar()->insertPermanentWidget(3, volume_button);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
@ -3821,8 +3838,45 @@ void GMainWindow::UpdateAPIIndicator(bool update) {
graphics_api_button->setStyleSheet(style_sheet);
}
void GMainWindow::UpdateOutputScalingIndicator(bool update) {
static std::array output_scaling_options = {
QStringLiteral("NEAREST"),
QStringLiteral("BILINEAR"),
QStringLiteral("ADAPTIVE"),
QStringLiteral("FSR"),
QStringLiteral("SHARP BILINEAR"),
};
u32 selection_index = static_cast<u32>(Settings::values.output_scaling.GetValue());
if (update) {
selection_index = (selection_index + 1) % output_scaling_options.size();
Settings::values.output_scaling = static_cast<Settings::OutputScaling>(selection_index);
}
output_scaling_button->setText(output_scaling_options[selection_index]);
}
void GMainWindow::UpdateAntialiasingFilterIndicator(bool update) {
static std::array antialiasing_filter_options = {
QStringLiteral("NO AA"),
QStringLiteral("FXAA"),
QStringLiteral("SMAA"),
};
u32 selection_index = static_cast<u32>(Settings::values.antialiasing_filter.GetValue());
if (update) {
selection_index = (selection_index + 1) % antialiasing_filter_options.size();
Settings::values.antialiasing_filter = static_cast<Settings::AntiAliasingFilter>(selection_index);
}
antialiasing_filter_button->setText(antialiasing_filter_options[selection_index]);
}
void GMainWindow::UpdateStatusButtons() {
UpdateAPIIndicator();
UpdateOutputScalingIndicator();
UpdateAntialiasingFilterIndicator();
UpdateVolumeUI();
}

View file

@ -333,6 +333,8 @@ private:
void OpenPerGameConfiguration(u64 title_id, const QString& file_name);
void UpdateVolumeUI();
void UpdateAPIIndicator(bool update = false);
void UpdateOutputScalingIndicator(bool update = false);
void UpdateAntialiasingFilterIndicator(bool update = false);
void UpdateStatusButtons();
#ifdef __unix__
void SetGamemodeEnabled(bool state);
@ -359,6 +361,8 @@ private:
QLabel* emu_frametime_label = nullptr;
QPushButton* graphics_api_button = nullptr;
QPushButton* volume_button = nullptr;
QPushButton* output_scaling_button = nullptr;
QPushButton* antialiasing_filter_button = nullptr;
QWidget* volume_popup = nullptr;
QSlider* volume_slider = nullptr;
QTimer status_bar_update_timer;

View file

@ -199,7 +199,7 @@ AF1 outputSizeInPixelsY){
con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY));
con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX));
con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY));
con3[2]=con3[3]=0;}
con3[2]=con3[3]=0u;}
//If the an offset into the input image resource
A_STATIC void FsrEasuConOffset(
@ -668,8 +668,8 @@ AF1 sharpness){
varAF2(hSharp)=initAF2(sharpness,sharpness);
con[0]=AU1_AF1(sharpness);
con[1]=AU1_AH2_AF2(hSharp);
con[2]=0;
con[3]=0;}
con[2]=0u;
con[3]=0u;}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//_____________________________________________________________/\_______________________________________________________________

View file

@ -52,8 +52,8 @@ vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords) {
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x - 0.5);
int y_range = int(f_end.y - f_beg.y - 0.5);
int x_range = int(float(f_end.x) - float(f_beg.x) - 0.5);
int y_range = int(float(f_end.y) - float(f_beg.y) - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {

View file

@ -138,6 +138,7 @@ void RendererOpenGL::SwapBuffers() {
render_window.SwapBuffers();
#else
const auto& main_layout = render_window.GetFramebufferLayout();
isSecondaryWindow = false;
RenderToMailbox(main_layout, render_window.mailbox, false);
#ifdef ANDROID
@ -145,6 +146,7 @@ void RendererOpenGL::SwapBuffers() {
// it means we have a second display
if (secondary_window) {
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents();
}
@ -152,6 +154,7 @@ void RendererOpenGL::SwapBuffers() {
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
ASSERT(secondary_window);
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents();
}
@ -412,17 +415,24 @@ void RendererOpenGL::AllocatePPTextures(){
}
void RendererOpenGL::AllocateOutputSizeTextures(){
for (int i = 0; i < intermediateOutputSizeTextures.size(); i++){
if (currOutputScreenRects[i].GetHeight() != 0 && currOutputScreenRects[i].GetWidth() != 0){
for (int j = 0; j < intermediateOutputSizeTextures[0].size(); j++){
intermediateOutputSizeTextures[i][j].Release();
intermediateOutputSizeTextures[i][j].Create();
intermediateOutputSizeTextures[i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currOutputScreenRects[i].GetWidth(), currOutputScreenRects[i].GetHeight());
for (int i = 0; i < intermediateOutputSizeTextures[isSecondaryWindow].size(); i++){
if (currOutputScreenRects[isSecondaryWindow][i].GetHeight() != 0 && currOutputScreenRects[isSecondaryWindow][i].GetWidth() != 0){
for (int j = 0; j < intermediateOutputSizeTextures[isSecondaryWindow][0].size(); j++){
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Release();
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Create();
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currOutputScreenRects[isSecondaryWindow][i].GetWidth(), currOutputScreenRects[isSecondaryWindow][i].GetHeight());
}
}
}
LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures");
LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures.\nPrevRects:\n{}x{}\n{}x{}\n{}x{}\nCurrRects:\n{}x{}\n{}x{}\n{}x{}",
prevOutputScreenRects[isSecondaryWindow][0].GetWidth(), prevOutputScreenRects[isSecondaryWindow][0].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][1].GetWidth(), prevOutputScreenRects[isSecondaryWindow][1].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][2].GetWidth(), prevOutputScreenRects[isSecondaryWindow][2].GetHeight(),
currOutputScreenRects[isSecondaryWindow][0].GetWidth(), currOutputScreenRects[isSecondaryWindow][0].GetHeight(),
currOutputScreenRects[isSecondaryWindow][1].GetWidth(), currOutputScreenRects[isSecondaryWindow][1].GetHeight(),
currOutputScreenRects[isSecondaryWindow][2].GetWidth(), currOutputScreenRects[isSecondaryWindow][2].GetHeight()
);
}
/**
@ -589,7 +599,9 @@ void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) {
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART3_FRAG;
FSR_PASS_1_shader.Create(HostShaders::OPENGL_FSR_PASS1_VERT, FSR_PASS_1_shader_frag_data);
SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, HostShaders::OPENGL_SHARPBILINEAR_FRAG);
std::string SharpBilinear_shader_frag_data = fragment_shader_precision_OES;
SharpBilinear_shader_frag_data += HostShaders::OPENGL_SHARPBILINEAR_FRAG;
SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, SharpBilinear_shader_frag_data);
state.Apply();
if (render_3d == Settings::StereoRenderOption::Anaglyph ||
@ -1085,7 +1097,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[currOutputScreen][0].handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_0_shader.handle;
state.Apply();
@ -1104,12 +1116,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[currOutputScreen][1].handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_1_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[currOutputScreen][0].handle;
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1f(uniform_fsr_sharpening, fsr_sharpening);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
@ -1129,7 +1141,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.draw.shader_program = Present_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[currOutputScreen][1].handle;
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
@ -1304,15 +1316,21 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
prevBottomTextureHeight = currBottomTextureHeight;
//Track Layout Changes
currOutputScreenRects[0] = layout.top_screen;
currOutputScreenRects[1] = layout.bottom_screen;
currOutputScreenRects[2] = layout.additional_screen;
if (currOutputScreenRects[0] != prevOutputScreenRects[0] || currOutputScreenRects[1] != prevOutputScreenRects[1] || currOutputScreenRects[2] != prevOutputScreenRects[2]){
AllocateOutputSizeTextures();
currOutputScreenRects[isSecondaryWindow][0] = layout.top_screen;
currOutputScreenRects[isSecondaryWindow][1] = layout.bottom_screen;
currOutputScreenRects[isSecondaryWindow][2] = layout.additional_screen;
if (currOutputScreenRects[isSecondaryWindow][0] != prevOutputScreenRects[isSecondaryWindow][0] || currOutputScreenRects[isSecondaryWindow][1] != prevOutputScreenRects[isSecondaryWindow][1]){
if (layout.additional_screen_enabled){
if (currOutputScreenRects[isSecondaryWindow][2] != prevOutputScreenRects[isSecondaryWindow][2]){
AllocateOutputSizeTextures();
}
} else {
AllocateOutputSizeTextures();
}
}
prevOutputScreenRects[0] = currOutputScreenRects[0];
prevOutputScreenRects[1] = currOutputScreenRects[1];
prevOutputScreenRects[2] = currOutputScreenRects[2];
prevOutputScreenRects[isSecondaryWindow][0] = currOutputScreenRects[isSecondaryWindow][0];
prevOutputScreenRects[isSecondaryWindow][1] = currOutputScreenRects[isSecondaryWindow][1];
prevOutputScreenRects[isSecondaryWindow][2] = currOutputScreenRects[isSecondaryWindow][2];
//Set the Viewport
state.viewport.x = 0;

View file

@ -116,9 +116,9 @@ private:
std::array<OGLTexture, 2> antialiasFBOTexture;
//Intermediate Textures at output size. These are for Top Screen, Bottom Screen and Additional Screen Respectively
std::array<std::array<OGLTexture, 3>, 3> intermediateOutputSizeTextures;
std::array<Common::Rectangle<u32>, 3> prevOutputScreenRects;
std::array<Common::Rectangle<u32>, 3> currOutputScreenRects;
std::array<std::array<std::array<OGLTexture, 3>, 3>, 2> intermediateOutputSizeTextures;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> prevOutputScreenRects;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> currOutputScreenRects;
int currOutputScreen;
OGLTexture areatex;
OGLTexture searchtex;
@ -158,6 +158,10 @@ private:
float currBottomTextureWidth;
float currBottomTextureHeight;
std::array<int, 4> originalViewport;
// Secondary Layout Fix
bool isSecondaryWindow;
};
} // namespace OpenGL

View file

@ -294,10 +294,8 @@ void RendererVulkan::CreateTextureRenderPass(){
}
void RendererVulkan::AllocateTexture(TextureInfo& texture, int width, int height, vk::Format colorFormat){
if (width == 0 || height == 0){
textureReallocationNeeded = true;
return;
}
width = std::max(width, 10);
height = std::max(height, 10);
vk::Device device = instance.GetDevice();
if (texture.image_view) {
device.destroyImageView(texture.image_view);
@ -338,7 +336,7 @@ void RendererVulkan::AllocateTexture(TextureInfo& texture, int width, int height
LOG_CRITICAL(Render_Vulkan, "Failed allocating regular texture ({}x{}) with error {}", texture.width, texture.height, result);
UNREACHABLE();
} else {
LOG_INFO(Render_Vulkan, "Successfully allocated regular texture");
// LOG_INFO(Render_Vulkan, "Successfully allocated regular texture");
}
texture.image = vk::Image{unsafe_image};
@ -398,7 +396,7 @@ void RendererVulkan::AllocateStagedTexture(StagedTextureInfo& texture, int width
LOG_CRITICAL(Render_Vulkan, "Failed allocating regular texture ({}x{}) with error {}", texture.width, texture.height, result);
UNREACHABLE();
} else {
LOG_INFO(Render_Vulkan, "Successfully allocated regular texture");
// LOG_INFO(Render_Vulkan, "Successfully allocated regular texture");
}
texture.image = vk::Image{unsafe_image};
@ -579,22 +577,31 @@ void RendererVulkan::AllocatePPTextures(){
}
AllocateTexture(antialiasTextures[0], currTopTextureWidth, currTopTextureHeight, vk::Format::eR16G16B16A16Sfloat);
AllocateTexture(antialiasTextures[1], currBottomTextureWidth, currBottomTextureHeight, vk::Format::eR16G16B16A16Sfloat);
LOG_INFO(Render_Vulkan, "Reallocated Post Processing Textures");
};
void RendererVulkan::AllocateOutputSizeTextures(){
for (int i = 0; i < intermediateOutputSizeTextures.size(); i++){
for (int j = 0; j < intermediateOutputSizeTextures[0].size(); j++){
AllocateTexture(intermediateOutputSizeTextures[i][j], currOutputScreenRects[i].GetWidth(), currOutputScreenRects[i].GetHeight(), vk::Format::eR16G16B16A16Sfloat);
for (int i = 0; i < intermediateOutputSizeTextures[isSecondaryWindow].size(); i++){
for (int j = 0; j < intermediateOutputSizeTextures[isSecondaryWindow][0].size(); j++){
AllocateTexture(intermediateOutputSizeTextures[isSecondaryWindow][i][j], currOutputScreenRects[isSecondaryWindow][i].GetWidth(), currOutputScreenRects[isSecondaryWindow][i].GetHeight(), vk::Format::eR16G16B16A16Sfloat);
}
}
LOG_INFO(Render_Vulkan, "Reallocated OutputSize Textures");
// LOG_INFO(Render_Vulkan, "Reallocated Output Size Textures");
LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures.\nPrevRects:\n{}x{}\n{}x{}\n{}x{}\nCurrRects:\n{}x{}\n{}x{}\n{}x{}",
prevOutputScreenRects[isSecondaryWindow][0].GetWidth(), prevOutputScreenRects[isSecondaryWindow][0].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][1].GetWidth(), prevOutputScreenRects[isSecondaryWindow][1].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][2].GetWidth(), prevOutputScreenRects[isSecondaryWindow][2].GetHeight(),
currOutputScreenRects[isSecondaryWindow][0].GetWidth(), currOutputScreenRects[isSecondaryWindow][0].GetHeight(),
currOutputScreenRects[isSecondaryWindow][1].GetWidth(), currOutputScreenRects[isSecondaryWindow][1].GetHeight(),
currOutputScreenRects[isSecondaryWindow][2].GetWidth(), currOutputScreenRects[isSecondaryWindow][2].GetHeight()
);
};
void RendererVulkan::CreateOutputSizeTextureFramebuffers(){
for (int i = 0; i < intermediateOutputSizeTextures.size(); i++){
if (currOutputScreenRects[i].GetHeight() != 0 && currOutputScreenRects[i].GetWidth() != 0){
for (int j = 0; j < intermediateOutputSizeTextures[0].size(); j++){
CreateTextureFramebuffer(intermediateOutputSizeTextures[i][j], intermediateOutputSizeTextureFBOs[i][j]);
for (int i = 0; i < intermediateOutputSizeTextures[isSecondaryWindow].size(); i++){
if (currOutputScreenRects[isSecondaryWindow][i].GetHeight() != 0 && currOutputScreenRects[isSecondaryWindow][i].GetWidth() != 0){
for (int j = 0; j < intermediateOutputSizeTextures[isSecondaryWindow][0].size(); j++){
CreateTextureFramebuffer(intermediateOutputSizeTextures[isSecondaryWindow][i][j], intermediateOutputSizeTextureFBOs[isSecondaryWindow][i][j]);
}
}
}
@ -1640,7 +1647,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr
Draw(vertexBufferPointers[currentPass], drawInfos[currentPass]);
currentPass++;
// Edge Detection
// Edge Detection
texturesToSample.assign({intermediateTextures[currScreen][0]});
PrepareTextureDrawFromTextureInfo(intermediateTextures[currScreen][1], intermediateTextureFBOs[currScreen][1], post_pipelines_texture[2], texturesToSample, 1);
UpdateVertexBuffer(pass_through_vertices, vertexBufferPointers[currentPass]);
@ -1731,7 +1738,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr
} else {
// EASU (to output resolution)
texturesToSample.assign({antialiasTextures[currScreen]});
PrepareTextureDrawFromTextureInfo(intermediateOutputSizeTextures[currScreen][0], intermediateOutputSizeTextureFBOs[currScreen][0], post_pipelines_texture[5], texturesToSample, 0);
PrepareTextureDrawFromTextureInfo(intermediateOutputSizeTextures[isSecondaryWindow][currScreen][0], intermediateOutputSizeTextureFBOs[isSecondaryWindow][currScreen][0], post_pipelines_texture[5], texturesToSample, 0);
UpdateVertexBuffer(pass_through_vertices, vertexBufferPointers[currentPass]);
drawInfos[currentPass].i_resolution = Common::Vec4f{textureWidth, textureHeight, 1.0f/ textureWidth, 1.0f / textureHeight};
drawInfos[currentPass].o_resolution = Common::Vec4f{screenWidth, screenHeight, 1.0f/ screenWidth, 1.0f / screenHeight};
@ -1739,8 +1746,8 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr
currentPass++;
// RCAS
texturesToSample.assign({intermediateOutputSizeTextures[currScreen][0]});
PrepareTextureDrawFromTextureInfo(intermediateOutputSizeTextures[currScreen][1], intermediateOutputSizeTextureFBOs[currScreen][1], post_pipelines_texture[6], texturesToSample, 1);
texturesToSample.assign({intermediateOutputSizeTextures[isSecondaryWindow][currScreen][0]});
PrepareTextureDrawFromTextureInfo(intermediateOutputSizeTextures[isSecondaryWindow][currScreen][1], intermediateOutputSizeTextureFBOs[isSecondaryWindow][currScreen][1], post_pipelines_texture[6], texturesToSample, 1);
UpdateVertexBuffer(pass_through_vertices, vertexBufferPointers[currentPass]);
drawInfos[currentPass].FSR_SHARPENING = fsr_sharpening;
drawInfos[currentPass].o_resolution = Common::Vec4f{screenWidth, screenHeight, 1.0f/ screenWidth, 1.0f / screenHeight};
@ -1748,7 +1755,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr
currentPass++;
// Normal Present
texturesToSample.assign({intermediateOutputSizeTextures[currScreen][1]});
texturesToSample.assign({intermediateOutputSizeTextures[isSecondaryWindow][currScreen][1]});
PrepareDrawFromTextureInfo(currentFrame, currentFramebufferLayout, present_pipelines[current_pipeline], texturesToSample, 1);
UpdateVertexBuffer(output_vertices, vertexBufferPointers[currentPass]);
drawInfos[currentPass].convert_colors = 0;
@ -1791,6 +1798,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr
// UpdateVertexBuffer(output_vertices, vertexBufferPointers[1]);
// drawInfos[1].convert_colors = 2;
// Draw(vertexBufferPointers[1], drawInfos[1]);
}
@ -2042,11 +2050,10 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
currTopTextureHeight = static_cast<float>(screen_infos[0].texture.width * GetResolutionScaleFactor());
currBottomTextureWidth = static_cast<float>(screen_infos[2].texture.height * GetResolutionScaleFactor());
currBottomTextureHeight = static_cast<float>(screen_infos[2].texture.width * GetResolutionScaleFactor());
if (currTopTextureWidth != prevTopTextureWidth || currTopTextureHeight != prevTopTextureHeight || currBottomTextureWidth != prevBottomTextureWidth || currBottomTextureHeight != prevBottomTextureHeight || textureReallocationNeeded){
if (currTopTextureWidth != prevTopTextureWidth || currTopTextureHeight != prevTopTextureHeight || currBottomTextureWidth != prevBottomTextureWidth || currBottomTextureHeight != prevBottomTextureHeight){
AllocatePPTextures();
CreatePPTextureFramebuffers();
textureReallocationNeeded = false;
LOG_INFO(Render_Vulkan, "PrevTopTexture Res: {}x{}, CurrTopTexture Res: {}x{}, PrevBottomTexture Res: {}x{}, CurrBottomTexture Res: {}x{}", prevTopTextureWidth, prevTopTextureHeight, currTopTextureWidth, currTopTextureHeight, prevBottomTextureWidth, prevBottomTextureHeight, currBottomTextureWidth, currBottomTextureHeight);
// LOG_INFO(Render_Vulkan, "PrevTopTexture Res: {}x{}, CurrTopTexture Res: {}x{}, PrevBottomTexture Res: {}x{}, CurrBottomTexture Res: {}x{}", prevTopTextureWidth, prevTopTextureHeight, currTopTextureWidth, currTopTextureHeight, prevBottomTextureWidth, prevBottomTextureHeight, currBottomTextureWidth, currBottomTextureHeight);
}
prevTopTextureWidth = currTopTextureWidth;
prevTopTextureHeight = currTopTextureHeight;
@ -2054,16 +2061,25 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
prevBottomTextureHeight = currBottomTextureHeight;
//Track Layout Changes
currOutputScreenRects[0] = layout.top_screen;
currOutputScreenRects[1] = layout.bottom_screen;
currOutputScreenRects[2] = layout.additional_screen;
if (currOutputScreenRects[0] != prevOutputScreenRects[0] || currOutputScreenRects[1] != prevOutputScreenRects[1] || currOutputScreenRects[2] != prevOutputScreenRects[2]){
AllocateOutputSizeTextures();
CreateOutputSizeTextureFramebuffers();
currOutputScreenRects[isSecondaryWindow][0] = layout.top_screen;
currOutputScreenRects[isSecondaryWindow][1] = layout.bottom_screen;
currOutputScreenRects[isSecondaryWindow][2] = layout.additional_screen;
if (currOutputScreenRects[isSecondaryWindow][0] != prevOutputScreenRects[isSecondaryWindow][0] || currOutputScreenRects[isSecondaryWindow][1] != prevOutputScreenRects[isSecondaryWindow][1]){
if (layout.additional_screen_enabled){
if (currOutputScreenRects[isSecondaryWindow][2] != prevOutputScreenRects[isSecondaryWindow][2]){
AllocateOutputSizeTextures();
CreateOutputSizeTextureFramebuffers();
}
} else {
AllocateOutputSizeTextures();
CreateOutputSizeTextureFramebuffers();
}
}
prevOutputScreenRects[0] = currOutputScreenRects[0];
prevOutputScreenRects[1] = currOutputScreenRects[1];
prevOutputScreenRects[2] = currOutputScreenRects[2];
prevOutputScreenRects[isSecondaryWindow][0] = currOutputScreenRects[isSecondaryWindow][0];
prevOutputScreenRects[isSecondaryWindow][1] = currOutputScreenRects[isSecondaryWindow][1];
prevOutputScreenRects[isSecondaryWindow][2] = currOutputScreenRects[isSecondaryWindow][2];
currentFrame = frame;
currentFramebufferLayout = layout;
@ -2074,24 +2090,76 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
clearingColorAttachment = true;
applyingOpacity = true;
if (!Settings::values.swap_screen.GetValue()) {
drawingPrimaryScreen = true;
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = false;
clearingColorAttachment = false;
DrawBottomScreen(layout, bottom_screen);
if (!secondaryWindowEnabled){
if (!Settings::values.swap_screen.GetValue()) {
drawingPrimaryScreen = true;
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = false;
clearingColorAttachment = false;
DrawBottomScreen(layout, bottom_screen);
} else {
drawingPrimaryScreen = true;
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = true;
clearingColorAttachment = false;
DrawTopScreen(layout, top_screen);
}
} else {
drawingPrimaryScreen = true;
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = true;
clearingColorAttachment = false;
DrawTopScreen(layout, top_screen);
if (!Settings::values.swap_screen.GetValue()) {
if (usingAndroid){
// Change this block to conditionally display based on layout of primary and secondary screen
// if (isSecondaryWindow){
// return;
// } else {
drawingPrimaryScreen = true;
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = false;
clearingColorAttachment = false;
DrawBottomScreen(layout, bottom_screen);
// }
} else {
if (isSecondaryWindow) {
drawingPrimaryScreen = true;
DrawBottomScreen(layout, bottom_screen);
} else {
drawingPrimaryScreen = true;
DrawTopScreen(layout, top_screen);
}
}
} else {
if (usingAndroid){
// Change this block to conditionally display based on layout of primary and secondary screen
// if (isSecondaryWindow){
// return;
// } else {
drawingPrimaryScreen = true;
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
drawingPrimaryScreen = false;
usingTopOpacity = true;
clearingColorAttachment = false;
DrawTopScreen(layout, top_screen);
// }
} else {
if (isSecondaryWindow) {
drawingPrimaryScreen = true;
DrawTopScreen(layout, top_screen);
} else {
drawingPrimaryScreen = true;
DrawBottomScreen(layout, bottom_screen);
}
}
}
}
applyingOpacity = false;
if (layout.additional_screen_enabled) {
const auto& additional_screen = layout.additional_screen;
@ -2168,9 +2236,27 @@ void RendererVulkan::DrawCursor(const Layout::FramebufferLayout& layout) {
void RendererVulkan::SwapBuffers() {
system.perf_stats->StartSwap();
#ifndef ANDROID
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
ASSERT(secondary_window);
secondaryWindowEnabled = true;
} else {
secondaryWindowEnabled = false;
}
#endif
#ifdef ANDROID
usingAndroid = true;
if (secondary_window) {
secondaryWindowEnabled = true;
} else {
secondaryWindowEnabled = false;
}
#endif
const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout();
PrepareRendertarget();
RenderScreenshot();
isSecondaryWindow = false;
RenderToWindow(main_present_window, layout, false);
#ifndef ANDROID
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
@ -2180,6 +2266,7 @@ void RendererVulkan::SwapBuffers() {
secondary_present_window_ptr = std::make_unique<PresentWindow>(
*secondary_window, instance, scheduler, IsLowRefreshRate());
}
isSecondaryWindow = true;
RenderToWindow(*secondary_present_window_ptr, secondary_layout, false);
secondary_window->PollEvents();
}
@ -2192,6 +2279,7 @@ void RendererVulkan::SwapBuffers() {
secondary_present_window_ptr = std::make_unique<PresentWindow>(
*secondary_window, instance, scheduler, IsLowRefreshRate());
}
isSecondaryWindow = true;
RenderToWindow(*secondary_present_window_ptr, secondary_layout, false);
secondary_window->PollEvents();
}

View file

@ -238,10 +238,10 @@ private:
// Array of framebuffer objects. 0 is top screen, 1 is bottom screen.
std::array<std::array<vk::Framebuffer, 7>, 2> intermediateTextureFBOs;
std::array<vk::Framebuffer, 2 > antialiasTextureFBOs;
std::array<std::array<TextureInfo, 3>, 3> intermediateOutputSizeTextures;
std::array<std::array<vk::Framebuffer, 3>, 3> intermediateOutputSizeTextureFBOs;
std::array<Common::Rectangle<u32>, 3> prevOutputScreenRects;
std::array<Common::Rectangle<u32>, 3> currOutputScreenRects;
std::array<std::array<std::array<TextureInfo, 3>, 3>, 2> intermediateOutputSizeTextures;
std::array<std::array<std::array<vk::Framebuffer, 3>, 3>, 2> intermediateOutputSizeTextureFBOs;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> prevOutputScreenRects;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> currOutputScreenRects;
int currOutputScreen;
float currTopTextureWidth;
float currTopTextureHeight;
@ -259,7 +259,6 @@ private:
bool applyingOpacity = true;
bool drawingPrimaryScreen = false;
bool usingTopOpacity = false;
bool textureReallocationNeeded = false;
std::array<ScreenInfo, 3> screen_infos{};
PresentUniformData draw_info{};
vk::ClearColorValue clear_color{};
@ -267,6 +266,11 @@ private:
vk::ShaderModule cursor_fragment_shader{};
vk::Pipeline cursor_pipeline{};
vk::UniquePipelineLayout cursor_pipeline_layout{};
// Secondary Layout Fix
bool isSecondaryWindow;
bool secondaryWindowEnabled;
bool usingAndroid;
};
} // namespace Vulkan

View file

@ -490,7 +490,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
vk::RenderPass PresentWindow::CreateRenderpass() {
const vk::AttachmentReference color_ref = {
.attachment = 0,
.layout = vk::ImageLayout::eGeneral,
.layout = vk::ImageLayout::eColorAttachmentOptimal,
};
const vk::SubpassDescription subpass = {
@ -537,7 +537,7 @@ vk::RenderPass PresentWindow::CreateRenderpass() {
vk::RenderPass PresentWindow::CreateLoadRenderpass() {
const vk::AttachmentReference color_ref = {
.attachment = 0,
.layout = vk::ImageLayout::eGeneral,
.layout = vk::ImageLayout::eColorAttachmentOptimal,
};
const vk::SubpassDescription subpass = {

View file

@ -6,15 +6,17 @@
// High precision may or may not be supported in GLES3. If it isn't, use medium precision instead.
static constexpr char fragment_shader_precision_OES[] = R"(
#if GL_ES
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp int;
precision highp float;
precision highp sampler2D;
precision highp samplerBuffer;
precision highp uimage2D;
#else
precision mediump int;
precision mediump float;
precision highp sampler2D;
precision mediump samplerBuffer;
precision mediump uimage2D;
#endif // GL_FRAGMENT_PRECISION_HIGH