Merge with KhronosGroup/SPIRV-Cross

This commit is contained in:
Polona Caserman 2017-01-15 16:14:21 +01:00 committed by Robert Konrad
commit 84c3092aba
12 changed files with 727 additions and 816 deletions

View File

@ -16,6 +16,8 @@ cmake_minimum_required(VERSION 2.8)
project(SPIRV-Cross)
enable_testing()
option(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS "Instead of throwing exceptions assert" OFF)
if(${CMAKE_GENERATOR} MATCHES "Makefile")
if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
message(FATAL_ERROR "Build out of tree to avoid overwriting Makefile")
@ -50,24 +52,35 @@ target_link_libraries(spirv-cross-msl spirv-cross-glsl)
target_link_libraries(spirv-cross-cpp spirv-cross-glsl)
target_include_directories(spirv-cross-core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
set(spirv-compiler-options "")
set(spirv-compiler-defines "")
if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
endif()
# To specify special debug or optimization options, use
# -DCMAKE_CXX_COMPILE_FLAGS
# However, we require the C++11 dialect.
if (NOT "${MSVC}")
set(spirv-compiler-options -std=c++11 -Wall -Wextra -Werror -Wshadow)
set(spirv-compiler-defines __STDC_LIMIT_MACROS)
target_compile_options(spirv-cross-core PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-glsl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-msl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-cpp PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options})
target_compile_definitions(spirv-cross-core PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-glsl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-msl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-cpp PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines})
endif(NOT "${MSVC}")
set(spirv-compiler-options ${spirv-compiler-options} -std=c++11 -Wall -Wextra -Werror -Wshadow)
set(spirv-compiler-defines ${spirv-compiler-defines} __STDC_LIMIT_MACROS)
if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions)
endif()
endif()
target_compile_options(spirv-cross-core PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-glsl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-msl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-cpp PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options})
target_compile_definitions(spirv-cross-core PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-glsl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-msl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-cpp PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines})
# Set up tests, using only the simplest modes of the test_shaders
# script. You have to invoke the script manually to:
@ -80,5 +93,6 @@ if(${PYTHONINTERP_FOUND} AND ${PYTHON_VERSION_MAJOR} GREATER 2)
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py
${CMAKE_CURRENT_SOURCE_DIR}/shaders)
else()
message(WARNING "Testing disabled. Could not find python3")
message(WARNING "Testing disabled. Could not find python3. If you have python3 installed try running "
"cmake with -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python3 to help it find the executable")
endif()

View File

@ -15,7 +15,11 @@ CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow -D__STDC_LIMIT_MACROS
ifeq ($(DEBUG), 1)
CXXFLAGS += -O0 -g
else
CXXFLAGS += -O2 -g
CXXFLAGS += -O2 -DNDEBUG
endif
ifeq ($(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS), 1)
CXXFLAGS += -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS -fno-exceptions
endif
all: $(TARGET)

View File

@ -23,6 +23,8 @@ However, most missing features are expected to be "trivial" improvements at this
SPIRV-Cross has been tested on Linux, OSX and Windows.
The make and CMake build flavors offer the option to treat exceptions as assertions. To disable exceptions for make just append SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=1 to the command line. For CMake append -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=ON. By default exceptions are enabled.
### Linux and macOS
Just run `make` on the command line. A recent GCC (4.8+) or Clang (3.x+) compiler is required as SPIRV-Cross uses C++11 extensively.

View File

@ -30,6 +30,17 @@ using namespace spv;
using namespace spirv_cross;
using namespace std;
#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
#define THROW(x) \
do \
{ \
fprintf(stderr, "%s.", x); \
exit(1); \
} while (0)
#else
#define THROW(x) runtime_error(x)
#endif
struct CLIParser;
struct CLICallbacks
{
@ -53,7 +64,9 @@ struct CLIParser
bool parse()
{
#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
try
#endif
{
while (argc && !ended_state)
{
@ -69,7 +82,7 @@ struct CLIParser
auto itr = cbs.callbacks.find(next);
if (itr == ::end(cbs.callbacks))
{
throw logic_error("Invalid argument.\n");
THROW("Invalid argument");
}
itr->second(*this);
@ -78,6 +91,7 @@ struct CLIParser
return true;
}
#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
catch (...)
{
if (cbs.error_handler)
@ -86,6 +100,7 @@ struct CLIParser
}
return false;
}
#endif
}
void end()
@ -97,13 +112,13 @@ struct CLIParser
{
if (!argc)
{
throw logic_error("Tried to parse uint, but nothing left in arguments.\n");
THROW("Tried to parse uint, but nothing left in arguments");
}
uint32_t val = stoul(*argv);
if (val > numeric_limits<uint32_t>::max())
{
throw out_of_range("next_uint() out of range.\n");
THROW("next_uint() out of range");
}
argc--;
@ -116,7 +131,7 @@ struct CLIParser
{
if (!argc)
{
throw logic_error("Tried to parse double, but nothing left in arguments.\n");
THROW("Tried to parse double, but nothing left in arguments");
}
double val = stod(*argv);
@ -131,7 +146,7 @@ struct CLIParser
{
if (!argc)
{
throw logic_error("Tried to parse string, but nothing left in arguments.\n");
THROW("Tried to parse string, but nothing left in arguments");
}
const char *ret = *argv;
@ -202,7 +217,7 @@ static void print_resources(const Compiler &compiler, const char *tag, const vec
uint32_t block_size = 0;
if (is_sized_block)
block_size = compiler.get_declared_struct_size(compiler.get_type(res.base_type_id));
block_size = uint32_t(compiler.get_declared_struct_size(compiler.get_type(res.base_type_id)));
string array;
for (auto arr : type.array)
@ -219,6 +234,10 @@ static void print_resources(const Compiler &compiler, const char *tag, const vec
fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding));
if (mask & (1ull << DecorationInputAttachmentIndex))
fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex));
if (mask & (1ull << DecorationNonReadable))
fprintf(stderr, " writeonly");
if (mask & (1ull << DecorationNonWritable))
fprintf(stderr, " readonly");
if (is_sized_block)
fprintf(stderr, " (BlockSize : %u bytes)", block_size);
fprintf(stderr, "\n");

View File

@ -17,13 +17,31 @@
#ifndef SPIRV_CROSS_COMMON_HPP
#define SPIRV_CROSS_COMMON_HPP
#include <cstdio>
#include <cstring>
#include <functional>
#include <sstream>
#include <stdio.h>
#include <string.h>
namespace spirv_cross
{
#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
#ifndef _MSC_VER
[[noreturn]]
#endif
inline void
report_and_abort(const std::string &msg)
{
#ifdef NDEBUG
(void)msg;
#else
fprintf(stderr, "There was a compiler error: %s\n", msg.c_str());
#endif
abort();
}
#define SPIRV_CROSS_THROW(x) report_and_abort(x)
#else
class CompilerError : public std::runtime_error
{
public:
@ -33,6 +51,9 @@ public:
}
};
#define SPIRV_CROSS_THROW(x) throw CompilerError(x)
#endif
namespace inner
{
template <typename T>
@ -792,7 +813,7 @@ public:
{
holder = std::move(val);
if (type != TypeNone && type != new_type)
throw CompilerError("Overwriting a variant with new type.");
SPIRV_CROSS_THROW("Overwriting a variant with new type.");
type = new_type;
}
@ -800,9 +821,9 @@ public:
T &get()
{
if (!holder)
throw CompilerError("nullptr");
SPIRV_CROSS_THROW("nullptr");
if (T::type != type)
throw CompilerError("Bad cast");
SPIRV_CROSS_THROW("Bad cast");
return *static_cast<T *>(holder.get());
}
@ -810,9 +831,9 @@ public:
const T &get() const
{
if (!holder)
throw CompilerError("nullptr");
SPIRV_CROSS_THROW("nullptr");
if (T::type != type)
throw CompilerError("Bad cast");
SPIRV_CROSS_THROW("Bad cast");
return *static_cast<const T *>(holder.get());
}
@ -872,7 +893,6 @@ struct Meta
uint32_t input_attachment = 0;
uint32_t spec_id = 0;
bool builtin = false;
bool per_instance = false;
};
Decoration decoration;

View File

@ -116,8 +116,8 @@ void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
auto &type = get<SPIRType>(var.basetype);
auto &flags = meta[var.self].decoration.decoration_flags;
if ((flags & (1ull << DecorationBinding)) || (flags & (1ull << DecorationDescriptorSet)))
throw CompilerError("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
"Remap to location with reflection API first or disable these decorations.");
SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
"Remap to location with reflection API first or disable these decorations.");
emit_block_struct(type);
auto buffer_name = to_name(type.self);
@ -298,7 +298,7 @@ string CompilerCPP::compile()
do
{
if (pass_count >= 3)
throw CompilerError("Over 3 compilation loops detected. Must be a bug!");
SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
resource_registrations.clear();
reset();
@ -469,7 +469,7 @@ void CompilerCPP::emit_header()
break;
default:
throw CompilerError("Unsupported execution model.");
SPIRV_CROSS_THROW("Unsupported execution model.");
}
switch (execution.model)
@ -506,6 +506,6 @@ void CompilerCPP::emit_header()
break;
default:
throw CompilerError("Unsupported execution model.");
SPIRV_CROSS_THROW("Unsupported execution model.");
}
}

