From 54b8af14442279a89c3effaedc2390cb1f772bcb Mon Sep 17 00:00:00 2001 From: BreadFish64 Date: Tue, 24 Mar 2020 10:44:44 -0500 Subject: [PATCH] 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 --- src/video_core/CMakeLists.txt | 5 + .../renderer_opengl/depth_to_color.frag | 10 + .../renderer_opengl/depth_to_color.vert | 8 + .../renderer_opengl/ds_to_color.frag | 9 + .../renderer_opengl/gl_rasterizer_cache.cpp | 122 ++------- .../renderer_opengl/gl_rasterizer_cache.h | 12 + .../renderer_opengl/gl_shader_util.h | 6 +- .../renderer_opengl/texture_downloader_es.cpp | 254 ++++++++++++++++++ .../renderer_opengl/texture_downloader_es.h | 36 +++ 9 files changed, 359 insertions(+), 103 deletions(-) create mode 100644 src/video_core/renderer_opengl/depth_to_color.frag create mode 100644 src/video_core/renderer_opengl/depth_to_color.vert create mode 100644 src/video_core/renderer_opengl/ds_to_color.frag create mode 100644 src/video_core/renderer_opengl/texture_downloader_es.cpp create mode 100644 src/video_core/renderer_opengl/texture_downloader_es.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 53dbfe7b0..0397ea3fc 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -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 diff --git a/src/video_core/renderer_opengl/depth_to_color.frag b/src/video_core/renderer_opengl/depth_to_color.frag new file mode 100644 index 000000000..e69bed890 --- /dev/null +++ b/src/video_core/renderer_opengl/depth_to_color.frag @@ -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)); +} diff --git a/src/video_core/renderer_opengl/depth_to_color.vert b/src/video_core/renderer_opengl/depth_to_color.vert new file mode 100644 index 000000000..866d43b46 --- /dev/null +++ b/src/video_core/renderer_opengl/depth_to_color.vert @@ -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); +} diff --git a/src/video_core/renderer_opengl/ds_to_color.frag b/src/video_core/renderer_opengl/ds_to_color.frag new file mode 100644 index 000000000..954217064 --- /dev/null +++ b/src/video_core/renderer_opengl/ds_to_color.frag @@ -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); +} diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 262a936cf..3139c1101 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -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 fb_format_tuples_oes = {{ {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 }}; -static constexpr std::array 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 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 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(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& 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& 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(Settings::values.texture_filter_name, resolution_scale_factor); format_reinterpreter = std::make_unique(); + if (GLES) + texture_downloader_es = std::make_unique(false); read_framebuffer.Create(); draw_framebuffer.Create(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 6f7b7443a..da795a968 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -171,6 +171,8 @@ private: bool valid = false; }; +class RasterizerCacheOpenGL; + struct CachedSurface : SurfaceParams, std::enable_shared_from_this { CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} ~CachedSurface(); @@ -267,6 +269,15 @@ struct CachedTextureCube { std::shared_ptr nz; }; +static constexpr std::array 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 texture_filterer; std::unique_ptr format_reinterpreter; + std::unique_ptr texture_downloader_es; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 1871403f9..8b1beb5c0 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -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 )"; diff --git a/src/video_core/renderer_opengl/texture_downloader_es.cpp b/src/video_core/renderer_opengl/texture_downloader_es.cpp new file mode 100644 index 000000000..11663512e --- /dev/null +++ b/src/video_core/renderer_opengl/texture_downloader_es.cpp @@ -0,0 +1,254 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include + +#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(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{}, 4096, + [](std::size_t idx) { return static_cast((idx << 8) | (idx & 0xFF)); }); + LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT24 download test starting"); + test(depth_format_tuples[2], std::vector{}, 4096, + [](std::size_t idx) { return static_cast(idx << 8); }); + LOG_INFO(Render_OpenGL, "GL_DEPTH_COMPONENT16 download test starting"); + test(depth_format_tuples[0], std::vector{}, 256, + [](std::size_t idx) { return static_cast(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 diff --git a/src/video_core/renderer_opengl/texture_downloader_es.h b/src/video_core/renderer_opengl/texture_downloader_es.h new file mode 100644 index 000000000..66c27dde1 --- /dev/null +++ b/src/video_core/renderer_opengl/texture_downloader_es.h @@ -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