blob: 1dfbb6db169d236f97b35d678398b3e9e774d8f3 [file] [log] [blame]
// Copyright 2015 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.
#include "libshaderc_util/compiler.h"
#include <sstream>
#include <gmock/gmock.h>
#include "death_test.h"
#include "libshaderc_util/counting_includer.h"
#include "libshaderc_util/spirv_tools_wrapper.h"
namespace {
using shaderc_util::Compiler;
using shaderc_util::GlslangClientInfo;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
// A trivial vertex shader
const char kVertexShader[] =
"#version 140\n"
"void main() {}";
// A shader that parses under OpenGL compatibility profile rules.
// It does not compile because Glslang does not support SPIR-V
// code generation for OpenGL compatibility profile.
const char kOpenGLCompatibilityFragShader[] =
R"(#version 140
uniform highp sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, vec2(0.0,0.0));
})";
// A shader that compiles under OpenGL core profile rules.
const char kOpenGLVertexShader[] =
R"(#version 330
void main() { int t = gl_VertexID; })";
// A shader that compiles under OpenGL core profile rules, even when
// deducing the stage.
const char kOpenGLVertexShaderDeducibleStage[] =
R"(#version 330
#pragma shader_stage(vertex)
void main() { int t = gl_VertexID; })";
// A shader that compiles under Vulkan rules.
// See the GL_KHR_vuklan_glsl extension to GLSL.
const char kVulkanVertexShader[] =
R"(#version 310 es
void main() { int t = gl_VertexIndex; })";
// A shader that needs valueless macro predefinition E, to be compiled
// successfully.
const std::string kValuelessPredefinitionShader =
"#version 140\n"
"#ifdef E\n"
"void main(){}\n"
"#else\n"
"#error\n"
"#endif";
// An HLSL vertex shader.
const char kHlslVertexShader[] =
R"(float4 EntryPoint(uint index : SV_VERTEXID) : SV_POSITION
{ return float4(1.0, 2.0, 3.0, 4.0); })";
// A GLSL fragment shader without bindings for its uniforms.
// This also can be compiled as a vertex or compute shader.
const char kGlslFragShaderNoExplicitBinding[] =
R"(#version 450
#extension GL_ARB_sparse_texture2: enable
uniform texture2D my_tex;
uniform sampler my_sam;
layout(rgba32f) uniform image2D my_img;
layout(rgba32f) uniform imageBuffer my_imbuf;
uniform block { float x; float y; } my_ubo;
void main() {
texture(sampler2D(my_tex,my_sam),vec2(1.0));
vec4 t = vec4(1.0);
sparseImageLoadARB(my_img,ivec2(0),t);
imageLoad(my_imbuf,2);
float x = my_ubo.x;
})";
// A GLSL vertex shader with the location defined for its non-opaque uniform
// variable.
const char kGlslVertShaderExplicitLocation[] =
R"(#version 450
layout(location = 10) uniform mat4 my_mat;
layout(location = 0) in vec4 my_vec;
void main(void) {
gl_Position = my_mat * my_vec;
})";
// A GLSL vertex shader without the location defined for its non-opaque uniform
// variable.
const char kGlslVertShaderNoExplicitLocation[] =
R"(#version 450
uniform mat4 my_mat;
layout(location = 0) in vec4 my_vec;
void main(void) {
gl_Position = my_mat * my_vec;
})";
// A GLSL vertex shader with a weirdly packed block.
const char kGlslShaderWeirdPacking[] =
R"(#version 450
layout(set = 0, binding = 0)
buffer B { float x; vec3 foo; } my_ssbo;
void main() { my_ssbo.x = 1.0; })";
const char kHlslShaderForLegalizationTest[] = R"(
struct CombinedTextureSampler {
Texture2D tex;
SamplerState sampl;
};
float4 sampleTexture(CombinedTextureSampler c, float2 loc) {
return c.tex.Sample(c.sampl, loc);
};
[[vk::binding(0,0)]]
Texture2D gTex;
[[vk::binding(0,1)]]
SamplerState gSampler;
float4 main(float2 loc: A) : SV_Target {
CombinedTextureSampler cts;
cts.tex = gTex;
cts.sampl = gSampler;
return sampleTexture(cts, loc);
})";
const char kHlslShaderWithCounterBuffer[] = R"(
[[vk::binding(0,0)]]
RWStructuredBuffer<float4> Ainc;
float4 main() : SV_Target0 {
return float4(Ainc.IncrementCounter(), 0, 0, 0);
}
)";
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) {
std::string result;
shaderc_util::SpirvToolsDisassemble(Compiler::TargetEnv::Vulkan,
Compiler::TargetEnvVersion::Vulkan_1_2,
binary, &result);
return result;
}
// A CountingIncluder that never returns valid content for a requested
// file inclusion.
class DummyCountingIncluder : public shaderc_util::CountingIncluder {
private:
// Returns a pair of empty strings.
virtual glslang::TShader::Includer::IncludeResult* include_delegate(
const char*, const char*, IncludeType, size_t) override {
return nullptr;
}
virtual void release_delegate(
glslang::TShader::Includer::IncludeResult*) override {}
};
// A test fixture for compiling GLSL shaders.
class CompilerTest : public testing::Test {
public:
// Returns true if the given compiler successfully compiles the given shader
// source for the given shader stage to the specified output type. No
// includes are permitted, and shader stage deduction falls back to an invalid
// shader stage.
bool SimpleCompilationSucceedsForOutputType(
std::string source, EShLanguage stage, Compiler::OutputType output_type) {
shaderc_util::GlslangInitializer initializer;
std::stringstream errors;
size_t total_warnings = 0;
size_t total_errors = 0;
bool result = false;
DummyCountingIncluder dummy_includer;
std::tie(result, std::ignore, std::ignore) = compiler_.Compile(
source, stage, "shader", "main", dummy_stage_callback_, dummy_includer,
Compiler::OutputType::SpirvBinary, &errors, &total_warnings,
&total_errors);
errors_ = errors.str();
return result;
}
// Returns the result of SimpleCompilationSucceedsForOutputType, where
// the output type is a SPIR-V binary module.
bool SimpleCompilationSucceeds(std::string source, EShLanguage stage) {
return SimpleCompilationSucceedsForOutputType(
source, stage, Compiler::OutputType::SpirvBinary);
}
// Returns the SPIR-V binary for a successful compilation of a shader.
std::vector<uint32_t> SimpleCompilationBinary(std::string source,
EShLanguage stage) {
shaderc_util::GlslangInitializer initializer;
std::stringstream errors;
size_t total_warnings = 0;
size_t total_errors = 0;
bool result = false;
DummyCountingIncluder dummy_includer;
std::vector<uint32_t> words;
std::tie(result, words, std::ignore) = compiler_.Compile(
source, stage, "shader", "main", dummy_stage_callback_, dummy_includer,
Compiler::OutputType::SpirvBinary, &errors, &total_warnings,
&total_errors);
errors_ = errors.str();
EXPECT_TRUE(result) << errors_;
return words;
}
protected:
Compiler compiler_;
// The error string from the most recent compilation.
std::string errors_;
std::function<EShLanguage(std::ostream*, const shaderc_util::string_piece&)>
dummy_stage_callback_ =
[](std::ostream*, const shaderc_util::string_piece&) {
return EShLangCount;
};
};
TEST_F(CompilerTest, SimpleVertexShaderCompilesSuccessfullyToBinary) {
EXPECT_TRUE(SimpleCompilationSucceeds(kVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, SimpleVertexShaderCompilesSuccessfullyToAssembly) {
EXPECT_TRUE(SimpleCompilationSucceedsForOutputType(
kVertexShader, EShLangVertex, Compiler::OutputType::SpirvAssemblyText));
}
TEST_F(CompilerTest, SimpleVertexShaderPreprocessesSuccessfully) {
EXPECT_TRUE(SimpleCompilationSucceedsForOutputType(
kVertexShader, EShLangVertex, Compiler::OutputType::PreprocessedText));
}
TEST_F(CompilerTest, BadVertexShaderFailsCompilation) {
EXPECT_FALSE(SimpleCompilationSucceeds(" bogus ", EShLangVertex));
}
TEST_F(CompilerTest, SimpleVulkanShaderCompilesWithDefaultCompilerSettings) {
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, OpenGLCompatibilityProfileNotSupported) {
const EShLanguage stage = EShLangVertex;
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGLCompat);
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLVertexShader, stage));
EXPECT_EQ(errors_, "error: OpenGL compatibility profile is not supported");
}
TEST_F(CompilerTest, RespectTargetEnvOnOpenGLShaderForOpenGLShader) {
const EShLanguage stage = EShLangVertex;
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
EXPECT_TRUE(SimpleCompilationSucceeds(kOpenGLVertexShader, stage));
}
TEST_F(CompilerTest, RespectTargetEnvOnOpenGLShaderWhenDeducingStage) {
const EShLanguage stage = EShLangVertex;
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
EXPECT_TRUE(
SimpleCompilationSucceeds(kOpenGLVertexShaderDeducibleStage, stage));
}
TEST_F(CompilerTest, RespectTargetEnvOnVulkanShader) {
compiler_.SetTargetEnv(Compiler::TargetEnv::Vulkan);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, VulkanSpecificShaderFailsUnderOpenGLCompatibilityRules) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGLCompat);
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, VulkanSpecificShaderFailsUnderOpenGLRules) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, OpenGLSpecificShaderFailsUnderDefaultRules) {
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLVertexShader, EShLangVertex));
}
TEST_F(CompilerTest,
OpenGLCompatibilitySpecificShaderFailsUnderOpenGLCompatibilityRules) {
// OpenGLCompat mode now errors out. It's been deprecated for a long time.
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGLCompat);
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLCompatibilityFragShader,
EShLangFragment));
}
TEST_F(CompilerTest, OpenGLCompatibilitySpecificShaderFailsUnderOpenGLRules) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLCompatibilityFragShader,
EShLangFragment));
}
TEST_F(CompilerTest, OpenGLCompatibilitySpecificShaderFailsUnderVulkanRules) {
compiler_.SetTargetEnv(Compiler::TargetEnv::Vulkan);
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLCompatibilityFragShader,
EShLangFragment));
}
TEST_F(CompilerTest, OpenGLSpecificShaderFailsUnderVulkanRules) {
compiler_.SetTargetEnv(Compiler::TargetEnv::Vulkan);
EXPECT_FALSE(SimpleCompilationSucceeds(kOpenGLVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, BadTargetEnvFails) {
compiler_.SetTargetEnv(static_cast<Compiler::TargetEnv>(32767));
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, HasSubstr("Invalid target client environment 32767"));
}
TEST_F(CompilerTest, BadTargetEnvVulkanVersionFails) {
compiler_.SetTargetEnv(Compiler::TargetEnv::Vulkan,
static_cast<Compiler::TargetEnvVersion>(123));
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_,
HasSubstr("Invalid target client version 123 for Vulkan environment 0"));
}
TEST_F(CompilerTest, BadTargetEnvOpenGLVersionFails) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL,
static_cast<Compiler::TargetEnvVersion>(123));
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_,
HasSubstr("Invalid target client version 123 for OpenGL environment 1"));
}
TEST_F(CompilerTest, SpirvTargetVersion1_0Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_0);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetVersion1_1Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_1);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetVersion1_2Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_2);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetVersion1_3Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_3);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetVersion1_4Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_4);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetVersion1_5Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_5);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, Eq(""));
}
TEST_F(CompilerTest, SpirvTargetBadVersionFails) {
compiler_.SetTargetSpirv(static_cast<Compiler::SpirvVersion>(0x090900));
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
EXPECT_THAT(errors_, HasSubstr(": Unknown SPIR-V version 90900"));
}
TEST_F(CompilerTest, AddMacroDefinition) {
const std::string kMinimalExpandedShader = "#version 140\nvoid E(){}";
compiler_.AddMacroDefinition("E", 1u, "main", 4u);
EXPECT_TRUE(SimpleCompilationSucceeds(kMinimalExpandedShader, EShLangVertex));
}
TEST_F(CompilerTest, AddValuelessMacroDefinitionNullPointer) {
compiler_.AddMacroDefinition("E", 1u, nullptr, 100u);
EXPECT_TRUE(
SimpleCompilationSucceeds(kValuelessPredefinitionShader, EShLangVertex));
}
TEST_F(CompilerTest, AddValuelessMacroDefinitionZeroLength) {
compiler_.AddMacroDefinition("E", 1u, "something", 0u);
EXPECT_TRUE(
SimpleCompilationSucceeds(kValuelessPredefinitionShader, EShLangVertex));
}
TEST_F(CompilerTest, AddMacroDefinitionNotNullTerminated) {
const std::string kMinimalExpandedShader = "#version 140\nvoid E(){}";
compiler_.AddMacroDefinition("EFGH", 1u, "mainnnnnn", 4u);
EXPECT_TRUE(SimpleCompilationSucceeds(kMinimalExpandedShader, EShLangVertex));
}
// A convert-string-to-vector test case consists of 1) an input string; 2) an
// expected vector after the conversion.
struct ConvertStringToVectorTestCase {
std::string input_str;
std::vector<uint32_t> expected_output_vec;
};
// Test the shaderc_util::ConvertStringToVector() function. The content of the
// input string, including the null terminator, should be packed into uint32_t
// cells and stored in the returned vector of uint32_t. In case extra bytes are
// required to complete the ending uint32_t element, bytes with value 0x00
// should be used to fill the space.
using ConvertStringToVectorTestFixture =
testing::TestWithParam<ConvertStringToVectorTestCase>;
TEST_P(ConvertStringToVectorTestFixture, VariousStringSize) {
const ConvertStringToVectorTestCase& test_case = GetParam();
EXPECT_EQ(test_case.expected_output_vec,
shaderc_util::ConvertStringToVector(test_case.input_str))
<< "test_case.input_str: " << test_case.input_str << std::endl;
}
INSTANTIATE_TEST_SUITE_P(
ConvertStringToVectorTest, ConvertStringToVectorTestFixture,
testing::ValuesIn(std::vector<ConvertStringToVectorTestCase>{
{"", {0x00000000}},
{"1", {0x00000031}},
{"12", {0x00003231}},
{"123", {0x00333231}},
{"1234", {0x34333231, 0x00000000}},
{"12345", {0x34333231, 0x00000035}},
{"123456", {0x34333231, 0x00003635}},
{"1234567", {0x34333231, 0x00373635}},
{"12345678", {0x34333231, 0x38373635, 0x00000000}},
{"123456789", {0x34333231, 0x38373635, 0x00000039}},
}));
TEST_F(CompilerTest, SetSourceLanguageToGLSLSucceeds) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::GLSL);
EXPECT_TRUE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, SetSourceLanguageToGLSLFailsOnHLSL) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::GLSL);
EXPECT_FALSE(SimpleCompilationSucceeds(kHlslVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, SetSourceLanguageToHLSLSucceeds) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
EXPECT_TRUE(SimpleCompilationSucceeds(kHlslVertexShader, EShLangVertex))
<< errors_;
}
TEST_F(CompilerTest, SetSourceLanguageToHLSLFailsOnGLSL) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
EXPECT_FALSE(SimpleCompilationSucceeds(kVulkanVertexShader, EShLangVertex));
}
TEST_F(CompilerTest, EntryPointParameterTakesEffectForHLSL) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
std::stringstream errors;
size_t total_warnings = 0;
size_t total_errors = 0;
shaderc_util::GlslangInitializer initializer;
bool result = false;
DummyCountingIncluder dummy_includer;
std::vector<uint32_t> words;
std::tie(result, words, std::ignore) =
compiler_.Compile(kHlslVertexShader, EShLangVertex, "shader",
"EntryPoint", dummy_stage_callback_, dummy_includer,
Compiler::OutputType::SpirvAssemblyText, &errors,
&total_warnings, &total_errors);
EXPECT_TRUE(result);
std::string assembly(reinterpret_cast<char*>(words.data()));
EXPECT_THAT(assembly,
HasSubstr("OpEntryPoint Vertex %EntryPoint \"EntryPoint\""))
<< assembly;
}
// A test case for setting resource limits.
struct SetLimitCase {
Compiler::Limit limit;
int default_value;
int value;
};
using LimitTest = testing::TestWithParam<SetLimitCase>;
TEST_P(LimitTest, Sample) {
Compiler compiler;
EXPECT_THAT(compiler.GetLimit(GetParam().limit),
Eq(GetParam().default_value));
compiler.SetLimit(GetParam().limit, GetParam().value);
EXPECT_THAT(compiler.GetLimit(GetParam().limit), Eq(GetParam().value));
}
#define CASE(LIMIT, DEFAULT, NEW) \
{ Compiler::Limit::LIMIT, DEFAULT, NEW }
INSTANTIATE_TEST_SUITE_P(
CompilerTest, LimitTest,
// See resources.cc for the defaults.
testing::ValuesIn(std::vector<SetLimitCase>{
// clang-format off
// This is just a sampling of the possible values.
CASE(MaxLights, 8, 99),
CASE(MaxClipPlanes, 6, 10929),
CASE(MaxTessControlAtomicCounters, 0, 72),
CASE(MaxSamples, 4, 8),
// clang-format on
}));
#undef CASE
// Returns a fragment shader accessing a texture with the given
// offset.
std::string ShaderWithTexOffset(int offset) {
std::ostringstream oss;
oss << "#version 450\n"
"layout (binding=0) uniform sampler1D tex;\n"
"void main() { vec4 x = textureOffset(tex, 1.0, "
<< offset << "); }\n";
return oss.str();
}
// Ensure compilation is sensitive to limit setting. Sample just
// two particular limits. The default minimum texel offset is -8,
// and the default maximum texel offset is 7.
TEST_F(CompilerTest, TexelOffsetDefaults) {
const EShLanguage stage = EShLangFragment;
EXPECT_FALSE(SimpleCompilationSucceeds(ShaderWithTexOffset(-9), stage));
EXPECT_TRUE(SimpleCompilationSucceeds(ShaderWithTexOffset(-8), stage));
EXPECT_TRUE(SimpleCompilationSucceeds(ShaderWithTexOffset(7), stage));
EXPECT_FALSE(SimpleCompilationSucceeds(ShaderWithTexOffset(8), stage));
}
TEST_F(CompilerTest, TexelOffsetLowerTheMinimum) {
const EShLanguage stage = EShLangFragment;
compiler_.SetLimit(Compiler::Limit::MinProgramTexelOffset, -99);
EXPECT_FALSE(SimpleCompilationSucceeds(ShaderWithTexOffset(-100), stage));
EXPECT_TRUE(SimpleCompilationSucceeds(ShaderWithTexOffset(-99), stage));
}
TEST_F(CompilerTest, TexelOffsetRaiseTheMaximum) {
const EShLanguage stage = EShLangFragment;
compiler_.SetLimit(Compiler::Limit::MaxProgramTexelOffset, 100);
EXPECT_TRUE(SimpleCompilationSucceeds(ShaderWithTexOffset(100), stage));
EXPECT_FALSE(SimpleCompilationSucceeds(ShaderWithTexOffset(101), stage));
}
TEST_F(CompilerTest, GeneratorWordIsShadercOverGlslang) {
const auto words = SimpleCompilationBinary(kVertexShader, EShLangVertex);
const uint32_t shaderc_over_glslang = 13; // From SPIR-V XML Registry
const uint32_t generator_word_index = 2; // From SPIR-V binary layout
EXPECT_EQ(shaderc_over_glslang, words[generator_word_index] >> 16u);
}
TEST_F(CompilerTest, NoBindingsAndNoAutoMapBindingsFailsCompile) {
compiler_.SetAutoBindUniforms(false);
EXPECT_FALSE(SimpleCompilationSucceeds(kGlslFragShaderNoExplicitBinding,
EShLangFragment));
EXPECT_THAT(errors_,
HasSubstr("sampler/texture/image requires layout(binding=X)"));
}
TEST_F(CompilerTest, AutoMapBindingsSetsBindings) {
compiler_.SetAutoBindUniforms(true);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 0"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 3"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 4"));
}
TEST_F(CompilerTest, SetBindingBaseForTextureAdjustsTextureBindingsOnly) {
compiler_.SetAutoBindUniforms(true);
compiler_.SetAutoBindingBase(Compiler::UniformKind::Texture, 42);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 42"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 0"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 3"));
}
TEST_F(CompilerTest, SetBindingBaseForSamplersAdjustsSamplerBindingsOnly) {
compiler_.SetAutoBindUniforms(true);
compiler_.SetAutoBindingBase(Compiler::UniformKind::Sampler, 42);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 0"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 42"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 3"));
}
TEST_F(CompilerTest, SetBindingBaseForImagesAdjustsImageBindingsOnly) {
compiler_.SetAutoBindUniforms(true);
compiler_.SetAutoBindingBase(Compiler::UniformKind::Image, 42);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 0"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 42"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 43"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 2"));
}
TEST_F(CompilerTest, SetBindingBaseForBufferAdjustsBufferBindingsOnly) {
compiler_.SetAutoBindUniforms(true);
compiler_.SetAutoBindingBase(Compiler::UniformKind::Buffer, 42);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 0"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 3"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 42"));
}
TEST_F(CompilerTest,
AutoMapBindingsSetsBindingsSetFragTextureBindingBaseCompiledAsFrag) {
compiler_.SetAutoBindUniforms(true);
compiler_.SetAutoBindingBaseForStage(Compiler::Stage::Fragment,
Compiler::UniformKind::Texture, 100);
const auto words = SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding,
EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 100"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 0"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 3"));
}
TEST_F(CompilerTest,
AutoMapBindingsSetsBindingsSetFragImageBindingBaseCompiledAsVert) {
compiler_.SetAutoBindUniforms(true);
// This is ignored because we're compiling the shader as a vertex shader, not
// as a fragment shader.
compiler_.SetAutoBindingBaseForStage(Compiler::Stage::Fragment,
Compiler::UniformKind::Image, 100);
const auto words =
SimpleCompilationBinary(kGlslFragShaderNoExplicitBinding, EShLangVertex);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_tex Binding 0"))
<< disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_sam Binding 1"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_img Binding 2"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_imbuf Binding 3"));
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_ubo Binding 4"));
}
TEST_F(CompilerTest, NoAutoMapLocationsFailsCompilationOnOpenGLShader) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
compiler_.SetAutoMapLocations(false);
const auto words =
SimpleCompilationBinary(kGlslVertShaderExplicitLocation, EShLangVertex);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpDecorate %my_mat Location 10"))
<< disassembly;
EXPECT_FALSE(SimpleCompilationSucceeds(kGlslVertShaderNoExplicitLocation,
EShLangVertex));
}
TEST_F(CompilerTest, AutoMapLocationsSetsLocationsOnOpenGLShader) {
compiler_.SetTargetEnv(Compiler::TargetEnv::OpenGL);
compiler_.SetAutoMapLocations(true);
const auto words_no_auto =
SimpleCompilationBinary(kGlslVertShaderExplicitLocation, EShLangVertex);
const auto disassembly_no_auto = Disassemble(words_no_auto);
EXPECT_THAT(disassembly_no_auto, HasSubstr("OpDecorate %my_mat Location 10"))
<< disassembly_no_auto;
const auto words_auto =
SimpleCompilationBinary(kGlslVertShaderNoExplicitLocation, EShLangVertex);
const auto disassembly_auto = Disassemble(words_auto);
EXPECT_THAT(disassembly_auto, HasSubstr("OpDecorate %my_mat Location 0"))
<< disassembly_auto;
}
TEST_F(CompilerTest, EmitMessageTextOnlyOnce) {
// Emit a warning by compiling a shader without a default entry point name.
// The warning should only be emitted once even though we do parsing, linking,
// and IO mapping.
Compiler c;
std::stringstream errors;
size_t total_warnings = 0;
size_t total_errors = 0;
shaderc_util::GlslangInitializer initializer;
bool result = false;
DummyCountingIncluder dummy_includer;
std::tie(result, std::ignore, std::ignore) = c.Compile(
"#version 150\nvoid MyEntryPoint(){}", EShLangVertex, "shader", "",
dummy_stage_callback_, dummy_includer, Compiler::OutputType::SpirvBinary,
&errors, &total_warnings, &total_errors);
const std::string errs = errors.str();
EXPECT_THAT(errs, Eq("shader: error: Linking vertex stage: Missing entry "
"point: Each stage requires one entry point\n"))
<< errs;
}
TEST_F(CompilerTest, GlslDefaultPackingUsed) {
const auto words =
SimpleCompilationBinary(kGlslShaderWeirdPacking, EShLangVertex);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpMemberDecorate %B 1 Offset 16"))
<< disassembly;
}
TEST_F(CompilerTest, HlslOffsetsOptionDisableRespected) {
compiler_.SetHlslOffsets(false);
const auto words =
SimpleCompilationBinary(kGlslShaderWeirdPacking, EShLangVertex);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpMemberDecorate %B 1 Offset 16"))
<< disassembly;
}
TEST_F(CompilerTest, HlslOffsetsOptionEnableRespected) {
compiler_.SetHlslOffsets(true);
const auto words =
SimpleCompilationBinary(kGlslShaderWeirdPacking, EShLangVertex);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpMemberDecorate %B 1 Offset 4"))
<< disassembly;
}
TEST_F(CompilerTest, HlslLegalizationEnabledNoSizeOpt) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
const auto words =
SimpleCompilationBinary(kHlslShaderForLegalizationTest, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, Not(HasSubstr("OpFunctionCall"))) << disassembly;
EXPECT_THAT(disassembly, HasSubstr("OpName")) << disassembly;
}
TEST_F(CompilerTest, HlslLegalizationEnabledWithSizeOpt) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
compiler_.SetOptimizationLevel(Compiler::OptimizationLevel::Size);
const auto words =
SimpleCompilationBinary(kHlslShaderForLegalizationTest, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, Not(HasSubstr("OpFunctionCall"))) << disassembly;
EXPECT_THAT(disassembly, Not(HasSubstr("OpName"))) << disassembly;
}
TEST_F(CompilerTest, HlslLegalizationDisabled) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
compiler_.EnableHlslLegalization(false);
const auto words =
SimpleCompilationBinary(kHlslShaderForLegalizationTest, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly, HasSubstr("OpFunctionCall")) << disassembly;
}
TEST_F(CompilerTest, HlslFunctionality1Enabled) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::HLSL);
compiler_.EnableHlslFunctionality1(true);
compiler_.SetAutoBindUniforms(true); // Counter variable needs a binding.
const auto words =
SimpleCompilationBinary(kHlslShaderWithCounterBuffer, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly,
HasSubstr("OpExtension \"SPV_GOOGLE_hlsl_functionality1\""))
<< disassembly;
EXPECT_THAT(disassembly,
HasSubstr("OpDecorateString %_entryPointOutput "
"UserSemantic \"SV_TARGET0\""))
<< 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;
}
// A test coase for Glslang
// expected vector after the conversion.
struct GetGlslangClientInfoCase {
std::string prefix;
Compiler::TargetEnv env;
Compiler::TargetEnvVersion env_version;
Compiler::SpirvVersion spv_version;
bool spv_forced;
// Expected results. The error field is matched as a substring.
GlslangClientInfo expected;
};
// Test the shaderc_util::GetGlslangClientInfo function.
using GetGlslangClientInfoTest =
testing::TestWithParam<GetGlslangClientInfoCase>;
TEST_P(GetGlslangClientInfoTest, Sample) {
const auto& c = GetParam();
const auto& expected = c.expected;
auto result = shaderc_util::GetGlslangClientInfo(
c.prefix, c.env, c.env_version, c.spv_version, c.spv_forced);
EXPECT_THAT(result.error.empty(), Eq(expected.error.empty()));
if (result.error.empty()) {
EXPECT_THAT(result.client, Eq(expected.client));
EXPECT_THAT(result.client_version, Eq(expected.client_version));
EXPECT_THAT(result.target_language, Eq(expected.target_language));
EXPECT_THAT(result.target_language_version,
Eq(expected.target_language_version));
} else {
EXPECT_THAT(result.error, HasSubstr(expected.error));
}
}
#define CASE_VK(VKVER, SPVVER) \
"", Compiler::TargetEnv::Vulkan, Compiler::TargetEnvVersion::Vulkan_##VKVER, \
Compiler::SpirvVersion::v##SPVVER
#define BADCASE_VK(STR, VKVER, SPVVER) \
STR, Compiler::TargetEnv::Vulkan, \
static_cast<Compiler::TargetEnvVersion>(VKVER), \
static_cast<Compiler::SpirvVersion>(SPVVER)
#define CASE_GL(GLVER, SPVVER) \
"", Compiler::TargetEnv::OpenGL, Compiler::TargetEnvVersion::OpenGL_##GLVER, \
Compiler::SpirvVersion::v##SPVVER
#define BADCASE_GL(STR, GLVER, SPVVER) \
STR, Compiler::TargetEnv::OpenGL, \
static_cast<Compiler::TargetEnvVersion>(GLVER), \
static_cast<Compiler::SpirvVersion>(SPVVER)
#define GCASE_VK(STR, VKVER, SPVVER) \
shaderc_util::GlslangClientInfo { \
std::string(STR), glslang::EShClientVulkan, \
glslang::EShTargetVulkan_##VKVER, glslang::EShTargetSpv, \
glslang::EShTargetSpv_##SPVVER \
}
#define GCASE_GL(STR, GLVER, SPVVER) \
shaderc_util::GlslangClientInfo { \
std::string(STR), glslang::EShClientOpenGL, \
glslang::EShTargetOpenGL_##GLVER, glslang::EShTargetSpv, \
glslang::EShTargetSpv_##SPVVER \
}
INSTANTIATE_TEST_SUITE_P(
UnforcedSpirvSuccess, GetGlslangClientInfoTest,
testing::ValuesIn(std::vector<GetGlslangClientInfoCase>{
// Unforced SPIR-V version. Success cases.
{CASE_VK(1_0, 1_4), false, GCASE_VK("", 1_0, 1_0)},
{CASE_VK(1_1, 1_4), false, GCASE_VK("", 1_1, 1_3)},
{CASE_GL(4_5, 1_4), false, GCASE_GL("", 450, 1_0)},
}));
INSTANTIATE_TEST_SUITE_P(
ForcedSpirvSuccess, GetGlslangClientInfoTest,
testing::ValuesIn(std::vector<GetGlslangClientInfoCase>{
// Forced SPIR-V version. Success cases.
{CASE_VK(1_0, 1_0), true, GCASE_VK("", 1_0, 1_0)},
{CASE_VK(1_0, 1_1), true, GCASE_VK("", 1_0, 1_1)},
{CASE_VK(1_0, 1_2), true, GCASE_VK("", 1_0, 1_2)},
{CASE_VK(1_0, 1_3), true, GCASE_VK("", 1_0, 1_3)},
{CASE_VK(1_1, 1_0), true, GCASE_VK("", 1_1, 1_0)},
{CASE_VK(1_1, 1_1), true, GCASE_VK("", 1_1, 1_1)},
{CASE_VK(1_1, 1_2), true, GCASE_VK("", 1_1, 1_2)},
{CASE_VK(1_1, 1_3), true, GCASE_VK("", 1_1, 1_3)},
{CASE_GL(4_5, 1_0), true, GCASE_GL("", 450, 1_0)},
{CASE_GL(4_5, 1_1), true, GCASE_GL("", 450, 1_1)},
{CASE_GL(4_5, 1_2), true, GCASE_GL("", 450, 1_2)},
}));
INSTANTIATE_TEST_SUITE_P(
Failure, GetGlslangClientInfoTest,
testing::ValuesIn(std::vector<GetGlslangClientInfoCase>{
// Failure cases.
{BADCASE_VK("foo", 999, Compiler::SpirvVersion::v1_0), false,
GCASE_VK("error:foo: Invalid target client version 999 for Vulkan "
"environment 0",
1_0, 1_0)},
{BADCASE_GL("foo", 999, Compiler::SpirvVersion::v1_0), false,
GCASE_GL("error:foo: Invalid target client version 999 for OpenGL "
"environment 1",
450, 1_0)},
// For bad SPIR-V versions, have to force=true to make it pay attention.
{BADCASE_VK("foo", Compiler::TargetEnvVersion::Vulkan_1_0, 999), true,
GCASE_VK("error:foo: Unknown SPIR-V version 3e7", 1_0, 1_0)},
{BADCASE_GL("foo", Compiler::TargetEnvVersion::OpenGL_4_5, 999), true,
GCASE_GL("error:foo: Unknown SPIR-V version 3e7", 450, 1_0)},
}));
#undef CASE_VK
#undef CASE_GL
#undef BADCASE_VK
#undef BADCASE_GL
#undef GCASE_VK
#undef GCASE_GL
} // anonymous namespace