From f9be06b15f08cb559580e1d19b43158640a37d67 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 2 Dec 2015 13:23:51 -0500 Subject: [PATCH 1/3] PICA: Implement scissor test --- src/video_core/pica.h | 32 ++++++++++++++++++- src/video_core/rasterizer.cpp | 22 ++++++++++++- .../renderer_opengl/gl_rasterizer.cpp | 26 +++++++++++++++ .../renderer_opengl/gl_rasterizer.h | 12 ++++++- .../renderer_opengl/gl_shader_gen.cpp | 16 ++++++++++ 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 09702d46ad..065a3fd0c1 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -115,7 +115,36 @@ struct Regs { BitField<24, 5, Semantic> map_w; } vs_output_attributes[7]; - INSERT_PADDING_WORDS(0x11); + INSERT_PADDING_WORDS(0xe); + + enum class ScissorMode : u32 { + Disabled = 0, + Exclude = 1, // Exclude pixels inside the scissor box + + Include = 3 // Exclude pixels outside the scissor box + }; + + struct { + BitField<0, 2, ScissorMode> mode; + + union { + BitField< 0, 16, u32> right; + BitField<16, 16, u32> bottom; + }; + + union { + BitField< 0, 16, u32> left_minus_1; + BitField<16, 16, u32> top_minus_1; + }; + + u32 GetTop() const { + return top_minus_1 + 1; + } + + u32 GetLeft() const { + return left_minus_1 + 1; + } + } scissor_test; union { BitField< 0, 10, s32> x; @@ -1328,6 +1357,7 @@ ASSERT_REG_POSITION(viewport_depth_range, 0x4d); ASSERT_REG_POSITION(viewport_depth_near_plane, 0x4e); ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); +ASSERT_REG_POSITION(scissor_test, 0x65); ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(depthmap_enable, 0x6D); ASSERT_REG_POSITION(texture0_enable, 0x80); diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index a84170094c..514d642088 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -338,12 +338,25 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, return; } - // TODO: Proper scissor rect test! u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x}); u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); + // Convert the scissor box coordinates to 12.4 fixed point + u16 scissor_left = (u16)(regs.scissor_test.GetLeft() << 4); + u16 scissor_top = (u16)(regs.scissor_test.GetTop() << 4); + u16 scissor_right = (u16)(regs.scissor_test.right << 4); + u16 scissor_bottom = (u16)(regs.scissor_test.bottom << 4); + + if (regs.scissor_test.mode == Regs::ScissorMode::Include) { + // Calculate the new bounds + min_x = std::max(min_x, scissor_right); + min_y = std::max(min_y, scissor_bottom); + max_x = std::min(max_x, scissor_left); + max_y = std::min(max_y, scissor_top); + } + min_x &= Fix12P4::IntMask(); min_y &= Fix12P4::IntMask(); max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask()); @@ -383,6 +396,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, for (u16 y = min_y + 8; y < max_y; y += 0x10) { for (u16 x = min_x + 8; x < max_x; x += 0x10) { + // Do not process the pixel if it's inside the scissor box and the scissor mode is set to Exclude + if (regs.scissor_test.mode == Regs::ScissorMode::Exclude && + x >= scissor_right && x <= scissor_left && + y >= scissor_bottom && y <= scissor_top) { + continue; + } + // Calculate the barycentric coordinates w0, w1 and w2 int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y}); int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y}); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 328a4f66be..14ee97d57f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -353,6 +353,15 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncColorWriteMask(); break; + // Scissor test + case PICA_REG_INDEX(scissor_test.mode): + shader_dirty = true; + break; + case PICA_REG_INDEX(scissor_test.right): + case PICA_REG_INDEX(scissor_test.left_minus_1): + SyncScissorTest(); + break; + // Logic op case PICA_REG_INDEX(output_merger.logic_op): SyncLogicOp(); @@ -1002,6 +1011,7 @@ void RasterizerOpenGL::SetShader() { SyncDepthOffset(); SyncAlphaTest(); SyncCombinerColor(); + SyncScissorTest(); auto& tev_stages = Pica::g_state.regs.GetTevStages(); for (int index = 0; index < tev_stages.size(); ++index) SyncTevConstColor(index, tev_stages[index]); @@ -1166,6 +1176,22 @@ void RasterizerOpenGL::SyncDepthTest() { PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; } +void RasterizerOpenGL::SyncScissorTest() { + const auto& regs = Pica::g_state.regs; + + if (uniform_block_data.data.scissor_right != regs.scissor_test.right || + uniform_block_data.data.scissor_bottom != regs.scissor_test.bottom || + uniform_block_data.data.scissor_left != regs.scissor_test.GetLeft() || + uniform_block_data.data.scissor_top != regs.scissor_test.GetTop()) { + + uniform_block_data.data.scissor_right = regs.scissor_test.right; + uniform_block_data.data.scissor_bottom = regs.scissor_test.bottom; + uniform_block_data.data.scissor_left = regs.scissor_test.GetLeft(); + uniform_block_data.data.scissor_top = regs.scissor_test.GetTop(); + uniform_block_data.dirty = true; + } +} + void RasterizerOpenGL::SyncCombinerColor() { auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 42482df4b6..193c102910 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -56,6 +56,8 @@ union PicaShaderConfig { const auto& regs = Pica::g_state.regs; + state.scissor_test_mode = regs.scissor_test.mode; + state.depthmap_enable = regs.depthmap_enable; state.alpha_test_func = regs.output_merger.alpha_test.enable ? @@ -172,6 +174,7 @@ union PicaShaderConfig { struct State { Pica::Regs::CompareFunc alpha_test_func; + Pica::Regs::ScissorMode scissor_test_mode; Pica::Regs::TextureConfig::TextureType texture0_type; std::array tev_stages; u8 combiner_buffer_input; @@ -328,6 +331,10 @@ private: GLint alphatest_ref; GLfloat depth_scale; GLfloat depth_offset; + GLint scissor_right; + GLint scissor_bottom; + GLint scissor_left; + GLint scissor_top; alignas(16) GLvec3 fog_color; alignas(16) GLvec3 lighting_global_ambient; LightSrc light_src[8]; @@ -335,7 +342,7 @@ private: alignas(16) GLvec4 tev_combiner_buffer_color; }; - static_assert(sizeof(UniformData) == 0x3A0, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) == 0x3B0, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Sets the OpenGL shader in accordance with the current PICA register state @@ -384,6 +391,9 @@ private: /// Syncs the depth test states to match the PICA register void SyncDepthTest(); + /// Syncs the scissor test state to match the PICA register + void SyncScissorTest(); + /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3bace7f01d..10bb44210b 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -539,6 +539,8 @@ in float texcoord0_w; in vec4 normquat; in vec3 view; +in vec4 gl_FragCoord; + out vec4 color; struct LightSrc { @@ -555,6 +557,10 @@ layout (std140) uniform shader_data { int alphatest_ref; float depth_scale; float depth_offset; + int scissor_right; + int scissor_bottom; + int scissor_left; + int scissor_top; vec3 fog_color; vec3 lighting_global_ambient; LightSrc light_src[NUM_LIGHTS]; @@ -582,6 +588,16 @@ vec4 secondary_fragment_color = vec4(0.0); return out; } + // Append the scissor test + if (state.scissor_test_mode == Regs::ScissorMode::Include || state.scissor_test_mode == Regs::ScissorMode::Exclude) { + out += "if (scissor_left <= scissor_right || scissor_top <= scissor_bottom) discard;\n"; + out += "if ("; + // Negate the condition if we have to keep only the pixels outside the scissor box + if (state.scissor_test_mode == Regs::ScissorMode::Include) + out += "!"; + out += "(gl_FragCoord.x >= scissor_right && gl_FragCoord.x <= scissor_left && gl_FragCoord.y >= scissor_bottom && gl_FragCoord.y <= scissor_top)) discard;\n"; + } + out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; out += "float depth = z_over_w * depth_scale + depth_offset;\n"; if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { From f0b9bc14b64765bd8bc78cb4cfb4a30eb9ab9324 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Sun, 24 Jan 2016 22:59:16 -0800 Subject: [PATCH 2/3] PICA: Scissor fixes and cleanups --- src/video_core/pica.h | 16 +++--------- src/video_core/rasterizer.cpp | 25 ++++++++++--------- .../renderer_opengl/gl_rasterizer.cpp | 20 +++++++-------- .../renderer_opengl/gl_rasterizer.h | 8 +++--- .../renderer_opengl/gl_shader_gen.cpp | 15 +++++------ 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 065a3fd0c1..7099c31a0b 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -128,22 +128,14 @@ struct Regs { BitField<0, 2, ScissorMode> mode; union { - BitField< 0, 16, u32> right; - BitField<16, 16, u32> bottom; + BitField< 0, 16, u32> x1; + BitField<16, 16, u32> y1; }; union { - BitField< 0, 16, u32> left_minus_1; - BitField<16, 16, u32> top_minus_1; + BitField< 0, 16, u32> x2; + BitField<16, 16, u32> y2; }; - - u32 GetTop() const { - return top_minus_1 + 1; - } - - u32 GetLeft() const { - return left_minus_1 + 1; - } } scissor_test; union { diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 514d642088..6f369a00e1 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -344,17 +344,18 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y}); // Convert the scissor box coordinates to 12.4 fixed point - u16 scissor_left = (u16)(regs.scissor_test.GetLeft() << 4); - u16 scissor_top = (u16)(regs.scissor_test.GetTop() << 4); - u16 scissor_right = (u16)(regs.scissor_test.right << 4); - u16 scissor_bottom = (u16)(regs.scissor_test.bottom << 4); + u16 scissor_x1 = (u16)( regs.scissor_test.x1 << 4); + u16 scissor_y1 = (u16)( regs.scissor_test.y1 << 4); + // x2,y2 have +1 added to cover the entire sub-pixel area + u16 scissor_x2 = (u16)((regs.scissor_test.x2 + 1) << 4); + u16 scissor_y2 = (u16)((regs.scissor_test.y2 + 1) << 4); if (regs.scissor_test.mode == Regs::ScissorMode::Include) { // Calculate the new bounds - min_x = std::max(min_x, scissor_right); - min_y = std::max(min_y, scissor_bottom); - max_x = std::min(max_x, scissor_left); - max_y = std::min(max_y, scissor_top); + min_x = std::max(min_x, scissor_x1); + min_y = std::max(min_y, scissor_y1); + max_x = std::min(max_x, scissor_x2); + max_y = std::min(max_y, scissor_y2); } min_x &= Fix12P4::IntMask(); @@ -397,10 +398,10 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, for (u16 x = min_x + 8; x < max_x; x += 0x10) { // Do not process the pixel if it's inside the scissor box and the scissor mode is set to Exclude - if (regs.scissor_test.mode == Regs::ScissorMode::Exclude && - x >= scissor_right && x <= scissor_left && - y >= scissor_bottom && y <= scissor_top) { - continue; + if (regs.scissor_test.mode == Regs::ScissorMode::Exclude) { + if (x >= scissor_x1 && x < scissor_x2 && + y >= scissor_y1 && y < scissor_y2) + continue; } // Calculate the barycentric coordinates w0, w1 and w2 diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 14ee97d57f..ab02aadc9f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -357,8 +357,8 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { case PICA_REG_INDEX(scissor_test.mode): shader_dirty = true; break; - case PICA_REG_INDEX(scissor_test.right): - case PICA_REG_INDEX(scissor_test.left_minus_1): + case PICA_REG_INDEX(scissor_test.x1): // and y1 + case PICA_REG_INDEX(scissor_test.x2): // and y2 SyncScissorTest(); break; @@ -1179,15 +1179,15 @@ void RasterizerOpenGL::SyncDepthTest() { void RasterizerOpenGL::SyncScissorTest() { const auto& regs = Pica::g_state.regs; - if (uniform_block_data.data.scissor_right != regs.scissor_test.right || - uniform_block_data.data.scissor_bottom != regs.scissor_test.bottom || - uniform_block_data.data.scissor_left != regs.scissor_test.GetLeft() || - uniform_block_data.data.scissor_top != regs.scissor_test.GetTop()) { + if (uniform_block_data.data.scissor_x1 != regs.scissor_test.x1 || + uniform_block_data.data.scissor_y1 != regs.scissor_test.y1 || + uniform_block_data.data.scissor_x2 != regs.scissor_test.x2 || + uniform_block_data.data.scissor_y2 != regs.scissor_test.y2) { - uniform_block_data.data.scissor_right = regs.scissor_test.right; - uniform_block_data.data.scissor_bottom = regs.scissor_test.bottom; - uniform_block_data.data.scissor_left = regs.scissor_test.GetLeft(); - uniform_block_data.data.scissor_top = regs.scissor_test.GetTop(); + uniform_block_data.data.scissor_x1 = regs.scissor_test.x1; + uniform_block_data.data.scissor_y1 = regs.scissor_test.y1; + uniform_block_data.data.scissor_x2 = regs.scissor_test.x2; + uniform_block_data.data.scissor_y2 = regs.scissor_test.y2; uniform_block_data.dirty = true; } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 193c102910..653ac9cd96 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -331,10 +331,10 @@ private: GLint alphatest_ref; GLfloat depth_scale; GLfloat depth_offset; - GLint scissor_right; - GLint scissor_bottom; - GLint scissor_left; - GLint scissor_top; + GLint scissor_x1; + GLint scissor_y1; + GLint scissor_x2; + GLint scissor_y2; alignas(16) GLvec3 fog_color; alignas(16) GLvec3 lighting_global_ambient; LightSrc light_src[8]; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 10bb44210b..b2e452afe2 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -557,10 +557,10 @@ layout (std140) uniform shader_data { int alphatest_ref; float depth_scale; float depth_offset; - int scissor_right; - int scissor_bottom; - int scissor_left; - int scissor_top; + int scissor_x1; + int scissor_y1; + int scissor_x2; + int scissor_y2; vec3 fog_color; vec3 lighting_global_ambient; LightSrc light_src[NUM_LIGHTS]; @@ -589,13 +589,14 @@ vec4 secondary_fragment_color = vec4(0.0); } // Append the scissor test - if (state.scissor_test_mode == Regs::ScissorMode::Include || state.scissor_test_mode == Regs::ScissorMode::Exclude) { - out += "if (scissor_left <= scissor_right || scissor_top <= scissor_bottom) discard;\n"; + if (state.scissor_test_mode != Regs::ScissorMode::Disabled) { out += "if ("; // Negate the condition if we have to keep only the pixels outside the scissor box if (state.scissor_test_mode == Regs::ScissorMode::Include) out += "!"; - out += "(gl_FragCoord.x >= scissor_right && gl_FragCoord.x <= scissor_left && gl_FragCoord.y >= scissor_bottom && gl_FragCoord.y <= scissor_top)) discard;\n"; + // x2,y2 have +1 added to cover the entire pixel area + out += "(gl_FragCoord.x >= scissor_x1 && gl_FragCoord.x < scissor_x2 + 1 && " + "gl_FragCoord.y >= scissor_y1 && gl_FragCoord.y < scissor_y2 + 1)) discard;\n"; } out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; From ecf6ecf32537634db15946630d62ac3bdc4fe8c9 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 27 Jun 2016 22:16:04 -0700 Subject: [PATCH 3/3] OpenGL: Add scaled resolution support to scissor --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 8 ++++++++ src/video_core/renderer_opengl/gl_rasterizer.h | 3 ++- src/video_core/renderer_opengl/gl_shader_gen.cpp | 7 +++++-- src/video_core/renderer_opengl/pica_to_gl.h | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ab02aadc9f..f8393c6185 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -196,6 +196,14 @@ void RasterizerOpenGL::DrawTriangles() { (GLint)(rect.bottom + regs.viewport_corner.y * color_surface->res_scale_height), (GLsizei)(viewport_width * color_surface->res_scale_width), (GLsizei)(viewport_height * color_surface->res_scale_height)); + if (uniform_block_data.data.framebuffer_scale[0] != color_surface->res_scale_width || + uniform_block_data.data.framebuffer_scale[1] != color_surface->res_scale_height) { + + uniform_block_data.data.framebuffer_scale[0] = color_surface->res_scale_width; + uniform_block_data.data.framebuffer_scale[1] = color_surface->res_scale_height; + uniform_block_data.dirty = true; + } + // Sync and bind the texture surfaces const auto pica_textures = regs.GetTextures(); for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 653ac9cd96..c5029432b9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -328,6 +328,7 @@ private: // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. // Not following that rule will cause problems on some AMD drivers. struct UniformData { + alignas(8) GLvec2 framebuffer_scale; GLint alphatest_ref; GLfloat depth_scale; GLfloat depth_offset; @@ -342,7 +343,7 @@ private: alignas(16) GLvec4 tev_combiner_buffer_color; }; - static_assert(sizeof(UniformData) == 0x3B0, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) == 0x3C0, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Sets the OpenGL shader in accordance with the current PICA register state diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index b2e452afe2..36513dedc5 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -554,6 +554,7 @@ struct LightSrc { }; layout (std140) uniform shader_data { + vec2 framebuffer_scale; int alphatest_ref; float depth_scale; float depth_offset; @@ -595,8 +596,10 @@ vec4 secondary_fragment_color = vec4(0.0); if (state.scissor_test_mode == Regs::ScissorMode::Include) out += "!"; // x2,y2 have +1 added to cover the entire pixel area - out += "(gl_FragCoord.x >= scissor_x1 && gl_FragCoord.x < scissor_x2 + 1 && " - "gl_FragCoord.y >= scissor_y1 && gl_FragCoord.y < scissor_y2 + 1)) discard;\n"; + out += "(gl_FragCoord.x >= scissor_x1 * framebuffer_scale.x && " + "gl_FragCoord.y >= scissor_y1 * framebuffer_scale.y && " + "gl_FragCoord.x < (scissor_x2 + 1) * framebuffer_scale.x && " + "gl_FragCoord.y < (scissor_y2 + 1) * framebuffer_scale.y)) discard;\n"; } out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 6dc2758c5a..d9b9c9cc2e 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -17,6 +17,7 @@ #include "video_core/pica.h" +using GLvec2 = std::array; using GLvec3 = std::array; using GLvec4 = std::array;