Merge pull request #1470 from FernandoS27/alpha_testing

Implemented Alpha Test using Shader Emulation
This commit is contained in:
bunnei 2018-10-23 10:21:30 -04:00 committed by GitHub
commit 75d807788c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 20 deletions

View file

@ -643,8 +643,10 @@ public:
u32 d3d_cull_mode; u32 d3d_cull_mode;
ComparisonOp depth_test_func; ComparisonOp depth_test_func;
float alpha_test_ref;
ComparisonOp alpha_test_func;
INSERT_PADDING_WORDS(0xB); INSERT_PADDING_WORDS(0x9);
struct { struct {
u32 separate_alpha; u32 separate_alpha;

View file

@ -570,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
SyncBlendState(); SyncBlendState();
SyncLogicOpState(); SyncLogicOpState();
SyncCullMode(); SyncCullMode();
SyncAlphaTest();
SyncScissorTest(); SyncScissorTest();
// Alpha Testing is synced on shaders.
SyncTransformFeedback(); SyncTransformFeedback();
SyncPointState(); SyncPointState();
CheckAlphaTests();
// TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync framebuffer_scale uniform here
// TODO(bunnei): Sync scissorbox uniform(s) here // TODO(bunnei): Sync scissorbox uniform(s) here
@ -1007,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
} }
void RasterizerOpenGL::SyncAlphaTest() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
// implemented with a test+discard in fragment shaders.
if (regs.alpha_test_enabled != 0) {
LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
UNREACHABLE();
}
}
void RasterizerOpenGL::SyncScissorTest() { void RasterizerOpenGL::SyncScissorTest() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@ -1052,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
state.point.size = regs.point_size == 0 ? 1 : regs.point_size; state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
} }
void RasterizerOpenGL::CheckAlphaTests() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
LOG_CRITICAL(
Render_OpenGL,
"Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
UNREACHABLE();
}
}
} // namespace OpenGL } // namespace OpenGL

View file

@ -162,9 +162,6 @@ private:
/// Syncs the LogicOp state to match the guest state /// Syncs the LogicOp state to match the guest state
void SyncLogicOpState(); void SyncLogicOpState();
/// Syncs the alpha test state to match the guest state
void SyncAlphaTest();
/// Syncs the scissor test state to match the guest state /// Syncs the scissor test state to match the guest state
void SyncScissorTest(); void SyncScissorTest();
@ -174,6 +171,9 @@ private:
/// Syncs the point state to match the guest state /// Syncs the point state to match the guest state
void SyncPointState(); void SyncPointState();
/// Check asserts for alpha testing.
void CheckAlphaTests();
bool has_ARB_direct_state_access = false; bool has_ARB_direct_state_access = false;
bool has_ARB_multi_bind = false; bool has_ARB_multi_bind = false;
bool has_ARB_separate_shader_objects = false; bool has_ARB_separate_shader_objects = false;

View file

@ -1266,9 +1266,29 @@ private:
ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
shader.AddLine("if (alpha_test[0] != 0) {");
++shader.scope;
// We start on the register containing the alpha value in the first RT.
u32 current_reg = 3;
for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
++render_target) {
// TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
// multiple render targets are used.
if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
regs.GetRegisterAsFloat(current_reg)));
current_reg += 4;
}
}
--shader.scope;
shader.AddLine('}');
// Write the color outputs using the data in the shader registers, disabled // Write the color outputs using the data in the shader registers, disabled
// rendertargets/components are skipped in the register assignment. // rendertargets/components are skipped in the register assignment.
u32 current_reg = 0; current_reg = 0;
for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
++render_target) { ++render_target) {
// TODO(Subv): Figure out how dual-source blending is configured in the Switch. // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@ -3516,7 +3536,7 @@ private:
// Declarations // Declarations
std::set<std::string> declr_predicates; std::set<std::string> declr_predicates;
}; // namespace Decompiler }; // namespace OpenGL::GLShader::Decompiler
std::string GetCommonDeclarations() { std::string GetCommonDeclarations() {
return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",

View file

@ -29,6 +29,7 @@ layout(std140) uniform vs_config {
vec4 viewport_flip; vec4 viewport_flip;
uvec4 instance_id; uvec4 instance_id;
uvec4 flip_stage; uvec4 flip_stage;
uvec4 alpha_test;
}; };
)"; )";
@ -105,6 +106,7 @@ layout (std140) uniform gs_config {
vec4 viewport_flip; vec4 viewport_flip;
uvec4 instance_id; uvec4 instance_id;
uvec4 flip_stage; uvec4 flip_stage;
uvec4 alpha_test;
}; };
void main() { void main() {
@ -142,8 +144,33 @@ layout (std140) uniform fs_config {
vec4 viewport_flip; vec4 viewport_flip;
uvec4 instance_id; uvec4 instance_id;
uvec4 flip_stage; uvec4 flip_stage;
uvec4 alpha_test;
}; };
bool AlphaFunc(in float value) {
float ref = uintBitsToFloat(alpha_test[2]);
switch (alpha_test[1]) {
case 1:
return false;
case 2:
return value < ref;
case 3:
return value == ref;
case 4:
return value <= ref;
case 5:
return value > ref;
case 6:
return value != ref;
case 7:
return value >= ref;
case 8:
return true;
default:
return false;
}
}
void main() { void main() {
exec_fragment(); exec_fragment();
} }
@ -152,4 +179,4 @@ void main() {
out += program.first; out += program.first;
return {out, program.second}; return {out, program.second};
} }
} // namespace OpenGL::GLShader } // namespace OpenGL::GLShader

View file

@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
u32 func = static_cast<u32>(regs.alpha_test_func);
// Normalize the gl variants of opCompare to be the same as the normal variants
u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
if (func >= op_gl_variant_base) {
func = func - op_gl_variant_base + 1U;
}
alpha_test.enabled = regs.alpha_test_enabled;
alpha_test.func = func;
alpha_test.ref = regs.alpha_test_ref;
// We only assign the instance to the first component of the vector, the rest is just padding. // We only assign the instance to the first component of the vector, the rest is just padding.
instance_id[0] = state.current_instance; instance_id[0] = state.current_instance;

View file

@ -22,8 +22,14 @@ struct MaxwellUniformData {
alignas(16) GLvec4 viewport_flip; alignas(16) GLvec4 viewport_flip;
alignas(16) GLuvec4 instance_id; alignas(16) GLuvec4 instance_id;
alignas(16) GLuvec4 flip_stage; alignas(16) GLuvec4 flip_stage;
struct alignas(16) {
GLuint enabled;
GLuint func;
GLfloat ref;
GLuint padding;
} alpha_test;
}; };
static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
static_assert(sizeof(MaxwellUniformData) < 16384, static_assert(sizeof(MaxwellUniformData) < 16384,
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");