diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 71a1b5eccf..0d737573b6 100644 --- a/src/video_core/CMakeLists.txt +++ b/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}) diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp new file mode 100644 index 0000000000..10239c8a73 --- /dev/null +++ b/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 +#include + +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 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 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 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; +} + +} diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h new file mode 100644 index 0000000000..563f1015c1 --- /dev/null +++ b/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 + +namespace ShaderUtil { + +GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); + +} diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h new file mode 100644 index 0000000000..f84424c47e --- /dev/null +++ b/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; +})"; + +} diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f11a64fad9..dc1b8e28ba 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 + +static const GLfloat kViewportAspectRatio = + (static_cast(VideoCore::kScreenTopHeight) + VideoCore::kScreenBottomHeight) / VideoCore::kScreenTopWidth; + +// Fullscreen quad dimensions +static const GLfloat kTopScreenWidthNormalized = 2; +static const GLfloat kTopScreenHeightNormalized = kTopScreenWidthNormalized * (static_cast(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth); +static const GLfloat kBottomScreenWidthNormalized = kTopScreenWidthNormalized * (static_cast(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth); +static const GLfloat kBottomScreenHeightNormalized = kBottomScreenWidthNormalized * (static_cast(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 // -------------------------- diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 30f4febe05..b21092f07f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -11,26 +11,25 @@ #include "video_core/renderer_base.h" +#include 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& 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]; }; diff --git a/src/video_core/video_core.vcxproj b/src/video_core/video_core.vcxproj index 4e129fbe7d..885567b6d9 100644 --- a/src/video_core/video_core.vcxproj +++ b/src/video_core/video_core.vcxproj @@ -1,4 +1,4 @@ - + @@ -21,6 +21,7 @@ + @@ -43,6 +44,8 @@ + + diff --git a/src/video_core/video_core.vcxproj.filters b/src/video_core/video_core.vcxproj.filters index 90541aca0f..ee6d8e8b43 100644 --- a/src/video_core/video_core.vcxproj.filters +++ b/src/video_core/video_core.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -12,6 +12,9 @@ renderer_opengl + + renderer_opengl + @@ -35,7 +38,15 @@ - + + renderer_opengl + + + renderer_opengl + + + renderer_opengl + debug_utils @@ -43,4 +54,4 @@ - \ No newline at end of file +