mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #539 from linkmauve/framebuffer-formats
Framebuffer formats
This commit is contained in:
		
						commit
						20dc07721c
					
				
					 3 changed files with 86 additions and 12 deletions
				
			
		|  | @ -53,6 +53,7 @@ struct Regs { | ||||||
|                   "Structure size and register block length don't match") |                   "Structure size and register block length don't match") | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |     // All of those formats are described in reverse byte order, since the 3DS is little-endian.
 | ||||||
|     enum class PixelFormat : u32 { |     enum class PixelFormat : u32 { | ||||||
|         RGBA8  = 0, |         RGBA8  = 0, | ||||||
|         RGB8   = 1, |         RGB8   = 1, | ||||||
|  | @ -61,6 +62,24 @@ struct Regs { | ||||||
|         RGBA4  = 4, |         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); |     INSERT_PADDING_WORDS(0x4); | ||||||
| 
 | 
 | ||||||
|     struct { |     struct { | ||||||
|  |  | ||||||
|  | @ -61,15 +61,13 @@ void RendererOpenGL::SwapBuffers() { | ||||||
|     for(int i : {0, 1}) { |     for(int i : {0, 1}) { | ||||||
|         const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; |         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.
 |             // Reallocate texture if the framebuffer size has changed.
 | ||||||
|             // This is expected to not happen very often and hence should not be a
 |             // This is expected to not happen very often and hence should not be a
 | ||||||
|             // performance problem.
 |             // performance problem.
 | ||||||
|             glBindTexture(GL_TEXTURE_2D, textures[i].handle); |             ConfigureFramebufferTexture(textures[i], framebuffer); | ||||||
|             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; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]); |         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); |     const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); | ||||||
| 
 | 
 | ||||||
|     // TODO: Handle other pixel formats
 |     int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); | ||||||
|     ASSERT_MSG(framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, |     size_t pixel_stride = framebuffer.stride / bpp; | ||||||
|                      "Unsupported 3DS pixel format."); |  | ||||||
| 
 | 
 | ||||||
|     size_t pixel_stride = framebuffer.stride / 3; |  | ||||||
|     // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
 |     // 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
 |     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
 | ||||||
|     // only allows rows to have a memory alignement of 4.
 |     // only allows rows to have a memory alignement of 4.
 | ||||||
|     ASSERT(pixel_stride % 4 == 0); |     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
 |     // TODO: Applications could theoretically crash Citra here by specifying too large
 | ||||||
|     //       framebuffer sizes. We should make sure that this cannot happen.
 |     //       framebuffer sizes. We should make sure that this cannot happen.
 | ||||||
|     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, |     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); |     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||||
| 
 | 
 | ||||||
|  | @ -171,6 +168,59 @@ void RendererOpenGL::InitOpenGLObjects() { | ||||||
|     glBindTexture(GL_TEXTURE_2D, 0); |     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. |  * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | @ -43,9 +43,14 @@ private: | ||||||
|         GLuint handle; |         GLuint handle; | ||||||
|         GLsizei width; |         GLsizei width; | ||||||
|         GLsizei height; |         GLsizei height; | ||||||
|  |         GPU::Regs::PixelFormat format; | ||||||
|  |         GLenum gl_format; | ||||||
|  |         GLenum gl_type; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     void InitOpenGLObjects(); |     void InitOpenGLObjects(); | ||||||
|  |     static void ConfigureFramebufferTexture(TextureInfo& texture, | ||||||
|  |                                             const GPU::Regs::FramebufferConfig& framebuffer); | ||||||
|     void DrawScreens(); |     void DrawScreens(); | ||||||
|     void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); |     void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); | ||||||
|     void UpdateFramerate(); |     void UpdateFramerate(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue