mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	video_core: implement GLES depth/stencil downloads
video_core: disable depth/stencil texture download on OpenGL ES Disable deptch stencil shader in texture_downloader_es for now enable_depth_stencil DepthStencil remove GL_DEBUG_OUTPUT_SYNCHRONOUS
This commit is contained in:
		
							parent
							
								
									91f52c2fdb
								
							
						
					
					
						commit
						54b8af1444
					
				
					 9 changed files with 359 additions and 103 deletions
				
			
		|  | @ -54,6 +54,8 @@ add_library(video_core STATIC | |||
|     renderer_opengl/post_processing_opengl.h | ||||
|     renderer_opengl/renderer_opengl.cpp | ||||
|     renderer_opengl/renderer_opengl.h | ||||
|     renderer_opengl/texture_downloader_es.cpp | ||||
|     renderer_opengl/texture_downloader_es.h | ||||
|     renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp | ||||
|     renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h | ||||
|     renderer_opengl/texture_filters/bicubic/bicubic.cpp | ||||
|  | @ -99,6 +101,9 @@ add_library(video_core STATIC | |||
| ) | ||||
| 
 | ||||
| set(SHADER_FILES | ||||
|     renderer_opengl/depth_to_color.frag | ||||
|     renderer_opengl/depth_to_color.vert | ||||
|     renderer_opengl/ds_to_color.frag | ||||
|     renderer_opengl/texture_filters/anime4k/refine.frag | ||||
|     renderer_opengl/texture_filters/anime4k/x_gradient.frag | ||||
|     renderer_opengl/texture_filters/anime4k/y_gradient.frag | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/video_core/renderer_opengl/depth_to_color.frag
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/video_core/renderer_opengl/depth_to_color.frag
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| //? #version 320 es | ||||
| 
 | ||||
| out highp uint color; | ||||
| 
 | ||||
| uniform highp sampler2D depth; | ||||
| uniform int lod; | ||||
| 
 | ||||
| void main() { | ||||
|     color = uint(texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x * (exp2(32.0) - 1.0)); | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/video_core/renderer_opengl/depth_to_color.vert
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/video_core/renderer_opengl/depth_to_color.vert
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| //? #version 320 es | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/video_core/renderer_opengl/ds_to_color.frag
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/video_core/renderer_opengl/ds_to_color.frag
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| //? #version 320 es | ||||
| #extension GL_ARM_shader_framebuffer_fetch_depth_stencil : enable | ||||
| 
 | ||||
| out highp uint color; | ||||
| 
 | ||||
| void main() { | ||||
|     color = uint(gl_LastFragDepthARM * (exp2(24.0) - 1.0)) << 8; | ||||
|     color |= uint(gl_LastFragStencilARM); | ||||
| } | ||||
|  | @ -36,6 +36,7 @@ | |||
| #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_downloader_es.h" | ||||
| #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | ||||
| #include "video_core/utils.h" | ||||
| #include "video_core/video_core.h" | ||||
|  | @ -64,13 +65,6 @@ static constexpr std::array<FormatTuple, 5> fb_format_tuples_oes = {{ | |||
|     {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4},   // RGBA4
 | ||||
| }}; | ||||
| 
 | ||||
| static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{ | ||||
|     {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
 | ||||
|     {}, | ||||
|     {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT},   // D24
 | ||||
|     {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
 | ||||
| }}; | ||||
| 
 | ||||
| const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { | ||||
|     const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); | ||||
|     if (type == SurfaceType::Color) { | ||||
|  | @ -87,79 +81,6 @@ const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { | |||
|     return tex_tuple; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the | ||||
|  * texture to a framebuffer. | ||||
|  * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
 | ||||
|  */ | ||||
| static void GetTexImageOES(GLenum target, GLint level, GLenum format, GLenum type, GLint height, | ||||
|                            GLint width, GLint depth, GLubyte* pixels, std::size_t size) { | ||||
|     memset(pixels, 0x80, size); | ||||
| 
 | ||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
|     OpenGLState state; | ||||
| 
 | ||||
|     GLenum texture_binding = GL_NONE; | ||||
|     switch (target) { | ||||
|     case GL_TEXTURE_2D: | ||||
|         texture_binding = cur_state.texture_units[0].texture_2d; | ||||
|         break; | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: | ||||
|         texture_binding = cur_state.texture_cube_unit.texture_cube; | ||||
|         break; | ||||
|     default: | ||||
|         LOG_CRITICAL(Render_OpenGL, "Unexpected target {:x}", target); | ||||
|         UNIMPLEMENTED(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     GLint texture = 0; | ||||
|     glGetIntegerv(texture_binding, &texture); | ||||
|     if (!texture) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     OGLFramebuffer fbo; | ||||
|     fbo.Create(); | ||||
|     state.draw.read_framebuffer = fbo.handle; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     switch (target) { | ||||
|     case GL_TEXTURE_2D: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: { | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, | ||||
|                                level); | ||||
|         GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); | ||||
|         if (status != GL_FRAMEBUFFER_COMPLETE) { | ||||
|             LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status); | ||||
|         } | ||||
|         glReadPixels(0, 0, width, height, format, type, pixels); | ||||
|         break; | ||||
|     } | ||||
|     case GL_TEXTURE_3D_OES: | ||||
|         for (int i = 0; i < depth; i++) { | ||||
|             glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, | ||||
|                                    texture, level, i); | ||||
|             glReadPixels(0, 0, width, height, format, type, pixels + 4 * i * width * height); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     cur_state.Apply(); | ||||
| 
 | ||||
|     fbo.Release(); | ||||
| } | ||||
| 
 | ||||
| template <typename Map, typename Interval> | ||||
| static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { | ||||
|     return boost::make_iterator_range(map.equal_range(interval)); | ||||
|  | @ -775,23 +696,28 @@ void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) { | |||
|         LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path); | ||||
|         std::vector<u8> decoded_texture; | ||||
|         decoded_texture.resize(width * height * 4); | ||||
|         glBindTexture(GL_TEXTURE_2D, target_tex); | ||||
|         OpenGLState state = OpenGLState::GetCurState(); | ||||
|         GLuint old_texture = state.texture_units[0].texture_2d; | ||||
|         state.Apply(); | ||||
|         /*
 | ||||
|            GetTexImageOES is used even if not using OpenGL ES to work around a small issue that | ||||
|            happens if using custom textures with texture dumping at the same. | ||||
|            Let's say there's 2 textures that are both 32x32 and one of them gets replaced with a | ||||
|            higher quality 256x256 texture. If the 256x256 texture is displayed first and the 32x32 | ||||
|            texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture will | ||||
|            appear in the corner of the 256x256 texture. | ||||
|            If texture dumping is enabled and the 32x32 is undumped, Citra will attempt to dump it. | ||||
|            Since the underlying OpenGL texture is still 256x256, Citra crashes because it thinks the | ||||
|            texture is only 32x32. | ||||
|            higher quality 256x256 texture. If the 256x256 texture is displayed first and the | ||||
|            32x32 texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture | ||||
|            will appear in the corner of the 256x256 texture. If texture dumping is enabled and | ||||
|            the 32x32 is undumped, Citra will attempt to dump it. Since the underlying OpenGL | ||||
|            texture is still 256x256, Citra crashes because it thinks the texture is only 32x32. | ||||
|            GetTexImageOES conveniently only dumps the specified region, and works on both | ||||
|            desktop and ES. | ||||
|         */ | ||||
|         GetTexImageOES(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, height, width, 0, | ||||
|                        &decoded_texture[0], decoded_texture.size()); | ||||
|         glBindTexture(GL_TEXTURE_2D, 0); | ||||
|         // if the backend isn't OpenGL ES, this won't be initialized yet
 | ||||
|         if (!owner.texture_downloader_es) | ||||
|             owner.texture_downloader_es = std::make_unique<TextureDownloaderES>(false); | ||||
|         owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||||
|                                                  height, width, &decoded_texture[0]); | ||||
|         state.texture_units[0].texture_2d = old_texture; | ||||
|         state.Apply(); | ||||
|         Common::FlipRGBA8Texture(decoded_texture, width, height); | ||||
|         if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height)) | ||||
|             LOG_ERROR(Render_OpenGL, "Failed to save decoded texture"); | ||||
|  | @ -905,14 +831,6 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (GLES) { | ||||
|         if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { | ||||
|             // TODO(bunnei): This is unsupported on GLES right now, fixme
 | ||||
|             LOG_WARNING(Render_OpenGL, "Unsupported depth/stencil surface download"); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     MICROPROFILE_SCOPE(OpenGL_TextureDL); | ||||
| 
 | ||||
|     if (gl_buffer.empty()) { | ||||
|  | @ -950,9 +868,9 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint | |||
| 
 | ||||
|         glActiveTexture(GL_TEXTURE0); | ||||
|         if (GLES) { | ||||
|             GetTexImageOES(GL_TEXTURE_2D, 0, tuple.format, tuple.type, rect.GetHeight(), | ||||
|                            rect.GetWidth(), 0, &gl_buffer[buffer_offset], | ||||
|                            gl_buffer.size() - buffer_offset); | ||||
|             owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, | ||||
|                                                      rect.GetHeight(), rect.GetWidth(), | ||||
|                                                      &gl_buffer[buffer_offset]); | ||||
|         } else { | ||||
|             glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]); | ||||
|         } | ||||
|  | @ -1106,6 +1024,8 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() { | |||
|     texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name, | ||||
|                                                          resolution_scale_factor); | ||||
|     format_reinterpreter = std::make_unique<FormatReinterpreterOpenGL>(); | ||||
|     if (GLES) | ||||
|         texture_downloader_es = std::make_unique<TextureDownloaderES>(false); | ||||
| 
 | ||||
|     read_framebuffer.Create(); | ||||
|     draw_framebuffer.Create(); | ||||
|  |  | |||
|  | @ -171,6 +171,8 @@ private: | |||
|     bool valid = false; | ||||
| }; | ||||
| 
 | ||||
| class RasterizerCacheOpenGL; | ||||
| 
 | ||||
| struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> { | ||||
|     CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} | ||||
|     ~CachedSurface(); | ||||
|  | @ -267,6 +269,15 @@ struct CachedTextureCube { | |||
|     std::shared_ptr<SurfaceWatcher> nz; | ||||
| }; | ||||
| 
 | ||||
| static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{ | ||||
|     {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
 | ||||
|     {}, | ||||
|     {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT},   // D24
 | ||||
|     {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
 | ||||
| }}; | ||||
| 
 | ||||
| class TextureDownloaderES; | ||||
| 
 | ||||
| class RasterizerCacheOpenGL : NonCopyable { | ||||
| public: | ||||
|     RasterizerCacheOpenGL(); | ||||
|  | @ -373,6 +384,7 @@ public: | |||
| 
 | ||||
|     std::unique_ptr<TextureFilterer> texture_filterer; | ||||
|     std::unique_ptr<FormatReinterpreterOpenGL> format_reinterpreter; | ||||
|     std::unique_ptr<TextureDownloaderES> texture_downloader_es; | ||||
| }; | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  |  | |||
|  | @ -12,10 +12,12 @@ namespace OpenGL { | |||
| // High precision may or may not supported in GLES3. If it isn't, use medium precision instead.
 | ||||
| static constexpr char fragment_shader_precision_OES[] = R"( | ||||
| #ifdef GL_FRAGMENT_PRECISION_HIGH | ||||
|     precision highp float; | ||||
| precision highp int; | ||||
| precision highp float; | ||||
| precision highp samplerBuffer; | ||||
| #else | ||||
|     precision mediump float; | ||||
| precision mediump int; | ||||
| precision mediump float; | ||||
| precision mediump samplerBuffer; | ||||
| #endif // GL_FRAGMENT_PRECISION_HIGH
 | ||||
| )"; | ||||
|  |  | |||
							
								
								
									
										254
									
								
								src/video_core/renderer_opengl/texture_downloader_es.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								src/video_core/renderer_opengl/texture_downloader_es.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,254 @@ | |||
| // Copyright 2020 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <chrono> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <fmt/chrono.h> | ||||
| 
 | ||||
| #include "common/logging/log.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_downloader_es.h" | ||||
| 
 | ||||
| #include "shaders/depth_to_color.frag" | ||||
| #include "shaders/depth_to_color.vert" | ||||
| #include "shaders/ds_to_color.frag" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| /**
 | ||||
|  * Self tests for the texture downloader | ||||
|  */ | ||||
| void TextureDownloaderES::Test() { | ||||
|     auto cur_state = OpenGLState::GetCurState(); | ||||
|     OpenGLState state; | ||||
| 
 | ||||
|     { | ||||
|         GLint range[2]; | ||||
|         GLint precision; | ||||
| #define PRECISION_TEST(type)                                                                       \ | ||||
|     glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, type, range, &precision);                       \ | ||||
|     LOG_INFO(Render_OpenGL, #type " range: [{}, {}], precision: {}", range[0], range[1], precision); | ||||
|         PRECISION_TEST(GL_LOW_INT); | ||||
|         PRECISION_TEST(GL_MEDIUM_INT); | ||||
|         PRECISION_TEST(GL_HIGH_INT); | ||||
|         PRECISION_TEST(GL_LOW_FLOAT); | ||||
|         PRECISION_TEST(GL_MEDIUM_FLOAT); | ||||
|         PRECISION_TEST(GL_HIGH_FLOAT); | ||||
| #undef PRECISION_TEST | ||||
|     } | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
| 
 | ||||
|     const auto test = [this, &state](FormatTuple tuple, auto original_data, std::size_t tex_size, | ||||
|                                      auto data_generator) { | ||||
|         OGLTexture texture; | ||||
|         texture.Create(); | ||||
|         state.texture_units[0].texture_2d = texture.handle; | ||||
|         state.Apply(); | ||||
| 
 | ||||
|         original_data.resize(tex_size * tex_size); | ||||
|         for (std::size_t idx = 0; idx < original_data.size(); ++idx) | ||||
|             original_data[idx] = data_generator(idx); | ||||
|         glTexStorage2D(GL_TEXTURE_2D, 1, tuple.internal_format, tex_size, tex_size); | ||||
|         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_size, tex_size, tuple.format, tuple.type, | ||||
|                         original_data.data()); | ||||
| 
 | ||||
|         decltype(original_data) new_data(original_data.size()); | ||||
|         glFinish(); | ||||
|         auto start = std::chrono::high_resolution_clock::now(); | ||||
|         GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, tex_size, tex_size, | ||||
|                     new_data.data()); | ||||
|         glFinish(); | ||||
|         auto time = std::chrono::high_resolution_clock::now() - start; | ||||
|         LOG_INFO(Render_OpenGL, "test took {}", std::chrono::duration<double, std::milli>(time)); | ||||
| 
 | ||||
|         int diff = 0; | ||||
|         for (std::size_t idx = 0; idx < original_data.size(); ++idx) | ||||
|             if (new_data[idx] - original_data[idx] != diff) { | ||||
|                 diff = new_data[idx] - original_data[idx]; | ||||
|                 // every time the error between the real and expected value changes, log it
 | ||||
|                 // some error is expected in D24 due to floating point precision
 | ||||
|                 LOG_WARNING(Render_OpenGL, "difference changed at {:#X}: {:#X} -> {:#X}", idx, | ||||
|                             original_data[idx], new_data[idx]); | ||||
|             } | ||||
|     }; | ||||
|     LOG_INFO(Render_OpenGL, "GL_DEPTH24_STENCIL8 download test starting"); | ||||
|     test(depth_format_tuples[3], std::vector<u32>{}, 4096, | ||||
|          [](std::size_t idx) { return static_cast<u32>((idx << 8) | (idx & 0xFF)); }); | ||||
|     LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT24 download test starting"); | ||||
|     test(depth_format_tuples[2], std::vector<u32>{}, 4096, | ||||
|          [](std::size_t idx) { return static_cast<u32>(idx << 8); }); | ||||
|     LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT16 download test starting"); | ||||
|     test(depth_format_tuples[0], std::vector<u16>{}, 256, | ||||
|          [](std::size_t idx) { return static_cast<u16>(idx); }); | ||||
| 
 | ||||
|     cur_state.Apply(); | ||||
| } | ||||
| 
 | ||||
| TextureDownloaderES::TextureDownloaderES(bool enable_depth_stencil) { | ||||
|     vao.Create(); | ||||
|     read_fbo_generic.Create(); | ||||
| 
 | ||||
|     depth32_fbo.Create(); | ||||
|     r32ui_renderbuffer.Create(); | ||||
|     depth16_fbo.Create(); | ||||
|     r16_renderbuffer.Create(); | ||||
| 
 | ||||
|     const auto init_program = [](ConversionShader& converter, std::string_view frag) { | ||||
|         converter.program.Create(depth_to_color_vert.data(), frag.data()); | ||||
|         converter.lod_location = glGetUniformLocation(converter.program.handle, "lod"); | ||||
|     }; | ||||
| 
 | ||||
|     // xperia64: The depth stencil shader currently uses a GLES extension that is not supported
 | ||||
|     // across all devices Reportedly broken on Tegra devices and the Nexus 6P, so enabling it can be
 | ||||
|     // toggled
 | ||||
|     if (enable_depth_stencil) { | ||||
|         init_program(d24s8_r32ui_conversion_shader, ds_to_color_frag); | ||||
|     } | ||||
| 
 | ||||
|     init_program(d24_r32ui_conversion_shader, depth_to_color_frag); | ||||
|     init_program(d16_r16_conversion_shader, R"( | ||||
| out highp float color; | ||||
| 
 | ||||
| uniform highp sampler2D depth; | ||||
| uniform int lod; | ||||
| 
 | ||||
| void main(){ | ||||
|     color = texelFetch(depth, ivec2(gl_FragCoord.xy), lod).x; | ||||
| } | ||||
| )"); | ||||
| 
 | ||||
|     sampler.Create(); | ||||
|     glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||
|     glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||
| 
 | ||||
|     auto cur_state = OpenGLState::GetCurState(); | ||||
|     auto state = cur_state; | ||||
| 
 | ||||
|     state.draw.shader_program = d24s8_r32ui_conversion_shader.program.handle; | ||||
|     state.draw.draw_framebuffer = depth32_fbo.handle; | ||||
|     state.renderbuffer = r32ui_renderbuffer.handle; | ||||
|     state.Apply(); | ||||
|     glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, max_size, max_size); | ||||
|     glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||||
|                               r32ui_renderbuffer.handle); | ||||
|     glUniform1i(glGetUniformLocation(d24s8_r32ui_conversion_shader.program.handle, "depth"), 1); | ||||
| 
 | ||||
