First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
# Copyright 2020 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:=shaderc_util
LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -DENABLE_HLSL=1
LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH)/include
LOCAL_SRC_FILES:=src/args.cc \
src/compiler.cc \
src/file_finder.cc \
src/io_shaderc.cc \
src/message.cc \
src/resources.cc \
src/shader_stage.cc \
src/spirv_tools_wrapper.cc \
src/version_profile.cc
LOCAL_STATIC_LIBRARIES:=SPIRV SPIRV-Tools-opt
LOCAL_C_INCLUDES:=$(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,89 @@
# Copyright 2020 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.
project(libshaderc_util)
add_library(shaderc_util STATIC
include/libshaderc_util/counting_includer.h
include/libshaderc_util/file_finder.h
include/libshaderc_util/format.h
include/libshaderc_util/io_shaderc.h
include/libshaderc_util/mutex.h
include/libshaderc_util/message.h
include/libshaderc_util/resources.h
include/libshaderc_util/spirv_tools_wrapper.h
include/libshaderc_util/string_piece.h
include/libshaderc_util/universal_unistd.h
include/libshaderc_util/version_profile.h
src/args.cc
src/compiler.cc
src/file_finder.cc
src/io_shaderc.cc
src/message.cc
src/resources.cc
src/shader_stage.cc
src/spirv_tools_wrapper.cc
src/version_profile.cc
)
shaderc_default_compile_options(shaderc_util)
target_include_directories(shaderc_util
PUBLIC include PRIVATE ${glslang_SOURCE_DIR})
# We use parts of Glslang's HLSL compilation interface, which
# now requires this preprocessor definition.
add_definitions(-DENABLE_HLSL)
find_package(Threads)
target_link_libraries(shaderc_util PRIVATE
glslang SPIRV
SPIRV-Tools-opt ${CMAKE_THREAD_LIBS_INIT})
shaderc_add_tests(
TEST_PREFIX shaderc_util
LINK_LIBS shaderc_util
TEST_NAMES
counting_includer
string_piece
format
file_finder
io_shaderc
message
mutex
version_profile)
if(${SHADERC_ENABLE_TESTS})
target_include_directories(shaderc_util_counting_includer_test
PRIVATE ${glslang_SOURCE_DIR})
target_include_directories(shaderc_util_version_profile_test
PRIVATE ${glslang_SOURCE_DIR})
endif()
shaderc_add_tests(
TEST_PREFIX shaderc_util
LINK_LIBS shaderc_util
INCLUDE_DIRS
${glslang_SOURCE_DIR}
${spirv-tools_SOURCE_DIR}/include
TEST_NAMES
compiler)
# This target copies content of testdata into the build directory.
add_custom_target(testdata COMMAND
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/testdata/copy-to-build.cmake
COMMENT "Copy testdata into build directory")
if(${SHADERC_ENABLE_TESTS})
add_dependencies(shaderc_util_file_finder_test testdata)
add_dependencies(shaderc_util_io_shaderc_test testdata)
endif()

View File

@@ -0,0 +1,41 @@
// 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.
#ifndef LIBSHADERC_UTIL_INC_ARGS_H
#define LIBSHADERC_UTIL_INC_ARGS_H
#include <cstdint>
#include <string>
#include "libshaderc_util/string_piece.h"
namespace shaderc_util {
// Gets the option argument for the option at *index in argv in a way consistent
// with clang/gcc. On success, returns true and writes the parsed argument into
// *option_argument. Returns false if any errors occur. After calling this
// function, *index will be the index of the last command line argument
// consumed.
bool GetOptionArgument(int argc, char** argv, int* index,
const std::string& option,
string_piece* option_argument);
// Parses the given string as a number of the specified type. Returns true
// if parsing succeeded, and stores the parsed value via |value|.
// (I've worked out the general case for this in
// SPIRV-Tools source/util/parse_number.h. -- dneto)
bool ParseUint32(const std::string& str, uint32_t* value);
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_INC_ARGS_H

View File

@@ -0,0 +1,656 @@
// 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.
#ifndef LIBSHADERC_UTIL_INC_COMPILER_H
#define LIBSHADERC_UTIL_INC_COMPILER_H
#include <array>
#include <cassert>
#include <functional>
#include <mutex>
#include <ostream>
#include <string>
#include <unordered_map>
#include <utility>
#include "glslang/Public/ShaderLang.h"
#include "counting_includer.h"
#include "file_finder.h"
#include "mutex.h"
#include "resources.h"
#include "string_piece.h"
// Fix a typo in glslang/Public/ShaderLang.h
#define EShTargetClientVersion EshTargetClientVersion
namespace shaderc_util {
// To break recursive including. This header is already included in
// spirv_tools_wrapper.h, so cannot include spirv_tools_wrapper.h here.
enum class PassId;
// Initializes glslang on creation, and destroys it on completion.
// Used to tie gslang process operations to object lifetimes.
// Additionally initialization/finalization of glslang is not thread safe, so
// synchronizes these operations.
class GlslangInitializer {
public:
GlslangInitializer();
~GlslangInitializer();
private:
static unsigned int initialize_count_;
// Using a bare pointer here to avoid any global class construction at the
// beginning of the execution.
static std::mutex* glslang_mutex_;
};
// Maps macro names to their definitions. Stores string_pieces, so the
// underlying strings must outlive it.
using MacroDictionary = std::unordered_map<std::string, std::string>;
// Holds all of the state required to compile source GLSL into SPIR-V.
class Compiler {
public:
// Source language
enum class SourceLanguage {
GLSL, // The default
HLSL,
};
// Target environment.
enum class TargetEnv {
Vulkan, // Default to Vulkan 1.0
OpenGL, // Default to OpenGL 4.5
OpenGLCompat, // Support removed. Generates error if used.
};
// Target environment versions. These numbers match those used by Glslang.
enum class TargetEnvVersion : uint32_t {
Default = 0, // Default for the corresponding target environment
// For Vulkan, use numbering scheme from vulkan.h
Vulkan_1_0 = ((1 << 22)), // Vulkan 1.0
Vulkan_1_1 = ((1 << 22) | (1 << 12)), // Vulkan 1.1
Vulkan_1_2 = ((1 << 22) | (2 << 12)), // Vulkan 1.2
Vulkan_1_3 = ((1 << 22) | (3 << 12)), // Vulkan 1.2
// For OpenGL, use the numbering from #version in shaders.
OpenGL_4_5 = 450,
};
// SPIR-V version.
enum class SpirvVersion : uint32_t {
v1_0 = 0x010000u,
v1_1 = 0x010100u,
v1_2 = 0x010200u,
v1_3 = 0x010300u,
v1_4 = 0x010400u,
v1_5 = 0x010500u,
v1_6 = 0x010600u,
};
enum class OutputType {
SpirvBinary, // A binary module, as defined by the SPIR-V specification.
SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project.
PreprocessedText, // Preprocessed source code.
};
// Supported optimization levels.
enum class OptimizationLevel {
Zero, // No optimization.
Size, // Optimization towards reducing code size.
Performance, // Optimization towards better performance.
};
// Resource limits. These map to the "max*" fields in
// glslang::TBuiltInResource.
enum class Limit {
#define RESOURCE(NAME, FIELD, CNAME) NAME,
#include "resources.inc"
#undef RESOURCE
};
// Types of uniform variables.
enum class UniformKind {
// Image, and image buffer.
Image = 0,
// Pure sampler.
Sampler = 1,
// Sampled texture in GLSL.
// Shader Resource View, for HLSL. (Read-only image or storage buffer.)
Texture = 2,
// Uniform Buffer Object, or UBO, in GLSL.
// Also a Cbuffer in HLSL.
Buffer = 3,
// Shader Storage Buffer Object, or SSBO
StorageBuffer = 4,
// Uniform Access View, in HLSL. (Writable storage image or storage
// buffer.)
UnorderedAccessView = 5,
};
enum { kNumUniformKinds = int(UniformKind::UnorderedAccessView) + 1 };
// Shader pipeline stage.
// TODO(dneto): Replaces interface uses of EShLanguage with this enum.
enum class Stage {
Vertex,
TessEval,
TessControl,
Geometry,
Fragment,
Compute,
RayGenNV,
IntersectNV,
AnyHitNV,
ClosestHitNV,
MissNV,
CallableNV,
TaskNV,
MeshNV,
StageEnd,
};
enum { kNumStages = int(Stage::StageEnd) };
// Returns a std::array of all the Stage values.
const std::array<Stage, kNumStages>& stages() const {
static std::array<Stage, kNumStages> values{{
Stage::Vertex,
Stage::TessEval,
Stage::TessControl,
Stage::Geometry,
Stage::Fragment,
Stage::Compute,
Stage::RayGenNV,
Stage::IntersectNV,
Stage::AnyHitNV,
Stage::ClosestHitNV,
Stage::MissNV,
Stage::CallableNV,
Stage::TaskNV,
Stage::MeshNV,
}};
return values;
}
// Creates an default compiler instance targeting at Vulkan environment. Uses
// version 110 and no profile specification as the default for GLSL.
Compiler()
// The default version for glsl is 110, or 100 if you are using an es
// profile. But we want to default to a non-es profile.
: default_version_(110),
default_profile_(ENoProfile),
force_version_profile_(false),
warnings_as_errors_(false),
suppress_warnings_(false),
generate_debug_info_(false),
emit_non_semantic_debug_info_(false),
enabled_opt_passes_(),
target_env_(TargetEnv::Vulkan),
target_env_version_(TargetEnvVersion::Default),
target_spirv_version_(SpirvVersion::v1_0),
target_spirv_version_is_forced_(false),
source_language_(SourceLanguage::GLSL),
limits_(kDefaultTBuiltInResource),
auto_bind_uniforms_(false),
auto_combined_image_sampler_(false),
auto_binding_base_(),
auto_map_locations_(false),
preserve_bindings_(false),
hlsl_iomap_(false),
hlsl_offsets_(false),
hlsl_legalization_enabled_(true),
hlsl_functionality1_enabled_(false),
hlsl_16bit_types_enabled_(false),
invert_y_enabled_(false),
nan_clamp_(false),
hlsl_explicit_bindings_() {}
// Requests that the compiler place debug information into the object code,
// such as identifier names and line numbers.
void SetGenerateDebugInfo();
// Requests that the compiler emit non-semantic debug information.
// Requires VK_KHR_shader_non_semantic_info.
void SetEmitNonSemanticDebugInfo();
// Sets the optimization level to the given level. Only the last one takes
// effect if multiple calls of this method exist.
void SetOptimizationLevel(OptimizationLevel level);
// Enables or disables HLSL legalization passes.
void EnableHlslLegalization(bool hlsl_legalization_enabled);
// Enables or disables extension SPV_GOOGLE_hlsl_functionality1
void EnableHlslFunctionality1(bool enable);
// Enables or disables HLSL 16-bit types.
void EnableHlsl16BitTypes(bool enable);
// Enables or disables relaxed Vulkan rules.
//
// This allows most OpenGL shaders to compile under Vulkan semantics.
void SetVulkanRulesRelaxed(bool enable);
// 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();
// Any warning message generated is suppressed before it is output.
void SetSuppressWarnings();
// Adds an implicit macro definition obeyed by subsequent CompileShader()
// calls. The macro and definition should be passed in with their char*
// pointer and their lengths. They can be modified or deleted after this
// function has returned.
void AddMacroDefinition(const char* macro, size_t macro_length,
const char* definition, size_t definition_length);
// Sets the target environment, including version. The version value should
// be 0 or one of the values from TargetEnvVersion. The default value maps
// to Vulkan 1.0 if the target environment is Vulkan, and it maps to OpenGL
// 4.5 if the target environment is OpenGL.
void SetTargetEnv(TargetEnv env,
TargetEnvVersion version = TargetEnvVersion::Default);
// Sets the target version of SPIR-V. The module will use this version
// of SPIR-V. Defaults to the highest version of SPIR-V required to be
// supported by the target environment. E.g. default to SPIR-V 1.0 for
// Vulkan 1.0, and SPIR-V 1.3 for Vulkan 1.1.
void SetTargetSpirv(SpirvVersion version);
// Sets the souce language.
void SetSourceLanguage(SourceLanguage lang);
// Forces (without any verification) the default version and profile for
// subsequent CompileShader() calls.
void SetForcedVersionProfile(int version, EProfile profile);
// Sets a resource limit.
void SetLimit(Limit limit, int value);
// Returns the current limit.
int GetLimit(Limit limit) const;
// Set whether the compiler automatically assigns bindings to
// uniform variables that don't have explicit bindings.
void SetAutoBindUniforms(bool auto_bind) { auto_bind_uniforms_ = auto_bind; }
// Sets whether the compiler should automatically remove sampler variables
// and convert image variables to combined image-sampler variables.
void SetAutoCombinedImageSampler(bool auto_combine) {
auto_combined_image_sampler_ = auto_combine;
}
// Sets the lowest binding number used when automatically assigning bindings
// for uniform resources of the given type, for all shader stages. The default
// base is zero.
void SetAutoBindingBase(UniformKind kind, uint32_t base) {
for (auto stage : stages()) {
SetAutoBindingBaseForStage(stage, kind, base);
}
}
// Sets the lowest binding number used when automatically assigning bindings
// for uniform resources of the given type for a specific shader stage. The
// default base is zero.
void SetAutoBindingBaseForStage(Stage stage, UniformKind kind,
uint32_t base) {
auto_binding_base_[static_cast<int>(stage)][static_cast<int>(kind)] = base;
}
// Sets whether the compiler should preserve all bindings, even when those
// bindings are not used.
void SetPreserveBindings(bool preserve_bindings) {
preserve_bindings_ = preserve_bindings;
}
// Sets whether the compiler automatically assigns locations to
// uniform variables that don't have explicit locations.
void SetAutoMapLocations(bool auto_map) { auto_map_locations_ = auto_map; }
// Use HLSL IO mapping rules for bindings. Default is false.
void SetHlslIoMapping(bool hlsl_iomap) { hlsl_iomap_ = hlsl_iomap; }
// Use HLSL rules for offsets in "transparent" memory. These allow for
// tighter packing of some combinations of types than standard GLSL packings.
void SetHlslOffsets(bool hlsl_offsets) { hlsl_offsets_ = hlsl_offsets; }
// Sets an explicit set and binding for the given HLSL register.
void SetHlslRegisterSetAndBinding(const std::string& reg,
const std::string& set,
const std::string& binding) {
for (auto stage : stages()) {
SetHlslRegisterSetAndBindingForStage(stage, reg, set, binding);
}
}
// Sets an explicit set and binding for the given HLSL register in the given
// shader stage. For example,
// SetHlslRegisterSetAndBinding(Stage::Fragment, "t1", "4", "5")
// means register "t1" in a fragment shader should map to binding 5 in set 4.
// (Glslang wants this data as strings, not ints or enums.) The string data is
// copied.
void SetHlslRegisterSetAndBindingForStage(Stage stage, const std::string& reg,
const std::string& set,
const std::string& binding) {
hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(reg);
hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(set);
hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(binding);
}
// Compiles the shader source in the input_source_string parameter.
//
// If the forced_shader stage parameter is not EShLangCount then
// the shader is assumed to be of the given stage.
//
// For HLSL compilation, entry_point_name is the null-terminated string for
// the entry point. For GLSL compilation, entry_point_name is ignored, and
// compilation assumes the entry point is named "main".
//
// The stage_callback function will be called if a shader_stage has
// not been forced and the stage can not be determined
// from the shader text. Any #include directives are parsed with the given
// includer.
//
// The initializer parameter must be a valid GlslangInitializer object.
// Acquire will be called on the initializer and the result will be
// destroyed before the function ends.
//
// The output_type parameter determines what kind of output should be
// produced.
//
// Any error messages are written as if the file name were error_tag.
// Any errors are written to the error_stream parameter.
// total_warnings and total_errors are incremented once for every
// warning or error encountered respectively.
//
// Returns a tuple consisting of three fields. 1) a boolean which is true when
// the compilation succeeded, and false otherwise; 2) a vector of 32-bit words
// which contains the compilation output data, either compiled SPIR-V binary
// code, or the text string generated in preprocessing-only or disassembly
// mode; 3) the size of the output data in bytes. When the output is SPIR-V
// binary code, the size is the number of bytes of valid data in the vector.
// If the output is a text string, the size equals the length of that string.
std::tuple<bool, std::vector<uint32_t>, size_t> Compile(
const string_piece& input_source_string, EShLanguage forced_shader_stage,
const std::string& error_tag, const char* entry_point_name,
const std::function<EShLanguage(std::ostream* error_stream,
const string_piece& error_tag)>&
stage_callback,
CountingIncluder& includer, OutputType output_type,
std::ostream* error_stream, size_t* total_warnings, size_t* total_errors) const;
static EShMessages GetDefaultRules() {
return static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules |
EShMsgCascadingErrors);
}
protected:
// Preprocesses a shader whose filename is filename and content is
// shader_source. If preprocessing is successful, returns true, the
// preprocessed shader, and any warning message as a tuple. Otherwise,
// returns false, an empty string, and error messages as a tuple.
//
// The error_tag parameter is the name to use for outputting errors.
// The shader_source parameter is the input shader's source text.
// The shader_preamble parameter is a context-specific preamble internally
// prepended to shader_text without affecting the validity of its #version
// position.
//
// Any #include directives are processed with the given includer.
//
// If force_version_profile_ is set, the shader's version/profile is forced
// to be default_version_/default_profile_ regardless of the #version
// directive in the source code.
std::tuple<bool, std::string, std::string> PreprocessShader(
const std::string& error_tag, const string_piece& shader_source,
const string_piece& shader_preamble, CountingIncluder& includer) const;
// Cleans up the preamble in a given preprocessed shader.
//
// The error_tag parameter is the name to be given for the main file.
// The pound_extension parameter is the #extension directive we prepended to
// the original shader source code via preamble.
// The num_include_directives parameter is the number of #include directives
// appearing in the original shader source code.
// The is_for_next_line means whether the #line sets the line number for the
// next line.
//
// If no #include directive is used in the shader source code, we can safely
// delete the #extension directive we injected via preamble. Otherwise, we
// need to adjust it if there exists a #version directive in the original
// shader source code.
std::string CleanupPreamble(const string_piece& preprocessed_shader,
const string_piece& error_tag,
const string_piece& pound_extension,
int num_include_directives,
bool is_for_next_line) const;
// Determines version and profile from command line, or the source code.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> DeduceVersionProfile(
const std::string& preprocessed_shader) const;
// Determines the shader stage from pragmas embedded in the source text if
// possible. In the returned pair, the glslang EShLanguage is the shader
// stage deduced. If no #pragma directives for shader stage exist, it's
// EShLangCount. If errors occur, the second element in the pair is the
// error message. Otherwise, it's an empty string.
std::pair<EShLanguage, std::string> GetShaderStageFromSourceCode(
string_piece filename, const std::string& preprocessed_shader) const;
// Determines version and profile from command line, or the source code.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> DeduceVersionProfile(
const std::string& preprocessed_shader);
// Gets version and profile specification from the given preprocessedshader.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> GetVersionProfileFromSourceCode(
const std::string& preprocessed_shader) const;
// Version to use when force_version_profile_ is true.
int default_version_;
// Profile to use when force_version_profile_ is true.
EProfile default_profile_;
// When true, use the default version and profile from eponymous data members.
bool force_version_profile_;
// Macro definitions that must be available to reference in the shader source.
MacroDictionary predefined_macros_;
// When true, treat warnings as errors.
bool warnings_as_errors_;
// Supress warnings when true.
bool suppress_warnings_;
// When true, compilation will generate debug info with the binary SPIR-V
// output.
bool generate_debug_info_;
// When true and generate_debug_info_ is also set, generate non-semantic debug
// information.
bool emit_non_semantic_debug_info_;
// Optimization passes to be applied.
std::vector<PassId> enabled_opt_passes_;
// The target environment to compile with. This controls the glslang
// EshMessages bitmask, which determines which dialect of GLSL and which
// SPIR-V codegen semantics are used. This impacts the warning & error
// messages as well as the set of available builtins, as per the
// implementation of glslang.
TargetEnv target_env_;
// The version number of the target environment. The numbering scheme is
// particular to each target environment. If this is 0, then use a default
// for that particular target environment. See libshaders/shaderc/shaderc.h
// for those defaults.
TargetEnvVersion target_env_version_;
// The SPIR-V version to be used for the generated module. Defaults to 1.0.
SpirvVersion target_spirv_version_;
// True if the user explicitly set the target SPIR-V version.
bool target_spirv_version_is_forced_;
// The source language. Defaults to GLSL.
SourceLanguage source_language_;
// The resource limits to be used.
TBuiltInResource limits_;
// True if the compiler should automatically bind uniforms that don't
// have explicit bindings.
bool auto_bind_uniforms_;
// True if the compiler should automatically remove sampler variables
// and convert image variables to combined image-sampler variables.
bool auto_combined_image_sampler_;
// The base binding number per uniform type, per stage, used when automatically
// binding uniforms that don't hzve explicit bindings in the shader source.
// The default is zero.
uint32_t auto_binding_base_[kNumStages][kNumUniformKinds];
// True if the compiler should automatically map uniforms that don't
// have explicit locations.
bool auto_map_locations_;
// True if the compiler should preserve all bindings, even when unused.
bool preserve_bindings_;
// True if the compiler should use HLSL IO mapping rules when compiling HLSL.
bool hlsl_iomap_;
// True if the compiler should determine block member offsets using HLSL
// packing rules instead of standard GLSL rules.
bool hlsl_offsets_;
// True if the compiler should perform legalization optimization passes if
// source language is HLSL.
bool hlsl_legalization_enabled_;
// True if the compiler should support extension SPV_GOOGLE_hlsl_functionality1.
bool hlsl_functionality1_enabled_;
// True if the compiler should support 16-bit HLSL types.
bool hlsl_16bit_types_enabled_;
// True if the compiler should relax Vulkan rules to allow OGL shaders to
// compile.
bool vulkan_rules_relaxed_ = false;
// 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.
std::vector<std::string> hlsl_explicit_bindings_[kNumStages];
};
// Converts a string to a vector of uint32_t by copying the content of a given
// string to the vector and returns it. Appends '\0' at the end if extra bytes
// are required to complete the last element.
std::vector<uint32_t> ConvertStringToVector(const std::string& str);
// Converts a valid Glslang shader stage value to a Compiler::Stage value.
inline Compiler::Stage ConvertToStage(EShLanguage stage) {
switch (stage) {
case EShLangVertex:
return Compiler::Stage::Vertex;
case EShLangTessControl:
return Compiler::Stage::TessEval;
case EShLangTessEvaluation:
return Compiler::Stage::TessControl;
case EShLangGeometry:
return Compiler::Stage::Geometry;
case EShLangFragment:
return Compiler::Stage::Fragment;
case EShLangCompute:
return Compiler::Stage::Compute;
case EShLangRayGenNV:
return Compiler::Stage::RayGenNV;
case EShLangIntersectNV:
return Compiler::Stage::IntersectNV;
case EShLangAnyHitNV:
return Compiler::Stage::AnyHitNV;
case EShLangClosestHitNV:
return Compiler::Stage::ClosestHitNV;
case EShLangMissNV:
return Compiler::Stage::MissNV;
case EShLangCallableNV:
return Compiler::Stage::CallableNV;
case EShLangTaskNV:
return Compiler::Stage::TaskNV;
case EShLangMeshNV:
return Compiler::Stage::MeshNV;
default:
break;
}
assert(false && "Invalid case");
return Compiler::Stage::Compute;
}
// A GlslangClientInfo captures target client version and desired SPIR-V
// version.
struct GlslangClientInfo {
GlslangClientInfo() {}
GlslangClientInfo(const std::string& e, glslang::EShClient c,
glslang::EShTargetClientVersion cv,
glslang::EShTargetLanguage l,
glslang::EShTargetLanguageVersion lv)
: error(e),
client(c),
client_version(cv),
target_language(l),
target_language_version(lv) {}
std::string error; // Empty if ok, otherwise contains the error message.
glslang::EShClient client = glslang::EShClientNone;
glslang::EShTargetClientVersion client_version;
glslang::EShTargetLanguage target_language = glslang::EShTargetSpv;
glslang::EShTargetLanguageVersion target_language_version =
glslang::EShTargetSpv_1_0;
};
// Returns the mappings to Glslang client, client version, and SPIR-V version.
// Also indicates whether the input values were valid.
GlslangClientInfo GetGlslangClientInfo(
const std::string& error_tag, // Indicates source location, for errors.
shaderc_util::Compiler::TargetEnv env,
shaderc_util::Compiler::TargetEnvVersion env_version,
shaderc_util::Compiler::SpirvVersion spv_version,
bool spv_version_is_forced);
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_INC_COMPILER_H

