renderer_opengl: isolate core presentation code
This commit is contained in:
		| @@ -118,6 +118,8 @@ add_library(video_core STATIC | ||||
|     renderer_null/renderer_null.h | ||||
|     renderer_opengl/blit_image.cpp | ||||
|     renderer_opengl/blit_image.h | ||||
|     renderer_opengl/gl_blit_screen.cpp | ||||
|     renderer_opengl/gl_blit_screen.h | ||||
|     renderer_opengl/gl_buffer_cache_base.cpp | ||||
|     renderer_opengl/gl_buffer_cache.cpp | ||||
|     renderer_opengl/gl_buffer_cache.h | ||||
|   | ||||
							
								
								
									
										519
									
								
								src/video_core/renderer_opengl/gl_blit_screen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								src/video_core/renderer_opengl/gl_blit_screen.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,519 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // 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/fxaa_frag.h" | ||||
| #include "video_core/host_shaders/fxaa_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/opengl_smaa_glsl.h" | ||||
| #include "video_core/host_shaders/present_bicubic_frag.h" | ||||
| #include "video_core/host_shaders/present_gaussian_frag.h" | ||||
| #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||||
| #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||||
| #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||||
| #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||||
| #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||||
| #include "video_core/host_shaders/smaa_neighborhood_blending_vert.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/smaa_area_tex.h" | ||||
| #include "video_core/smaa_search_tex.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 | ||||
|     fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||||
|     fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||||
|  | ||||
|     const auto replace_include = [](std::string& shader_source, std::string_view include_name, | ||||
|                                     std::string_view include_content) { | ||||
|         const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||||
|         const std::size_t pos = shader_source.find(include_string); | ||||
|         ASSERT(pos != std::string::npos); | ||||
|         shader_source.replace(pos, include_string.size(), include_content); | ||||
|     }; | ||||
|  | ||||
|     const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||||
|         std::string shader_source{specialized_source}; | ||||
|         replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||||
|         return CreateProgram(shader_source, stage); | ||||
|     }; | ||||
|  | ||||
|     smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_edge_detection_frag = | ||||
|         SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||||
|     smaa_blending_weight_calculation_vert = | ||||
|         SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_blending_weight_calculation_frag = | ||||
|         SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||||
|     smaa_neighborhood_blending_vert = | ||||
|         SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_neighborhood_blending_frag = | ||||
|         SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||||
|  | ||||
|     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); | ||||
|  | ||||
|     std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||||
|     replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||||
|     replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||||
|  | ||||
|     std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||||
|     std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||||
|     replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||||
|     replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||||
|  | ||||
|     fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, | ||||
|                                 fsr_rcas_frag_source); | ||||
|  | ||||
|     // 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); | ||||
|  | ||||
|     const GLuint texture = framebuffer_texture.resource.handle; | ||||
|     glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||||
|  | ||||
|     // Clear screen to black | ||||
|     const u8 framebuffer_data[4] = {0, 0, 0, 0}; | ||||
|     glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||||
|                     framebuffer_data); | ||||
|  | ||||
|     aa_framebuffer.Create(); | ||||
|  | ||||
|     smaa_area_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||||
|     glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||||
|                         GL_UNSIGNED_BYTE, areaTexBytes); | ||||
|     smaa_search_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||||
|     glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||||
|                         GL_UNSIGNED_BYTE, searchTexBytes); | ||||
|  | ||||
|     // 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| FramebufferTextureInfo BlitScreen::PrepareRenderTarget( | ||||
|     const Tegra::FramebufferConfig& framebuffer) { | ||||
|     // If framebuffer is provided, reload it from memory to a texture | ||||
|     if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || | ||||
|         framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || | ||||
|         framebuffer_texture.pixel_format != framebuffer.pixel_format || | ||||
|         gl_framebuffer_data.empty()) { | ||||
|         // Reallocate texture if the framebuffer size has changed. | ||||
|         // This is expected to not happen very often and hence should not be a | ||||
|         // performance problem. | ||||
|         ConfigureFramebufferTexture(framebuffer); | ||||
|     } | ||||
|  | ||||
|     // Load the framebuffer from memory if needed | ||||
|     return LoadFBToScreenInfo(framebuffer); | ||||
| } | ||||
|  | ||||
| FramebufferTextureInfo BlitScreen::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     const DAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||||
|     const auto accelerated_info = | ||||
|         rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||||
|     if (accelerated_info) { | ||||
|         return *accelerated_info; | ||||
|     } | ||||
|  | ||||
|     // Reset the screen info's display texture to its own permanent texture | ||||
|     FramebufferTextureInfo info{}; | ||||
|     info.display_texture = framebuffer_texture.resource.handle; | ||||
|     info.width = framebuffer.width; | ||||
|     info.height = framebuffer.height; | ||||
|     info.scaled_width = framebuffer.width; | ||||
|     info.scaled_height = framebuffer.height; | ||||
|  | ||||
|     // TODO(Rodrigo): Read this from HLE | ||||
|     constexpr u32 block_height_log2 = 4; | ||||
|     const auto pixel_format{ | ||||
|         VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||||
|     const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||||
|     const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||||
|         true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||||
|     const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||||
|     const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||||
|     Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||||
|                                      framebuffer.width, framebuffer.height, 1, block_height_log2, | ||||
|                                      0); | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||||
|  | ||||
|     // Update existing texture | ||||
|     // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||||
|     //       they differ from the LCD resolution. | ||||
|     // TODO: Applications could theoretically crash yuzu here by specifying too large | ||||
|     //       framebuffer sizes. We should make sure that this cannot happen. | ||||
|     glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, | ||||
|                         framebuffer.height, framebuffer_texture.gl_format, | ||||
|                         framebuffer_texture.gl_type, gl_framebuffer_data.data()); | ||||
|  | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||
|  | ||||
|     return info; | ||||
| } | ||||
|  | ||||
| void BlitScreen::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     framebuffer_texture.width = framebuffer.width; | ||||
|     framebuffer_texture.height = framebuffer.height; | ||||
|     framebuffer_texture.pixel_format = framebuffer.pixel_format; | ||||
|  | ||||
|     const auto pixel_format{ | ||||
|         VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||||
|     const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||||
|     gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * | ||||
|                                bytes_per_pixel); | ||||
|  | ||||
|     GLint internal_format; | ||||
|     switch (framebuffer.pixel_format) { | ||||
|     case Service::android::PixelFormat::Rgba8888: | ||||
|         internal_format = GL_RGBA8; | ||||
|         framebuffer_texture.gl_format = GL_RGBA; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||
|         break; | ||||
|     case Service::android::PixelFormat::Rgb565: | ||||
|         internal_format = GL_RGB565; | ||||
|         framebuffer_texture.gl_format = GL_RGB; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||||
|         break; | ||||
|     default: | ||||
|         internal_format = GL_RGBA8; | ||||
|         framebuffer_texture.gl_format = GL_RGBA; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||
|         // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||||
|         //                   static_cast<u32>(framebuffer.pixel_format)); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     framebuffer_texture.resource.Release(); | ||||
|     framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, | ||||
|                        framebuffer_texture.width, framebuffer_texture.height); | ||||
|     aa_texture.Release(); | ||||
|     aa_texture.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
|     glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); | ||||
|     smaa_edges_tex.Release(); | ||||
|     smaa_edges_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
|     smaa_blend_tex.Release(); | ||||
|     smaa_blend_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     // TODO: Signal state tracker about these changes | ||||
|     state_tracker.NotifyScreenDrawVertexArray(); | ||||
|     state_tracker.NotifyPolygonModes(); | ||||
|     state_tracker.NotifyViewport0(); | ||||
|     state_tracker.NotifyScissor0(); | ||||
|     state_tracker.NotifyColorMask(0); | ||||
|     state_tracker.NotifyBlend0(); | ||||
|     state_tracker.NotifyFramebuffer(); | ||||
|     state_tracker.NotifyFrontFace(); | ||||
|     state_tracker.NotifyCullTest(); | ||||
|     state_tracker.NotifyDepthTest(); | ||||
|     state_tracker.NotifyStencilTest(); | ||||
|     state_tracker.NotifyPolygonOffset(); | ||||
|     state_tracker.NotifyRasterizeEnable(); | ||||
|     state_tracker.NotifyFramebufferSRGB(); | ||||
|     state_tracker.NotifyLogicOp(); | ||||
|     state_tracker.NotifyClipControl(); | ||||
|     state_tracker.NotifyAlphaTest(); | ||||
|  | ||||
|     state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||||
|  | ||||
|     glEnable(GL_CULL_FACE); | ||||
|     glDisable(GL_COLOR_LOGIC_OP); | ||||
|     glDisable(GL_DEPTH_TEST); | ||||
|     glDisable(GL_STENCIL_TEST); | ||||
|     glDisable(GL_POLYGON_OFFSET_FILL); | ||||
|     glDisable(GL_RASTERIZER_DISCARD); | ||||
|     glDisable(GL_ALPHA_TEST); | ||||
|     glDisablei(GL_BLEND, 0); | ||||
|     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||||
|     glCullFace(GL_BACK); | ||||
|     glFrontFace(GL_CW); | ||||
|     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||||
|     glDepthRangeIndexed(0, 0.0, 0.0); | ||||
|  | ||||
|     glBindTextureUnit(0, 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); | ||||
|         auto viewport_width = static_cast<GLfloat>(scissor_width); | ||||
|         auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); | ||||
|         auto viewport_height = static_cast<GLfloat>(scissor_height); | ||||
|  | ||||
|         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: { | ||||
|             program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); | ||||
|             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||||
|             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|         } break; | ||||
|         case Settings::AntiAliasing::Smaa: { | ||||
|             glClearColor(0, 0, 0, 0); | ||||
|             glFrontFace(GL_CCW); | ||||
|             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||||
|             glBindSampler(1, present_sampler.handle); | ||||
|             glBindSampler(2, present_sampler.handle); | ||||
|  | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       smaa_edges_tex.handle, 0); | ||||
|             glClear(GL_COLOR_BUFFER_BIT); | ||||
|             program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, | ||||
|                                                 smaa_edge_detection_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|  | ||||
|             glBindTextureUnit(0, smaa_edges_tex.handle); | ||||
|             glBindTextureUnit(1, smaa_area_tex.handle); | ||||
|             glBindTextureUnit(2, smaa_search_tex.handle); | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       smaa_blend_tex.handle, 0); | ||||
|             glClear(GL_COLOR_BUFFER_BIT); | ||||
|             program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, | ||||
|                                                 smaa_blending_weight_calculation_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|  | ||||
|             glBindTextureUnit(0, info.display_texture); | ||||
|             glBindTextureUnit(1, smaa_blend_tex.handle); | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       aa_texture.handle, 0); | ||||
|             program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, | ||||
|                                                 smaa_neighborhood_blending_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|             glFrontFace(GL_CW); | ||||
|         } break; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|  | ||||
|         glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||||
|         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||||
|  | ||||
|         glBindTextureUnit(0, aa_texture.handle); | ||||
|     } | ||||
|     glDisablei(GL_SCISSOR_TEST, 0); | ||||
|  | ||||
|     if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||||
|         if (!fsr->AreBuffersInitialized()) { | ||||
|             fsr->InitBuffers(); | ||||
|         } | ||||
|  | ||||
|         glBindSampler(0, present_sampler.handle); | ||||
|         fsr->Draw(program_manager, layout.screen, info.scaled_width, info.scaled_height, crop); | ||||
|     } else { | ||||
|         if (fsr->AreBuffersInitialized()) { | ||||
|             fsr->ReleaseBuffers(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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::NearestNeighbor: | ||||
|         case Settings::ScalingFilter::Bilinear: | ||||
|             return present_bilinear_fragment.handle; | ||||
|         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::Fsr: | ||||
|             return fsr->GetPresentFragmentProgram().handle; | ||||
|         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); | ||||
|  | ||||
|     // TODO | ||||
|     // program_manager.RestoreGuestPipeline(); | ||||
| } | ||||
|  | ||||
| } // namespace OpenGL | ||||
							
								
								
									
										110
									
								
								src/video_core/renderer_opengl/gl_blit_screen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/video_core/renderer_opengl/gl_blit_screen.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include <vector> | ||||
