aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/video_core/renderer_opengl/renderer_opengl.cpp
diff options
context:
space:
mode:
authorGravatar Kevin Hartman <kevin@hart.mn>2014-08-21 00:27:53 -0700
committerGravatar Kevin Hartman <kevin@hart.mn>2014-08-25 20:56:59 -0700
commitcbfd6b6e52e3b8c1d6324d86461f4e8aa240a756 (patch)
tree31355774610d25174521e88ca7dcebfe5bbfe715 /src/video_core/renderer_opengl/renderer_opengl.cpp
parentaa7472057ae9b56e3f7216931cbb9e927e39a930 (diff)
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.
Diffstat (limited to 'src/video_core/renderer_opengl/renderer_opengl.cpp')
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp322
1 files changed, 146 insertions, 176 deletions
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f11a64fa..dc1b8e28 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/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
// --------------------------