View File

@@ -0,0 +1,101 @@
// 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.
#ifndef LIBSHADERC_UTIL_COUNTING_INCLUDER_H
#define LIBSHADERC_UTIL_COUNTING_INCLUDER_H
#include <atomic>
#include "glslang/Public/ShaderLang.h"
#include "libshaderc_util/mutex.h"
namespace shaderc_util {
// An Includer that counts how many #include directives it saw.
// Inclusions are internally serialized, but releasing a previous result
// can occur concurrently.
class CountingIncluder : public glslang::TShader::Includer {
public:
// Done as .store(0) instead of in the initializer list for the following
// reasons:
// Clang > 3.6 will complain about it if it is written as ({0}).
// VS2013 fails if it is written as {0}.
// G++-4.8 does not correctly support std::atomic_init.
CountingIncluder() {
num_include_directives_.store(0);
}
enum class IncludeType {
System, // Only do < > include search
Local, // Only do " " include search
};
// Resolves an include request for a source by name, type, and name of the
// requesting source. For the semantics of the result, see the base class.
// Also increments num_include_directives and returns the results of
// include_delegate(filename). Subclasses should override include_delegate()
// instead of this method. Inclusions are serialized.
glslang::TShader::Includer::IncludeResult* includeSystem(
const char* requested_source, const char* requesting_source,
size_t include_depth) final {
++num_include_directives_;
include_mutex_.lock();
auto result = include_delegate(requested_source, requesting_source,
IncludeType::System, include_depth);
include_mutex_.unlock();
return result;
}
// Like includeSystem, but for "local" include search.
glslang::TShader::Includer::IncludeResult* includeLocal(
const char* requested_source, const char* requesting_source,
size_t include_depth) final {
++num_include_directives_;
include_mutex_.lock();
auto result = include_delegate(requested_source, requesting_source,
IncludeType::Local, include_depth);
include_mutex_.unlock();
return result;
}
// Releases the given IncludeResult.
void releaseInclude(glslang::TShader::Includer::IncludeResult* result) final {
release_delegate(result);
}
int num_include_directives() const { return num_include_directives_.load(); }
private:
// Invoked by this class to provide results to
// glslang::TShader::Includer::include.
virtual glslang::TShader::Includer::IncludeResult* include_delegate(
const char* requested_source, const char* requesting_source,
IncludeType type, size_t include_depth) = 0;
// Release the given IncludeResult.
virtual void release_delegate(
glslang::TShader::Includer::IncludeResult* result) = 0;
// The number of #include directive encountered.
std::atomic_int num_include_directives_;
// A mutex to protect against concurrent inclusions. We can't trust
// our delegates to be safe for concurrent inclusions.
shaderc_util::mutex include_mutex_;
};
}
#endif // LIBSHADERC_UTIL_COUNTING_INCLUDER_H

View File

@@ -0,0 +1,26 @@
// Copyright 2018 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.
#ifndef LIBSHADERC_UTIL_EXCEPTIONS_H_
#define LIBSHADERC_UTIL_EXCEPTIONS_H_
#if (defined(_MSC_VER) && !defined(_CPPUNWIND)) || !defined(__EXCEPTIONS)
#define TRY_IF_EXCEPTIONS_ENABLED
#define CATCH_IF_EXCEPTIONS_ENABLED(X) if (0)
#else
#define TRY_IF_EXCEPTIONS_ENABLED try
#define CATCH_IF_EXCEPTIONS_ENABLED(X) catch (X)
#endif
#endif // LIBSHADERC_UTIL_EXCEPTIONS_H_

View File

