diff options
| author | bunnei <bunneidev@gmail.com> | 2018-01-10 22:43:17 -0500 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2018-01-10 23:28:59 -0500 | 
| commit | ee4691297f2f030cb112b2467db4ada142e2ba80 (patch) | |
| tree | 55c36da6343dd4e70641bc11cf4340013406b81c | |
| parent | 236d463c52afb4dbd3d36e3a34ff7fac5b4046d4 (diff) | |
renderer_opengl: Support rendering Switch framebuffer.
| -rw-r--r-- | src/video_core/renderer_base.h | 7 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 197 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 17 | 
3 files changed, 83 insertions, 138 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; - -    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); +    MortonCopyPixels128(framebuffer_info.width, framebuffer_info.height, bpp, 4, +                        Memory::GetPointer(framebuffer_info.address), gl_framebuffer_data.data(), +                        true); -    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); - -    if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, -                                         static_cast<u32>(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<float>(0.f, 0.f, 1.f, 1.f); +    ASSERT(framebuffer_info.stride % 4 == 0); -        Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); +    // 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<float>(0.f, 0.f, 1.f, 1.f); -        const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); +    Memory::RasterizerFlushRegion(framebuffer_info.address, size_in_bytes); -        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)pixel_stride); +    glActiveTexture(GL_TEXTURE0); +    glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)framebuffer_info.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.width, framebuffer.height, -                        screen_info.texture.gl_format, screen_info.texture.gl_type, -                        framebuffer_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_info.width, framebuffer_info.height, +                    screen_info.texture.gl_format, screen_info.texture.gl_type, +                    gl_framebuffer_data.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; -    GLint internal_format; +                                                 const FramebufferInfo& framebuffer_info) { -    texture.format = format; -    texture.width = framebuffer.width; -    texture.height = framebuffer.height; +    texture.width = framebuffer_info.width; +    texture.height = framebuffer_info.height; -    switch (format) { -    case GPU::Regs::PixelFormat::RGBA8: +    GLint internal_format; +    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<ScreenRectVertex, 4> 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 <array> +#include <vector>  #include <glad/glad.h>  #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<ScreenInfo, 2> screen_infos; +    /// Display information for Switch screen +    ScreenInfo screen_info; + +    /// OpenGL framebuffer data +    std::vector<u8> gl_framebuffer_data;      // Shader uniform location indices      GLuint uniform_modelview_matrix;  | 
