فهرست منبع

Merge pull request #74 from kevinhartman/master

Initial rewrite of opengl renderer.
bunnei 12 سال پیش
والد
کامیت
fef62d0e54

+ 7 - 4
src/video_core/CMakeLists.txt

@@ -5,8 +5,9 @@ set(SRCS    clipper.cpp
             utils.cpp
             vertex_shader.cpp
             video_core.cpp
-            debug_utils/debug_utils.cpp
-            renderer_opengl/renderer_opengl.cpp)
+            renderer_opengl/renderer_opengl.cpp
+            renderer_opengl/gl_shader_util.cpp
+            debug_utils/debug_utils.cpp)
 
 set(HEADERS clipper.h
             command_processor.h
@@ -18,7 +19,9 @@ set(HEADERS clipper.h
             renderer_base.h
             vertex_shader.h
             video_core.h
-            debug_utils/debug_utils.h
-            renderer_opengl/renderer_opengl.h)
+            renderer_opengl/renderer_opengl.h
+            renderer_opengl/gl_shader_util.h
+            renderer_opengl/gl_shaders.h
+            debug_utils/debug_utils.h)
 
 add_library(video_core STATIC ${SRCS} ${HEADERS})

+ 81 - 0
src/video_core/renderer_opengl/gl_shader_util.cpp

@@ -0,0 +1,81 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "gl_shader_util.h"
+#include "common/log.h"
+
+#include <vector>
+#include <algorithm>
+
+namespace ShaderUtil {
+
+GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
+
+    // Create the shaders
+    GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
+    GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
+
+    GLint result = GL_FALSE;
+    int info_log_length;
+
+    // Compile Vertex Shader
+    DEBUG_LOG(GPU, "Compiling vertex shader.");
+
+    glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL);
+    glCompileShader(vertex_shader_id);
+
+    // Check Vertex Shader
+    glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
+    glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+    std::vector<char> vertex_shader_error(info_log_length);
+    glGetShaderInfoLog(vertex_shader_id, info_log_length, NULL, &vertex_shader_error[0]);
+
+    if (info_log_length > 1) {
+        DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]);
+    }
+
+    // Compile Fragment Shader
+    DEBUG_LOG(GPU, "Compiling fragment shader.");
+
+    glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL);
+    glCompileShader(fragment_shader_id);
+
+    // Check Fragment Shader
+    glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
+    glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+    std::vector<char> fragment_shader_error(info_log_length);
+    glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]);
+
+    if (info_log_length > 1) {
+        DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]);
+    }
+
+    // Link the program
+    DEBUG_LOG(GPU, "Linking program.");
+
+    GLuint program_id = glCreateProgram();
+    glAttachShader(program_id, vertex_shader_id);
+    glAttachShader(program_id, fragment_shader_id);
+    glLinkProgram(program_id);
+
+    // Check the program
+    glGetProgramiv(program_id, GL_LINK_STATUS, &result);
+    glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length);
+
+    std::vector<char> program_error(std::max(info_log_length, int(1)));
+    glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]);
+
+    if (info_log_length > 1) {
+        DEBUG_LOG(GPU, "%s", &program_error[0]);
+    }
+
+    glDeleteShader(vertex_shader_id);
+    glDeleteShader(fragment_shader_id);
+
+    return program_id;
+}
+
+}

+ 13 - 0
src/video_core/renderer_opengl/gl_shader_util.h

@@ -0,0 +1,13 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <GL/glew.h>
+
+namespace ShaderUtil {
+
+GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path);
+
+}

+ 39 - 0
src/video_core/renderer_opengl/gl_shaders.h

