GPU: Properly handle semitransparent pixels

This commit is contained in:
Connor McLaughlin 2019-09-27 17:40:26 +10:00
parent 40d2497087
commit c02cbc57e8
6 changed files with 144 additions and 40 deletions

View File

@ -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

View File

@ -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;

View File

@ -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
}
)";

View File

@ -18,6 +18,7 @@ protected:
u32 color;
u16 texcoord;
u16 padding;
u32 texpage;
static constexpr std::tuple<u8, u8> 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();

View File

@ -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<TextureColorMode>(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<TextureColorMode>(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<u32>(m_batch.texture_color_mode)];
[BoolToUInt32(m_batch.transparency_enable)][static_cast<u32>(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<u32>(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<BlendVars, 4> 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<u8>(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()

View File

@ -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<std::array<std::array<GL::Program, 3>, 2>, 2> m_render_programs;
std::array<std::array<std::array<std::array<GL::Program, 3>, 2>, 2>, 2> m_render_programs;
std::array<GL::Program, 3> m_texture_page_programs;
GLStats m_stats = {};