|     state.draw.draw_framebuffer = depth16_fbo.handle; | ||||
|     state.renderbuffer = r16_renderbuffer.handle; | ||||
|     state.Apply(); | ||||
|     glRenderbufferStorage(GL_RENDERBUFFER, GL_R16, max_size, max_size); | ||||
|     glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||||
|                               r16_renderbuffer.handle); | ||||
| 
 | ||||
|     cur_state.Apply(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * OpenGL ES does not support glReadBuffer for depth/stencil formats | ||||
|  * This gets around it by converting to a Red surface before downloading | ||||
|  */ | ||||
| GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, | ||||
|                                                 GLint height, GLint width) { | ||||
|     ASSERT(width <= max_size && height <= max_size); | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
|     OpenGLState state; | ||||
|     state.texture_units[0] = {cur_state.texture_units[0].texture_2d, sampler.handle}; | ||||
|     state.draw.vertex_array = vao.handle; | ||||
| 
 | ||||
|     OGLTexture texture_view; | ||||
|     const ConversionShader* converter; | ||||
|     switch (type) { | ||||
|     case GL_UNSIGNED_SHORT: | ||||
|         state.draw.draw_framebuffer = depth16_fbo.handle; | ||||
|         converter = &d16_r16_conversion_shader; | ||||
|         format = GL_RED; | ||||
|         break; | ||||
|     case GL_UNSIGNED_INT: | ||||
|         state.draw.draw_framebuffer = depth32_fbo.handle; | ||||
|         converter = &d24_r32ui_conversion_shader; | ||||
|         format = GL_RED_INTEGER; | ||||
|         break; | ||||
|     case GL_UNSIGNED_INT_24_8: | ||||
|         state.draw.draw_framebuffer = depth32_fbo.handle; | ||||
|         converter = &d24s8_r32ui_conversion_shader; | ||||
|         format = GL_RED_INTEGER; | ||||
|         type = GL_UNSIGNED_INT; | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE_MSG("Destination type not recognized"); | ||||
|     } | ||||
|     state.draw.shader_program = converter->program.handle; | ||||
|     state.viewport = {0, 0, width, height}; | ||||
|     state.Apply(); | ||||
|     if (converter->program.handle == d24s8_r32ui_conversion_shader.program.handle) { | ||||
|         // TODO BreadFish64: the ARM framebuffer reading extension is probably not the most optimal
 | ||||
|         // way to do this, search for another solution
 | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||
|                                state.texture_units[0].texture_2d, level); | ||||
|     } | ||||
| 
 | ||||