@@ -0,0 +1,57 @@
// 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.
#ifndef LIBSHADERC_UTIL_SRC_FILE_FINDER_H_
#define LIBSHADERC_UTIL_SRC_FILE_FINDER_H_
#include <string>
#include <vector>
namespace shaderc_util {
// Finds files within a search path.
class FileFinder {
public:
// Searches for a read-openable file based on filename, which must be
// non-empty. The search is attempted on filename prefixed by each element of
// search_path() in turn. The first hit is returned, or an empty string if
// there are no hits. Search attempts treat their argument the way
// std::fopen() treats its filename argument, ignoring whether the path is
// absolute or relative.
//
// If a search_path() element is non-empty and not ending in a slash, then a
// slash is inserted between it and filename before its search attempt. An
// empty string in search_path() means that the filename is tried as-is.
std::string FindReadableFilepath(const std::string& filename) const;
// Searches for a read-openable file based on filename, which must be
// non-empty. The search is first attempted as a path relative to
// the requesting_file parameter. If no file is found relative to the
// requesting_file then this acts as FindReadableFilepath does. If
// requesting_file does not contain a '/' or a '\' character then it is
// assumed to be a filename and the request will be relative to the
// current directory.
std::string FindRelativeReadableFilepath(const std::string& requesting_file,
const std::string& filename) const;
// Search path for Find(). Users may add/remove elements as desired.
std::vector<std::string>& search_path() { return search_path_; }
private:
std::vector<std::string> search_path_;
};
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_SRC_FILE_FINDER_H_

View File

@@ -0,0 +1,36 @@
// 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.
#ifndef LIBSHADERC_UTIL_FORMAT_H_
#define LIBSHADERC_UTIL_FORMAT_H_
#include <sstream>
namespace shaderc_util {
// Returns a string containing <prefix><key><infix><value><postfix> for every
// key-value entry in map.
template <typename Map>
std::string format(const Map& map, const std::string& prefix,
const std::string& infix, const std::string& postfix) {
std::stringstream s;
for (const auto& pair : map) {
s << prefix << pair.first << infix << pair.second << postfix;
}
return s.str();
}
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_FORMAT_H_

View File

@@ -0,0 +1,69 @@
// 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.
#ifndef LIBSHADERC_UTIL_IO_H_
#define LIBSHADERC_UTIL_IO_H_
#include <string>
#include <vector>
#include "string_piece.h"
namespace shaderc_util {
// Returns true if the given path is an absolute path.
bool IsAbsolutePath(const std::string& path);
// A helper function to return the base file name from either absolute path or
// relative path representation of a file. It keeps the component from the last
// '/' or '\' to the end of the given string. If the component is '..' or '.',
// returns an empty string. If '/' or '\' is the last char of the given string,
// also returns an empty string.
// e.g.: dir_a/dir_b/file_c.vert => file_c.vert
// dir_a/dir_b/.. => <empty string>
// dir_a/dir_b/. => <empty string>
// dir_a/dirb/c/ => <empty string>
// Note that this method doesn't check whether the given path is a valid one or
// not.
std::string GetBaseFileName(const std::string& file_path);
// Reads all of the characters in a given file into input_data. Outputs an
// error message to std::cerr if the file could not be read and returns false if
// there was an error. If the input_file is "-", then input is read from
// std::cin.
bool ReadFile(const std::string& input_file_name,
std::vector<char>* input_data);
// Returns and initializes the file_stream parameter if the output_filename
// refers to a file, or returns &std::cout if the output_filename is "-".
// Returns nullptr and emits an error message to err if the file could
// not be opened for writing. If the output refers to a file, and the open
// failed for writing, file_stream is left with its fail_bit set.
std::ostream* GetOutputStream(const string_piece& output_filename,
std::ofstream* file_stream, std::ostream* err);
// Writes output_data to a file, overwriting if it exists. If output_file_name
// is "-", writes to std::cout.
bool WriteFile(std::ostream* output_stream, const string_piece& output_data);
// Flush the standard output stream and set it to binary mode. Subsequent
// output will not translate newlines to carriage-return newline pairs.
void FlushAndSetBinaryModeOnStdout();
// Flush the standard output stream and set it to text mode. Subsequent
// output will translate newlines to carriage-return newline pairs.
void FlushAndSetTextModeOnStdout();
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_IO_H_

View File

@@ -0,0 +1,86 @@
// 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.
#ifndef LIBSHADERC_UTIL_SRC_MESSAGE_H_
#define LIBSHADERC_UTIL_SRC_MESSAGE_H_
#include "libshaderc_util/string_piece.h"
namespace shaderc_util {
// TODO(antiagainst): document the differences of the following message types.
enum class MessageType {
Warning,
Error,
ErrorSummary,
WarningSummary,
GlobalWarning,
GlobalError,
Unknown,
Ignored
};
// Given a glslang warning/error message, processes it in the following way and
// returns its message type.
//
// * Places the source name into the source_name parameter, if found.
// Otherwise, clears the source_name parameter.
// * Places the line number into the line_number parameter, if found.
// Otherwise, clears the line_number parameter.
// * Places the rest of the message (the text past warning/error prefix, source
// name, and line number) into the rest parameter.
//
// If warnings_as_errors is set to true, then all warnings will be treated as
// errors.
// If suppress_warnings is set to true, then no warnings will be emitted. This
// takes precedence over warnings_as_errors.
//
// Examples:
// "ERROR: 0:2: Message"
// source_name="0", line_number="2", rest="Message"
// "Warning, Message"
// source_name="", line_number="", rest="Message"
// "ERROR: 2 errors found."
// source_name="2", line_number="", rest="errors found".
//
// Note that filenames can contain colons:
// "ERROR: foo:bar.comp.hlsl:2: 'a' : unknown variable"
MessageType ParseGlslangOutput(const shaderc_util::string_piece& message,
bool warnings_as_errors, bool suppress_warnings,
shaderc_util::string_piece* source_name,
shaderc_util::string_piece* line_number,
shaderc_util::string_piece* rest);
// Filters error_messages received from glslang, and outputs, to error_stream,
// any that are not ignored in a clang like format. If the warnings_as_errors
// boolean is set, then all warnings will be treated as errors. If the
// suppress_warnings boolean is set then any warning messages are ignored. This
// takes precedence over warnings_as_errors. Increments total_warnings and
// total_errors based on the message types.
// Returns true if no new errors were found when parsing the messages.
// "<command line>" will substitute "-1" appearing at the string name/number
// segment.
bool PrintFilteredErrors(const shaderc_util::string_piece& file_name,
std::ostream* error_stream, bool warnings_as_errors,
bool suppress_warnings, const char* error_list,
size_t* total_warnings, size_t* total_errors);
// Outputs, to error_stream, the number of warnings and errors if there are
// any.
void OutputMessages(std::ostream* error_stream, size_t total_warnings,
size_t total_errors);
} // namespace glslc
#endif // LIBSHADERC_UTIL_SRC_MESSAGE_H_

View File

@@ -0,0 +1,107 @@
// 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.
#ifndef LIBSHADERC_UTIL_INC_MUTEX_H
#define LIBSHADERC_UTIL_INC_MUTEX_H
// shaderc_util::mutex will be defined and specialized
// depending on the platform that is being compiled.
// It is more or less conformant to the C++11 specification of std::mutex.
// However it does not implement try_lock.
#ifdef _WIN32
// windows.h #defines min and max if we don't define this.
// this means things like std::min and std::max break
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
namespace shaderc_util {
// As the name suggests, this mutex class is for running on windows.
// It conforms to the c++11 mutex implementation, and should be a
// drop in replacement.
class windows_mutex {
public:
using native_handle_type = HANDLE;
windows_mutex() { mutex_ = CreateMutex(nullptr, false, nullptr); }
~windows_mutex() {
if (mutex_ != INVALID_HANDLE_VALUE) {
CloseHandle(mutex_);
}
}
windows_mutex(const windows_mutex&) = delete;
windows_mutex& operator=(const windows_mutex&) = delete;
// Locks this mutex, waiting until the mutex is unlocked if it is not already.
// It is not valid to lock a mutex that has already been locked.
void lock() { WaitForSingleObject(mutex_, INFINITE); }
// Unlocks this mutex. It is invalid to unlock a mutex that this thread
// has not already locked.
void unlock() { ReleaseMutex(mutex_); }
// Returns the native handle for this mutex. In this case a HANDLE object.
native_handle_type native_handle() { return mutex_; }
private:
HANDLE mutex_;
};
using mutex = windows_mutex;
}
#else
#include <pthread.h>
#include <memory>
namespace shaderc_util {
// As the name suggests, this mutex class is for running with pthreads.
// It conforms to the c++11 mutex implementation, and should be a
// drop in replacement.
class posix_mutex {
public:
using native_handle_type = pthread_mutex_t*;
posix_mutex() { pthread_mutex_init(&mutex_, nullptr); }
~posix_mutex() { pthread_mutex_destroy(&mutex_); }
posix_mutex(const posix_mutex&) = delete;
posix_mutex& operator=(const posix_mutex&) = delete;
// Locks this mutex, waiting until the mutex is unlocked if it is not already.
// It is not valid to lock a mutex that has already been locked.
void lock() { pthread_mutex_lock(&mutex_); }
// Unlocks this mutex. It is invalid to unlock a mutex that this thread
// has not already locked.
void unlock() { pthread_mutex_unlock(&mutex_); }
// Returns the native handle for this mutex. In this case a pthread_mutex_t*.
native_handle_type native_handle() { return &mutex_; }
private:
pthread_mutex_t mutex_;
};
using mutex = posix_mutex;
}
#endif
#endif // LIBSHADERC_UTIL_INC_MUTEX_H

View File

@@ -0,0 +1,30 @@
// 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.
#ifndef LIBSHADERC_UTIL_RESOURCES_H_
#define LIBSHADERC_UTIL_RESOURCES_H_
// We want TBuiltInResource
#include "glslang/Include/ResourceLimits.h"
namespace shaderc_util {
using TBuiltInResource = ::TBuiltInResource;
// A set of suitable defaults.
extern const TBuiltInResource kDefaultTBuiltInResource;
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_RESOURCES_H_

View File

@@ -0,0 +1,143 @@
// Copyright 2016 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.
// These are the resource limits in a glslang::TBuiltInResource.
// The first field is the string name to be used in a configuration setting.
// The second field is the fieldname in TBuiltInResource.
// The third field is the enum name fragment for shaderc_limit.
//
// TODO(dneto): Consider using a single list of names, but use a Python script
// to generate *this* file. The original data file would have the first field,
// then generate the second field by lowering the case of the first letter, and
// generate the third field by taking the second field, and converting a
// lower-to-upper case transition into an underscore with lower-case.
RESOURCE(MaxLights,maxLights,max_lights)
RESOURCE(MaxClipPlanes,maxClipPlanes,max_clip_planes)
RESOURCE(MaxTextureUnits,maxTextureUnits,max_texture_units)
RESOURCE(MaxTextureCoords,maxTextureCoords,max_texture_coords)
RESOURCE(MaxVertexAttribs,maxVertexAttribs,max_vertex_attribs)
RESOURCE(MaxVertexUniformComponents,maxVertexUniformComponents,max_vertex_uniform_components)
RESOURCE(MaxVaryingFloats,maxVaryingFloats,max_varying_floats)
RESOURCE(MaxVertexTextureImageUnits,maxVertexTextureImageUnits,max_vertex_texture_image_units)
RESOURCE(MaxCombinedTextureImageUnits,maxCombinedTextureImageUnits,max_combined_texture_image_units)
RESOURCE(MaxTextureImageUnits,maxTextureImageUnits,max_texture_image_units)
RESOURCE(MaxFragmentUniformComponents,maxFragmentUniformComponents,max_fragment_uniform_components)
RESOURCE(MaxDrawBuffers,maxDrawBuffers,max_draw_buffers)
RESOURCE(MaxVertexUniformVectors,maxVertexUniformVectors,max_vertex_uniform_vectors)
RESOURCE(MaxVaryingVectors,maxVaryingVectors,max_varying_vectors)
RESOURCE(MaxFragmentUniformVectors,maxFragmentUniformVectors,max_fragment_uniform_vectors)
RESOURCE(MaxVertexOutputVectors,maxVertexOutputVectors,max_vertex_output_vectors)
RESOURCE(MaxFragmentInputVectors,maxFragmentInputVectors,max_fragment_input_vectors)
RESOURCE(MinProgramTexelOffset,minProgramTexelOffset,min_program_texel_offset)
RESOURCE(MaxProgramTexelOffset,maxProgramTexelOffset,max_program_texel_offset)
RESOURCE(MaxClipDistances,maxClipDistances,max_clip_distances)
RESOURCE(MaxComputeWorkGroupCountX,maxComputeWorkGroupCountX,max_compute_work_group_count_x)
RESOURCE(MaxComputeWorkGroupCountY,maxComputeWorkGroupCountY,max_compute_work_group_count_y)
RESOURCE(MaxComputeWorkGroupCountZ,maxComputeWorkGroupCountZ,max_compute_work_group_count_z)
RESOURCE(MaxComputeWorkGroupSizeX,maxComputeWorkGroupSizeX,max_compute_work_group_size_x)
RESOURCE(MaxComputeWorkGroupSizeY,maxComputeWorkGroupSizeY,max_compute_work_group_size_y)
RESOURCE(MaxComputeWorkGroupSizeZ,maxComputeWorkGroupSizeZ,max_compute_work_group_size_z)
RESOURCE(MaxComputeUniformComponents,maxComputeUniformComponents,max_compute_uniform_components)
RESOURCE(MaxComputeTextureImageUnits,maxComputeTextureImageUnits,max_compute_texture_image_units)
RESOURCE(MaxComputeImageUniforms,maxComputeImageUniforms,max_compute_image_uniforms)
RESOURCE(MaxComputeAtomicCounters,maxComputeAtomicCounters,max_compute_atomic_counters)
RESOURCE(MaxComputeAtomicCounterBuffers,maxComputeAtomicCounterBuffers,max_compute_atomic_counter_buffers)
RESOURCE(MaxVaryingComponents,maxVaryingComponents,max_varying_components)
RESOURCE(MaxVertexOutputComponents,maxVertexOutputComponents,max_vertex_output_components)
RESOURCE(MaxGeometryInputComponents,maxGeometryInputComponents,max_geometry_input_components)
RESOURCE(MaxGeometryOutputComponents,maxGeometryOutputComponents,max_geometry_output_components)
RESOURCE(MaxFragmentInputComponents,maxFragmentInputComponents,max_fragment_input_components)
RESOURCE(MaxImageUnits,maxImageUnits,max_image_units)
RESOURCE(MaxCombinedImageUnitsAndFragmentOutputs,maxCombinedImageUnitsAndFragmentOutputs,max_combined_image_units_and_fragment_outputs)
RESOURCE(MaxCombinedShaderOutputResources,maxCombinedShaderOutputResources,max_combined_shader_output_resources)
RESOURCE(MaxImageSamples,maxImageSamples,max_image_samples)
RESOURCE(MaxVertexImageUniforms,maxVertexImageUniforms,max_vertex_image_uniforms)
RESOURCE(MaxTessControlImageUniforms,maxTessControlImageUniforms,max_tess_control_image_uniforms)
RESOURCE(MaxTessEvaluationImageUniforms,maxTessEvaluationImageUniforms,max_tess_evaluation_image_uniforms)
RESOURCE(MaxGeometryImageUniforms,maxGeometryImageUniforms,max_geometry_image_uniforms)
RESOURCE(MaxFragmentImageUniforms,maxFragmentImageUniforms,max_fragment_image_uniforms)
RESOURCE(MaxCombinedImageUniforms,maxCombinedImageUniforms,max_combined_image_uniforms)
RESOURCE(MaxGeometryTextureImageUnits,maxGeometryTextureImageUnits,max_geometry_texture_image_units)
RESOURCE(MaxGeometryOutputVertices,maxGeometryOutputVertices,max_geometry_output_vertices)
RESOURCE(MaxGeometryTotalOutputComponents,maxGeometryTotalOutputComponents,max_geometry_total_output_components)
RESOURCE(MaxGeometryUniformComponents,maxGeometryUniformComponents,max_geometry_uniform_components)
RESOURCE(MaxGeometryVaryingComponents,maxGeometryVaryingComponents,max_geometry_varying_components)
RESOURCE(MaxTessControlInputComponents,maxTessControlInputComponents,max_tess_control_input_components)
RESOURCE(MaxTessControlOutputComponents,maxTessControlOutputComponents,max_tess_control_output_components)
RESOURCE(MaxTessControlTextureImageUnits,maxTessControlTextureImageUnits,max_tess_control_texture_image_units)
RESOURCE(MaxTessControlUniformComponents,maxTessControlUniformComponents,max_tess_control_uniform_components)
RESOURCE(MaxTessControlTotalOutputComponents,maxTessControlTotalOutputComponents,max_tess_control_total_output_components)
RESOURCE(MaxTessEvaluationInputComponents,maxTessEvaluationInputComponents,max_tess_evaluation_input_components)
RESOURCE(MaxTessEvaluationOutputComponents,maxTessEvaluationOutputComponents,max_tess_evaluation_output_components)
RESOURCE(MaxTessEvaluationTextureImageUnits,maxTessEvaluationTextureImageUnits,max_tess_evaluation_texture_image_units)
RESOURCE(MaxTessEvaluationUniformComponents,maxTessEvaluationUniformComponents,max_tess_evaluation_uniform_components)
RESOURCE(MaxTessPatchComponents,maxTessPatchComponents,max_tess_patch_components)
RESOURCE(MaxPatchVertices,maxPatchVertices,max_patch_vertices)
RESOURCE(MaxTessGenLevel,maxTessGenLevel,max_tess_gen_level)
RESOURCE(MaxViewports,maxViewports,max_viewports)
RESOURCE(MaxVertexAtomicCounters,maxVertexAtomicCounters,max_vertex_atomic_counters)
RESOURCE(MaxTessControlAtomicCounters,maxTessControlAtomicCounters,max_tess_control_atomic_counters)
RESOURCE(MaxTessEvaluationAtomicCounters,maxTessEvaluationAtomicCounters,max_tess_evaluation_atomic_counters)
RESOURCE(MaxGeometryAtomicCounters,maxGeometryAtomicCounters,max_geometry_atomic_counters)
RESOURCE(MaxFragmentAtomicCounters,maxFragmentAtomicCounters,max_fragment_atomic_counters)
RESOURCE(MaxCombinedAtomicCounters,maxCombinedAtomicCounters,max_combined_atomic_counters)
RESOURCE(MaxAtomicCounterBindings,maxAtomicCounterBindings,max_atomic_counter_bindings)
RESOURCE(MaxVertexAtomicCounterBuffers,maxVertexAtomicCounterBuffers,max_vertex_atomic_counter_buffers)
RESOURCE(MaxTessControlAtomicCounterBuffers,maxTessControlAtomicCounterBuffers,max_tess_control_atomic_counter_buffers)
RESOURCE(MaxTessEvaluationAtomicCounterBuffers,maxTessEvaluationAtomicCounterBuffers,max_tess_evaluation_atomic_counter_buffers)
RESOURCE(MaxGeometryAtomicCounterBuffers,maxGeometryAtomicCounterBuffers,max_geometry_atomic_counter_buffers)
RESOURCE(MaxFragmentAtomicCounterBuffers,maxFragmentAtomicCounterBuffers,max_fragment_atomic_counter_buffers)
RESOURCE(MaxCombinedAtomicCounterBuffers,maxCombinedAtomicCounterBuffers,max_combined_atomic_counter_buffers)
RESOURCE(MaxAtomicCounterBufferSize,maxAtomicCounterBufferSize,max_atomic_counter_buffer_size)
RESOURCE(MaxTransformFeedbackBuffers,maxTransformFeedbackBuffers,max_transform_feedback_buffers)
RESOURCE(MaxTransformFeedbackInterleavedComponents,maxTransformFeedbackInterleavedComponents,max_transform_feedback_interleaved_components)
RESOURCE(MaxCullDistances,maxCullDistances,max_cull_distances)
RESOURCE(MaxCombinedClipAndCullDistances,maxCombinedClipAndCullDistances,max_combined_clip_and_cull_distances)
RESOURCE(MaxSamples,maxSamples,max_samples)
RESOURCE(MaxMeshOutputVerticesNV, maxMeshOutputVerticesNV,
max_mesh_output_vertices_nv)
RESOURCE(MaxMeshOutputPrimitivesNV, maxMeshOutputPrimitivesNV,
max_mesh_output_primitives_nv)
RESOURCE(MaxMeshWorkGroupSizeX_NV, maxMeshWorkGroupSizeX_NV,
max_mesh_work_group_size_x_nv)
RESOURCE(MaxMeshWorkGroupSizeY_NV, maxMeshWorkGroupSizeY_NV,
max_mesh_work_group_size_y_nv)
RESOURCE(MaxMeshWorkGroupSizeZ_NV, maxMeshWorkGroupSizeZ_NV,
max_mesh_work_group_size_z_nv)
RESOURCE(MaxTaskWorkGroupSizeX_NV, maxTaskWorkGroupSizeX_NV,
max_task_work_group_size_x_nv)
RESOURCE(MaxTaskWorkGroupSizeY_NV, maxTaskWorkGroupSizeY_NV,
max_task_work_group_size_y_nv)
RESOURCE(MaxTaskWorkGroupSizeZ_NV, maxTaskWorkGroupSizeZ_NV,
max_task_work_group_size_z_nv)
RESOURCE(MaxMeshViewCountNV, maxMeshViewCountNV, max_mesh_view_count_nv)
RESOURCE(MaxMeshOutputVerticesEXT, maxMeshOutputVerticesEXT,
max_mesh_output_vertices_ext)
RESOURCE(MaxMeshOutputPrimitivesEXT, maxMeshOutputPrimitivesEXT,
max_mesh_output_primitives_ext)
RESOURCE(MaxMeshWorkGroupSizeX_EXT, maxMeshWorkGroupSizeX_EXT,
max_mesh_work_group_size_x_ext)
RESOURCE(MaxMeshWorkGroupSizeY_EXT, maxMeshWorkGroupSizeY_EXT,
max_mesh_work_group_size_y_ext)
RESOURCE(MaxMeshWorkGroupSizeZ_EXT, maxMeshWorkGroupSizeZ_EXT,
max_mesh_work_group_size_z_ext)
RESOURCE(MaxTaskWorkGroupSizeX_EXT, maxTaskWorkGroupSizeX_EXT,
max_task_work_group_size_x_ext)
RESOURCE(MaxTaskWorkGroupSizeY_EXT, maxTaskWorkGroupSizeY_EXT,
max_task_work_group_size_y_ext)
RESOURCE(MaxTaskWorkGroupSizeZ_EXT, maxTaskWorkGroupSizeZ_EXT,
max_task_work_group_size_z_ext)
RESOURCE(MaxMeshViewCountEXT, maxMeshViewCountEXT, max_mesh_view_count_ext)
RESOURCE(MaxDualSourceDrawBuffersEXT, maxDualSourceDrawBuffersEXT,
max_dual_source_draw_buffers_ext)

View File

@@ -0,0 +1,36 @@
// 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.
#ifndef LIBSHADERC_UTIL_SHADER_STAGE_H_
#define LIBSHADERC_UTIL_SHADER_STAGE_H_
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "glslang/Public/ShaderLang.h"
#include "libshaderc_util/string_piece.h"
namespace shaderc_util {
// Given a string representing a stage, returns the glslang EShLanguage for it.
// If the stage string is not recognized, returns EShLangCount.
EShLanguage MapStageNameToLanguage(
const shaderc_util::string_piece& stage_name);
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_SHADER_STAGE_H_

View File

@@ -0,0 +1,68 @@
// Copyright 2016 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.
#ifndef LIBSHADERC_UTIL_INC_SPIRV_TOOLS_WRAPPER_H
#define LIBSHADERC_UTIL_INC_SPIRV_TOOLS_WRAPPER_H
#include <string>
#include <vector>
#include "spirv-tools/libspirv.hpp"
#include "libshaderc_util/compiler.h"
#include "libshaderc_util/string_piece.h"
namespace shaderc_util {
// Assembles the given assembly. On success, returns true, writes the assembled
// binary to *binary, and clears *errors. Otherwise, writes the error message
// into *errors.
bool SpirvToolsAssemble(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const string_piece assembly, spv_binary* binary,
std::string* errors);
// Disassembles the given binary. Returns true and writes the disassembled text
// to *text_or_error if successful. Otherwise, writes the error message to
// *text_or_error.
bool SpirvToolsDisassemble(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const std::vector<uint32_t>& binary,
std::string* text_or_error);
// The ids of a list of supported optimization passes.
enum class PassId {
// SPIRV-Tools standard recipes
kLegalizationPasses,
kPerformancePasses,
kSizePasses,
// SPIRV-Tools specific passes
kNullPass,
kStripDebugInfo,
kCompactIds,
};
// Optimizes the given binary. Passes are registered in the exact order as shown
// in enabled_passes, without de-duplication. Returns true and writes the
// optimized binary back to *binary if successful. Otherwise, writes errors to
// *errors and the content of binary may be in an invalid state.
bool SpirvToolsOptimize(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const std::vector<PassId>& enabled_passes,
spvtools::OptimizerOptions& optimizer_options,
std::vector<uint32_t>* binary, std::string* errors);
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_INC_SPIRV_TOOLS_WRAPPER_H

View File

@@ -0,0 +1,357 @@
// 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.
#ifndef LIBSHADERC_UTIL_STRING_PIECE_H_
#define LIBSHADERC_UTIL_STRING_PIECE_H_
#include <cassert>
#include <cstring>
#include <ostream>
#include <vector>
namespace shaderc_util {
// Provides a read-only view into a string (cstring or std::string).
// This must be created after the string in question, and cannot
// outlive the memory of the string in question.
// Any operations that may modify the location or size of the
// original data render the associated string_piece invalid.
class string_piece {
public:
typedef const char* iterator;
static const size_t npos = -1;
string_piece() {}
string_piece(const char* begin, const char* end) : begin_(begin), end_(end) {
assert((begin == nullptr) == (end == nullptr) &&
"either both begin and end must be nullptr or neither must be");
}
string_piece(const char* string) : begin_(string), end_(string) {
if (string) {
end_ += strlen(string);
}
}
string_piece(const std::string& str) {
if (!str.empty()) {
begin_ = &(str.front());
end_ = &(str.back()) + 1;
}
}
string_piece(const string_piece& other) {
begin_ = other.begin_;
end_ = other.end_;
}
string_piece& operator=(const string_piece& other) = default;
// Clears the string_piece removing any reference to the original string.
void clear() {
begin_ = nullptr;
end_ = nullptr;
}
// Returns a pointer to the data contained in the underlying string.
// If there is no underlying string, returns a nullptr.
const char* data() const { return begin_; }
// Returns an std::string copy of the internal data.
std::string str() const { return std::string(begin_, end_); }
// Returns a string_piece that points to a substring in the original string.
string_piece substr(size_t pos, size_t len = npos) const {
assert(len == npos || pos + len <= size());
return string_piece(begin_ + pos, len == npos ? end_ : begin_ + pos + len);
}
// Takes any function object predicate that takes a char and returns a
// boolean.
// Returns the index of the first element that does not return true for the
// predicate.
// Returns string_piece::npos if all elements match.
template <typename T>
size_t find_first_not_matching(T callee) {
for (auto it = begin_; it != end_; ++it) {
if (!callee(*it)) {
return it - begin_;
}
}
return npos;
}
// Returns the index of the first character that does not match any character
// in the input string_piece.
// The search only includes characters at or after position pos.
// Returns string_piece::npos if all match.
size_t find_first_not_of(const string_piece& to_search,
size_t pos = 0) const {
if (pos >= size()) {
return npos;
}
for (auto it = begin_ + pos; it != end_; ++it) {
if (to_search.find_first_of(*it) == npos) {
return it - begin_;
}
}
return npos;
}
// Returns find_first_not_of(str, pos) where str is a string_piece
// containing only to_search.
size_t find_first_not_of(char to_search, size_t pos = 0) const {
return find_first_not_of(string_piece(&to_search, &to_search + 1), pos);
}
// Returns the index of the first character that matches any character in the
// input string_piece.
// The search only includes characters at or after position pos.
// Returns string_piece::npos if there is no match.
size_t find_first_of(const string_piece& to_search, size_t pos = 0) const {
if (pos >= size()) {
return npos;
}
for (auto it = begin_ + pos; it != end_; ++it) {
for (char c : to_search) {
if (c == *it) {
return it - begin_;
}
}
}
return npos;
}
// Returns find_first_of(str, pos) where str is a string_piece
// containing only to_search.
size_t find_first_of(char to_search, size_t pos = 0) const {
return find_first_of(string_piece(&to_search, &to_search + 1), pos);
}
// Returns the index of the last character that matches any character in the
// input string_piece.
// The search only includes characters at or before position pos.
// Returns string_piece::npos if there is no match.
size_t find_last_of(const string_piece& to_search, size_t pos = npos) const {
if (empty()) return npos;
if (pos >= size()) {
pos = size();
}
auto it = begin_ + pos + 1;
do {
--it;
if (to_search.find_first_of(*it) != npos) {
return it - begin_;
}
} while (it != begin_);
return npos;
}
// Returns find_last_of(str, pos) where str is a string_piece
// containing only to_search.
size_t find_last_of(char to_search, size_t pos = npos) const {
return find_last_of(string_piece(&to_search, &to_search + 1), pos);
}
// Returns the index of the last character that does not match any character
// in the input string_piece.
// The search only includes characters at or before position pos.
// Returns string_piece::npos if there is no match.
size_t find_last_not_of(const string_piece& to_search,
size_t pos = npos) const {
if (empty()) return npos;
if (pos >= size()) {
pos = size();
}
auto it = begin_ + pos + 1;
do {
--it;
if (to_search.find_first_of(*it) == npos) {
return it - begin_;
}
} while (it != begin_);
return npos;
}
// Returns find_last_not_of(str, pos) where str is a string_piece
// containing only to_search.
size_t find_last_not_of(char to_search, size_t pos = 0) const {
return find_last_not_of(string_piece(&to_search, &to_search + 1), pos);
}
// Continuously removes characters appearing in chars_to_strip from the left.
string_piece lstrip(const string_piece& chars_to_strip) const {
iterator begin = begin_;
for (; begin < end_; ++begin)
if (chars_to_strip.find_first_of(*begin) == npos) break;
if (begin >= end_) return string_piece();
return string_piece(begin, end_);
}
// Continuously removes characters appearing in chars_to_strip from the right.
string_piece rstrip(const string_piece& chars_to_strip) const {
iterator end = end_;
for (; begin_ < end; --end)
if (chars_to_strip.find_first_of(*(end - 1)) == npos) break;
if (begin_ >= end) return string_piece();
return string_piece(begin_, end);
}
// Continuously removes characters appearing in chars_to_strip from both
// sides.
string_piece strip(const string_piece& chars_to_strip) const {
return lstrip(chars_to_strip).rstrip(chars_to_strip);
}
string_piece strip_whitespace() const { return strip(" \t\n\r\f\v"); }
// Returns the character at index i in the string_piece.
const char& operator[](size_t i) const { return *(begin_ + i); }
// Standard comparison operator.
bool operator==(const string_piece& other) const {
// Either end_ and _begin_ are nullptr or neither of them are.
assert(((end_ == nullptr) == (begin_ == nullptr)));
assert(((other.end_ == nullptr) == (other.begin_ == nullptr)));
if (size() != other.size()) {
return false;
}
return (memcmp(begin_, other.begin_, end_ - begin_) == 0);
}
bool operator!=(const string_piece& other) const {
return !operator==(other);
}
// Returns an iterator to the first element.
iterator begin() const { return begin_; }
// Returns an iterator to one past the last element.
iterator end() const { return end_; }
const char& front() const {
assert(!empty());
return *begin_;
}
const char& back() const {
assert(!empty());
return *(end_ - 1);
}
// Returns true is this string_piece starts with the same
// characters as other.
bool starts_with(const string_piece& other) const {
const char* iter = begin_;
const char* other_iter = other.begin();
while (iter != end_ && other_iter != other.end()) {
if (*iter++ != *other_iter++) {
return false;
}
}
return other_iter == other.end();
}
// Returns the index of the start of the first substring that matches
// the input string_piece.
// The search only includes substrings starting at or after position pos.
// Returns npos if the string cannot be found.
size_t find(const string_piece& substr, size_t pos = 0) const {
if (empty()) return npos;
if (pos >= size()) return npos;
if (substr.empty()) return 0;
for (auto it = begin_ + pos;
end() - it >= static_cast<decltype(end() - it)>(substr.size()); ++it) {
if (string_piece(it, end()).starts_with(substr)) return it - begin_;
}
return npos;
}
// Returns the index of the start of the first character that matches
// the input character.
// The search only includes substrings starting at or after position pos.
// Returns npos if the character cannot be found.
size_t find(char character, size_t pos = 0) const {
return find_first_of(character, pos);
}
// Returns true if the string_piece is empty.
bool empty() const { return begin_ == end_; }
// Returns the number of characters in the string_piece.
size_t size() const { return end_ - begin_; }
// Returns a vector of string_pieces representing delimiter delimited
// fields found. If the keep_delimiter parameter is true, then each
// delimiter character is kept with the string to its left.
std::vector<string_piece> get_fields(char delimiter,
bool keep_delimiter = false) const {
std::vector<string_piece> fields;
size_t first = 0;
size_t field_break = find_first_of(delimiter);
while (field_break != npos) {
fields.push_back(substr(first, field_break - first + keep_delimiter));
first = field_break + 1;
field_break = find_first_of(delimiter, first);
}
if (size() - first > 0) {
fields.push_back(substr(first, size() - first));
}
return fields;
}
friend std::ostream& operator<<(std::ostream& os, const string_piece& piece);
private:
// It is expected that begin_ and end_ will both be null or
// they will both point to valid pieces of memory, but it is invalid
// to have one of them being nullptr and the other not.
string_piece::iterator begin_ = nullptr;
string_piece::iterator end_ = nullptr;
};
inline std::ostream& operator<<(std::ostream& os, const string_piece& piece) {
// Either end_ and _begin_ are nullptr or neither of them are.
assert(((piece.end_ == nullptr) == (piece.begin_ == nullptr)));
if (piece.end_ != piece.begin_) {
os.write(piece.begin_, piece.end_ - piece.begin_);
}
return os;
}
inline bool operator==(const char* first, const string_piece second) {
return second == string_piece(first);
}
inline bool operator!=(const char* first, const string_piece second) {
return !operator==(first, second);
}
}
namespace std {
template <>
struct hash<shaderc_util::string_piece> {
size_t operator()(const shaderc_util::string_piece& piece) const {
// djb2 algorithm.
size_t hash = 5381;
for (char c : piece) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
};
}
#endif // LIBSHADERC_UTIL_STRING_PIECE_H_

View File

@@ -0,0 +1,32 @@
// 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.
#ifndef LIBSHADERC_UTIL_UNIVERSAL_UNISTD_H_
#define LIBSHADERC_UTIL_UNIVERSAL_UNISTD_H_
#ifndef _MSC_VER
#include <unistd.h>
#else
// Minimal set of <unistd> needed to compile on windows.
#include <io.h>
#define access _access
// https://msdn.microsoft.com/en-us/library/1w06ktdy.aspx
// Defines these constants.
#define R_OK 4
#define W_OK 2
#endif //_MSC_VER
#endif // LIBSHADERC_UTIL_UNIVERSAL_UNISTD_H_

View File

@@ -0,0 +1,61 @@
// 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.
#ifndef LIBSHADERC_UTIL_INC_VERSION_PROFILE_H_
#define LIBSHADERC_UTIL_INC_VERSION_PROFILE_H_
#include <string>
#include "glslang/MachineIndependent/Versions.h"
namespace shaderc_util {
// Returns true if the given version is an accepted GLSL (ES) version.
inline bool IsKnownVersion(int version) {
switch (version) {
case 100:
case 110:
case 120:
case 130:
case 140:
case 150:
case 300:
case 310:
case 320:
case 330:
case 400:
case 410:
case 420:
case 430:
case 440:
case 450:
case 460:
return true;
default:
break;
}
return false;
}
// Given a string version_profile containing both version and profile, decodes
// it and puts the decoded version in version, decoded profile in profile.
// Returns true if decoding is successful and version and profile are accepted.
// This does not validate the version number against the profile. For example,
// "460es" doesn't make sense (yet), but is still accepted.
bool ParseVersionProfile(const std::string& version_profile, int* version,
EProfile* profile);
} // namespace shaderc_util
#endif // LIBSHADERC_UTIL_INC_VERSION_PROFILE_H_

View File

@@ -0,0 +1,62 @@
// 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.
#include "libshaderc_util/args.h"
#include <iomanip>
#include <sstream>
namespace shaderc_util {
bool GetOptionArgument(int argc, char** argv, int* index,
const std::string& option,
string_piece* option_argument) {
const string_piece arg = argv[*index];
assert(arg.starts_with(option));
if (arg.size() != option.size()) {
*option_argument = arg.substr(option.size());
return true;
}
if (option.back() == '=') {
*option_argument = "";
return true;
}
if (++(*index) >= argc) return false;
*option_argument = argv[*index];
return true;
}
bool ParseUint32(const std::string& str, uint32_t* value) {
std::istringstream iss(str);
iss >> std::setbase(0);
iss >> *value;
// We should have read something.
bool ok = !str.empty() && !iss.bad();
// It should have been all the text.
ok = ok && iss.eof();
// It should have been in range.
ok = ok && !iss.fail();
// Work around a bugs in various C++ standard libraries.
// Count any negative number as an error, including "-0".
ok = ok && (str[0] != '-');
return ok;
}
} // namespace shaderc_util

View File

@@ -0,0 +1,826 @@
// 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 <cstdint>
#include <iomanip>
#include <sstream>
#include <thread>
#include <tuple>
#include "SPIRV/GlslangToSpv.h"
#include "libshaderc_util/format.h"
#include "libshaderc_util/io_shaderc.h"
#include "libshaderc_util/message.h"
#include "libshaderc_util/resources.h"
#include "libshaderc_util/shader_stage.h"
#include "libshaderc_util/spirv_tools_wrapper.h"
#include "libshaderc_util/string_piece.h"
#include "libshaderc_util/version_profile.h"
#include "spirv-tools/libspirv.hpp"
namespace {
using shaderc_util::string_piece;
// For use with glslang parsing calls.
const bool kNotForwardCompatible = false;
// Returns true if #line directive sets the line number for the next line in the
// given version and profile.
inline bool LineDirectiveIsForNextLine(int version, EProfile profile) {
return profile == EEsProfile || version >= 330;
}
// Returns a #line directive whose arguments are line and filename.
inline std::string GetLineDirective(int line, const string_piece& filename) {
return "#line " + std::to_string(line) + " \"" + filename.str() + "\"\n";
}
// Given a canonicalized #line directive (starting exactly with "#line", using
// single spaces to separate different components, and having an optional
// newline at the end), returns the line number and string name/number. If no
// string name/number is provided, the second element in the returned pair is an
// empty string_piece. Behavior is undefined if the directive parameter is not a
// canonicalized #line directive.
std::pair<int, string_piece> DecodeLineDirective(string_piece directive) {
const string_piece kLineDirective = "#line ";
assert(directive.starts_with(kLineDirective));
directive = directive.substr(kLineDirective.size());
const int line = std::atoi(directive.data());
const size_t space_loc = directive.find_first_of(' ');
if (space_loc == string_piece::npos) return std::make_pair(line, "");
directive = directive.substr(space_loc);
directive = directive.strip("\" \n");
return std::make_pair(line, directive);
}
// Returns the Glslang message rules for the given target environment,
// source language, and whether we want HLSL offset rules. We assume
// only valid combinations are used.
EShMessages GetMessageRules(shaderc_util::Compiler::TargetEnv env,
shaderc_util::Compiler::SourceLanguage lang,
bool hlsl_offsets, bool hlsl_16bit_types,
bool debug_info) {
using shaderc_util::Compiler;
EShMessages result = EShMsgCascadingErrors;
if (lang == Compiler::SourceLanguage::HLSL) {
result = static_cast<EShMessages>(result | EShMsgReadHlsl);
}
switch (env) {
case Compiler::TargetEnv::OpenGLCompat:
// The compiler will have already errored out before now.
// But we need to handle this enum.
break;
case Compiler::TargetEnv::OpenGL:
result = static_cast<EShMessages>(result | EShMsgSpvRules);
break;
case Compiler::TargetEnv::Vulkan:
result =
static_cast<EShMessages>(result | EShMsgSpvRules | EShMsgVulkanRules);
break;
}
if (hlsl_offsets) {
result = static_cast<EShMessages>(result | EShMsgHlslOffsets);
}
if (hlsl_16bit_types) {
result = static_cast<EShMessages>(result | EShMsgHlslEnable16BitTypes);
}
if (debug_info) {
result = static_cast<EShMessages>(result | EShMsgDebugInfo);
}
return result;
}
} // anonymous namespace
namespace shaderc_util {
unsigned int GlslangInitializer::initialize_count_ = 0;
std::mutex* GlslangInitializer::glslang_mutex_ = nullptr;
GlslangInitializer::GlslangInitializer() {
static std::mutex first_call_mutex;
// If this is the first call, glslang_mutex_ needs to be created, but in
// thread safe manner.
{
const std::lock_guard<std::mutex> first_call_lock(first_call_mutex);
if (glslang_mutex_ == nullptr) {
glslang_mutex_ = new std::mutex();
}
}
const std::lock_guard<std::mutex> glslang_lock(*glslang_mutex_);
if (initialize_count_ == 0) {
glslang::InitializeProcess();
}
initialize_count_++;
}
GlslangInitializer::~GlslangInitializer() {
const std::lock_guard<std::mutex> glslang_lock(*glslang_mutex_);
initialize_count_--;
if (initialize_count_ == 0) {
glslang::FinalizeProcess();
// There is no delete for glslang_mutex_ here, because we cannot guarantee
// there isn't a caller waiting for glslang_mutex_ in GlslangInitializer().
//
// This means that this class does leak one std::mutex worth of memory after
// the final instance is destroyed, but this allows us to defer allocating
// and constructing until we are sure we need to.
}
}
void Compiler::SetLimit(Compiler::Limit limit, int value) {
switch (limit) {
#define RESOURCE(NAME, FIELD, CNAME) \
case Limit::NAME: \
limits_.FIELD = value; \
break;
#include "libshaderc_util/resources.inc"
#undef RESOURCE
}
}
int Compiler::GetLimit(Compiler::Limit limit) const {
switch (limit) {
#define RESOURCE(NAME, FIELD, CNAME) \
case Limit::NAME: \
return limits_.FIELD;
#include "libshaderc_util/resources.inc"
#undef RESOURCE
}
return 0; // Unreachable
}
std::tuple<bool, std::vector<uint32_t>, size_t> Compiler::Compile(
const string_piece& input_source_string, EShLanguage forced_shader_stage,
const std::string& error_tag, const char* entry_point_name,
const std::function<EShLanguage(std::ostream* error_stream,
const string_piece& error_tag)>&
stage_callback,
CountingIncluder& includer, OutputType output_type,
std::ostream* error_stream, size_t* total_warnings, size_t* total_errors) const {
// Compilation results to be returned:
// Initialize the result tuple as a failed compilation. In error cases, we
// should return result_tuple directly without setting its members.
auto result_tuple =
std::make_tuple(false, std::vector<uint32_t>(), (size_t)0u);
// Get the reference of the members of the result tuple. We should set their
// values for succeeded compilation before returning the result tuple.
bool& succeeded = std::get<0>(result_tuple);
std::vector<uint32_t>& compilation_output_data = std::get<1>(result_tuple);
size_t& compilation_output_data_size_in_bytes = std::get<2>(result_tuple);
// Check target environment.
const auto target_client_info = GetGlslangClientInfo(
error_tag, target_env_, target_env_version_,
target_spirv_version_, target_spirv_version_is_forced_);
if (!target_client_info.error.empty()) {
*error_stream << target_client_info.error;
*total_warnings = 0;
*total_errors = 1;
return result_tuple;
}
EShLanguage used_shader_stage = forced_shader_stage;
const std::string macro_definitions =
shaderc_util::format(predefined_macros_, "#define ", " ", "\n");
const std::string pound_extension =
"#extension GL_GOOGLE_include_directive : enable\n";
const std::string preamble = macro_definitions + pound_extension;
std::string preprocessed_shader;
// If only preprocessing, we definitely need to preprocess. Otherwise, if
// we don't know the stage until now, we need the preprocessed shader to
// deduce the shader stage.
if (output_type == OutputType::PreprocessedText ||
used_shader_stage == EShLangCount) {
bool success;
std::string glslang_errors;
std::tie(success, preprocessed_shader, glslang_errors) =
PreprocessShader(error_tag, input_source_string, preamble, includer);
success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,
/* suppress_warnings = */ true,
glslang_errors.c_str(), total_warnings,
total_errors);
if (!success) return result_tuple;
// Because of the behavior change of the #line directive, the #line
// directive introducing each file's content must use the syntax for the
// specified version. So we need to probe this shader's version and
// profile.
int version;
EProfile profile;
std::tie(version, profile) = DeduceVersionProfile(preprocessed_shader);
const bool is_for_next_line = LineDirectiveIsForNextLine(version, profile);
preprocessed_shader =
CleanupPreamble(preprocessed_shader, error_tag, pound_extension,
includer.num_include_directives(), is_for_next_line);
if (output_type == OutputType::PreprocessedText) {
// Set the values of the result tuple.
succeeded = true;
compilation_output_data = ConvertStringToVector(preprocessed_shader);
compilation_output_data_size_in_bytes = preprocessed_shader.size();
return result_tuple;
} else if (used_shader_stage == EShLangCount) {
std::string errors;
std::tie(used_shader_stage, errors) =
GetShaderStageFromSourceCode(error_tag, preprocessed_shader);
if (!errors.empty()) {
*error_stream << errors;
return result_tuple;
}
if (used_shader_stage == EShLangCount) {
if ((used_shader_stage = stage_callback(error_stream, error_tag)) ==
EShLangCount) {
return result_tuple;
}
}
}
}
// Parsing requires its own Glslang symbol tables.
glslang::TShader shader(used_shader_stage);
const char* shader_strings = input_source_string.data();
const int shader_lengths = static_cast<int>(input_source_string.size());
const char* string_names = error_tag.c_str();
shader.setStringsWithLengthsAndNames(&shader_strings, &shader_lengths,
&string_names, 1);
shader.setPreamble(preamble.c_str());
shader.setEntryPoint(entry_point_name);
shader.setAutoMapBindings(auto_bind_uniforms_);
if (auto_combined_image_sampler_) {
shader.setTextureSamplerTransformMode(EShTexSampTransUpgradeTextureRemoveSampler);
}
shader.setAutoMapLocations(auto_map_locations_);
const auto& bases = auto_binding_base_[static_cast<int>(used_shader_stage)];
shader.setShiftImageBinding(bases[static_cast<int>(UniformKind::Image)]);
shader.setShiftSamplerBinding(bases[static_cast<int>(UniformKind::Sampler)]);
shader.setShiftTextureBinding(bases[static_cast<int>(UniformKind::Texture)]);
shader.setShiftUboBinding(bases[static_cast<int>(UniformKind::Buffer)]);
shader.setShiftSsboBinding(
bases[static_cast<int>(UniformKind::StorageBuffer)]);
shader.setShiftUavBinding(
bases[static_cast<int>(UniformKind::UnorderedAccessView)]);
shader.setHlslIoMapping(hlsl_iomap_);
shader.setResourceSetBinding(
hlsl_explicit_bindings_[static_cast<int>(used_shader_stage)]);
shader.setEnvClient(target_client_info.client,
target_client_info.client_version);
shader.setEnvTarget(target_client_info.target_language,
target_client_info.target_language_version);
if (hlsl_functionality1_enabled_) {
shader.setEnvTargetHlslFunctionality1();
}
if (vulkan_rules_relaxed_) {
glslang::EShSource language = glslang::EShSourceNone;
switch(source_language_) {
case SourceLanguage::GLSL:
language = glslang::EShSourceGlsl;
break;
case SourceLanguage::HLSL:
language = glslang::EShSourceHlsl;
break;
}
// This option will only be used if the Vulkan client is used.
// If new versions of GL_KHR_vulkan_glsl come out, it would make sense to
// let callers specify which version to use. For now, just use 100.
shader.setEnvInput(language, used_shader_stage, glslang::EShClientVulkan, 100);
shader.setEnvInputVulkanRulesRelaxed();
}
shader.setInvertY(invert_y_enabled_);
shader.setNanMinMaxClamp(nan_clamp_);
const EShMessages rules =
GetMessageRules(target_env_, source_language_, hlsl_offsets_,
hlsl_16bit_types_enabled_, generate_debug_info_);
bool success = shader.parse(&limits_, default_version_, default_profile_,
force_version_profile_, kNotForwardCompatible,
rules, includer);
success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,
suppress_warnings_, shader.getInfoLog(),
total_warnings, total_errors);
if (!success) return result_tuple;
glslang::TProgram program;
program.addShader(&shader);
success = program.link(EShMsgDefault) && program.mapIO();
success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,
suppress_warnings_, program.getInfoLog(),
total_warnings, total_errors);
if (!success) return result_tuple;
// 'spirv' is an alias for the compilation_output_data. This alias is added
// to serve as an input for the call to DissassemblyBinary.
std::vector<uint32_t>& spirv = compilation_output_data;
glslang::SpvOptions options;
options.generateDebugInfo = generate_debug_info_;
options.disableOptimizer = true;
options.optimizeSize = false;
options.emitNonSemanticShaderDebugInfo =
generate_debug_info_ && emit_non_semantic_debug_info_;
options.emitNonSemanticShaderDebugSource =
generate_debug_info_ && emit_non_semantic_debug_info_;
// Note the call to GlslangToSpv also populates compilation_output_data.
glslang::GlslangToSpv(*program.getIntermediate(used_shader_stage), spirv,
&options);
// Set the tool field (the top 16-bits) in the generator word to
// 'Shaderc over Glslang'.
const uint32_t shaderc_generator_word = 13; // From SPIR-V XML Registry
const uint32_t generator_word_index = 2; // SPIR-V 2.3: Physical layout
assert(spirv.size() > generator_word_index);
spirv[generator_word_index] =
(spirv[generator_word_index] & 0xffff) | (shaderc_generator_word << 16);
std::vector<PassId> opt_passes;
if (hlsl_legalization_enabled_ && source_language_ == SourceLanguage::HLSL) {
// If from HLSL, run this passes to "legalize" the SPIR-V for Vulkan
// eg. forward and remove memory writes of opaque types.
opt_passes.push_back(PassId::kLegalizationPasses);
}
opt_passes.insert(opt_passes.end(), enabled_opt_passes_.begin(),
enabled_opt_passes_.end());
if (!opt_passes.empty()) {
spvtools::OptimizerOptions opt_options;
opt_options.set_preserve_bindings(preserve_bindings_);
std::string opt_errors;
if (!SpirvToolsOptimize(target_env_, target_env_version_, opt_passes,
opt_options, &spirv, &opt_errors)) {
*error_stream << "shaderc: internal error: compilation succeeded but "
"failed to optimize: "
<< opt_errors << "\n";
return result_tuple;
}
}
if (output_type == OutputType::SpirvAssemblyText) {
std::string text_or_error;
if (!SpirvToolsDisassemble(target_env_, target_env_version_, spirv,
&text_or_error)) {
*error_stream << "shaderc: internal error: compilation succeeded but "
"failed to disassemble: "
<< text_or_error << "\n";
return result_tuple;
}
succeeded = true;
compilation_output_data = ConvertStringToVector(text_or_error);
compilation_output_data_size_in_bytes = text_or_error.size();
return result_tuple;
} else {
succeeded = true;
// Note compilation_output_data is already populated in GlslangToSpv().
compilation_output_data_size_in_bytes = spirv.size() * sizeof(spirv[0]);
return result_tuple;
}
}
void Compiler::AddMacroDefinition(const char* macro, size_t macro_length,
const char* definition,
size_t definition_length) {
predefined_macros_[std::string(macro, macro_length)] =
definition ? std::string(definition, definition_length) : "";
}
void Compiler::SetTargetEnv(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version) {
target_env_ = env;
target_env_version_ = version;
}
void Compiler::SetTargetSpirv(Compiler::SpirvVersion version) {
target_spirv_version_ = version;
target_spirv_version_is_forced_ = true;
}
void Compiler::SetSourceLanguage(Compiler::SourceLanguage lang) {
source_language_ = lang;
}
void Compiler::SetForcedVersionProfile(int version, EProfile profile) {
default_version_ = version;
default_profile_ = profile;
force_version_profile_ = true;
}
void Compiler::SetWarningsAsErrors() { warnings_as_errors_ = true; }
void Compiler::SetGenerateDebugInfo() {
generate_debug_info_ = true;
for (size_t i = 0; i < enabled_opt_passes_.size(); ++i) {
if (enabled_opt_passes_[i] == PassId::kStripDebugInfo) {
enabled_opt_passes_[i] = PassId::kNullPass;
}
}
}
void Compiler::SetEmitNonSemanticDebugInfo() {
emit_non_semantic_debug_info_ = true;
}
void Compiler::SetOptimizationLevel(Compiler::OptimizationLevel level) {
// Clear previous settings first.
enabled_opt_passes_.clear();
switch (level) {
case OptimizationLevel::Size:
if (!generate_debug_info_) {
enabled_opt_passes_.push_back(PassId::kStripDebugInfo);
}
enabled_opt_passes_.push_back(PassId::kSizePasses);
break;
case OptimizationLevel::Performance:
if (!generate_debug_info_) {
enabled_opt_passes_.push_back(PassId::kStripDebugInfo);
}
enabled_opt_passes_.push_back(PassId::kPerformancePasses);
break;
default:
break;
}
}
void Compiler::EnableHlslLegalization(bool hlsl_legalization_enabled) {
hlsl_legalization_enabled_ = hlsl_legalization_enabled;
}
void Compiler::EnableHlslFunctionality1(bool enable) {
hlsl_functionality1_enabled_ = enable;
}
void Compiler::SetVulkanRulesRelaxed(bool enable) {
vulkan_rules_relaxed_ = enable;
}
void Compiler::EnableHlsl16BitTypes(bool enable) {
hlsl_16bit_types_enabled_ = enable;
}
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(
const std::string& error_tag, const string_piece& shader_source,
const string_piece& shader_preamble, CountingIncluder& includer) const {
// The stage does not matter for preprocessing.
glslang::TShader shader(EShLangVertex);
const char* shader_strings = shader_source.data();
const int shader_lengths = static_cast<int>(shader_source.size());
const char* string_names = error_tag.c_str();
shader.setStringsWithLengthsAndNames(&shader_strings, &shader_lengths,
&string_names, 1);
shader.setPreamble(shader_preamble.data());
auto target_client_info = GetGlslangClientInfo(
error_tag, target_env_, target_env_version_,
target_spirv_version_, target_spirv_version_is_forced_);
if (!target_client_info.error.empty()) {
return std::make_tuple(false, "", target_client_info.error);
}
shader.setEnvClient(target_client_info.client,
target_client_info.client_version);
if (hlsl_functionality1_enabled_) {
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
// flag.
const auto rules = static_cast<EShMessages>(
EShMsgOnlyPreprocessor |
GetMessageRules(target_env_, source_language_, hlsl_offsets_,
hlsl_16bit_types_enabled_, false));
std::string preprocessed_shader;
const bool success = shader.preprocess(
&limits_, default_version_, default_profile_, force_version_profile_,
kNotForwardCompatible, rules, &preprocessed_shader, includer);
if (success) {
return std::make_tuple(true, preprocessed_shader, shader.getInfoLog());
}
return std::make_tuple(false, "", shader.getInfoLog());
}
std::string Compiler::CleanupPreamble(const string_piece& preprocessed_shader,
const string_piece& error_tag,
const string_piece& pound_extension,
int num_include_directives,
bool is_for_next_line) const {
// Those #define directives in preamble will become empty lines after
// preprocessing. We also injected an #extension directive to turn on #include
// directive support. In the original preprocessing output from glslang, it
// appears before the user source string. We need to do proper adjustment:
// * Remove empty lines generated from #define directives in preamble.
// * If there is no #include directive in the source code, we do not need to
// output the injected #extension directive. Otherwise,
// * If there exists a #version directive in the source code, it should be
// placed at the first line. Its original line will be filled with an empty
// line as placeholder to maintain the code structure.
const std::vector<string_piece> lines =
preprocessed_shader.get_fields('\n', /* keep_delimiter = */ true);
std::ostringstream output_stream;
size_t pound_extension_index = lines.size();
size_t pound_version_index = lines.size();
for (size_t i = 0; i < lines.size(); ++i) {
if (lines[i] == pound_extension) {
pound_extension_index = std::min(i, pound_extension_index);
} else if (lines[i].starts_with("#version")) {
// In a preprocessed shader, directives are in a canonical format, so we
// can confidently compare to '#version' verbatim, without worrying about
// whitespace.
pound_version_index = i;
if (num_include_directives > 0) output_stream << lines[i];
break;
}
}
// We know that #extension directive exists and appears before #version
// directive (if any).
assert(pound_extension_index < lines.size());
for (size_t i = 0; i < pound_extension_index; ++i) {
// All empty lines before the #line directive we injected are generated by
// preprocessing preamble. Do not output them.
if (lines[i].strip_whitespace().empty()) continue;
output_stream << lines[i];
}
if (num_include_directives > 0) {
output_stream << pound_extension;
// Also output a #line directive for the main file.
output_stream << GetLineDirective(is_for_next_line, error_tag);
}
for (size_t i = pound_extension_index + 1; i < lines.size(); ++i) {
if (i == pound_version_index) {
if (num_include_directives > 0) {
output_stream << "\n";
} else {
output_stream << lines[i];
}
} else {
output_stream << lines[i];
}
}
return output_stream.str();
}
std::pair<EShLanguage, std::string> Compiler::GetShaderStageFromSourceCode(
string_piece filename, const std::string& preprocessed_shader) const {
const string_piece kPragmaShaderStageDirective = "#pragma shader_stage";
const string_piece kLineDirective = "#line";
int version;
EProfile profile;
std::tie(version, profile) = DeduceVersionProfile(preprocessed_shader);
const bool is_for_next_line = LineDirectiveIsForNextLine(version, profile);
std::vector<string_piece> lines =
string_piece(preprocessed_shader).get_fields('\n');
// The filename, logical line number (which starts from 1 and is sensitive to
// #line directives), and stage value for #pragma shader_stage() directives.
std::vector<std::tuple<string_piece, size_t, string_piece>> stages;
// The physical line numbers of the first #pragma shader_stage() line and
// first non-preprocessing line in the preprocessed shader text.
size_t first_pragma_physical_line = lines.size() + 1;
size_t first_non_pp_line = lines.size() + 1;
for (size_t i = 0, logical_line_no = 1; i < lines.size(); ++i) {
const string_piece current_line = lines[i].strip_whitespace();
if (current_line.starts_with(kPragmaShaderStageDirective)) {
const string_piece stage_value =
current_line.substr(kPragmaShaderStageDirective.size()).strip("()");
stages.emplace_back(filename, logical_line_no, stage_value);
first_pragma_physical_line = std::min(first_pragma_physical_line, i + 1);
} else if (!current_line.empty() && !current_line.starts_with("#")) {
first_non_pp_line = std::min(first_non_pp_line, i + 1);
}
// Update logical line number for the next line.
if (current_line.starts_with(kLineDirective)) {
string_piece name;
std::tie(logical_line_no, name) = DecodeLineDirective(current_line);
if (!name.empty()) filename = name;
// Note that for core profile, the meaning of #line changed since version
// 330. The line number given by #line used to mean the logical line
// number of the #line line. Now it means the logical line number of the
// next line after the #line line.
if (!is_for_next_line) ++logical_line_no;
} else {
++logical_line_no;
}
}
if (stages.empty()) return std::make_pair(EShLangCount, "");
std::string error_message;
const string_piece& first_pragma_filename = std::get<0>(stages[0]);
const std::string first_pragma_line = std::to_string(std::get<1>(stages[0]));
const string_piece& first_pragma_stage = std::get<2>(stages[0]);
if (first_pragma_physical_line > first_non_pp_line) {
error_message += first_pragma_filename.str() + ":" + first_pragma_line +
": error: '#pragma': the first 'shader_stage' #pragma "
"must appear before any non-preprocessing code\n";
}
EShLanguage stage = MapStageNameToLanguage(first_pragma_stage);
if (stage == EShLangCount) {
error_message +=
first_pragma_filename.str() + ":" + first_pragma_line +
": error: '#pragma': invalid stage for 'shader_stage' #pragma: '" +
first_pragma_stage.str() + "'\n";
}
for (size_t i = 1; i < stages.size(); ++i) {
const string_piece& current_stage = std::get<2>(stages[i]);
if (current_stage != first_pragma_stage) {
const string_piece& current_filename = std::get<0>(stages[i]);
const std::string current_line = std::to_string(std::get<1>(stages[i]));
error_message += current_filename.str() + ":" + current_line +
": error: '#pragma': conflicting stages for "
"'shader_stage' #pragma: '" +
current_stage.str() + "' (was '" +
first_pragma_stage.str() + "' at " +
first_pragma_filename.str() + ":" + first_pragma_line +
")\n";
}
}
return std::make_pair(error_message.empty() ? stage : EShLangCount,
error_message);
}
std::pair<int, EProfile> Compiler::DeduceVersionProfile(
const std::string& preprocessed_shader) const {
int version = default_version_;
EProfile profile = default_profile_;
if (!force_version_profile_) {
std::tie(version, profile) =
GetVersionProfileFromSourceCode(preprocessed_shader);
if (version == 0 && profile == ENoProfile) {
version = default_version_;
profile = default_profile_;
}
}
return std::make_pair(version, profile);
}
std::pair<int, EProfile> Compiler::GetVersionProfileFromSourceCode(
const std::string& preprocessed_shader) const {
string_piece pound_version = preprocessed_shader;
const size_t pound_version_loc = pound_version.find("#version");
if (pound_version_loc == string_piece::npos) {
return std::make_pair(0, ENoProfile);
}
pound_version =
pound_version.substr(pound_version_loc + std::strlen("#version"));
pound_version = pound_version.substr(0, pound_version.find_first_of("\n"));
std::string version_profile;
for (const auto character : pound_version) {
if (character != ' ') version_profile += character;
}
int version;
EProfile profile;
if (!ParseVersionProfile(version_profile, &version, &profile)) {
return std::make_pair(0, ENoProfile);
}
return std::make_pair(version, profile);
}
// Converts a string to a vector of uint32_t by copying the content of a given
// string to a vector<uint32_t> and returns it. Appends '\0' at the end if extra
// bytes are required to complete the last element.
std::vector<uint32_t> ConvertStringToVector(const std::string& str) {
size_t num_bytes_str = str.size() + 1u;
size_t vector_length =
(num_bytes_str + sizeof(uint32_t) - 1) / sizeof(uint32_t);
std::vector<uint32_t> result_vec(vector_length, 0);
std::strncpy(reinterpret_cast<char*>(result_vec.data()), str.c_str(),
str.size());
return result_vec;
}
GlslangClientInfo GetGlslangClientInfo(
const std::string& error_tag, shaderc_util::Compiler::TargetEnv env,
shaderc_util::Compiler::TargetEnvVersion env_version,
shaderc_util::Compiler::SpirvVersion spv_version,
bool spv_version_is_forced) {
GlslangClientInfo result;
std::ostringstream errs;
using shaderc_util::Compiler;
switch (env) {
case Compiler::TargetEnv::Vulkan:
result.client = glslang::EShClientVulkan;
if (env_version == Compiler::TargetEnvVersion::Default ||
env_version == Compiler::TargetEnvVersion::Vulkan_1_0) {
result.client_version = glslang::EShTargetVulkan_1_0;
} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_1) {
result.client_version = glslang::EShTargetVulkan_1_1;
result.target_language_version = glslang::EShTargetSpv_1_3;
} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_2) {
result.client_version = glslang::EShTargetVulkan_1_2;
result.target_language_version = glslang::EShTargetSpv_1_5;
} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_3) {
result.client_version = glslang::EShTargetVulkan_1_3;
result.target_language_version = glslang::EShTargetSpv_1_6;
} else {
errs << "error:" << error_tag << ": Invalid target client version "
<< static_cast<uint32_t>(env_version) << " for Vulkan environment "
<< int(env);
}
break;
case Compiler::TargetEnv::OpenGLCompat:
errs << "error: OpenGL compatibility profile is not supported";
break;
case Compiler::TargetEnv::OpenGL:
result.client = glslang::EShClientOpenGL;
if (env_version == Compiler::TargetEnvVersion::Default ||
env_version == Compiler::TargetEnvVersion::OpenGL_4_5) {
result.client_version = glslang::EShTargetOpenGL_450;
} else {
errs << "error:" << error_tag << ": Invalid target client version "
<< static_cast<uint32_t>(env_version) << " for OpenGL environment "
<< int(env);
}
break;
default:
errs << "error:" << error_tag << ": Invalid target client environment "
<< int(env);
break;
}
if (spv_version_is_forced && errs.str().empty()) {
switch (spv_version) {
case Compiler::SpirvVersion::v1_0:
result.target_language_version = glslang::EShTargetSpv_1_0;
break;
case Compiler::SpirvVersion::v1_1:
result.target_language_version = glslang::EShTargetSpv_1_1;
break;
case Compiler::SpirvVersion::v1_2:
result.target_language_version = glslang::EShTargetSpv_1_2;
break;
case Compiler::SpirvVersion::v1_3:
result.target_language_version = glslang::EShTargetSpv_1_3;
break;
case Compiler::SpirvVersion::v1_4:
result.target_language_version = glslang::EShTargetSpv_1_4;
break;
case Compiler::SpirvVersion::v1_5:
result.target_language_version = glslang::EShTargetSpv_1_5;
break;
case Compiler::SpirvVersion::v1_6:
result.target_language_version = glslang::EShTargetSpv_1_6;
break;
default:
errs << "error:" << error_tag << ": Unknown SPIR-V version " << std::hex
<< uint32_t(spv_version);
break;
}
}
result.error = errs.str();
return result;
}
} // namespace shaderc_util

View File

@@ -0,0 +1,974 @@
// 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 fragment shader with the location defined for its non-opaque uniform
// variable.
const char kGlslFragShaderOpaqueUniforms[] =
R"(#version 320 es
precision lowp float;
layout(location = 0) out vec4 oColor;
layout(location = 0) uniform float a;
void main(void) {
oColor = vec4(1.0, 0.0, 0.0, a);
})";
// 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_3,
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, SpirvTargetVersion1_6Succeeds) {
compiler_.SetTargetSpirv(Compiler::SpirvVersion::v1_6);
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, RelaxedVulkanRulesEnabled) {
compiler_.SetSourceLanguage(Compiler::SourceLanguage::GLSL);
compiler_.SetAutoBindUniforms(true); // Uniform variable needs a binding
compiler_.SetVulkanRulesRelaxed(true);
const auto words =
SimpleCompilationBinary(kGlslFragShaderOpaqueUniforms, EShLangFragment);
const auto disassembly = Disassemble(words);
EXPECT_THAT(disassembly,
HasSubstr("OpMemberName %gl_DefaultUniformBlock 0 \"a\""))
<< 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

View File

@@ -0,0 +1,93 @@
// 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/counting_includer.h"
#include <thread>
#include <vector>
#include <gmock/gmock.h>
namespace {
// A trivial implementation of CountingIncluder's virtual methods, so tests can
// instantiate.
class ConcreteCountingIncluder : public shaderc_util::CountingIncluder {
public:
using IncludeResult = glslang::TShader::Includer::IncludeResult;
~ConcreteCountingIncluder() {
// Avoid leaks.
for (auto result : results_) {
release_delegate(result);
}
}
virtual IncludeResult* include_delegate(
const char* requested, const char* requestor, IncludeType,
size_t) override {
const char kError[] = "Unexpected #include";
results_.push_back(new IncludeResult{"", kError, strlen(kError), nullptr});
return results_.back();
}
virtual void release_delegate(IncludeResult* include_result) override {
delete include_result;
}
private:
// All the results we've returned so far.
std::vector<IncludeResult*> results_;
};
TEST(CountingIncluderTest, InitialCount) {
EXPECT_EQ(0, ConcreteCountingIncluder().num_include_directives());
}
TEST(CountingIncluderTest, OneIncludeLocal) {
ConcreteCountingIncluder includer;
includer.includeLocal("random file name", "from me", 0);
EXPECT_EQ(1, includer.num_include_directives());
}
TEST(CountingIncluderTest, TwoIncludesAnyIncludeType) {
ConcreteCountingIncluder includer;
includer.includeSystem("name1", "from me", 0);
includer.includeLocal("name2", "me", 0);
EXPECT_EQ(2, includer.num_include_directives());
}
TEST(CountingIncluderTest, ManyIncludes) {
ConcreteCountingIncluder includer;
for (int i = 0; i < 100; ++i) {
includer.includeLocal("filename", "from me", i);
includer.includeSystem("filename", "from me", i);
}
EXPECT_EQ(200, includer.num_include_directives());
}
#ifndef SHADERC_DISABLE_THREADED_TESTS
TEST(CountingIncluderTest, ThreadedIncludes) {
ConcreteCountingIncluder includer;
std::thread t1(
[&includer]() { includer.includeLocal("name1", "me", 0); });
std::thread t2(
[&includer]() { includer.includeSystem("name2", "me", 1); });
std::thread t3(
[&includer]() { includer.includeLocal("name3", "me", 2); });
t1.join();
t2.join();
t3.join();
EXPECT_EQ(3, includer.num_include_directives());
}
#endif // SHADERC_DISABLE_THREADED_TESTS
} // anonymous namespace

View File

@@ -0,0 +1,25 @@
// 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.
#ifndef LIBSHADERC_UTIL_SRC_DEATH_TEST_H
#define LIBSHADERC_UTIL_SRC_DEATH_TEST_H
#ifdef NDEBUG
#define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regexp)
#else
#define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regexp) \
EXPECT_DEATH_IF_SUPPORTED(statement, regexp)
#endif
#endif // LIBSHADERC_UTIL_SRC_DEATH_TEST_H

View File

@@ -0,0 +1,71 @@
// 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/file_finder.h"
#include "libshaderc_util/string_piece.h"
#include <cassert>
#include <fstream>
#include <ios>
namespace {
// Returns "" if path is empty or ends in '/'. Otherwise, returns "/".
std::string MaybeSlash(const shaderc_util::string_piece& path) {
return (path.empty() || path.back() == '/') ? "" : "/";
}
} // anonymous namespace
namespace shaderc_util {
std::string FileFinder::FindReadableFilepath(
const std::string& filename) const {
assert(!filename.empty());
static const auto for_reading = std::ios_base::in;
std::filebuf opener;
for (const auto& prefix : search_path_) {
const std::string prefixed_filename =
prefix + MaybeSlash(prefix) + filename;
if (opener.open(prefixed_filename, for_reading)) return prefixed_filename;
}
return "";
}
std::string FileFinder::FindRelativeReadableFilepath(
const std::string& requesting_file, const std::string& filename) const {
assert(!filename.empty());
string_piece dir_name(requesting_file);
size_t last_slash = requesting_file.find_last_of("/\\");
if (last_slash != std::string::npos) {
dir_name = string_piece(requesting_file.c_str(),
requesting_file.c_str() + last_slash);
}
if (dir_name.size() == requesting_file.size()) {
dir_name.clear();
}
static const auto for_reading = std::ios_base::in;
std::filebuf opener;
const std::string relative_filename =
dir_name.str() + MaybeSlash(dir_name) + filename;
if (opener.open(relative_filename, for_reading)) return relative_filename;
return FindReadableFilepath(filename);
}
} // namespace shaderc_util

View File

@@ -0,0 +1,146 @@
// 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/file_finder.h"
#include <gtest/gtest.h>
// We need getcwd
#if WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "death_test.h"
namespace {
using shaderc_util::FileFinder;
// Returns the absolute path of the current working directory.
std::string GetCurrentDir() {
// Provide generous space to write the path.
char buf[1000];
#if WIN32
return _getcwd(buf, sizeof(buf));
#else
return getcwd(buf, sizeof(buf));
#endif
}
class FileFinderTest : public testing::Test {
protected:
FileFinder finder;
// Absolute path of the current working directory.
const std::string current_dir = GetCurrentDir();
};
TEST_F(FileFinderTest, PathStartsEmpty) {
EXPECT_TRUE(FileFinder().search_path().empty());
}
TEST_F(FileFinderTest, EmptyPath) {
finder.search_path().clear();
EXPECT_EQ("", finder.FindReadableFilepath("include_file.1"));
}
TEST_F(FileFinderTest, EmptyStringInPath) {
finder.search_path() = {""};
EXPECT_EQ("include_file.1", finder.FindReadableFilepath("include_file.1"));
EXPECT_EQ("dir/subdir/include_file.2",
finder.FindReadableFilepath("dir/subdir/include_file.2"));
}
TEST_F(FileFinderTest, SimplePath) {
finder.search_path() = {"dir"};
EXPECT_EQ("dir/subdir/include_file.2",
finder.FindReadableFilepath("subdir/include_file.2"));
}
TEST_F(FileFinderTest, PathEndsInSlash) {
finder.search_path() = {"dir/"};
EXPECT_EQ("dir/subdir/include_file.2",
finder.FindReadableFilepath("subdir/include_file.2"));
}
TEST_F(FileFinderTest, ParentDir) {
finder.search_path() = {"dir"};
EXPECT_EQ("dir/../include_file.1",
finder.FindReadableFilepath("../include_file.1"));
}
TEST_F(FileFinderTest, EntirePathIsActive) {
finder.search_path() = {"", "dir/subdir/"};
EXPECT_EQ("include_file.1", finder.FindReadableFilepath("include_file.1"));
EXPECT_EQ("dir/subdir/include_file.2",
finder.FindReadableFilepath("include_file.2"));
}
TEST_F(FileFinderTest, NonExistingFile) {
finder.search_path() = {"", "dir/subdir/"};
EXPECT_EQ("", finder.FindReadableFilepath("garbage.xyxyxyxyxyxz"));
}
TEST_F(FileFinderTest, FirstHitReturned) {
finder.search_path() = {".", "", "dir/../"};
EXPECT_EQ("./include_file.1", finder.FindReadableFilepath("include_file.1"));
}
TEST_F(FileFinderTest, IrrelevantPaths) {
finder.search_path() = {".", "garbage.xyxyxyxyxyz", "dir/../"};
EXPECT_EQ("", finder.FindReadableFilepath("include_file.2"));
finder.search_path().push_back("dir/subdir");
EXPECT_EQ("dir/subdir/include_file.2",
finder.FindReadableFilepath("include_file.2"));
}
TEST_F(FileFinderTest, CurrentDirectory) {
ASSERT_GE(current_dir.size(), 0u);
// Either the directory should start with / (if we are on Linux),
// Or it should beither X:/ or X:\ or // (if we are on Windows).
ASSERT_TRUE(current_dir.front() == '\\' || current_dir.front() == '/' ||
(current_dir.size() >= 3u && current_dir[1] == ':' &&
(current_dir[2] == '\\' || current_dir[2] == '/')));
}
TEST_F(FileFinderTest, AbsolutePath) {
ASSERT_NE('/', current_dir.back());
finder.search_path() = {current_dir};
EXPECT_EQ(current_dir + "/include_file.1",
finder.FindReadableFilepath("include_file.1"));
EXPECT_EQ(current_dir + "/dir/subdir/include_file.2",
finder.FindReadableFilepath("dir/subdir/include_file.2"));
}
TEST_F(FileFinderTest, AbsoluteFilename) {
ASSERT_NE('/', current_dir.back());
finder.search_path() = {""};
const std::string absolute_file1 = current_dir + "/include_file.1";
EXPECT_EQ(absolute_file1, finder.FindReadableFilepath(absolute_file1));
EXPECT_EQ("", finder.FindReadableFilepath("/dir/subdir/include_file.2"));
finder.search_path().push_back(".");
EXPECT_EQ(".//dir/subdir/include_file.2",
finder.FindReadableFilepath("/dir/subdir/include_file.2"));
}
TEST(FileFinderDeathTest, EmptyFilename) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FileFinder().FindReadableFilepath(""),
"Assertion");
}
} // anonymous namespace

