From 2b1dd3bef511806aa479ec93e3d9b414db80d4a9 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 12 Jan 2024 00:46:17 -0500 Subject: [PATCH] renderer_opengl: isolate core presentation code --- src/video_core/CMakeLists.txt | 2 + .../renderer_opengl/gl_blit_screen.cpp | 519 ++++++++++++++++++ .../renderer_opengl/gl_blit_screen.h | 110 ++++ .../renderer_opengl/gl_rasterizer.h | 1 + .../renderer_opengl/renderer_opengl.cpp | 506 +---------------- .../renderer_opengl/renderer_opengl.h | 78 +-- 6 files changed, 639 insertions(+), 577 deletions(-) create mode 100644 src/video_core/renderer_opengl/gl_blit_screen.cpp create mode 100644 src/video_core/renderer_opengl/gl_blit_screen.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 36aa7bb66..c158970f2 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -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 diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp new file mode 100644 index 000000000..88757ba38 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -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(x), static_cast(y)}}, tex_coord{{u, v}} {} + + std::array position; + std::array tex_coord; +}; + +/** + * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left + * corner and (width, height) on the lower-bottom. + * + * The projection part of the matrix is trivial, hence these operations are represented + * by a 3x2 matrix. + */ +std::array MakeOrthographicMatrix(float width, float height) { + std::array matrix; // Laid out in column-major order + + // clang-format off + matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; + matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; + // Last matrix row is implicitly assumed to be [0, 0, 1]. + // clang-format on + + return matrix; +} +} // namespace + +BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, + Tegra::MaxwellDeviceMemoryManager& device_memory_, + StateTracker& state_tracker_, ProgramManager& program_manager_, + Device& device_) + : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), + program_manager(program_manager_), device(device_) { + // Create shader programs + 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(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(framebuffer.width) || + framebuffer_texture.height != static_cast(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(framebuffer_addr)}; + const std::span 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(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(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(scissor_width); + auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); + auto viewport_height = static_cast(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(layout.width), static_cast(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(layout.width), + static_cast(layout.height)); + + glEnableVertexAttribArray(PositionLocation); + glEnableVertexAttribArray(TexCoordLocation); + glVertexAttribDivisor(PositionLocation, 0); + glVertexAttribDivisor(TexCoordLocation, 0); + glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, position)); + glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, tex_coord)); + glVertexAttribBinding(PositionLocation, 0); + glVertexAttribBinding(TexCoordLocation, 0); + if (device.HasVertexBufferUnifiedMemory()) { + glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, + sizeof(vertices)); + } else { + glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); + } + + if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { + glBindSampler(0, present_sampler.handle); + } else { + glBindSampler(0, present_sampler_nn.handle); + } + + // Update background color before drawing + glClearColor(Settings::values.bg_red.GetValue() / 255.0f, + Settings::values.bg_green.GetValue() / 255.0f, + Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); + + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // TODO + // program_manager.RestoreGuestPipeline(); +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h new file mode 100644 index 000000000..13d769958 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#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; + + /// OpenGL framebuffer data + std::vector gl_framebuffer_data; + + // GPU address of the vertex buffer + GLuint64EXT vertex_buffer_address = 0; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index ee82d9f3a..6eae51ff7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -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" diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 2b9ebff92..38b0aacf4 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -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(x), static_cast(y)}}, tex_coord{{u, v}} {} - - std::array position; - std::array tex_coord; -}; - -/** - * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - */ -std::array MakeOrthographicMatrix(float width, float height) { - std::array matrix; // Laid out in column-major order - - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; - // Last matrix row is implicitly assumed to be [0, 0, 1]. - // clang-format on - - return matrix; -} - 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(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(framebuffer.width) || - framebuffer_texture.height != static_cast(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(framebuffer_addr)}; - const std::span 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(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(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(glGetString(GL_VERSION))}; const char* const gpu_vendor{reinterpret_cast(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(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(scissor_width); - auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); - auto viewport_height = static_cast(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(layout.width), static_cast(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(layout.width), - static_cast(layout.height)); - - glEnableVertexAttribArray(PositionLocation); - glEnableVertexAttribArray(TexCoordLocation); - glVertexAttribDivisor(PositionLocation, 0); - glVertexAttribDivisor(TexCoordLocation, 0); - glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, position)); - glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, tex_coord)); - glVertexAttribBinding(PositionLocation, 0); - glVertexAttribBinding(TexCoordLocation, 0); - if (device.HasVertexBufferUnifiedMemory()) { - glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); - glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, - sizeof(vertices)); - } else { - glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); - } - - if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { - glBindSampler(0, present_sampler.handle); - } else { - glBindSampler(0, present_sampler_nn.handle); - } - - // Update background color before drawing - glClearColor(Settings::values.bg_red.GetValue() / 255.0f, - Settings::values.bg_green.GetValue() / 255.0f, - Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); - - glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - // 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); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 3a83a9b78..23aff055a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -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; - - /// OpenGL framebuffer data - std::vector gl_framebuffer_data; + std::unique_ptr blit_screen; }; } // namespace OpenGL