summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/maxwell_compute.cpp6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp17
-rw-r--r--src/video_core/engines/shader_bytecode.h18
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp25
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp189
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp29
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h8
11 files changed, 205 insertions, 114 deletions
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8afd26fe9..bca014a4a 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -13,8 +13,7 @@
#include "video_core/renderer_base.h"
#include "video_core/textures/texture.h"
-namespace Tegra {
-namespace Engines {
+namespace Tegra::Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
@@ -408,5 +407,4 @@ void Maxwell3D::ProcessClearBuffers() {
rasterizer.Clear();
}
-} // namespace Engines
-} // namespace Tegra
+} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8af1c6b6..0e09a7ee5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -643,8 +643,10 @@ public:
u32 d3d_cull_mode;
ComparisonOp depth_test_func;
+ float alpha_test_ref;
+ ComparisonOp alpha_test_func;
- INSERT_PADDING_WORDS(0xB);
+ INSERT_PADDING_WORDS(0x9);
struct {
u32 separate_alpha;
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index 59e28b22d..8b5f08351 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -6,8 +6,7 @@
#include "core/core.h"
#include "video_core/engines/maxwell_compute.h"
-namespace Tegra {
-namespace Engines {
+namespace Tegra::Engines {
void MaxwellCompute::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) {
}
}
-} // namespace Engines
-} // namespace Tegra
+} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 103cd110e..b8a78cf82 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -7,8 +7,7 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
-namespace Tegra {
-namespace Engines {
+namespace Tegra::Engines {
MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
@@ -78,9 +77,9 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.exec.enable_2d == 1);
- std::size_t copy_size = regs.x_count * regs.y_count;
+ const std::size_t copy_size = regs.x_count * regs.y_count;
- const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) {
+ const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
// copying.
rasterizer.FlushRegion(source_cpu, src_size);
@@ -91,14 +90,11 @@ void MaxwellDMA::HandleCopy() {
rasterizer.InvalidateRegion(dest_cpu, dst_size);
};
- u8* src_buffer = Memory::GetPointer(source_cpu);
- u8* dst_buffer = Memory::GetPointer(dest_cpu);
-
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
ASSERT(regs.src_params.size_z == 1);
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
- u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
+ const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
copy_size * src_bytes_per_pixel);
@@ -111,7 +107,7 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.dst_params.size_z == 1);
ASSERT(regs.src_pitch == regs.x_count);
- u32 src_bpp = regs.src_pitch / regs.x_count;
+ const u32 src_bpp = regs.src_pitch / regs.x_count;
FlushAndInvalidate(regs.src_pitch * regs.y_count,
regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
@@ -122,5 +118,4 @@ void MaxwellDMA::HandleCopy() {
}
}
-} // namespace Engines
-} // namespace Tegra
+} // namespace Tegra::Engines
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index ac50bb622..6cd08d28b 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,7 +214,7 @@ enum class IMinMaxExchange : u64 {
XHi = 3,
};
-enum class VmadType : u64 {
+enum class VideoType : u64 {
Size16_Low = 0,
Size16_High = 1,
Size32 = 2,
@@ -783,6 +783,14 @@ union Instruction {
} psetp;
union {
+ BitField<43, 4, PredCondition> cond;
+ BitField<45, 2, PredOperation> op;
+ BitField<3, 3, u64> pred3;
+ BitField<0, 3, u64> pred0;
+ BitField<39, 3, u64> pred39;
+ } vsetp;
+
+ union {
BitField<12, 3, u64> pred12;
BitField<15, 1, u64> neg_pred12;
BitField<24, 2, PredOperation> cond;
@@ -1154,15 +1162,17 @@ union Instruction {
union {
BitField<48, 1, u64> signed_a;
BitField<38, 1, u64> is_byte_chunk_a;
- BitField<36, 2, VmadType> type_a;
+ BitField<36, 2, VideoType> type_a;
BitField<36, 2, u64> byte_height_a;
BitField<49, 1, u64> signed_b;
BitField<50, 1, u64> use_register_b;
BitField<30, 1, u64> is_byte_chunk_b;
- BitField<28, 2, VmadType> type_b;
+ BitField<28, 2, VideoType> type_b;
BitField<28, 2, u64> byte_height_b;
+ } video;
+ union {
BitField<51, 2, VmadShr> shr;
BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
BitField<47, 1, u64> cc;
@@ -1238,6 +1248,7 @@ public:
OUT_R, // Emit vertex/primitive
ISBERD,
VMAD,
+ VSETP,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
@@ -1495,6 +1506,7 @@ private:
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
+ INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3daccf82f..be51c5215 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -570,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
SyncBlendState();
SyncLogicOpState();
SyncCullMode();
- SyncAlphaTest();
SyncScissorTest();
+ // Alpha Testing is synced on shaders.
SyncTransformFeedback();
SyncPointState();
+ CheckAlphaTests();
// TODO(bunnei): Sync framebuffer_scale uniform 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);
}
-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() {
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;
}
+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
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b1f7ccc7e..0e90a31f5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -162,9 +162,6 @@ private:
/// Syncs the LogicOp state to match the guest state
void SyncLogicOpState();
- /// Syncs the alpha test state to match the guest state
- void SyncAlphaTest();
-
/// Syncs the scissor test state to match the guest state
void SyncScissorTest();
@@ -174,6 +171,9 @@ private:
/// Syncs the point state to match the guest state
void SyncPointState();
+ /// Check asserts for alpha testing.
+ void CheckAlphaTests();
+
bool has_ARB_direct_state_access = false;
bool has_ARB_multi_bind = false;
bool has_ARB_separate_shader_objects = false;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index d36f190b7..fe4d1bd83 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1266,9 +1266,29 @@ private:
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
// 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;
++render_target) {
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@@ -1292,6 +1312,63 @@ private:
}
}
+ /// Unpacks a video instruction operand (e.g. VMAD).
+ std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
+ Tegra::Shader::VideoType type, u64 byte_height) {
+ const std::string value = [&]() {
+ if (!is_chunk) {
+ const auto offset = static_cast<u32>(byte_height * 8);
+ return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
+ }
+ const std::string zero = "0";
+
+ switch (type) {
+ case Tegra::Shader::VideoType::Size16_Low:
+ return '(' + op + " & 0xffff)";
+ case Tegra::Shader::VideoType::Size16_High:
+ return '(' + op + " >> 16)";
+ case Tegra::Shader::VideoType::Size32:
+ // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
+ // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
+ // explanation is found: assert.
+ UNIMPLEMENTED();
+ return zero;
+ case Tegra::Shader::VideoType::Invalid:
+ UNREACHABLE_MSG("Invalid instruction encoding");
+ return zero;
+ default:
+ UNREACHABLE();
+ return zero;
+ }
+ }();
+
+ if (is_signed) {
+ return "int(" + value + ')';
+ }
+ return value;
+ };
+
+ /// Gets the A operand for a video instruction.
+ std::string GetVideoOperandA(Instruction instr) {
+ return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
+ instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
+ instr.video.type_a, instr.video.byte_height_a);
+ }
+
+ /// Gets the B operand for a video instruction.
+ std::string GetVideoOperandB(Instruction instr) {
+ if (instr.video.use_register_b) {
+ return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
+ instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
+ instr.video.type_b, instr.video.byte_height_b);
+ } else {
+ return '(' +
+ std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
+ : instr.alu.GetImm20_16()) +
+ ')';
+ }
+ }
+
/**
* Compiles a single instruction from Tegra to GLSL.
* @param offset the offset of the Tegra shader instruction.
@@ -3301,87 +3378,51 @@ private:
break;
}
case OpCode::Id::VMAD: {
- const bool signed_a = instr.vmad.signed_a == 1;
- const bool signed_b = instr.vmad.signed_b == 1;
- const bool result_signed = signed_a || signed_b;
- boost::optional<std::string> forced_result;
-
- auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
- Tegra::Shader::VmadType type, u64 byte_height) {
- const std::string value = [&]() {
- if (!is_chunk) {
- const auto shift = static_cast<u32>(byte_height * 8);
- return "((" + op + " >> " + std::to_string(shift) + ") & 0xff)";
- }
- const std::string zero = "0";
-
- switch (type) {
- case Tegra::Shader::VmadType::Size16_Low:
- return '(' + op + " & 0xffff)";
- case Tegra::Shader::VmadType::Size16_High:
- return '(' + op + " >> 16)";
- case Tegra::Shader::VmadType::Size32:
- // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
- // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
- // explanation is found: assert.
- UNREACHABLE_MSG("Unimplemented");
- return zero;
- case Tegra::Shader::VmadType::Invalid:
- // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
- // testing (even though it's invalid) this makes the whole instruction
- // assign zero to target register.
- forced_result = boost::make_optional(zero);
- return zero;
- default:
- UNREACHABLE();
- return zero;
- }
- }();
-
- if (is_signed) {
- return "int(" + value + ')';
- }
- return value;
- };
-
- const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
- instr.vmad.is_byte_chunk_a != 0, signed_a,
- instr.vmad.type_a, instr.vmad.byte_height_a);
-
- std::string op_b;
- if (instr.vmad.use_register_b) {
- op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
- instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
- instr.vmad.byte_height_b);
- } else {
- op_b = '(' +
- std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
- : instr.alu.GetImm20_16()) +
- ')';
- }
-
+ const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
+ const std::string op_a = GetVideoOperandA(instr);
+ const std::string op_b = GetVideoOperandB(instr);
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
- std::string result;
- if (forced_result) {
- result = *forced_result;
- } else {
- result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
+ std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
- switch (instr.vmad.shr) {
- case Tegra::Shader::VmadShr::Shr7:
- result = '(' + result + " >> 7)";
- break;
- case Tegra::Shader::VmadShr::Shr15:
- result = '(' + result + " >> 15)";
- break;
- }
+ switch (instr.vmad.shr) {
+ case Tegra::Shader::VmadShr::Shr7:
+ result = '(' + result + " >> 7)";
+ break;
+ case Tegra::Shader::VmadShr::Shr15:
+ result = '(' + result + " >> 15)";
+ break;
}
+
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
instr.vmad.saturate == 1, 0, Register::Size::Word,
instr.vmad.cc);
break;
}
+ case OpCode::Id::VSETP: {
+ const std::string op_a = GetVideoOperandA(instr);
+ const std::string op_b = GetVideoOperandB(instr);
+
+ // We can't use the constant predicate as destination.
+ ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
+
+ const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
+
+ const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
+
+ const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
+ // Set the primary predicate to the result of Predicate OP SecondPredicate
+ SetPredicate(instr.vsetp.pred3,
+ '(' + predicate + ") " + combiner + " (" + second_pred + ')');
+
+ if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
+ // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
+ // if enabled
+ SetPredicate(instr.vsetp.pred0,
+ "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
+ }
+ break;
+ }
default: {
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
UNREACHABLE();
@@ -3516,7 +3557,7 @@ private:
// Declarations
std::set<std::string> declr_predicates;
-}; // namespace Decompiler
+}; // namespace OpenGL::GLShader::Decompiler
std::string GetCommonDeclarations() {
return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index ecbc9d8ed..e883ffb1d 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -29,6 +29,7 @@ layout(std140) uniform vs_config {
vec4 viewport_flip;
uvec4 instance_id;
uvec4 flip_stage;
+ uvec4 alpha_test;
};
)";
@@ -105,6 +106,7 @@ layout (std140) uniform gs_config {
vec4 viewport_flip;
uvec4 instance_id;
uvec4 flip_stage;
+ uvec4 alpha_test;
};
void main() {
@@ -142,8 +144,33 @@ layout (std140) uniform fs_config {
vec4 viewport_flip;
uvec4 instance_id;
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() {
exec_fragment();
}
@@ -152,4 +179,4 @@ void main() {
out += program.first;
return {out, program.second};
}
-} // namespace OpenGL::GLShader \ No newline at end of file
+} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 010857ec6..8b8869ecb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -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[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.
instance_id[0] = state.current_instance;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index b3a191cf2..36fe1f04c 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -22,8 +22,14 @@ struct MaxwellUniformData {
alignas(16) GLvec4 viewport_flip;
alignas(16) GLuvec4 instance_id;
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,
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");