View File

@@ -0,0 +1,105 @@
// 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/format.h"
#include <gmock/gmock.h>
#include <map>
#include <string>
#include <unordered_map>
namespace {
using testing::AllOf;
using testing::HasSubstr;
using testing::IsEmpty;
class FormatMap : public testing::Test {
public:
FormatMap()
: map1({{"one", 1}}),
umap1({map1.begin(), map1.end()}),
map8({{1, "one"},
{2, "two"},
{3, "three"},
{4, "four"},
{5, "five"},
{6, "six"},
{7, "seven"},
{8, "eight"}}),
umap8({map8.begin(), map8.end()}),
mmap({{1, 100}, {1, 200}, {2, 100}, {2, 200}}),
ummap({mmap.begin(), mmap.end()}) {}
protected:
std::map<int, int> empty_map;
std::unordered_map<int, int> empty_umap;
std::map<std::string, int> map1;
std::unordered_map<std::string, int> umap1;
std::map<int, std::string> map8;
std::unordered_map<int, std::string> umap8;
std::multimap<int, int> mmap;
std::unordered_multimap<int, int> ummap;
};
TEST_F(FormatMap, EmptyMap) {
EXPECT_THAT(shaderc_util::format(empty_map, "pre", "in", "post"), IsEmpty());
EXPECT_THAT(shaderc_util::format(empty_umap, "pre", "in", "post"), IsEmpty());
}
TEST_F(FormatMap, SingleEntry) {
EXPECT_EQ("PREoneIN1POST", shaderc_util::format(map1, "PRE", "IN", "POST"));
EXPECT_EQ("PREoneIN1POST", shaderc_util::format(umap1, "PRE", "IN", "POST"));
}
TEST_F(FormatMap, EmptyPrefix) {
EXPECT_EQ("oneIN1POST", shaderc_util::format(map1, "", "IN", "POST"));
EXPECT_EQ("oneIN1POST", shaderc_util::format(umap1, "", "IN", "POST"));
}
TEST_F(FormatMap, EmptyInfix) {
EXPECT_EQ("PREone1POST", shaderc_util::format(map1, "PRE", "", "POST"));
EXPECT_EQ("PREone1POST", shaderc_util::format(umap1, "PRE", "", "POST"));
}
TEST_F(FormatMap, EmptyPostfix) {
EXPECT_EQ("PREoneIN1", shaderc_util::format(map1, "PRE", "IN", ""));
EXPECT_EQ("PREoneIN1", shaderc_util::format(umap1, "PRE", "IN", ""));
}
TEST_F(FormatMap, LargerMap) {
const std::string result = shaderc_util::format(map8, "", "", "\n"),
uresult = shaderc_util::format(umap8, "", "", "\n");
auto has_all =
AllOf(HasSubstr("1one\n"), HasSubstr("2two\n"), HasSubstr("3three\n"),
HasSubstr("4four\n"), HasSubstr("5five\n"), HasSubstr("6six\n"),
HasSubstr("7seven\n"), HasSubstr("8eight\n"));
EXPECT_THAT(result, has_all);
EXPECT_EQ(48u, result.size());
EXPECT_THAT(uresult, has_all);
EXPECT_EQ(48u, uresult.size());
}
TEST_F(FormatMap, Multimap) {
const std::string result = shaderc_util::format(mmap, " ", "&", ""),
uresult = shaderc_util::format(ummap, " ", "&", "");
auto has_all = AllOf(HasSubstr(" 1&100"), HasSubstr(" 1&200"),
HasSubstr(" 2&100"), HasSubstr(" 2&200"));
EXPECT_THAT(result, has_all);
EXPECT_EQ(4 * 6u, result.size());
EXPECT_THAT(uresult, has_all);
EXPECT_EQ(4 * 6u, uresult.size());
}
} // anonymous namespace

View File

@@ -0,0 +1,146 @@
// 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/io_shaderc.h"
#include "libshaderc_util/universal_unistd.h"
#if _WIN32
// Need _fileno from stdio.h
// Need _O_BINARY and _O_TEXT from fcntl.h
#include <fcntl.h>
#include <stdio.h>
#endif
#include <errno.h>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
namespace {
// Outputs a descriptive message for errno_value to cerr.
// This may be truncated to 1023 bytes on certain platforms.
void OutputFileErrorMessage(int errno_value) {
#ifdef _MSC_VER
// If the error message is more than 1023 bytes it will be truncated.
char buffer[1024];
strerror_s(buffer, errno_value);
std::cerr << ": " << buffer << std::endl;
#else
std::cerr << ": " << strerror(errno_value) << std::endl;
#endif
}
} // anonymous namespace
namespace shaderc_util {
bool IsAbsolutePath(const std::string& path) {
if (path.empty()) return false;
// Unix-like OS: /path/to/file
if (path.front() == '/') return true;
// Windows: \\server\user\file
if (path.size() > 1 && path[0] == '\\' && path[1] == '\\') {
return true;
}
// Windows: X:\path\to\file
if (path.size() > 2 && ::isalpha(path[0]) && path[1] == ':' &&
path[2] == '\\') {
return true;
}
return false;
}
std::string GetBaseFileName(const std::string& file_path) {
size_t loc_slash = file_path.find_last_of("/\\");
std::string base_name =
file_path.substr((loc_slash == std::string::npos ? -1 : loc_slash) + 1);
if (base_name == ".." || base_name == ".") {
base_name = "";
}
return base_name;
}
bool ReadFile(const std::string& input_file_name,
std::vector<char>* input_data) {
std::istream* stream = &std::cin;
std::ifstream input_file;
if (input_file_name != "-") {
input_file.open(input_file_name, std::ios_base::binary);
stream = &input_file;
if (input_file.fail()) {
std::cerr << "glslc: error: cannot open input file: '" << input_file_name
<< "'";
if (access(input_file_name.c_str(), R_OK) != 0) {
OutputFileErrorMessage(errno);
return false;
}
std::cerr << std::endl;
return false;
}
}
*input_data = std::vector<char>((std::istreambuf_iterator<char>(*stream)),
std::istreambuf_iterator<char>());
return true;
}
std::ostream* GetOutputStream(const string_piece& output_filename,
std::ofstream* file_stream, std::ostream* err) {
std::ostream* stream = &std::cout;
if (output_filename != "-") {
file_stream->open(output_filename.str(), std::ios_base::binary);
stream = file_stream;
if (file_stream->fail()) {
*err << "glslc: error: cannot open output file: '" << output_filename
<< "'";
if (access(output_filename.str().c_str(), W_OK) != 0) {
OutputFileErrorMessage(errno);
return nullptr;
}
std::cerr << std::endl;
return nullptr;
}
}
return stream;
}
bool WriteFile(std::ostream* stream, const string_piece& output_data) {
if (output_data.size() > 0) {
stream->write(output_data.data(), output_data.size());
if (!stream->good()) {
return false;
}
}
stream->flush();
return true;
}
void FlushAndSetBinaryModeOnStdout() {
std::fflush(stdout);
#if _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
}
void FlushAndSetTextModeOnStdout() {
std::fflush(stdout);
#if _WIN32
_setmode(_fileno(stdout), _O_TEXT);
#endif
}
} // namespace shaderc_util

