SPIRV-Cross/spirv_reflect.cpp

574 lines
16 KiB
C++
Raw Normal View History

2018-06-03 18:16:37 +00:00
/*
2018-06-18 16:30:16 +00:00
* Copyright 2018 Bradley Austin Davis
2018-06-03 18:16:37 +00:00
*
* 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 "spirv_reflect.hpp"
2018-06-18 16:30:16 +00:00
#include "spirv_glsl.hpp"
#include <iomanip>
2018-06-03 18:16:37 +00:00
using namespace spv;
using namespace spirv_cross;
using namespace std;
2018-06-18 16:30:16 +00:00
namespace simple_json
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
enum class Type
{
Object,
Array,
};
2018-06-03 18:16:37 +00:00
2018-06-18 16:30:16 +00:00
using State = std::pair<Type, bool>;
using Stack = std::stack<State>;
class Stream
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
Stack stack;
std::ostringstream buffer;
uint32_t indent{ 0 };
public:
void begin_json_object();
void end_json_object();
void emit_json_key(const std::string &key);
void emit_json_key_value(const std::string &key, const std::string &value);
void emit_json_key_value(const std::string &key, bool value);
void emit_json_key_value(const std::string &key, uint32_t value);
void emit_json_key_value(const std::string &key, int32_t value);
void emit_json_key_value(const std::string &key, float value);
2018-06-18 16:30:16 +00:00
void emit_json_key_object(const std::string &key);
void emit_json_key_array(const std::string &key);
void begin_json_array();
void end_json_array();
void emit_json_array_value(const std::string &value);
void emit_json_array_value(uint32_t value);
std::string str() const
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
return buffer.str();
}
private:
inline void statement_indent()
{
for (uint32_t i = 0; i < indent; i++)
buffer << " ";
}
template <typename T>
inline void statement_inner(T &&t)
{
buffer << std::forward<T>(t);
}
template <typename T, typename... Ts>
inline void statement_inner(T &&t, Ts &&... ts)
{
buffer << std::forward<T>(t);
statement_inner(std::forward<Ts>(ts)...);
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
template <typename... Ts>
inline void statement(Ts &&... ts)
{
statement_indent();
statement_inner(std::forward<Ts>(ts)...);
buffer << '\n';
}
template <typename... Ts>
void statement_no_return(Ts &&... ts)
{
statement_indent();
statement_inner(std::forward<Ts>(ts)...);
}
};
} // namespace simple_json
using namespace simple_json;
// Hackery to emit JSON without using nlohmann/json C++ library (which requires a
// higher level of compiler compliance than is required by SPIRV-Cross
void Stream::begin_json_array()
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (!stack.empty() && stack.top().second)
2018-06-03 18:16:37 +00:00
{
statement_inner(",\n");
}
statement("[");
++indent;
2018-06-18 16:30:16 +00:00
stack.emplace(Type::Array, false);
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::end_json_array()
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (stack.empty() || stack.top().first != Type::Array)
2018-06-03 18:16:37 +00:00
SPIRV_CROSS_THROW("Invalid JSON state");
2018-06-18 16:30:16 +00:00
if (stack.top().second)
2018-06-03 18:16:37 +00:00
{
statement_inner("\n");
}
--indent;
statement_no_return("]");
2018-06-18 16:30:16 +00:00
stack.pop();
if (!stack.empty())
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
stack.top().second = true;
2018-06-03 18:16:37 +00:00
}
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_array_value(const std::string &value)
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (stack.empty() || stack.top().first != Type::Array)
2018-06-03 18:16:37 +00:00
SPIRV_CROSS_THROW("Invalid JSON state");
2018-06-18 16:30:16 +00:00
if (stack.top().second)
2018-06-03 18:16:37 +00:00
statement_inner(",\n");
statement_no_return("\"", value, "\"");
2018-06-18 16:30:16 +00:00
stack.top().second = true;
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_array_value(uint32_t value)
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (stack.empty() || stack.top().first != Type::Array)
2018-06-03 18:16:37 +00:00
SPIRV_CROSS_THROW("Invalid JSON state");
2018-06-18 16:30:16 +00:00
if (stack.top().second)
2018-06-03 18:16:37 +00:00
statement_inner(",\n");
statement_no_return(std::to_string(value));
2018-06-18 16:30:16 +00:00
stack.top().second = true;
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::begin_json_object()
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (!stack.empty() && stack.top().second)
2018-06-03 18:16:37 +00:00
{
statement_inner(",\n");
}
2018-06-18 16:30:16 +00:00
statement("{");
++indent;
stack.emplace(Type::Object, false);
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::end_json_object()
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (stack.empty() || stack.top().first != Type::Object)
2018-06-03 18:16:37 +00:00
SPIRV_CROSS_THROW("Invalid JSON state");
2018-06-18 16:30:16 +00:00
if (stack.top().second)
2018-06-03 18:16:37 +00:00
{
statement_inner("\n");
}
--indent;
statement_no_return("}");
2018-06-18 16:30:16 +00:00
stack.pop();
if (!stack.empty())
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
stack.top().second = true;
2018-06-03 18:16:37 +00:00
}
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key(const std::string &key)
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (stack.empty() || stack.top().first != Type::Object)
2018-06-03 18:16:37 +00:00
SPIRV_CROSS_THROW("Invalid JSON state");
2018-06-18 16:30:16 +00:00
if (stack.top().second)
2018-06-03 18:16:37 +00:00
statement_inner(",\n");
statement_no_return("\"", key, "\" : ");
2018-06-18 16:30:16 +00:00
stack.top().second = true;
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key_value(const std::string &key, const std::string &value)
2018-06-03 18:16:37 +00:00
{
emit_json_key(key);
statement_inner("\"", value, "\"");
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key_value(const std::string &key, uint32_t value)
2018-06-03 18:16:37 +00:00
{
emit_json_key(key);
statement_inner(value);
}
void Stream::emit_json_key_value(const std::string &key, int32_t value)
{
emit_json_key(key);
statement_inner(value);
}
void Stream::emit_json_key_value(const std::string &key, float value)
{
emit_json_key(key);
statement_inner(value);
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key_value(const std::string &key, bool value)
2018-06-03 18:16:37 +00:00
{
emit_json_key(key);
statement_inner(value ? "true" : "false");
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key_object(const std::string &key)
2018-06-03 18:16:37 +00:00
{
emit_json_key(key);
statement_inner("{\n");
++indent;
2018-06-18 16:30:16 +00:00
stack.emplace(Type::Object, false);
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void Stream::emit_json_key_array(const std::string &key)
2018-06-03 18:16:37 +00:00
{
emit_json_key(key);
statement_inner("[\n");
++indent;
2018-06-18 16:30:16 +00:00
stack.emplace(Type::Array, false);
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
void CompilerReflection::set_format(const std::string &format)
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
if (format != "json")
{
SPIRV_CROSS_THROW("Unsupported format");
}
}
2018-06-03 18:16:37 +00:00
2018-06-18 16:30:16 +00:00
string CompilerReflection::compile()
{
2018-06-03 18:16:37 +00:00
// Force a classic "C" locale, reverts when function returns
ClassicLocale classic_locale;
// Move constructor for this type is broken on GCC 4.9 ...
2018-06-20 17:25:38 +00:00
json_stream = std::make_shared<simple_json::Stream>();
2018-06-18 16:30:16 +00:00
json_stream->begin_json_object();
2018-06-20 17:25:38 +00:00
emit_entry_points();
2018-06-03 18:16:37 +00:00
emit_types();
emit_resources();
emit_specialization_constants();
2018-06-18 16:30:16 +00:00
json_stream->end_json_object();
return json_stream->str();
2018-06-03 18:16:37 +00:00
}
void CompilerReflection::emit_types()
{
bool emitted_open_tag = false;
for (auto &id : ids)
{
2018-06-18 16:30:16 +00:00
auto idType = id.get_type();
if (idType == TypeType)
2018-06-03 18:16:37 +00:00
{
auto &type = id.get<SPIRType>();
2018-06-18 16:30:16 +00:00
if (type.basetype == SPIRType::Struct && !type.pointer && type.array.empty())
2018-06-03 18:16:37 +00:00
{
emit_type(type, emitted_open_tag);
}
}
}
if (emitted_open_tag)
{
2018-06-18 16:30:16 +00:00
json_stream->end_json_object();
2018-06-03 18:16:37 +00:00
}
}
void CompilerReflection::emit_type(const SPIRType &type, bool &emitted_open_tag)
{
2018-06-20 17:25:38 +00:00
auto name = type_to_glsl(type);
2018-06-03 18:16:37 +00:00
2018-06-20 17:25:38 +00:00
if (type.type_alias != 0)
return;
2018-06-03 18:16:37 +00:00
if (!emitted_open_tag)
{
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_object("types");
2018-06-03 18:16:37 +00:00
emitted_open_tag = true;
}
2018-06-21 16:24:22 +00:00
json_stream->emit_json_key_object("_" + std::to_string(type.self));
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("name", name);
json_stream->emit_json_key_array("members");
2018-06-20 17:25:38 +00:00
// FIXME ideally we'd like to emit the size of a structure as a
// convenience to people parsing the reflected JSON. The problem
// is that there's no implicit size for a type. It's final size
// will be determined by the top level declaration in which it's
// included. So there might be one size for the struct if it's
// included in a std140 uniform block and another if it's included
// in a std430 uniform block.
// The solution is to include *all* potential sizes as a map of
// layout type name to integer, but that will probably require
// some additional logic being written in this class, or in the
// parent CompilerGLSL class.
2018-06-03 18:16:37 +00:00
auto size = type.member_types.size();
for (uint32_t i = 0; i < size; ++i)
{
emit_type_member(type, i);
}
2018-06-18 16:30:16 +00:00
json_stream->end_json_array();
json_stream->end_json_object();
2018-06-03 18:16:37 +00:00
}
void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index)
{
auto &membertype = get<SPIRType>(type.member_types[index]);
2018-06-18 16:30:16 +00:00
json_stream->begin_json_object();
2018-06-03 18:16:37 +00:00
auto name = to_member_name(type, index);
2018-06-20 17:25:38 +00:00
// FIXME we'd like to emit the offset of each member, but such offsets are
// context dependent. See the comment above regarding structure sizes
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("name", name);
if (membertype.basetype == SPIRType::Struct)
{
2018-06-21 16:24:22 +00:00
json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self));
2018-06-18 16:30:16 +00:00
}
else
{
2018-06-20 17:25:38 +00:00
json_stream->emit_json_key_value("type", type_to_glsl(membertype));
2018-06-18 16:30:16 +00:00
}
2018-06-03 18:16:37 +00:00
emit_type_member_qualifiers(type, index);
2018-06-18 16:30:16 +00:00
json_stream->end_json_object();
2018-06-03 18:16:37 +00:00
}
void CompilerReflection::emit_type_array(const SPIRType &type)
{
if (!type.array.empty())
{
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_array("array");
2018-06-20 17:25:38 +00:00
// Note that we emit the zeros here as a means of identifying
// unbounded arrays. This is necessary as otherwise there would
// be no way of differentiating between float[4] and float[4][]
2018-06-03 18:16:37 +00:00
for (const auto &value : type.array)
2018-06-18 16:30:16 +00:00
json_stream->emit_json_array_value(value);
json_stream->end_json_array();
2018-06-03 18:16:37 +00:00
}
}
void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index)
{
auto flags = combined_decoration_for_member(type, index);
if (flags.get(DecorationRowMajor))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("row_major", true);
2018-06-03 18:16:37 +00:00
auto &membertype = get<SPIRType>(type.member_types[index]);
emit_type_array(membertype);
auto &memb = meta[type.self].members;
if (index < memb.size())
{
auto &dec = memb[index];
2018-06-18 16:30:16 +00:00
if (dec.decoration_flags.get(DecorationLocation))
json_stream->emit_json_key_value("location", dec.location);
if (dec.decoration_flags.get(DecorationOffset))
json_stream->emit_json_key_value("offset", dec.offset);
2018-06-03 18:16:37 +00:00
}
}
2018-06-20 17:25:38 +00:00
string CompilerReflection::execution_model_to_str(spv::ExecutionModel model)
2018-06-03 18:16:37 +00:00
{
switch (model)
{
case spv::ExecutionModelVertex:
2018-06-20 17:25:38 +00:00
return "vert";
2018-06-03 18:16:37 +00:00
case spv::ExecutionModelTessellationControl:
2018-06-20 17:25:38 +00:00
return "tesc";
2018-06-03 18:16:37 +00:00
case ExecutionModelTessellationEvaluation:
2018-06-20 17:25:38 +00:00
return "tese";
2018-06-03 18:16:37 +00:00
case ExecutionModelGeometry:
2018-06-20 17:25:38 +00:00
return "geom";
2018-06-03 18:16:37 +00:00
case ExecutionModelFragment:
2018-06-20 17:25:38 +00:00
return "frag";
2018-06-03 18:16:37 +00:00
case ExecutionModelGLCompute:
2018-06-20 17:25:38 +00:00
return "comp";
2018-06-03 18:16:37 +00:00
default:
return "???";
}
}
2018-06-18 16:30:16 +00:00
// FIXME include things like the local_size dimensions, geometry output vertex count, etc
2018-06-03 18:16:37 +00:00
void CompilerReflection::emit_entry_points()
{
2018-06-22 07:41:43 +00:00
auto entries = get_entry_points_and_stages();
if (!entries.empty())
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_array("entryPoints");
2018-06-22 07:41:43 +00:00
for (auto &e : entries)
2018-06-03 18:16:37 +00:00
{
2018-06-18 16:30:16 +00:00
json_stream->begin_json_object();
json_stream->emit_json_key_value("name", e.name);
json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model));
json_stream->end_json_object();
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
json_stream->end_json_array();
2018-06-03 18:16:37 +00:00
}
}
void CompilerReflection::emit_resources()
{
auto res = get_shader_resources();
emit_resources("subpass_inputs", res.subpass_inputs);
emit_resources("inputs", res.stage_inputs);
emit_resources("outputs", res.stage_outputs);
emit_resources("textures", res.sampled_images);
emit_resources("separate_images", res.separate_images);
emit_resources("separate_samplers", res.separate_samplers);
emit_resources("images", res.storage_images);
emit_resources("ssbos", res.storage_buffers);
emit_resources("ubos", res.uniform_buffers);
emit_resources("push_constants", res.push_constant_buffers);
2018-06-03 18:16:37 +00:00
emit_resources("counters", res.atomic_counters);
}
void CompilerReflection::emit_resources(const char *tag, const vector<Resource> &resources)
{
if (resources.empty())
{
return;
}
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_array(tag);
2018-06-03 18:16:37 +00:00
for (auto &res : resources)
{
auto &type = get_type(res.type_id);
auto typeflags = meta[type.self].decoration.decoration_flags;
auto &mask = get_decoration_bitset(res.id);
// If we don't have a name, use the fallback for the type instead of the variable
// for SSBOs and UBOs since those are the only meaningful names to use externally.
// Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant;
bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) ||
get_decoration_bitset(type.self).get(DecorationBufferBlock);
2018-06-18 16:30:16 +00:00
2018-06-03 18:16:37 +00:00
uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id;
2018-06-18 16:30:16 +00:00
json_stream->begin_json_object();
if (type.basetype == SPIRType::Struct)
{
2018-06-21 16:24:22 +00:00
json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id));
2018-06-18 16:30:16 +00:00
}
else
{
2018-06-20 17:25:38 +00:00
json_stream->emit_json_key_value("type", type_to_glsl(type));
2018-06-18 16:30:16 +00:00
}
json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id));
2018-06-03 18:16:37 +00:00
{
bool ssbo_block = type.storage == StorageClassStorageBuffer ||
(type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock));
if (ssbo_block)
{
2018-06-18 16:30:16 +00:00
auto buffer_flags = get_buffer_block_flags(res.id);
if (buffer_flags.get(DecorationNonReadable))
json_stream->emit_json_key_value("writeonly", true);
if (buffer_flags.get(DecorationNonWritable))
json_stream->emit_json_key_value("readonly", true);
if (buffer_flags.get(DecorationRestrict))
json_stream->emit_json_key_value("restrict", true);
if (buffer_flags.get(DecorationCoherent))
json_stream->emit_json_key_value("coherent", true);
2018-06-03 18:16:37 +00:00
}
}
emit_type_array(type);
{
bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform ||
get_storage_class(res.id) == StorageClassUniformConstant);
if (is_sized_block)
2018-06-18 16:30:16 +00:00
{
uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id)));
json_stream->emit_json_key_value("block_size", block_size);
}
2018-06-03 18:16:37 +00:00
}
if (type.storage == StorageClassPushConstant)
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("push_constant", true);
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationLocation))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation));
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationRowMajor))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("row_major", true);
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationColMajor))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("column_major", true);
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationIndex))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex));
2018-06-03 18:16:37 +00:00
if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet));
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationBinding))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding));
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationInputAttachmentIndex))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("input_attachment_index",
get_decoration(res.id, DecorationInputAttachmentIndex));
2018-06-03 18:16:37 +00:00
if (mask.get(DecorationOffset))
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset));
2018-06-03 18:16:37 +00:00
// For images, the type itself adds a layout qualifer.
// Only emit the format for storage images.
if (type.basetype == SPIRType::Image && type.image.sampled == 2)
{
2018-06-20 17:25:38 +00:00
const char *fmt = format_to_glsl(type.image.format);
2018-06-03 18:16:37 +00:00
if (fmt != nullptr)
2018-06-18 16:30:16 +00:00
json_stream->emit_json_key_value("format", std::string(fmt));
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
json_stream->end_json_object();
2018-06-03 18:16:37 +00:00
}
2018-06-18 16:30:16 +00:00
json_stream->end_json_array();
}
void CompilerReflection::emit_specialization_constants()
{
auto specialization_constants = get_specialization_constants();
if (specialization_constants.empty())
return;
json_stream->emit_json_key_array("specialization_constants");
for (const auto spec_const : specialization_constants)
{
auto &c = get<SPIRConstant>(spec_const.id);
auto type = get<SPIRType>(c.constant_type);
json_stream->begin_json_object();
json_stream->emit_json_key_value("id", spec_const.constant_id);
json_stream->emit_json_key_value("type", type_to_glsl(type));
switch (type.basetype)
{
case SPIRType::UInt:
json_stream->emit_json_key_value("default_value", c.scalar());
break;
case SPIRType::Int:
json_stream->emit_json_key_value("default_value", c.scalar_i32());
break;
case SPIRType::Float:
json_stream->emit_json_key_value("default_value", c.scalar_f32());
break;
case SPIRType::Boolean:
json_stream->emit_json_key_value("default_value", c.scalar() != 0);
break;
2018-06-22 07:41:43 +00:00
default:
break;
}
json_stream->end_json_object();
}
json_stream->end_json_array();
}
2018-06-18 16:30:16 +00:00
string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const
{
auto &memb = meta[type.self].members;
if (index < memb.size() && !memb[index].alias.empty())
return memb[index].alias;
else
return join("_m", index);
2018-06-03 18:16:37 +00:00
}