diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h index 386f87b9e..034a40f82 100644 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ b/src/video_core/renderer_opengl/gl_shaders.h @@ -13,9 +13,14 @@ in vec2 vert_position; in vec2 vert_tex_coord; out vec2 frag_tex_coord; +// This is a truncated 3x3 matrix for 2D transformations, with the last row +// assumed to implicitly be [0, 0, 1]. uniform mat3x2 proj_matrix; void main() { + // Multiply vector by the rotscale part of the matrix and then manually translate by the last + // column. This is equivalent to if proj_matrix was a full 3x3 matrix and the vector were + // expanded to `vec3(vert_position.xy, 1.0)`. gl_Position = vec4(mat2(proj_matrix) * vert_position + proj_matrix[2], 0.0, 1.0); frag_tex_coord = vert_tex_coord; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 9269ab916..c676da37f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -13,9 +13,18 @@ #include "video_core/renderer_opengl/gl_shaders.h" #include -#include -struct GLVertex { +/** + * Vertex structure that the drawn screen rectangles are composed of. + */ +struct ScreenRectVertex { + ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) { + position[0] = x; + position[1] = y; + tex_coord[0] = u; + tex_coord[1] = v; + } + GLfloat position[2]; GLfloat tex_coord[2]; }; @@ -24,9 +33,14 @@ struct GLVertex { * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left * corner and (width, height) on the lower-bottom. */ -static void write_orthographic_matrix(std::array& m, float width, float height) { +static std::array MakeOrthographicMatrix(const float width, const float height) { + std::array m; + m[0] = 2.f / width; m[2] = 0.f; m[4] = -1.f; m[1] = 0.f; m[3] = -2.f / height; m[5] = 1.f; + // Last row is implicitly [0, 0, 1]. + + return m; } /// RendererOpenGL constructor @@ -56,7 +70,7 @@ void RendererOpenGL::SwapBuffers() { } /** - * Resolves a framebuffer from emulated video memory to a OpenGL texture. + * Loads framebuffer from emulated video memory onto an OpenGL texture. */ void RendererOpenGL::RenderXFB(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer) { const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( @@ -70,13 +84,14 @@ void RendererOpenGL::RenderXFB(TextureInfo& texture, const GPU::Regs::Framebuffe const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); // TODO: Handle other pixel formats - assert(framebuffer.color_format == GPU::Regs::PixelFormat::RGB8); + _dbg_assert_msg_(RENDER, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, "Currently unsupported 3DS pixel format."); size_t pixel_stride = framebuffer.stride / 3; - // OpenGL only supports specifying a stride in units of pixels, unfortunately - assert(pixel_stride * 3 == framebuffer.stride); - // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - assert(pixel_stride % 4 == 0); + // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately + _dbg_assert_(RENDER, pixel_stride * 3 == framebuffer.stride); + // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default + // only allows rows to have a memory alignement of 4. + _dbg_assert_(RENDER, pixel_stride % 4 == 0); glBindTexture(GL_TEXTURE_2D, texture.handle); glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); @@ -84,9 +99,9 @@ void RendererOpenGL::RenderXFB(TextureInfo& texture, const GPU::Regs::Framebuffe // TODO: Testing needs to be done on hardware to see if scaling is really // what happens if you change the framebuffer dimensions so that they // differ from the LCD resolution. - // TODO: Prevent memory overread + // TODO: Prevent overread from memory buffer if (texture.width != framebuffer.width || texture.height != framebuffer.height) { - // If the framebuffer size was changed, re-allocated the texture. The + // If the framebuffer size was changed, re-allocate the texture. The // expectation is that this won't happen very often. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, framebuffer.width, framebuffer.height, 0, GL_BGR, GL_UNSIGNED_BYTE, framebuffer_data); @@ -109,9 +124,8 @@ void RendererOpenGL::RenderXFB(TextureInfo& texture, const GPU::Regs::Framebuffe void RendererOpenGL::InitOpenGLObjects() { glClearColor(1.0f, 1.0f, 1.0f, 0.0f); glDisable(GL_DEPTH_TEST); - //glDisable(GL_CULL_FACE); - // Links shaders and get variable locations + // Link shaders and get variable locations program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); uniform_proj_matrix = glGetUniformLocation(program_id, "proj_matrix"); uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); @@ -127,9 +141,9 @@ void RendererOpenGL::InitOpenGLObjects() { // Attach vertex data to VAO glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle); - glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertex) * 4, nullptr, GL_STREAM_DRAW); - glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), (GLvoid*)offsetof(GLVertex, position)); - glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), (GLvoid*)offsetof(GLVertex, tex_coord)); + glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); + glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position)); + glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord)); glEnableVertexAttribArray(attrib_position); glEnableVertexAttribArray(attrib_tex_coord); @@ -154,12 +168,12 @@ void RendererOpenGL::InitOpenGLObjects() { * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. */ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h) { - std::array vertices = {{ - {{x, y}, {1.f, 0.f}}, - {{x+w, y}, {1.f, 1.f}}, - {{x, y+h}, {0.f, 0.f}}, - {{x+w, y+h}, {0.f, 1.f}}, - }}; + std::array vertices = { + ScreenRectVertex(x, y, 1.f, 0.f), + ScreenRectVertex(x+w, y, 1.f, 1.f), + ScreenRectVertex(x, y+h, 0.f, 0.f), + ScreenRectVertex(x+w, y+h, 0.f, 1.f), + }; glBindTexture(GL_TEXTURE_2D, texture.handle); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle); @@ -177,8 +191,7 @@ void RendererOpenGL::DrawScreens() { glUseProgram(program_id); // Set projection matrix - std::array ortho_matrix; - write_orthographic_matrix(ortho_matrix, (float)resolution_width, (float)resolution_height); + std::array ortho_matrix = MakeOrthographicMatrix((float)resolution_width, (float)resolution_height); glUniformMatrix3x2fv(uniform_proj_matrix, 1, GL_FALSE, ortho_matrix.data()); // Bind texture in Texture Unit 0 diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index fc0b9f95e..667329a3a 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -17,9 +17,10 @@ namespace VideoCore { // 3DS Video Constants // ------------------- -// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because -// of that the framebuffers in video memory are stored in column-major order -// and rendered sideways, causing the widths and heights here to be switched. +// NOTE: The LCDs actually rotate the image 90 degrees when displaying. Because of that the +// framebuffers in video memory are stored in column-major order and rendered sideways, causing +// the widths and heights of the framebuffers read by the LCD to be switched compared to the +// heights and widths of the screens listed here. static const int kScreenTopWidth = 400; ///< 3DS top screen width static const int kScreenTopHeight = 240; ///< 3DS top screen height static const int kScreenBottomWidth = 320; ///< 3DS bottom screen width