View File

@@ -0,0 +1,139 @@
// 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/io_shaderc.h"
#include <gmock/gmock.h>
#include <fstream>
namespace {
using shaderc_util::GetBaseFileName;
using shaderc_util::GetOutputStream;
using shaderc_util::IsAbsolutePath;
using shaderc_util::ReadFile;
using shaderc_util::WriteFile;
using testing::Eq;
using testing::HasSubstr;
std::string ToString(const std::vector<char>& v) {
return std::string(v.data(), v.size());
}
class ReadFileTest : public testing::Test {
protected:
// A vector to pass to ReadFile.
std::vector<char> read_data;
};
TEST(IsAbsolutePathTest, Linux) {
EXPECT_FALSE(IsAbsolutePath(""));
EXPECT_TRUE(IsAbsolutePath("/"));
EXPECT_FALSE(IsAbsolutePath("."));
EXPECT_FALSE(IsAbsolutePath(".."));
EXPECT_TRUE(IsAbsolutePath("/bin/echo"));
EXPECT_TRUE(IsAbsolutePath("//etc/shadow"));
EXPECT_TRUE(IsAbsolutePath("/../../../lib"));
EXPECT_FALSE(IsAbsolutePath("./something"));
EXPECT_FALSE(IsAbsolutePath("input"));
EXPECT_FALSE(IsAbsolutePath("../test"));
EXPECT_FALSE(IsAbsolutePath(" /abc"));
EXPECT_TRUE(IsAbsolutePath("/abc def/ttt"));
}
TEST(IsAbsolutePathTest, Windows) {
EXPECT_TRUE(IsAbsolutePath(R"(\\Server1000\superuser\file)"));
EXPECT_TRUE(IsAbsolutePath(R"(\\zzzz 1000\user with space\file with space)"));
EXPECT_TRUE(
IsAbsolutePath(R"(C:\Program Files (x86)\Windows Folder\shader.glsl)"));
EXPECT_FALSE(IsAbsolutePath(R"(third_party\gmock)"));
EXPECT_FALSE(IsAbsolutePath(R"(C:..\File.txt)"));
}
TEST(GetBaseFileName, Linux) {
EXPECT_EQ("", GetBaseFileName(""));
EXPECT_EQ("", GetBaseFileName("/"));
EXPECT_EQ("", GetBaseFileName("."));
EXPECT_EQ("", GetBaseFileName(".."));
EXPECT_EQ("echo", GetBaseFileName("/bin/echo"));
EXPECT_EQ("shadow", GetBaseFileName("//etc/shadow"));
EXPECT_EQ("lib", GetBaseFileName("/../../../lib"));
EXPECT_EQ("something", GetBaseFileName("./something"));
EXPECT_EQ("input", GetBaseFileName("input"));
EXPECT_EQ("test", GetBaseFileName("../test"));
EXPECT_EQ("abc", GetBaseFileName(" /abc"));
EXPECT_EQ("ttt", GetBaseFileName("/abc def/ttt"));
}
TEST(GetBaseFileName, Windows) {
EXPECT_EQ("file", GetBaseFileName(R"(\\Server1000\superuser\file)"));
EXPECT_EQ("file with space",
GetBaseFileName(R"(\\zzzz 1000\user with space\file with space)"));
EXPECT_EQ(
"shader.glsl",
GetBaseFileName(R"(C:\Program Files (x86)\Windows Folder\shader.glsl)"));
EXPECT_EQ("gmock", GetBaseFileName(R"(third_party\gmock)"));
EXPECT_EQ("File.txt", GetBaseFileName(R"(C:..\File.txt)"));
}
TEST_F(ReadFileTest, CorrectContent) {
ASSERT_TRUE(ReadFile("include_file.1", &read_data));
EXPECT_EQ("The quick brown fox jumps over a lazy dog.", ToString(read_data));
}
TEST_F(ReadFileTest, EmptyContent) {
ASSERT_TRUE(ReadFile("dir/subdir/include_file.2", &read_data));
EXPECT_TRUE(read_data.empty());
}
TEST_F(ReadFileTest, FileNotFound) {
EXPECT_FALSE(ReadFile("garbage garbage vjoiarhiupo hrfewi", &read_data));
}
TEST_F(ReadFileTest, EmptyFilename) { EXPECT_FALSE(ReadFile("", &read_data)); }
TEST(WriteFiletest, BadStream) {
std::ofstream fstream;
std::ostringstream err;
std::ostream* output_stream = GetOutputStream(
"/this/should/not/be/writable/asdfasdfasdfasdf", &fstream, &err);
EXPECT_EQ(nullptr, output_stream);
EXPECT_TRUE(fstream.fail());
EXPECT_EQ(nullptr, output_stream);
EXPECT_THAT(err.str(), HasSubstr("cannot open output file"));
}
TEST(WriteFileTest, Roundtrip) {
const std::string content = "random content 12345";
const std::string filename = "WriteFileTestOutput.tmp";
std::ofstream fstream;
std::ostringstream err;
std::ostream* output_stream = GetOutputStream(filename, &fstream, &err);
ASSERT_EQ(output_stream, &fstream);
EXPECT_THAT(err.str(), Eq(""));
ASSERT_TRUE(WriteFile(output_stream, content));
std::vector<char> read_data;
ASSERT_TRUE(ReadFile(filename, &read_data));
EXPECT_EQ(content, ToString(read_data));
}
TEST(OutputStreamTest, Stdout) {
std::ofstream fstream;
std::ostringstream err;
std::ostream* output_stream = GetOutputStream("-", &fstream, &err);
EXPECT_EQ(&std::cout, output_stream);
EXPECT_THAT(err.str(), Eq(""));
}
} // anonymous namespace

View File

@@ -0,0 +1,293 @@
// 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/message.h"
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
namespace shaderc_util {
namespace {
// Given a message, deduces and returns its type. If the message type is
// recognized, advances *message past the prefix indicating the type. Otherwise,
// leaves *message unchanged and returns MessageType::Unknown.
MessageType DeduceMessageType(string_piece* message) {
static const char kErrorMessage[] = "ERROR: ";
static const char kWarningMessage[] = "WARNING: ";
static const char kGlobalWarningMessage[] = "Warning, ";
if (message->starts_with(kErrorMessage)) {
*message = message->substr(::strlen(kErrorMessage));
return MessageType::Error;
} else if (message->starts_with(kWarningMessage)) {
*message = message->substr(::strlen(kWarningMessage));
return MessageType::Warning;
} else if (message->starts_with(kGlobalWarningMessage)) {
*message = message->substr(::strlen(kGlobalWarningMessage));
return MessageType::GlobalWarning;
}
return MessageType::Unknown;
}
// Deduces a location specification from the given message. A location
// specification is of the form "<source-name>:<line-number>:" and a trailing
// space. If the deduction is successful, returns true and updates source_name
// and line_number to the deduced source name and line numer respectively. The
// prefix standing for the location specification in message is skipped.
// Otherwise, returns false and keeps all parameters untouched.
bool DeduceLocationSpec(string_piece* message, string_piece* source_name,
string_piece* line_number) {
if (!message || message->empty()) {
return false;
}
// When we find a pattern like this:
// colon
// digits
// colon
// space
// Then deduce that the source_name is the text before the first colon,
// the line number is the digits, and the message is the text after the
// second colon.
const size_t size = message->size();
if (size <= 4) {
// A valid message must have a colon, a digit, a colon, and a space.
return false;
}
// The last possible position of the first colon.
const size_t first_colon_cutoff = size - 4;
// The last possible position of the second colon.
const size_t next_colon_cutoff = size - 2;
for (size_t first_colon_pos = message->find_first_of(':'), next_colon_pos = 0;
// There is a first colon, and it's not too close to the end
(first_colon_pos != string_piece::npos) &&
(first_colon_pos <= first_colon_cutoff);
// Try the next pair of colons.
first_colon_pos = next_colon_pos) {
// We're guaranteed to have at least 3 more characters.
// Guarantee progress toward the end of the string.
next_colon_pos = message->find_first_of(':', first_colon_pos + 1);
if ((next_colon_pos == string_piece::npos) ||
(next_colon_pos > next_colon_cutoff)) {
// No valid solution.
return false;
}
if (first_colon_pos + 1 == next_colon_pos) {
// There is no room for digits.
continue;
}
if ((message->data()[next_colon_pos + 1] != ' ')) {
// There is no space character after the second colon.
continue;
}
if (message->find_first_not_of("0123456789", first_colon_pos + 1) ==
next_colon_pos) {
// We found the first solution.
*source_name = message->substr(0, first_colon_pos);
*line_number = message->substr(first_colon_pos + 1,
next_colon_pos - 1 - first_colon_pos);
*message = message->substr(next_colon_pos + 2);
return true;
}
}
return false;
}
// Returns true if the given message is a summary message.
bool IsSummaryMessage(const string_piece& message) {
const size_t space_loc = message.find_first_of(' ');
if (space_loc == string_piece::npos) return false;
const string_piece number = message.substr(0, space_loc);
const string_piece rest = message.substr(space_loc + 1);
if (!std::all_of(number.begin(), number.end(), ::isdigit)) return false;
if (!rest.starts_with("compilation errors.")) return false;
return true;
}
} // anonymous namespace
MessageType ParseGlslangOutput(const string_piece& message,
bool warnings_as_errors, bool suppress_warnings,
string_piece* source_name,
string_piece* line_number, string_piece* rest) {
string_piece rest_of_message(message);
source_name->clear();
line_number->clear();
rest->clear();
// The glslang warning/error messages are typically of the following form:
// <message-type> <location-specification> <message-body>
//
// <message-type> can be "WARNING:", "ERROR:", or "Warning, ". "WARNING:"
// means a warning message for a certain line, while "Warning, " means a
// global one.
//
// <location-specification> is of the form:
// <filename-or-string-number>:<line-number>:
// It doesn't exist if the warning/error message is a global one.
//
// See Glslang's TInfoSink class implementation for details.
bool is_error = false;
// Handle <message-type>.
switch (DeduceMessageType(&rest_of_message)) {
case MessageType::Warning:
if (suppress_warnings) return MessageType::Ignored;
break;
case MessageType::Error:
is_error = true;
break;
case MessageType::GlobalWarning:
if (suppress_warnings) return MessageType::Ignored;
*rest = rest_of_message;
return warnings_as_errors ? MessageType::GlobalError
: MessageType::GlobalWarning;
case MessageType::Unknown:
*rest = rest_of_message;
return MessageType::Unknown;
default:
break;
}
rest_of_message = rest_of_message.strip_whitespace();
if (rest_of_message.empty()) return MessageType::Unknown;
// Now we have stripped the <message-type>. Try to see if we can find
// a <location-specification>.
if (DeduceLocationSpec(&rest_of_message, source_name, line_number)) {
*rest = rest_of_message;
return (is_error || warnings_as_errors) ? MessageType::Error
: MessageType::Warning;
} else {
// No <location-specification>. This is a global warning/error message.
// A special kind of global message is summary message, which should
// start with a number.
*rest = rest_of_message;
if (IsSummaryMessage(rest_of_message)) {
return (is_error || warnings_as_errors) ? MessageType::ErrorSummary
: MessageType::WarningSummary;
}
return (is_error || warnings_as_errors) ? MessageType::GlobalError
: MessageType::GlobalWarning;
}
return MessageType::Unknown;
}
bool PrintFilteredErrors(const string_piece& file_name,
std::ostream* error_stream, bool warnings_as_errors,
bool suppress_warnings, const char* error_list,
size_t* total_warnings, size_t* total_errors) {
const char* ignored_error_strings[] = {
"Warning, version 310 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 400 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 410 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 420 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 430 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 440 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Warning, version 450 is not yet complete; most version-specific "
"features are present, but some are missing.",
"Linked vertex stage:", "Linked fragment stage:",
"Linked tessellation control stage:",
"Linked tessellation evaluation stage:", "Linked geometry stage:",
"Linked compute stage:", ""};
size_t existing_total_errors = *total_errors;
string_piece error_messages(error_list);
for (const string_piece& message : error_messages.get_fields('\n')) {
if (std::find(std::begin(ignored_error_strings),
std::end(ignored_error_strings),
message) == std::end(ignored_error_strings)) {
string_piece source_name;
string_piece line_number;
string_piece rest;
const MessageType type =
ParseGlslangOutput(message, warnings_as_errors, suppress_warnings,
&source_name, &line_number, &rest);
string_piece name = file_name;
if (!source_name.empty()) {
// -1 is the string number for the preamble injected by us.
name = source_name == "-1" ? "<command line>" : source_name;
}
switch (type) {
case MessageType::Error:
case MessageType::Warning:
assert(!name.empty() && !line_number.empty() && !rest.empty());
*error_stream << name << ":" << line_number << ": "
<< (type == MessageType::Error ? "error: "
: "warning: ")
<< rest.strip_whitespace() << std::endl;
*total_errors += type == MessageType::Error;
*total_warnings += type == MessageType::Warning;
break;
case MessageType::ErrorSummary:
case MessageType::WarningSummary:
break;
case MessageType::GlobalError:
case MessageType::GlobalWarning:
assert(!rest.empty());
*total_errors += type == MessageType::GlobalError;
*total_warnings += type == MessageType::GlobalWarning;
*error_stream << name << ": "
<< (type == MessageType::GlobalError ? "error"
: "warning")
<< ": " << rest.strip_whitespace() << std::endl;
break;
case MessageType::Unknown:
*error_stream << name << ":";
*error_stream << " " << message << std::endl;
break;
case MessageType::Ignored:
break;
}
}
}
return (existing_total_errors == *total_errors);
}
// Outputs the number of warnings and errors if there are any.
void OutputMessages(std::ostream* error_stream, size_t total_warnings,
size_t total_errors) {
if (total_warnings > 0 || total_errors > 0) {
if (total_warnings > 0 && total_errors > 0) {
*error_stream << total_warnings << " warning"
<< (total_warnings > 1 ? "s" : "") << " and "
<< total_errors << " error" << (total_errors > 1 ? "s" : "")
<< " generated." << std::endl;
} else if (total_warnings > 0) {
*error_stream << total_warnings << " warning"
<< (total_warnings > 1 ? "s" : "") << " generated."
<< std::endl;
} else if (total_errors > 0) {
*error_stream << total_errors << " error" << (total_errors > 1 ? "s" : "")
<< " generated." << std::endl;
}
}
}
} // namespace glslc

View File

