Merge pull request #539 from linkmauve/framebuffer-formats
Framebuffer formats
This commit is contained in:
		
						commit
						20dc07721c
					
				@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user