View File

@ -33,7 +33,7 @@ Instruction::Instruction(const vector<uint32_t> &spirv, uint32_t &index)
count = (spirv[index] >> 16) & 0xffff;
if (count == 0)
throw CompilerError("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
offset = index + 1;
length = count - 1;
@ -41,7 +41,7 @@ Instruction::Instruction(const vector<uint32_t> &spirv, uint32_t &index)
index += count;
if (index > spirv.size())
throw CompilerError("SPIR-V instruction goes out of bounds.");
SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds.");
}
Compiler::Compiler(vector<uint32_t> ir)
@ -328,7 +328,7 @@ const SPIRType &Compiler::expression_type(uint32_t id) const
return get<SPIRType>(get<SPIRUndef>(id).basetype);
default:
throw CompilerError("Cannot resolve expression type.");
SPIRV_CROSS_THROW("Cannot resolve expression type.");
}
}
@ -665,7 +665,7 @@ static string extract_string(const vector<uint32_t> &spirv, uint32_t offset)
}
}
throw CompilerError("String was not terminated before EOF");
SPIRV_CROSS_THROW("String was not terminated before EOF");
}
static bool is_valid_spirv_version(uint32_t version)
@ -687,7 +687,7 @@ void Compiler::parse()
{
auto len = spirv.size();
if (len < 5)
throw CompilerError("SPIRV file too small.");
SPIRV_CROSS_THROW("SPIRV file too small.");
auto s = spirv.data();
@ -696,7 +696,7 @@ void Compiler::parse()
transform(begin(spirv), end(spirv), begin(spirv), [](uint32_t c) { return swap_endian(c); });
if (s[0] != MagicNumber || !is_valid_spirv_version(s[1]))
throw CompilerError("Invalid SPIRV format.");
SPIRV_CROSS_THROW("Invalid SPIRV format.");
uint32_t bound = s[3];
ids.resize(bound);
@ -710,9 +710,9 @@ void Compiler::parse()
parse(i);
if (current_function)
throw CompilerError("Function was not terminated.");
SPIRV_CROSS_THROW("Function was not terminated.");
if (current_block)
throw CompilerError("Block was not terminated.");
SPIRV_CROSS_THROW("Block was not terminated.");
}
void Compiler::flatten_interface_block(uint32_t id)
@ -722,24 +722,24 @@ void Compiler::flatten_interface_block(uint32_t id)
auto flags = meta.at(type.self).decoration.decoration_flags;
if (!type.array.empty())
throw CompilerError("Type is array of UBOs.");
SPIRV_CROSS_THROW("Type is array of UBOs.");
if (type.basetype != SPIRType::Struct)
throw CompilerError("Type is not a struct.");
SPIRV_CROSS_THROW("Type is not a struct.");
if ((flags & (1ull << DecorationBlock)) == 0)
throw CompilerError("Type is not a block.");
SPIRV_CROSS_THROW("Type is not a block.");
if (type.member_types.empty())
throw CompilerError("Member list of struct is empty.");
SPIRV_CROSS_THROW("Member list of struct is empty.");
uint32_t t = type.member_types[0];
for (auto &m : type.member_types)
if (t != m)
throw CompilerError("Types in block differ.");
SPIRV_CROSS_THROW("Types in block differ.");
auto &mtype = get<SPIRType>(t);
if (!mtype.array.empty())
throw CompilerError("Member type cannot be arrays.");
SPIRV_CROSS_THROW("Member type cannot be arrays.");
if (mtype.basetype == SPIRType::Struct)
throw CompilerError("Member type cannot be struct.");
SPIRV_CROSS_THROW("Member type cannot be struct.");
// Inherit variable name from interface block name.
meta.at(var.self).decoration.alias = meta.at(type.self).decoration.alias;
@ -794,7 +794,7 @@ void Compiler::set_name(uint32_t id, const std::string &name)
return;
// Functions in glslangValidator are mangled with name(<mangled> stuff.
// Normally, we would never see '(' in any legal indentifiers, so just strip them out.
// Normally, we would never see '(' in any legal identifiers, so just strip them out.
str = name.substr(0, name.find('('));
for (uint32_t i = 0; i < str.size(); i++)
@ -890,7 +890,7 @@ uint32_t Compiler::get_member_decoration(uint32_t id, uint32_t index, Decoration
case DecorationSpecId:
return dec.spec_id;
default:
return 0;
return 1;
}
}
@ -1025,7 +1025,7 @@ uint32_t Compiler::get_decoration(uint32_t id, Decoration decoration) const
case DecorationSpecId:
return dec.spec_id;
default:
return 0;
return 1;
}
}
@ -1118,7 +1118,7 @@ void Compiler::parse(const Instruction &instruction)
{
uint32_t cap = ops[0];
if (cap == CapabilityKernel)
throw CompilerError("Kernel capability not supported.");
SPIRV_CROSS_THROW("Kernel capability not supported.");
break;
}
@ -1129,7 +1129,7 @@ void Compiler::parse(const Instruction &instruction)
if (ext == "GLSL.std.450")
set<SPIRExtension>(id, SPIRExtension::GLSL);
else
throw CompilerError("Only GLSL.std.450 extension interface supported.");
SPIRV_CROSS_THROW("Only GLSL.std.450 extension interface supported.");
break;
}
@ -1336,6 +1336,10 @@ void Compiler::parse(const Instruction &instruction)
type.image.ms = ops[5] != 0;
type.image.sampled = ops[6];
type.image.format = static_cast<ImageFormat>(ops[7]);
if (length > 8)
type.image.video = ops[8];
else
type.image.video = false;
break;
}
@ -1368,7 +1372,7 @@ void Compiler::parse(const Instruction &instruction)
ptrbase = base;
if (ptrbase.pointer)
throw CompilerError("Cannot make pointer-to-pointer type.");
SPIRV_CROSS_THROW("Cannot make pointer-to-pointer type.");
ptrbase.pointer = true;
ptrbase.storage = static_cast<StorageClass>(ops[1]);
@ -1431,7 +1435,7 @@ void Compiler::parse(const Instruction &instruction)
if (storage == StorageClassFunction)
{
if (!current_function)
throw CompilerError("No function currently in scope");
SPIRV_CROSS_THROW("No function currently in scope");
current_function->add_local_variable(id);
}
else if (storage == StorageClassPrivate || storage == StorageClassWorkgroup || storage == StorageClassOutput)
@ -1444,17 +1448,6 @@ void Compiler::parse(const Instruction &instruction)
if (variable_storage_is_aliased(var))
aliased_variables.push_back(var.self);
// glslangValidator does not emit required qualifiers here.
// Solve this by making the image access as restricted as possible
// and loosen up if we need to.
auto &vartype = expression_type(id);
if (vartype.basetype == SPIRType::Image)
{
auto &flags = meta.at(id).decoration.decoration_flags;
flags |= 1ull << DecorationNonWritable;
flags |= 1ull << DecorationNonReadable;
}
break;
}
@ -1466,9 +1459,9 @@ void Compiler::parse(const Instruction &instruction)
case OpPhi:
{
if (!current_function)
throw CompilerError("No function currently in scope");
SPIRV_CROSS_THROW("No function currently in scope");
if (!current_block)
throw CompilerError("No block currently in scope");
SPIRV_CROSS_THROW("No block currently in scope");
uint32_t result_type = ops[0];
uint32_t id = ops[1];
@ -1560,7 +1553,7 @@ void Compiler::parse(const Instruction &instruction)
break;
default:
throw CompilerError("OpConstantComposite only supports 1, 2, 3 and 4 columns.");
SPIRV_CROSS_THROW("OpConstantComposite only supports 1, 2, 3 and 4 columns.");
}
}
else
@ -1618,7 +1611,7 @@ void Compiler::parse(const Instruction &instruction)
break;
default:
throw CompilerError("OpConstantComposite only supports 1, 2, 3 and 4 components.");
SPIRV_CROSS_THROW("OpConstantComposite only supports 1, 2, 3 and 4 components.");
}
}
@ -1635,7 +1628,7 @@ void Compiler::parse(const Instruction &instruction)
uint32_t type = ops[3];
if (current_function)
throw CompilerError("Must end a function before starting a new one!");
SPIRV_CROSS_THROW("Must end a function before starting a new one!");
current_function = &set<SPIRFunction>(id, res, type);
break;
@ -1647,7 +1640,7 @@ void Compiler::parse(const Instruction &instruction)
uint32_t id = ops[1];
if (!current_function)
throw CompilerError("Must be in a function!");
SPIRV_CROSS_THROW("Must be in a function!");
current_function->add_parameter(type, id);
set<SPIRVariable>(id, type, StorageClassFunction);
@ -1659,7 +1652,7 @@ void Compiler::parse(const Instruction &instruction)
if (current_block)
{
// Very specific error message, but seems to come up quite often.
throw CompilerError(
SPIRV_CROSS_THROW(
"Cannot end a function before ending the current block.\n"
"Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid.");
}
@ -1672,7 +1665,7 @@ void Compiler::parse(const Instruction &instruction)
{
// OpLabel always starts a block.
if (!current_function)
throw CompilerError("Blocks cannot exist outside functions!");
SPIRV_CROSS_THROW("Blocks cannot exist outside functions!");
uint32_t id = ops[0];
@ -1681,7 +1674,7 @@ void Compiler::parse(const Instruction &instruction)
current_function->entry_block = id;
if (current_block)
throw CompilerError("Cannot start a block before ending the current block.");
SPIRV_CROSS_THROW("Cannot start a block before ending the current block.");
current_block = &set<SPIRBlock>(id);
break;
@ -1691,7 +1684,7 @@ void Compiler::parse(const Instruction &instruction)
case OpBranch:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
uint32_t target = ops[0];
current_block->terminator = SPIRBlock::Direct;
@ -1703,7 +1696,7 @@ void Compiler::parse(const Instruction &instruction)
case OpBranchConditional:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
current_block->condition = ops[0];
current_block->true_block = ops[1];
@ -1717,10 +1710,10 @@ void Compiler::parse(const Instruction &instruction)
case OpSwitch:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
if (current_block->merge == SPIRBlock::MergeNone)
throw CompilerError("Switch statement is not structured");
SPIRV_CROSS_THROW("Switch statement is not structured");
current_block->terminator = SPIRBlock::MultiSelect;
@ -1740,7 +1733,7 @@ void Compiler::parse(const Instruction &instruction)
case OpKill:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
current_block->terminator = SPIRBlock::Kill;
current_block = nullptr;
break;
@ -1749,7 +1742,7 @@ void Compiler::parse(const Instruction &instruction)
case OpReturn:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
current_block->terminator = SPIRBlock::Return;
current_block = nullptr;
break;
@ -1758,7 +1751,7 @@ void Compiler::parse(const Instruction &instruction)
case OpReturnValue:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
current_block->terminator = SPIRBlock::Return;
current_block->return_value = ops[0];
current_block = nullptr;
@ -1768,7 +1761,7 @@ void Compiler::parse(const Instruction &instruction)
case OpUnreachable:
{
if (!current_block)
throw CompilerError("Trying to end a non-existing block.");
SPIRV_CROSS_THROW("Trying to end a non-existing block.");
current_block->terminator = SPIRBlock::Unreachable;
current_block = nullptr;
break;
@ -1777,7 +1770,7 @@ void Compiler::parse(const Instruction &instruction)
case OpSelectionMerge:
{
if (!current_block)
throw CompilerError("Trying to modify a non-existing block.");
SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
current_block->next_block = ops[0];
current_block->merge = SPIRBlock::MergeSelection;
@ -1788,7 +1781,7 @@ void Compiler::parse(const Instruction &instruction)
case OpLoopMerge:
{
if (!current_block)
throw CompilerError("Trying to modify a non-existing block.");
SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
current_block->merge_block = ops[0];
current_block->continue_block = ops[1];
@ -1808,7 +1801,7 @@ void Compiler::parse(const Instruction &instruction)
case OpSpecConstantOp:
{
if (length < 3)
throw CompilerError("OpSpecConstantOp not enough arguments.");
SPIRV_CROSS_THROW("OpSpecConstantOp not enough arguments.");
uint32_t result_type = ops[0];
uint32_t id = ops[1];
@ -1822,7 +1815,7 @@ void Compiler::parse(const Instruction &instruction)
default:
{
if (!current_block)
throw CompilerError("Currently no block to insert opcode.");
SPIRV_CROSS_THROW("Currently no block to insert opcode.");
current_block->ops.push_back(instruction);
break;
@ -2046,7 +2039,7 @@ uint32_t Compiler::type_struct_member_offset(const SPIRType &type, uint32_t inde
if (dec.decoration_flags & (1ull << DecorationOffset))
return dec.offset;
else
throw CompilerError("Struct member does not have Offset set.");
SPIRV_CROSS_THROW("Struct member does not have Offset set.");
}
uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_t index) const
@ -2057,7 +2050,7 @@ uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_
if (dec.decoration_flags & (1ull << DecorationArrayStride))
return dec.array_stride;
else
throw CompilerError("Struct member does not have ArrayStride set.");
SPIRV_CROSS_THROW("Struct member does not have ArrayStride set.");
}
size_t Compiler::get_declared_struct_size(const SPIRType &type) const
@ -2084,7 +2077,7 @@ size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, ui
case SPIRType::Image:
case SPIRType::SampledImage:
case SPIRType::Sampler:
throw CompilerError("Querying size for object with opaque size.\n");
SPIRV_CROSS_THROW("Querying size for object with opaque size.\n");
default:
break;
@ -2363,7 +2356,7 @@ SPIREntryPoint &Compiler::get_entry_point(const std::string &name)
[&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.name == name; });
if (itr == end(entry_points))
throw CompilerError("Entry point does not exist.");
SPIRV_CROSS_THROW("Entry point does not exist.");
return itr->second;
}
@ -2375,7 +2368,7 @@ const SPIREntryPoint &Compiler::get_entry_point(const std::string &name) const
[&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.name == name; });
if (itr == end(entry_points))
throw CompilerError("Entry point does not exist.");
SPIRV_CROSS_THROW("Entry point does not exist.");
return itr->second;
}
@ -2619,11 +2612,11 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
bool separate_sampler = type.basetype == SPIRType::Sampler;
if (separate_image)
throw CompilerError(
SPIRV_CROSS_THROW(
"Attempting to use arrays of separate images. This is not possible to statically remap to plain GLSL.");
if (separate_sampler)
throw CompilerError("Attempting to use arrays of separate samplers. This is not possible to statically "
"remap to plain GLSL.");
SPIRV_CROSS_THROW("Attempting to use arrays of separate samplers. This is not possible to statically "
"remap to plain GLSL.");
return true;
}
@ -2938,7 +2931,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
if (potential == 0)
potential = block;
else
potential = -1u;
potential = ~(0u);
}
builder.add_block(block);
}
@ -2965,7 +2958,7 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry)
auto block = loop_variable.second;
// The variable was accessed in multiple continue blocks, ignore.
if (block == -1u || block == 0)
if (block == ~(0u) || block == 0)
continue;
// Dead code.