@@ -0,0 +1,281 @@
// 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.
// Some of the tests here check code paths that are not checked by
// integration tests.
// Generally, these would be conditions not generated by the Glslang
// compiler. It's easier to write these unit tests than to inject
// a dependency on a fake compiler.
#include "libshaderc_util/message.h"
#include <gtest/gtest.h>
using shaderc_util::MessageType;
using shaderc_util::ParseGlslangOutput;
using shaderc_util::string_piece;
namespace {
TEST(ParseGlslangOutputTest, EmptyMessageBody) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Unknown,
ParseGlslangOutput("WARNING: ", false, false, &segment_number,
&line_number, &rest));
EXPECT_EQ(MessageType::Unknown,
ParseGlslangOutput("ERROR: ", false, false, &segment_number,
&line_number, &rest));
}
TEST(ParseGlslangOutputTest, GlobalError) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(
MessageType::GlobalError,
ParseGlslangOutput("ERROR: too many functions: got 1666473 of them",
false, false, &segment_number, &line_number, &rest));
EXPECT_EQ("too many functions: got 1666473 of them", rest.str());
EXPECT_EQ(
MessageType::GlobalError,
ParseGlslangOutput(
"ERROR: #version: versions before 150 do not allow a profile token",
false, false, &segment_number, &line_number, &rest));
EXPECT_EQ("#version: versions before 150 do not allow a profile token",
rest.str());
}
TEST(ParseGlslangOutputTest, GlobalWarning) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("Warning, version 1000 is unknown.", false,
false, &segment_number, &line_number, &rest));
EXPECT_EQ("version 1000 is unknown.", rest.str());
}
TEST(ParseGlslangOutputTest, InvalidSuffixAfterSegmentNumber) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("WARNING: 12a", false, false, &segment_number,
&line_number, &rest));
EXPECT_EQ(MessageType::GlobalError,
ParseGlslangOutput("WARNING: 12a", true, false, &segment_number,
&line_number, &rest));
EXPECT_EQ(MessageType::GlobalError,
ParseGlslangOutput("ERROR: 42!", false, false, &segment_number,
&line_number, &rest));
}
TEST(ParseGlslangOutputTest, OnlyANumber) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("WARNING: 12", false, false, &source_name,
&line_number, &rest));
EXPECT_TRUE(source_name.empty());
EXPECT_TRUE(line_number.empty());
EXPECT_EQ("12", rest.str());
EXPECT_EQ(MessageType::GlobalError,
ParseGlslangOutput("WARNING: 12", true, false, &source_name,
&line_number, &rest));
EXPECT_TRUE(source_name.empty());
EXPECT_TRUE(line_number.empty());
EXPECT_EQ("12", rest.str());
EXPECT_EQ(MessageType::GlobalError,
ParseGlslangOutput("ERROR: 42", false, false, &source_name,
&line_number, &rest));
EXPECT_TRUE(source_name.empty());
EXPECT_TRUE(line_number.empty());
EXPECT_EQ("42", rest.str());
}
TEST(ParseGlslangOutputTest, InvalidSuffixAfterSegmentNumberColon) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("WARNING: 12:0", false, false, &segment_number,
&line_number, &rest));
EXPECT_EQ(MessageType::GlobalError,
ParseGlslangOutput("ERROR: 42:1234", false, false, &segment_number,
&line_number, &rest));
}
TEST(ParseGlslangOutputTest, CompletelyUnrecognized) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Unknown,
ParseGlslangOutput("hello world!", false, false, &segment_number,
&line_number, &rest));
}
TEST(ParseGlslangOutputTest, LocationSpecification) {
string_piece segment_number;
string_piece line_number;
string_piece rest;
// Glslang reading from strings can give string segment numbers as
// the filename part.
EXPECT_EQ(
MessageType::Error,
ParseGlslangOutput("ERROR: 0:2: '#' : invalid directive: foo", false,
false, &segment_number, &line_number, &rest));
EXPECT_EQ("0", segment_number.str());
EXPECT_EQ("2", line_number.str());
EXPECT_EQ("'#' : invalid directive: foo", rest.str());
EXPECT_EQ(
MessageType::Warning,
ParseGlslangOutput("WARNING: 15:36: The following extension must be "
"enabled to use this feature:",
false, false, &segment_number, &line_number, &rest));
EXPECT_EQ("15", segment_number.str());
EXPECT_EQ("36", line_number.str());
EXPECT_EQ("The following extension must be enabled to use this feature:",
rest.str());
}
TEST(ParseGlslangOutputTest, FileName_BaseAndExtension) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Error,
ParseGlslangOutput("ERROR: shader.vert:5: something wrong", false,
false, &source_name, &line_number, &rest));
EXPECT_EQ("shader.vert", source_name.str());
EXPECT_EQ("5", line_number.str());
EXPECT_EQ("something wrong", rest.str());
}
TEST(ParseGlslangOutputTest, FileName_BaseOnly) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Warning,
ParseGlslangOutput("WARNING: file:42: something wrong", false,
false, &source_name, &line_number, &rest));
EXPECT_EQ("file", source_name.str());
EXPECT_EQ("42", line_number.str());
EXPECT_EQ("something wrong", rest.str());
}
TEST(ParseGlslangOutputTest, FileName_HexNumber) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Warning,
ParseGlslangOutput("WARNING: 0xdeedbeef:0: wa:ha:ha", false, false,
&source_name, &line_number, &rest));
EXPECT_EQ("0xdeedbeef", source_name.str());
EXPECT_EQ("0", line_number.str());
EXPECT_EQ("wa:ha:ha", rest.str());
}
TEST(ParseGlslangOutputTest, FileName_ContainsColons) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Warning,
ParseGlslangOutput("WARNING: foo:bar:0: wa:ha:ha", false, false,
&source_name, &line_number, &rest));
EXPECT_EQ("foo:bar", source_name.str());
EXPECT_EQ("0", line_number.str());
EXPECT_EQ("wa:ha:ha", rest.str());
}
TEST(ParseGlslangOutputTest, NoFile) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(MessageType::Warning,
ParseGlslangOutput("WARNING: :12: abc", false, false, &source_name,
&line_number, &rest));
EXPECT_EQ("", source_name.str());
EXPECT_EQ("12", line_number.str());
EXPECT_EQ("abc", rest.str());
}
TEST(ParseGlslangOutputTest, NoLineNumber_InferredAsGlobalNoLocation) {
string_piece source_name;
string_piece line_number;
string_piece rest;
// No solution since there is no room for digits.
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("WARNING: foo:: abc", false, false,
&source_name, &line_number, &rest));
EXPECT_EQ("", source_name.str());
EXPECT_EQ("", line_number.str());
EXPECT_EQ("foo:: abc", rest.str());
}
TEST(ParseGlslangOutputTest, NoSpaceAfterColon_InferredAsGlobalNoLocation) {
string_piece source_name;
string_piece line_number;
string_piece rest;
// No solution since there is no space after the line-number-and-colon.
EXPECT_EQ(MessageType::GlobalWarning,
ParseGlslangOutput("WARNING: foo:12:abc", false, false,
&source_name, &line_number, &rest));
EXPECT_EQ("", source_name.str());
EXPECT_EQ("", line_number.str());
EXPECT_EQ("foo:12:abc", rest.str());
}
TEST(ParseGlslangOutputTest, WindowsPath) {
string_piece source_name;
string_piece line_number;
string_piece rest;
EXPECT_EQ(
MessageType::Error,
ParseGlslangOutput(R"(ERROR: C:\path\to\shader.glsl:5: something wrong)",
false, false, &source_name, &line_number, &rest));
EXPECT_EQ(R"(C:\path\to\shader.glsl)", source_name.str());
EXPECT_EQ("5", line_number.str());
EXPECT_EQ("something wrong", rest.str());
EXPECT_EQ(
MessageType::Warning,
ParseGlslangOutput(R"(WARNING: \\path\without\drive.vert:42: BOOM!)",
false, false, &source_name, &line_number, &rest));
EXPECT_EQ(R"(\\path\without\drive.vert)", source_name.str());
EXPECT_EQ("42", line_number.str());
EXPECT_EQ("BOOM!", rest.str());
EXPECT_EQ(MessageType::Warning,
ParseGlslangOutput(R"(WARNING: X:\123.456\789:0: wa:ha:ha)", false,
false, &source_name, &line_number, &rest));
EXPECT_EQ(R"(X:\123.456\789)", source_name.str());
EXPECT_EQ("0", line_number.str());
EXPECT_EQ("wa:ha:ha", rest.str());
}
} // anonymous namespace

View File

@@ -0,0 +1,51 @@
// 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/mutex.h"
#include <gmock/gmock.h>
#include <thread>
namespace {
TEST(MutexTest, CanCreateMutex) {
shaderc_util::mutex mutex;
mutex.lock();
mutex.unlock();
}
#ifndef SHADERC_DISABLE_THREADED_TESTS
void increment_by_1000(shaderc_util::mutex& mut, int& i) {
for(size_t j = 0; j < 1000; ++j) {
mut.lock();
i = i + 1;
mut.unlock();
}
}
TEST(MutexTest, MutexLocks) {
shaderc_util::mutex mutex;
int i = 0;
std::thread t1([&mutex, &i]() { increment_by_1000(mutex, i); });
std::thread t2([&mutex, &i]() { increment_by_1000(mutex, i); });
std::thread t3([&mutex, &i]() { increment_by_1000(mutex, i); });
t1.join();
t2.join();
t3.join();
EXPECT_EQ(3000, i);
}
#endif // SHADERC_DISABLE_THREADED_TESTS
} // anonymous namespace

View File

@@ -0,0 +1,153 @@
// 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/resources.h"
#include "glslang/Include/ResourceLimits.h"
namespace shaderc_util {
// These numbers come from the OpenGL 4.4 core profile specification Chapter 23
// unless otherwise specified.
const TBuiltInResource kDefaultTBuiltInResource = {
/*.maxLights = */ 8, // From OpenGL 3.0 table 6.46.
/*.maxClipPlanes = */ 6, // From OpenGL 3.0 table 6.46.
/*.maxTextureUnits = */ 2, // From OpenGL 3.0 table 6.50.
/*.maxTextureCoords = */ 8, // From OpenGL 3.0 table 6.50.
/*.maxVertexAttribs = */ 16,
/*.maxVertexUniformComponents = */ 4096,
/*.maxVaryingFloats = */ 60, // From OpenGLES 3.1 table 6.44.
/*.maxVertexTextureImageUnits = */ 16,
/*.maxCombinedTextureImageUnits = */ 80,
/*.maxTextureImageUnits = */ 16,
/*.maxFragmentUniformComponents = */ 1024,
// glslang has 32 maxDrawBuffers.
// Pixel phone Vulkan driver in Android N has 8
// maxFragmentOutputAttachments.
/*.maxDrawBuffers = */ 8,
/*.maxVertexUniformVectors = */ 256,
/*.maxVaryingVectors = */ 15, // From OpenGLES 3.1 table 6.44.
/*.maxFragmentUniformVectors = */ 256,
/*.maxVertexOutputVectors = */ 16, // maxVertexOutputComponents / 4
/*.maxFragmentInputVectors = */ 15, // maxFragmentInputComponents / 4
/*.minProgramTexelOffset = */ -8,
/*.maxProgramTexelOffset = */ 7,
/*.maxClipDistances = */ 8,
/*.maxComputeWorkGroupCountX = */ 65535,
/*.maxComputeWorkGroupCountY = */ 65535,
/*.maxComputeWorkGroupCountZ = */ 65535,
/*.maxComputeWorkGroupSizeX = */ 1024,
/*.maxComputeWorkGroupSizeX = */ 1024,
/*.maxComputeWorkGroupSizeZ = */ 64,
/*.maxComputeUniformComponents = */ 512,
/*.maxComputeTextureImageUnits = */ 16,
/*.maxComputeImageUniforms = */ 8,
/*.maxComputeAtomicCounters = */ 8,
/*.maxComputeAtomicCounterBuffers = */ 1, // From OpenGLES 3.1 Table 6.43
/*.maxVaryingComponents = */ 60,
/*.maxVertexOutputComponents = */ 64,
/*.maxGeometryInputComponents = */ 64,
/*.maxGeometryOutputComponents = */ 128,
/*.maxFragmentInputComponents = */ 128,
/*.maxImageUnits = */ 8, // This does not seem to be defined anywhere,
// set to ImageUnits.
/*.maxCombinedImageUnitsAndFragmentOutputs = */ 8,
/*.maxCombinedShaderOutputResources = */ 8,
/*.maxImageSamples = */ 0,
/*.maxVertexImageUniforms = */ 0,
/*.maxTessControlImageUniforms = */ 0,
/*.maxTessEvaluationImageUniforms = */ 0,
/*.maxGeometryImageUniforms = */ 0,
/*.maxFragmentImageUniforms = */ 8,
/*.maxCombinedImageUniforms = */ 8,
/*.maxGeometryTextureImageUnits = */ 16,
/*.maxGeometryOutputVertices = */ 256,
/*.maxGeometryTotalOutputComponents = */ 1024,
/*.maxGeometryUniformComponents = */ 512,
/*.maxGeometryVaryingComponents = */ 60, // Does not seem to be defined
// anywhere, set equal to
// maxVaryingComponents.
/*.maxTessControlInputComponents = */ 128,
/*.maxTessControlOutputComponents = */ 128,
/*.maxTessControlTextureImageUnits = */ 16,
/*.maxTessControlUniformComponents = */ 1024,
/*.maxTessControlTotalOutputComponents = */ 4096,
/*.maxTessEvaluationInputComponents = */ 128,
/*.maxTessEvaluationOutputComponents = */ 128,
/*.maxTessEvaluationTextureImageUnits = */ 16,
/*.maxTessEvaluationUniformComponents = */ 1024,
/*.maxTessPatchComponents = */ 120,
/*.maxPatchVertices = */ 32,
/*.maxTessGenLevel = */ 64,
/*.maxViewports = */ 16,
/*.maxVertexAtomicCounters = */ 0,
/*.maxTessControlAtomicCounters = */ 0,
/*.maxTessEvaluationAtomicCounters = */ 0,
/*.maxGeometryAtomicCounters = */ 0,
/*.maxFragmentAtomicCounters = */ 8,
/*.maxCombinedAtomicCounters = */ 8,
/*.maxAtomicCounterBindings = */ 1,
/*.maxVertexAtomicCounterBuffers = */ 0, // From OpenGLES 3.1 Table 6.41.
// ARB_shader_atomic_counters.
/*.maxTessControlAtomicCounterBuffers = */ 0,
/*.maxTessEvaluationAtomicCounterBuffers = */ 0,
/*.maxGeometryAtomicCounterBuffers = */ 0,
// /ARB_shader_atomic_counters.
/*.maxFragmentAtomicCounterBuffers = */ 0, // From OpenGLES 3.1 Table 6.43.
/*.maxCombinedAtomicCounterBuffers = */ 1,
/*.maxAtomicCounterBufferSize = */ 32,
/*.maxTransformFeedbackBuffers = */ 4,
/*.maxTransformFeedbackInterleavedComponents = */ 64,
/*.maxCullDistances = */ 8, // ARB_cull_distance.
/*.maxCombinedClipAndCullDistances = */ 8, // ARB_cull_distance.
/*.maxSamples = */ 4,
/* .maxMeshOutputVerticesNV = */ 256,
/* .maxMeshOutputPrimitivesNV = */ 512,
/* .maxMeshWorkGroupSizeX_NV = */ 32,
/* .maxMeshWorkGroupSizeY_NV = */ 1,
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
/* .maxTaskWorkGroupSizeX_NV = */ 32,
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
/* .maxMeshOutputVerticesEXT = */ 256,
/* .maxMeshOutputPrimitivesEXT = */ 256,
/* .maxMeshWorkGroupSizeX_EXT = */ 128,
/* .maxMeshWorkGroupSizeY_EXT = */ 128,
/* .maxMeshWorkGroupSizeZ_EXT = */ 128,
/* .maxTaskWorkGroupSizeX_EXT = */ 128,
/* .maxTaskWorkGroupSizeY_EXT = */ 128,
/* .maxTaskWorkGroupSizeZ_EXT = */ 128,
/* .maxMeshViewCountEXT = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
// This is the glslang TLimits structure.
// It defines whether or not the following features are enabled.
// We want them to all be enabled.
/*.limits = */ {
/*.nonInductiveForLoops = */ 1,
/*.whileLoops = */ 1,
/*.doWhileLoops = */ 1,
/*.generalUniformIndexing = */ 1,
/*.generalAttributeMatrixVectorIndexing = */ 1,
/*.generalVaryingIndexing = */ 1,
/*.generalSamplerIndexing = */ 1,
/*.generalVariableIndexing = */ 1,
/*.generalConstantMatrixVectorIndexing = */ 1,
}};
} // namespace shaderc_util

View File

@@ -0,0 +1,53 @@
// 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/shader_stage.h"
namespace {
// Maps an identifier to a language.
struct LanguageMapping {
const char* id;
EShLanguage language;
};
} // anonymous namespace
namespace shaderc_util {
EShLanguage MapStageNameToLanguage(const string_piece& stage_name) {
const LanguageMapping string_to_stage[] = {
{"vertex", EShLangVertex},
{"fragment", EShLangFragment},
{"tesscontrol", EShLangTessControl},
{"tesseval", EShLangTessEvaluation},
{"geometry", EShLangGeometry},
{"compute", EShLangCompute},
{"raygen", EShLangRayGenNV},
{"intersect", EShLangIntersectNV},
{"anyhit", EShLangAnyHitNV},
{"closest", EShLangClosestHitNV},
{"miss", EShLangMissNV},
{"callable", EShLangCallableNV},
{"task", EShLangTaskNV},
{"mesh", EShLangMeshNV},
};
for (const auto& entry : string_to_stage) {
if (stage_name == entry.id) return entry.language;
}
return EShLangCount;
}
} // namespace shaderc_util

View File

@@ -0,0 +1,172 @@
// Copyright 2016 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/spirv_tools_wrapper.h"
#include <algorithm>
#include <sstream>
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/optimizer.hpp"
namespace shaderc_util {
namespace {
// Gets the corresponding target environment used in SPIRV-Tools.
spv_target_env GetSpirvToolsTargetEnv(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version) {
switch (env) {
case Compiler::TargetEnv::Vulkan:
switch (version) {
case Compiler::TargetEnvVersion::Default:
return SPV_ENV_VULKAN_1_0;
case Compiler::TargetEnvVersion::Vulkan_1_0:
return SPV_ENV_VULKAN_1_0;
case Compiler::TargetEnvVersion::Vulkan_1_1:
return SPV_ENV_VULKAN_1_1;
case Compiler::TargetEnvVersion::Vulkan_1_2:
return SPV_ENV_VULKAN_1_2;
case Compiler::TargetEnvVersion::Vulkan_1_3:
return SPV_ENV_VULKAN_1_3;
default:
break;
}
break;
case Compiler::TargetEnv::OpenGL:
return SPV_ENV_OPENGL_4_5;
case Compiler::TargetEnv::OpenGLCompat:
// Errors out before getting here. But the compiler wants us to handle
// enum anyway.
return SPV_ENV_OPENGL_4_5;
}
assert(false && "unexpected target environment or version");
return SPV_ENV_VULKAN_1_0;
}
} // anonymous namespace
bool SpirvToolsDisassemble(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const std::vector<uint32_t>& binary,
std::string* text_or_error) {
spvtools::SpirvTools tools(GetSpirvToolsTargetEnv(env, version));
std::ostringstream oss;
tools.SetMessageConsumer([&oss](spv_message_level_t, const char*,
const spv_position_t& position,
const char* message) {
oss << position.index << ": " << message;
});
const bool success =
tools.Disassemble(binary, text_or_error,
SPV_BINARY_TO_TEXT_OPTION_INDENT |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
if (!success) {
*text_or_error = oss.str();
}
return success;
}
bool SpirvToolsAssemble(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const string_piece assembly, spv_binary* binary,
std::string* errors) {
auto spvtools_context =
spvContextCreate(GetSpirvToolsTargetEnv(env, version));
spv_diagnostic spvtools_diagnostic = nullptr;
*binary = nullptr;
errors->clear();
const bool success =
spvTextToBinary(spvtools_context, assembly.data(), assembly.size(),
binary, &spvtools_diagnostic) == SPV_SUCCESS;
if (!success) {
std::ostringstream oss;
oss << spvtools_diagnostic->position.line + 1 << ":"
<< spvtools_diagnostic->position.column + 1 << ": "
<< spvtools_diagnostic->error;
*errors = oss.str();
}
spvDiagnosticDestroy(spvtools_diagnostic);
spvContextDestroy(spvtools_context);
return success;
}
bool SpirvToolsOptimize(Compiler::TargetEnv env,
Compiler::TargetEnvVersion version,
const std::vector<PassId>& enabled_passes,
spvtools::OptimizerOptions& optimizer_options,
std::vector<uint32_t>* binary, std::string* errors) {
errors->clear();
if (enabled_passes.empty()) return true;
if (std::all_of(
enabled_passes.cbegin(), enabled_passes.cend(),
[](const PassId& pass) { return pass == PassId::kNullPass; })) {
return true;
}
spvtools::ValidatorOptions val_opts;
// This allows flexible memory layout for HLSL.
val_opts.SetSkipBlockLayout(true);
// This allows HLSL legalization regarding resources.
val_opts.SetRelaxLogicalPointer(true);
// This uses relaxed rules for pre-legalized HLSL.
val_opts.SetBeforeHlslLegalization(true);
// Set additional optimizer options.
optimizer_options.set_validator_options(val_opts);
optimizer_options.set_run_validator(true);
spvtools::Optimizer optimizer(GetSpirvToolsTargetEnv(env, version));
std::ostringstream oss;
optimizer.SetMessageConsumer(
[&oss](spv_message_level_t, const char*, const spv_position_t&,
const char* message) { oss << message << "\n"; });
for (const auto& pass : enabled_passes) {
switch (pass) {
case PassId::kLegalizationPasses:
optimizer.RegisterLegalizationPasses();
break;
case PassId::kPerformancePasses:
optimizer.RegisterPerformancePasses();
break;
case PassId::kSizePasses:
optimizer.RegisterSizePasses();
break;
case PassId::kNullPass:
// We actually don't need to do anything for null pass.
break;
case PassId::kStripDebugInfo:
optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass());
break;
case PassId::kCompactIds:
optimizer.RegisterPass(spvtools::CreateCompactIdsPass());
break;
}
}
if (!optimizer.Run(binary->data(), binary->size(), binary,
optimizer_options)) {
*errors = oss.str();
return false;
}
return true;
}
} // namespace shaderc_util

View File

