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); 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 } // namespace GL

View file

@ -40,6 +40,15 @@ public:
void Uniform2f(u32 index, float x, float y) const; void Uniform2f(u32 index, float x, float y) const;
void Uniform3f(u32 index, float x, float y, float z) 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 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: private:
GLuint m_program_id = 0; GLuint m_program_id = 0;

View file

@ -176,7 +176,7 @@ in ivec2 a_pos;
in vec4 a_col0; in vec4 a_col0;
in vec2 a_tex0; in vec2 a_tex0;
out vec4 v_col0; out vec3 v_col0;
#if TEXTURED #if TEXTURED
out vec2 v_tex0; out vec2 v_tex0;
#endif #endif
@ -190,7 +190,7 @@ void main()
float pos_y = (float(a_pos.y + u_pos_offset.y) / -256.0) + 1.0; 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); gl_Position = vec4(pos_x, pos_y, 0.0, 1.0);
v_col0 = a_col0; v_col0 = a_col0.rgb;
#if TEXTURED #if TEXTURED
v_tex0 = a_tex0; v_tex0 = a_tex0;
#endif #endif
@ -200,7 +200,8 @@ void main()
return ss.str(); 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; std::stringstream ss;
GenerateShaderHeader(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); DefineMacro(ss, "PALETTE_8_BIT", textured && texture_color_mode == GPU::TextureColorMode::Palette8Bit);
ss << R"( ss << R"(
in vec4 v_col0; in vec3 v_col0;
uniform vec2 u_transparent_alpha;
#if TEXTURED #if TEXTURED
in vec2 v_tex0; in vec2 v_tex0;
uniform sampler2D samp0; uniform sampler2D samp0;
@ -272,13 +274,30 @@ void main()
if (texcol == vec4(0.0, 0.0, 0.0, 0.0)) if (texcol == vec4(0.0, 0.0, 0.0, 0.0))
discard; discard;
vec3 color;
#if BLENDING #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 #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 #endif
#else #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 #endif
} }
)"; )";

View file

@ -18,6 +18,7 @@ protected:
u32 color; u32 color;
u16 texcoord; u16 texcoord;
u16 padding; u16 padding;
u32 texpage;
static constexpr std::tuple<u8, u8> DecodeTexcoord(u16 texcoord) static constexpr std::tuple<u8, u8> DecodeTexcoord(u16 texcoord)
{ {
@ -74,7 +75,7 @@ protected:
void CalcScissorRect(int* left, int* top, int* right, int* bottom); void CalcScissorRect(int* left, int* top, int* right, int* bottom);
std::string GenerateVertexShader(bool textured); 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 GenerateScreenQuadVertexShader();
std::string GenerateFillFragmentShader(); std::string GenerateFillFragmentShader();

View file

@ -143,13 +143,17 @@ bool GPU_HW_OpenGL::CompilePrograms()
{ {
for (u32 blending = 0; blending < 2; blending++) 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 for (u32 format = 0; format < 3; format++)
if (!CompileProgram(m_render_programs[textured][blending][format], ConvertToBoolUnchecked(textured),
ConvertToBoolUnchecked(blending), static_cast<TextureColorMode>(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; 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 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())) if (!prog.Compile(vs.c_str(), fs.c_str()))
return false; return false;
@ -177,14 +182,16 @@ bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool textured, bool blendi
prog.Bind(); prog.Bind();
prog.RegisterUniform("u_pos_offset"); prog.RegisterUniform("u_pos_offset");
prog.RegisterUniform("u_transparent_alpha");
prog.Uniform2i(0, 0, 0); prog.Uniform2i(0, 0, 0);
prog.Uniform2f(1, 1.0f, 0.0f);
if (textured) if (textured)
{ {
prog.RegisterUniform("samp0"); prog.RegisterUniform("samp0");
prog.RegisterUniform("u_texture_page_base"); prog.RegisterUniform("u_texture_page_base");
prog.RegisterUniform("u_texture_palette_base"); prog.RegisterUniform("u_texture_palette_base");
prog.Uniform1i(1, 0); prog.Uniform1i(2, 0);
} }
return true; return true;
@ -194,16 +201,26 @@ void GPU_HW_OpenGL::SetProgram()
{ {
const GL::Program& prog = const GL::Program& prog =
m_render_programs[BoolToUInt32(m_batch.texture_enable)][BoolToUInt32(m_batch.texture_blending_enable)] 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.Bind();
prog.Uniform2i(0, m_drawing_offset.x, m_drawing_offset.y); 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) if (m_batch.texture_enable)
{ {
m_vram_read_texture->Bind(); m_vram_read_texture->Bind();
prog.Uniform2i(2, m_batch.texture_page_x, m_batch.texture_page_y); prog.Uniform2i(3, 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(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() 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) if (!m_batch.transparency_enable)
{ {
glDisable(GL_BLEND); glDisable(GL_BLEND);
return; return;
} }
const BlendVars& vars = blend_vars[static_cast<u8>(m_batch.transparency_mode)];
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(vars.src_factor, vars.dst_factor, GL_ONE, GL_ZERO); glBlendEquationSeparate(m_batch.transparency_mode == GPU::TransparencyMode::BackgroundMinusForeground ?
glBlendEquationSeparate(vars.func, GL_FUNC_ADD); GL_FUNC_REVERSE_SUBTRACT :
if (vars.color >= 0.0f) GL_FUNC_ADD,
glBlendColor(vars.color, vars.color, vars.color, 1.0f); GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_ONE, GL_ZERO);
} }
void GPU_HW_OpenGL::UpdateDisplay() void GPU_HW_OpenGL::UpdateDisplay()

View file

@ -44,7 +44,7 @@ private:
void CreateVertexBuffer(); void CreateVertexBuffer();
bool CompilePrograms(); 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 SetProgram();
void SetViewport(); void SetViewport();
@ -62,7 +62,7 @@ private:
GLuint m_vao_id = 0; GLuint m_vao_id = 0;
GLuint m_attributeless_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; std::array<GL::Program, 3> m_texture_page_programs;
GLStats m_stats = {}; GLStats m_stats = {};