diff options
-rw-r--r-- | src/video_core/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_blit_screen.cpp | 198 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_blit_screen.h | 38 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/present/layer.cpp | 215 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/present/layer.h | 80 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/present/present_uniforms.h | 43 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/present/window_adapt_pass.cpp | 91 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/present/window_adapt_pass.h | 14 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 8 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 2 |
10 files changed, 402 insertions, 290 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 7526de699..16c905db9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -122,6 +122,9 @@ add_library(video_core STATIC renderer_opengl/present/fsr.h renderer_opengl/present/fxaa.cpp renderer_opengl/present/fxaa.h + renderer_opengl/present/layer.cpp + renderer_opengl/present/layer.h + renderer_opengl/present/present_uniforms.h renderer_opengl/present/smaa.cpp renderer_opengl/present/smaa.h renderer_opengl/present/util.h diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index f9dbef0fc..6ba8b214b 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -1,18 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "video_core/framebuffer_config.h" +#include "common/settings.h" #include "video_core/renderer_opengl/gl_blit_screen.h" -#include "video_core/renderer_opengl/gl_rasterizer.h" -#include "video_core/renderer_opengl/gl_shader_manager.h" -#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state_tracker.h" #include "video_core/renderer_opengl/present/filters.h" -#include "video_core/renderer_opengl/present/fsr.h" -#include "video_core/renderer_opengl/present/fxaa.h" -#include "video_core/renderer_opengl/present/smaa.h" +#include "video_core/renderer_opengl/present/layer.h" #include "video_core/renderer_opengl/present/window_adapt_pass.h" -#include "video_core/textures/decoders.h" namespace OpenGL { @@ -21,130 +15,12 @@ BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, StateTracker& state_tracker_, ProgramManager& program_manager_, Device& device_) : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), - program_manager(program_manager_), device(device_) { - // 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); -} + program_manager(program_manager_), device(device_) {} BlitScreen::~BlitScreen() = default; -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); - - fxaa.reset(); - smaa.reset(); -} - -void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, +void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, const Layout::FramebufferLayout& layout) { - FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); - auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); - // TODO: Signal state tracker about these changes state_tracker.NotifyScreenDrawVertexArray(); state_tracker.NotifyPolygonModes(); @@ -163,7 +39,6 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, state_tracker.NotifyLogicOp(); state_tracker.NotifyClipControl(); state_tracker.NotifyAlphaTest(); - state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); glEnable(GL_CULL_FACE); @@ -180,76 +55,17 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthRangeIndexed(0, 0.0, 0.0); - GLint old_read_fb; - GLint old_draw_fb; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - - GLuint texture = info.display_texture; - - auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); - if (anti_aliasing != Settings::AntiAliasing::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); - - switch (anti_aliasing) { - case Settings::AntiAliasing::Fxaa: - CreateFXAA(); - texture = fxaa->Draw(program_manager, info.display_texture); - break; - case Settings::AntiAliasing::Smaa: - default: - CreateSMAA(); - texture = smaa->Draw(program_manager, info.display_texture); - break; - } + while (layers.size() < framebuffers.size()) { + layers.emplace_back(rasterizer, device_memory); } - glDisablei(GL_SCISSOR_TEST, 0); - - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - if (!fsr || fsr->NeedsRecreation(layout.screen)) { - fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); - } - - texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); - crop = {0, 0, 1, 1}; - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); - CreateWindowAdapt(); - window_adapt->DrawToFramebuffer(program_manager, texture, layout, crop); + window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); // TODO // program_manager.RestoreGuestPipeline(); } -void BlitScreen::CreateFXAA() { - smaa.reset(); - if (!fxaa) { - fxaa = std::make_unique<FXAA>( - Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), - Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); - } -} - -void BlitScreen::CreateSMAA() { - fxaa.reset(); - if (!smaa) { - smaa = std::make_unique<SMAA>( - Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), - Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); - } -} - void BlitScreen::CreateWindowAdapt() { if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { return; diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h index f42f89dee..0c3d838f1 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.h +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -3,8 +3,9 @@ #pragma once +#include <list> #include <memory> -#include <vector> +#include <span> #include "core/hle/service/nvnflinger/pixel_format.h" #include "video_core/host1x/gpu_device_memory_manager.h" @@ -25,24 +26,12 @@ enum class ScalingFilter : u32; namespace OpenGL { class Device; -class FSR; -class FXAA; +class Layer; class ProgramManager; class RasterizerOpenGL; -class SMAA; class StateTracker; class WindowAdaptPass; -/// 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{}; @@ -60,20 +49,11 @@ public: Device& device); ~BlitScreen(); - void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); - /// Draws the emulated screens to the emulator window. - void DrawScreen(const Tegra::FramebufferConfig& framebuffer, + void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, const Layout::FramebufferLayout& layout); - /// Loads framebuffer from emulated memory into the active OpenGL texture. - FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); - - FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); - private: - void CreateFXAA(); - void CreateSMAA(); void CreateWindowAdapt(); RasterizerOpenGL& rasterizer; @@ -82,18 +62,10 @@ private: ProgramManager& program_manager; Device& device; - /// Display information for Switch screen - TextureInfo framebuffer_texture; - - std::unique_ptr<FSR> fsr; - std::unique_ptr<FXAA> fxaa; - std::unique_ptr<SMAA> smaa; - Settings::ScalingFilter current_window_adapt{}; std::unique_ptr<WindowAdaptPass> window_adapt; - /// OpenGL framebuffer data - std::vector<u8> gl_framebuffer_data; + std::list<Layer> layers; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp new file mode 100644 index 000000000..8643e07c6 --- /dev/null +++ b/src/video_core/renderer_opengl/present/layer.cpp @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/framebuffer_config.h" +#include "video_core/renderer_opengl/gl_blit_screen.h" +#include "video_core/renderer_opengl/gl_rasterizer.h" +#include "video_core/renderer_opengl/present/fsr.h" +#include "video_core/renderer_opengl/present/fxaa.h" +#include "video_core/renderer_opengl/present/layer.h" +#include "video_core/renderer_opengl/present/present_uniforms.h" +#include "video_core/renderer_opengl/present/smaa.h" +#include "video_core/surface.h" +#include "video_core/textures/decoders.h" + +namespace OpenGL { + +Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) + : rasterizer(rasterizer_), device_memory(device_memory_) { + // 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); +} + +Layer::~Layer() = default; + +GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, + std::array<ScreenRectVertex, 4>& out_vertices, + ProgramManager& program_manager, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout) { + FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); + auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); + GLuint texture = info.display_texture; + + auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + if (anti_aliasing != Settings::AntiAliasing::None) { + glEnablei(GL_SCISSOR_TEST, 0); + auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); + auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); + + glScissorIndexed(0, 0, 0, viewport_width, viewport_height); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width), + static_cast<GLfloat>(viewport_height)); + + switch (anti_aliasing) { + case Settings::AntiAliasing::Fxaa: + CreateFXAA(); + texture = fxaa->Draw(program_manager, info.display_texture); + break; + case Settings::AntiAliasing::Smaa: + default: + CreateSMAA(); + texture = smaa->Draw(program_manager, info.display_texture); + break; + } + } + + glDisablei(GL_SCISSOR_TEST, 0); + + if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + if (!fsr || fsr->NeedsRecreation(layout.screen)) { + fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); + } + + texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); + crop = {0, 0, 1, 1}; + } + + out_matrix = + MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); + + // 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(); + + out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); + out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); + out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); + out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); + + return texture; +} + +FramebufferTextureInfo Layer::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 Layer::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 Layer::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); + + fxaa.reset(); + smaa.reset(); +} + +void Layer::CreateFXAA() { + smaa.reset(); + if (!fxaa) { + fxaa = std::make_unique<FXAA>( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +void Layer::CreateSMAA() { + fxaa.reset(); + if (!smaa) { + smaa = std::make_unique<SMAA>( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h new file mode 100644 index 000000000..ef1055abf --- /dev/null +++ b/src/video_core/renderer_opengl/present/layer.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <vector> + +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Service::android { +enum class PixelFormat : u32; +}; + +namespace Tegra { +struct FramebufferConfig; +} + +namespace OpenGL { + +struct FramebufferTextureInfo; +class FSR; +class FXAA; +class ProgramManager; +class RasterizerOpenGL; +class SMAA; + +/// 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; +}; + +struct ScreenRectVertex; + +class Layer { +public: + explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); + ~Layer(); + + GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, + std::array<ScreenRectVertex, 4>& out_vertices, + ProgramManager& program_manager, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout); + +private: + /// Loads framebuffer from emulated memory into the active OpenGL texture. + FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); + FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); + void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); + + void CreateFXAA(); + void CreateSMAA(); + +private: + RasterizerOpenGL& rasterizer; + Tegra::MaxwellDeviceMemoryManager& device_memory; + + /// OpenGL framebuffer data + std::vector<u8> gl_framebuffer_data; + + /// Display information for Switch screen + TextureInfo framebuffer_texture; + + std::unique_ptr<FSR> fsr; + std::unique_ptr<FXAA> fxaa; + std::unique_ptr<SMAA> smaa; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/present_uniforms.h b/src/video_core/renderer_opengl/present/present_uniforms.h new file mode 100644 index 000000000..3a19f05c7 --- /dev/null +++ b/src/video_core/renderer_opengl/present/present_uniforms.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +constexpr GLint PositionLocation = 0; +constexpr GLint TexCoordLocation = 1; +constexpr GLint ModelViewMatrixLocation = 0; + +struct ScreenRectVertex { + constexpr ScreenRectVertex() = default; + + constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) + : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} + + std::array<GLfloat, 2> position{}; + std::array<GLfloat, 2> tex_coord{}; +}; + +/** + * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left + * corner and (width, height) on the lower-bottom. + * + * The projection part of the matrix is trivial, hence these operations are represented + * by a 3x2 matrix. + */ +static inline 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 OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp index 168fa1aea..4d681606b 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp @@ -2,47 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" +#include "video_core/framebuffer_config.h" #include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/layer.h" +#include "video_core/renderer_opengl/present/present_uniforms.h" #include "video_core/renderer_opengl/present/window_adapt_pass.h" namespace OpenGL { -namespace { -constexpr GLint PositionLocation = 0; -constexpr GLint TexCoordLocation = 1; -constexpr GLint ModelViewMatrixLocation = 0; - -struct ScreenRectVertex { - constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) - : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} - - std::array<GLfloat, 2> position; - std::array<GLfloat, 2> tex_coord; -}; - -/** - * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - */ -std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { - std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order - - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; - // Last matrix row is implicitly assumed to be [0, 0, 1]. - // clang-format on - - return matrix; -} -} // namespace - WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, std::string_view frag_source) : device(device_), sampler(std::move(sampler_)) { @@ -65,32 +35,30 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, WindowAdaptPass::~WindowAdaptPass() = default; -void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, GLuint texture, - const Layout::FramebufferLayout& layout, - const Common::Rectangle<f32>& crop) { - glBindTextureUnit(0, texture); +void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout) { + GLint old_read_fb; + GLint old_draw_fb; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + + const size_t layer_count = framebuffers.size(); + std::vector<GLuint> textures(layer_count); + std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count); + std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count); + + auto layer_it = layers.begin(); + for (size_t i = 0; i < layer_count; i++) { + textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, + framebuffers[i], layout); + layer_it++; + } - const std::array ortho_matrix = - MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); program_manager.BindPresentPrograms(vert.handle, frag.handle); - glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, - ortho_matrix.data()); - - // Map the coordinates to the screen. - const auto& screen = layout.screen; - const auto x = screen.left; - const auto y = screen.top; - const auto w = screen.GetWidth(); - const auto h = screen.GetHeight(); - - const std::array vertices = { - ScreenRectVertex(x, y, crop.left, crop.top), - ScreenRectVertex(x + w, y, crop.right, crop.top), - ScreenRectVertex(x, y + h, crop.left, crop.bottom), - ScreenRectVertex(x + w, y + h, crop.right, crop.bottom), - }; - glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); glDisable(GL_FRAMEBUFFER_SRGB); glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), @@ -109,7 +77,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, GLuint if (device.HasVertexBufferUnifiedMemory()) { glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, - sizeof(vertices)); + sizeof(decltype(vertices)::value_type)); } else { glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); } @@ -122,7 +90,14 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, GLuint Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + for (size_t i = 0; i < layer_count; i++) { + glBindTextureUnit(0, textures[i]); + glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, + matrices[i].data()); + glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h index 65dcd09ff..00975a9c6 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.h +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h @@ -3,6 +3,9 @@ #pragma once +#include <list> +#include <span> + #include "common/math_util.h" #include "video_core/renderer_opengl/gl_resource_manager.h" @@ -10,9 +13,14 @@ namespace Layout { struct FramebufferLayout; } +namespace Tegra { +struct FramebufferConfig; +} + namespace OpenGL { class Device; +class Layer; class ProgramManager; class WindowAdaptPass final { @@ -21,9 +29,9 @@ public: std::string_view frag_source); ~WindowAdaptPass(); - void DrawToFramebuffer(ProgramManager& program_manager, GLuint texture, - const Layout::FramebufferLayout& layout, - const Common::Rectangle<f32>& crop); + void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout); private: const Device& device; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 0d138c189..10a9f973c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -130,10 +130,10 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { return; } - RenderScreenshot(*framebuffer); + RenderScreenshot(framebuffer); state_tracker.BindFramebuffer(0); - blit_screen->DrawScreen(*framebuffer, emu_window.GetFramebufferLayout()); + blit_screen->DrawScreen(std::span(framebuffer, 1), emu_window.GetFramebufferLayout()); ++m_current_frame; @@ -159,7 +159,7 @@ void RendererOpenGL::AddTelemetryFields() { telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); } -void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer) { +void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { if (!renderer_settings.screenshot_requested) { return; } @@ -181,7 +181,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); - blit_screen->DrawScreen(framebuffer, layout); + blit_screen->DrawScreen(std::span(framebuffer, 1), 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 7ab163372..df76d3d05 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -52,7 +52,7 @@ public: private: void AddTelemetryFields(); - void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); + void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); Core::TelemetrySession& telemetry_session; Core::Frontend::EmuWindow& emu_window; |