mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Feat] (MG_Util/ShaderTranspiler): do FloatEqualsZero elimination by leveraging SPIRV-Tool opt pass
This commit is contained in:
+21
-22
@@ -136,6 +136,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp
|
MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp
|
||||||
MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp
|
MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp
|
||||||
MobileGL/MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.cpp
|
MobileGL/MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.cpp
|
||||||
|
MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/FloatEqualsZeroEliminationPass.cpp
|
||||||
|
|
||||||
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
||||||
|
|
||||||
@@ -204,6 +205,22 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_State/GLState/RenderbufferState/RenderbufferState.cpp
|
MobileGL/MG_State/GLState/RenderbufferState/RenderbufferState.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(MOBILEGL_LINK_LIBRARIES
|
||||||
|
glslang::glslang
|
||||||
|
spirv-cross-c
|
||||||
|
SPIRV-Tools-opt
|
||||||
|
SPIRV-Tools
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MOBILEGL_INCLUDE_DIR
|
||||||
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
${CMAKE_SOURCE_DIR}/MobileGL
|
||||||
|
${spirv-tools_SOURCE_DIR}
|
||||||
|
${spirv-tools_SOURCE_DIR}/include
|
||||||
|
${spirv-tools_BINARY_DIR}
|
||||||
|
${SPIRV-Headers_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
)
|
)
|
||||||
@@ -223,21 +240,12 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
||||||
${CMAKE_SOURCE_DIR}/include
|
${MOBILEGL_INCLUDE_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/MobileGL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# Diligent-Common
|
${MOBILEGL_LINK_LIBRARIES}
|
||||||
# Diligent-GraphicsEngineOpenGL-shared
|
|
||||||
# Diligent-GraphicsEngineVk-shared
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
|
||||||
PRIVATE
|
|
||||||
glslang::glslang
|
|
||||||
spirv-cross-c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${CMAKE_PROJECT_NAME}_s STATIC
|
add_library(${CMAKE_PROJECT_NAME}_s STATIC
|
||||||
@@ -259,21 +267,12 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME}_s PUBLIC
|
target_include_directories(${CMAKE_PROJECT_NAME}_s PUBLIC
|
||||||
${CMAKE_SOURCE_DIR}/include
|
${MOBILEGL_INCLUDE_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/MobileGL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}_s
|
target_link_libraries(${CMAKE_PROJECT_NAME}_s
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# Diligent-Common
|
${MOBILEGL_LINK_LIBRARIES}
|
||||||
# Diligent-GraphicsEngineOpenGL-shared
|
|
||||||
# Diligent-GraphicsEngineVk-shared
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}_s
|
|
||||||
PRIVATE
|
|
||||||
glslang::glslang
|
|
||||||
spirv-cross-c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (TRACY_ENABLE)
|
if (TRACY_ENABLE)
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const auto& backendBufferObject = backendBufferIt->second;
|
const auto& backendBufferObject = backendBufferIt->second;
|
||||||
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
|
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
|
||||||
} else {
|
} else {
|
||||||
MGLOG_E("No backend buffer found for index buffer binding, cannot bind index buffer.");
|
MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,7 +873,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
spvc_compiler_options options;
|
spvc_compiler_options options;
|
||||||
spvcSession.CreateOptions(&options);
|
spvcSession.CreateOptions(&options);
|
||||||
|
|
||||||
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 320);
|
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 300);
|
||||||
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE);
|
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE);
|
||||||
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
||||||
|
|
||||||
|
|||||||
@@ -455,6 +455,10 @@ namespace MobileGL {
|
|||||||
|
|
||||||
for (SizeT i = 0; i < m_generatedSpirv.size(); i++) {
|
for (SizeT i = 0; i < m_generatedSpirv.size(); i++) {
|
||||||
auto& spv = m_generatedSpirv[i];
|
auto& spv = m_generatedSpirv[i];
|
||||||
|
|
||||||
|
auto success = ShaderCompiler::SanitizeBinary(spv, spv);
|
||||||
|
MOBILEGL_ASSERT(success, "SanitizeBinary failed");
|
||||||
|
|
||||||
auto shaderType = shaderTypes[i];
|
auto shaderType = shaderTypes[i];
|
||||||
MGLOG_D("ProgramObject %u: GenerateBinary - parsing SPIR-V meta data for module %zu "
|
MGLOG_D("ProgramObject %u: GenerateBinary - parsing SPIR-V meta data for module %zu "
|
||||||
"(shaderType=%u, wordCount=%zu)",
|
"(shaderType=%u, wordCount=%zu)",
|
||||||
|
|||||||
@@ -922,26 +922,26 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitVertexIn) {
|
|||||||
|
|
||||||
auto programObject = MG_State::pGLContext->GetCurrentProgram();
|
auto programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||||
auto& spirvs = programObject->GetGeneratedSpirv();
|
auto& spirvs = programObject->GetGeneratedSpirv();
|
||||||
auto& vertexSpirv = spirvs[1]; // 0 - fragment, 1 - vertex
|
// auto& vertexSpirv = spirvs[1]; // 0 - fragment, 1 - vertex
|
||||||
char* pSrcVertIn = nullptr;
|
char* pSrcVertIn = nullptr;
|
||||||
const char* needle = "layout(location = 2) in vec2 UV0;";
|
const char* needle = "layout(location = 2) in vec2 UV0;";
|
||||||
// for (auto spirv: spirvs) {
|
for (auto spirv: spirvs) {
|
||||||
MG_Util::ShaderTranspiler::SpvcSession spvcSession(vertexSpirv);
|
MG_Util::ShaderTranspiler::SpvcSession spvcSession(spirv);
|
||||||
spvc_compiler_options options;
|
spvc_compiler_options options;
|
||||||
spvcSession.CreateOptions(&options);
|
spvcSession.CreateOptions(&options);
|
||||||
|
|
||||||
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 460);
|
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 460);
|
||||||
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE);
|
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE);
|
||||||
// spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
// spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
|
||||||
|
|
||||||
spvcSession.SetOptions(options);
|
spvcSession.SetOptions(options);
|
||||||
|
|
||||||
const char* result = nullptr;
|
const char* result = nullptr;
|
||||||
spvcSession.Compile(&result);
|
spvcSession.Compile(&result);
|
||||||
printf("%s\n\n", result);
|
printf("%s\n\n", result);
|
||||||
const char* ret = strstr(result, needle);
|
const char* ret = strstr(result, needle);
|
||||||
if (ret) pSrcVertIn = (char*)ret;
|
if (ret) pSrcVertIn = (char*)ret;
|
||||||
// }
|
}
|
||||||
ASSERT_TRUE(pSrcVertIn != nullptr) << "Not found expected string in generated shader.\n(Searching for \"" << needle
|
ASSERT_TRUE(pSrcVertIn != nullptr) << "Not found expected string in generated shader.\n(Searching for \"" << needle
|
||||||
<< "\")";
|
<< "\")";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,11 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "ShaderCompiler.h"
|
#include "ShaderCompiler.h"
|
||||||
|
|
||||||
|
#include "SpirvPasses/FloatEqualsZeroEliminationPass.h"
|
||||||
|
#include "spirv-tools/libspirv.h"
|
||||||
|
#include "spirv-tools/optimizer.hpp"
|
||||||
|
|
||||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||||
#include <MG_Util/Converters/GLToGlslang/ProgramEnumConverter.h>
|
#include <MG_Util/Converters/GLToGlslang/ProgramEnumConverter.h>
|
||||||
|
|
||||||
@@ -182,7 +187,7 @@ namespace MobileGL {
|
|||||||
return std::unexpected(r);
|
return std::unexpected(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto [name, loc] : attrib.explicitVertexInLocations) {
|
for (const auto& [name, loc] : attrib.explicitVertexInLocations) {
|
||||||
MGLOG_D("%s: got explicitly set - layout(location = %d) %s;", __func__, loc, name.c_str());
|
MGLOG_D("%s: got explicitly set - layout(location = %d) %s;", __func__, loc, name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +227,14 @@ namespace MobileGL {
|
|||||||
return allSpirv;
|
return allSpirv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderCompiler::SanitizeBinary(const Vector<Uint32>& inputBinary, Vector<uint32_t>& outputBinary) {
|
||||||
|
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_5);
|
||||||
|
|
||||||
|
optimizer.RegisterPass(spvtools::Optimizer::PassToken(MakeUnique<FloatEqualsZeroEliminationPass>()));
|
||||||
|
|
||||||
|
return optimizer.Run(inputBinary.data(), inputBinary.size(), &outputBinary);
|
||||||
|
}
|
||||||
|
|
||||||
Result<String> ShaderCompiler::DecompileShader(SpvcSession& session) {
|
Result<String> ShaderCompiler::DecompileShader(SpvcSession& session) {
|
||||||
spvc_compiler_options options;
|
spvc_compiler_options options;
|
||||||
session.CreateOptions(&options);
|
session.CreateOptions(&options);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace MobileGL {
|
|||||||
static Result<SharedPtr<glslang::TShader>> CompileShader(const ShaderAttrib& attrib);
|
static Result<SharedPtr<glslang::TShader>> CompileShader(const ShaderAttrib& attrib);
|
||||||
static Result<SharedPtr<glslang::TProgram>> LinkProgram(const ProgramAttrib& attrib);
|
static Result<SharedPtr<glslang::TProgram>> LinkProgram(const ProgramAttrib& attrib);
|
||||||
static Result<Vector<Vector<unsigned>>> GetSpirvBinaryFromProgram(const ProgramBinaryAttrib& attrib);
|
static Result<Vector<Vector<unsigned>>> GetSpirvBinaryFromProgram(const ProgramBinaryAttrib& attrib);
|
||||||
|
static bool SanitizeBinary(const Vector<Uint32>& inputBinary, Vector<uint32_t>& outputBinary);
|
||||||
static Result<String> DecompileShader(SpvcSession& session);
|
static Result<String> DecompileShader(SpvcSession& session);
|
||||||
};
|
};
|
||||||
} // namespace ShaderTranspiler
|
} // namespace ShaderTranspiler
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Util/ShaderTranspiler/FloatEqualsZeroEliminationPass.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "FloatEqualsZeroEliminationPass.h"
|
||||||
|
|
||||||
|
#include "spirv.hpp"
|
||||||
|
#include "source/opt/constants.h"
|
||||||
|
#include "source/opt/def_use_manager.h"
|
||||||
|
#include "source/opt/instruction.h"
|
||||||
|
#include "source/opt/ir_builder.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
#include "source/opt/module.h"
|
||||||
|
#include "source/opt/type_manager.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace MobileGL {
|
||||||
|
namespace MG_Util {
|
||||||
|
namespace ShaderTranspiler {
|
||||||
|
spvtools::opt::Pass::Status FloatEqualsZeroEliminationPass::Process() {
|
||||||
|
using namespace spvtools;
|
||||||
|
using namespace spvtools::opt;
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||||
|
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||||
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||||
|
|
||||||
|
// 2. Import `GLSL.std.450` extension ID (for abs() func)
|
||||||
|
uint32_t glsl_std_450_id = context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
|
||||||
|
if (glsl_std_450_id == 0) {
|
||||||
|
return Status::SuccessWithoutChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. iterate all function -> basic block -> insn
|
||||||
|
for (auto& func : *get_module()) {
|
||||||
|
for (auto& bb : func) {
|
||||||
|
for (auto itInst = bb.begin(); itInst != bb.end(); ++itInst) {
|
||||||
|
auto& inst = *itInst;
|
||||||
|
|
||||||
|
// Check if opcode is `OpFOrdEqual` or `OpFUnordEqual`
|
||||||
|
if (inst.opcode() != spv::Op::OpFOrdEqual &&
|
||||||
|
inst.opcode() != spv::Op::OpFUnordEqual) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if operand is "float 0.0"
|
||||||
|
// OpFOrdEqual ResultType ResultID Operand1 Operand2
|
||||||
|
uint32_t op1_id = inst.GetSingleWordInOperand(0);
|
||||||
|
uint32_t op2_id = inst.GetSingleWordInOperand(1);
|
||||||
|
|
||||||
|
uint32_t var_id = 0;
|
||||||
|
|
||||||
|
auto is_float_zero = [&](uint32_t id) -> bool {
|
||||||
|
const analysis::Constant* c = const_mgr->FindDeclaredConstant(id);
|
||||||
|
if (c && c->AsFloatConstant() && fabs(c->AsFloatConstant()->GetFloat()) <= K_EPSILON) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_float_zero(op2_id)) {
|
||||||
|
var_id = op1_id; // x == 0.0
|
||||||
|
} else if (is_float_zero(op1_id)) {
|
||||||
|
var_id = op2_id; // 0.0 == x
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Found it, continue to patch it ---
|
||||||
|
|
||||||
|
// 1. Get var type (Float) and result type (Bool)
|
||||||
|
uint32_t float_type_id = def_use_mgr->GetDef(var_id)->type_id();
|
||||||
|
uint32_t bool_type_id = inst.type_id();
|
||||||
|
|
||||||
|
// 2. Create constant ID for `Epsilon`
|
||||||
|
const analysis::Constant* eps_const = const_mgr->GetConstant(
|
||||||
|
type_mgr->GetType(float_type_id),
|
||||||
|
{*(reinterpret_cast<const uint32_t*>(&K_EPSILON))}
|
||||||
|
);
|
||||||
|
uint32_t eps_id = const_mgr->GetDefiningInstruction(eps_const)->result_id();
|
||||||
|
|
||||||
|
// 3. Build Abs(x) inst
|
||||||
|
// OpExtInst %float_type %glsl_import Abs %x
|
||||||
|
InstructionBuilder builder(
|
||||||
|
context(),
|
||||||
|
&inst,
|
||||||
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<Operand> abs_operands;
|
||||||
|
abs_operands.push_back({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {glsl_std_450_id}});
|
||||||
|
abs_operands.push_back({spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {4}}); // 4 is FAbs
|
||||||
|
abs_operands.push_back({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}});
|
||||||
|
|
||||||
|
// In GLSL.std.450, `FAbs`'s OpCode == 4
|
||||||
|
// Ref: https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html
|
||||||
|
Instruction* abs_inst = builder.AddInstruction(MakeUnique<Instruction>(
|
||||||
|
context(),
|
||||||
|
spv::Op::OpExtInst,
|
||||||
|
float_type_id,
|
||||||
|
context()->TakeNextId(),
|
||||||
|
abs_operands
|
||||||
|
));
|
||||||
|
|
||||||
|
// 4. build Abs(x) < Epsilon
|
||||||
|
// OpFOrdLessThan %bool_type %abs_val %eps
|
||||||
|
std::vector<Operand> less_operands;
|
||||||
|
less_operands.push_back({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {abs_inst->result_id()}});
|
||||||
|
less_operands.push_back({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {eps_id}});
|
||||||
|
|
||||||
|
Instruction* less_than_inst = builder.AddInstruction(MakeUnique<Instruction>(
|
||||||
|
context(),
|
||||||
|
spv::Op::OpFOrdLessThan,
|
||||||
|
bool_type_id,
|
||||||
|
context()->TakeNextId(),
|
||||||
|
less_operands
|
||||||
|
));
|
||||||
|
|
||||||
|
// 5. Replaces all uses of old insn with new one
|
||||||
|
context()->ReplaceAllUsesWith(inst.result_id(), less_than_inst->result_id());
|
||||||
|
|
||||||
|
// 6. Kill old instruction (will be cleaned up by DCE later)
|
||||||
|
auto nextInstIt = context()->KillInst(&inst);
|
||||||
|
if (nextInstIt) {
|
||||||
|
itInst = nextInstIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Util/ShaderTranspiler/FloatEqualsZeroEliminationPass.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "source/opt/pass.h"
|
||||||
|
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL {
|
||||||
|
namespace MG_Util {
|
||||||
|
namespace ShaderTranspiler {
|
||||||
|
class FloatEqualsZeroEliminationPass: public spvtools::opt::Pass {
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "float-equals-zero-elimination"; }
|
||||||
|
Status Process() override;
|
||||||
|
private:
|
||||||
|
const float K_EPSILON = 0.0001f;
|
||||||
|
};
|
||||||
|
} // namespace ShaderTranspiler
|
||||||
|
} // namespace MG_Util
|
||||||
|
} // namespace MobileGL
|
||||||
Reference in New Issue
Block a user