|  | ||||
| #include "core/hle/service/nvnflinger/pixel_format.h" | ||||
| #include "video_core/host1x/gpu_device_memory_manager.h" | ||||
| #include "video_core/renderer_opengl/gl_fsr.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
|  | ||||
| namespace Layout { | ||||
| struct FramebufferLayout; | ||||
| } | ||||
|  | ||||
| namespace Tegra { | ||||
| struct FramebufferConfig; | ||||
| } | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
| class Device; | ||||
| class RasterizerOpenGL; | ||||
| class StateTracker; | ||||
|  | ||||
| /// Structure used for storing information about the textures for the Switch screen | ||||
| struct TextureInfo { | ||||
|     OGLTexture resource; | ||||
|     GLsizei width; | ||||
|     GLsizei height; | ||||
|     GLenum gl_format; | ||||
|     GLenum gl_type; | ||||
|     Service::android::PixelFormat pixel_format; | ||||
| }; | ||||
|  | ||||
| /// Structure used for storing information about the display target for the Switch screen | ||||
| struct FramebufferTextureInfo { | ||||
|     GLuint display_texture{}; | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     u32 scaled_width; | ||||
|     u32 scaled_height; | ||||
| }; | ||||
|  | ||||
| class BlitScreen { | ||||
| public: | ||||
|     explicit BlitScreen(RasterizerOpenGL& rasterizer, | ||||
|                         Tegra::MaxwellDeviceMemoryManager& device_memory, | ||||
|                         StateTracker& state_tracker, ProgramManager& program_manager, | ||||
|                         Device& device); | ||||
|  | ||||
|     void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); | ||||
|  | ||||
|     /// Draws the emulated screens to the emulator window. | ||||
|     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: | ||||
|     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 fxaa_vertex; | ||||
|     OGLProgram fxaa_fragment; | ||||
|     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; | ||||
|     OGLTexture aa_texture; | ||||
|     OGLFramebuffer aa_framebuffer; | ||||
|  | ||||
|     OGLProgram smaa_edge_detection_vert; | ||||
|     OGLProgram smaa_blending_weight_calculation_vert; | ||||
|     OGLProgram smaa_neighborhood_blending_vert; | ||||
|     OGLProgram smaa_edge_detection_frag; | ||||
|     OGLProgram smaa_blending_weight_calculation_frag; | ||||
|     OGLProgram smaa_neighborhood_blending_frag; | ||||
|     OGLTexture smaa_area_tex; | ||||
|     OGLTexture smaa_search_tex; | ||||
|     OGLTexture smaa_edges_tex; | ||||
|     OGLTexture smaa_blend_tex; | ||||
|  | ||||
|     std::unique_ptr<FSR> fsr; | ||||
|  | ||||
|     /// OpenGL framebuffer data | ||||
|     std::vector<u8> gl_framebuffer_data; | ||||
|  | ||||
|     // GPU address of the vertex buffer | ||||
|     GLuint64EXT vertex_buffer_address = 0; | ||||
| }; | ||||
|  | ||||
| } // namespace OpenGL | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "video_core/engines/maxwell_dma.h" | ||||
| #include "video_core/rasterizer_interface.h" | ||||
| #include "video_core/renderer_opengl/blit_image.h" | ||||
| #include "video_core/renderer_opengl/gl_blit_screen.h" | ||||
| #include "video_core/renderer_opengl/gl_buffer_cache.h" | ||||
| #include "video_core/renderer_opengl/gl_device.h" | ||||
| #include "video_core/renderer_opengl/gl_fence_manager.h" | ||||
|   | ||||
| @@ -16,68 +16,16 @@ | ||||
| #include "core/core_timing.h" | ||||
| #include "core/frontend/emu_window.h" | ||||
| #include "core/telemetry_session.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/fxaa_frag.h" | ||||
| #include "video_core/host_shaders/fxaa_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/opengl_smaa_glsl.h" | ||||
| #include "video_core/host_shaders/present_bicubic_frag.h" | ||||
| #include "video_core/host_shaders/present_gaussian_frag.h" | ||||
| #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||||
| #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||||
| #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||||
| #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||||
| #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||||
| #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" | ||||
| #include "video_core/renderer_opengl/gl_blit_screen.h" | ||||
| #include "video_core/renderer_opengl/gl_fsr.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/renderer_opengl.h" | ||||
| #include "video_core/smaa_area_tex.h" | ||||
| #include "video_core/smaa_search_tex.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; | ||||
| } | ||||
|  | ||||
| const char* GetSource(GLenum source) { | ||||
|     switch (source) { | ||||
|     case GL_DEBUG_SOURCE_API: | ||||
| @@ -155,7 +103,6 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | ||||
|         glDebugMessageCallback(DebugHandler, nullptr); | ||||
|     } | ||||
|     AddTelemetryFields(); | ||||
|     InitOpenGLObjects(); | ||||
|  | ||||
|     // Initialize default attributes to match hardware's disabled attributes | ||||
|     GLint max_attribs{}; | ||||
| @@ -167,14 +114,8 @@ 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 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); | ||||
|     } | ||||
|     blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, | ||||
|                                                program_manager, device); | ||||
| } | ||||
|  | ||||
| RendererOpenGL::~RendererOpenGL() = default; | ||||
| @@ -187,7 +128,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||||
|     RenderScreenshot(*framebuffer); | ||||
|  | ||||
|     state_tracker.BindFramebuffer(0); | ||||
|     DrawScreen(*framebuffer, emu_window.GetFramebufferLayout()); | ||||
|     blit_screen->DrawScreen(*framebuffer, emu_window.GetFramebufferLayout()); | ||||
|  | ||||
|     ++m_current_frame; | ||||
|  | ||||
| @@ -198,166 +139,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||||
|     render_window.OnFrameDisplayed(); | ||||
| } | ||||
|  | ||||
| FramebufferTextureInfo RendererOpenGL::PrepareRenderTarget( | ||||
|     const Tegra::FramebufferConfig& framebuffer) { | ||||
|     // If framebuffer is provided, reload it from memory to a texture | ||||
|     if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || | ||||
|         framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || | ||||
|         framebuffer_texture.pixel_format != framebuffer.pixel_format || | ||||
|         gl_framebuffer_data.empty()) { | ||||
|         // Reallocate texture if the framebuffer size has changed. | ||||
|         // This is expected to not happen very often and hence should not be a | ||||
|         // performance problem. | ||||
|         ConfigureFramebufferTexture(framebuffer); | ||||
|     } | ||||
|  | ||||
|     // Load the framebuffer from memory if needed | ||||
|     return LoadFBToScreenInfo(framebuffer); | ||||
| } | ||||
|  | ||||
| FramebufferTextureInfo RendererOpenGL::LoadFBToScreenInfo( | ||||
|     const Tegra::FramebufferConfig& framebuffer) { | ||||
|     const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||||
|     const auto accelerated_info = | ||||
|         rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||||
|     if (accelerated_info) { | ||||
|         return *accelerated_info; | ||||
|     } | ||||
|  | ||||
|     // Reset the screen info's display texture to its own permanent texture | ||||
|     FramebufferTextureInfo info{}; | ||||
|     info.display_texture = framebuffer_texture.resource.handle; | ||||
|     info.width = framebuffer.width; | ||||
|     info.height = framebuffer.height; | ||||
|     info.scaled_width = framebuffer.width; | ||||
|     info.scaled_height = framebuffer.height; | ||||
|  | ||||
|     // TODO(Rodrigo): Read this from HLE | ||||
|     constexpr u32 block_height_log2 = 4; | ||||
|     const auto pixel_format{ | ||||
|         VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||||
|     const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||||
|     const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||||
|         true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||||
|     const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||||
|     const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||||
|     Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||||
|                                      framebuffer.width, framebuffer.height, 1, block_height_log2, | ||||
|                                      0); | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||||
|  | ||||
|     // Update existing texture | ||||
|     // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||||
|     //       they differ from the LCD resolution. | ||||
|     // TODO: Applications could theoretically crash yuzu here by specifying too large | ||||
|     //       framebuffer sizes. We should make sure that this cannot happen. | ||||
|     glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, | ||||
|                         framebuffer.height, framebuffer_texture.gl_format, | ||||
|                         framebuffer_texture.gl_type, gl_framebuffer_data.data()); | ||||
|  | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||
|  | ||||
|     return info; | ||||
| } | ||||
|  | ||||
| void RendererOpenGL::InitOpenGLObjects() { | ||||
|     // Create shader programs | ||||
|     fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||||
|     fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||||
|  | ||||
|     const auto replace_include = [](std::string& shader_source, std::string_view include_name, | ||||
|                                     std::string_view include_content) { | ||||
|         const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||||
|         const std::size_t pos = shader_source.find(include_string); | ||||
|         ASSERT(pos != std::string::npos); | ||||
|         shader_source.replace(pos, include_string.size(), include_content); | ||||
|     }; | ||||
|  | ||||
|     const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||||
|         std::string shader_source{specialized_source}; | ||||
|         replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||||
|         return CreateProgram(shader_source, stage); | ||||
|     }; | ||||
|  | ||||
|     smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_edge_detection_frag = | ||||
|         SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||||
|     smaa_blending_weight_calculation_vert = | ||||
|         SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_blending_weight_calculation_frag = | ||||
|         SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||||
|     smaa_neighborhood_blending_vert = | ||||
|         SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||||
|     smaa_neighborhood_blending_frag = | ||||
|         SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||||
|  | ||||
|     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); | ||||
|  | ||||
|     std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||||
|     replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||||
|     replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||||
|  | ||||
|     std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||||
|     std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||||
|     replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||||
|     replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||||
|  | ||||
|     fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, | ||||
|                                 fsr_rcas_frag_source); | ||||
|  | ||||
|     // 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); | ||||
|  | ||||
|     const GLuint texture = framebuffer_texture.resource.handle; | ||||
|     glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||||
|  | ||||
|     // Clear screen to black | ||||
|     const u8 framebuffer_data[4] = {0, 0, 0, 0}; | ||||
|     glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||||
|                     framebuffer_data); | ||||
|  | ||||
|     aa_framebuffer.Create(); | ||||
|  | ||||
|     smaa_area_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||||
|     glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||||
|                         GL_UNSIGNED_BYTE, areaTexBytes); | ||||
|     smaa_search_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||||
|     glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||||
|                         GL_UNSIGNED_BYTE, searchTexBytes); | ||||
| } | ||||
|  | ||||
| void RendererOpenGL::AddTelemetryFields() { | ||||
|     const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; | ||||
|     const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; | ||||
| @@ -373,283 +154,6 @@ void RendererOpenGL::AddTelemetryFields() { | ||||
|     telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | ||||
| } | ||||
|  | ||||
| void RendererOpenGL::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     framebuffer_texture.width = framebuffer.width; | ||||
|     framebuffer_texture.height = framebuffer.height; | ||||
|     framebuffer_texture.pixel_format = framebuffer.pixel_format; | ||||
|  | ||||
|     const auto pixel_format{ | ||||
|         VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||||
|     const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||||
|     gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * | ||||
|                                bytes_per_pixel); | ||||
|  | ||||
|     GLint internal_format; | ||||
|     switch (framebuffer.pixel_format) { | ||||
|     case Service::android::PixelFormat::Rgba8888: | ||||
|         internal_format = GL_RGBA8; | ||||
|         framebuffer_texture.gl_format = GL_RGBA; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||
|         break; | ||||
|     case Service::android::PixelFormat::Rgb565: | ||||
|         internal_format = GL_RGB565; | ||||
|         framebuffer_texture.gl_format = GL_RGB; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||||
|         break; | ||||
|     default: | ||||
|         internal_format = GL_RGBA8; | ||||
|         framebuffer_texture.gl_format = GL_RGBA; | ||||
|         framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||
|         // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||||
|         //                   static_cast<u32>(framebuffer.pixel_format)); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     framebuffer_texture.resource.Release(); | ||||
|     framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, | ||||
|                        framebuffer_texture.width, framebuffer_texture.height); | ||||
|     aa_texture.Release(); | ||||
|     aa_texture.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
|     glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); | ||||
|     smaa_edges_tex.Release(); | ||||
|     smaa_edges_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
|     smaa_blend_tex.Release(); | ||||
|     smaa_blend_tex.Create(GL_TEXTURE_2D); | ||||
|     glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||||
|                        Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||||
| } | ||||
|  | ||||
| void RendererOpenGL::DrawScreen(const Tegra::FramebufferConfig& framebuffer, | ||||
|                                 const Layout::FramebufferLayout& layout) { | ||||
|     FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); | ||||
|     const auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); | ||||
|  | ||||
|     // TODO: Signal state tracker about these changes | ||||
|     state_tracker.NotifyScreenDrawVertexArray(); | ||||
|     state_tracker.NotifyPolygonModes(); | ||||
|     state_tracker.NotifyViewport0(); | ||||
|     state_tracker.NotifyScissor0(); | ||||
|     state_tracker.NotifyColorMask(0); | ||||
|     state_tracker.NotifyBlend0(); | ||||
|     state_tracker.NotifyFramebuffer(); | ||||
|     state_tracker.NotifyFrontFace(); | ||||
|     state_tracker.NotifyCullTest(); | ||||
|     state_tracker.NotifyDepthTest(); | ||||
|     state_tracker.NotifyStencilTest(); | ||||
|     state_tracker.NotifyPolygonOffset(); | ||||
|     state_tracker.NotifyRasterizeEnable(); | ||||
|     state_tracker.NotifyFramebufferSRGB(); | ||||
|     state_tracker.NotifyLogicOp(); | ||||
|     state_tracker.NotifyClipControl(); | ||||
|     state_tracker.NotifyAlphaTest(); | ||||
|  | ||||
|     state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||||
|  | ||||
|     glEnable(GL_CULL_FACE); | ||||
|     glDisable(GL_COLOR_LOGIC_OP); | ||||
|     glDisable(GL_DEPTH_TEST); | ||||
|     glDisable(GL_STENCIL_TEST); | ||||
|     glDisable(GL_POLYGON_OFFSET_FILL); | ||||
|     glDisable(GL_RASTERIZER_DISCARD); | ||||
|     glDisable(GL_ALPHA_TEST); | ||||
|     glDisablei(GL_BLEND, 0); | ||||
|     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||||
|     glCullFace(GL_BACK); | ||||
|     glFrontFace(GL_CW); | ||||
|     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||||
|     glDepthRangeIndexed(0, 0.0, 0.0); | ||||
|  | ||||
|     glBindTextureUnit(0, 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); | ||||
|         auto viewport_width = static_cast<GLfloat>(scissor_width); | ||||
|         auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); | ||||
|         auto viewport_height = static_cast<GLfloat>(scissor_height); | ||||
|  | ||||
|         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: { | ||||
|             program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); | ||||
|             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||||
|             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|         } break; | ||||
|         case Settings::AntiAliasing::Smaa: { | ||||
|             glClearColor(0, 0, 0, 0); | ||||
|             glFrontFace(GL_CCW); | ||||
|             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||||
|             glBindSampler(1, present_sampler.handle); | ||||
|             glBindSampler(2, present_sampler.handle); | ||||
|  | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       smaa_edges_tex.handle, 0); | ||||
|             glClear(GL_COLOR_BUFFER_BIT); | ||||
|             program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, | ||||
|                                                 smaa_edge_detection_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|  | ||||
|             glBindTextureUnit(0, smaa_edges_tex.handle); | ||||
|             glBindTextureUnit(1, smaa_area_tex.handle); | ||||
|             glBindTextureUnit(2, smaa_search_tex.handle); | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       smaa_blend_tex.handle, 0); | ||||
|             glClear(GL_COLOR_BUFFER_BIT); | ||||
|             program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, | ||||
|                                                 smaa_blending_weight_calculation_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|  | ||||
|             glBindTextureUnit(0, info.display_texture); | ||||
|             glBindTextureUnit(1, smaa_blend_tex.handle); | ||||
|             glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||||
|                                       aa_texture.handle, 0); | ||||
|             program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, | ||||
|                                                 smaa_neighborhood_blending_frag.handle); | ||||
|             glDrawArrays(GL_TRIANGLES, 0, 3); | ||||
|             glFrontFace(GL_CW); | ||||
|         } break; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|  | ||||
|         glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||||
|         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||||
|  | ||||
|         glBindTextureUnit(0, aa_texture.handle); | ||||
|     } | ||||
|     glDisablei(GL_SCISSOR_TEST, 0); | ||||
|  | ||||
|     if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||||
|         if (!fsr->AreBuffersInitialized()) { | ||||
|             fsr->InitBuffers(); | ||||
|         } | ||||
|  | ||||
|         glBindSampler(0, present_sampler.handle); | ||||
|         fsr->Draw(program_manager, layout.screen, info.scaled_width, info.scaled_height, crop); | ||||
|     } else { | ||||
|         if (fsr->AreBuffersInitialized()) { | ||||
|             fsr->ReleaseBuffers(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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::NearestNeighbor: | ||||
|         case Settings::ScalingFilter::Bilinear: | ||||
|             return present_bilinear_fragment.handle; | ||||
|         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::Fsr: | ||||
|             return fsr->GetPresentFragmentProgram().handle; | ||||
|         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); | ||||
|  | ||||
|     // TODO | ||||
|     // program_manager.RestoreGuestPipeline(); | ||||
| } | ||||
|  | ||||
| void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     if (!renderer_settings.screenshot_requested) { | ||||
|         return; | ||||
| @@ -672,7 +176,7 @@ void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffe | ||||
|     glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | ||||
|     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | ||||
|  | ||||
|     DrawScreen(framebuffer, layout); | ||||
|     blit_screen->DrawScreen(framebuffer, layout); | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | ||||
|     glPixelStorei(GL_PACK_ROW_LENGTH, 0); | ||||
|   | ||||
| @@ -25,38 +25,13 @@ namespace Core::Frontend { | ||||
| class EmuWindow; | ||||
| } | ||||
|  | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
|  | ||||
| namespace Layout { | ||||
| struct FramebufferLayout; | ||||
| } | ||||
|  | ||||
| namespace Tegra { | ||||
| class GPU; | ||||
| } | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
| /// Structure used for storing information about the textures for the Switch screen | ||||
| struct TextureInfo { | ||||
|     OGLTexture resource; | ||||
|     GLsizei width; | ||||
|     GLsizei height; | ||||
|     GLenum gl_format; | ||||
|     GLenum gl_type; | ||||
|     Service::android::PixelFormat pixel_format; | ||||
| }; | ||||
|  | ||||
| /// Structure used for storing information about the display target for the Switch screen | ||||
| struct FramebufferTextureInfo { | ||||
|     GLuint display_texture{}; | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     u32 scaled_width; | ||||
|     u32 scaled_height; | ||||
| }; | ||||
| class BlitScreen; | ||||
|  | ||||
| class RendererOpenGL final : public VideoCore::RendererBase { | ||||
| public: | ||||
| @@ -77,24 +52,9 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     /// Initializes the OpenGL state and creates persistent objects. | ||||
|     void InitOpenGLObjects(); | ||||
|  | ||||
|     void AddTelemetryFields(); | ||||
|  | ||||
|     void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); | ||||
|  | ||||
|     /// Draws the emulated screens to the emulator window. | ||||
|     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); | ||||
|  | ||||
|     Core::TelemetrySession& telemetry_session; | ||||
|     Core::Frontend::EmuWindow& emu_window; | ||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||
| @@ -104,43 +64,9 @@ private: | ||||
|     StateTracker state_tracker; | ||||
|     ProgramManager program_manager; | ||||
|     RasterizerOpenGL rasterizer; | ||||
|  | ||||
|     // OpenGL object IDs | ||||
|     OGLSampler present_sampler; | ||||
|     OGLSampler present_sampler_nn; | ||||
|     OGLBuffer vertex_buffer; | ||||
|     OGLProgram fxaa_vertex; | ||||
|     OGLProgram fxaa_fragment; | ||||
|     OGLProgram present_vertex; | ||||
|     OGLProgram present_bilinear_fragment; | ||||
|     OGLProgram present_bicubic_fragment; | ||||
|     OGLProgram present_gaussian_fragment; | ||||
|     OGLProgram present_scaleforce_fragment; | ||||
|     OGLFramebuffer screenshot_framebuffer; | ||||
|  | ||||
|     // GPU address of the vertex buffer | ||||
|     GLuint64EXT vertex_buffer_address = 0; | ||||
|  | ||||
|     /// Display information for Switch screen | ||||
|     TextureInfo framebuffer_texture; | ||||
|     OGLTexture aa_texture; | ||||
|     OGLFramebuffer aa_framebuffer; | ||||
|  | ||||
|     OGLProgram smaa_edge_detection_vert; | ||||
|     OGLProgram smaa_blending_weight_calculation_vert; | ||||
|     OGLProgram smaa_neighborhood_blending_vert; | ||||
|     OGLProgram smaa_edge_detection_frag; | ||||
|     OGLProgram smaa_blending_weight_calculation_frag; | ||||
|     OGLProgram smaa_neighborhood_blending_frag; | ||||
|     OGLTexture smaa_area_tex; | ||||
|     OGLTexture smaa_search_tex; | ||||
|     OGLTexture smaa_edges_tex; | ||||
|     OGLTexture smaa_blend_tex; | ||||
|  | ||||
|     std::unique_ptr<FSR> fsr; | ||||
|  | ||||
|     /// OpenGL framebuffer data | ||||
|     std::vector<u8> gl_framebuffer_data; | ||||
|     std::unique_ptr<BlitScreen> blit_screen; | ||||
| }; | ||||
|  | ||||
| } // namespace OpenGL | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Liam
					Liam