diff options
-rw-r--r-- | src/core/hw/gpu.h | 19 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 74 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 5 |
3 files changed, 86 insertions, 12 deletions
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 7c3a17ee5..9fd694f65 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -53,6 +53,7 @@ struct Regs { "Structure size and register block length don't match") #endif + // All of those formats are described in reverse byte order, since the 3DS is little-endian. enum class PixelFormat : u32 { RGBA8 = 0, RGB8 = 1, @@ -61,6 +62,24 @@ struct Regs { RGBA4 = 4, }; + /** + * Returns the number of bytes per pixel. + */ + static int BytesPerPixel(PixelFormat format) { + switch (format) { + case PixelFormat::RGBA8: + return 4; + case PixelFormat::RGB8: + return 3; + case PixelFormat::RGB565: + case PixelFormat::RGB5A1: + case PixelFormat::RGBA4: + return 2; + default: + UNIMPLEMENTED(); + } + } + INSERT_PADDING_WORDS(0x4); struct { diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 735c0cf45..272695174 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -61,15 +61,13 @@ void RendererOpenGL::SwapBuffers() { for(int i : {0, 1}) { const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; - if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) { + if (textures[i].width != (GLsizei)framebuffer.width || + textures[i].height != (GLsizei)framebuffer.height || + textures[i].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. - glBindTexture(GL_TEXTURE_2D, textures[i].handle); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, framebuffer.width, framebuffer.height, 0, - GL_BGR, GL_UNSIGNED_BYTE, nullptr); - textures[i].width = framebuffer.width; - textures[i].height = framebuffer.height; + ConfigureFramebufferTexture(textures[i], framebuffer); } LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]); @@ -98,13 +96,12 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); - // TODO: Handle other pixel formats - ASSERT_MSG(framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, - "Unsupported 3DS pixel format."); + int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); + size_t pixel_stride = framebuffer.stride / bpp; - size_t pixel_stride = framebuffer.stride / 3; // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately - ASSERT(pixel_stride * 3 == framebuffer.stride); + ASSERT(pixel_stride * bpp == framebuffer.stride); + // 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); @@ -118,7 +115,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& // 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, - GL_BGR, GL_UNSIGNED_BYTE, framebuffer_data); + texture.gl_format, texture.gl_type, framebuffer_data); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -171,6 +168,59 @@ void RendererOpenGL::InitOpenGLObjects() { glBindTexture(GL_TEXTURE_2D, 0); } +void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, + const GPU::Regs::FramebufferConfig& framebuffer) { + GPU::Regs::PixelFormat format = framebuffer.color_format; + GLint internal_format; + + texture.format = format; + texture.width = framebuffer.width; + texture.height = framebuffer.height; + + switch (format) { + case GPU::Regs::PixelFormat::RGBA8: + internal_format = GL_RGBA; + texture.gl_format = GL_RGBA; + texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; + 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(); + } + + glBindTexture(GL_TEXTURE_2D, texture.handle); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, + texture.gl_format, texture.gl_type, nullptr); +} + /** * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. */ diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index cf78c1e77..bcabab557 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -43,9 +43,14 @@ private: GLuint handle; GLsizei width; GLsizei height; + GPU::Regs::PixelFormat format; + GLenum gl_format; + GLenum gl_type; }; void InitOpenGLObjects(); + static void ConfigureFramebufferTexture(TextureInfo& texture, + const GPU::Regs::FramebufferConfig& framebuffer); void DrawScreens(); void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); void UpdateFramerate(); |