View File

@ -140,6 +140,8 @@ public:
bool is_decoration_set(uint32_t id, spv::Decoration decoration) const;
// Gets the value for decorations which take arguments.
// If the decoration is a boolean (i.e. spv::DecorationNonWritable),
// 1 will be returned.
// If decoration doesn't exist or decoration is not recognized,
// 0 will be returned.
uint32_t get_decoration(uint32_t id, spv::Decoration decoration) const;
@ -329,7 +331,7 @@ protected:
return nullptr;
if (instr.offset + instr.length > spirv.size())
throw CompilerError("Compiler::stream() out of range.");
SPIRV_CROSS_THROW("Compiler::stream() out of range.");
return &spirv[instr.offset];
}
std::vector<uint32_t> spirv;
@ -485,7 +487,7 @@ protected:
void analyze_variable_scope(SPIRFunction &function);
private:
protected:
void parse();
void parse(const Instruction &i);

View File

@ -184,7 +184,7 @@ void CompilerGLSL::remap_pls_variables()
}
if (var.storage != StorageClassInput && !input_is_target)
throw CompilerError("Can only use in and target variables for PLS inputs.");
SPIRV_CROSS_THROW("Can only use in and target variables for PLS inputs.");
var.remapped_variable = true;
}
@ -192,7 +192,7 @@ void CompilerGLSL::remap_pls_variables()
{
auto &var = get<SPIRVariable>(output.id);
if (var.storage != StorageClassOutput)
throw CompilerError("Can only use out variables for PLS outputs.");
SPIRV_CROSS_THROW("Can only use out variables for PLS outputs.");
var.remapped_variable = true;
}
}
@ -207,7 +207,7 @@ void CompilerGLSL::find_static_extensions()
if (type.basetype == SPIRType::Double)
{
if (options.es)
throw CompilerError("FP64 not supported in ES profile.");
SPIRV_CROSS_THROW("FP64 not supported in ES profile.");
if (!options.es && options.version < 400)
require_extension("GL_ARB_gpu_shader_fp64");
}
@ -215,7 +215,7 @@ void CompilerGLSL::find_static_extensions()
if (type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64)
{
if (options.es)
throw CompilerError("64-bit integers not supported in ES profile.");
SPIRV_CROSS_THROW("64-bit integers not supported in ES profile.");
if (!options.es)
require_extension("GL_ARB_gpu_shader_int64");
}
@ -229,7 +229,7 @@ void CompilerGLSL::find_static_extensions()
if (!options.es && options.version < 430)
require_extension("GL_ARB_compute_shader");
if (options.es && options.version < 310)
throw CompilerError("At least ESSL 3.10 required for compute shaders.");
SPIRV_CROSS_THROW("At least ESSL 3.10 required for compute shaders.");
break;
case ExecutionModelGeometry:
@ -266,12 +266,13 @@ string CompilerGLSL::compile()
{
// Scan the SPIR-V to find trivial uses of extensions.
find_static_extensions();
fixup_image_load_store_access();
uint32_t pass_count = 0;
do
{
if (pass_count >= 3)
throw CompilerError("Over 3 compilation loops detected. Must be a bug!");
SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
reset();
@ -561,7 +562,7 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format)
{
auto check_desktop = [this] {
if (options.es)
throw CompilerError("Attempting to use image format not supported in ES profile.");
SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile.");
};
switch (format)
@ -747,7 +748,7 @@ uint32_t CompilerGLSL::type_to_std430_alignment(const SPIRType &type, uint64_t f
// Rule 8 implied.
}
throw CompilerError("Did not find suitable std430 rule for type. Bogus decorations?");
SPIRV_CROSS_THROW("Did not find suitable std430 rule for type. Bogus decorations?");
}
uint32_t CompilerGLSL::type_to_std430_array_stride(const SPIRType &type, uint64_t flags)
@ -978,7 +979,7 @@ void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var)
#if 0
if (flags & ((1ull << DecorationBinding) | (1ull << DecorationDescriptorSet)))
throw CompilerError("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
"Remap to location with reflection API first or disable these decorations.");
#endif
@ -1096,7 +1097,7 @@ void CompilerGLSL::emit_uniform(const SPIRVariable &var)
if (!options.es && options.version < 420)
require_extension("GL_ARB_shader_image_load_store");
else if (options.es && options.version < 310)
throw CompilerError("At least ESSL 3.10 required for shader image load store.");
SPIRV_CROSS_THROW("At least ESSL 3.10 required for shader image load store.");
}
add_resource_name(var.self);
@ -1182,14 +1183,14 @@ void CompilerGLSL::replace_fragment_output(SPIRVariable &var)
// FIXME: This seems like an extremely odd-ball case, so it's probably fine to leave it like this for now.
m.alias = "gl_FragData";
if (location != 0)
throw CompilerError("Arrayed output variable used, but location is not 0. "
"This is unimplemented in SPIRV-Cross.");
SPIRV_CROSS_THROW("Arrayed output variable used, but location is not 0. "
"This is unimplemented in SPIRV-Cross.");
if (is_legacy_es())
require_extension("GL_EXT_draw_buffers");
}
else
throw CompilerError("Array-of-array output variable used. This cannot be implemented in legacy GLSL.");
SPIRV_CROSS_THROW("Array-of-array output variable used. This cannot be implemented in legacy GLSL.");
var.compat_builtin = true; // We don't want to declare this variable, but use the name as-is.
}
@ -1234,13 +1235,13 @@ void CompilerGLSL::emit_pls()
{
auto &execution = get_entry_point();
if (execution.model != ExecutionModelFragment)
throw CompilerError("Pixel local storage only supported in fragment shaders.");
SPIRV_CROSS_THROW("Pixel local storage only supported in fragment shaders.");
if (!options.es)
throw CompilerError("Pixel local storage only supported in OpenGL ES.");
SPIRV_CROSS_THROW("Pixel local storage only supported in OpenGL ES.");
if (options.version < 300)
throw CompilerError("Pixel local storage only supported in ESSL 3.0 and above.");
SPIRV_CROSS_THROW("Pixel local storage only supported in ESSL 3.0 and above.");
if (!pls_inputs.empty())
{
@ -1263,6 +1264,30 @@ void CompilerGLSL::emit_pls()
}
}
void CompilerGLSL::fixup_image_load_store_access()
{
for (auto &id : ids)
{
if (id.get_type() != TypeVariable)
continue;
uint32_t var = id.get<SPIRVariable>().self;
auto &vartype = expression_type(var);
if (vartype.basetype == SPIRType::Image)
{
// Older glslangValidator does not emit required qualifiers here.
// Solve this by making the image access as restricted as possible and loosen up if we need to.
// If any no-read/no-write flags are actually set, assume that the compiler knows what it's doing.
auto &flags = meta.at(var).decoration.decoration_flags;
static const uint64_t NoWrite = 1ull << DecorationNonWritable;
static const uint64_t NoRead = 1ull << DecorationNonReadable;
if ((flags & (NoWrite | NoRead)) == 0)
flags |= NoRead | NoWrite;
}
}
}
void CompilerGLSL::emit_resources()
{
auto &execution = get_entry_point();
@ -1651,7 +1676,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
case OpSelect:
{
if (cop.arguments.size() < 3)
throw CompilerError("Not enough arguments to OpSpecConstantOp.");
SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
// This one is pretty annoying. It's triggered from
// uint(bool), int(bool) from spec constants.
@ -1660,7 +1685,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
// If we cannot, fail.
if (!to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0]))
{
throw CompilerError(
SPIRV_CROSS_THROW(
"Cannot implement specialization constant op OpSelect. "
"Need trivial select implementation which can be resolved to a simple cast from boolean.");
}
@ -1669,7 +1694,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
default:
// Some opcodes are unimplemented here, these are currently not possible to test from glslang.
throw CompilerError("Unimplemented spec constant op.");
SPIRV_CROSS_THROW("Unimplemented spec constant op.");
}
SPIRType::BaseType input_type;
@ -1692,7 +1717,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
if (binary)
{
if (cop.arguments.size() < 2)
throw CompilerError("Not enough arguments to OpSpecConstantOp.");
SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
string cast_op0;
string cast_op1;
@ -1714,7 +1739,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
else if (unary)
{
if (cop.arguments.size() < 1)
throw CompilerError("Not enough arguments to OpSpecConstantOp.");
SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
// Auto-bitcast to result type as needed.
// Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants.
@ -1723,7 +1748,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
else
{
if (cop.arguments.size() < 1)
throw CompilerError("Not enough arguments to OpSpecConstantOp.");
SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
return join(op, "(", to_expression(cop.arguments[0]), ")");
}
}
@ -1939,7 +1964,7 @@ string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t
break;
default:
throw CompilerError("Invalid constant expression basetype.");
SPIRV_CROSS_THROW("Invalid constant expression basetype.");
}
if (c.vector_size() > 1)
@ -2206,7 +2231,9 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp
else if (op == "textureProjLod")
return join("texture", type, is_legacy_es() ? "ProjLodEXT" : "ProjLod");
else
throw CompilerError(join("Unsupported legacy texture op: ", op));
{
SPIRV_CROSS_THROW(join("Unsupported legacy texture op: ", op));
}
}
bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t left, uint32_t right, uint32_t lerp)
@ -2351,7 +2378,7 @@ string CompilerGLSL::to_combined_image_sampler(uint32_t image_id, uint32_t samp_
return to_expression(itr->id);
else
{
throw CompilerError(
SPIRV_CROSS_THROW(
"Cannot find mapping for combined sampler parameter, was build_combined_image_samplers() used "
"before compile() was called?");
}
@ -2368,8 +2395,8 @@ string CompilerGLSL::to_combined_image_sampler(uint32_t image_id, uint32_t samp_
return to_expression(itr->combined_id);
else
{
throw CompilerError("Cannot find mapping for combined sampler, was build_combined_image_samplers() used "
"before compile() was called?");
SPIRV_CROSS_THROW("Cannot find mapping for combined sampler, was build_combined_image_samplers() used "
"before compile() was called?");
}
}
}
@ -2392,7 +2419,7 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
uint32_t length = i.length;
if (i.offset + length > spirv.size())
throw CompilerError("Compiler::parse() opcode out of range.");
SPIRV_CROSS_THROW("Compiler::parse() opcode out of range.");
uint32_t result_type = ops[0];
uint32_t id = ops[1];
@ -2402,6 +2429,7 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
uint32_t comp = 0;
bool gather = false;
bool proj = false;
bool fetch = false;
const uint32_t *opt = nullptr;
switch (op)
@ -2416,23 +2444,29 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
case OpImageSampleProjDrefImplicitLod:
case OpImageSampleProjDrefExplicitLod:
dref = ops[4];
proj = true;
opt = &ops[5];
length -= 5;
proj = true;
break;
case OpImageDrefGather:
dref = ops[4];
opt = &ops[5];
gather = true;
length -= 5;
gather = true;
break;
case OpImageGather:
comp = ops[4];
opt = &ops[5];
gather = true;
length -= 5;
gather = true;
break;
case OpImageFetch:
opt = &ops[4];
length -= 4;
fetch = true;
break;
case OpImageSampleProjImplicitLod:
@ -2489,8 +2523,7 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
if (length)
{
flags = opt[0];
opt++;
flags = *opt++;
length--;
}
@ -2512,35 +2545,55 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
test(sample, ImageOperandsSampleMask);
string expr;
string texop;
bool forward = false;
expr += to_function_name(img, imgtype, fetch, gather, proj, coffsets, (coffset || offset), (grad_x || grad_y), lod,
dref);
expr += "(";
expr += to_function_args(img, imgtype, fetch, gather, proj, coord, coord_components, dref, grad_x, grad_y, lod,
coffset, offset, bias, comp, sample, &forward);
expr += ")";
if (op == OpImageFetch)
texop += "texelFetch";
emit_op(result_type, id, expr, forward);
}
// Returns the function name for a texture sampling function for the specified image and sampling characteristics.
// For some subclasses, the function is a method on the specified image.
string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
bool has_array_offsets, bool has_offset, bool has_grad, bool has_lod, bool)
{
string fname;
if (is_fetch)
fname += "texelFetch";
else
{
texop += "texture";
fname += "texture";
if (gather)
texop += "Gather";
if (coffsets)
texop += "Offsets";
if (proj)
texop += "Proj";
if (grad_x || grad_y)
texop += "Grad";
if (lod)
texop += "Lod";
if (is_gather)
fname += "Gather";
if (has_array_offsets)
fname += "Offsets";
if (is_proj)
fname += "Proj";
if (has_grad)
fname += "Grad";
if (has_lod)
fname += "Lod";
}
if (coffset || offset)
texop += "Offset";
if (has_offset)
fname += "Offset";
if (is_legacy())
texop = legacy_tex_op(texop, imgtype);
return is_legacy() ? legacy_tex_op(fname, imgtype) : fname;
}
expr += texop;
expr += "(";
expr += to_expression(img);
// Returns the function args for a texture sampling function for the specified image and sampling characteristics.
string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &, bool, bool, bool, uint32_t coord,
uint32_t coord_components, uint32_t dref, uint32_t grad_x, uint32_t grad_y,
uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp,
uint32_t sample, bool *p_forward)
{
string farg_str = to_expression(img);
bool swizz_func = backend.swizzle_is_function;
auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * {
@ -2576,84 +2629,93 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
// SPIR-V splits dref and coordinate.
if (coord_components == 4) // GLSL also splits the arguments in two.
{
expr += ", ";
expr += to_expression(coord);
expr += ", ";
expr += to_expression(dref);
farg_str += ", ";
farg_str += to_expression(coord);
farg_str += ", ";
farg_str += to_expression(dref);
}
else
{
// Create a composite which merges coord/dref into a single vector.
auto type = expression_type(coord);
type.vecsize = coord_components + 1;
expr += ", ";
expr += type_to_glsl_constructor(type);
expr += "(";
expr += coord_expr;
expr += ", ";
expr += to_expression(dref);
expr += ")";
farg_str += ", ";
farg_str += type_to_glsl_constructor(type);
farg_str += "(";
farg_str += coord_expr;
farg_str += ", ";
farg_str += to_expression(dref);
farg_str += ")";
}
}
else
{
expr += ", ";
expr += coord_expr;
farg_str += ", ";
farg_str += coord_expr;
}
if (grad_x || grad_y)
{
forward = forward && should_forward(grad_x);
forward = forward && should_forward(grad_y);
expr += ", ";
expr += to_expression(grad_x);
expr += ", ";
expr += to_expression(grad_y);
farg_str += ", ";
farg_str += to_expression(grad_x);
farg_str += ", ";
farg_str += to_expression(grad_y);
}
if (lod)
{
forward = forward && should_forward(lod);
expr += ", ";
expr += to_expression(lod);
farg_str += ", ";
farg_str += to_expression(lod);
}
if (coffset)
{
forward = forward && should_forward(coffset);
expr += ", ";
expr += to_expression(coffset);
farg_str += ", ";
farg_str += to_expression(coffset);
}
else if (offset)
{
forward = forward && should_forward(offset);
expr += ", ";
expr += to_expression(offset);
farg_str += ", ";
farg_str += to_expression(offset);
}
if (bias)
{
forward = forward && should_forward(bias);
expr += ", ";
expr += to_expression(bias);
farg_str += ", ";
farg_str += to_expression(bias);
}
if (comp)
{
forward = forward && should_forward(comp);
expr += ", ";
expr += to_expression(comp);
farg_str += ", ";
farg_str += to_expression(comp);
}
if (sample)
{
expr += ", ";
expr += to_expression(sample);
farg_str += ", ";
farg_str += to_expression(sample);
}
expr += ")";
*p_forward = forward;
emit_op(result_type, id, expr, forward);
return farg_str;
}
// Some languages may have additional standard library functions whose names conflict
// with a function defined in the body of the shader. Subclasses can override to rename
// the function name defined in the shader to avoid conflict with the language standard
// functions (eg. MSL includes saturate()).
string CompilerGLSL::clean_func_name(string func_name)
{
return func_name;
}
void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t)
@ -2671,7 +2733,7 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
if ((options.es && options.version >= 300) || (!options.es && options.version >= 130))
emit_unary_func_op(result_type, id, args[0], "roundEven");
else
throw CompilerError("roundEven supported only in ESSL 300 and GLSL 130 and up.");
SPIRV_CROSS_THROW("roundEven supported only in ESSL 300 and GLSL 130 and up.");
break;
case GLSLstd450Trunc:
@ -2955,14 +3017,16 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin)
return "gl_Position";
case BuiltInPointSize:
return "gl_PointSize";
case BuiltInClipDistance:
return "gl_ClipDistance";
case BuiltInVertexId:
if (options.vulkan_semantics)
throw CompilerError(
SPIRV_CROSS_THROW(
"Cannot implement gl_VertexID in Vulkan GLSL. This shader was created with GL semantics.");
return "gl_VertexID";
case BuiltInInstanceId:
if (options.vulkan_semantics)
throw CompilerError(
SPIRV_CROSS_THROW(
"Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
return "gl_InstanceID";
case BuiltInVertexIndex:
@ -3008,7 +3072,7 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin)
case BuiltInLocalInvocationIndex:
return "gl_LocalInvocationIndex";
default:
return "gl_???";
return join("gl_BuiltIn_", convert_to_string(builtin));
}
}
@ -3025,7 +3089,7 @@ const char *CompilerGLSL::index_to_swizzle(uint32_t index)
case 3:
return "w";
default:
throw CompilerError("Swizzle index out of range");
SPIRV_CROSS_THROW("Swizzle index out of range");
}
}
@ -3074,7 +3138,7 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
index = get<SPIRConstant>(index).scalar();
if (index >= type->member_types.size())
throw CompilerError("Member index is out of bounds!");
SPIRV_CROSS_THROW("Member index is out of bounds!");
BuiltIn builtin;
if (is_member_builtin(*type, index, &builtin))
@ -3148,7 +3212,7 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
temp.vecsize = 1;
}
else
throw CompilerError("Cannot subdivide a scalar value!");
SPIRV_CROSS_THROW("Cannot subdivide a scalar value!");
}
return expr;
@ -3582,7 +3646,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
string funexpr;
vector<string> arglist;
funexpr += to_name(func) + "(";
funexpr += clean_func_name(to_name(func)) + "(";
for (uint32_t i = 0; i < length; i++)
{
// Do not pass in separate images or samplers if we're remapping
@ -3656,7 +3720,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
length -= 2;
if (!length)
throw CompilerError("Invalid input to OpCompositeConstruct.");
SPIRV_CROSS_THROW("Invalid input to OpCompositeConstruct.");
bool forward = true;
for (uint32_t i = 0; i < length; i++)
@ -4233,7 +4297,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
break;
}
default:
throw CompilerError("Illegal argument to OpQuantizeToF16.");
SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16.");
}
emit_op(result_type, id, op, should_forward(arg));
@ -4460,7 +4524,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
BFOP(textureQueryLOD);
}
else if (options.es)
throw CompilerError("textureQueryLod not supported in ES profile.");
SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile.");
else
BFOP(textureQueryLod);
break;
@ -4471,7 +4535,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
if (!options.es && options.version < 430)
require_extension("GL_ARB_texture_query_levels");
if (options.es)
throw CompilerError("textureQueryLevels not supported in ES profile.");
SPIRV_CROSS_THROW("textureQueryLevels not supported in ES profile.");
UFOP(textureQueryLevels);
break;
}
@ -4480,7 +4544,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
auto *var = maybe_get_backing_variable(ops[2]);
if (!var)
throw CompilerError(
SPIRV_CROSS_THROW(
"Bug. OpImageQuerySamples must have a backing variable so we know if the image is sampled or not.");
auto &type = get<SPIRType>(var->basetype);
@ -4531,7 +4595,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code
{
if (type.image.ms)
throw CompilerError("Trying to remap multisampled image to variable, this is not possible.");
SPIRV_CROSS_THROW("Trying to remap multisampled image to variable, this is not possible.");
auto itr =
find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; });
@ -4541,7 +4605,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// For non-PLS inputs, we rely on subpass type remapping information to get it right
// since ImageRead always returns 4-component vectors and the backing type is opaque.
if (!var->remapped_components)
throw CompilerError("subpassInput was remapped, but remap_components is not set correctly.");
SPIRV_CROSS_THROW("subpassInput was remapped, but remap_components is not set correctly.");
imgexpr = remap_swizzle(result_type, var->remapped_components, ops[2]);
}
else
@ -4562,7 +4626,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
SPIRV_CROSS_THROW(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
@ -4577,7 +4641,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
SPIRV_CROSS_THROW(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
@ -4599,8 +4663,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ",
@ -4660,7 +4723,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t operands = ops[3];
if (operands != ImageOperandsSampleMask || length != 5)
throw CompilerError("Multisampled image used in OpImageWrite, but unexpected operand mask was used.");
SPIRV_CROSS_THROW("Multisampled image used in OpImageWrite, but unexpected operand mask was used.");
uint32_t samples = ops[4];
statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(samples),
", ", to_expression(ops[2]), ");");
@ -4686,7 +4749,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
emit_op(result_type, id, join("imageSize(", to_expression(ops[2]), ")"), true);
}
else
throw CompilerError("Invalid type for OpImageQuerySize.");
SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize.");
break;
}
@ -4801,7 +4864,7 @@ bool CompilerGLSL::is_non_native_row_major_matrix(uint32_t id)
// swaps matrix elements while retaining the original dimensional form of the matrix.
const auto type = expression_type(id);
if (type.columns != type.vecsize)
throw CompilerError("Row-major matrices must be square on this platform.");
SPIRV_CROSS_THROW("Row-major matrices must be square on this platform.");
return true;
}
@ -4822,7 +4885,7 @@ bool CompilerGLSL::member_is_non_native_row_major_matrix(const SPIRType &type, u
// swaps matrix elements while retaining the original dimensional form of the matrix.
const auto mbr_type = get<SPIRType>(type.member_types[index]);
if (mbr_type.columns != mbr_type.vecsize)
throw CompilerError("Row-major matrices must be square on this platform.");
SPIRV_CROSS_THROW("Row-major matrices must be square on this platform.");
return true;
}
@ -4989,7 +5052,7 @@ uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t inde
assert(type.array.size() == type.array_size_literal.size());
if (!type.array_size_literal[index])
throw CompilerError("The array size is not a literal, but a specialization constant or spec constant op.");
SPIRV_CROSS_THROW("The array size is not a literal, but a specialization constant or spec constant op.");
return type.array[index];
}
@ -5086,7 +5149,7 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type)
res += "2D";
break;
default:
throw CompilerError("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported.");
SPIRV_CROSS_THROW("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported.");
}
if (type.image.ms)
@ -5304,11 +5367,11 @@ void CompilerGLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
if (func.self == entry_point)
{
decl += "main";
decl += clean_func_name("main");
processing_entry_point = true;
}
else
decl += to_name(func.self);
decl += clean_func_name(to_name(func.self));
decl += "(";
vector<string> arglist;
@ -5746,7 +5809,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
break;
default:
throw CompilerError("For/while loop detected, but need while/for loop semantics.");
SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
}
begin_scope();
@ -5793,7 +5856,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
break;
default:
throw CompilerError("For/while loop detected, but need while/for loop semantics.");
SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
}
begin_scope();
@ -5959,7 +6022,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
statement("default:");
begin_scope();
if (is_break(block.default_block))
throw CompilerError("Cannot break; out of a switch statement and out of a loop at the same time ...");
SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ...");
branch(block.self, block.default_block);
end_scope();
}
@ -6000,7 +6063,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
break;
default:
throw CompilerError("Unimplemented block terminator.");
SPIRV_CROSS_THROW("Unimplemented block terminator.");
}
if (block.next_block && emit_next_block)
@ -6046,7 +6109,7 @@ void CompilerGLSL::begin_scope()
void CompilerGLSL::end_scope()
{
if (!indent)
throw CompilerError("Popping empty indent stack.");
SPIRV_CROSS_THROW("Popping empty indent stack.");
indent--;
statement("}");
}
@ -6054,7 +6117,7 @@ void CompilerGLSL::end_scope()
void CompilerGLSL::end_scope_decl()
{
if (!indent)
throw CompilerError("Popping empty indent stack.");
SPIRV_CROSS_THROW("Popping empty indent stack.");
indent--;
statement("};");
}
@ -6062,7 +6125,7 @@ void CompilerGLSL::end_scope_decl()
void CompilerGLSL::end_scope_decl(const string &decl)
{
if (!indent)
throw CompilerError("Popping empty indent stack.");
SPIRV_CROSS_THROW("Popping empty indent stack.");
indent--;
statement("} ", decl, ";");
}
@ -6082,10 +6145,10 @@ void CompilerGLSL::check_function_call_constraints(const uint32_t *args, uint32_
auto &type = get<SPIRType>(var->basetype);
if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
{
throw CompilerError("Tried passing a remapped subpassInput variable to a function. "
"This will not work correctly because type-remapping information is lost. "
"To workaround, please consider not passing the subpass input as a function parameter, "
"or use in/out variables instead which do not need type remapping information.");
SPIRV_CROSS_THROW("Tried passing a remapped subpassInput variable to a function. "
"This will not work correctly because type-remapping information is lost. "
"To workaround, please consider not passing the subpass input as a function parameter, "
"or use in/out variables instead which do not need type remapping information.");
}
}
}

