mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-30 18:24:15 +01:00 
			
		
		
		
	renderer_opengl: split up blit screen resources into antialias and window adapt passes
This commit is contained in:
		
							parent
							
								
									dd2918efd8
								
							
						
					
					
						commit
						d4de04584f
					
				| @ -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 | ||||
|  | ||||
| @ -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<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||||
| 
 | ||||
|     std::array<GLfloat, 2> position; | ||||
|     std::array<GLfloat, 2> 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<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||||
|     std::array<GLfloat, 3 * 2> 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<FXAA>( | ||||
|         Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|         Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
|     smaa = std::make_unique<SMAA>( | ||||
|         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<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); | ||||
|         } | ||||
| 
 | ||||
|         texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); | ||||
|         crop = {0, 0, 1, 1}; | ||||
|     } | ||||
| 
 | ||||
|     glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||||
|     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||||
|     } | ||||
| 
 | ||||
|     glBindTextureUnit(0, texture); | ||||
| 
 | ||||
|     const std::array ortho_matrix = | ||||
|         MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(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<GLfloat>(layout.width), | ||||
|                        static_cast<GLfloat>(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<FXAA>( | ||||
|             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<SMAA>( | ||||
|             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
 | ||||
|  | ||||
| @ -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> fxaa; | ||||
|     std::unique_ptr<SMAA> smaa; | ||||
| 
 | ||||
|     Settings::ScalingFilter current_window_adapt{}; | ||||
|     std::unique_ptr<WindowAdaptPass> window_adapt; | ||||
| 
 | ||||
|     /// OpenGL framebuffer data
 | ||||
|     std::vector<u8> gl_framebuffer_data; | ||||
| 
 | ||||
|     // GPU address of the vertex buffer
 | ||||
|     GLuint64EXT vertex_buffer_address = 0; | ||||
| }; | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  | ||||
							
								
								
									
										39
									
								
								src/video_core/renderer_opengl/present/filters.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/video_core/renderer_opengl/present/filters.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<WindowAdaptPass> MakeNearestNeighbor(const Device& device) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(), | ||||
|                                              HostShaders::OPENGL_PRESENT_FRAG); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||||
|                                              HostShaders::OPENGL_PRESENT_FRAG); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||||
|                                              HostShaders::PRESENT_BICUBIC_FRAG); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||||
|                                              HostShaders::PRESENT_GAUSSIAN_FRAG); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) { | ||||
|     return std::make_unique<WindowAdaptPass>( | ||||
|         device, CreateBilinearSampler(), | ||||
|         fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG)); | ||||
| } | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
							
								
								
									
										17
									
								
								src/video_core/renderer_opengl/present/filters.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/video_core/renderer_opengl/present/filters.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include "video_core/renderer_opengl/present/window_adapt_pass.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device); | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device); | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device); | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device); | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device); | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
							
								
								
									
										128
									
								
								src/video_core/renderer_opengl/present/window_adapt_pass.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/video_core/renderer_opengl/present/window_adapt_pass.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||||
| 
 | ||||
|     std::array<GLfloat, 2> position; | ||||
|     std::array<GLfloat, 2> 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<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||||
|     std::array<GLfloat, 3 * 2> 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<f32>& crop) { | ||||
|     glBindTextureUnit(0, texture); | ||||
| 
 | ||||
|     const std::array ortho_matrix = | ||||
|         MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(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<GLfloat>(layout.width), | ||||
|                        static_cast<GLfloat>(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
 | ||||
							
								
								
									
										39
									
								
								src/video_core/renderer_opengl/present/window_adapt_pass.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/video_core/renderer_opengl/present/window_adapt_pass.h
									
									
									
									
									
										Normal file
									
								
							| @ -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<f32>& 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
 | ||||
| @ -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<BlitScreen>(rasterizer, device_memory, state_tracker, | ||||
|                                                program_manager, device); | ||||
| } | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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{}, | ||||
|         }; | ||||
|     }(); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user