video_core/renderer_opengl/gl_rasterizer_cache: Create Format Reinterpretation Framework (#5170)
* video_core/renderer_opengl/gl_rasterizer_cache: Create Format Reinterpretation Framework Adds RGBA4 -> RGB5A1 reinterpretation commonly used by virtual console If no matching surface can be found, ValidateSurface checks for a surface in the cache which is reinterpretable to the requested format. If that fails, the cache is checked for any surface with a matching bit-width. If one is found, the region is flushed. If not, the region is checked against dirty_regions to see if it was created entirely on the GPU. If not, then the surface is flushed. Co-Authored-By: James Rowe <jroweboy@users.noreply.github.com> Co-Authored-By: Ben <b3n30@users.noreply.github.com> temporary change to avoid merge conflicts with video dumping * re-add D24S8->RGBA8 res_scale hack * adress review comments * fix dirty region check * check for surfaces with invalid pixel format, and break logic into separate functions
This commit is contained in:
		| @@ -63,6 +63,9 @@ add_library(video_core STATIC | ||||
|     renderer_opengl/texture_filters/texture_filterer.h | ||||
|     renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp | ||||
|     renderer_opengl/texture_filters/xbrz/xbrz_freescale.h | ||||
|     #temporary, move these back in alphabetical order before merging | ||||
|     renderer_opengl/gl_format_reinterpreter.cpp | ||||
|     renderer_opengl/gl_format_reinterpreter.h | ||||
|     shader/debug_data.h | ||||
|     shader/shader.cpp | ||||
|     shader/shader.h | ||||
|   | ||||
							
								
								
									
										238
									
								
								src/video_core/renderer_opengl/gl_format_reinterpreter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								src/video_core/renderer_opengl/gl_format_reinterpreter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | ||||
| // Copyright 2020 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "video_core/renderer_opengl/gl_format_reinterpreter.h" | ||||
| #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||
| #include "video_core/renderer_opengl/gl_state.h" | ||||
| #include "video_core/renderer_opengl/gl_vars.h" | ||||
| #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
| using PixelFormat = SurfaceParams::PixelFormat; | ||||
|  | ||||
| class RGBA4toRGB5A1 final : public FormatReinterpreterBase { | ||||
| public: | ||||
|     RGBA4toRGB5A1() { | ||||
|         constexpr std::string_view vs_source = R"( | ||||
| out vec2 dst_coord; | ||||
|  | ||||
| uniform mediump ivec2 dst_size; | ||||
|  | ||||
| const vec2 vertices[4] = | ||||
|     vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); | ||||
|  | ||||
| void main() { | ||||
|     gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); | ||||
|     dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size); | ||||
| } | ||||
| )"; | ||||
|  | ||||
|         constexpr std::string_view fs_source = R"( | ||||
| in mediump vec2 dst_coord; | ||||
|  | ||||
| out lowp vec4 frag_color; | ||||
|  | ||||
| uniform lowp sampler2D source; | ||||
| uniform mediump ivec2 dst_size; | ||||
| uniform mediump ivec2 src_size; | ||||
| uniform mediump ivec2 src_offset; | ||||
|  | ||||
| void main() { | ||||
|     mediump ivec2 tex_coord; | ||||
|     if (src_size == dst_size) { | ||||
|         tex_coord = ivec2(dst_coord); | ||||
|     } else { | ||||
|         highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x); | ||||
|         mediump int y = tex_index / src_size.x; | ||||
|         tex_coord = ivec2(tex_index - y * src_size.x, y); | ||||
|     } | ||||
|     tex_coord -= src_offset; | ||||
|  | ||||
|     lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_coord, 0) * (exp2(4.0) - 1.0)); | ||||
|     lowp ivec3 rgb5 = | ||||
|         ((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F; | ||||
|     frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01); | ||||
| } | ||||
| )"; | ||||
|  | ||||
|         program.Create(vs_source.data(), fs_source.data()); | ||||
|         dst_size_loc = glGetUniformLocation(program.handle, "dst_size"); | ||||
|         src_size_loc = glGetUniformLocation(program.handle, "src_size"); | ||||
|         src_offset_loc = glGetUniformLocation(program.handle, "src_offset"); | ||||
|         vao.Create(); | ||||
|     } | ||||
|  | ||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, | ||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, | ||||
|                      GLuint draw_fb_handle) override { | ||||
|         OpenGLState prev_state = OpenGLState::GetCurState(); | ||||
|         SCOPE_EXIT({ prev_state.Apply(); }); | ||||
|  | ||||
|         OpenGLState state; | ||||
|         state.texture_units[0].texture_2d = src_tex; | ||||
|         state.draw.draw_framebuffer = draw_fb_handle; | ||||
|         state.draw.shader_program = program.handle; | ||||
|         state.draw.vertex_array = vao.handle; | ||||
|         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||
|                           static_cast<GLsizei>(dst_rect.GetWidth()), | ||||
|                           static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||
|         state.Apply(); | ||||
|  | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, | ||||
|                                0); | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                0); | ||||
|  | ||||
|         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); | ||||
|         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); | ||||
|         glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom); | ||||
|         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     OGLProgram program; | ||||
|     GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1}; | ||||
|     OGLVertexArray vao; | ||||
| }; | ||||
|  | ||||
| class PixelBufferD24S8toABGR final : public FormatReinterpreterBase { | ||||
| public: | ||||
|     PixelBufferD24S8toABGR() { | ||||
|         attributeless_vao.Create(); | ||||
|         d24s8_abgr_buffer.Create(); | ||||
|         d24s8_abgr_buffer_size = 0; | ||||
|  | ||||
|         constexpr std::string_view vs_source = R"( | ||||
| const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), | ||||
|                                  vec2(-1.0,  1.0), vec2(1.0,  1.0)); | ||||
| void main() { | ||||
|     gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); | ||||
| } | ||||
| )"; | ||||
|  | ||||
|         std::string fs_source = GLES ? fragment_shader_precision_OES : ""; | ||||
|         fs_source += R"( | ||||
| uniform samplerBuffer tbo; | ||||
| uniform vec2 tbo_size; | ||||
| uniform vec4 viewport; | ||||
|  | ||||
| out vec4 color; | ||||
|  | ||||
| void main() { | ||||
|     vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw; | ||||
|     int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x); | ||||
|     color = texelFetch(tbo, tbo_offset).rabg; | ||||
| } | ||||
| )"; | ||||
|         d24s8_abgr_shader.Create(vs_source.data(), fs_source.c_str()); | ||||
|  | ||||
|         OpenGLState state = OpenGLState::GetCurState(); | ||||
|         GLuint old_program = state.draw.shader_program; | ||||
|         state.draw.shader_program = d24s8_abgr_shader.handle; | ||||
|         state.Apply(); | ||||
|  | ||||
|         GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo"); | ||||
|         ASSERT(tbo_u_id != -1); | ||||
|         glUniform1i(tbo_u_id, 0); | ||||
|  | ||||
|         state.draw.shader_program = old_program; | ||||
|         state.Apply(); | ||||
|  | ||||
|         d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size"); | ||||
|         ASSERT(d24s8_abgr_tbo_size_u_id != -1); | ||||
|         d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport"); | ||||
|         ASSERT(d24s8_abgr_viewport_u_id != -1); | ||||
|     } | ||||
|  | ||||
|     ~PixelBufferD24S8toABGR() {} | ||||
|  | ||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, | ||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, | ||||
|                      GLuint draw_fb_handle) override { | ||||
|         OpenGLState prev_state = OpenGLState::GetCurState(); | ||||
|         SCOPE_EXIT({ prev_state.Apply(); }); | ||||
|  | ||||
|         OpenGLState state; | ||||
|         state.draw.read_framebuffer = read_fb_handle; | ||||
|         state.draw.draw_framebuffer = draw_fb_handle; | ||||
|         state.Apply(); | ||||
|  | ||||
|         glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); | ||||
|  | ||||
|         GLsizeiptr target_pbo_size = | ||||
|             static_cast<GLsizeiptr>(src_rect.GetWidth()) * src_rect.GetHeight() * 4; | ||||
|         if (target_pbo_size > d24s8_abgr_buffer_size) { | ||||
|             d24s8_abgr_buffer_size = target_pbo_size * 2; | ||||
|             glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY); | ||||
|         } | ||||
|  | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||
|                                src_tex, 0); | ||||
|         glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom), | ||||
|                      static_cast<GLsizei>(src_rect.GetWidth()), | ||||
|                      static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, | ||||
|                      GL_UNSIGNED_INT_24_8, 0); | ||||
|  | ||||
|         glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | ||||
|  | ||||
|         // PBO now contains src_tex in RABG format | ||||
|         state.draw.shader_program = d24s8_abgr_shader.handle; | ||||
|         state.draw.vertex_array = attributeless_vao.handle; | ||||
|         state.viewport.x = static_cast<GLint>(dst_rect.left); | ||||
|         state.viewport.y = static_cast<GLint>(dst_rect.bottom); | ||||
|         state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth()); | ||||
|         state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight()); | ||||
|         state.Apply(); | ||||
|  | ||||
|         OGLTexture tbo; | ||||
|         tbo.Create(); | ||||
|         glActiveTexture(GL_TEXTURE0); | ||||
|         glBindTexture(GL_TEXTURE_BUFFER, tbo.handle); | ||||
|         glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle); | ||||
|  | ||||
|         glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()), | ||||
|                     static_cast<GLfloat>(src_rect.GetHeight())); | ||||
|         glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x), | ||||
|                     static_cast<GLfloat>(state.viewport.y), | ||||
|                     static_cast<GLfloat>(state.viewport.width), | ||||
|                     static_cast<GLfloat>(state.viewport.height)); | ||||
|  | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, | ||||
|                                0); | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                0); | ||||
|         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|  | ||||
|         glBindTexture(GL_TEXTURE_BUFFER, 0); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     OGLVertexArray attributeless_vao; | ||||
|     OGLBuffer d24s8_abgr_buffer; | ||||
|     GLsizeiptr d24s8_abgr_buffer_size; | ||||
|     OGLProgram d24s8_abgr_shader; | ||||
|     GLint d24s8_abgr_tbo_size_u_id; | ||||
|     GLint d24s8_abgr_viewport_u_id; | ||||
| }; | ||||
|  | ||||
| FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { | ||||
|     reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, | ||||
|                            std::make_unique<PixelBufferD24S8toABGR>()); | ||||
|     reinterpreters.emplace(PixelFormatPair{PixelFormat::RGB5A1, PixelFormat::RGBA4}, | ||||
|                            std::make_unique<RGBA4toRGB5A1>()); | ||||
| } | ||||
|  | ||||
| FormatReinterpreterOpenGL::~FormatReinterpreterOpenGL() = default; | ||||
|  | ||||
| std::pair<FormatReinterpreterOpenGL::ReinterpreterMap::iterator, | ||||
|           FormatReinterpreterOpenGL::ReinterpreterMap::iterator> | ||||
| FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) { | ||||
|     return reinterpreters.equal_range(dst_format); | ||||
| } | ||||
|  | ||||
| } // namespace OpenGL | ||||
							
								
								
									
										62
									
								
								src/video_core/renderer_opengl/gl_format_reinterpreter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/video_core/renderer_opengl/gl_format_reinterpreter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| // Copyright 2020 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <map> | ||||
| #include <type_traits> | ||||
| #include <glad/glad.h> | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| #include "video_core/renderer_opengl/gl_surface_params.h" | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
| class RasterizerCacheOpenGL; | ||||
|  | ||||
| struct PixelFormatPair { | ||||
|     const SurfaceParams::PixelFormat dst_format, src_format; | ||||
|     struct less { | ||||
|         using is_transparent = void; | ||||
|         constexpr bool operator()(OpenGL::PixelFormatPair lhs, OpenGL::PixelFormatPair rhs) const { | ||||
|             return std::tie(lhs.dst_format, lhs.src_format) < | ||||
|                    std::tie(rhs.dst_format, rhs.src_format); | ||||
|         } | ||||
|         constexpr bool operator()(OpenGL::SurfaceParams::PixelFormat lhs, | ||||
|                                   OpenGL::PixelFormatPair rhs) const { | ||||
|             return lhs < rhs.dst_format; | ||||
|         } | ||||
|         constexpr bool operator()(OpenGL::PixelFormatPair lhs, | ||||
|                                   OpenGL::SurfaceParams::PixelFormat rhs) const { | ||||
|             return lhs.dst_format < rhs; | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| class FormatReinterpreterBase { | ||||
| public: | ||||
|     virtual ~FormatReinterpreterBase() = default; | ||||
|  | ||||
|     virtual void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, | ||||
|                              GLuint read_fb_handle, GLuint dst_tex, | ||||
|                              const Common::Rectangle<u32>& dst_rect, GLuint draw_fb_handle) = 0; | ||||
| }; | ||||
|  | ||||
| class FormatReinterpreterOpenGL : NonCopyable { | ||||
|     using ReinterpreterMap = | ||||
|         std::map<PixelFormatPair, std::unique_ptr<FormatReinterpreterBase>, PixelFormatPair::less>; | ||||
|  | ||||
| public: | ||||
|     explicit FormatReinterpreterOpenGL(); | ||||
|     ~FormatReinterpreterOpenGL(); | ||||
|  | ||||
|     std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator> GetPossibleReinterpretations( | ||||
|         SurfaceParams::PixelFormat dst_format); | ||||
|  | ||||
| private: | ||||
|     ReinterpreterMap reinterpreters; | ||||
| }; | ||||
|  | ||||
| } // namespace OpenGL | ||||
| @@ -32,6 +32,7 @@ | ||||
| #include "core/settings.h" | ||||
| #include "video_core/pica_state.h" | ||||
| #include "video_core/renderer_base.h" | ||||
| #include "video_core/renderer_opengl/gl_format_reinterpreter.h" | ||||
| #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||
| #include "video_core/renderer_opengl/gl_state.h" | ||||
| #include "video_core/renderer_opengl/gl_vars.h" | ||||
| @@ -1063,54 +1064,10 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() { | ||||
|     resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); | ||||
|     texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name, | ||||
|                                                          resolution_scale_factor); | ||||
|     format_reinterpreter = std::make_unique<FormatReinterpreterOpenGL>(); | ||||
|  | ||||
|     read_framebuffer.Create(); | ||||
|     draw_framebuffer.Create(); | ||||
|  | ||||
|     attributeless_vao.Create(); | ||||
|  | ||||
|     d24s8_abgr_buffer.Create(); | ||||
|     d24s8_abgr_buffer_size = 0; | ||||
|  | ||||
|     std::string vs_source = R"( | ||||
| const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); | ||||
| void main() { | ||||
|     gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); | ||||
| } | ||||
| )"; | ||||
|  | ||||
|     std::string fs_source = GLES ? fragment_shader_precision_OES : ""; | ||||
|     fs_source += R"( | ||||
| uniform samplerBuffer tbo; | ||||
| uniform vec2 tbo_size; | ||||
| uniform vec4 viewport; | ||||
|  | ||||
| out vec4 color; | ||||
|  | ||||
| void main() { | ||||
|     vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw; | ||||
|     int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x); | ||||
|     color = texelFetch(tbo, tbo_offset).rabg; | ||||
| } | ||||
| )"; | ||||
|     d24s8_abgr_shader.Create(vs_source.c_str(), fs_source.c_str()); | ||||
|  | ||||
|     OpenGLState state = OpenGLState::GetCurState(); | ||||
|     GLuint old_program = state.draw.shader_program; | ||||
|     state.draw.shader_program = d24s8_abgr_shader.handle; | ||||
|     state.Apply(); | ||||
|  | ||||
|     GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo"); | ||||
|     ASSERT(tbo_u_id != -1); | ||||
|     glUniform1i(tbo_u_id, 0); | ||||
|  | ||||
|     state.draw.shader_program = old_program; | ||||
|     state.Apply(); | ||||
|  | ||||
|     d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size"); | ||||
|     ASSERT(d24s8_abgr_tbo_size_u_id != -1); | ||||
|     d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport"); | ||||
|     ASSERT(d24s8_abgr_viewport_u_id != -1); | ||||
| } | ||||
|  | ||||
| RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { | ||||
| @@ -1136,64 +1093,6 @@ bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface, | ||||
|                         draw_framebuffer.handle); | ||||
| } | ||||
|  | ||||
| void RasterizerCacheOpenGL::ConvertD24S8toABGR(GLuint src_tex, | ||||
|                                                const Common::Rectangle<u32>& src_rect, | ||||
|                                                GLuint dst_tex, | ||||
|                                                const Common::Rectangle<u32>& dst_rect) { | ||||
|     OpenGLState prev_state = OpenGLState::GetCurState(); | ||||
|     SCOPE_EXIT({ prev_state.Apply(); }); | ||||
|  | ||||
|     OpenGLState state; | ||||
|     state.draw.read_framebuffer = read_framebuffer.handle; | ||||
|     state.draw.draw_framebuffer = draw_framebuffer.handle; | ||||
|     state.Apply(); | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); | ||||
|  | ||||
|     GLsizeiptr target_pbo_size = src_rect.GetWidth() * src_rect.GetHeight() * 4; | ||||
|     if (target_pbo_size > d24s8_abgr_buffer_size) { | ||||
|         d24s8_abgr_buffer_size = target_pbo_size * 2; | ||||
|         glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY); | ||||
|     } | ||||
|  | ||||
|     glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||
|     glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex, | ||||
|                            0); | ||||
|     glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom), | ||||
|                  static_cast<GLsizei>(src_rect.GetWidth()), | ||||
|                  static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, | ||||
|                  0); | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | ||||
|  | ||||
|     // PBO now contains src_tex in RABG format | ||||
|     state.draw.shader_program = d24s8_abgr_shader.handle; | ||||
|     state.draw.vertex_array = attributeless_vao.handle; | ||||
|     state.viewport.x = static_cast<GLint>(dst_rect.left); | ||||
|     state.viewport.y = static_cast<GLint>(dst_rect.bottom); | ||||
|     state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth()); | ||||
|     state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight()); | ||||
|     state.Apply(); | ||||
|  | ||||
|     OGLTexture tbo; | ||||
|     tbo.Create(); | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
|     glBindTexture(GL_TEXTURE_BUFFER, tbo.handle); | ||||
|     glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle); | ||||
|  | ||||
|     glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()), | ||||
|                 static_cast<GLfloat>(src_rect.GetHeight())); | ||||
|     glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x), | ||||
|                 static_cast<GLfloat>(state.viewport.y), static_cast<GLfloat>(state.viewport.width), | ||||
|                 static_cast<GLfloat>(state.viewport.height)); | ||||
|  | ||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); | ||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|  | ||||
|     glBindTexture(GL_TEXTURE_BUFFER, 0); | ||||
| } | ||||
|  | ||||
| Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, | ||||
|                                           bool load_if_create) { | ||||
|     if (params.addr == 0 || params.height * params.width == 0) { | ||||
| @@ -1721,9 +1620,15 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     auto validate_regions = surface->invalid_regions & validate_interval; | ||||
|     auto notify_validated = [&](SurfaceInterval interval) { | ||||
|         surface->invalid_regions.erase(interval); | ||||
|         validate_regions.erase(interval); | ||||
|     }; | ||||
|  | ||||
|     while (true) { | ||||
|         const auto it = surface->invalid_regions.find(validate_interval); | ||||
|         if (it == surface->invalid_regions.end()) | ||||
|         const auto it = validate_regions.begin(); | ||||
|         if (it == validate_regions.end()) | ||||
|             break; | ||||
|  | ||||
|         const auto interval = *it & validate_interval; | ||||
| @@ -1735,27 +1640,27 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, | ||||
|         if (copy_surface != nullptr) { | ||||
|             SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface); | ||||
|             CopySurface(copy_surface, surface, copy_interval); | ||||
|             surface->invalid_regions.erase(copy_interval); | ||||
|             notify_validated(copy_interval); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         // D24S8 to RGBA8 | ||||
|         if (surface->pixel_format == PixelFormat::RGBA8) { | ||||
|             params.pixel_format = PixelFormat::D24S8; | ||||
|             Surface reinterpret_surface = | ||||
|                 FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval); | ||||
|             if (reinterpret_surface != nullptr) { | ||||
|                 ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8); | ||||
|  | ||||
|                 SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface); | ||||
|                 SurfaceParams convert_params = surface->FromInterval(convert_interval); | ||||
|                 auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params); | ||||
|                 auto dest_rect = surface->GetScaledSubRect(convert_params); | ||||
|  | ||||
|                 ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect, | ||||
|                                    surface->texture.handle, dest_rect); | ||||
|  | ||||
|                 surface->invalid_regions.erase(convert_interval); | ||||
|         // Try to find surface in cache with different format | ||||
|         // that can can be reinterpreted to the requested format. | ||||
|         if (ValidateByReinterpretation(surface, params, interval)) { | ||||
|             notify_validated(interval); | ||||
|             continue; | ||||
|         } | ||||
|         // Could not find a matching reinterpreter, check if we need to implement a | ||||
|         // reinterpreter | ||||
|         if (NoUnimplementedReinterpretations(surface, params, interval) && | ||||
|             !IntervalHasInvalidPixelFormat(params, interval)) { | ||||
|             // No surfaces were found in the cache that had a matching bit-width. | ||||
|             // If the region was created entirely on the GPU, | ||||
|             // assume it was a developer mistake and skip flushing. | ||||
|             if (boost::icl::contains(dirty_regions, interval)) { | ||||
|                 LOG_DEBUG(Render_OpenGL, "Region created fully on GPU and reinterpretation is " | ||||
|                                          "invalid. Skipping validation"); | ||||
|                 validate_regions.erase(interval); | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
| @@ -1765,10 +1670,103 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, | ||||
|         surface->LoadGLBuffer(params.addr, params.end); | ||||
|         surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle, | ||||
|                                  draw_framebuffer.handle); | ||||
|         surface->invalid_regions.erase(params.GetInterval()); | ||||
|         notify_validated(params.GetInterval()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surface, | ||||
|                                                              SurfaceParams& params, | ||||
|                                                              const SurfaceInterval& interval) { | ||||
|     static constexpr std::array<PixelFormat, 17> all_formats{ | ||||
|         PixelFormat::RGBA8, PixelFormat::RGB8,   PixelFormat::RGB5A1, PixelFormat::RGB565, | ||||
|         PixelFormat::RGBA4, PixelFormat::IA8,    PixelFormat::RG8,    PixelFormat::I8, | ||||
|         PixelFormat::A8,    PixelFormat::IA4,    PixelFormat::I4,     PixelFormat::A4, | ||||
|         PixelFormat::ETC1,  PixelFormat::ETC1A4, PixelFormat::D16,    PixelFormat::D24, | ||||
|         PixelFormat::D24S8, | ||||
|     }; | ||||
|     bool implemented = true; | ||||
|     for (PixelFormat format : all_formats) { | ||||
|         if (SurfaceParams::GetFormatBpp(format) == surface->GetFormatBpp()) { | ||||
|             params.pixel_format = format; | ||||
|             // This could potentially be expensive, | ||||
|             // although experimentally it hasn't been too bad | ||||
|             Surface test_surface = | ||||
|                 FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval); | ||||
|             if (test_surface != nullptr) { | ||||
|                 LOG_WARNING(Render_OpenGL, "Missing pixel_format reinterpreter: {} -> {}", | ||||
|                             SurfaceParams::PixelFormatAsString(format), | ||||
|                             SurfaceParams::PixelFormatAsString(surface->pixel_format)); | ||||
|                 implemented = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return implemented; | ||||
| } | ||||
|  | ||||
| bool RasterizerCacheOpenGL::IntervalHasInvalidPixelFormat(SurfaceParams& params, | ||||
|                                                           const SurfaceInterval& interval) { | ||||
|     params.pixel_format = PixelFormat::Invalid; | ||||
|     for (const auto& set : RangeFromInterval(surface_cache, interval)) | ||||
|         for (const auto& surface : set.second) | ||||
|             if (surface->pixel_format == PixelFormat::Invalid) { | ||||
|                 LOG_WARNING(Render_OpenGL, "Surface found with invalid pixel format"); | ||||
|                 return true; | ||||
|             } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface, | ||||
|                                                        SurfaceParams& params, | ||||
|                                                        const SurfaceInterval& interval) { | ||||
|     auto [cvt_begin, cvt_end] = | ||||
|         format_reinterpreter->GetPossibleReinterpretations(surface->pixel_format); | ||||
|     for (auto reinterpreter = cvt_begin; reinterpreter != cvt_end; ++reinterpreter) { | ||||
|         PixelFormat format = reinterpreter->first.src_format; | ||||
|         params.pixel_format = format; | ||||
|         Surface reinterpret_surface = | ||||
|             FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval); | ||||
|  | ||||
|         if (reinterpret_surface != nullptr) { | ||||
|             SurfaceInterval reinterpret_interval = params.GetCopyableInterval(reinterpret_surface); | ||||
|             SurfaceParams reinterpret_params = surface->FromInterval(reinterpret_interval); | ||||
|             auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params); | ||||
|             auto dest_rect = surface->GetScaledSubRect(reinterpret_params); | ||||
|  | ||||
|             if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 && | ||||
|                 surface->res_scale == resolution_scale_factor) { | ||||
|                 // The destination surface is either a framebuffer, or a filtered texture. | ||||
|                 OGLTexture tmp_tex; | ||||
|                 tmp_tex.Create(); | ||||
|                 // Create an intermediate surface to convert to before blitting to the | ||||
|                 // destination. | ||||
|                 Common::Rectangle<u32> tmp_rect{0, dest_rect.GetHeight() / resolution_scale_factor, | ||||
|                                                 dest_rect.GetWidth() / resolution_scale_factor, 0}; | ||||
|                 AllocateSurfaceTexture(tmp_tex.handle, | ||||
|                                        GetFormatTuple(reinterpreter->first.dst_format), | ||||
|                                        tmp_rect.right, tmp_rect.top); | ||||
|                 reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect, | ||||
|                                                    read_framebuffer.handle, tmp_tex.handle, | ||||
|                                                    tmp_rect, draw_framebuffer.handle); | ||||
|                 SurfaceParams::SurfaceType type = | ||||
|                     SurfaceParams::GetFormatType(reinterpreter->first.dst_format); | ||||
|  | ||||
|                 if (!texture_filterer->Filter(tmp_tex.handle, tmp_rect, surface->texture.handle, | ||||
|                                               dest_rect, type, read_framebuffer.handle, | ||||
|                                               draw_framebuffer.handle)) { | ||||
|                     BlitTextures(tmp_tex.handle, tmp_rect, surface->texture.handle, dest_rect, type, | ||||
|                                  read_framebuffer.handle, draw_framebuffer.handle); | ||||
|                 } | ||||
|             } else { | ||||
|                 reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect, | ||||
|                                                    read_framebuffer.handle, surface->texture.handle, | ||||
|                                                    dest_rect, draw_framebuffer.handle); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) { | ||||
|     if (size == 0) | ||||
|         return; | ||||
|   | ||||
| @@ -34,6 +34,7 @@ namespace OpenGL { | ||||
|  | ||||
| class RasterizerCacheOpenGL; | ||||
| class TextureFilterer; | ||||
| class FormatReinterpreterOpenGL; | ||||
|  | ||||
| struct TextureCubeConfig { | ||||
|     PAddr px; | ||||
| @@ -240,9 +241,6 @@ public: | ||||
|     bool BlitSurfaces(const Surface& src_surface, const Common::Rectangle<u32>& src_rect, | ||||
|                       const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect); | ||||
|  | ||||
|     void ConvertD24S8toABGR(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | ||||
|                             const Common::Rectangle<u32>& dst_rect); | ||||
|  | ||||
|     /// Copy one surface's region to another | ||||
|     void CopySurface(const Surface& src_surface, const Surface& dst_surface, | ||||
|                      SurfaceInterval copy_interval); | ||||
| @@ -288,6 +286,18 @@ private: | ||||
|     /// Update surface's texture for given region when necessary | ||||
|     void ValidateSurface(const Surface& surface, PAddr addr, u32 size); | ||||
|  | ||||
|     // Returns false if there is a surface in the cache at the interval with the same bit-width, | ||||
|     bool NoUnimplementedReinterpretations(const OpenGL::Surface& surface, | ||||
|                                           OpenGL::SurfaceParams& params, | ||||
|                                           const OpenGL::SurfaceInterval& interval); | ||||
|  | ||||
|     // Return true if a surface with an invalid pixel format exists at the interval | ||||
|     bool IntervalHasInvalidPixelFormat(SurfaceParams& params, const SurfaceInterval& interval); | ||||
|  | ||||
|     // Attempt to find a reinterpretable surface in the cache and use it to copy for validation | ||||
|     bool ValidateByReinterpretation(const Surface& surface, SurfaceParams& params, | ||||
|                                     const SurfaceInterval& interval); | ||||
|  | ||||
|     /// Create a new surface | ||||
|     Surface CreateSurface(const SurfaceParams& params); | ||||
|  | ||||
| @@ -308,18 +318,13 @@ private: | ||||
|     OGLFramebuffer read_framebuffer; | ||||
|     OGLFramebuffer draw_framebuffer; | ||||
|  | ||||
|     OGLVertexArray attributeless_vao; | ||||
|     OGLBuffer d24s8_abgr_buffer; | ||||
|     GLsizeiptr d24s8_abgr_buffer_size; | ||||
|     OGLProgram d24s8_abgr_shader; | ||||
|     GLint d24s8_abgr_tbo_size_u_id; | ||||
|     GLint d24s8_abgr_viewport_u_id; | ||||
|     u16 resolution_scale_factor; | ||||
|  | ||||
|     std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; | ||||
|  | ||||
| public: | ||||
|     std::unique_ptr<TextureFilterer> texture_filterer; | ||||
|     std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter; | ||||
| }; | ||||
|  | ||||
| struct FormatTuple { | ||||
|   | ||||
| @@ -91,6 +91,47 @@ public: | ||||
|         return GetFormatBpp(pixel_format); | ||||
|     } | ||||
|  | ||||
|     static std::string_view PixelFormatAsString(PixelFormat format) { | ||||
|         switch (format) { | ||||
|         case PixelFormat::RGBA8: | ||||
|             return "RGBA8"; | ||||
|         case PixelFormat::RGB8: | ||||
|             return "RGB8"; | ||||
|         case PixelFormat::RGB5A1: | ||||
|             return "RGB5A1"; | ||||
|         case PixelFormat::RGB565: | ||||
|             return "RGB565"; | ||||
|         case PixelFormat::RGBA4: | ||||
|             return "RGBA4"; | ||||
|         case PixelFormat::IA8: | ||||
|             return "IA8"; | ||||
|         case PixelFormat::RG8: | ||||
|             return "RG8"; | ||||
|         case PixelFormat::I8: | ||||
|             return "I8"; | ||||
|         case PixelFormat::A8: | ||||
|             return "A8"; | ||||
|         case PixelFormat::IA4: | ||||
|             return "IA4"; | ||||
|         case PixelFormat::I4: | ||||
|             return "I4"; | ||||
|         case PixelFormat::A4: | ||||
|             return "A4"; | ||||
|         case PixelFormat::ETC1: | ||||
|             return "ETC1"; | ||||
|         case PixelFormat::ETC1A4: | ||||
|             return "ETC1A4"; | ||||
|         case PixelFormat::D16: | ||||
|             return "D16"; | ||||
|         case PixelFormat::D24: | ||||
|             return "D24"; | ||||
|         case PixelFormat::D24S8: | ||||
|             return "D24S8"; | ||||
|         default: | ||||
|             return "Not a real pixel format"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { | ||||
|         return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user