From 752faff2bcfa536a9efc81416156b1d42d05a2cc Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Wed, 19 Sep 2018 01:18:20 -0400 Subject: [PATCH] Implemented Depth Compare and Shadow Samplers --- .../renderer_opengl/gl_rasterizer.cpp | 18 ++ .../renderer_opengl/gl_rasterizer.h | 2 + .../renderer_opengl/gl_shader_decompiler.cpp | 215 +++++++++++++----- .../renderer_opengl/gl_shader_gen.h | 14 +- .../renderer_opengl/maxwell_to_gl.h | 25 ++ src/video_core/textures/texture.h | 13 +- 6 files changed, 223 insertions(+), 64 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 587d9dffb1..cc07b47213 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -601,10 +601,13 @@ void RasterizerOpenGL::SamplerInfo::Create() { sampler.Create(); mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap; + uses_depth_compare = false; + depth_compare_func = Tegra::Texture::DepthCompareFunc::Never; // default is GL_LINEAR_MIPMAP_LINEAR glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Other attributes have correct defaults + glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); } void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { @@ -632,6 +635,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p)); } + if (uses_depth_compare != (config.depth_compare_enabled == 1)) { + uses_depth_compare = (config.depth_compare_enabled == 1); + if (uses_depth_compare) { + glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + } else { + glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + } + + if (depth_compare_func != config.depth_compare_func) { + depth_compare_func = config.depth_compare_func; + glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC, + MaxwellToGL::DepthCompareFunc(depth_compare_func)); + } + if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || wrap_p == Tegra::Texture::WrapMode::Border) { const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 4c8ecbd1c1..987c4f4fb2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -94,6 +94,8 @@ private: Tegra::Texture::WrapMode wrap_u; Tegra::Texture::WrapMode wrap_v; Tegra::Texture::WrapMode wrap_p; + bool uses_depth_compare; + Tegra::Texture::DepthCompareFunc depth_compare_func; GLvec4 border_color; }; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d73234ec3c..7e57de78aa 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -508,7 +508,7 @@ public: /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if /// necessary. std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, - bool is_array) { + bool is_array, bool is_shadow) { const std::size_t offset = static_cast(sampler.index.Value()); // If this sampler has already been used, return the existing mapping. @@ -517,13 +517,14 @@ public: [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); if (itr != used_samplers.end()) { - ASSERT(itr->GetType() == type && itr->IsArray() == is_array); + ASSERT(itr->GetType() == type && itr->IsArray() == is_array && + itr->IsShadow() == is_shadow); return itr->GetName(); } // Otherwise create a new mapping for this sampler const std::size_t next_index = used_samplers.size(); - const SamplerEntry entry{stage, offset, next_index, type, is_array}; + const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; used_samplers.emplace_back(entry); return entry.GetName(); } @@ -747,8 +748,9 @@ private: } /// Generates code representing a texture sampler. - std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) { - return regs.AccessSampler(sampler, type, is_array); + std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, + bool is_shadow) { + return regs.AccessSampler(sampler, type, is_array, is_shadow); } /** @@ -1002,6 +1004,24 @@ private: shader.AddLine('}'); } + static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { + switch (texture_type) { + case Tegra::Shader::TextureType::Texture1D: { + return 1; + } + case Tegra::Shader::TextureType::Texture2D: { + return 2; + } + case Tegra::Shader::TextureType::TextureCube: { + return 3; + } + default: + LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast(texture_type)); + UNREACHABLE(); + return 0; + } + } + /* * Emits code to push the input target address to the SSY address stack, incrementing the stack * top. @@ -1896,24 +1916,35 @@ private: "NODEP is not implemented"); ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); - ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), - "DC is not implemented"); - switch (texture_type) { - case Tegra::Shader::TextureType::Texture1D: { + const bool depth_compare = + instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); + u32 num_coordinates = TextureCoordinates(texture_type); + if (depth_compare) + num_coordinates += 1; + + switch (num_coordinates) { + case 1: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); coord = "float coords = " + x + ';'; break; } - case Tegra::Shader::TextureType::Texture2D: { + case 2: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); coord = "vec2 coords = vec2(" + x + ", " + y + ");"; break; } + case 3: { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + break; + } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", - static_cast(texture_type)); + LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", + static_cast(num_coordinates)); UNREACHABLE(); // Fallback to interpreting as a 2D texture for now @@ -1924,9 +1955,10 @@ private: } // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias // or lod. - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20); + std::string op_c; - const std::string sampler = GetSampler(instr.sampler, texture_type, false); + const std::string sampler = + GetSampler(instr.sampler, texture_type, false, depth_compare); // Add an extra scope and declare the texture coords inside to prevent // overwriting them in case they are used as outputs of the texs instruction. @@ -1946,12 +1978,22 @@ private: } case Tegra::Shader::TextureProcessMode::LB: case Tegra::Shader::TextureProcessMode::LBA: { + if (num_coordinates <= 2) { + op_c = regs.GetRegisterAsFloat(instr.gpr20); + } else { + op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + } // TODO: Figure if A suffix changes the equation at all. texture = "texture(" + sampler + ", coords, " + op_c + ')'; break; } case Tegra::Shader::TextureProcessMode::LL: case Tegra::Shader::TextureProcessMode::LLA: { + if (num_coordinates <= 2) { + op_c = regs.GetRegisterAsFloat(instr.gpr20); + } else { + op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + } // TODO: Figure if A suffix changes the equation at all. texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; break; @@ -1963,14 +2005,18 @@ private: UNREACHABLE(); } } - std::size_t dest_elem{}; - for (std::size_t elem = 0; elem < 4; ++elem) { - if (!instr.tex.IsComponentEnabled(elem)) { - // Skip disabled components - continue; + if (!depth_compare) { + std::size_t dest_elem{}; + for (std::size_t elem = 0; elem < 4; ++elem) { + if (!instr.tex.IsComponentEnabled(elem)) { + // Skip disabled components + continue; + } + regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); + ++dest_elem; } - regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); - ++dest_elem; + } else { + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } --shader.scope; shader.AddLine("}"); @@ -1983,11 +2029,15 @@ private: ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); - ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), - "DC is not implemented"); - switch (texture_type) { - case Tegra::Shader::TextureType::Texture2D: { + const bool depth_compare = + instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); + u32 num_coordinates = TextureCoordinates(texture_type); + if (depth_compare) + num_coordinates += 1; + + switch (num_coordinates) { + case 2: { if (is_array) { const std::string index = regs.GetRegisterAsInteger(instr.gpr8); const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); @@ -2000,17 +2050,25 @@ private: } break; } - case Tegra::Shader::TextureType::TextureCube: { - ASSERT_MSG(!is_array, "Unimplemented"); - std::string x = regs.GetRegisterAsFloat(instr.gpr8); - std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + case 3: { + if (is_array) { + UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented"); + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + texture_type = Tegra::Shader::TextureType::Texture2D; + is_array = false; + } else { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + } break; } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", - static_cast(texture_type)); + LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", + static_cast(num_coordinates)); UNREACHABLE(); // Fallback to interpreting as a 2D texture for now @@ -2020,9 +2078,9 @@ private: texture_type = Tegra::Shader::TextureType::Texture2D; is_array = false; } - const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, depth_compare); std::string texture; - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); switch (instr.texs.GetTextureProcessMode()) { case Tegra::Shader::TextureProcessMode::None: { texture = "texture(" + sampler + ", coords)"; @@ -2033,6 +2091,7 @@ private: break; } case Tegra::Shader::TextureProcessMode::LL: { + const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; break; } @@ -2043,7 +2102,11 @@ private: UNREACHABLE(); } } - WriteTexsInstruction(instr, coord, texture); + if (!depth_compare) { + WriteTexsInstruction(instr, coord, texture); + } else { + WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + } break; } case OpCode::Id::TLDS: { @@ -2083,7 +2146,8 @@ private: static_cast(texture_type)); UNREACHABLE(); } - const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, false); std::string texture = "texelFetch(" + sampler + ", coords, 0)"; const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1); switch (instr.tlds.GetTextureProcessMode()) { @@ -2114,28 +2178,43 @@ private: "NODEP is not implemented"); ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); - ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), - "DC is not implemented"); ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), "NDV is not implemented"); ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), "PTP is not implemented"); + const bool depth_compare = + instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); + auto texture_type = instr.tld4.texture_type.Value(); + u32 num_coordinates = TextureCoordinates(texture_type); + if (depth_compare) + num_coordinates += 1; - switch (instr.tld4.texture_type) { - case Tegra::Shader::TextureType::Texture2D: { + switch (num_coordinates) { + case 2: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); coord = "vec2 coords = vec2(" + x + ", " + y + ");"; break; } + case 3: { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + break; + } default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", - static_cast(instr.tld4.texture_type.Value())); + LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", + static_cast(num_coordinates)); UNREACHABLE(); + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + texture_type = Tegra::Shader::TextureType::Texture2D; } const std::string sampler = - GetSampler(instr.sampler, instr.tld4.texture_type, false); + GetSampler(instr.sampler, texture_type, false, depth_compare); // Add an extra scope and declare the texture coords inside to prevent // overwriting them in case they are used as outputs of the texs instruction. shader.AddLine("{"); @@ -2143,15 +2222,18 @@ private: shader.AddLine(coord); const std::string texture = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4.component) + ')'; - - std::size_t dest_elem{}; - for (std::size_t elem = 0; elem < 4; ++elem) { - if (!instr.tex.IsComponentEnabled(elem)) { - // Skip disabled components - continue; + if (!depth_compare) { + std::size_t dest_elem{}; + for (std::size_t elem = 0; elem < 4; ++elem) { + if (!instr.tex.IsComponentEnabled(elem)) { + // Skip disabled components + continue; + } + regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); + ++dest_elem; } - regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); - ++dest_elem; + } else { + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } --shader.scope; shader.AddLine("}"); @@ -2162,18 +2244,30 @@ private: "NODEP is not implemented"); ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); - ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), - "DC is not implemented"); + const bool depth_compare = + instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. - const std::string sampler = - GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); - const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; + const std::string sampler = GetSampler( + instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); + std::string coord; + if (!depth_compare) { + coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; + } else { + // Note: TLD4S coordinate encoding works just like TEXS's + const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; + } const std::string texture = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4s.component) + ')'; - WriteTexsInstruction(instr, coord, texture); + + if (!depth_compare) { + WriteTexsInstruction(instr, coord, texture); + } else { + WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + } break; } case OpCode::Id::TXQ: { @@ -2184,7 +2278,7 @@ private: // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. const std::string sampler = - GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); + GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); switch (instr.txq.query_type) { case Tegra::Shader::TextureQueryType::Dimension: { const std::string texture = "textureQueryLevels(" + sampler + ')'; @@ -2209,7 +2303,8 @@ private: const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const bool is_array = instr.tmml.array != 0; auto texture_type = instr.tmml.texture_type.Value(); - const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, false); // TODO: add coordinates for different samplers once other texture types are // implemented. diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index d53b93ad58..e56f39e788 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -75,8 +75,9 @@ class SamplerEntry { public: SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index, - Tegra::Shader::TextureType type, bool is_array) - : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} + Tegra::Shader::TextureType type, bool is_array, bool is_shadow) + : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array), + is_shadow(is_shadow) {} std::size_t GetOffset() const { return offset; @@ -117,6 +118,8 @@ public: } if (is_array) glsl_type += "Array"; + if (is_shadow) + glsl_type += "Shadow"; return glsl_type; } @@ -128,6 +131,10 @@ public: return is_array; } + bool IsShadow() const { + return is_shadow; + } + u32 GetHash() const { return (static_cast(stage) << 16) | static_cast(sampler_index); } @@ -147,7 +154,8 @@ private: Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc) - bool is_array; ///< Whether the texture is being sampled as an array texture or not. + bool is_array; ///< Whether the texture is being sampled as an array texture or not. + bool is_shadow; ///< Whether the texture is being sampled as a depth texture or not. }; struct ShaderEntries { diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 67273e1648..3c3bcaae4e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -159,6 +159,31 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { return {}; } +inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { + switch (func) { + case Tegra::Texture::DepthCompareFunc::Never: + return GL_NEVER; + case Tegra::Texture::DepthCompareFunc::Less: + return GL_LESS; + case Tegra::Texture::DepthCompareFunc::LessEqual: + return GL_LEQUAL; + case Tegra::Texture::DepthCompareFunc::Equal: + return GL_EQUAL; + case Tegra::Texture::DepthCompareFunc::NotEqual: + return GL_NOTEQUAL; + case Tegra::Texture::DepthCompareFunc::Greater: + return GL_GREATER; + case Tegra::Texture::DepthCompareFunc::GreaterEqual: + return GL_GEQUAL; + case Tegra::Texture::DepthCompareFunc::Always: + return GL_ALWAYS; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", + static_cast(func)); + UNREACHABLE(); + return {}; +} + inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { case Maxwell::Blend::Equation::Add: diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 14aea48383..8f31d825a3 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -227,6 +227,17 @@ enum class WrapMode : u32 { MirrorOnceClampOGL = 7, }; +enum class DepthCompareFunc : u32 { + Never = 0, + Less = 1, + Equal = 2, + LessEqual = 3, + Greater = 4, + NotEqual = 5, + GreaterEqual = 6, + Always = 7, +}; + enum class TextureFilter : u32 { Nearest = 1, Linear = 2, @@ -244,7 +255,7 @@ struct TSCEntry { BitField<3, 3, WrapMode> wrap_v; BitField<6, 3, WrapMode> wrap_p; BitField<9, 1, u32> depth_compare_enabled; - BitField<10, 3, u32> depth_compare_func; + BitField<10, 3, DepthCompareFunc> depth_compare_func; }; union { BitField<0, 2, TextureFilter> mag_filter;