View File

@ -160,6 +160,15 @@ protected:
virtual void emit_fixup();
virtual std::string variable_decl(const SPIRType &type, const std::string &name);
virtual std::string to_func_call_arg(uint32_t id);
virtual std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad,
bool has_lod, bool has_dref);
virtual std::string to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
bool is_proj, uint32_t coord, uint32_t coord_components, uint32_t dref,
uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset,
uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample,
bool *p_forward);
virtual std::string clean_func_name(std::string func_name);
std::unique_ptr<std::ostringstream> buffer;
@ -390,8 +399,8 @@ protected:
void find_static_extensions();
std::string emit_for_loop_initializers(const SPIRBlock &block);
bool optimize_read_modify_write(const std::string &lhs, const std::string &rhs);
void fixup_image_load_store_access();
};
}

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,8 @@
#include "spirv_glsl.hpp"
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace spirv_cross
@ -39,10 +41,6 @@ struct MSLConfiguration
struct MSLVertexAttr
{
uint32_t location = 0;
uint32_t msl_buffer = 0;
uint32_t msl_offset = 0;
uint32_t msl_stride = 0;
bool per_instance = false;
bool used_by_shader = false;
};
@ -95,7 +93,7 @@ public:
// Compiles the SPIR-V code into Metal Shading Language using default configuration parameters.
std::string compile() override;
void set_func_name(std::string func_name);
void set_entry_point_name(std::string func_name);
protected:
void emit_instruction(const Instruction &instr) override;
@ -104,7 +102,6 @@ protected:
void emit_header() override;
void emit_function_prototype(SPIRFunction &func, uint64_t return_flags) override;
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
void emit_texture_op(const Instruction &i) override;
void emit_fixup() override;
std::string type_to_glsl(const SPIRType &type) override;
std::string image_type_glsl(const SPIRType &type) override;
@ -114,28 +111,34 @@ protected:
size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override;
std::string to_func_call_arg(uint32_t id) override;
std::string to_name(uint32_t id, bool allow_alias = true) override;
std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
bool has_array_offsets, bool has_offset, bool has_grad, bool has_lod,
bool has_dref) override;
std::string to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x,
uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias,
uint32_t comp, uint32_t sample, bool *p_forward) override;
void extract_builtins();
void add_builtin(spv::BuiltIn builtin_type);
void register_custom_functions();
void emit_custom_functions();
void localize_global_variables();
void extract_global_variables_from_functions();
void extract_global_variables_from_function(uint32_t func_id, std::set<uint32_t> &added_arg_ids,
std::set<uint32_t> &global_var_ids,
std::set<uint32_t> &processed_func_ids);
std::unordered_map<uint32_t, std::set<uint32_t>> function_global_vars;
void add_interface_structs();
void bind_vertex_attributes(std::set<uint32_t> &bindings);
uint32_t add_interface_struct(spv::StorageClass storage, uint32_t vtx_binding = 0);
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> function_global_vars;
void extract_global_variables_from_function(uint32_t func_id, std::unordered_set<uint32_t> &added_arg_ids,
std::unordered_set<uint32_t> &global_var_ids,
std::unordered_set<uint32_t> &processed_func_ids);
uint32_t add_interface_block(spv::StorageClass storage);
void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
void emit_resources();
void emit_interface_block(uint32_t ib_var_id);
void emit_function_prototype(SPIRFunction &func, bool is_decl);
void emit_function_declarations();
void populate_func_name_overrides();
std::string func_type_decl(SPIRType &type);
std::string _clean_msl_main_func_name;
std::string clean_func_name(std::string func_name);
std::string clean_func_name(std::string func_name) override;
std::string entry_point_args(bool append_comma);
std::string get_entry_point_name();
std::string to_qualified_member_name(const SPIRType &type, uint32_t index);
@ -145,54 +148,70 @@ protected:
std::string builtin_type_decl(spv::BuiltIn builtin);
std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
std::string argument_decl(const SPIRFunction::Parameter &arg);
std::string get_vtx_idx_var_name(bool per_instance);
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype);
uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index);
uint32_t pad_to_offset(SPIRType &struct_type, bool is_indxd_vtx_input, uint32_t offset, uint32_t struct_size);
SPIRType &get_pad_type(uint32_t pad_len);
size_t get_declared_type_size(const SPIRType &type) const;
size_t get_declared_type_size(const SPIRType &type, uint64_t dec_mask) const;
size_t get_declared_type_size(uint32_t type_id) const;
size_t get_declared_type_size(uint32_t type_id, uint64_t dec_mask) const;
std::string to_component_argument(uint32_t id);
MSLConfiguration msl_config;
std::unordered_map<std::string, std::string> func_name_overrides;
std::set<uint32_t> custom_function_ops;
std::unordered_map<uint32_t, MSLVertexAttr *> vtx_attrs_by_location;
std::vector<MSLResourceBinding *> resource_bindings;
std::unordered_map<uint32_t, uint32_t> builtin_vars;
MSLResourceBinding next_metal_resource_index;
std::unordered_map<uint32_t, uint32_t> pad_type_ids_by_pad_len;
std::vector<uint32_t> stage_in_var_ids;
uint32_t stage_in_var_id = 0;
uint32_t stage_out_var_id = 0;
uint32_t stage_uniforms_var_id = 0;
uint32_t stage_uniforms_var_id = 0;
std::string qual_pos_var_name;
std::string stage_in_var_name = "in";
std::string stage_out_var_name = "out";
std::string stage_uniform_var_name = "uniforms";
std::string stage_uniform_var_name = "uniforms";
std::string sampler_name_suffix = "Smplr";
std::vector<std::string> reserved_names = {"kernel", "bias"};
};
std::vector<std::string> reserved_names = {"kernel", "bias"};
// Sorts the members of a SPIRType and associated Meta info based on a settable sorting
// aspect, which defines which aspect of the struct members will be used to sort them.
// Regardless of the sorting aspect, built-in members always appear at the end of the struct.
struct MemberSorter
{
enum SortAspect
// Extracts a set of opcodes that should be implemented as a bespoke custom function
// whose full source code is output as part of the shader source code.
struct CustomFunctionHandler : OpcodeHandler
{
Location,
Offset,
Alphabetical
CustomFunctionHandler(const CompilerMSL &compiler_, std::set<uint32_t> &custom_function_ops_)
: compiler(compiler_)
, custom_function_ops(custom_function_ops_)
{
}
bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
const CompilerMSL &compiler;
std::set<uint32_t> &custom_function_ops;
};
void sort();
bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2);
MemberSorter(SPIRType &t, Meta &m, SortAspect sa)
: type(t)
, meta(m)
, sort_aspect(sa)
// Sorts the members of a SPIRType and associated Meta info based on a settable sorting
// aspect, which defines which aspect of the struct members will be used to sort them.
// Regardless of the sorting aspect, built-in members always appear at the end of the struct.
struct MemberSorter
{
}
SPIRType &type;
Meta &meta;
SortAspect sort_aspect;
enum SortAspect
{
Location,
LocationReverse,
Offset,
OffsetThenLocationReverse,
Alphabetical
};
void sort();
bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2);
MemberSorter(SPIRType &t, Meta &m, SortAspect sa)
: type(t)
, meta(m)
, sort_aspect(sa)
{
}
SPIRType &type;
Meta &meta;
SortAspect sort_aspect;
};
};
}