@ -6,24 +6,56 @@
# include "video_core/video_core.h"
# include "video_core/renderer_opengl/renderer_opengl.h"
# include "video_core/renderer_opengl/gl_shader_util.h"
# include "video_core/renderer_opengl/gl_shaders.h"
# include "core/mem_map.h"
# include <algorithm>
static const GLfloat kViewportAspectRatio =
( static_cast < float > ( VideoCore : : kScreenTopHeight ) + VideoCore : : kScreenBottomHeight ) / VideoCore : : kScreenTopWidth ;
// Fullscreen quad dimensions
static const GLfloat kTopScreenWidthNormalized = 2 ;
static const GLfloat kTopScreenHeightNormalized = kTopScreenWidthNormalized * ( static_cast < float > ( VideoCore : : kScreenTopHeight ) / VideoCore : : kScreenTopWidth ) ;
static const GLfloat kBottomScreenWidthNormalized = kTopScreenWidthNormalized * ( static_cast < float > ( VideoCore : : kScreenBottomWidth ) / VideoCore : : kScreenTopWidth ) ;
static const GLfloat kBottomScreenHeightNormalized = kBottomScreenWidthNormalized * ( static_cast < float > ( VideoCore : : kScreenBottomHeight ) / VideoCore : : kScreenBottomWidth ) ;
static const GLfloat g_vbuffer_top [ ] = {
// x, y, z u, v
- 1.0f , 0.0f , 0.0f , 0.0f , 1.0f ,
1.0f , 0.0f , 0.0f , 1.0f , 1.0f ,
1.0f , kTopScreenHeightNormalized , 0.0f , 1.0f , 0.0f ,
1.0f , kTopScreenHeightNormalized , 0.0f , 1.0f , 0.0f ,
- 1.0f , kTopScreenHeightNormalized , 0.0f , 0.0f , 0.0f ,
- 1.0f , 0.0f , 0.0f , 0.0f , 1.0f
} ;
static const GLfloat g_vbuffer_bottom [ ] = {
// x, y, z u, v
- ( kBottomScreenWidthNormalized / 2 ) , - kBottomScreenHeightNormalized , 0.0f , 0.0f , 1.0f ,
( kBottomScreenWidthNormalized / 2 ) , - kBottomScreenHeightNormalized , 0.0f , 1.0f , 1.0f ,
( kBottomScreenWidthNormalized / 2 ) , 0.0f , 0.0f , 1.0f , 0.0f ,
( kBottomScreenWidthNormalized / 2 ) , 0.0f , 0.0f , 1.0f , 0.0f ,
- ( kBottomScreenWidthNormalized / 2 ) , 0.0f , 0.0f , 0.0f , 0.0f ,
- ( kBottomScreenWidthNormalized / 2 ) , - kBottomScreenHeightNormalized , 0.0f , 0.0f , 1.0f
} ;
/// RendererOpenGL constructor
RendererOpenGL : : RendererOpenGL ( ) {
memset ( m_fbo , 0 , sizeof ( m_fbo ) ) ;
memset ( m_fbo_rbo , 0 , sizeof ( m_fbo_rbo ) ) ;
memset ( m_fbo_depth_buffers , 0 , sizeof ( m_fbo_depth_buffers ) ) ;
m_resolution_width = max ( VideoCore : : kScreenTopWidth , VideoCore : : kScreenBottomWidth ) ;
m_resolution_height = VideoCore : : kScreenTopHeight + VideoCore : : kScreenBottomHeight ;
resolution_width = std : : max ( VideoCore : : kScreenTopWidth , VideoCore : : kScreenBottomWidth ) ;
resolution_height = VideoCore : : kScreenTopHeight + VideoCore : : kScreenBottomHeight ;
m_xfb_texture_top = 0 ;
m_xfb_texture_bottom = 0 ;
// Initialize screen info
screen_info . Top ( ) . width = VideoCore : : kScreenTopWidth ;
screen_info . Top ( ) . height = VideoCore : : kScreenTopHeight ;
screen_info . Top ( ) . flipped_xfb_data = xfb_top_flipped ;
m_xfb_top = 0 ;
m_xfb_bottom = 0 ;
screen_info . Bottom ( ) . width = VideoCore : : kScreenBottomWidth ;
screen_info . Bottom ( ) . height = VideoCore : : kScreenBottomHeight ;
screen_info . Bottom ( ) . flipped_xfb_data = xfb_bottom_flipped ;
}
/// RendererOpenGL destructor
@ -32,41 +64,41 @@ RendererOpenGL::~RendererOpenGL() {
/// Swap buffers (render frame)
void RendererOpenGL : : SwapBuffers ( ) {
m_ render_window- > MakeCurrent ( ) ;
render_window- > MakeCurrent ( ) ;
// EFB->XFB copy
// TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some
// register write We're also treating both framebuffers as a single one in OpenGL.
common : : Rect framebuffer_size ( 0 , 0 , m_resolution_width , m_resolution_height ) ;
// register write.
//
// TODO(princesspeachum): (related to above^) this should only be called when there's new data, not every frame.
// Currently this uploads data that shouldn't have changed.
common : : Rect framebuffer_size ( 0 , 0 , resolution_width , resolution_height ) ;
RenderXFB ( framebuffer_size , framebuffer_size ) ;
// XFB->Window copy
RenderFramebuffer ( ) ;
// Swap buffers
m_render_window - > PollEvents ( ) ;
m_render_window - > SwapBuffers ( ) ;
// Switch back to EFB and clear
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_fbo [ kFramebuffer_EFB ] ) ;
render_window - > PollEvents ( ) ;
render_window - > SwapBuffers ( ) ;
}
/**
* Helper function to flip framebuffer from left - to - right to top - to - bottom
* @ param in Pointer to input raw framebuffer in V / RAM
* @ param out Pointer to output buffer with flipped framebuff er
* @ param raw_data Pointer to input raw framebuffer in V / RAM
* @ param screen_info ScreenInfo structure with screen size and output buffer point er
* @ todo Early on hack . . . I ' d like to find a more efficient way of doing this / bunnei
*/
void RendererOpenGL : : FlipFramebuffer ( const u8 * in, u8 * out ) {
void RendererOpenGL : : FlipFramebuffer ( const u8 * raw_data, ScreenInfo & screen_info ) {
int in_coord = 0 ;
for ( int x = 0 ; x < VideoCore: : kScreenTopW idth; x + + ) {
for ( int y = VideoCore: : kScreenTopH eight- 1 ; y > = 0 ; y - - ) {
for ( int x = 0 ; x < screen_info. w idth; x + + ) {
for ( int y = screen_info. h eight- 1 ; y > = 0 ; y - - ) {
// TODO: Properly support other framebuffer formats
int out_coord = ( x + y * VideoCore: : kScreenTopW idth) * 3 ;
out[ out_coord ] = in [ in_coord ] ; // blue?
out[ out_coord + 1 ] = in [ in_coord + 1 ] ; // green?
out[ out_coord + 2 ] = in [ in_coord + 2 ] ; // red?
in_coord + = 3 ;
int out_coord = ( x + y * screen_info. w idth) * 3 ;
screen_info. flipped_xfb_data [ out_coord ] = raw_data [ in_coord + 2 ] ; // Red
screen_info. flipped_xfb_data [ out_coord + 1 ] = raw_data [ in_coord + 1 ] ; // Green
screen_info. flipped_xfb_data [ out_coord + 2 ] = raw_data [ in_coord ] ; // Blue
in_coord + = 3 ;
}
}
}
@ -77,167 +109,116 @@ void RendererOpenGL::FlipFramebuffer(const u8* in, u8* out) {
* @ param dst_rect Destination rectangle in output framebuffer to copy to
*/
void RendererOpenGL : : RenderXFB ( const common : : Rect & src_rect , const common : : Rect & dst_rect ) {
const auto & framebuffer_top = GPU : : g_regs . framebuffer_config [ 0 ] ;
const auto & framebuffer_sub = GPU : : g_regs . framebuffer_config [ 1 ] ;
const u32 active_fb_top = ( framebuffer_top . active_fb = = 1 )
? Memory : : PhysicalToVirtualAddress ( framebuffer_top . address_left2 )
: Memory : : PhysicalToVirtualAddress ( framebuffer_top . address_left1 ) ;
? Memory : : PhysicalToVirtualAddress ( framebuffer_top . address_left2 )
: Memory : : PhysicalToVirtualAddress ( framebuffer_top . address_left1 ) ;
const u32 active_fb_sub = ( framebuffer_sub . active_fb = = 1 )
? Memory : : PhysicalToVirtualAddress ( framebuffer_sub . address_left2 )
: Memory : : PhysicalToVirtualAddress ( framebuffer_sub . address_left1 ) ;
? Memory : : PhysicalToVirtualAddress ( framebuffer_sub . address_left2 )
: Memory : : PhysicalToVirtualAddress ( framebuffer_sub . address_left1 ) ;
DEBUG_LOG ( GPU , " RenderXFB: 0x%08x bytes from 0x%08x(%dx%d), fmt %x " ,
framebuffer_top . stride * framebuffer_top . height ,
active_fb_top , ( int ) framebuffer_top . width ,
( int ) framebuffer_top . height , ( int ) framebuffer_top . format ) ;
// TODO: This should consider the GPU registers for framebuffer width, height and stride.
FlipFramebuffer ( Memory : : GetPointer ( active_fb_top ) , m_xfb_top_flipped ) ;
FlipFramebuffer ( Memory : : GetPointer ( active_fb_sub ) , m_xfb_bottom_flipped ) ;
FlipFramebuffer ( Memory : : GetPointer ( active_fb_top ) , screen_info . Top ( ) ) ;
FlipFramebuffer ( Memory : : GetPointer ( active_fb_sub ) , screen_info . Bottom ( ) ) ;
// Blit the top framebuffer
// ------------------------
for ( int i = 0 ; i < 2 ; i + + ) {
ScreenInfo * current_screen = & screen_info [ i ] ;
glBindTexture ( GL_TEXTURE_2D , current_screen - > texture_id ) ;
// TODO: This should consider the GPU registers for framebuffer width, height and stride.
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , current_screen - > width , current_screen - > height ,
GL_RGB , GL_UNSIGNED_BYTE , current_screen - > flipped_xfb_data ) ;
}
// Update textures with contents of XFB in RAM - top
glBindTexture ( GL_TEXTURE_2D , m_xfb_texture_top ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , VideoCore : : kScreenTopWidth , VideoCore : : kScreenTopHeight ,
GL_BGR , GL_UNSIGNED_BYTE , m_xfb_top_flipped ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
// Render target is destination framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_fbo [ kFramebuffer_VirtualXFB ] ) ;
glViewport ( 0 , 0 , VideoCore : : kScreenTopWidth , VideoCore : : kScreenTopHeight ) ;
// Render source is our EFB
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_xfb_top ) ;
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
// Blit
glBlitFramebuffer ( src_rect . x0_ , src_rect . y0_ , src_rect . x1_ , src_rect . y1_ ,
dst_rect . x0_ , dst_rect . y1_ , dst_rect . x1_ , dst_rect . y0_ ,
GL_COLOR_BUFFER_BIT , GL_LINEAR ) ;
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
// Blit the bottom framebuffer
// ---------------------------
// Update textures with contents of XFB in RAM - bottom
glBindTexture ( GL_TEXTURE_2D , m_xfb_texture_bottom ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , VideoCore : : kScreenTopWidth , VideoCore : : kScreenTopHeight ,
GL_BGR , GL_UNSIGNED_BYTE , m_xfb_bottom_flipped ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
// Render target is destination framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_fbo [ kFramebuffer_VirtualXFB ] ) ;
glViewport ( 0 , 0 ,
VideoCore : : kScreenBottomWidth , VideoCore : : kScreenBottomHeight ) ;
// Render source is our EFB
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_xfb_bottom ) ;
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
// Blit
int offset = ( VideoCore : : kScreenTopWidth - VideoCore : : kScreenBottomWidth ) / 2 ;
glBlitFramebuffer ( 0 , 0 , VideoCore : : kScreenBottomWidth , VideoCore : : kScreenBottomHeight ,
offset , VideoCore : : kScreenBottomHeight , VideoCore : : kScreenBottomWidth + offset , 0 ,
GL_COLOR_BUFFER_BIT , GL_LINEAR ) ;
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
// TODO(princesspeachum):
// Only the subset src_rect of the GPU buffer
// should be copied into the texture of the relevant screen.
//
// The method's parameters also only include src_rect and dest_rec for one screen,
// so this may need to be changed (pair for each screen).
}
/// Initialize the FBO
void RendererOpenGL : : InitFramebuffer ( ) {
// TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
// separate framebuffers
program_id = ShaderUtil : : LoadShaders ( GLShaders : : g_vertex_shader , GLShaders : : g_fragment_shader ) ;
sampler_id = glGetUniformLocation ( program_id , " sampler " ) ;
// Init the FBOs
// -------------
// Generate vertex buffers for both screens
glGenBuffers ( 1 , & screen_info . Top ( ) . vertex_buffer_id ) ;
glGenBuffers ( 1 , & screen_info . Bottom ( ) . vertex_buffer_id ) ;
glGenFramebuffers ( kMaxFramebuffers , m_fbo ) ; // Generate primary framebuffer
gl GenRenderbuffers( kMaxFramebuffers , m_fbo_rbo ) ; // Generate primary RBOs
gl GenRenderbuffers( kMaxFramebuffers , m_fbo_depth_buffers ) ; // Generate primary depth buffer
// Attach vertex data for top screen
glBindBuffer ( GL_ARRAY_BUFFER , screen_info . Top ( ) . vertex_buffer_id ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( g_vbuffer_top ) , g_vbuffer_top , GL_STATIC_DRAW ) ;
for ( int i = 0 ; i < kMaxFramebuffers ; i + + ) {
// Generate color buffer storage
glBindRenderbuffer ( GL_RENDERBUFFER , m_fbo_rbo [ i ] ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_RGBA8 , VideoCore : : kScreenTopWidth ,
VideoCore : : kScreenTopHeight + VideoCore : : kScreenBottomHeight ) ;
// Attach vertex data for bottom screen
glBindBuffer ( GL_ARRAY_BUFFER , screen_info . Bottom ( ) . vertex_buffer_id ) ;
glBufferData ( GL_ARRAY_BUFFER , sizeof ( g_vbuffer_bottom ) , g_vbuffer_bottom , GL_STATIC_DRAW ) ;
// Generate depth buffer storage
glBindRenderbuffer ( GL_RENDERBUFFER , m_fbo_depth_buffers [ i ] ) ;
glRenderbufferStorage ( GL_RENDERBUFFER , GL_DEPTH_COMPONENT32 , VideoCore : : kScreenTopWidth ,
VideoCore : : kScreenTopHeight + VideoCore : : kScreenBottomHeight ) ;
// Create color buffers for both screens
glGenTextures ( 1 , & screen_info . Top ( ) . texture_id ) ;
glGenTextures ( 1 , & screen_info . Bottom ( ) . texture_id ) ;
// Attach the buffers
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_fbo [ i ] ) ;
glFramebufferRenderbuffer ( GL_DRAW_FRAMEBUFFER , GL_DEPTH_ATTACHMENT ,
GL_RENDERBUFFER , m_fbo_depth_buffers [ i ] ) ;
glFramebufferRenderbuffer ( GL_DRAW_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 ,
GL_RENDERBUFFER , m_fbo_rbo [ i ] ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
// Check for completeness
if ( GL_FRAMEBUFFER_COMPLETE = = glCheckFramebufferStatus ( GL_DRAW_FRAMEBUFFER ) ) {
NOTICE_LOG ( RENDER , " framebuffer(%d) initialized ok " , i ) ;
} else {
ERROR_LOG ( RENDER , " couldn't create OpenGL frame buffer " ) ;
exit ( 1 ) ;
}
ScreenInfo * current_screen = & screen_info [ i ] ;
// Allocate texture
glBindTexture ( GL_TEXTURE_2D , current_screen - > vertex_buffer_id ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , current_screen - > width , current_screen - > height ,
0 , GL_RGB , GL_UNSIGNED_BYTE , NULL ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
}
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ; // Unbind our frame buffer(s)
// Initialize framebuffer textures
// -------------------------------
// Create XFB textures
glGenTextures ( 1 , & m_xfb_texture_top ) ;
glGenTextures ( 1 , & m_xfb_texture_bottom ) ;
// Alocate video memorry for XFB textures
glBindTexture ( GL_TEXTURE_2D , m_xfb_texture_top ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , VideoCore : : kScreenTopWidth , VideoCore : : kScreenTopHeight ,
0 , GL_RGB , GL_UNSIGNED_BYTE , NULL ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glBindTexture ( GL_TEXTURE_2D , m_xfb_texture_bottom ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB , VideoCore : : kScreenTopWidth , VideoCore : : kScreenTopHeight ,
0 , GL_RGB , GL_UNSIGNED_BYTE , NULL ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
// Create the FBO and attach color/depth textures
glGenFramebuffers ( 1 , & m_xfb_top ) ; // Generate framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_xfb_top ) ;
glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D ,
m_xfb_texture_top , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
glGenFramebuffers ( 1 , & m_xfb_bottom ) ; // Generate framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_xfb_bottom ) ;
glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER , GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D ,
m_xfb_texture_bottom , 0 ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
}
/// Blit the FBO to the OpenGL default framebuffer
void RendererOpenGL : : RenderFramebuffer ( ) {
// Render target is default framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , 0 ) ;
glViewport ( 0 , 0 , m_resolution_width , m_resolution_height ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
// Render source is our XFB
glBindFramebuffer ( GL_READ_FRAMEBUFFER , m_fbo [ kFramebuffer_VirtualXFB ] ) ;
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
glUseProgram ( program_id ) ;
// Blit
glBlitFramebuffer ( 0 , 0 , m_resolution_width , m_resolution_height , 0 , 0 , m_resolution_width ,
m_resolution_height , GL_COLOR_BUFFER_BIT , GL_LINEAR ) ;
// Bind texture in Texture Unit 0
glActiveTexture ( GL_TEXTURE0 ) ;
// Update the FPS count
UpdateFramerate( ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
// Rebind EFB
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER , m_fbo [ kFramebuffer_EFB ] ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
ScreenInfo * current_screen = & screen_info [ i ] ;
glBindTexture ( GL_TEXTURE_2D , current_screen - > texture_id ) ;
// Set sampler on Texture Unit 0
glUniform1i ( sampler_id , 0 ) ;
glBindBuffer ( GL_ARRAY_BUFFER , current_screen - > vertex_buffer_id ) ;
// Vertex buffer layout
const GLsizei stride = 5 * sizeof ( GLfloat ) ;
const GLvoid * uv_offset = ( const GLvoid * ) ( 3 * sizeof ( GLfloat ) ) ;
// Configure vertex buffer
glVertexAttribPointer ( 0 , 3 , GL_FLOAT , GL_FALSE , stride , NULL ) ;
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , stride , uv_offset ) ;
// Draw screen
glDrawArrays ( GL_TRIANGLES , 0 , 6 ) ;
}
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
m_current_frame + + ;
}
@ -251,40 +232,29 @@ void RendererOpenGL::UpdateFramerate() {
* @ param window EmuWindow handle to emulator window to use for rendering
*/
void RendererOpenGL : : SetWindow ( EmuWindow * window ) {
m_ render_window = window ;
render_window = window ;
}
/// Initialize the renderer
void RendererOpenGL : : Init ( ) {
m_render_window - > MakeCurrent ( ) ;
glShadeModel ( GL_SMOOTH ) ;
glStencilFunc ( GL_ALWAYS , 0 , 0 ) ;
glBlendFunc ( GL_ONE , GL_ONE ) ;
glViewport ( 0 , 0 , m_resolution_width , m_resolution_height ) ;
glClearDepth ( 1.0f ) ;
glEnable ( GL_DEPTH_TEST ) ;
glDisable ( GL_LIGHTING ) ;
glDepthFunc ( GL_LEQUAL ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ) ;
glDisable ( GL_STENCIL_TEST ) ;
glEnable ( GL_SCISSOR_TEST ) ;
glScissor ( 0 , 0 , m_resolution_width , m_resolution_height ) ;
glClearDepth ( 1.0f ) ;
render_window - > MakeCurrent ( ) ;
GLenum err = glewInit ( ) ;
if ( GLEW_OK ! = err ) {
ERROR_LOG ( RENDER , " Failed to initialize GLEW! Error message: \" %s \" . Exiting... " ,
glewGetErrorString ( err ) ) ;
glewGetErrorString ( err ) ) ;
exit ( - 1 ) ;
}
// Generate VAO
glGenVertexArrays ( 1 , & vertex_array_id ) ;
glBindVertexArray ( vertex_array_id ) ;
glClearColor ( 1.0f , 1.0f , 1.0f , 0.0f ) ;
glDisable ( GL_DEPTH_TEST ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 4 ) ;
// Initialize everything else
// --------------------------