From d4de04584f14f3ea8fde4cd79102b887c084fbc2 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 15 Jan 2024 15:08:21 -0500 Subject: [PATCH] renderer_opengl: split up blit screen resources into antialias and window adapt passes --- src/video_core/CMakeLists.txt | 4 + .../renderer_opengl/gl_blit_screen.cpp | 278 +++++------------- .../renderer_opengl/gl_blit_screen.h | 26 +- .../renderer_opengl/present/filters.cpp | 39 +++ .../renderer_opengl/present/filters.h | 17 ++ .../renderer_opengl/present/fxaa.cpp | 1 + .../renderer_opengl/present/smaa.cpp | 6 - src/video_core/renderer_opengl/present/util.h | 11 + .../present/window_adapt_pass.cpp | 128 ++++++++ .../present/window_adapt_pass.h | 39 +++ .../renderer_opengl/renderer_opengl.cpp | 6 + .../present/window_adapt_pass.cpp | 6 +- .../renderer_vulkan/renderer_vulkan.cpp | 3 + 13 files changed, 332 insertions(+), 232 deletions(-) create mode 100644 src/video_core/renderer_opengl/present/filters.cpp create mode 100644 src/video_core/renderer_opengl/present/filters.h create mode 100644 src/video_core/renderer_opengl/present/window_adapt_pass.cpp create mode 100644 src/video_core/renderer_opengl/present/window_adapt_pass.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 9879c3ad7..c6b0d628d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -116,6 +116,8 @@ add_library(video_core STATIC renderer_null/null_rasterizer.h renderer_null/renderer_null.cpp renderer_null/renderer_null.h + renderer_opengl/present/filters.cpp + renderer_opengl/present/filters.h renderer_opengl/present/fsr.cpp renderer_opengl/present/fsr.h renderer_opengl/present/fxaa.cpp @@ -123,6 +125,8 @@ add_library(video_core STATIC renderer_opengl/present/smaa.cpp renderer_opengl/present/smaa.h renderer_opengl/present/util.h + renderer_opengl/present/window_adapt_pass.cpp + renderer_opengl/present/window_adapt_pass.h renderer_opengl/blit_image.cpp renderer_opengl/blit_image.h renderer_opengl/gl_blit_screen.cpp diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 5f6221b9b..f9dbef0fc 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -2,100 +2,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "video_core/framebuffer_config.h" -#include "video_core/host_shaders/ffx_a_h.h" -#include "video_core/host_shaders/ffx_fsr1_h.h" -#include "video_core/host_shaders/full_screen_triangle_vert.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" -#include "video_core/host_shaders/opengl_present_frag.h" -#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" -#include "video_core/host_shaders/opengl_present_vert.h" -#include "video_core/host_shaders/present_bicubic_frag.h" -#include "video_core/host_shaders/present_gaussian_frag.h" - #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state_tracker.h" +#include "video_core/renderer_opengl/present/filters.h" #include "video_core/renderer_opengl/present/fsr.h" #include "video_core/renderer_opengl/present/fxaa.h" #include "video_core/renderer_opengl/present/smaa.h" +#include "video_core/renderer_opengl/present/window_adapt_pass.h" #include "video_core/textures/decoders.h" namespace OpenGL { -namespace { -constexpr GLint PositionLocation = 0; -constexpr GLint TexCoordLocation = 1; -constexpr GLint ModelViewMatrixLocation = 0; - -struct ScreenRectVertex { - constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) - : position{{static_cast(x), static_cast(y)}}, tex_coord{{u, v}} {} - - std::array position; - std::array tex_coord; -}; - -/** - * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - */ -std::array MakeOrthographicMatrix(float width, float height) { - std::array matrix; // Laid out in column-major order - - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; - // Last matrix row is implicitly assumed to be [0, 0, 1]. - // clang-format on - - return matrix; -} -} // namespace - BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, StateTracker& state_tracker_, ProgramManager& program_manager_, Device& device_) : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), program_manager(program_manager_), device(device_) { - // Create shader programs - present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); - present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); - present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); - present_gaussian_fragment = - CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); - present_scaleforce_fragment = - CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), - GL_FRAGMENT_SHADER); - - // Generate presentation sampler - present_sampler.Create(); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - present_sampler_nn.Create(); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Generate VBO handle for drawing - vertex_buffer.Create(); - - // Attach vertex data to VAO - glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); - // Allocate textures for the screen framebuffer_texture.resource.Create(GL_TEXTURE_2D); @@ -106,15 +32,6 @@ BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, const u8 framebuffer_data[4] = {0, 0, 0, 0}; glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); - - // Enable unified vertex attributes and query vertex buffer address when the driver supports it - if (device.HasVertexBufferUnifiedMemory()) { - glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); - glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); - glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); - glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, - &vertex_buffer_address); - } } BlitScreen::~BlitScreen() = default; @@ -219,18 +136,14 @@ void BlitScreen::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& fra glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, framebuffer_texture.width, framebuffer_texture.height); - fxaa = std::make_unique( - Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), - Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); - smaa = std::make_unique( - Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), - Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + fxaa.reset(); + smaa.reset(); } void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout& layout) { FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); - const auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); + auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); // TODO: Signal state tracker about these changes state_tracker.NotifyScreenDrawVertexArray(); @@ -267,15 +180,14 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthRangeIndexed(0, 0.0, 0.0); + GLint old_read_fb; + GLint old_draw_fb; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + GLuint texture = info.display_texture; auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); - if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) { - LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); - anti_aliasing = Settings::AntiAliasing::None; - Settings::values.anti_aliasing.SetValue(anti_aliasing); - } - if (anti_aliasing != Settings::AntiAliasing::None) { glEnablei(GL_SCISSOR_TEST, 0); auto scissor_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); @@ -286,137 +198,83 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, glScissorIndexed(0, 0, 0, scissor_width, scissor_height); glViewportIndexedf(0, 0.0f, 0.0f, viewport_width, viewport_height); - glBindSampler(0, present_sampler.handle); - GLint old_read_fb; - GLint old_draw_fb; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - switch (anti_aliasing) { - case Settings::AntiAliasing::Fxaa: { + case Settings::AntiAliasing::Fxaa: + CreateFXAA(); texture = fxaa->Draw(program_manager, info.display_texture); - } break; - case Settings::AntiAliasing::Smaa: { - texture = smaa->Draw(program_manager, info.display_texture); - } break; + break; + case Settings::AntiAliasing::Smaa: default: - UNREACHABLE(); + CreateSMAA(); + texture = smaa->Draw(program_manager, info.display_texture); + break; } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); } + glDisablei(GL_SCISSOR_TEST, 0); if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - GLint old_read_fb; - GLint old_draw_fb; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - if (!fsr || fsr->NeedsRecreation(layout.screen)) { fsr = std::make_unique(layout.screen.GetWidth(), layout.screen.GetHeight()); } texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); + crop = {0, 0, 1, 1}; } - glBindTextureUnit(0, texture); + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); - const std::array ortho_matrix = - MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); - - const auto fragment_handle = [this]() { - switch (Settings::values.scaling_filter.GetValue()) { - case Settings::ScalingFilter::Bicubic: - return present_bicubic_fragment.handle; - case Settings::ScalingFilter::Gaussian: - return present_gaussian_fragment.handle; - case Settings::ScalingFilter::ScaleForce: - return present_scaleforce_fragment.handle; - case Settings::ScalingFilter::NearestNeighbor: - case Settings::ScalingFilter::Bilinear: - case Settings::ScalingFilter::Fsr: - default: - return present_bilinear_fragment.handle; - } - }(); - program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); - glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, - ortho_matrix.data()); - - f32 left, top, right, bottom; - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - // FSR has already applied the crop, so we just want to render the image - // it has produced. - left = 0; - top = 0; - right = 1; - bottom = 1; - } else { - // Apply the precomputed crop. - left = crop.left; - top = crop.top; - right = crop.right; - bottom = crop.bottom; - } - - // Map the coordinates to the screen. - const auto& screen = layout.screen; - const auto x = screen.left; - const auto y = screen.top; - const auto w = screen.GetWidth(); - const auto h = screen.GetHeight(); - - const std::array vertices = { - ScreenRectVertex(x, y, left, top), - ScreenRectVertex(x + w, y, right, top), - ScreenRectVertex(x, y + h, left, bottom), - ScreenRectVertex(x + w, y + h, right, bottom), - }; - glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); - - glDisable(GL_FRAMEBUFFER_SRGB); - glViewportIndexedf(0, 0.0f, 0.0f, static_cast(layout.width), - static_cast(layout.height)); - - glEnableVertexAttribArray(PositionLocation); - glEnableVertexAttribArray(TexCoordLocation); - glVertexAttribDivisor(PositionLocation, 0); - glVertexAttribDivisor(TexCoordLocation, 0); - glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, position)); - glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, tex_coord)); - glVertexAttribBinding(PositionLocation, 0); - glVertexAttribBinding(TexCoordLocation, 0); - if (device.HasVertexBufferUnifiedMemory()) { - glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); - glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, - sizeof(vertices)); - } else { - glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); - } - - if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { - glBindSampler(0, present_sampler.handle); - } else { - glBindSampler(0, present_sampler_nn.handle); - } - - // Update background color before drawing - glClearColor(Settings::values.bg_red.GetValue() / 255.0f, - Settings::values.bg_green.GetValue() / 255.0f, - Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); - - glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + CreateWindowAdapt(); + window_adapt->DrawToFramebuffer(program_manager, texture, layout, crop); // TODO // program_manager.RestoreGuestPipeline(); } +void BlitScreen::CreateFXAA() { + smaa.reset(); + if (!fxaa) { + fxaa = std::make_unique( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +void BlitScreen::CreateSMAA() { + fxaa.reset(); + if (!smaa) { + smaa = std::make_unique( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +void BlitScreen::CreateWindowAdapt() { + if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { + return; + } + + current_window_adapt = Settings::values.scaling_filter.GetValue(); + switch (current_window_adapt) { + case Settings::ScalingFilter::NearestNeighbor: + window_adapt = MakeNearestNeighbor(device); + break; + case Settings::ScalingFilter::Bicubic: + window_adapt = MakeBicubic(device); + break; + case Settings::ScalingFilter::Gaussian: + window_adapt = MakeGaussian(device); + break; + case Settings::ScalingFilter::ScaleForce: + window_adapt = MakeScaleForce(device); + break; + case Settings::ScalingFilter::Fsr: + case Settings::ScalingFilter::Bilinear: + default: + window_adapt = MakeBilinear(device); + break; + } +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h index 2cb9a5015..f42f89dee 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.h +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -18,6 +18,10 @@ namespace Tegra { struct FramebufferConfig; } +namespace Settings { +enum class ScalingFilter : u32; +} + namespace OpenGL { class Device; @@ -27,6 +31,7 @@ class ProgramManager; class RasterizerOpenGL; class SMAA; class StateTracker; +class WindowAdaptPass; /// Structure used for storing information about the textures for the Switch screen struct TextureInfo { @@ -61,29 +66,22 @@ public: void DrawScreen(const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout& layout); - void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); - /// Loads framebuffer from emulated memory into the active OpenGL texture. FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); private: + void CreateFXAA(); + void CreateSMAA(); + void CreateWindowAdapt(); + RasterizerOpenGL& rasterizer; Tegra::MaxwellDeviceMemoryManager& device_memory; StateTracker& state_tracker; ProgramManager& program_manager; Device& device; - OGLSampler present_sampler; - OGLSampler present_sampler_nn; - OGLBuffer vertex_buffer; - OGLProgram present_vertex; - OGLProgram present_bilinear_fragment; - OGLProgram present_bicubic_fragment; - OGLProgram present_gaussian_fragment; - OGLProgram present_scaleforce_fragment; - /// Display information for Switch screen TextureInfo framebuffer_texture; @@ -91,11 +89,11 @@ private: std::unique_ptr fxaa; std::unique_ptr smaa; + Settings::ScalingFilter current_window_adapt{}; + std::unique_ptr window_adapt; + /// OpenGL framebuffer data std::vector gl_framebuffer_data; - - // GPU address of the vertex buffer - GLuint64EXT vertex_buffer_address = 0; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp new file mode 100644 index 000000000..819e5d77f --- /dev/null +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host_shaders/opengl_present_frag.h" +#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" +#include "video_core/host_shaders/present_bicubic_frag.h" +#include "video_core/host_shaders/present_gaussian_frag.h" +#include "video_core/renderer_opengl/present/filters.h" +#include "video_core/renderer_opengl/present/util.h" + +namespace OpenGL { + +std::unique_ptr MakeNearestNeighbor(const Device& device) { + return std::make_unique(device, CreateNearestNeighborSampler(), + HostShaders::OPENGL_PRESENT_FRAG); +} + +std::unique_ptr MakeBilinear(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::OPENGL_PRESENT_FRAG); +} + +std::unique_ptr MakeBicubic(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_BICUBIC_FRAG); +} + +std::unique_ptr MakeGaussian(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_GAUSSIAN_FRAG); +} + +std::unique_ptr MakeScaleForce(const Device& device) { + return std::make_unique( + device, CreateBilinearSampler(), + fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG)); +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h new file mode 100644 index 000000000..122ab7436 --- /dev/null +++ b/src/video_core/renderer_opengl/present/filters.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "video_core/renderer_opengl/present/window_adapt_pass.h" + +namespace OpenGL { + +std::unique_ptr MakeNearestNeighbor(const Device& device); +std::unique_ptr MakeBilinear(const Device& device); +std::unique_ptr MakeBicubic(const Device& device); +std::unique_ptr MakeGaussian(const Device& device); +std::unique_ptr MakeScaleForce(const Device& device); + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/fxaa.cpp b/src/video_core/renderer_opengl/present/fxaa.cpp index 9425c42fa..d9b58512d 100644 --- a/src/video_core/renderer_opengl/present/fxaa.cpp +++ b/src/video_core/renderer_opengl/present/fxaa.cpp @@ -31,6 +31,7 @@ GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) { program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); glBindTextureUnit(0, input_texture); + glBindSampler(0, sampler.handle); glDrawArrays(GL_TRIANGLES, 0, 3); glFrontFace(GL_CW); diff --git a/src/video_core/renderer_opengl/present/smaa.cpp b/src/video_core/renderer_opengl/present/smaa.cpp index a9a0eb6c6..de7f6e502 100644 --- a/src/video_core/renderer_opengl/present/smaa.cpp +++ b/src/video_core/renderer_opengl/present/smaa.cpp @@ -36,13 +36,7 @@ SMAA::SMAA(u32 width, u32 height) { SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); - glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); area_tex.Create(GL_TEXTURE_2D); glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); diff --git a/src/video_core/renderer_opengl/present/util.h b/src/video_core/renderer_opengl/present/util.h index 0aa8b110c..67f03aa27 100644 --- a/src/video_core/renderer_opengl/present/util.h +++ b/src/video_core/renderer_opengl/present/util.h @@ -29,4 +29,15 @@ static inline OGLSampler CreateBilinearSampler() { return sampler; } +static inline OGLSampler CreateNearestNeighborSampler() { + OGLSampler sampler; + sampler.Create(); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + return sampler; +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp new file mode 100644 index 000000000..168fa1aea --- /dev/null +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "video_core/host_shaders/opengl_present_vert.h" +#include "video_core/renderer_opengl/gl_device.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/window_adapt_pass.h" + +namespace OpenGL { + +namespace { +constexpr GLint PositionLocation = 0; +constexpr GLint TexCoordLocation = 1; +constexpr GLint ModelViewMatrixLocation = 0; + +struct ScreenRectVertex { + constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) + : position{{static_cast(x), static_cast(y)}}, tex_coord{{u, v}} {} + + std::array position; + std::array tex_coord; +}; + +/** + * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left + * corner and (width, height) on the lower-bottom. + * + * The projection part of the matrix is trivial, hence these operations are represented + * by a 3x2 matrix. + */ +std::array MakeOrthographicMatrix(float width, float height) { + std::array matrix; // Laid out in column-major order + + // clang-format off + matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; + matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; + // Last matrix row is implicitly assumed to be [0, 0, 1]. + // clang-format on + + return matrix; +} +} // namespace + +WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, + std::string_view frag_source) + : device(device_), sampler(std::move(sampler_)) { + vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); + frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER); + + // Generate VBO handle for drawing + vertex_buffer.Create(); + + // Attach vertex data to VAO + glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); + + // Query vertex buffer address when the driver supports unified vertex attributes + if (device.HasVertexBufferUnifiedMemory()) { + glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); + glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, + &vertex_buffer_address); + } +} + +WindowAdaptPass::~WindowAdaptPass() = default; + +void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, GLuint texture, + const Layout::FramebufferLayout& layout, + const Common::Rectangle& crop) { + glBindTextureUnit(0, texture); + + const std::array ortho_matrix = + MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); + + program_manager.BindPresentPrograms(vert.handle, frag.handle); + glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, + ortho_matrix.data()); + + // Map the coordinates to the screen. + const auto& screen = layout.screen; + const auto x = screen.left; + const auto y = screen.top; + const auto w = screen.GetWidth(); + const auto h = screen.GetHeight(); + + const std::array vertices = { + ScreenRectVertex(x, y, crop.left, crop.top), + ScreenRectVertex(x + w, y, crop.right, crop.top), + ScreenRectVertex(x, y + h, crop.left, crop.bottom), + ScreenRectVertex(x + w, y + h, crop.right, crop.bottom), + }; + glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); + + glDisable(GL_FRAMEBUFFER_SRGB); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast(layout.width), + static_cast(layout.height)); + + glEnableVertexAttribArray(PositionLocation); + glEnableVertexAttribArray(TexCoordLocation); + glVertexAttribDivisor(PositionLocation, 0); + glVertexAttribDivisor(TexCoordLocation, 0); + glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, position)); + glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, tex_coord)); + glVertexAttribBinding(PositionLocation, 0); + glVertexAttribBinding(TexCoordLocation, 0); + if (device.HasVertexBufferUnifiedMemory()) { + glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, + sizeof(vertices)); + } else { + glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); + } + + glBindSampler(0, sampler.handle); + + // Update background color before drawing + glClearColor(Settings::values.bg_red.GetValue() / 255.0f, + Settings::values.bg_green.GetValue() / 255.0f, + Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); + + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h new file mode 100644 index 000000000..65dcd09ff --- /dev/null +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace OpenGL { + +class Device; +class ProgramManager; + +class WindowAdaptPass final { +public: + explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler, + std::string_view frag_source); + ~WindowAdaptPass(); + + void DrawToFramebuffer(ProgramManager& program_manager, GLuint texture, + const Layout::FramebufferLayout& layout, + const Common::Rectangle& crop); + +private: + const Device& device; + OGLSampler sampler; + OGLProgram vert; + OGLProgram frag; + OGLBuffer vertex_buffer; + + // GPU address of the vertex buffer + GLuint64EXT vertex_buffer_address = 0; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 3d75fd17a..0d138c189 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -113,6 +113,12 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } + + // Enable unified vertex attributes when the driver supports it + if (device.HasVertexBufferUnifiedMemory()) { + glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); + glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); + } blit_screen = std::make_unique(rasterizer, device_memory, state_tracker, program_manager, device); } diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index 7fd9ecd22..1d1828a4c 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -92,7 +92,9 @@ void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView const VkFramebuffer host_framebuffer{*dst->framebuffer}; const VkRenderPass renderpass{*render_pass}; const VkPipeline graphics_pipeline{*pipeline}; + const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; const VkDescriptorSet descriptor_set{descriptor_sets[image_index]}; + const VkBuffer vertex_buffer{*buffer}; const VkExtent2D render_area{ .width = dst->width, .height = dst->height, @@ -134,8 +136,8 @@ void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); cmdbuf.SetViewport(0, viewport); cmdbuf.SetScissor(0, scissor); - cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, + cmdbuf.BindVertexBuffer(0, vertex_buffer, offsetof(BufferData, vertices)); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, descriptor_set, {}); cmdbuf.Draw(4, 1, 0, 0); cmdbuf.EndRenderPass(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 2912aaff6..a99ef08a5 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -222,6 +222,9 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr .image = std::move(staging_image), .image_view = std::move(dst_view), .framebuffer = std::move(screenshot_fb), + .cmdbuf{}, + .render_ready{}, + .present_done{}, }; }();