diff options
| author | 2024-01-12 00:46:17 -0500 | |
|---|---|---|
| committer | 2024-01-31 11:27:20 -0500 | |
| commit | 2b1dd3bef511806aa479ec93e3d9b414db80d4a9 (patch) | |
| tree | 24ef32a78ab1c4c13363fbed9d61774724753aa8 /src/video_core/renderer_opengl | |
| parent | video_core: consistently account for resolution scaling when rendering (diff) | |
| download | yuzu-2b1dd3bef511806aa479ec93e3d9b414db80d4a9.tar.gz yuzu-2b1dd3bef511806aa479ec93e3d9b414db80d4a9.tar.xz yuzu-2b1dd3bef511806aa479ec93e3d9b414db80d4a9.zip | |
renderer_opengl: isolate core presentation code
Diffstat (limited to 'src/video_core/renderer_opengl')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_blit_screen.cpp | 519 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_blit_screen.h | 110 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 1 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 506 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 78 |
5 files changed, 637 insertions, 577 deletions
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/framebuffer_config.h" | ||
| 5 | #include "video_core/host_shaders/ffx_a_h.h" | ||
| 6 | #include "video_core/host_shaders/ffx_fsr1_h.h" | ||
| 7 | #include "video_core/host_shaders/full_screen_triangle_vert.h" | ||
| 8 | #include "video_core/host_shaders/fxaa_frag.h" | ||
| 9 | #include "video_core/host_shaders/fxaa_vert.h" | ||
| 10 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" | ||
| 11 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" | ||
| 12 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" | ||
| 13 | #include "video_core/host_shaders/opengl_present_frag.h" | ||
| 14 | #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" | ||
| 15 | #include "video_core/host_shaders/opengl_present_vert.h" | ||
| 16 | #include "video_core/host_shaders/opengl_smaa_glsl.h" | ||
| 17 | #include "video_core/host_shaders/present_bicubic_frag.h" | ||
| 18 | #include "video_core/host_shaders/present_gaussian_frag.h" | ||
| 19 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||
| 20 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||
| 21 | #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||
| 22 | #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||
| 23 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||
| 24 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" | ||
| 25 | #include "video_core/renderer_opengl/gl_blit_screen.h" | ||
| 26 | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||
| 27 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 28 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 29 | #include "video_core/renderer_opengl/gl_state_tracker.h" | ||
| 30 | #include "video_core/smaa_area_tex.h" | ||
| 31 | #include "video_core/smaa_search_tex.h" | ||
| 32 | #include "video_core/textures/decoders.h" | ||
| 33 | |||
| 34 | namespace OpenGL { | ||
| 35 | |||
| 36 | namespace { | ||
| 37 | constexpr GLint PositionLocation = 0; | ||
| 38 | constexpr GLint TexCoordLocation = 1; | ||
| 39 | constexpr GLint ModelViewMatrixLocation = 0; | ||
| 40 | |||
| 41 | struct ScreenRectVertex { | ||
| 42 | constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) | ||
| 43 | : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||
| 44 | |||
| 45 | std::array<GLfloat, 2> position; | ||
| 46 | std::array<GLfloat, 2> tex_coord; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left | ||
| 51 | * corner and (width, height) on the lower-bottom. | ||
| 52 | * | ||
| 53 | * The projection part of the matrix is trivial, hence these operations are represented | ||
| 54 | * by a 3x2 matrix. | ||
| 55 | */ | ||
| 56 | std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||
| 57 | std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order | ||
| 58 | |||
| 59 | // clang-format off | ||
| 60 | matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; | ||
| 61 | matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; | ||
| 62 | // Last matrix row is implicitly assumed to be [0, 0, 1]. | ||
| 63 | // clang-format on | ||
| 64 | |||
| 65 | return matrix; | ||
| 66 | } | ||
| 67 | } // namespace | ||
| 68 | |||
| 69 | BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, | ||
| 70 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | ||
| 71 | StateTracker& state_tracker_, ProgramManager& program_manager_, | ||
| 72 | Device& device_) | ||
| 73 | : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), | ||
| 74 | program_manager(program_manager_), device(device_) { | ||
| 75 | // Create shader programs | ||
| 76 | fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||
| 77 | fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||
| 78 | |||
| 79 | const auto replace_include = [](std::string& shader_source, std::string_view include_name, | ||
| 80 | std::string_view include_content) { | ||
| 81 | const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||
| 82 | const std::size_t pos = shader_source.find(include_string); | ||
| 83 | ASSERT(pos != std::string::npos); | ||
| 84 | shader_source.replace(pos, include_string.size(), include_content); | ||
| 85 | }; | ||
| 86 | |||
| 87 | const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||
| 88 | std::string shader_source{specialized_source}; | ||
| 89 | replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||
| 90 | return CreateProgram(shader_source, stage); | ||
| 91 | }; | ||
| 92 | |||
| 93 | smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||
| 94 | smaa_edge_detection_frag = | ||
| 95 | SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||
| 96 | smaa_blending_weight_calculation_vert = | ||
| 97 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||
| 98 | smaa_blending_weight_calculation_frag = | ||
| 99 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||
| 100 | smaa_neighborhood_blending_vert = | ||
| 101 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||
| 102 | smaa_neighborhood_blending_frag = | ||
| 103 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||
| 104 | |||
| 105 | present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); | ||
| 106 | present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); | ||
| 107 | present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); | ||
| 108 | present_gaussian_fragment = | ||
| 109 | CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); | ||
| 110 | present_scaleforce_fragment = | ||
| 111 | CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), | ||
| 112 | GL_FRAGMENT_SHADER); | ||
| 113 | |||
| 114 | std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||
| 115 | replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||
| 116 | replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||
| 117 | |||
| 118 | std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||
| 119 | std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||
| 120 | replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 121 | replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 122 | |||
| 123 | fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, | ||
| 124 | fsr_rcas_frag_source); | ||
| 125 | |||
| 126 | // Generate presentation sampler | ||
| 127 | present_sampler.Create(); | ||
| 128 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 129 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 130 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 131 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 132 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 133 | |||
| 134 | present_sampler_nn.Create(); | ||
| 135 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
| 136 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
| 137 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 138 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 139 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 140 | |||
| 141 | // Generate VBO handle for drawing | ||
| 142 | vertex_buffer.Create(); | ||
| 143 | |||
| 144 | // Attach vertex data to VAO | ||
| 145 | glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); | ||
| 146 | |||
| 147 | // Allocate textures for the screen | ||
| 148 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 149 | |||
| 150 | const GLuint texture = framebuffer_texture.resource.handle; | ||
| 151 | glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||
| 152 | |||
| 153 | // Clear screen to black | ||
| 154 | const u8 framebuffer_data[4] = {0, 0, 0, 0}; | ||
| 155 | glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||
| 156 | framebuffer_data); | ||
| 157 | |||
| 158 | aa_framebuffer.Create(); | ||
| 159 | |||
| 160 | smaa_area_tex.Create(GL_TEXTURE_2D); | ||
| 161 | glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||
| 162 | glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||
| 163 | GL_UNSIGNED_BYTE, areaTexBytes); | ||
| 164 | smaa_search_tex.Create(GL_TEXTURE_2D); | ||
| 165 | glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||
| 166 | glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||
| 167 | GL_UNSIGNED_BYTE, searchTexBytes); | ||
| 168 | |||
| 169 | // Enable unified vertex attributes and query vertex buffer address when the driver supports it | ||
| 170 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 171 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); | ||
| 172 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); | ||
| 173 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 174 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 175 | &vertex_buffer_address); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | FramebufferTextureInfo BlitScreen::PrepareRenderTarget( | ||
| 180 | const Tegra::FramebufferConfig& framebuffer) { | ||
| 181 | // If framebuffer is provided, reload it from memory to a texture | ||
| 182 | if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || | ||
| 183 | framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || | ||
| 184 | framebuffer_texture.pixel_format != framebuffer.pixel_format || | ||
| 185 | gl_framebuffer_data.empty()) { | ||
| 186 | // Reallocate texture if the framebuffer size has changed. | ||
| 187 | // This is expected to not happen very often and hence should not be a | ||
| 188 | // performance problem. | ||
| 189 | ConfigureFramebufferTexture(framebuffer); | ||
| 190 | } | ||
| 191 | |||
| 192 | // Load the framebuffer from memory if needed | ||
| 193 | return LoadFBToScreenInfo(framebuffer); | ||
| 194 | } | ||
| 195 | |||
| 196 | FramebufferTextureInfo BlitScreen::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | ||
| 197 | const DAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||
| 198 | const auto accelerated_info = | ||
| 199 | rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||
| 200 | if (accelerated_info) { | ||
| 201 | return *accelerated_info; | ||
| 202 | } | ||
| 203 | |||
| 204 | // Reset the screen info's display texture to its own permanent texture | ||
| 205 | FramebufferTextureInfo info{}; | ||
| 206 | info.display_texture = framebuffer_texture.resource.handle; | ||
| 207 | info.width = framebuffer.width; | ||
| 208 | info.height = framebuffer.height; | ||
| 209 | info.scaled_width = framebuffer.width; | ||
| 210 | info.scaled_height = framebuffer.height; | ||
| 211 | |||
| 212 | // TODO(Rodrigo): Read this from HLE | ||
| 213 | constexpr u32 block_height_log2 = 4; | ||
| 214 | const auto pixel_format{ | ||
| 215 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 216 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 217 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||
| 218 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 219 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||
| 220 | const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||
| 221 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||
| 222 | framebuffer.width, framebuffer.height, 1, block_height_log2, | ||
| 223 | 0); | ||
| 224 | |||
| 225 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||
| 226 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||
| 227 | |||
| 228 | // Update existing texture | ||
| 229 | // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||
| 230 | // they differ from the LCD resolution. | ||
| 231 | // TODO: Applications could theoretically crash yuzu here by specifying too large | ||
| 232 | // framebuffer sizes. We should make sure that this cannot happen. | ||
| 233 | glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, | ||
| 234 | framebuffer.height, framebuffer_texture.gl_format, | ||
| 235 | framebuffer_texture.gl_type, gl_framebuffer_data.data()); | ||
| 236 | |||
| 237 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 238 | |||
| 239 | return info; | ||
| 240 | } | ||
| 241 | |||
| 242 | void BlitScreen::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { | ||
| 243 | framebuffer_texture.width = framebuffer.width; | ||
| 244 | framebuffer_texture.height = framebuffer.height; | ||
| 245 | framebuffer_texture.pixel_format = framebuffer.pixel_format; | ||
| 246 | |||
| 247 | const auto pixel_format{ | ||
| 248 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 249 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 250 | gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * | ||
| 251 | bytes_per_pixel); | ||
| 252 | |||
| 253 | GLint internal_format; | ||
| 254 | switch (framebuffer.pixel_format) { | ||
| 255 | case Service::android::PixelFormat::Rgba8888: | ||
| 256 | internal_format = GL_RGBA8; | ||
| 257 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 258 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 259 | break; | ||
| 260 | case Service::android::PixelFormat::Rgb565: | ||
| 261 | internal_format = GL_RGB565; | ||
| 262 | framebuffer_texture.gl_format = GL_RGB; | ||
| 263 | framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||
| 264 | break; | ||
| 265 | default: | ||
| 266 | internal_format = GL_RGBA8; | ||
| 267 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 268 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 269 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||
| 270 | // static_cast<u32>(framebuffer.pixel_format)); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | |||
| 274 | framebuffer_texture.resource.Release(); | ||
| 275 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 276 | glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, | ||
| 277 | framebuffer_texture.width, framebuffer_texture.height); | ||
| 278 | aa_texture.Release(); | ||
| 279 | aa_texture.Create(GL_TEXTURE_2D); | ||
| 280 | glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, | ||
| 281 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 282 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 283 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); | ||
| 284 | smaa_edges_tex.Release(); | ||
| 285 | smaa_edges_tex.Create(GL_TEXTURE_2D); | ||
| 286 | glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, | ||
| 287 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 288 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 289 | smaa_blend_tex.Release(); | ||
| 290 | smaa_blend_tex.Create(GL_TEXTURE_2D); | ||
| 291 | glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, | ||
| 292 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 293 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 294 | } | ||
| 295 | |||
| 296 | void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer, | ||
| 297 | const Layout::FramebufferLayout& layout) { | ||
| 298 | FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); | ||
| 299 | const auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); | ||
| 300 | |||
| 301 | // TODO: Signal state tracker about these changes | ||
| 302 | state_tracker.NotifyScreenDrawVertexArray(); | ||
| 303 | state_tracker.NotifyPolygonModes(); | ||
| 304 | state_tracker.NotifyViewport0(); | ||
| 305 | state_tracker.NotifyScissor0(); | ||
| 306 | state_tracker.NotifyColorMask(0); | ||
| 307 | state_tracker.NotifyBlend0(); | ||
| 308 | state_tracker.NotifyFramebuffer(); | ||
| 309 | state_tracker.NotifyFrontFace(); | ||
| 310 | state_tracker.NotifyCullTest(); | ||
| 311 | state_tracker.NotifyDepthTest(); | ||
| 312 | state_tracker.NotifyStencilTest(); | ||
| 313 | state_tracker.NotifyPolygonOffset(); | ||
| 314 | state_tracker.NotifyRasterizeEnable(); | ||
| 315 | state_tracker.NotifyFramebufferSRGB(); | ||
| 316 | state_tracker.NotifyLogicOp(); | ||
| 317 | state_tracker.NotifyClipControl(); | ||
| 318 | state_tracker.NotifyAlphaTest(); | ||
| 319 | |||
| 320 | state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||
| 321 | |||
| 322 | glEnable(GL_CULL_FACE); | ||
| 323 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 324 | glDisable(GL_DEPTH_TEST); | ||
| 325 | glDisable(GL_STENCIL_TEST); | ||
| 326 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 327 | glDisable(GL_RASTERIZER_DISCARD); | ||
| 328 | glDisable(GL_ALPHA_TEST); | ||
| 329 | glDisablei(GL_BLEND, 0); | ||
| 330 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
| 331 | glCullFace(GL_BACK); | ||
| 332 | glFrontFace(GL_CW); | ||
| 333 | glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||
| 334 | glDepthRangeIndexed(0, 0.0, 0.0); | ||
| 335 | |||
| 336 | glBindTextureUnit(0, info.display_texture); | ||
| 337 | |||
| 338 | auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); | ||
| 339 | if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) { | ||
| 340 | LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); | ||
| 341 | anti_aliasing = Settings::AntiAliasing::None; | ||
| 342 | Settings::values.anti_aliasing.SetValue(anti_aliasing); | ||
| 343 | } | ||
| 344 | |||
| 345 | if (anti_aliasing != Settings::AntiAliasing::None) { | ||
| 346 | glEnablei(GL_SCISSOR_TEST, 0); | ||
| 347 | auto scissor_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); | ||
| 348 | auto viewport_width = static_cast<GLfloat>(scissor_width); | ||
| 349 | auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); | ||
| 350 | auto viewport_height = static_cast<GLfloat>(scissor_height); | ||
| 351 | |||
| 352 | glScissorIndexed(0, 0, 0, scissor_width, scissor_height); | ||
| 353 | glViewportIndexedf(0, 0.0f, 0.0f, viewport_width, viewport_height); | ||
| 354 | |||
| 355 | glBindSampler(0, present_sampler.handle); | ||
| 356 | GLint old_read_fb; | ||
| 357 | GLint old_draw_fb; | ||
| 358 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 359 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 360 | |||
| 361 | switch (anti_aliasing) { | ||
| 362 | case Settings::AntiAliasing::Fxaa: { | ||
| 363 | program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); | ||
| 364 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 365 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 366 | } break; | ||
| 367 | case Settings::AntiAliasing::Smaa: { | ||
| 368 | glClearColor(0, 0, 0, 0); | ||
| 369 | glFrontFace(GL_CCW); | ||
| 370 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 371 | glBindSampler(1, present_sampler.handle); | ||
| 372 | glBindSampler(2, present_sampler.handle); | ||
| 373 | |||
| 374 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 375 | smaa_edges_tex.handle, 0); | ||
| 376 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 377 | program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, | ||
| 378 | smaa_edge_detection_frag.handle); | ||
| 379 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 380 | |||
| 381 | glBindTextureUnit(0, smaa_edges_tex.handle); | ||
| 382 | glBindTextureUnit(1, smaa_area_tex.handle); | ||
| 383 | glBindTextureUnit(2, smaa_search_tex.handle); | ||
| 384 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 385 | smaa_blend_tex.handle, 0); | ||
| 386 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 387 | program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, | ||
| 388 | smaa_blending_weight_calculation_frag.handle); | ||
| 389 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 390 | |||
| 391 | glBindTextureUnit(0, info.display_texture); | ||
| 392 | glBindTextureUnit(1, smaa_blend_tex.handle); | ||
| 393 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 394 | aa_texture.handle, 0); | ||
| 395 | program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, | ||
| 396 | smaa_neighborhood_blending_frag.handle); | ||
| 397 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 398 | glFrontFace(GL_CW); | ||
| 399 | } break; | ||
| 400 | default: | ||
| 401 | UNREACHABLE(); | ||
| 402 | } | ||
| 403 | |||
| 404 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 405 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 406 | |||
| 407 | glBindTextureUnit(0, aa_texture.handle); | ||
| 408 | } | ||
| 409 | glDisablei(GL_SCISSOR_TEST, 0); | ||
| 410 | |||
| 411 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 412 | if (!fsr->AreBuffersInitialized()) { | ||
| 413 | fsr->InitBuffers(); | ||
| 414 | } | ||
| 415 | |||
| 416 | glBindSampler(0, present_sampler.handle); | ||
| 417 | fsr->Draw(program_manager, layout.screen, info.scaled_width, info.scaled_height, crop); | ||
| 418 | } else { | ||
| 419 | if (fsr->AreBuffersInitialized()) { | ||
| 420 | fsr->ReleaseBuffers(); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | const std::array ortho_matrix = | ||
| 425 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); | ||
| 426 | |||
| 427 | const auto fragment_handle = [this]() { | ||
| 428 | switch (Settings::values.scaling_filter.GetValue()) { | ||
| 429 | case Settings::ScalingFilter::NearestNeighbor: | ||
| 430 | case Settings::ScalingFilter::Bilinear: | ||
| 431 | return present_bilinear_fragment.handle; | ||
| 432 | case Settings::ScalingFilter::Bicubic: | ||
| 433 | return present_bicubic_fragment.handle; | ||
| 434 | case Settings::ScalingFilter::Gaussian: | ||
| 435 | return present_gaussian_fragment.handle; | ||
| 436 | case Settings::ScalingFilter::ScaleForce: | ||
| 437 | return present_scaleforce_fragment.handle; | ||
| 438 | case Settings::ScalingFilter::Fsr: | ||
| 439 | return fsr->GetPresentFragmentProgram().handle; | ||
| 440 | default: | ||
| 441 | return present_bilinear_fragment.handle; | ||
| 442 | } | ||
| 443 | }(); | ||
| 444 | program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); | ||
| 445 | glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, | ||
| 446 | ortho_matrix.data()); | ||
| 447 | |||
| 448 | f32 left, top, right, bottom; | ||
| 449 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 450 | // FSR has already applied the crop, so we just want to render the image | ||
| 451 | // it has produced. | ||
| 452 | left = 0; | ||
| 453 | top = 0; | ||
| 454 | right = 1; | ||
| 455 | bottom = 1; | ||
| 456 | } else { | ||
| 457 | // Apply the precomputed crop. | ||
| 458 | left = crop.left; | ||
| 459 | top = crop.top; | ||
| 460 | right = crop.right; | ||
| 461 | bottom = crop.bottom; | ||
| 462 | } | ||
| 463 | |||
| 464 | // Map the coordinates to the screen. | ||
| 465 | const auto& screen = layout.screen; | ||
| 466 | const auto x = screen.left; | ||
| 467 | const auto y = screen.top; | ||
| 468 | const auto w = screen.GetWidth(); | ||
| 469 | const auto h = screen.GetHeight(); | ||
| 470 | |||
| 471 | const std::array vertices = { | ||
| 472 | ScreenRectVertex(x, y, left, top), | ||
| 473 | ScreenRectVertex(x + w, y, right, top), | ||
| 474 | ScreenRectVertex(x, y + h, left, bottom), | ||
| 475 | ScreenRectVertex(x + w, y + h, right, bottom), | ||
| 476 | }; | ||
| 477 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); | ||
| 478 | |||
| 479 | glDisable(GL_FRAMEBUFFER_SRGB); | ||
| 480 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), | ||
| 481 | static_cast<GLfloat>(layout.height)); | ||
| 482 | |||
| 483 | glEnableVertexAttribArray(PositionLocation); | ||
| 484 | glEnableVertexAttribArray(TexCoordLocation); | ||
| 485 | glVertexAttribDivisor(PositionLocation, 0); | ||
| 486 | glVertexAttribDivisor(TexCoordLocation, 0); | ||
| 487 | glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 488 | offsetof(ScreenRectVertex, position)); | ||
| 489 | glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 490 | offsetof(ScreenRectVertex, tex_coord)); | ||
| 491 | glVertexAttribBinding(PositionLocation, 0); | ||
| 492 | glVertexAttribBinding(TexCoordLocation, 0); | ||
| 493 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 494 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 495 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 496 | sizeof(vertices)); | ||
| 497 | } else { | ||
| 498 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 499 | } | ||
| 500 | |||
| 501 | if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { | ||
| 502 | glBindSampler(0, present_sampler.handle); | ||
| 503 | } else { | ||
| 504 | glBindSampler(0, present_sampler_nn.handle); | ||
| 505 | } | ||
| 506 | |||
| 507 | // Update background color before drawing | ||
| 508 | glClearColor(Settings::values.bg_red.GetValue() / 255.0f, | ||
| 509 | Settings::values.bg_green.GetValue() / 255.0f, | ||
| 510 | Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); | ||
| 511 | |||
| 512 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 513 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 514 | |||
| 515 | // TODO | ||
| 516 | // program_manager.RestoreGuestPipeline(); | ||
| 517 | } | ||
| 518 | |||
| 519 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 10 | #include "video_core/host1x/gpu_device_memory_manager.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_fsr.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 13 | |||
| 14 | namespace Layout { | ||
| 15 | struct FramebufferLayout; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Tegra { | ||
| 19 | struct FramebufferConfig; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace OpenGL { | ||
| 23 | |||
| 24 | class Device; | ||
| 25 | class RasterizerOpenGL; | ||
| 26 | class StateTracker; | ||
| 27 | |||
| 28 | /// Structure used for storing information about the textures for the Switch screen | ||
| 29 | struct TextureInfo { | ||
| 30 | OGLTexture resource; | ||
| 31 | GLsizei width; | ||
| 32 | GLsizei height; | ||
| 33 | GLenum gl_format; | ||
| 34 | GLenum gl_type; | ||
| 35 | Service::android::PixelFormat pixel_format; | ||
| 36 | }; | ||
| 37 | |||
| 38 | /// Structure used for storing information about the display target for the Switch screen | ||
| 39 | struct FramebufferTextureInfo { | ||
| 40 | GLuint display_texture{}; | ||
| 41 | u32 width; | ||
| 42 | u32 height; | ||
| 43 | u32 scaled_width; | ||
| 44 | u32 scaled_height; | ||
| 45 | }; | ||
| 46 | |||
| 47 | class BlitScreen { | ||
| 48 | public: | ||
| 49 | explicit BlitScreen(RasterizerOpenGL& rasterizer, | ||
| 50 | Tegra::MaxwellDeviceMemoryManager& device_memory, | ||
| 51 | StateTracker& state_tracker, ProgramManager& program_manager, | ||
| 52 | Device& device); | ||
| 53 | |||
| 54 | void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); | ||
| 55 | |||
| 56 | /// Draws the emulated screens to the emulator window. | ||
| 57 | void DrawScreen(const Tegra::FramebufferConfig& framebuffer, | ||
| 58 | const Layout::FramebufferLayout& layout); | ||
| 59 | |||
| 60 | void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); | ||
| 61 | |||
| 62 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | ||
| 63 | FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); | ||
| 64 | |||
| 65 | FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); | ||
| 66 | |||
| 67 | private: | ||
| 68 | RasterizerOpenGL& rasterizer; | ||
| 69 | Tegra::MaxwellDeviceMemoryManager& device_memory; | ||
| 70 | StateTracker& state_tracker; | ||
| 71 | ProgramManager& program_manager; | ||
| 72 | Device& device; | ||
| 73 | |||
| 74 | OGLSampler present_sampler; | ||
| 75 | OGLSampler present_sampler_nn; | ||
| 76 | OGLBuffer vertex_buffer; | ||
| 77 | OGLProgram fxaa_vertex; | ||
| 78 | OGLProgram fxaa_fragment; | ||
| 79 | OGLProgram present_vertex; | ||
| 80 | OGLProgram present_bilinear_fragment; | ||
| 81 | OGLProgram present_bicubic_fragment; | ||
| 82 | OGLProgram present_gaussian_fragment; | ||
| 83 | OGLProgram present_scaleforce_fragment; | ||
| 84 | |||
| 85 | /// Display information for Switch screen | ||
| 86 | TextureInfo framebuffer_texture; | ||
| 87 | OGLTexture aa_texture; | ||
| 88 | OGLFramebuffer aa_framebuffer; | ||
| 89 | |||
| 90 | OGLProgram smaa_edge_detection_vert; | ||
| 91 | OGLProgram smaa_blending_weight_calculation_vert; | ||
| 92 | OGLProgram smaa_neighborhood_blending_vert; | ||
| 93 | OGLProgram smaa_edge_detection_frag; | ||
| 94 | OGLProgram smaa_blending_weight_calculation_frag; | ||
| 95 | OGLProgram smaa_neighborhood_blending_frag; | ||
| 96 | OGLTexture smaa_area_tex; | ||
| 97 | OGLTexture smaa_search_tex; | ||
| 98 | OGLTexture smaa_edges_tex; | ||
| 99 | OGLTexture smaa_blend_tex; | ||
| 100 | |||
| 101 | std::unique_ptr<FSR> fsr; | ||
| 102 | |||
| 103 | /// OpenGL framebuffer data | ||
| 104 | std::vector<u8> gl_framebuffer_data; | ||
| 105 | |||
| 106 | // GPU address of the vertex buffer | ||
| 107 | GLuint64EXT vertex_buffer_address = 0; | ||
| 108 | }; | ||
| 109 | |||
| 110 | } // 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 @@ | |||
| 16 | #include "video_core/engines/maxwell_dma.h" | 16 | #include "video_core/engines/maxwell_dma.h" |
| 17 | #include "video_core/rasterizer_interface.h" | 17 | #include "video_core/rasterizer_interface.h" |
| 18 | #include "video_core/renderer_opengl/blit_image.h" | 18 | #include "video_core/renderer_opengl/blit_image.h" |
| 19 | #include "video_core/renderer_opengl/gl_blit_screen.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 20 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 20 | #include "video_core/renderer_opengl/gl_device.h" | 21 | #include "video_core/renderer_opengl/gl_device.h" |
| 21 | #include "video_core/renderer_opengl/gl_fence_manager.h" | 22 | #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 @@ | |||
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 18 | #include "core/telemetry_session.h" | 18 | #include "core/telemetry_session.h" |
| 19 | #include "video_core/host_shaders/ffx_a_h.h" | 19 | #include "video_core/renderer_opengl/gl_blit_screen.h" |
| 20 | #include "video_core/host_shaders/ffx_fsr1_h.h" | ||
| 21 | #include "video_core/host_shaders/full_screen_triangle_vert.h" | ||
| 22 | #include "video_core/host_shaders/fxaa_frag.h" | ||
| 23 | #include "video_core/host_shaders/fxaa_vert.h" | ||
| 24 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" | ||
| 25 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" | ||
| 26 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" | ||
| 27 | #include "video_core/host_shaders/opengl_present_frag.h" | ||
| 28 | #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" | ||
| 29 | #include "video_core/host_shaders/opengl_present_vert.h" | ||
| 30 | #include "video_core/host_shaders/opengl_smaa_glsl.h" | ||
| 31 | #include "video_core/host_shaders/present_bicubic_frag.h" | ||
| 32 | #include "video_core/host_shaders/present_gaussian_frag.h" | ||
| 33 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||
| 34 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||
| 35 | #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||
| 36 | #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||
| 37 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||
| 38 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" | ||
| 39 | #include "video_core/renderer_opengl/gl_fsr.h" | 20 | #include "video_core/renderer_opengl/gl_fsr.h" |
| 40 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 21 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 41 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 22 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 42 | #include "video_core/renderer_opengl/gl_shader_util.h" | 23 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 43 | #include "video_core/renderer_opengl/renderer_opengl.h" | 24 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 44 | #include "video_core/smaa_area_tex.h" | ||
| 45 | #include "video_core/smaa_search_tex.h" | ||
| 46 | #include "video_core/textures/decoders.h" | 25 | #include "video_core/textures/decoders.h" |
| 47 | 26 | ||
| 48 | namespace OpenGL { | 27 | namespace OpenGL { |
| 49 | namespace { | 28 | namespace { |
| 50 | constexpr GLint PositionLocation = 0; | ||
| 51 | constexpr GLint TexCoordLocation = 1; | ||
| 52 | constexpr GLint ModelViewMatrixLocation = 0; | ||
| 53 | |||
| 54 | struct ScreenRectVertex { | ||
| 55 | constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) | ||
| 56 | : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||
| 57 | |||
| 58 | std::array<GLfloat, 2> position; | ||
| 59 | std::array<GLfloat, 2> tex_coord; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left | ||
| 64 | * corner and (width, height) on the lower-bottom. | ||
| 65 | * | ||
| 66 | * The projection part of the matrix is trivial, hence these operations are represented | ||
| 67 | * by a 3x2 matrix. | ||
| 68 | */ | ||
| 69 | std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||
| 70 | std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order | ||
| 71 | |||
| 72 | // clang-format off | ||
| 73 | matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; | ||
| 74 | matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; | ||
| 75 | // Last matrix row is implicitly assumed to be [0, 0, 1]. | ||
| 76 | // clang-format on | ||
| 77 | |||
| 78 | return matrix; | ||
| 79 | } | ||
| 80 | |||
| 81 | const char* GetSource(GLenum source) { | 29 | const char* GetSource(GLenum source) { |
| 82 | switch (source) { | 30 | switch (source) { |
| 83 | case GL_DEBUG_SOURCE_API: | 31 | case GL_DEBUG_SOURCE_API: |
| @@ -155,7 +103,6 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 155 | glDebugMessageCallback(DebugHandler, nullptr); | 103 | glDebugMessageCallback(DebugHandler, nullptr); |
| 156 | } | 104 | } |
| 157 | AddTelemetryFields(); | 105 | AddTelemetryFields(); |
| 158 | InitOpenGLObjects(); | ||
| 159 | 106 | ||
| 160 | // Initialize default attributes to match hardware's disabled attributes | 107 | // Initialize default attributes to match hardware's disabled attributes |
| 161 | GLint max_attribs{}; | 108 | GLint max_attribs{}; |
| @@ -167,14 +114,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 167 | if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { | 114 | if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { |
| 168 | glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); | 115 | glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); |
| 169 | } | 116 | } |
| 170 | // Enable unified vertex attributes and query vertex buffer address when the driver supports it | 117 | blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, |
| 171 | if (device.HasVertexBufferUnifiedMemory()) { | 118 | program_manager, device); |
| 172 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); | ||
| 173 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); | ||
| 174 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 175 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 176 | &vertex_buffer_address); | ||
| 177 | } | ||
| 178 | } | 119 | } |
| 179 | 120 | ||
| 180 | RendererOpenGL::~RendererOpenGL() = default; | 121 | RendererOpenGL::~RendererOpenGL() = default; |
| @@ -187,7 +128,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 187 | RenderScreenshot(*framebuffer); | 128 | RenderScreenshot(*framebuffer); |
| 188 | 129 | ||
| 189 | state_tracker.BindFramebuffer(0); | 130 | state_tracker.BindFramebuffer(0); |
| 190 | DrawScreen(*framebuffer, emu_window.GetFramebufferLayout()); | 131 | blit_screen->DrawScreen(*framebuffer, emu_window.GetFramebufferLayout()); |
| 191 | 132 | ||
| 192 | ++m_current_frame; | 133 | ++m_current_frame; |
| 193 | 134 | ||
| @@ -198,166 +139,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 198 | render_window.OnFrameDisplayed(); | 139 | render_window.OnFrameDisplayed(); |
| 199 | } | 140 | } |
| 200 | 141 | ||
| 201 | FramebufferTextureInfo RendererOpenGL::PrepareRenderTarget( | ||
| 202 | const Tegra::FramebufferConfig& framebuffer) { | ||
| 203 | // If framebuffer is provided, reload it from memory to a texture | ||
| 204 | if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || | ||
| 205 | framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || | ||
| 206 | framebuffer_texture.pixel_format != framebuffer.pixel_format || | ||
| 207 | gl_framebuffer_data.empty()) { | ||
| 208 | // Reallocate texture if the framebuffer size has changed. | ||
| 209 | // This is expected to not happen very often and hence should not be a | ||
| 210 | // performance problem. | ||
| 211 | ConfigureFramebufferTexture(framebuffer); | ||
| 212 | } | ||
| 213 | |||
| 214 | // Load the framebuffer from memory if needed | ||
| 215 | return LoadFBToScreenInfo(framebuffer); | ||
| 216 | } | ||
| 217 | |||
| 218 | FramebufferTextureInfo RendererOpenGL::LoadFBToScreenInfo( | ||
| 219 | const Tegra::FramebufferConfig& framebuffer) { | ||
| 220 | const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||
| 221 | const auto accelerated_info = | ||
| 222 | rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||
| 223 | if (accelerated_info) { | ||
| 224 | return *accelerated_info; | ||
| 225 | } | ||
| 226 | |||
| 227 | // Reset the screen info's display texture to its own permanent texture | ||
| 228 | FramebufferTextureInfo info{}; | ||
| 229 | info.display_texture = framebuffer_texture.resource.handle; | ||
| 230 | info.width = framebuffer.width; | ||
| 231 | info.height = framebuffer.height; | ||
| 232 | info.scaled_width = framebuffer.width; | ||
| 233 | info.scaled_height = framebuffer.height; | ||
| 234 | |||
| 235 | // TODO(Rodrigo): Read this from HLE | ||
| 236 | constexpr u32 block_height_log2 = 4; | ||
| 237 | const auto pixel_format{ | ||
| 238 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 239 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 240 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||
| 241 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 242 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||
| 243 | const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||
| 244 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||
| 245 | framebuffer.width, framebuffer.height, 1, block_height_log2, | ||
| 246 | 0); | ||
| 247 | |||
| 248 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||
| 249 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||
| 250 | |||
| 251 | // Update existing texture | ||
| 252 | // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||
| 253 | // they differ from the LCD resolution. | ||
| 254 | // TODO: Applications could theoretically crash yuzu here by specifying too large | ||
| 255 | // framebuffer sizes. We should make sure that this cannot happen. | ||
| 256 | glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, | ||
| 257 | framebuffer.height, framebuffer_texture.gl_format, | ||
| 258 | framebuffer_texture.gl_type, gl_framebuffer_data.data()); | ||
| 259 | |||
| 260 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 261 | |||
| 262 | return info; | ||
| 263 | } | ||
| 264 | |||
| 265 | void RendererOpenGL::InitOpenGLObjects() { | ||
| 266 | // Create shader programs | ||
| 267 | fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||
| 268 | fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||
| 269 | |||
| 270 | const auto replace_include = [](std::string& shader_source, std::string_view include_name, | ||
| 271 | std::string_view include_content) { | ||
| 272 | const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||
| 273 | const std::size_t pos = shader_source.find(include_string); | ||
| 274 | ASSERT(pos != std::string::npos); | ||
| 275 | shader_source.replace(pos, include_string.size(), include_content); | ||
| 276 | }; | ||
| 277 | |||
| 278 | const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||
| 279 | std::string shader_source{specialized_source}; | ||
| 280 | replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||
| 281 | return CreateProgram(shader_source, stage); | ||
| 282 | }; | ||
| 283 | |||
| 284 | smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||
| 285 | smaa_edge_detection_frag = | ||
| 286 | SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||
| 287 | smaa_blending_weight_calculation_vert = | ||
| 288 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||
| 289 | smaa_blending_weight_calculation_frag = | ||
| 290 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||
| 291 | smaa_neighborhood_blending_vert = | ||
| 292 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||
| 293 | smaa_neighborhood_blending_frag = | ||
| 294 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||
| 295 | |||
| 296 | present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); | ||
| 297 | present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); | ||
| 298 | present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); | ||
| 299 | present_gaussian_fragment = | ||
| 300 | CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); | ||
| 301 | present_scaleforce_fragment = | ||
| 302 | CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), | ||
| 303 | GL_FRAGMENT_SHADER); | ||
| 304 | |||
| 305 | std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||
| 306 | replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||
| 307 | replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||
| 308 | |||
| 309 | std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||
| 310 | std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||
| 311 | replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 312 | replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 313 | |||
| 314 | fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, | ||
| 315 | fsr_rcas_frag_source); | ||
| 316 | |||
| 317 | // Generate presentation sampler | ||
| 318 | present_sampler.Create(); | ||
| 319 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 320 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 321 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 322 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 323 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 324 | |||
| 325 | present_sampler_nn.Create(); | ||
| 326 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
| 327 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
| 328 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 329 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 330 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 331 | |||
| 332 | // Generate VBO handle for drawing | ||
| 333 | vertex_buffer.Create(); | ||
| 334 | |||
| 335 | // Attach vertex data to VAO | ||
| 336 | glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); | ||
| 337 | |||
| 338 | // Allocate textures for the screen | ||
| 339 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 340 | |||
| 341 | const GLuint texture = framebuffer_texture.resource.handle; | ||
| 342 | glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||
| 343 | |||
| 344 | // Clear screen to black | ||
| 345 | const u8 framebuffer_data[4] = {0, 0, 0, 0}; | ||
| 346 | glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||
| 347 | framebuffer_data); | ||
| 348 | |||
| 349 | aa_framebuffer.Create(); | ||
| 350 | |||
| 351 | smaa_area_tex.Create(GL_TEXTURE_2D); | ||
| 352 | glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||
| 353 | glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||
| 354 | GL_UNSIGNED_BYTE, areaTexBytes); | ||
| 355 | smaa_search_tex.Create(GL_TEXTURE_2D); | ||
| 356 | glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||
| 357 | glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||
| 358 | GL_UNSIGNED_BYTE, searchTexBytes); | ||
| 359 | } | ||
| 360 | |||
| 361 | void RendererOpenGL::AddTelemetryFields() { | 142 | void RendererOpenGL::AddTelemetryFields() { |
| 362 | const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; | 143 | const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; |
| 363 | const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; | 144 | const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; |
| @@ -373,283 +154,6 @@ void RendererOpenGL::AddTelemetryFields() { | |||
| 373 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | 154 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |
| 374 | } | 155 | } |
| 375 | 156 | ||
| 376 | void RendererOpenGL::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { | ||
| 377 | framebuffer_texture.width = framebuffer.width; | ||
| 378 | framebuffer_texture.height = framebuffer.height; | ||
| 379 | framebuffer_texture.pixel_format = framebuffer.pixel_format; | ||
| 380 | |||
| 381 | const auto pixel_format{ | ||
| 382 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 383 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 384 | gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * | ||
| 385 | bytes_per_pixel); | ||
| 386 | |||
| 387 | GLint internal_format; | ||
| 388 | switch (framebuffer.pixel_format) { | ||
| 389 | case Service::android::PixelFormat::Rgba8888: | ||
| 390 | internal_format = GL_RGBA8; | ||
| 391 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 392 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 393 | break; | ||
| 394 | case Service::android::PixelFormat::Rgb565: | ||
| 395 | internal_format = GL_RGB565; | ||
| 396 | framebuffer_texture.gl_format = GL_RGB; | ||
| 397 | framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||
| 398 | break; | ||
| 399 | default: | ||
| 400 | internal_format = GL_RGBA8; | ||
| 401 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 402 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 403 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||
| 404 | // static_cast<u32>(framebuffer.pixel_format)); | ||
| 405 | break; | ||
| 406 | } | ||
| 407 | |||
| 408 | framebuffer_texture.resource.Release(); | ||
| 409 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 410 | glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, | ||
| 411 | framebuffer_texture.width, framebuffer_texture.height); | ||
| 412 | aa_texture.Release(); | ||
| 413 | aa_texture.Create(GL_TEXTURE_2D); | ||
| 414 | glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, | ||
| 415 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 416 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 417 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); | ||
| 418 | smaa_edges_tex.Release(); | ||
| 419 | smaa_edges_tex.Create(GL_TEXTURE_2D); | ||
| 420 | glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, | ||
| 421 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 422 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 423 | smaa_blend_tex.Release(); | ||
| 424 | smaa_blend_tex.Create(GL_TEXTURE_2D); | ||
| 425 | glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, | ||
| 426 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 427 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 428 | } | ||
| 429 | |||
| 430 | void RendererOpenGL::DrawScreen(const Tegra::FramebufferConfig& framebuffer, | ||
| 431 | const Layout::FramebufferLayout& layout) { | ||
| 432 | FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); | ||
| 433 | const auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); | ||
| 434 | |||
| 435 | // TODO: Signal state tracker about these changes | ||
| 436 | state_tracker.NotifyScreenDrawVertexArray(); | ||
| 437 | state_tracker.NotifyPolygonModes(); | ||
| 438 | state_tracker.NotifyViewport0(); | ||
| 439 | state_tracker.NotifyScissor0(); | ||
| 440 | state_tracker.NotifyColorMask(0); | ||
| 441 | state_tracker.NotifyBlend0(); | ||
| 442 | state_tracker.NotifyFramebuffer(); | ||
| 443 | state_tracker.NotifyFrontFace(); | ||
| 444 | state_tracker.NotifyCullTest(); | ||
| 445 | state_tracker.NotifyDepthTest(); | ||
| 446 | state_tracker.NotifyStencilTest(); | ||
| 447 | state_tracker.NotifyPolygonOffset(); | ||
| 448 | state_tracker.NotifyRasterizeEnable(); | ||
| 449 | state_tracker.NotifyFramebufferSRGB(); | ||
| 450 | state_tracker.NotifyLogicOp(); | ||
| 451 | state_tracker.NotifyClipControl(); | ||
| 452 | state_tracker.NotifyAlphaTest(); | ||
| 453 | |||
| 454 | state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||
| 455 | |||
| 456 | glEnable(GL_CULL_FACE); | ||
| 457 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 458 | glDisable(GL_DEPTH_TEST); | ||
| 459 | glDisable(GL_STENCIL_TEST); | ||
| 460 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 461 | glDisable(GL_RASTERIZER_DISCARD); | ||
| 462 | glDisable(GL_ALPHA_TEST); | ||
| 463 | glDisablei(GL_BLEND, 0); | ||
| 464 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
| 465 | glCullFace(GL_BACK); | ||
| 466 | glFrontFace(GL_CW); | ||
| 467 | glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||
| 468 | glDepthRangeIndexed(0, 0.0, 0.0); | ||
| 469 | |||
| 470 | glBindTextureUnit(0, info.display_texture); | ||
| 471 | |||
| 472 | auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); | ||
| 473 | if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) { | ||
| 474 | LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); | ||
| 475 | anti_aliasing = Settings::AntiAliasing::None; | ||
| 476 | Settings::values.anti_aliasing.SetValue(anti_aliasing); | ||
| 477 | } | ||
| 478 | |||
| 479 | if (anti_aliasing != Settings::AntiAliasing::None) { | ||
| 480 | glEnablei(GL_SCISSOR_TEST, 0); | ||
| 481 | auto scissor_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); | ||
| 482 | auto viewport_width = static_cast<GLfloat>(scissor_width); | ||
| 483 | auto scissor_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); | ||
| 484 | auto viewport_height = static_cast<GLfloat>(scissor_height); | ||
| 485 | |||
| 486 | glScissorIndexed(0, 0, 0, scissor_width, scissor_height); | ||
| 487 | glViewportIndexedf(0, 0.0f, 0.0f, viewport_width, viewport_height); | ||
| 488 | |||
| 489 | glBindSampler(0, present_sampler.handle); | ||
| 490 | GLint old_read_fb; | ||
| 491 | GLint old_draw_fb; | ||
| 492 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 493 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 494 | |||
| 495 | switch (anti_aliasing) { | ||
| 496 | case Settings::AntiAliasing::Fxaa: { | ||
| 497 | program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); | ||
| 498 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 499 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 500 | } break; | ||
| 501 | case Settings::AntiAliasing::Smaa: { | ||
| 502 | glClearColor(0, 0, 0, 0); | ||
| 503 | glFrontFace(GL_CCW); | ||
| 504 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 505 | glBindSampler(1, present_sampler.handle); | ||
| 506 | glBindSampler(2, present_sampler.handle); | ||
| 507 | |||
| 508 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 509 | smaa_edges_tex.handle, 0); | ||
| 510 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 511 | program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, | ||
| 512 | smaa_edge_detection_frag.handle); | ||
| 513 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 514 | |||
| 515 | glBindTextureUnit(0, smaa_edges_tex.handle); | ||
| 516 | glBindTextureUnit(1, smaa_area_tex.handle); | ||
| 517 | glBindTextureUnit(2, smaa_search_tex.handle); | ||
| 518 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 519 | smaa_blend_tex.handle, 0); | ||
| 520 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 521 | program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, | ||
| 522 | smaa_blending_weight_calculation_frag.handle); | ||
| 523 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 524 | |||
| 525 | glBindTextureUnit(0, info.display_texture); | ||
| 526 | glBindTextureUnit(1, smaa_blend_tex.handle); | ||
| 527 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 528 | aa_texture.handle, 0); | ||
| 529 | program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, | ||
| 530 | smaa_neighborhood_blending_frag.handle); | ||
| 531 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 532 | glFrontFace(GL_CW); | ||
| 533 | } break; | ||
| 534 | default: | ||
| 535 | UNREACHABLE(); | ||
| 536 | } | ||
| 537 | |||
| 538 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 539 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 540 | |||
| 541 | glBindTextureUnit(0, aa_texture.handle); | ||
| 542 | } | ||
| 543 | glDisablei(GL_SCISSOR_TEST, 0); | ||
| 544 | |||
| 545 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 546 | if (!fsr->AreBuffersInitialized()) { | ||
| 547 | fsr->InitBuffers(); | ||
| 548 | } | ||
| 549 | |||
| 550 | glBindSampler(0, present_sampler.handle); | ||
| 551 | fsr->Draw(program_manager, layout.screen, info.scaled_width, info.scaled_height, crop); | ||
| 552 | } else { | ||
| 553 | if (fsr->AreBuffersInitialized()) { | ||
| 554 | fsr->ReleaseBuffers(); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | const std::array ortho_matrix = | ||
| 559 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); | ||
| 560 | |||
| 561 | const auto fragment_handle = [this]() { | ||
| 562 | switch (Settings::values.scaling_filter.GetValue()) { | ||
| 563 | case Settings::ScalingFilter::NearestNeighbor: | ||
| 564 | case Settings::ScalingFilter::Bilinear: | ||
| 565 | return present_bilinear_fragment.handle; | ||
| 566 | case Settings::ScalingFilter::Bicubic: | ||
| 567 | return present_bicubic_fragment.handle; | ||
| 568 | case Settings::ScalingFilter::Gaussian: | ||
| 569 | return present_gaussian_fragment.handle; | ||
| 570 | case Settings::ScalingFilter::ScaleForce: | ||
| 571 | return present_scaleforce_fragment.handle; | ||
| 572 | case Settings::ScalingFilter::Fsr: | ||
| 573 | return fsr->GetPresentFragmentProgram().handle; | ||
| 574 | default: | ||
| 575 | return present_bilinear_fragment.handle; | ||
| 576 | } | ||
| 577 | }(); | ||
| 578 | program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); | ||
| 579 | glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, | ||
| 580 | ortho_matrix.data()); | ||
| 581 | |||
| 582 | f32 left, top, right, bottom; | ||
| 583 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 584 | // FSR has already applied the crop, so we just want to render the image | ||
| 585 | // it has produced. | ||
| 586 | left = 0; | ||
| 587 | top = 0; | ||
| 588 | right = 1; | ||
| 589 | bottom = 1; | ||
| 590 | } else { | ||
| 591 | // Apply the precomputed crop. | ||
| 592 | left = crop.left; | ||
| 593 | top = crop.top; | ||
| 594 | right = crop.right; | ||
| 595 | bottom = crop.bottom; | ||
| 596 | } | ||
| 597 | |||
| 598 | // Map the coordinates to the screen. | ||
| 599 | const auto& screen = layout.screen; | ||
| 600 | const auto x = screen.left; | ||
| 601 | const auto y = screen.top; | ||
| 602 | const auto w = screen.GetWidth(); | ||
| 603 | const auto h = screen.GetHeight(); | ||
| 604 | |||
| 605 | const std::array vertices = { | ||
| 606 | ScreenRectVertex(x, y, left, top), | ||
| 607 | ScreenRectVertex(x + w, y, right, top), | ||
| 608 | ScreenRectVertex(x, y + h, left, bottom), | ||
| 609 | ScreenRectVertex(x + w, y + h, right, bottom), | ||
| 610 | }; | ||
| 611 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); | ||
| 612 | |||
| 613 | glDisable(GL_FRAMEBUFFER_SRGB); | ||
| 614 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), | ||
| 615 | static_cast<GLfloat>(layout.height)); | ||
| 616 | |||
| 617 | glEnableVertexAttribArray(PositionLocation); | ||
| 618 | glEnableVertexAttribArray(TexCoordLocation); | ||
| 619 | glVertexAttribDivisor(PositionLocation, 0); | ||
| 620 | glVertexAttribDivisor(TexCoordLocation, 0); | ||
| 621 | glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 622 | offsetof(ScreenRectVertex, position)); | ||
| 623 | glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 624 | offsetof(ScreenRectVertex, tex_coord)); | ||
| 625 | glVertexAttribBinding(PositionLocation, 0); | ||
| 626 | glVertexAttribBinding(TexCoordLocation, 0); | ||
| 627 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 628 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 629 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 630 | sizeof(vertices)); | ||
| 631 | } else { | ||
| 632 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 633 | } | ||
| 634 | |||
| 635 | if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { | ||
| 636 | glBindSampler(0, present_sampler.handle); | ||
| 637 | } else { | ||
| 638 | glBindSampler(0, present_sampler_nn.handle); | ||
| 639 | } | ||
| 640 | |||
| 641 | // Update background color before drawing | ||
| 642 | glClearColor(Settings::values.bg_red.GetValue() / 255.0f, | ||
| 643 | Settings::values.bg_green.GetValue() / 255.0f, | ||
| 644 | Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); | ||
| 645 | |||
| 646 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 647 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 648 | |||
| 649 | // TODO | ||
| 650 | // program_manager.RestoreGuestPipeline(); | ||
| 651 | } | ||
| 652 | |||
| 653 | void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer) { | 157 | void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer) { |
| 654 | if (!renderer_settings.screenshot_requested) { | 158 | if (!renderer_settings.screenshot_requested) { |
| 655 | return; | 159 | return; |
| @@ -672,7 +176,7 @@ void RendererOpenGL::RenderScreenshot(const Tegra::FramebufferConfig& framebuffe | |||
| 672 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | 176 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); |
| 673 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | 177 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); |
| 674 | 178 | ||
| 675 | DrawScreen(framebuffer, layout); | 179 | blit_screen->DrawScreen(framebuffer, layout); |
| 676 | 180 | ||
| 677 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | 181 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| 678 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | 182 | 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 { | |||
| 25 | class EmuWindow; | 25 | class EmuWindow; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | namespace Core::Memory { | ||
| 29 | class Memory; | ||
| 30 | } | ||
| 31 | |||
| 32 | namespace Layout { | ||
| 33 | struct FramebufferLayout; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace Tegra { | 28 | namespace Tegra { |
| 37 | class GPU; | 29 | class GPU; |
| 38 | } | 30 | } |
| 39 | 31 | ||
| 40 | namespace OpenGL { | 32 | namespace OpenGL { |
| 41 | 33 | ||
| 42 | /// Structure used for storing information about the textures for the Switch screen | 34 | class BlitScreen; |
| 43 | struct TextureInfo { | ||
| 44 | OGLTexture resource; | ||
| 45 | GLsizei width; | ||
| 46 | GLsizei height; | ||
| 47 | GLenum gl_format; | ||
| 48 | GLenum gl_type; | ||
| 49 | Service::android::PixelFormat pixel_format; | ||
| 50 | }; | ||
| 51 | |||
| 52 | /// Structure used for storing information about the display target for the Switch screen | ||
| 53 | struct FramebufferTextureInfo { | ||
| 54 | GLuint display_texture{}; | ||
| 55 | u32 width; | ||
| 56 | u32 height; | ||
| 57 | u32 scaled_width; | ||
| 58 | u32 scaled_height; | ||
| 59 | }; | ||
| 60 | 35 | ||
| 61 | class RendererOpenGL final : public VideoCore::RendererBase { | 36 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 62 | public: | 37 | public: |
| @@ -77,24 +52,9 @@ public: | |||
| 77 | } | 52 | } |
| 78 | 53 | ||
| 79 | private: | 54 | private: |
| 80 | /// Initializes the OpenGL state and creates persistent objects. | ||
| 81 | void InitOpenGLObjects(); | ||
| 82 | |||
| 83 | void AddTelemetryFields(); | 55 | void AddTelemetryFields(); |
| 84 | |||
| 85 | void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); | ||
| 86 | |||
| 87 | /// Draws the emulated screens to the emulator window. | ||
| 88 | void DrawScreen(const Tegra::FramebufferConfig& framebuffer, | ||
| 89 | const Layout::FramebufferLayout& layout); | ||
| 90 | |||
| 91 | void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); | 56 | void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); |
| 92 | 57 | ||
| 93 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | ||
| 94 | FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); | ||
| 95 | |||
| 96 | FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); | ||
| 97 | |||
| 98 | Core::TelemetrySession& telemetry_session; | 58 | Core::TelemetrySession& telemetry_session; |
| 99 | Core::Frontend::EmuWindow& emu_window; | 59 | Core::Frontend::EmuWindow& emu_window; |
| 100 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 60 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| @@ -104,43 +64,9 @@ private: | |||
| 104 | StateTracker state_tracker; | 64 | StateTracker state_tracker; |
| 105 | ProgramManager program_manager; | 65 | ProgramManager program_manager; |
| 106 | RasterizerOpenGL rasterizer; | 66 | RasterizerOpenGL rasterizer; |
| 107 | |||
| 108 | // OpenGL object IDs | ||
| 109 | OGLSampler present_sampler; | ||
| 110 | OGLSampler present_sampler_nn; | ||
| 111 | OGLBuffer vertex_buffer; | ||
| 112 | OGLProgram fxaa_vertex; | ||
| 113 | OGLProgram fxaa_fragment; | ||
| 114 | OGLProgram present_vertex; | ||
| 115 | OGLProgram present_bilinear_fragment; | ||
| 116 | OGLProgram present_bicubic_fragment; | ||
| 117 | OGLProgram present_gaussian_fragment; | ||
| 118 | OGLProgram present_scaleforce_fragment; | ||
| 119 | OGLFramebuffer screenshot_framebuffer; | 67 | OGLFramebuffer screenshot_framebuffer; |
| 120 | 68 | ||
| 121 | // GPU address of the vertex buffer | 69 | std::unique_ptr<BlitScreen> blit_screen; |
| 122 | GLuint64EXT vertex_buffer_address = 0; | ||
| 123 | |||
| 124 | /// Display information for Switch screen | ||
| 125 | TextureInfo framebuffer_texture; | ||
| 126 | OGLTexture aa_texture; | ||
| 127 | OGLFramebuffer aa_framebuffer; | ||
| 128 | |||
| 129 | OGLProgram smaa_edge_detection_vert; | ||
| 130 | OGLProgram smaa_blending_weight_calculation_vert; | ||
| 131 | OGLProgram smaa_neighborhood_blending_vert; | ||
| 132 | OGLProgram smaa_edge_detection_frag; | ||
| 133 | OGLProgram smaa_blending_weight_calculation_frag; | ||
| 134 | OGLProgram smaa_neighborhood_blending_frag; | ||
| 135 | OGLTexture smaa_area_tex; | ||
| 136 | OGLTexture smaa_search_tex; | ||
| 137 | OGLTexture smaa_edges_tex; | ||
| 138 | OGLTexture smaa_blend_tex; | ||
| 139 | |||
| 140 | std::unique_ptr<FSR> fsr; | ||
| 141 | |||
| 142 | /// OpenGL framebuffer data | ||
| 143 | std::vector<u8> gl_framebuffer_data; | ||
| 144 | }; | 70 | }; |
| 145 | 71 | ||
| 146 | } // namespace OpenGL | 72 | } // namespace OpenGL |