From c02cbc57e8df1cdf56ce8cc468268f564a0c3633 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 27 Sep 2019 17:40:26 +1000 Subject: [PATCH] GPU: Properly handle semitransparent pixels --- src/common/gl_program.cpp | 72 +++++++++++++++++++++++++++++++++++++++ src/common/gl_program.h | 9 +++++ src/pse/gpu_hw.cpp | 33 ++++++++++++++---- src/pse/gpu_hw.h | 3 +- src/pse/gpu_hw_opengl.cpp | 63 ++++++++++++++++++---------------- src/pse/gpu_hw_opengl.h | 4 +-- 6 files changed, 144 insertions(+), 40 deletions(-) diff --git a/src/common/gl_program.cpp b/src/common/gl_program.cpp index 6a94afdde..c5bcd5d52 100644 --- a/src/common/gl_program.cpp +++ b/src/common/gl_program.cpp @@ -263,4 +263,76 @@ void Program::Uniform4f(u32 index, float x, float y, float z, float w) const glUniform4f(location, x, y, z, w); } +void Program::Uniform2uiv(u32 index, const u32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform2uiv(location, 1, v); +} + +void Program::Uniform3uiv(u32 index, const u32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform3uiv(location, 1, v); +} + +void Program::Uniform4uiv(u32 index, const u32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform4uiv(location, 1, v); +} + +void Program::Uniform2iv(u32 index, const s32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform2iv(location, 1, v); +} + +void Program::Uniform3iv(u32 index, const s32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform3iv(location, 1, v); +} + +void Program::Uniform4iv(u32 index, const s32* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform4iv(location, 1, v); +} + +void Program::Uniform2fv(u32 index, const float* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform2fv(location, 1, v); +} + +void Program::Uniform3fv(u32 index, const float* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform3fv(location, 1, v); +} + +void Program::Uniform4fv(u32 index, const float* v) const +{ + Assert(index < m_uniform_locations.size()); + const int location = m_uniform_locations[index]; + if (location >= 0) + glUniform4fv(location, 1, v); +} + } // namespace GL \ No newline at end of file diff --git a/src/common/gl_program.h b/src/common/gl_program.h index 2f7cc3e5d..c8c1cc321 100644 --- a/src/common/gl_program.h +++ b/src/common/gl_program.h @@ -40,6 +40,15 @@ public: void Uniform2f(u32 index, float x, float y) const; void Uniform3f(u32 index, float x, float y, float z) const; void Uniform4f(u32 index, float x, float y, float z, float w) const; + void Uniform2uiv(u32 index, const u32* v) const; + void Uniform3uiv(u32 index, const u32* v) const; + void Uniform4uiv(u32 index, const u32* v) const; + void Uniform2iv(u32 index, const s32* v) const; + void Uniform3iv(u32 index, const s32* v) const; + void Uniform4iv(u32 index, const s32* v) const; + void Uniform2fv(u32 index, const float* v) const; + void Uniform3fv(u32 index, const float* v) const; + void Uniform4fv(u32 index, const float* v) const; private: GLuint m_program_id = 0; diff --git a/src/pse/gpu_hw.cpp b/src/pse/gpu_hw.cpp index a029914b6..f9bf09744 100644 --- a/src/pse/gpu_hw.cpp +++ b/src/pse/gpu_hw.cpp @@ -176,7 +176,7 @@ in ivec2 a_pos; in vec4 a_col0; in vec2 a_tex0; -out vec4 v_col0; +out vec3 v_col0; #if TEXTURED out vec2 v_tex0; #endif @@ -190,7 +190,7 @@ void main() float pos_y = (float(a_pos.y + u_pos_offset.y) / -256.0) + 1.0; gl_Position = vec4(pos_x, pos_y, 0.0, 1.0); - v_col0 = a_col0; + v_col0 = a_col0.rgb; #if TEXTURED v_tex0 = a_tex0; #endif @@ -200,7 +200,8 @@ void main() return ss.str(); } -std::string GPU_HW::GenerateFragmentShader(bool textured, bool blending, TextureColorMode texture_color_mode) +std::string GPU_HW::GenerateFragmentShader(bool textured, bool blending, bool transparent, + TextureColorMode texture_color_mode) { std::stringstream ss; GenerateShaderHeader(ss); @@ -213,7 +214,8 @@ std::string GPU_HW::GenerateFragmentShader(bool textured, bool blending, Texture DefineMacro(ss, "PALETTE_8_BIT", textured && texture_color_mode == GPU::TextureColorMode::Palette8Bit); ss << R"( -in vec4 v_col0; +in vec3 v_col0; +uniform vec2 u_transparent_alpha; #if TEXTURED in vec2 v_tex0; uniform sampler2D samp0; @@ -272,13 +274,30 @@ void main() if (texcol == vec4(0.0, 0.0, 0.0, 0.0)) discard; + vec3 color; #if BLENDING - o_col0 = vec4((ivec4(v_col0 * 255.0) * ivec4(texcol * 255.0)) >> 7) / 255.0; + color = vec3((ivec3(v_col0 * 255.0) * ivec3(texcol.rgb * 255.0)) >> 7) / 255.0; #else - o_col0 = texcol; + color = texcol.rgb; + #endif + + #if TRANSPARENT + // Apply semitransparency. If not a semitransparent texel, destination alpha is ignored. + if (texcol.a != 0) + o_col0 = vec4(color * u_transparent_alpha.x, u_transparent_alpha.y); + else + o_col0 = vec4(color, 0.0); + #else + // Mask bit from texture. + o_col0 = vec4(color, texcol.a); #endif #else - o_col0 = v_col0; + #if TRANSPARENT + o_col0 = vec4(v_col0 * u_transparent_alpha.x, u_transparent_alpha.y); + #else + // Mask bit is cleared for untextured polygons. + o_col0 = vec4(v_col0, 0.0); + #endif #endif } )"; diff --git a/src/pse/gpu_hw.h b/src/pse/gpu_hw.h index 54a8d52f7..44ce1d8ce 100644 --- a/src/pse/gpu_hw.h +++ b/src/pse/gpu_hw.h @@ -18,6 +18,7 @@ protected: u32 color; u16 texcoord; u16 padding; + u32 texpage; static constexpr std::tuple DecodeTexcoord(u16 texcoord) { @@ -74,7 +75,7 @@ protected: void CalcScissorRect(int* left, int* top, int* right, int* bottom); std::string GenerateVertexShader(bool textured); - std::string GenerateFragmentShader(bool textured, bool blending, TextureColorMode texture_color_mode); + std::string GenerateFragmentShader(bool textured, bool blending, bool transparent, TextureColorMode texture_color_mode); std::string GenerateScreenQuadVertexShader(); std::string GenerateFillFragmentShader(); diff --git a/src/pse/gpu_hw_opengl.cpp b/src/pse/gpu_hw_opengl.cpp index 0e40d0557..f077a7c22 100644 --- a/src/pse/gpu_hw_opengl.cpp +++ b/src/pse/gpu_hw_opengl.cpp @@ -143,13 +143,17 @@ bool GPU_HW_OpenGL::CompilePrograms() { for (u32 blending = 0; blending < 2; blending++) { - for (u32 format = 0; format < 3; format++) + for (u32 transparent = 0; transparent < 2; transparent++) { - // TODO: eliminate duplicate shaders here - if (!CompileProgram(m_render_programs[textured][blending][format], ConvertToBoolUnchecked(textured), - ConvertToBoolUnchecked(blending), static_cast(format))) + for (u32 format = 0; format < 3; format++) { - return false; + // TODO: eliminate duplicate shaders here + if (!CompileProgram(m_render_programs[textured][blending][transparent][format], + ConvertToBoolUnchecked(textured), ConvertToBoolUnchecked(blending), + ConvertToBoolUnchecked(transparent), static_cast(format))) + { + return false; + } } } } @@ -158,10 +162,11 @@ bool GPU_HW_OpenGL::CompilePrograms() return true; } -bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool textured, bool blending, TextureColorMode texture_color_mode) +bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool textured, bool blending, bool transparent, + TextureColorMode texture_color_mode) { const std::string vs = GenerateVertexShader(textured); - const std::string fs = GenerateFragmentShader(textured, blending, texture_color_mode); + const std::string fs = GenerateFragmentShader(textured, blending, transparent, texture_color_mode); if (!prog.Compile(vs.c_str(), fs.c_str())) return false; @@ -177,14 +182,16 @@ bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool textured, bool blendi prog.Bind(); prog.RegisterUniform("u_pos_offset"); + prog.RegisterUniform("u_transparent_alpha"); prog.Uniform2i(0, 0, 0); + prog.Uniform2f(1, 1.0f, 0.0f); if (textured) { prog.RegisterUniform("samp0"); prog.RegisterUniform("u_texture_page_base"); prog.RegisterUniform("u_texture_palette_base"); - prog.Uniform1i(1, 0); + prog.Uniform1i(2, 0); } return true; @@ -194,16 +201,26 @@ void GPU_HW_OpenGL::SetProgram() { const GL::Program& prog = m_render_programs[BoolToUInt32(m_batch.texture_enable)][BoolToUInt32(m_batch.texture_blending_enable)] - [static_cast(m_batch.texture_color_mode)]; + [BoolToUInt32(m_batch.transparency_enable)][static_cast(m_batch.texture_color_mode)]; prog.Bind(); prog.Uniform2i(0, m_drawing_offset.x, m_drawing_offset.y); + if (m_batch.transparency_enable) + { + static constexpr float transparent_alpha[4][2] = {{0.5f, 0.5f}, {1.0f, 1.0f}, {1.0f, 1.0f}, {0.25f, 1.0f}}; + prog.Uniform2fv(1, transparent_alpha[static_cast(m_batch.transparency_mode)]); + } + else + { + static constexpr float disabled_alpha[2] = {1.0f, 0.0f}; + prog.Uniform2fv(1, disabled_alpha); + } if (m_batch.texture_enable) { m_vram_read_texture->Bind(); - prog.Uniform2i(2, m_batch.texture_page_x, m_batch.texture_page_y); - prog.Uniform2i(3, m_batch.texture_palette_x, m_batch.texture_palette_y); + prog.Uniform2i(3, m_batch.texture_page_x, m_batch.texture_page_y); + prog.Uniform2i(4, m_batch.texture_palette_x, m_batch.texture_palette_y); } } @@ -228,32 +245,18 @@ void GPU_HW_OpenGL::SetScissor() void GPU_HW_OpenGL::SetBlendState() { - struct BlendVars - { - GLenum src_factor; - GLenum func; - GLenum dst_factor; - float color; - }; - static const std::array blend_vars = {{ - {GL_CONSTANT_COLOR, GL_FUNC_ADD, GL_CONSTANT_COLOR, 0.5f}, // B/2 + F/2 - {GL_ONE, GL_FUNC_ADD, GL_ONE, -1.0f}, // B + F - {GL_ONE, GL_FUNC_REVERSE_SUBTRACT, GL_ONE, -1.0f}, // B - F - {GL_CONSTANT_COLOR, GL_FUNC_ADD, GL_ONE, 0.25f} // B + F/4 - }}; - if (!m_batch.transparency_enable) { glDisable(GL_BLEND); return; } - const BlendVars& vars = blend_vars[static_cast(m_batch.transparency_mode)]; glEnable(GL_BLEND); - glBlendFuncSeparate(vars.src_factor, vars.dst_factor, GL_ONE, GL_ZERO); - glBlendEquationSeparate(vars.func, GL_FUNC_ADD); - if (vars.color >= 0.0f) - glBlendColor(vars.color, vars.color, vars.color, 1.0f); + glBlendEquationSeparate(m_batch.transparency_mode == GPU::TransparencyMode::BackgroundMinusForeground ? + GL_FUNC_REVERSE_SUBTRACT : + GL_FUNC_ADD, + GL_FUNC_ADD); + glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_ONE, GL_ZERO); } void GPU_HW_OpenGL::UpdateDisplay() diff --git a/src/pse/gpu_hw_opengl.h b/src/pse/gpu_hw_opengl.h index c7b2e14ff..cc2776136 100644 --- a/src/pse/gpu_hw_opengl.h +++ b/src/pse/gpu_hw_opengl.h @@ -44,7 +44,7 @@ private: void CreateVertexBuffer(); bool CompilePrograms(); - bool CompileProgram(GL::Program& prog, bool textured, bool blending, TextureColorMode texture_color_mode); + bool CompileProgram(GL::Program& prog, bool textured, bool blending, bool transparent, TextureColorMode texture_color_mode); void SetProgram(); void SetViewport(); @@ -62,7 +62,7 @@ private: GLuint m_vao_id = 0; GLuint m_attributeless_vao_id = 0; - std::array, 2>, 2> m_render_programs; + std::array, 2>, 2>, 2> m_render_programs; std::array m_texture_page_programs; GLStats m_stats = {};