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

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/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());
// 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
glGenRenderbuffers(kMaxFramebuffers, m_fbo_rbo); // Generate primary RBOs
glGenRenderbuffers(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
// --------------------------

View File

@ -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();
/// 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
* @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);
* 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);
EmuWindow* render_window; ///< Handle to render window
u32 last_mode; ///< Last render mode
EmuWindow* m_render_window; ///< Handle to render window
u32 m_last_mode; ///< Last render mode
int resolution_width; ///< Current resolution width
int resolution_height; ///< Current resolution height
int m_resolution_width; ///< Current resolution width
int m_resolution_height; ///< Current resolution height
// OpenGL global object IDs
GLuint vertex_array_id;
GLuint program_id;
GLuint sampler_id;
// Framebuffers
// ------------
GLuint m_fbo[kMaxFramebuffers]; ///< Framebuffer objects
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
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];
};

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">
<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" />

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">
<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>