Skip to content

Commit

Permalink
Add -fnan-clamp (google#725)
Browse files Browse the repository at this point in the history
Tells the compiler to generate code for max and min builtins so that
when given a NaN operand, the builtin returns the other operand.
Similarly, the clamp builtin will favour the non-NaN operands, as
if clamp were implemented as a composition of max and min.

Requires Glslang commit a9b00ac5d58878c6e67cb50c2af93c170648d6c9
  • Loading branch information
dneto0 committed Jun 19, 2019
1 parent 6c4c6ed commit 2cbf790
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 1 deletion.
5 changes: 4 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
Revision history for Shaderc

v2019.1-dev 2019-06-04
- Start v2019.1-dev
- Add -fnan-clamp: Generate code for max and min builtins so that,
given a NaN operand, will return the other operand. Similarly, the
clamp builtin favours non-NaN operands, as if clamp was implemented
as the composition of max and min.

v2019.0 2019-06-04
- Add optional spvc, libshaderc_spvc as wrapper around SPIRV-Cross:
Expand Down
6 changes: 6 additions & 0 deletions glslc/src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ An input file of - represents standard input.
several times, only the last setting takes effect.
-flimit-file <file>
Set limits as specified in the given file.
-fnan-clamp Generate code for max and min builtins so that, when given
a NaN operand, the other operand is returned. Similarly,
the clamp builtin will favour the non-NaN operands, as if
clamp were implemented as a composition of max and min.
-fresource-set-binding [stage] <reg0> <set0> <binding0>
[<reg1> <set1> <binding1>...]
Explicitly sets the descriptor set and binding for
Expand Down Expand Up @@ -299,6 +303,8 @@ int main(int argc, char** argv) {
compiler.options().SetHlslFunctionality1(true);
} else if (arg == "-finvert-y") {
compiler.options().SetInvertY(true);
} else if (arg == "-fnan-clamp") {
compiler.options().SetNanClamp(true);
} else if (((u_kind = shaderc_uniform_kind_image),
(arg == "-fimage-binding-base")) ||
((u_kind = shaderc_uniform_kind_texture),
Expand Down
69 changes: 69 additions & 0 deletions glslc/test/option_dash_fnan_clamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2019 The Shaderc Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import expect
from glslc_test_framework import inside_glslc_testsuite
from placeholder import FileShader

# A GLSL shader using the clamp, max, and min builtin functions.
GLSL_FRAG_SHADER_WITH_CLAMP = """#version 450
layout(location=0) in vec4 i;
layout(location=0) out vec4 o;
void main() {
o = clamp(i, vec4(0.5), vec4(1.0))
+ max(i, vec4(0.5))
+ min(i, vec4(0.5));
}
"""


@inside_glslc_testsuite('OptionFNanClamp')
class TestClampMapsToFClampByDefault(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 FClamp'


@inside_glslc_testsuite('OptionFNanClamp')
class TestMaxMapsToFMaxByDefault(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 FMax'


@inside_glslc_testsuite('OptionFNanClamp')
class TestMinMapsToFMinByDefault(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 FMin'


@inside_glslc_testsuite('OptionFNanClamp')
class TestClampMapsToNClampWithFlag(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', '-fnan-clamp', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 NClamp'

@inside_glslc_testsuite('OptionFNanClamp')
class TestMaxMapsToNMaxWithFlag(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', '-fnan-clamp', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 NMax'


@inside_glslc_testsuite('OptionFNanClamp')
class TestMinMapsToNMinWithFlag(expect.ValidAssemblyFileWithSubstr):
shader = FileShader(GLSL_FRAG_SHADER_WITH_CLAMP, '.frag')
glslc_args = ['-S', '-fnan-clamp', shader]
expected_assembly_substr = 'OpExtInst %v4float %1 NMin'
4 changes: 4 additions & 0 deletions glslc/test/parameter_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class HelpParameters(
several times, only the last setting takes effect.
-flimit-file <file>
Set limits as specified in the given file.
-fnan-clamp Generate code for max and min builtins so that, when given
a NaN operand, the other operand is returned. Similarly,
the clamp builtin will favour the non-NaN operands, as if
clamp were implemented as a composition of max and min.
-fresource-set-binding [stage] <reg0> <set0> <binding0>
[<reg1> <set1> <binding1>...]
Explicitly sets the descriptor set and binding for
Expand Down
7 changes: 7 additions & 0 deletions libshaderc/include/shaderc/shaderc.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,13 @@ SHADERC_EXPORT void shaderc_compile_options_set_hlsl_functionality1(
SHADERC_EXPORT void shaderc_compile_options_set_invert_y(
shaderc_compile_options_t options, bool enable);

// Sets whether the compiler generates code for max and min builtins which,
// if given a NaN operand, will return the other operand. Similarly, the clamp
// builtin will favour the non-NaN operands, as if clamp were implemented
// as a composition of max and min.
SHADERC_EXPORT void shaderc_compile_options_set_nan_clamp(
shaderc_compile_options_t options, bool enable);

// An opaque handle to the results of a call to any shaderc_compile_into_*()
// function.
typedef struct shaderc_compilation_result* shaderc_compilation_result_t;
Expand Down
8 changes: 8 additions & 0 deletions libshaderc/include/shaderc/shaderc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,14 @@ class CompileOptions {
shaderc_compile_options_set_invert_y(options_, enable);
}

// Sets whether the compiler should generates code for max an min which,
// if given a NaN operand, will return the other operand. Similarly, the
// clamp builtin will favour the non-NaN operands, as if clamp were
// implemented as a composition of max and min.
void SetNanClamp(bool enable) {
shaderc_compile_options_set_nan_clamp(options_, enable);
}

private:
CompileOptions& operator=(const CompileOptions& other) = delete;
shaderc_compile_options_t options_;
Expand Down
7 changes: 7 additions & 0 deletions libshaderc/src/common_shaders_for_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ const char kHlslMemLayoutResourceSelect[] =
return Tex.Sample(samp, float2(0.5, 0.5)) + float4(a, b);
})";

const char kGlslShaderWithClamp[] =
R"(#version 450
layout(location=0) in vec4 i;
layout(location=0) out vec4 o;
void main() { o = clamp(i, vec4(0.5), vec4(1.0)); }
)";

#ifdef __cplusplus
}
#endif // __cplusplus
Expand Down
5 changes: 5 additions & 0 deletions libshaderc/src/shaderc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,11 @@ void shaderc_compile_options_set_invert_y(
options->compiler.EnableInvertY(enable);
}

void shaderc_compile_options_set_nan_clamp(shaderc_compile_options_t options,
bool enable) {
options->compiler.SetNanClamp(enable);
}

shaderc_compiler_t shaderc_compiler_initialize() {
static shaderc_util::GlslangInitializer* initializer =
new shaderc_util::GlslangInitializer;
Expand Down
24 changes: 24 additions & 0 deletions libshaderc/src/shaderc_cpp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1437,4 +1437,28 @@ TEST_F(CppInterface, HlslFunctionality1SurvivesCloning) {
EXPECT_THAT(disassembly_text, HasSubstr("OpDecorateString"));
}

TEST_F(CppInterface, NanClampDefaultsOff) {
CompileOptions options;
const std::string disassembly_text = AssemblyOutput(
kGlslShaderWithClamp, shaderc_glsl_fragment_shader, options);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 FClamp"));
}

TEST_F(CppInterface, NanClampMapsClampToNClamp) {
CompileOptions options;
options.SetNanClamp(true);
const std::string disassembly_text = AssemblyOutput(
kGlslShaderWithClamp, shaderc_glsl_fragment_shader, options);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp"));
}

TEST_F(CppInterface, NanClampSurvivesCloning) {
CompileOptions options;
options.SetNanClamp(true);
CompileOptions cloned_options(options);
const std::string disassembly_text = AssemblyOutput(
kGlslShaderWithClamp, shaderc_glsl_fragment_shader, cloned_options);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp"));
}

} // anonymous namespace
25 changes: 25 additions & 0 deletions libshaderc/src/shaderc_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1843,4 +1843,29 @@ TEST_F(CompileStringWithOptionsTest, HlslFlexibleMemoryLayoutAllowed) {
shaderc_fragment_shader, options_.get()));
}

TEST_F(CompileStringWithOptionsTest, ClampMapsToFClampByDefault) {
const std::string disassembly_text =
CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader,
options_.get(), OutputType::SpirvAssemblyText);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 FClamp"));
}

TEST_F(CompileStringWithOptionsTest, ClampMapsToNClampWithNanClamp) {
shaderc_compile_options_set_nan_clamp(options_.get(), true);
const std::string disassembly_text =
CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader,
options_.get(), OutputType::SpirvAssemblyText);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp"));
}

TEST_F(CompileStringWithOptionsTest, NanClampSurvivesCloning) {
shaderc_compile_options_set_nan_clamp(options_.get(), true);
compile_options_ptr cloned_options(
shaderc_compile_options_clone(options_.get()));
const std::string disassembly_text =
CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader,
cloned_options.get(), OutputType::SpirvAssemblyText);
EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp"));
}

} // anonymous namespace
13 changes: 13 additions & 0 deletions libshaderc_util/include/libshaderc_util/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class Compiler {
hlsl_legalization_enabled_(true),
hlsl_functionality1_enabled_(false),
invert_y_enabled_(false),
nan_clamp_(false),
hlsl_explicit_bindings_() {}

// Requests that the compiler place debug information into the object code,
Expand All @@ -251,6 +252,12 @@ class Compiler {
// Enables or disables invert position.Y output in vertex shader.
void EnableInvertY(bool enable);

// Sets whether the compiler generates code for max and min builtins which,
// if given a NaN operand, will return the other operand. Also, the clamp
// builtin will favour the non-NaN operands, as if clamp were implemented
// as a composition of max and min.
void SetNanClamp(bool enable);

// When a warning is encountered it treat it as an error.
void SetWarningsAsErrors();

Expand Down Expand Up @@ -525,6 +532,12 @@ class Compiler {
// True if the compiler should invert position.Y output in vertex shader.
bool invert_y_enabled_;

// True if the compiler generates code for max and min builtins which,
// if given a NaN operand, will return the other operand. Also, the clamp
// builtin will favour the non-NaN operands, as if clamp were implemented
// as a composition of max and min.
bool nan_clamp_;

// A sequence of triples, each triple representing a specific HLSL register
// name, and the set and binding numbers it should be mapped to, but in
// the form of strings. This is how Glslang wants to consume the data.
Expand Down
4 changes: 4 additions & 0 deletions libshaderc_util/src/compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ std::tuple<bool, std::vector<uint32_t>, size_t> Compiler::Compile(
shader.setEnvTargetHlslFunctionality1();
}
shader.setInvertY(invert_y_enabled_);
shader.setNanMinMaxClamp(nan_clamp_);

const EShMessages rules = GetMessageRules(target_env_, source_language_,
hlsl_offsets_,
Expand Down Expand Up @@ -454,6 +455,8 @@ void Compiler::EnableInvertY(bool enable) {
invert_y_enabled_ = enable;
}

void Compiler::SetNanClamp(bool enable) { nan_clamp_ = enable; }

void Compiler::SetSuppressWarnings() { suppress_warnings_ = true; }

std::tuple<bool, std::string, std::string> Compiler::PreprocessShader(
Expand Down Expand Up @@ -487,6 +490,7 @@ std::tuple<bool, std::string, std::string> Compiler::PreprocessShader(
shader.setEnvTargetHlslFunctionality1();
}
shader.setInvertY(invert_y_enabled_);
shader.setNanMinMaxClamp(nan_clamp_);

// The preprocessor might be sensitive to the target environment.
// So combine the existing rules with the just-give-me-preprocessor-output
Expand Down
23 changes: 23 additions & 0 deletions libshaderc_util/src/compiler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ float4 main() : SV_Target0 {
}
)";

const char kGlslShaderWithClamp[] = R"(#version 450
layout(location=0) in vec4 i;
layout(location=0) out vec4 o;
void main() { o = clamp(i, vec4(0.5), vec4(1.0)); }
)";

// Returns the disassembly of the given SPIR-V binary, as a string.
// Assumes the disassembly will be successful when targeting Vulkan.
std::string Disassemble(const std::vector<uint32_t> binary) {
Expand Down Expand Up @@ -741,4 +747,21 @@ TEST_F(CompilerTest, HlslFunctionality1Enabled) {
<< disassembly;
}

TEST_F(CompilerTest, ClampMapsToFClampByDefault) {
const auto words =
SimpleCompilationBinary(kGlslShaderWithClamp, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpExtInst %v4float %1 FClamp"))
<< disassembly;
}

TEST_F(CompilerTest, ClampMapsToFClampWithNanClamp) {
compiler_.SetNanClamp(true);
const auto words =
SimpleCompilationBinary(kGlslShaderWithClamp, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpExtInst %v4float %1 NClamp"))
<< disassembly;
}

} // anonymous namespace

0 comments on commit 2cbf790

Please sign in to comment.