@@ -0,0 +1,39 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace GLShaders {
+
+static const char g_vertex_shader[] = R"(
+#version 330 core
+layout(location = 0) in vec3 position;
+layout(location = 1) in vec2 texCoord;
+
+out vec2 UV;
+
+mat3 window_scale = mat3(
+                         vec3(1.0, 0.0, 0.0),
+                         vec3(0.0, 5.0/6.0, 0.0), // TODO(princesspeachum): replace hard-coded aspect with uniform
+                         vec3(0.0, 0.0, 1.0)
+                         );
+
+void main() {
+    gl_Position.xyz = window_scale * position;
+    gl_Position.w = 1.0;
+
+    UV = texCoord;
+})";
+
+static const char g_fragment_shader[] = R"(
+#version 330 core
+in vec2 UV;
+out vec3 color;
+uniform sampler2D sampler;
+
+void main() {
+    color = texture(sampler, UV).rgb;
+})";
+
+}

+ 146 - 176
src/video_core/renderer_opengl/renderer_opengl.cpp

@@ -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 framebuffer
+ * @param raw_data Pointer to input raw framebuffer in V/RAM
+ * @param screen_info ScreenInfo structure with screen size and output buffer pointer
  * @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::kScreenTopWidth; x++) {
-        for (int y = VideoCore::kScreenTopHeight-1; y >= 0; y--) {
+    for (int x = 0; x < screen_info.width; x++) {
+        for (int y = screen_info.height-1; y >= 0; y--) {
             // TODO: Properly support other framebuffer formats
-            int out_coord = (x + y * VideoCore::kScreenTopWidth) * 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.width) * 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());
+
+    for (int i = 0; i < 2; i++) {
+        ScreenInfo* current_screen = &screen_info[i];
 
-    // Blit the top framebuffer
-    // ------------------------
+        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);
+    // 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).
+}
 
-    // Render source is our EFB
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_top);
-    glReadBuffer(GL_COLOR_ATTACHMENT0);
+/// Initialize the FBO
+void RendererOpenGL::InitFramebuffer() {
+    program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
+    sampler_id = glGetUniformLocation(program_id, "sampler");
 
-    // 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);
+    // Generate vertex buffers for both screens
+    glGenBuffers(1, &screen_info.Top().vertex_buffer_id);
+    glGenBuffers(1, &screen_info.Bottom().vertex_buffer_id);
 
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    // 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);
 
-    // Blit the bottom framebuffer
-    // ---------------------------
+    // 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);
 
-    // 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);
+    // Create color buffers for both screens
+    glGenTextures(1, &screen_info.Top().texture_id);
+    glGenTextures(1, &screen_info.Bottom().texture_id);
 
-    // Render target is destination framebuffer
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
-    glViewport(0, 0,
-        VideoCore::kScreenBottomWidth, VideoCore::kScreenBottomHeight);
+    for (int i = 0; i < 2; i++) {
 
-    // Render source is our EFB
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_bottom);
-    glReadBuffer(GL_COLOR_ATTACHMENT0);
+        ScreenInfo* current_screen = &screen_info[i];
 
-    // 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);
+        // 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);
 
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    }
+
+    glBindTexture(GL_TEXTURE_2D, 0);
 }
 
-/// Initialize the FBO
-void RendererOpenGL::InitFramebuffer() {
-    // TODO(bunnei): This should probably be implemented with the top screen and bottom screen as
-    // separate framebuffers
-
-    // Init the FBOs
-    // -------------
-
-    glGenFramebuffers(kMaxFramebuffers, m_fbo); // Generate primary framebuffer
-    glGenRenderbuffers(kMaxFramebuffers, m_fbo_rbo); // Generate primary RBOs
-    glGenRenderbuffers(kMaxFramebuffers, m_fbo_depth_buffers); // Generate primary depth buffer
-
-    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);
-
-        // Generate depth buffer storage
-        glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]);
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth,
-            VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
-
-        // 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]);
-
-        // 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);
-        }
-    }
-    glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer(s)
+void RendererOpenGL::RenderFramebuffer() {
+    glClear(GL_COLOR_BUFFER_BIT);
 
-    // Initialize framebuffer textures
-    // -------------------------------
+    glUseProgram(program_id);
 
-    // Create XFB textures
-    glGenTextures(1, &m_xfb_texture_top);
-    glGenTextures(1, &m_xfb_texture_bottom);
+    // Bind texture in Texture Unit 0
+    glActiveTexture(GL_TEXTURE0);
 
-    // 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);
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
 
-    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);
+    for (int i = 0; i < 2; i++) {
 
-    // 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);
-}
+        ScreenInfo* current_screen = &screen_info[i];
 
