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 { | |||
| 53 | "Structure size and register block length don't match") | 53 | "Structure size and register block length don't match") |
| 54 | #endif | 54 | #endif |
| 55 | 55 | ||
| 56 | // All of those formats are described in reverse byte order, since the 3DS is little-endian. | ||
| 56 | enum class PixelFormat : u32 { | 57 | enum class PixelFormat : u32 { |
| 57 | RGBA8 = 0, | 58 | RGBA8 = 0, |
| 58 | RGB8 = 1, | 59 | RGB8 = 1, |
| @@ -61,6 +62,24 @@ struct Regs { | |||
| 61 | RGBA4 = 4, | 62 | RGBA4 = 4, |
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 65 | /** | ||
| 66 | * Returns the number of bytes per pixel. | ||
| 67 | */ | ||
| 68 | static int BytesPerPixel(PixelFormat format) { | ||
| 69 | switch (format) { | ||
| 70 | case PixelFormat::RGBA8: | ||
| 71 | return 4; | ||
| 72 | case PixelFormat::RGB8: | ||
| 73 | return 3; | ||
| 74 | case PixelFormat::RGB565: | ||
| 75 | case PixelFormat::RGB5A1: | ||
| 76 | case PixelFormat::RGBA4: | ||
| 77 | return 2; | ||
| 78 | default: | ||
| 79 | UNIMPLEMENTED(); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 64 | INSERT_PADDING_WORDS(0x4); | 83 | INSERT_PADDING_WORDS(0x4); |
| 65 | 84 | ||
| 66 | struct { | 85 | 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() { | |||
| 61 | for(int i : {0, 1}) { | 61 | for(int i : {0, 1}) { |
| 62 | const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; | 62 | const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; |
| 63 | 63 | ||
| 64 | if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) { | 64 | if (textures[i].width != (GLsizei)framebuffer.width || |
| 65 | textures[i].height != (GLsizei)framebuffer.height || | ||
| 66 | textures[i].format != framebuffer.color_format) { | ||
| 65 | // Reallocate texture if the framebuffer size has changed. | 67 | // Reallocate texture if the framebuffer size has changed. |
| 66 | // This is expected to not happen very often and hence should not be a | 68 | // This is expected to not happen very often and hence should not be a |
| 67 | // performance problem. | 69 | // performance problem. |
| 68 | glBindTexture(GL_TEXTURE_2D, textures[i].handle); | 70 | ConfigureFramebufferTexture(textures[i], framebuffer); |
| 69 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, framebuffer.width, framebuffer.height, 0, | ||
| 70 | GL_BGR, GL_UNSIGNED_BYTE, nullptr); | ||
| 71 | textures[i].width = framebuffer.width; | ||
| 72 | textures[i].height = framebuffer.height; | ||
| 73 | } | 71 | } |
| 74 | 72 | ||
| 75 | LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]); | 73 | LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]); |
| @@ -98,13 +96,12 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||
| 98 | 96 | ||
| 99 | const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); | 97 | const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); |
| 100 | 98 | ||
| 101 | // TODO: Handle other pixel formats | 99 | int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); |
| 102 | ASSERT_MSG(framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, | 100 | size_t pixel_stride = framebuffer.stride / bpp; |
| 103 | "Unsupported 3DS pixel format."); | ||
| 104 | 101 | ||
| 105 | size_t pixel_stride = framebuffer.stride / 3; | ||
| 106 | // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately | 102 | // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately |
| 107 | ASSERT(pixel_stride * 3 == framebuffer.stride); | 103 | ASSERT(pixel_stride * bpp == framebuffer.stride); |
| 104 | |||
| 108 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default | 105 | // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default |
| 109 | // only allows rows to have a memory alignement of 4. | 106 | // only allows rows to have a memory alignement of 4. |
| 110 | ASSERT(pixel_stride % 4 == 0); | 107 | ASSERT(pixel_stride % 4 == 0); |
| @@ -118,7 +115,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||
| 118 | // TODO: Applications could theoretically crash Citra here by specifying too large | 115 | // TODO: Applications could theoretically crash Citra here by specifying too large |
| 119 | // framebuffer sizes. We should make sure that this cannot happen. | 116 | // framebuffer sizes. We should make sure that this cannot happen. |
| 120 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, | 117 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, |
| 121 | GL_BGR, GL_UNSIGNED_BYTE, framebuffer_data); | 118 | texture.gl_format, texture.gl_type, framebuffer_data); |
| 122 | 119 | ||
| 123 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | 120 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| 124 | 121 | ||
| @@ -171,6 +168,59 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 171 | glBindTexture(GL_TEXTURE_2D, 0); | 168 | glBindTexture(GL_TEXTURE_2D, 0); |
| 172 | } | 169 | } |
| 173 | 170 | ||
| 171 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | ||
| 172 | const GPU::Regs::FramebufferConfig& framebuffer) { | ||
| 173 | GPU::Regs::PixelFormat format = framebuffer.color_format; | ||
| 174 | GLint internal_format; | ||
| 175 | |||
| 176 | texture.format = format; | ||
| 177 | texture.width = framebuffer.width; | ||
| 178 | texture.height = framebuffer.height; | ||
| 179 | |||
| 180 | switch (format) { | ||
| 181 | case GPU::Regs::PixelFormat::RGBA8: | ||
| 182 | internal_format = GL_RGBA; | ||
| 183 | texture.gl_format = GL_RGBA; | ||
| 184 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; | ||
| 185 | break; | ||
| 186 | |||
| 187 | case GPU::Regs::PixelFormat::RGB8: | ||
| 188 | // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every | ||
| 189 | // specific OpenGL type used in this function using native-endian (that is, little-endian | ||
| 190 | // mostly everywhere) for words or half-words. | ||
| 191 | // TODO: check how those behave on big-endian processors. | ||
| 192 | internal_format = GL_RGB; | ||
| 193 | texture.gl_format = GL_BGR; | ||
| 194 | texture.gl_type = GL_UNSIGNED_BYTE; | ||
| 195 | break; | ||
| 196 | |||
| 197 | case GPU::Regs::PixelFormat::RGB565: | ||
| 198 | internal_format = GL_RGB; | ||
| 199 | texture.gl_format = GL_RGB; | ||
| 200 | texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||
| 201 | break; | ||
| 202 | |||
| 203 | case GPU::Regs::PixelFormat::RGB5A1: | ||
| 204 | internal_format = GL_RGBA; | ||
| 205 | texture.gl_format = GL_RGBA; | ||
| 206 | texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1; | ||
| 207 | break; | ||
| 208 | |||
| 209 | case GPU::Regs::PixelFormat::RGBA4: | ||
| 210 | internal_format = GL_RGBA; | ||
| 211 | texture.gl_format = GL_RGBA; | ||
| 212 | texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4; | ||
| 213 | break; | ||
| 214 | |||
| 215 | default: | ||
| 216 | UNIMPLEMENTED(); | ||
| 217 | } | ||
| 218 | |||
| 219 | glBindTexture(GL_TEXTURE_2D, texture.handle); | ||
| 220 | glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | ||
| 221 | texture.gl_format, texture.gl_type, nullptr); | ||
| 222 | } | ||
| 223 | |||
| 174 | /** | 224 | /** |
| 175 | * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. | 225 | * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. |
| 176 | */ | 226 | */ |
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: | |||
| 43 | GLuint handle; | 43 | GLuint handle; |
| 44 | GLsizei width; | 44 | GLsizei width; |
| 45 | GLsizei height; | 45 | GLsizei height; |
| 46 | GPU::Regs::PixelFormat format; | ||
| 47 | GLenum gl_format; | ||
| 48 | GLenum gl_type; | ||
| 46 | }; | 49 | }; |
| 47 | 50 | ||
| 48 | void InitOpenGLObjects(); | 51 | void InitOpenGLObjects(); |
| 52 | static void ConfigureFramebufferTexture(TextureInfo& texture, | ||
| 53 | const GPU::Regs::FramebufferConfig& framebuffer); | ||
| 49 | void DrawScreens(); | 54 | void DrawScreens(); |
| 50 | void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); | 55 | void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); |
| 51 | void UpdateFramerate(); | 56 | void UpdateFramerate(); |