Rewrite of OpenGL renderer, including OS X support

Screen contents are now displayed using textured quads. This can be updated to expose an FBO once an OpenGL backend for when Pica rendering is being worked on. That FBO's texture can then be applied to the quads.

Previously, FBO blitting was used in order to display screen contents, which did not work on OS X. The new textured quad approach is less of a compatibility risk.
This commit is contained in:
Kevin Hartman 2014-08-21 00:27:53 -07:00
parent aa7472057a
commit cbfd6b6e52
8 changed files with 342 additions and 213 deletions

View File

@ -5,8 +5,9 @@ set(SRCS clipper.cpp
utils.cpp utils.cpp
vertex_shader.cpp vertex_shader.cpp
video_core.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 set(HEADERS clipper.h
command_processor.h command_processor.h
@ -18,7 +19,9 @@ set(HEADERS clipper.h
renderer_base.h renderer_base.h
vertex_shader.h vertex_shader.h
video_core.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}) add_library(video_core STATIC ${SRCS} ${HEADERS})

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
})";
}

View File

@ -6,24 +6,56 @@
#include "video_core/video_core.h" #include "video_core/video_core.h"
#include "video_core/renderer_opengl/renderer_opengl.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 "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 constructor
RendererOpenGL::RendererOpenGL() { 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); resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
m_resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
m_xfb_texture_top = 0; // Initialize screen info
m_xfb_texture_bottom = 0; 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; screen_info.Bottom().width = VideoCore::kScreenBottomWidth;
m_xfb_bottom = 0; screen_info.Bottom().height = VideoCore::kScreenBottomHeight;
screen_info.Bottom().flipped_xfb_data = xfb_bottom_flipped;
} }
/// RendererOpenGL destructor /// RendererOpenGL destructor
@ -32,41 +64,41 @@ RendererOpenGL::~RendererOpenGL() {
/// Swap buffers (render frame) /// Swap buffers (render frame)
void RendererOpenGL::SwapBuffers() { void RendererOpenGL::SwapBuffers() {
m_render_window->MakeCurrent(); render_window->MakeCurrent();
// EFB->XFB copy // EFB->XFB copy
// TODO(bunnei): This is a hack and does not belong here. The copy should be triggered by some // 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. // register write.
common::Rect framebuffer_size(0, 0, m_resolution_width, m_resolution_height); //
// 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); RenderXFB(framebuffer_size, framebuffer_size);
// XFB->Window copy // XFB->Window copy
RenderFramebuffer(); RenderFramebuffer();
// Swap buffers // Swap buffers
m_render_window->PollEvents(); render_window->PollEvents();
m_render_window->SwapBuffers(); render_window->SwapBuffers();
// Switch back to EFB and clear
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
} }
/** /**
* Helper function to flip framebuffer from left-to-right to top-to-bottom * Helper function to flip framebuffer from left-to-right to top-to-bottom
* @param in Pointer to input raw framebuffer in V/RAM * @param raw_data Pointer to input raw framebuffer in V/RAM
* @param out Pointer to output buffer with flipped framebuffer * @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 * @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; int in_coord = 0;
for (int x = 0; x < VideoCore::kScreenTopWidth; x++) { for (int x = 0; x < screen_info.width; x++) {
for (int y = VideoCore::kScreenTopHeight-1; y >= 0; y--) { for (int y = screen_info.height-1; y >= 0; y--) {
// TODO: Properly support other framebuffer formats // TODO: Properly support other framebuffer formats
int out_coord = (x + y * VideoCore::kScreenTopWidth) * 3; int out_coord = (x + y * screen_info.width) * 3;
out[out_coord] = in[in_coord]; // blue? screen_info.flipped_xfb_data[out_coord] = raw_data[in_coord + 2]; // Red
out[out_coord + 1] = in[in_coord + 1]; // green? screen_info.flipped_xfb_data[out_coord + 1] = raw_data[in_coord + 1]; // Green
out[out_coord + 2] = in[in_coord + 2]; // red? screen_info.flipped_xfb_data[out_coord + 2] = raw_data[in_coord]; // Blue
in_coord+=3; 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 * @param dst_rect Destination rectangle in output framebuffer to copy to
*/ */
void RendererOpenGL::RenderXFB(const common::Rect& src_rect, const common::Rect& dst_rect) { 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_top = GPU::g_regs.framebuffer_config[0];
const auto& framebuffer_sub = GPU::g_regs.framebuffer_config[1]; const auto& framebuffer_sub = GPU::g_regs.framebuffer_config[1];
const u32 active_fb_top = (framebuffer_top.active_fb == 1) const u32 active_fb_top = (framebuffer_top.active_fb == 1)
? Memory::PhysicalToVirtualAddress(framebuffer_top.address_left2) ? Memory::PhysicalToVirtualAddress(framebuffer_top.address_left2)
: Memory::PhysicalToVirtualAddress(framebuffer_top.address_left1); : Memory::PhysicalToVirtualAddress(framebuffer_top.address_left1);
const u32 active_fb_sub = (framebuffer_sub.active_fb == 1) const u32 active_fb_sub = (framebuffer_sub.active_fb == 1)
? Memory::PhysicalToVirtualAddress(framebuffer_sub.address_left2) ? Memory::PhysicalToVirtualAddress(framebuffer_sub.address_left2)
: Memory::PhysicalToVirtualAddress(framebuffer_sub.address_left1); : Memory::PhysicalToVirtualAddress(framebuffer_sub.address_left1);
DEBUG_LOG(GPU, "RenderXFB: 0x%08x bytes from 0x%08x(%dx%d), fmt %x", DEBUG_LOG(GPU, "RenderXFB: 0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer_top.stride * framebuffer_top.height, framebuffer_top.stride * framebuffer_top.height,
active_fb_top, (int)framebuffer_top.width, active_fb_top, (int)framebuffer_top.width,
(int)framebuffer_top.height, (int)framebuffer_top.format); (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), screen_info.Top());
FlipFramebuffer(Memory::GetPointer(active_fb_top), m_xfb_top_flipped); FlipFramebuffer(Memory::GetPointer(active_fb_sub), screen_info.Bottom());
FlipFramebuffer(Memory::GetPointer(active_fb_sub), m_xfb_bottom_flipped);
// 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); glBindTexture(GL_TEXTURE_2D, 0);
// Render target is destination framebuffer // TODO(princesspeachum):
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]); // Only the subset src_rect of the GPU buffer
glViewport(0, 0, VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight); // should be copied into the texture of the relevant screen.
//
// Render source is our EFB // The method's parameters also only include src_rect and dest_rec for one screen,
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_xfb_top); // so this may need to be changed (pair for each screen).
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);
} }
/// Initialize the FBO /// Initialize the FBO
void RendererOpenGL::InitFramebuffer() { void RendererOpenGL::InitFramebuffer() {
// TODO(bunnei): This should probably be implemented with the top screen and bottom screen as program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
// separate framebuffers 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 // Attach vertex data for top screen
glGenRenderbuffers(kMaxFramebuffers, m_fbo_rbo); // Generate primary RBOs glBindBuffer(GL_ARRAY_BUFFER, screen_info.Top().vertex_buffer_id);
glGenRenderbuffers(kMaxFramebuffers, m_fbo_depth_buffers); // Generate primary depth buffer glBufferData(GL_ARRAY_BUFFER, sizeof(g_vbuffer_top), g_vbuffer_top, GL_STATIC_DRAW);
for (int i = 0; i < kMaxFramebuffers; i++) { // Attach vertex data for bottom screen
// Generate color buffer storage glBindBuffer(GL_ARRAY_BUFFER, screen_info.Bottom().vertex_buffer_id);
glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_rbo[i]); glBufferData(GL_ARRAY_BUFFER, sizeof(g_vbuffer_bottom), g_vbuffer_bottom, GL_STATIC_DRAW);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, VideoCore::kScreenTopWidth,
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
// Generate depth buffer storage // Create color buffers for both screens
glBindRenderbuffer(GL_RENDERBUFFER, m_fbo_depth_buffers[i]); glGenTextures(1, &screen_info.Top().texture_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, VideoCore::kScreenTopWidth, glGenTextures(1, &screen_info.Bottom().texture_id);
VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
// Attach the buffers for (int i = 0; i < 2; i++) {
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 ScreenInfo* current_screen = &screen_info[i];
if (GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)) {
NOTICE_LOG(RENDER, "framebuffer(%d) initialized ok", i); // Allocate texture
} else { glBindTexture(GL_TEXTURE_2D, current_screen->vertex_buffer_id);
ERROR_LOG(RENDER, "couldn't create OpenGL frame buffer"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, current_screen->width, current_screen->height,
exit(1); 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, 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() { void RendererOpenGL::RenderFramebuffer() {
// Render target is default framebuffer glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0, 0, m_resolution_width, m_resolution_height);
// Render source is our XFB glUseProgram(program_id);
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo[kFramebuffer_VirtualXFB]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
// Blit // Bind texture in Texture Unit 0
glBlitFramebuffer(0, 0, m_resolution_width, m_resolution_height, 0, 0, m_resolution_width, glActiveTexture(GL_TEXTURE0);
m_resolution_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// Update the FPS count glEnableVertexAttribArray(0);
UpdateFramerate(); glEnableVertexAttribArray(1);
// Rebind EFB for (int i = 0; i < 2; i++) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo[kFramebuffer_EFB]);
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++; m_current_frame++;
} }
@ -251,40 +232,29 @@ void RendererOpenGL::UpdateFramerate() {
* @param window EmuWindow handle to emulator window to use for rendering * @param window EmuWindow handle to emulator window to use for rendering
*/ */
void RendererOpenGL::SetWindow(EmuWindow* window) { void RendererOpenGL::SetWindow(EmuWindow* window) {
m_render_window = window; render_window = window;
} }
/// Initialize the renderer /// Initialize the renderer
void RendererOpenGL::Init() { void RendererOpenGL::Init() {
m_render_window->MakeCurrent(); 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);
GLenum err = glewInit(); GLenum err = glewInit();
if (GLEW_OK != err) { if (GLEW_OK != err) {
ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...", ERROR_LOG(RENDER, "Failed to initialize GLEW! Error message: \"%s\". Exiting...",
glewGetErrorString(err)); glewGetErrorString(err));
exit(-1); 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 // Initialize everything else
// -------------------------- // --------------------------

View File

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

View File

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

View File

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