-/// 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);
+        glBindTexture(GL_TEXTURE_2D, current_screen->texture_id);
 
-    // Render source is our XFB
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
-    glReadBuffer(GL_COLOR_ATTACHMENT0);
+        // Set sampler on Texture Unit 0
+        glUniform1i(sampler_id, 0);
 
-    // Blit
-    glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width,
-        m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+        glBindBuffer(GL_ARRAY_BUFFER, current_screen->vertex_buffer_id);
 
-    // Update the FPS count
-    UpdateFramerate();
+        // Vertex buffer layout
+        const GLsizei stride = 5 * sizeof(GLfloat);
+        const GLvoid* uv_offset = (const GLvoid*)(3 * sizeof(GLfloat));
 
-    // Rebind EFB
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
+        // 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
     // --------------------------
 

+ 36 - 27
src/video_core/renderer_opengl/renderer_opengl.h

@@ -11,26 +11,25 @@
 
 #include "video_core/renderer_base.h"
 
+#include <array>
 
 class RendererOpenGL : virtual public RendererBase {
 public:
 
-    static const int kMaxFramebuffers = 2;  ///< Maximum number of framebuffers
-
     RendererOpenGL();
     ~RendererOpenGL();
 
     /// Swap buffers (render frame)
     void SwapBuffers();
 
-    /** 
+    /**
      * Renders external framebuffer (XFB)
      * @param src_rect Source rectangle in XFB to copy
      * @param dst_rect Destination rectangle in output framebuffer to copy to
      */
     void RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect);
 
-    /** 
+    /**
      * Set the emulator window to use for renderer
      * @param window EmuWindow handle to emulator window to use for rendering
      */
@@ -53,37 +52,47 @@ private:
     /// Updates the framerate
     void UpdateFramerate();
 
-    /**
-     * 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 framebuffer
-     * @todo Early on hack... I'd like to find a more efficient way of doing this /bunnei
-     */
-    void FlipFramebuffer(const u8* in, u8* out);
+    /// Structure used for storing information for rendering each 3DS screen
+    struct ScreenInfo {
+        // Properties
+        int width;
+        int height;
+
+        // OpenGL object IDs
+        GLuint texture_id;
+        GLuint vertex_buffer_id;
 
+        // Temporary
+        u8* flipped_xfb_data;
+    };
 
-    EmuWindow*  m_render_window;                    ///< Handle to render window
-    u32         m_last_mode;                        ///< Last render mode
+    /**
+    * Helper function to flip framebuffer from left-to-right to top-to-bottom
+    * @param raw_data Pointer to input raw framebuffer in V/RAM
+    * @param screen_info ScreenInfo structure with screen size and output buffer pointer
+    * @todo Early on hack... I'd like to find a more efficient way of doing this /bunnei
+    */
+    void FlipFramebuffer(const u8* raw_data, ScreenInfo& screen_info);
 
-    int m_resolution_width;                         ///< Current resolution width
-    int m_resolution_height;                        ///< Current resolution height
+    EmuWindow*  render_window;                    ///< Handle to render window
+    u32         last_mode;                        ///< Last render mode
 
-    // Framebuffers
-    // ------------
+    int resolution_width;                         ///< Current resolution width
+    int resolution_height;                        ///< Current resolution height
 
-    GLuint m_fbo[kMaxFramebuffers];                 ///< Framebuffer objects
-    GLuint m_fbo_rbo[kMaxFramebuffers];             ///< Render buffer objects
-    GLuint m_fbo_depth_buffers[kMaxFramebuffers];   ///< Depth buffers objects
+    // OpenGL global object IDs
+    GLuint vertex_array_id;
+    GLuint program_id;
+    GLuint sampler_id;
 
-    GLuint m_xfb_texture_top;                       ///< GL handle to top framebuffer texture
-    GLuint m_xfb_texture_bottom;                    ///< GL handle to bottom framebuffer texture
-           
-    GLuint m_xfb_top;                               ///< GL handle to top framebuffer
-    GLuint m_xfb_bottom;                            ///< GL handle to bottom framebuffer
+    struct : std::array<ScreenInfo, 2> {
+        ScreenInfo& Top() { return (*this)[0]; }
+        ScreenInfo& Bottom() { return (*this)[1]; }
+    } screen_info;
 
     // "Flipped" framebuffers translate scanlines from native 3DS left-to-right to top-to-bottom
     // as OpenGL expects them in a texture. There probably is a more efficient way of doing this:
+    u8 xfb_top_flipped[VideoCore::kScreenTopWidth * VideoCore::kScreenTopHeight * 4];
+    u8 xfb_bottom_flipped[VideoCore::kScreenBottomWidth * VideoCore::kScreenBottomHeight * 4];
 
-    u8 m_xfb_top_flipped[VideoCore::kScreenTopWidth * VideoCore::kScreenTopHeight * 4];
-    u8 m_xfb_bottom_flipped[VideoCore::kScreenBottomWidth * VideoCore::kScreenBottomHeight * 4];
 };

+ 4 - 1
src/video_core/video_core.vcxproj

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
@@ -21,6 +21,7 @@
   <ItemGroup>
     <ClCompile Include="debug_utils\debug_utils.cpp" />
     <ClCompile Include="renderer_opengl\renderer_opengl.cpp" />
+    <ClCompile Include="renderer_opengl\gl_shader_util.cpp" />
     <ClCompile Include="clipper.cpp" />
     <ClCompile Include="command_processor.cpp" />
     <ClCompile Include="primitive_assembly.cpp" />
@@ -43,6 +44,8 @@
     <ClInclude Include="video_core.h" />
     <ClInclude Include="debug_utils\debug_utils.h" />
     <ClInclude Include="renderer_opengl\renderer_opengl.h" />
+    <ClInclude Include="renderer_opengl\gl_shader_util.h" />
+    <ClInclude Include="renderer_opengl\gl_shaders.h" />
   </ItemGroup>
   <ItemGroup>
     <Text Include="CMakeLists.txt" />

+ 14 - 3
src/video_core/video_core.vcxproj.filters

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
     <Filter Include="renderer_opengl">
@@ -12,6 +12,9 @@
     <ClCompile Include="renderer_opengl\renderer_opengl.cpp">
       <Filter>renderer_opengl</Filter>
     </ClCompile>
+    <ClCompile Include="renderer_opengl\gl_shader_util.cpp">
+      <Filter>renderer_opengl</Filter>
+    </ClCompile>
     <ClCompile Include="clipper.cpp" />
     <ClCompile Include="command_processor.cpp" />
     <ClCompile Include="primitive_assembly.cpp" />
@@ -35,7 +38,15 @@
     <ClInclude Include="utils.h" />
     <ClInclude Include="vertex_shader.h" />
     <ClInclude Include="video_core.h" />
-    <ClInclude Include="renderer_opengl\renderer_opengl.h" />
+    <ClInclude Include="renderer_opengl\renderer_opengl.h">
+      <Filter>renderer_opengl</Filter>
+    </ClInclude>
+    <ClInclude Include="renderer_opengl\gl_shader_util.h">
+      <Filter>renderer_opengl</Filter>
+    </ClInclude>
+    <ClInclude Include="renderer_opengl\gl_shaders.h">
+      <Filter>renderer_opengl</Filter>
+    </ClInclude>
     <ClInclude Include="debug_utils\debug_utils.h">
       <Filter>debug_utils</Filter>
     </ClInclude>
@@ -43,4 +54,4 @@
   <ItemGroup>
     <Text Include="CMakeLists.txt" />
   </ItemGroup>
-</Project>
+</Project>