|     glUniform1i(converter->lod_location, level); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|     if (texture_view.handle) { | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); | ||||
|     } | ||||
|     return state.draw.draw_framebuffer; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the | ||||
|  * texture to a framebuffer. | ||||
|  * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
 | ||||
|  * Depth texture download assumes that the texture's format tuple matches what is found | ||||
|  * OpenGL::depth_format_tuples | ||||
|  */ | ||||
| void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type, | ||||
|                                       GLint height, GLint width, void* pixels) { | ||||
|     OpenGLState state = OpenGLState::GetCurState(); | ||||
|     GLuint texture; | ||||
|     const GLuint old_read_buffer = state.draw.read_framebuffer; | ||||
|     switch (target) { | ||||
|     case GL_TEXTURE_2D: | ||||
|         texture = state.texture_units[0].texture_2d; | ||||
|         break; | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: | ||||
|     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: | ||||
|     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: | ||||
|         texture = state.texture_cube_unit.texture_cube; | ||||
|         break; | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unexpected target {:x}", target); | ||||
|     } | ||||
| 
 | ||||
|     switch (format) { | ||||
|     case GL_DEPTH_COMPONENT: | ||||
|     case GL_DEPTH_STENCIL: | ||||
|         // unfortunately, the accurate way is too slow for release
 | ||||
|         return; | ||||
|         state.draw.read_framebuffer = ConvertDepthToColor(level, format, type, height, width); | ||||
|         state.Apply(); | ||||
|         break; | ||||
|     default: | ||||
|         state.draw.read_framebuffer = read_fbo_generic.handle; | ||||
|         state.Apply(); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, | ||||
|                                level); | ||||
|     } | ||||
|     GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); | ||||
|     if (status != GL_FRAMEBUFFER_COMPLETE) { | ||||
|         LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status); | ||||
|     } | ||||
|     glReadPixels(0, 0, width, height, format, type, pixels); | ||||
| 
 | ||||
