From ee4691297f2f030cb112b2467db4ada142e2ba80 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 10 Jan 2018 22:43:17 -0500 Subject: [PATCH] renderer_opengl: Support rendering Switch framebuffer. --- src/video_core/renderer_base.h | 7 +- .../renderer_opengl/renderer_opengl.cpp | 199 ++++++------------ .../renderer_opengl/renderer_opengl.h | 17 +- 3 files changed, 84 insertions(+), 139 deletions(-) diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index f18917da3..28015aba9 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -15,7 +15,10 @@ public: /// Used to reference a framebuffer enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture }; - /// Struct describing framebuffer metadata + /** + * Struct describing framebuffer metadata + * TODO(bunnei): This struct belongs in the GPU code, but we don't have a good place for it yet. + */ struct FramebufferInfo { enum class PixelFormat : u32 { ABGR8 = 1, @@ -44,7 +47,7 @@ public: virtual ~RendererBase() {} /// Swap buffers (render frame) - virtual void SwapBuffers() = 0; + virtual void SwapBuffers(const FramebufferInfo& framebuffer_info) = 0; /** * Set the emulator window to use for renderer diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index c481d1d76..de61987a8 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -56,7 +56,9 @@ out vec4 color; uniform sampler2D color_texture; void main() { - color = texture(color_texture, frag_tex_coord); + // Swap RGBA -> ABGR so we don't have to do this on the CPU. This needs to change if we have to + // support more framebuffer pixel formats. + color = texture(color_texture, frag_tex_coord).abgr; } )"; @@ -98,44 +100,20 @@ RendererOpenGL::RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() = default; /// Swap buffers (render frame) -void RendererOpenGL::SwapBuffers() { +void RendererOpenGL::SwapBuffers(const FramebufferInfo& framebuffer_info) { // Maintain the rasterizer's state as a priority OpenGLState prev_state = OpenGLState::GetCurState(); state.Apply(); - for (int i : {0, 1}) { - const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; - - // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04 - u32 lcd_color_addr = - (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom); - lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr; - LCD::Regs::ColorFill color_fill = {0}; - LCD::Read(color_fill.raw, lcd_color_addr); - - if (color_fill.is_enabled) { - LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, - screen_infos[i].texture); - - // Resize the texture in case the framebuffer size has changed - screen_infos[i].texture.width = 1; - screen_infos[i].texture.height = 1; - } else { - if (screen_infos[i].texture.width != (GLsizei)framebuffer.width || - screen_infos[i].texture.height != (GLsizei)framebuffer.height || - screen_infos[i].texture.format != framebuffer.color_format) { - // Reallocate texture if the framebuffer size has changed. - // This is expected to not happen very often and hence should not be a - // performance problem. - ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer); - } - LoadFBToScreenInfo(framebuffer, screen_infos[i]); - - // Resize the texture in case the framebuffer size has changed - screen_infos[i].texture.width = framebuffer.width; - screen_infos[i].texture.height = framebuffer.height; - } + if (screen_info.texture.width != (GLsizei)framebuffer_info.width || + screen_info.texture.height != (GLsizei)framebuffer_info.height || + screen_info.texture.pixel_format != framebuffer_info.pixel_format) { + // Reallocate texture if the framebuffer size has changed. + // This is expected to not happen very often and hence should not be a + // performance problem. + ConfigureFramebufferTexture(screen_info.texture, framebuffer_info); } + LoadFBToScreenInfo(framebuffer_info, screen_info); DrawScreens(); @@ -270,56 +248,48 @@ static void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 /** * Loads framebuffer from emulated memory into the active OpenGL texture. */ -void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, +void RendererOpenGL::LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info, ScreenInfo& screen_info) { + const u32 bpp{FramebufferInfo::BytesPerPixel(framebuffer_info.pixel_format)}; + const u32 size_in_bytes{framebuffer_info.stride * framebuffer_info.height * bpp}; - const PAddr framebuffer_addr = - framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; + MortonCopyPixels128(framebuffer_info.width, framebuffer_info.height, bpp, 4, + Memory::GetPointer(framebuffer_info.address), gl_framebuffer_data.data(), + true); - LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", - framebuffer.stride * framebuffer.height, framebuffer_addr, (int)framebuffer.width, - (int)framebuffer.height, (int)framebuffer.format); - - int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); - size_t pixel_stride = framebuffer.stride / bpp; - - // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately - ASSERT(pixel_stride * bpp == framebuffer.stride); + LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%llx(%dx%d), fmt %x", size_in_bytes, + framebuffer_info.address, framebuffer_info.width, framebuffer_info.height, + (int)framebuffer_info.format); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default // only allows rows to have a memory alignement of 4. - ASSERT(pixel_stride % 4 == 0); + ASSERT(framebuffer_info.stride % 4 == 0); - if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, - static_cast(pixel_stride), screen_info)) { - // Reset the screen info's display texture to its own permanent texture - screen_info.display_texture = screen_info.texture.resource.handle; - screen_info.display_texcoords = MathUtil::Rectangle(0.f, 0.f, 1.f, 1.f); + // Reset the screen info's display texture to its own permanent texture + screen_info.display_texture = screen_info.texture.resource.handle; + screen_info.display_texcoords = MathUtil::Rectangle(0.f, 0.f, 1.f, 1.f); - Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); + Memory::RasterizerFlushRegion(framebuffer_info.address, size_in_bytes); - const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); + state.texture_units[0].texture_2d = screen_info.texture.resource.handle; + state.Apply(); - state.texture_units[0].texture_2d = screen_info.texture.resource.handle; - state.Apply(); + glActiveTexture(GL_TEXTURE0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)framebuffer_info.stride); - glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); + // Update existing texture + // TODO: Test what happens on hardware when you change the framebuffer dimensions so that + // they differ from the LCD resolution. + // TODO: Applications could theoretically crash Citra here by specifying too large + // framebuffer sizes. We should make sure that this cannot happen. + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_info.width, framebuffer_info.height, + screen_info.texture.gl_format, screen_info.texture.gl_type, + gl_framebuffer_data.data()); - // Update existing texture - // TODO: Test what happens on hardware when you change the framebuffer dimensions so that - // they differ from the LCD resolution. - // TODO: Applications could theoretically crash Citra here by specifying too large - // framebuffer sizes. We should make sure that this cannot happen. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, - screen_info.texture.gl_format, screen_info.texture.gl_type, - framebuffer_data); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - } + state.texture_units[0].texture_2d = 0; + state.Apply(); } /** @@ -377,74 +347,43 @@ void RendererOpenGL::InitOpenGLObjects() { glEnableVertexAttribArray(attrib_position); glEnableVertexAttribArray(attrib_tex_coord); - // Allocate textures for each screen - for (auto& screen_info : screen_infos) { - screen_info.texture.resource.Create(); + // Allocate textures for the screen + screen_info.texture.resource.Create(); - // Allocation of storage is deferred until the first frame, when we - // know the framebuffer size. + // Allocation of storage is deferred until the first frame, when we + // know the framebuffer size. - state.texture_units[0].texture_2d = screen_info.texture.resource.handle; - state.Apply(); + state.texture_units[0].texture_2d = screen_info.texture.resource.handle; + state.Apply(); - glActiveTexture(GL_TEXTURE0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - screen_info.display_texture = screen_info.texture.resource.handle; - } + screen_info.display_texture = screen_info.texture.resource.handle; state.texture_units[0].texture_2d = 0; state.Apply(); } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, - const GPU::Regs::FramebufferConfig& framebuffer) { - GPU::Regs::PixelFormat format = framebuffer.color_format; + const FramebufferInfo& framebuffer_info) { + + texture.width = framebuffer_info.width; + texture.height = framebuffer_info.height; + GLint internal_format; - - texture.format = format; - texture.width = framebuffer.width; - texture.height = framebuffer.height; - - switch (format) { - case GPU::Regs::PixelFormat::RGBA8: + switch (framebuffer_info.pixel_format) { + case FramebufferInfo::PixelFormat::ABGR8: + // Use RGBA8 and swap in the fragment shader internal_format = GL_RGBA; texture.gl_format = GL_RGBA; texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; + gl_framebuffer_data.resize(texture.width * texture.height * 4); break; - - case GPU::Regs::PixelFormat::RGB8: - // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every - // specific OpenGL type used in this function using native-endian (that is, little-endian - // mostly everywhere) for words or half-words. - // TODO: check how those behave on big-endian processors. - internal_format = GL_RGB; - texture.gl_format = GL_BGR; - texture.gl_type = GL_UNSIGNED_BYTE; - break; - - case GPU::Regs::PixelFormat::RGB565: - internal_format = GL_RGB; - texture.gl_format = GL_RGB; - texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; - break; - - case GPU::Regs::PixelFormat::RGB5A1: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1; - break; - - case GPU::Regs::PixelFormat::RGBA4: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - default: UNIMPLEMENTED(); } @@ -465,10 +404,10 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl auto& texcoords = screen_info.display_texcoords; std::array vertices = {{ - ScreenRectVertex(x, y, texcoords.top, texcoords.left), - ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.left), - ScreenRectVertex(x, y + h, texcoords.top, texcoords.right), - ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.right), + ScreenRectVertex(x, y, texcoords.top, texcoords.right), + ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x, y + h, texcoords.top, texcoords.left), + ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.left), }}; state.texture_units[0].texture_2d = screen_info.display_texture; @@ -500,8 +439,8 @@ void RendererOpenGL::DrawScreens() { glActiveTexture(GL_TEXTURE0); glUniform1i(uniform_color_texture, 0); - DrawSingleScreen(screen_infos[0], (float)screen.left, (float)screen.top, - (float)screen.GetWidth(), (float)screen.GetHeight()); + DrawSingleScreen(screen_info, (float)screen.left, (float)screen.top, (float)screen.GetWidth(), + (float)screen.GetHeight()); m_current_frame++; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 111b78466..9d2bb8423 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -4,7 +4,7 @@ #pragma once -#include +#include #include #include "common/common_types.h" #include "common/math_util.h" @@ -20,9 +20,9 @@ struct TextureInfo { OGLTexture resource; GLsizei width; GLsizei height; - GPU::Regs::PixelFormat format; GLenum gl_format; GLenum gl_type; + RendererBase::FramebufferInfo::PixelFormat pixel_format; }; /// Structure used for storing information about the display target for each 3DS screen @@ -38,7 +38,7 @@ public: ~RendererOpenGL() override; /// Swap buffers (render frame) - void SwapBuffers() override; + void SwapBuffers(const FramebufferInfo& framebuffer_info) override; /** * Set the emulator window to use for renderer @@ -55,13 +55,13 @@ public: private: void InitOpenGLObjects(); void ConfigureFramebufferTexture(TextureInfo& texture, - const GPU::Regs::FramebufferConfig& framebuffer); + const FramebufferInfo& framebuffer_info); void DrawScreens(); void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h); void UpdateFramerate(); // Loads framebuffer from emulated memory into the display information structure - void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, + void LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info, ScreenInfo& screen_info); // Fills active OpenGL texture with the given RGB color. void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture); @@ -75,8 +75,11 @@ private: OGLBuffer vertex_buffer; OGLShader shader; - /// Display information for top and bottom screens respectively - std::array screen_infos; + /// Display information for Switch screen + ScreenInfo screen_info; + + /// OpenGL framebuffer data + std::vector gl_framebuffer_data; // Shader uniform location indices GLuint uniform_modelview_matrix;