@@ -0,0 +1,437 @@
// 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/string_piece.h"
#include <gtest/gtest.h>
#include <sstream>
#include <unordered_map>
#include "death_test.h"
namespace {
using shaderc_util::string_piece;
TEST(string_piece, creation) {
std::string my_string("std::string");
const char* my_c_string = "c::string";
string_piece my_string_piece(my_string);
string_piece my_c_string_piece(my_c_string);
string_piece my_partial_c_string_piece(my_c_string, my_c_string + 3);
string_piece my_string_piece_string_piece(my_string_piece);
EXPECT_EQ("std::string", my_string_piece);
EXPECT_EQ("c::string", my_c_string_piece);
EXPECT_EQ("c::", my_partial_c_string_piece);
EXPECT_EQ("std::string", my_string_piece_string_piece);
}
TEST(string_piece, creation_with_empty_data) {
string_piece my_string_piece(nullptr, nullptr);
EXPECT_EQ("", my_string_piece);
}
TEST(string_piece, creation_with_nullptr) {
string_piece my_string_piece(nullptr);
EXPECT_EQ("", my_string_piece);
}
TEST(string_pieceDeathTest, creation_causing_assert) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece("my_cstring", nullptr), ".*");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece(nullptr, "my_cstring"), ".*");
}
TEST(string_pieceDeathTest, front) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece(nullptr).front(), "Assertion");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece(nullptr, nullptr).front(),
"Assertion");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece("").front(), "Assertion");
string_piece s("nonempty");
s.clear();
EXPECT_DEBUG_DEATH_IF_SUPPORTED(s.front(), "Assertion");
}
TEST(string_pieceDeathTest, back) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece(nullptr).back(), "Assertion");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece(nullptr, nullptr).back(),
"Assertion");
EXPECT_DEBUG_DEATH_IF_SUPPORTED(string_piece("").back(), "Assertion");
string_piece s("nonempty");
s.clear();
EXPECT_DEBUG_DEATH_IF_SUPPORTED(s.back(), "Assertion");
}
TEST(string_piece, substr) {
string_piece my_string("my really long string");
EXPECT_EQ("my really long string", my_string.substr(0, string_piece::npos));
EXPECT_EQ("my really long string", my_string.substr(0));
EXPECT_EQ("really long string", my_string.substr(3, string_piece::npos));
EXPECT_EQ("really long string", my_string.substr(3));
EXPECT_EQ("really", my_string.substr(3, 6));
}
TEST(string_piece, length) {
EXPECT_EQ(0u, string_piece().size());
EXPECT_TRUE(string_piece().empty());
EXPECT_EQ(10u, string_piece("0123456789").size());
std::string my_string("std::string");
EXPECT_EQ(my_string.size(), string_piece(my_string).size());
}
TEST(string_piece, clear) {
string_piece my_string("my really long string");
EXPECT_EQ("my really long string", my_string);
string_piece other_string(my_string);
EXPECT_EQ("my really long string", other_string);
my_string.clear();
EXPECT_EQ("", my_string);
EXPECT_EQ("my really long string", other_string);
}
TEST(string_piece, str) {
std::string test_string;
{
std::string temporary_string("my really long string");
string_piece my_stringpiece(temporary_string);
string_piece my_substring = my_stringpiece.substr(3, 6);
EXPECT_EQ("really", my_substring);
test_string = my_substring.str();
}
EXPECT_EQ("really", test_string);
}
template <char C>
bool find_char(char c) {
return c == C;
}
TEST(string_piece, find_first_not_matching) {
string_piece my_string("aaaaaaa b");
EXPECT_EQ(7u, my_string.find_first_not_matching(find_char<'a'>));
EXPECT_EQ(0u, my_string.find_first_not_matching(find_char<'b'>));
EXPECT_EQ(0u, string_piece(" ").find_first_not_matching(::isdigit));
size_t npos = string_piece::npos;
EXPECT_EQ(npos, string_piece("").find_first_not_matching(::isdigit));
EXPECT_EQ(npos, string_piece("123").find_first_not_matching(::isdigit));
EXPECT_EQ(3u, string_piece("123 ").find_first_not_matching(::isdigit));
}
TEST(string_piece, find_first_not_of) {
size_t npos = string_piece::npos;
string_piece my_string("aaaaaaa b");
EXPECT_EQ(7u, my_string.find_first_not_of("a"));
EXPECT_EQ(0u, my_string.find_first_not_of("b"));
EXPECT_EQ(7u, my_string.find_first_not_of('a'));
EXPECT_EQ(0u, my_string.find_first_not_of('b'));
EXPECT_EQ(0u, string_piece(" ").find_first_not_of("0123456789"));
EXPECT_EQ(7u, my_string.find_first_not_of("a", 2));
EXPECT_EQ(2u, my_string.find_first_not_of("b", 2));
EXPECT_EQ(7u, my_string.find_first_not_of('a', 2));
EXPECT_EQ(4u, my_string.find_first_not_of('b', 4));
EXPECT_EQ(0u, string_piece(" ").find_first_not_of("0123456789"));
EXPECT_EQ(npos, string_piece(" ").find_first_not_of("0123456789", 5));
EXPECT_EQ(npos, string_piece("").find_first_not_of("012345689"));
EXPECT_EQ(npos, string_piece("").find_first_not_of("012345689", 1));
EXPECT_EQ(npos, string_piece("123").find_first_not_of("0123456789"));
EXPECT_EQ(npos, string_piece("123").find_first_not_of("0123456789", 1));
EXPECT_EQ(3u, string_piece("123 ").find_first_not_of("0123456789", 2));
EXPECT_EQ(npos, string_piece("123 ").find_first_not_of("0123456789", 4));
EXPECT_EQ(npos, string_piece("").find_first_not_of("1"));
EXPECT_EQ(npos, string_piece("111").find_first_not_of('1'));
}
TEST(string_piece, find_first_of_char) {
const size_t npos = string_piece::npos;
string_piece my_string("my really long string");
EXPECT_EQ(0u, my_string.find_first_of('m'));
EXPECT_EQ(3u, my_string.find_first_of('r'));
EXPECT_EQ(npos, my_string.find_first_of('z'));
size_t pos = my_string.find_first_of('l');
EXPECT_EQ(6u, pos);
// If pos points to a 'l' then we should just find that one
EXPECT_EQ(6u, my_string.find_first_of('l', pos));
EXPECT_EQ(7u, my_string.find_first_of('l', pos + 1));
EXPECT_EQ(10u, my_string.find_first_of('l', pos + 2));
EXPECT_EQ(npos, my_string.find_first_of('l', pos + 5));
EXPECT_EQ(npos, my_string.find_first_of('z', 0));
EXPECT_EQ(npos, my_string.find_first_of('z', npos));
my_string.clear();
EXPECT_EQ(npos, my_string.find_first_of('a'));
EXPECT_EQ(npos, my_string.find_first_of('a', 0));
}
TEST(string_piece, find_first_of) {
string_piece my_string("aaaaaa b");
EXPECT_EQ(0u, my_string.find_first_of("a"));
EXPECT_EQ(7u, my_string.find_first_of("b"));
EXPECT_EQ(6u, my_string.find_first_of(" "));
size_t npos = string_piece::npos;
EXPECT_EQ(npos, my_string.find_first_of("xh"));
EXPECT_EQ(6u, my_string.find_first_of(" x"));
EXPECT_EQ(6u, my_string.find_first_of(" b"));
EXPECT_EQ(0u, my_string.find_first_of("ab"));
EXPECT_EQ(6u, my_string.find_first_of(" x", 2));
EXPECT_EQ(6u, my_string.find_first_of(" b", 2));
EXPECT_EQ(2u, my_string.find_first_of("ab", 2));
EXPECT_EQ(npos, my_string.find_first_of("ab", 10));
EXPECT_EQ(npos, my_string.find_first_of("c"));
EXPECT_EQ(npos, my_string.find_first_of("c", 1));
EXPECT_EQ(npos, string_piece(" ").find_first_of("a"));
EXPECT_EQ(npos, string_piece(" ").find_first_of("a", 10));
EXPECT_EQ(npos, string_piece("aa").find_first_of(""));
EXPECT_EQ(npos, string_piece("aa").find_first_of("", 1));
EXPECT_EQ(npos, string_piece("").find_first_of(""));
EXPECT_EQ(npos, string_piece("").find_first_of("", 1));
EXPECT_EQ(npos, string_piece("").find_first_of("a"));
EXPECT_EQ(npos, string_piece("").find_first_of("ae"));
EXPECT_EQ(npos, string_piece("").find_first_of("ae", 32));
}
TEST(string_piece, find_last_of) {
string_piece my_string("aaaaaa b");
EXPECT_EQ(5u, my_string.find_last_of('a'));
EXPECT_EQ(7u, my_string.find_last_of('b'));
EXPECT_EQ(6u, my_string.find_last_of(' '));
EXPECT_EQ(5u, my_string.find_last_of("a"));
EXPECT_EQ(7u, my_string.find_last_of("b"));
EXPECT_EQ(6u, my_string.find_last_of(" "));
size_t npos = string_piece::npos;
EXPECT_EQ(npos, my_string.find_last_of("xh"));
EXPECT_EQ(6u, my_string.find_last_of(" x"));
EXPECT_EQ(7u, my_string.find_last_of(" b"));
EXPECT_EQ(7u, my_string.find_last_of("ab"));
EXPECT_EQ(4u, my_string.find_last_of('a', 4));
EXPECT_EQ(5u, my_string.find_last_of('a', 6));
EXPECT_EQ(0u, string_piece("abbbaa").find_last_of('a', 3));
EXPECT_EQ(4u, string_piece("abbbaa").find_last_of('a', 4));
EXPECT_EQ(5u, string_piece("abbbaa").find_last_of('a', 5));
EXPECT_EQ(5u, string_piece("abbbaa").find_last_of('a', 6));
EXPECT_EQ(npos, string_piece("abbbaa").find_last_of('c', 2));
EXPECT_EQ(npos, my_string.find_last_of("c"));
EXPECT_EQ(npos, string_piece(" ").find_last_of("a"));
EXPECT_EQ(npos, string_piece("aa").find_last_of(""));
EXPECT_EQ(npos, string_piece("").find_last_of(""));
EXPECT_EQ(npos, string_piece("").find_last_of("a"));
EXPECT_EQ(npos, my_string.find_last_of('c'));
EXPECT_EQ(npos, string_piece(" ").find_last_of('a'));
EXPECT_EQ(npos, string_piece("").find_last_of('a'));
EXPECT_EQ(npos, string_piece("").find_last_of("ae"));
}
TEST(string_piece, begin_end) {
const char* my_string = "my really long string";
string_piece p(my_string);
size_t pos = 0;
for (auto it = p.begin(); it != p.end(); ++it) {
EXPECT_EQ(my_string[pos++], *it);
}
pos = 0;
for (auto c : p) {
EXPECT_EQ(my_string[pos++], c);
}
}
TEST(string_piece, front_back) {
// EXPECT_TRUE() is used here because gtest will think we are comparing
// between pointer and integer here if EXPECT_EQ() is used.
const string_piece one_char("a");
EXPECT_TRUE(one_char.front() == 'a');
EXPECT_TRUE(one_char.back() == 'a');
const string_piece two_chars("bc");
EXPECT_TRUE(two_chars.front() == 'b');
EXPECT_TRUE(two_chars.back() == 'c');
const string_piece multi_chars("w vm g gg t\t");
EXPECT_TRUE(multi_chars.front() == 'w');
EXPECT_TRUE(multi_chars.back() == '\t');
}
TEST(string_piece, starts_with) {
EXPECT_TRUE(string_piece("my string").starts_with("my"));
EXPECT_TRUE(string_piece("my string").starts_with("my s"));
EXPECT_TRUE(string_piece("my string").starts_with("m"));
EXPECT_TRUE(string_piece("my string").starts_with(""));
EXPECT_TRUE(string_piece("my string").starts_with("my string"));
EXPECT_TRUE(string_piece("").starts_with(""));
EXPECT_FALSE(string_piece("").starts_with("a"));
EXPECT_FALSE(string_piece("my string").starts_with(" "));
EXPECT_FALSE(string_piece("my string").starts_with("my stq"));
EXPECT_FALSE(string_piece("my string").starts_with("a"));
EXPECT_FALSE(string_piece("my string").starts_with("my strings"));
}
TEST(string_piece, find) {
const size_t npos = string_piece::npos;
string_piece my_string("gooogle gooogle");
EXPECT_EQ(0u, my_string.find(""));
EXPECT_EQ(0u, my_string.find("g"));
EXPECT_EQ(4u, my_string.find("g", 1));
EXPECT_EQ(0u, my_string.find("go"));
EXPECT_EQ(8u, my_string.find("go", 1));
EXPECT_EQ(1u, my_string.find("oo"));
EXPECT_EQ(1u, my_string.find("oo", 1));
EXPECT_EQ(2u, my_string.find("oo", 2));
EXPECT_EQ(9u, my_string.find("oo", 3));
EXPECT_EQ(4u, my_string.find("gle"));
EXPECT_EQ(12u, my_string.find("gle", 5));
EXPECT_EQ(npos, my_string.find("0"));
EXPECT_EQ(npos, my_string.find("does-not-exist"));
EXPECT_EQ(npos, my_string.find("longer than gooogle gooogle"));
EXPECT_EQ(npos, my_string.find("", npos));
EXPECT_EQ(npos, my_string.find("gle", npos));
}
TEST(string_piece, get_fields) {
string_piece input;
std::vector<string_piece> expected_lines;
EXPECT_EQ(expected_lines, input.get_fields('\n'));
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
input = "first line";
expected_lines = {"first line"};
EXPECT_EQ(expected_lines, input.get_fields('\n'));
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
input = "first line\n";
expected_lines = {"first line"};
EXPECT_EQ(expected_lines, input.get_fields('\n'));
expected_lines = {"first line\n"};
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
input = "\nfirst line";
expected_lines = {"", "first line"};
EXPECT_EQ(expected_lines, input.get_fields('\n'));
expected_lines = {"\n", "first line"};
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
input = "first line\nsecond line\nthird line\n";
expected_lines = {"first line", "second line", "third line"};
EXPECT_EQ(expected_lines, input.get_fields('\n'));
expected_lines = {"first line\n", "second line\n", "third line\n"};
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
input = "first line\n\nsecond line\n\nthird line\n\n";
expected_lines = {"first line", "", "second line", "", "third line", ""};
EXPECT_EQ(expected_lines, input.get_fields('\n'));
expected_lines = {"first line\n", "\n", "second line\n",
"\n", "third line\n", "\n"};
EXPECT_EQ(expected_lines, input.get_fields('\n', true));
}
TEST(string_piece, operator_stream_out) {
std::stringstream stream;
string_piece my_string("my really long string");
stream << my_string;
EXPECT_EQ("my really long string", stream.str());
stream.str("");
stream << my_string.substr(3, 6);
EXPECT_EQ("really", stream.str());
stream.str("");
stream << string_piece();
EXPECT_EQ("", stream.str());
}
TEST(string_piece, lrstrip) {
string_piece nothing_to_remove("abcdefg");
EXPECT_EQ("abcdefg", nothing_to_remove.lstrip("hijklmn"));
EXPECT_EQ("abcdefg", nothing_to_remove.rstrip("hijklmn"));
EXPECT_EQ("abcdefg", nothing_to_remove.strip("hijklmn"));
string_piece empty_string("");
EXPECT_EQ(0u, empty_string.lstrip("google").size());
EXPECT_EQ(0u, empty_string.rstrip("google").size());
EXPECT_EQ(0u, empty_string.strip("google").size());
string_piece remove_nothing("asdfghjkl");
EXPECT_EQ("asdfghjkl", remove_nothing.lstrip(""));
EXPECT_EQ("asdfghjkl", remove_nothing.rstrip(""));
EXPECT_EQ("asdfghjkl", remove_nothing.strip(""));
string_piece strip_numbers("0123g4o5o6g7l8e9");
EXPECT_EQ("g4o5o6g7l8e9", strip_numbers.lstrip("0123456789"));
EXPECT_EQ("0123g4o5o6g7l8e", strip_numbers.rstrip("0123456789"));
EXPECT_EQ("g4o5o6g7l8e", strip_numbers.strip("0123456789"));
}
TEST(string_piece, strip_whitespace) {
string_piece lots_of_space(" b i n g o ");
EXPECT_EQ("b i n g o", lots_of_space.strip_whitespace());
string_piece whitespaces("\v\t\f\n\rleft\r\t\f\n\vright\f\n\t\v\r");
EXPECT_EQ("left\r\t\f\n\vright", whitespaces.strip_whitespace());
string_piece remove_all(" \t ");
EXPECT_EQ(0u, remove_all.strip_whitespace().size());
}
TEST(string_piece, not_equal) {
EXPECT_FALSE(string_piece() != string_piece());
EXPECT_FALSE(string_piece("") != string_piece());
EXPECT_TRUE(string_piece() != string_piece(" "));
EXPECT_FALSE(string_piece("abc") != string_piece("abc"));
EXPECT_TRUE(string_piece("abc") != string_piece("abc "));
EXPECT_TRUE(string_piece("abc") != string_piece("abd"));
EXPECT_FALSE("" != string_piece());
EXPECT_FALSE("" != string_piece(""));
EXPECT_TRUE(" " != string_piece(""));
EXPECT_FALSE("abc" != string_piece("abc"));
EXPECT_TRUE(" abc" != string_piece("abc"));
EXPECT_TRUE("abd" != string_piece("abc"));
}
TEST(string_piece, data) {
EXPECT_EQ(nullptr, string_piece().data());
const char* empty = "";
EXPECT_EQ(empty, string_piece(empty).data());
const char* space = " ";
EXPECT_EQ(space, string_piece(space).data());
const char* a = "a";
EXPECT_EQ(a, string_piece(a).data());
const char* abc = "abc";
EXPECT_EQ(abc, string_piece(abc).data());
EXPECT_EQ(abc + 1, string_piece(abc).substr(1).data());
EXPECT_EQ(abc + 3, string_piece(abc).substr(3).data());
}
TEST(string_piece, unordered_map) {
std::unordered_map<string_piece, int> dict;
dict["abc"] = 123;
EXPECT_EQ(123, dict["abc"]);
}
} // anonymous namespace

View File

@@ -0,0 +1,59 @@
// 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/version_profile.h"
#include <cctype>
#include <sstream>
namespace {
const int kVersionNumberLength = 3;
const int kMaxProfileLength = 13; // strlen(compatibility)
const int kMaxVersionProfileLength = kVersionNumberLength + kMaxProfileLength;
const int kMinVersionProfileLength = kVersionNumberLength;
} // anonymous namespace
namespace shaderc_util {
bool ParseVersionProfile(const std::string& version_profile, int* version,
EProfile* profile) {
if (version_profile.size() < kMinVersionProfileLength ||
version_profile.size() > kMaxVersionProfileLength ||
!::isdigit(version_profile.front()))
return false;
std::string profile_string;
std::istringstream(version_profile) >> *version >> profile_string;
if (!IsKnownVersion(*version)) {
return false;
}
if (profile_string.empty()) {
*profile = ENoProfile;
} else if (profile_string == "core") {
*profile = ECoreProfile;
} else if (profile_string == "es") {
*profile = EEsProfile;
} else if (profile_string == "compatibility") {
*profile = ECompatibilityProfile;
} else {
return false;
}
return true;
}
} // namespace shaderc_util

View File

@@ -0,0 +1,133 @@
// Copyright 2017 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/version_profile.h"
#include "gmock/gmock.h"
namespace {
using shaderc_util::IsKnownVersion;
using shaderc_util::ParseVersionProfile;
using ::testing::Eq;
using ::testing::ValuesIn;
TEST(IsKnownVersionTest, Samples) {
EXPECT_TRUE(IsKnownVersion(100));
EXPECT_TRUE(IsKnownVersion(110));
EXPECT_TRUE(IsKnownVersion(120));
EXPECT_TRUE(IsKnownVersion(130));
EXPECT_TRUE(IsKnownVersion(140));
EXPECT_TRUE(IsKnownVersion(150));
EXPECT_TRUE(IsKnownVersion(300));
EXPECT_TRUE(IsKnownVersion(330));
EXPECT_TRUE(IsKnownVersion(310));
EXPECT_TRUE(IsKnownVersion(400));
EXPECT_TRUE(IsKnownVersion(410));
EXPECT_TRUE(IsKnownVersion(420));
EXPECT_TRUE(IsKnownVersion(430));
EXPECT_TRUE(IsKnownVersion(440));
EXPECT_TRUE(IsKnownVersion(450));
EXPECT_TRUE(IsKnownVersion(460));
EXPECT_FALSE(IsKnownVersion(101));
EXPECT_FALSE(IsKnownVersion(470));
}
struct ParseVersionProfileCase {
std::string input;
bool success;
// The following are only used when success is true.
int expected_version;
EProfile expected_profile;
};
using ParseVersionProfileTest = ::testing::TestWithParam<ParseVersionProfileCase>;
TEST_P(ParseVersionProfileTest, Sample) {
int version = 0;
EProfile profile = EBadProfile;
const bool result = ParseVersionProfile(GetParam().input, &version, &profile);
EXPECT_THAT(result, GetParam().success);
if (result) {
EXPECT_THAT(version, GetParam().expected_version);
EXPECT_THAT(profile, GetParam().expected_profile);
}
}
// For OpenGL ES GLSL (ESSL) versions, see
// https://www.khronos.org/registry/OpenGL/index_e.php
INSTANTIATE_TEST_SUITE_P(OpenGLESCases, ParseVersionProfileTest,
ValuesIn(std::vector<ParseVersionProfileCase>{
{"100es", true, 100, EEsProfile},
{"300es", true, 300, EEsProfile},
{"310es", true, 310, EEsProfile},
{"320es", true, 320, EEsProfile},
{"99es", false, 0, EBadProfile},
{"500es", false, 0, EBadProfile},
}));
// For OpenGL GLSL versions, see
// https://www.khronos.org/registry/OpenGL/index_gl.php
INSTANTIATE_TEST_SUITE_P(OpenGLBlankCases, ParseVersionProfileTest,
ValuesIn(std::vector<ParseVersionProfileCase>{
{"110", true, 110, ENoProfile},
{"120", true, 120, ENoProfile},
{"130", true, 130, ENoProfile},
{"140", true, 140, ENoProfile},
{"150", true, 150, ENoProfile},
{"330", true, 330, ENoProfile},
{"400", true, 400, ENoProfile},
{"410", true, 410, ENoProfile},
{"420", true, 420, ENoProfile},
{"430", true, 430, ENoProfile},
{"440", true, 440, ENoProfile},
{"450", true, 450, ENoProfile},
{"460", true, 460, ENoProfile},
{"99", false, 0, EBadProfile},
{"500", false, 0, EBadProfile},
}));
INSTANTIATE_TEST_SUITE_P(OpenGLCoreCases, ParseVersionProfileTest,
ValuesIn(std::vector<ParseVersionProfileCase>{
{"320core", true, 320, ECoreProfile},
{"330core", true, 330, ECoreProfile},
{"400core", true, 400, ECoreProfile},
{"410core", true, 410, ECoreProfile},
{"420core", true, 420, ECoreProfile},
{"430core", true, 430, ECoreProfile},
{"440core", true, 440, ECoreProfile},
{"450core", true, 450, ECoreProfile},
{"460core", true, 460, ECoreProfile},
}));
INSTANTIATE_TEST_SUITE_P(
OpenGLCompatibilityCases, ParseVersionProfileTest,
ValuesIn(std::vector<ParseVersionProfileCase>{
{"320compatibility", true, 320, ECompatibilityProfile},
{"330compatibility", true, 330, ECompatibilityProfile},
{"400compatibility", true, 400, ECompatibilityProfile},
{"410compatibility", true, 410, ECompatibilityProfile},
{"420compatibility", true, 420, ECompatibilityProfile},
{"430compatibility", true, 430, ECompatibilityProfile},
{"440compatibility", true, 440, ECompatibilityProfile},
{"450compatibility", true, 450, ECompatibilityProfile},
{"460compatibility", true, 460, ECompatibilityProfile},
}));
} // anonymous namespace

View File

@@ -0,0 +1,16 @@
# Copyright 2020 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.
file(GLOB all_files ${CMAKE_CURRENT_LIST_DIR}/*)
file(COPY ${all_files} DESTINATION .)

View File

@@ -0,0 +1 @@
The quick brown fox jumps over a lazy dog.