|     state.draw.read_framebuffer = old_read_buffer; | ||||
|     state.Apply(); | ||||
| } | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
							
								
								
									
										36
									
								
								src/video_core/renderer_opengl/texture_downloader_es.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/video_core/renderer_opengl/texture_downloader_es.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| // Copyright 2020 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| class OpenGLState; | ||||
| 
 | ||||
| class TextureDownloaderES { | ||||
|     static constexpr u16 max_size = 1024; | ||||
| 
 | ||||
|     OGLVertexArray vao; | ||||
|     OGLFramebuffer read_fbo_generic; | ||||
|     OGLFramebuffer depth32_fbo, depth16_fbo; | ||||
|     OGLRenderbuffer r32ui_renderbuffer, r16_renderbuffer; | ||||
|     struct ConversionShader { | ||||
|         OGLProgram program; | ||||
|         GLint lod_location{-1}; | ||||
|     } d24_r32ui_conversion_shader, d16_r16_conversion_shader, d24s8_r32ui_conversion_shader; | ||||
|     OGLSampler sampler; | ||||
| 
 | ||||
|     void Test(); | ||||
|     GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, GLint height, | ||||
|                                GLint width); | ||||
| 
 | ||||
| public: | ||||
|     TextureDownloaderES(bool enable_depth_stencil); | ||||
| 
 | ||||
|     void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, GLint height, | ||||
|                      GLint width, void* pixels); | ||||
| }; | ||||
| } // namespace OpenGL
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue