aboutsummaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h16
-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.cpp139
-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
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h16
9 files changed, 171 insertions, 83 deletions
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/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e3d67ff87..ac50bb622 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -564,6 +564,10 @@ union Instruction {
} fmul;
union {
+ BitField<55, 1, u64> saturate;
+ } fmul32;
+
+ union {
BitField<48, 1, u64> is_signed;
} shift;
@@ -753,7 +757,6 @@ union Instruction {
BitField<45, 2, PredOperation> op;
BitField<47, 1, u64> ftz;
BitField<48, 4, PredCondition> cond;
- BitField<56, 1, u64> neg_b;
} fsetp;
union {
@@ -828,7 +831,6 @@ union Instruction {
BitField<53, 1, u64> neg_b;
BitField<54, 1, u64> abs_a;
BitField<55, 1, u64> ftz;
- BitField<56, 1, u64> neg_imm;
} fset;
union {
@@ -1211,11 +1213,13 @@ public:
KIL,
SSY,
SYNC,
+ BRK,
DEPBAR,
BFE_C,
BFE_R,
BFE_IMM,
BRA,
+ PBK,
LD_A,
LD_C,
ST_A,
@@ -1372,7 +1376,7 @@ public:
/// conditionally executed).
static bool IsPredicatedInstruction(Id opcode) {
// TODO(Subv): Add the rest of unpredicated instructions.
- return opcode != Id::SSY;
+ return opcode != Id::SSY && opcode != Id::PBK;
}
class Matcher {
@@ -1468,9 +1472,11 @@ private:
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
+ INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
+ INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
+ INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
- INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
@@ -1608,4 +1614,4 @@ private:
}
};
-} // namespace Tegra::Shader
+} // namespace Tegra::Shader \ No newline at end of file
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 a427353e9..7a019fc86 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -163,10 +163,11 @@ private:
const ExitMethod jmp = Scan(target, end, labels);
return exit_method = ParallelExit(no_jmp, jmp);
}
- case OpCode::Id::SSY: {
- // The SSY instruction uses a similar encoding as the BRA instruction.
+ case OpCode::Id::SSY:
+ case OpCode::Id::PBK: {
+ // The SSY and PBK use a similar encoding as the BRA instruction.
ASSERT_MSG(instr.bra.constant_buffer == 0,
- "Constant buffer SSY is not supported");
+ "Constant buffer branching is not supported");
const u32 target = offset + instr.bra.GetBranchTarget();
labels.insert(target);
// Continue scanning for an exit method.
@@ -378,8 +379,8 @@ public:
* @param reg The destination register to use.
* @param elem The element to use for the operation.
* @param value The code representing the value to assign. Type has to be half float.
- * @param type Half float kind of assignment.
- * @param dest_num_components Number of components in the destionation.
+ * @param merge Half float kind of assignment.
+ * @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_saturated Optional, when True, saturates the provided value.
* @param dest_elem Optional, the destination element to use for the operation.
@@ -422,6 +423,7 @@ public:
* @param reg The destination register to use.
* @param elem The element to use for the operation.
* @param attribute The input attribute to use as the source value.
+ * @param input_mode The input mode.
* @param vertex The register that decides which vertex to read from (used in GS).
*/
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
@@ -951,7 +953,7 @@ private:
// Can't assign to the constant predicate.
ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
- const std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
+ std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
shader.AddLine(variable + " = " + value + ';');
declr_predicates.insert(std::move(variable));
}
@@ -1058,7 +1060,7 @@ private:
/*
* Transforms the input string GLSL operand into an unpacked half float pair.
* @note This function returns a float type pair instead of a half float pair. This is because
- * real half floats are not standarized in GLSL but unpackHalf2x16 (which returns a vec2) is.
+ * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
* @param operand Input operand. It has to be an unsigned integer.
* @param type How to unpack the unsigned integer to a half float pair.
* @param abs Get the absolute value of unpacked half floats.
@@ -1232,27 +1234,27 @@ private:
}
/*
- * Emits code to push the input target address to the SSY address stack, incrementing the stack
+ * Emits code to push the input target address to the flow address stack, incrementing the stack
* top.
*/
- void EmitPushToSSYStack(u32 target) {
+ void EmitPushToFlowStack(u32 target) {
shader.AddLine('{');
++shader.scope;
- shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;");
- shader.AddLine("ssy_stack_top++;");
+ shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
+ shader.AddLine("flow_stack_top++;");
--shader.scope;
shader.AddLine('}');
}
/*
- * Emits code to pop an address from the SSY address stack, setting the jump address to the
+ * Emits code to pop an address from the flow address stack, setting the jump address to the
* popped address and decrementing the stack top.
*/
- void EmitPopFromSSYStack() {
+ void EmitPopFromFlowStack() {
shader.AddLine('{');
++shader.scope;
- shader.AddLine("ssy_stack_top--;");
- shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];");
+ shader.AddLine("flow_stack_top--;");
+ shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
shader.AddLine("break;");
--shader.scope;
shader.AddLine('}');
@@ -1264,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.
@@ -1459,9 +1481,10 @@ private:
break;
}
case OpCode::Id::FMUL32_IMM: {
- regs.SetRegisterToFloat(
- instr.gpr0, 0,
- regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0,
+ regs.GetRegisterAsFloat(instr.gpr8) + " * " +
+ GetImmediate32(instr),
+ 1, 1, instr.fmul32.saturate);
break;
}
case OpCode::Id::FADD32I: {
@@ -2736,20 +2759,13 @@ private:
break;
}
case OpCode::Type::FloatSetPredicate: {
- std::string op_a = instr.fsetp.neg_a ? "-" : "";
- op_a += regs.GetRegisterAsFloat(instr.gpr8);
-
- if (instr.fsetp.abs_a) {
- op_a = "abs(" + op_a + ')';
- }
+ const std::string op_a =
+ GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
+ instr.fsetp.neg_a != 0);
- std::string op_b{};
+ std::string op_b;
if (instr.is_b_imm) {
- if (instr.fsetp.neg_b) {
- // Only the immediate version of fsetp has a neg_b bit.
- op_b += '-';
- }
op_b += '(' + GetImmediate19(instr) + ')';
} else {
if (instr.is_b_gpr) {
@@ -2945,33 +2961,24 @@ private:
break;
}
case OpCode::Type::FloatSet: {
- std::string op_a = instr.fset.neg_a ? "-" : "";
- op_a += regs.GetRegisterAsFloat(instr.gpr8);
-
- if (instr.fset.abs_a) {
- op_a = "abs(" + op_a + ')';
- }
+ const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
+ instr.fset.abs_a != 0, instr.fset.neg_a != 0);
- std::string op_b = instr.fset.neg_b ? "-" : "";
+ std::string op_b;
if (instr.is_b_imm) {
const std::string imm = GetImmediate19(instr);
- if (instr.fset.neg_imm)
- op_b += "(-" + imm + ')';
- else
- op_b += imm;
+ op_b = imm;
} else {
if (instr.is_b_gpr) {
- op_b += regs.GetRegisterAsFloat(instr.gpr20);
+ op_b = regs.GetRegisterAsFloat(instr.gpr20);
} else {
- op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
- GLSLRegister::Type::Float);
+ op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
+ GLSLRegister::Type::Float);
}
}
- if (instr.fset.abs_b) {
- op_b = "abs(" + op_b + ')';
- }
+ op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
// The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
// condition is true, and to 0 otherwise.
@@ -3279,16 +3286,32 @@ private:
// The SSY opcode tells the GPU where to re-converge divergent execution paths, it
// sets the target of the jump that the SYNC instruction will make. The SSY opcode
// has a similar structure to the BRA opcode.
- ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
+ ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
+
+ const u32 target = offset + instr.bra.GetBranchTarget();
+ EmitPushToFlowStack(target);
+ break;
+ }
+ case OpCode::Id::PBK: {
+ // PBK pushes to a stack the address where BRK will jump to. This shares stack with
+ // SSY but using SYNC on a PBK address will kill the shader execution. We don't
+ // emulate this because it's very unlikely a driver will emit such invalid shader.
+ ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
const u32 target = offset + instr.bra.GetBranchTarget();
- EmitPushToSSYStack(target);
+ EmitPushToFlowStack(target);
break;
}
case OpCode::Id::SYNC: {
// The SYNC opcode jumps to the address previously set by the SSY opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
- EmitPopFromSSYStack();
+ EmitPopFromFlowStack();
+ break;
+ }
+ case OpCode::Id::BRK: {
+ // The BRK opcode jumps to the address previously set by the PBK opcode
+ ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
+ EmitPopFromFlowStack();
break;
}
case OpCode::Id::DEPBAR: {
@@ -3307,8 +3330,8 @@ private:
Tegra::Shader::VmadType 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 auto shift = static_cast<u32>(byte_height * 8);
+ return "((" + op + " >> " + std::to_string(shift) + ") & 0xff)";
}
const std::string zero = "0";
@@ -3442,11 +3465,11 @@ private:
labels.insert(subroutine.begin);
shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
- // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems
- // unlikely that shaders will use 20 nested SSYs.
- constexpr u32 SSY_STACK_SIZE = 20;
- shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];");
- shader.AddLine("uint ssy_stack_top = 0u;");
+ // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
+ // unlikely that shaders will use 20 nested SSYs and PBKs.
+ constexpr u32 FLOW_STACK_SIZE = 20;
+ shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
+ shader.AddLine("uint flow_stack_top = 0u;");
shader.AddLine("while (true) {");
++shader.scope;
@@ -3513,7 +3536,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");
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3c3bcaae4..0f6dcab2b 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
return {};
}
- case Maxwell::VertexAttribute::Type::Float:
- return GL_FLOAT;
+ case Maxwell::VertexAttribute::Type::Float: {
+ switch (attrib.size) {
+ case Maxwell::VertexAttribute::Size::Size_16:
+ case Maxwell::VertexAttribute::Size::Size_16_16:
+ case Maxwell::VertexAttribute::Size::Size_16_16_16:
+ case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
+ return GL_HALF_FLOAT;
+ case Maxwell::VertexAttribute::Size::Size_32:
+ case Maxwell::VertexAttribute::Size::Size_32_32:
+ case Maxwell::VertexAttribute::Size::Size_32_32_32:
+ case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
+ return GL_FLOAT;
+ }
+ }
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());