Initial support for Metal Shading Language.

This commit is contained in:
Bill Hollings 2016-04-06 17:42:27 -04:00
parent 147e53aeb2
commit 103aabf5e8
8 changed files with 1706 additions and 15 deletions

View File

@ -5,13 +5,14 @@ SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader
## Features ## Features
- Convert SPIR-V to readable, usable and efficient GLSL - Convert SPIR-V to readable, usable and efficient GLSL
- Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL) [EXPERIMENTAL]
- Convert SPIR-V to debuggable C++ [EXPERIMENTAL] - Convert SPIR-V to debuggable C++ [EXPERIMENTAL]
- Reflection API to simplify the creation of Vulkan pipeline layouts - Reflection API to simplify the creation of Vulkan pipeline layouts
- Reflection API to modify and tweak OpDecorations - Reflection API to modify and tweak OpDecorations
- Supports "all" of vertex, fragment, tessellation, geometry and compute shaders. - Supports "all" of vertex, fragment, tessellation, geometry and compute shaders.
SPIRV-Cross tries hard to emit readable and clean output from the SPIR-V. SPIRV-Cross tries hard to emit readable and clean output from the SPIR-V.
The goal is to emit GLSL that looks like it was written by a human and not awkward IR/assembly-like code. The goal is to emit GLSL or MSL that looks like it was written by a human and not awkward IR/assembly-like code.
NOTE: Individual features are expected to be mostly complete, but it is possible that certain obscure GLSL features are not yet supported. NOTE: Individual features are expected to be mostly complete, but it is possible that certain obscure GLSL features are not yet supported.
However, most missing features are expected to be "trivial" improvements at this stage. However, most missing features are expected to be "trivial" improvements at this stage.

View File

@ -72,12 +72,17 @@ namespace spirv_cross
return std::to_string(std::forward<T>(t)); return std::to_string(std::forward<T>(t));
} }
// Allow implementations to set a convenient standard precision
#ifndef SPVX_FLT_FMT
# define SPVX_FLT_FMT "%.32g"
#endif
inline std::string convert_to_string(float t) inline std::string convert_to_string(float t)
{ {
// std::to_string for floating point values is broken. // std::to_string for floating point values is broken.
// Fallback to something more sane. // Fallback to something more sane.
char buf[64]; char buf[64];
sprintf(buf, "%.32g", t); sprintf(buf, SPVX_FLT_FMT, t);
// Ensure that the literal is float. // Ensure that the literal is float.
if (!strchr(buf, '.') && !strchr(buf, 'e')) if (!strchr(buf, '.') && !strchr(buf, 'e'))
strcat(buf, ".0"); strcat(buf, ".0");
@ -89,7 +94,7 @@ namespace spirv_cross
// std::to_string for floating point values is broken. // std::to_string for floating point values is broken.
// Fallback to something more sane. // Fallback to something more sane.
char buf[64]; char buf[64];
sprintf(buf, "%.32g", t); sprintf(buf, SPVX_FLT_FMT, t);
// Ensure that the literal is float. // Ensure that the literal is float.
if (!strchr(buf, '.') && !strchr(buf, 'e')) if (!strchr(buf, '.') && !strchr(buf, 'e'))
strcat(buf, ".0"); strcat(buf, ".0");
@ -144,6 +149,7 @@ namespace spirv_cross
Unknown, Unknown,
Void, Void,
Bool, Bool,
Char,
Int, Int,
UInt, UInt,
AtomicCounter, AtomicCounter,
@ -169,6 +175,8 @@ namespace spirv_cross
std::vector<uint32_t> member_types; std::vector<uint32_t> member_types;
bool is_packed = false; // Tightly packed in memory (no alignment padding)
struct Image struct Image
{ {
uint32_t type; uint32_t type;
@ -631,6 +639,7 @@ namespace spirv_cross
uint32_t offset = 0; uint32_t offset = 0;
uint32_t array_stride = 0; uint32_t array_stride = 0;
bool builtin = false; bool builtin = false;
bool per_instance = false;
}; };
Decoration decoration; Decoration decoration;

View File

@ -337,6 +337,21 @@ bool Compiler::is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *
return false; return false;
} }
bool Compiler::is_scalar(const SPIRType &type) const
{
return type.vecsize == 1 && type.columns == 1;
}
bool Compiler::is_vector(const SPIRType &type) const
{
return type.vecsize > 1 && type.columns == 1;
}
bool Compiler::is_matrix(const SPIRType &type) const
{
return type.vecsize > 1 && type.columns > 1;
}
ShaderResources Compiler::get_shader_resources() const ShaderResources Compiler::get_shader_resources() const
{ {
ShaderResources res; ShaderResources res;
@ -1814,3 +1829,15 @@ std::vector<BufferRange> Compiler::get_active_buffer_ranges(unsigned id) const
return ranges; return ranges;
} }
// Increase the number of IDs by the specified incremental amount.
// Returns the value of the first ID available for use in the expanded bound.
uint32_t Compiler::increase_bound_by(uint32_t incr_amount)
{
uint32_t curr_bound = ids.size();
uint32_t new_bound = curr_bound + incr_amount;
ids.resize(new_bound);
meta.resize(new_bound);
return curr_bound;
}

View File

@ -157,6 +157,9 @@ namespace spirv_cross
// Returns the effective size of a buffer block. // Returns the effective size of a buffer block.
size_t get_declared_struct_size(const SPIRType &struct_type) const; size_t get_declared_struct_size(const SPIRType &struct_type) const;
// Returns the effective size of a buffer block struct member.
virtual size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const;
// Legacy GLSL compatibility method. // Legacy GLSL compatibility method.
// Takes a variable with a block interface and flattens it into a T array[N]; array instead. // Takes a variable with a block interface and flattens it into a T array[N]; array instead.
// For this to work, all types in the block must not themselves be composites // For this to work, all types in the block must not themselves be composites
@ -255,6 +258,9 @@ namespace spirv_cross
bool is_builtin_variable(const SPIRVariable &var) const; bool is_builtin_variable(const SPIRVariable &var) const;
bool is_immutable(uint32_t id) const; bool is_immutable(uint32_t id) const;
bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const; bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const;
bool is_scalar(const SPIRType &type) const;
bool is_vector(const SPIRType &type) const;
bool is_matrix(const SPIRType &type) const;
const SPIRType& expression_type(uint32_t id) const; const SPIRType& expression_type(uint32_t id) const;
bool expression_is_lvalue(uint32_t id) const; bool expression_is_lvalue(uint32_t id) const;
bool variable_storage_is_aliased(const SPIRVariable &var); bool variable_storage_is_aliased(const SPIRVariable &var);
@ -307,6 +313,8 @@ namespace spirv_cross
bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const;
uint32_t increase_bound_by(uint32_t incr_amount);
private: private:
void parse(); void parse();
void parse(const Instruction &i); void parse(const Instruction &i);
@ -337,8 +345,6 @@ namespace spirv_cross
bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const;
size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const;
}; };
} }

View File

@ -1866,7 +1866,7 @@ string CompilerGLSL::bitcast_glsl(uint32_t result_type, uint32_t argument)
return join(op, "(", to_expression(argument), ")"); return join(op, "(", to_expression(argument), ")");
} }
const char* CompilerGLSL::builtin_to_glsl(BuiltIn builtin) string CompilerGLSL::builtin_to_glsl(BuiltIn builtin)
{ {
switch (builtin) switch (builtin)
{ {

View File

@ -110,6 +110,14 @@ namespace spirv_cross
// Virtualize methods which need to be overridden by subclass targets like C++ and such. // Virtualize methods which need to be overridden by subclass targets like C++ and such.
virtual void emit_function_prototype(SPIRFunction &func, uint64_t return_flags); virtual void emit_function_prototype(SPIRFunction &func, uint64_t return_flags);
virtual void emit_header(); virtual void emit_header();
virtual void emit_texture_op(const Instruction &i);
virtual std::string type_to_glsl(const SPIRType &type);
virtual std::string builtin_to_glsl(spv::BuiltIn builtin);
virtual std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member);
virtual std::string image_type_glsl(const SPIRType &type);
virtual std::string constant_expression(const SPIRConstant &c);
virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
virtual void emit_fixup();
std::unique_ptr<std::ostringstream> buffer; std::unique_ptr<std::ostringstream> buffer;
@ -165,7 +173,6 @@ namespace spirv_cross
Options options; Options options;
std::string type_to_glsl(const SPIRType &type);
std::string type_to_array_glsl(const SPIRType &type); std::string type_to_array_glsl(const SPIRType &type);
std::string variable_decl(const SPIRVariable &variable); std::string variable_decl(const SPIRVariable &variable);
@ -189,7 +196,7 @@ namespace spirv_cross
void emit_struct(const SPIRType &type); void emit_struct(const SPIRType &type);
void emit_instruction(const Instruction &instr); void emit_instruction(const Instruction &instr);
private: protected:
void emit_resources(); void emit_resources();
void emit_buffer_block(const SPIRVariable &type); void emit_buffer_block(const SPIRVariable &type);
@ -209,7 +216,6 @@ namespace spirv_cross
void flush_undeclared_variables(); void flush_undeclared_variables();
bool should_forward(uint32_t id); bool should_forward(uint32_t id);
void emit_texture_op(const Instruction &i);
void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp); void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp);
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, uint32_t count); void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, uint32_t count);
void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, uint32_t op3, const char *op); void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, uint32_t op3, const char *op);
@ -229,13 +235,9 @@ namespace spirv_cross
std::string to_member_name(const SPIRType &type, uint32_t index); std::string to_member_name(const SPIRType &type, uint32_t index);
std::string type_to_glsl_constructor(const SPIRType &type); std::string type_to_glsl_constructor(const SPIRType &type);
std::string argument_decl(const SPIRFunction::Parameter &arg); std::string argument_decl(const SPIRFunction::Parameter &arg);
std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member);
std::string image_type_glsl(const SPIRType &type);
std::string to_qualifiers_glsl(uint32_t id); std::string to_qualifiers_glsl(uint32_t id);
const char* to_precision_qualifiers_glsl(uint32_t id); const char* to_precision_qualifiers_glsl(uint32_t id);
const char* flags_to_precision_qualifiers_glsl(const SPIRType &type, uint64_t flags); const char* flags_to_precision_qualifiers_glsl(const SPIRType &type, uint64_t flags);
std::string constant_expression(const SPIRConstant &c);
std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
const char* format_to_glsl(spv::ImageFormat format); const char* format_to_glsl(spv::ImageFormat format);
std::string layout_for_member(const SPIRType &type, uint32_t index); std::string layout_for_member(const SPIRType &type, uint32_t index);
uint64_t combined_decoration_for_member(const SPIRType &type, uint32_t index); uint64_t combined_decoration_for_member(const SPIRType &type, uint32_t index);
@ -248,7 +250,6 @@ namespace spirv_cross
std::string bitcast_glsl(uint32_t result_type, uint32_t arg); std::string bitcast_glsl(uint32_t result_type, uint32_t arg);
std::string bitcast_glsl_op(uint32_t result_type, uint32_t arg); std::string bitcast_glsl_op(uint32_t result_type, uint32_t arg);
const char* builtin_to_glsl(spv::BuiltIn builtin);
std::string build_composite_combiner(const uint32_t *elems, uint32_t length); std::string build_composite_combiner(const uint32_t *elems, uint32_t length);
bool remove_duplicate_swizzle(std::string &op); bool remove_duplicate_swizzle(std::string &op);
bool remove_unity_swizzle(uint32_t base, std::string &op); bool remove_unity_swizzle(uint32_t base, std::string &op);
@ -263,7 +264,6 @@ namespace spirv_cross
std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype); std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype);
uint32_t indent = 0; uint32_t indent = 0;
void emit_fixup();
std::unordered_set<uint32_t> emitted_functions; std::unordered_set<uint32_t> emitted_functions;

1487
spirv_msl.cpp Normal file

File diff suppressed because it is too large Load Diff

161
spirv_msl.hpp Normal file
View File

@ -0,0 +1,161 @@
/*
* Copyright 2015-2016 The Brenwill Workshop Ltd.
*
* 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 SPIRV_MSL_HPP
#define SPIRV_MSL_HPP
#include "spirv_glsl.hpp"
#include <vector>
#include <set>
// Hash implementation to allow spv::BuiltIn to be used as a key in an unordered_map
namespace std {
template <>
struct hash<spv::BuiltIn>
{
std::size_t operator()(const spv::BuiltIn& k) const
{
return k;
}
};
}
namespace spirv_cross {
// Options for compiling to Metal Shading Language
struct MSLOptions
{
uint32_t vtx_attr_stage_in_binding= 0;
bool flip_vert_y = true;
bool flip_frag_y =true;
bool is_rendering_points = false;
};
// Defines characteristics of vertex attributes at a particular location
struct MSLVertexAttr
{
uint32_t location = 0;
uint32_t buffer = 0;
uint32_t offset = 0;
uint32_t stride = 0;
bool per_instance = false;
};
// Specifies the binding index of a Metal resource for a binding within a descriptor set.
// Taken together, the stage, desc_set and binding combine to form a reference to a resource
// descriptor used in a particular shading stage. Generally, only one of the buffer, texture,
// or sampler elements will be populated.
struct MSLResourceBinding
{
spv::ExecutionModel stage;
uint32_t desc_set = 0;
uint32_t binding = 0;
uint32_t buffer = 0;
uint32_t texture = 0;
uint32_t sampler = 0;
};
// Special constant used in a MSLResourceBinding desc_set
// element to indicate the bindings for the push constants.
static const uint32_t kPushConstDescSet = UINT32_MAX;
// Special constant used in a MSLResourceBinding binding
// element to indicate the bindings for the push constants.
static const uint32_t kPushConstBinding = 0;
// Decompiles SPIR-V to Metal Shading Language
class CompilerMSL : public CompilerGLSL
{
public:
CompilerMSL(std::vector<uint32_t> spirv,
MSLOptions* p_msl_options = nullptr,
std::vector<MSLVertexAttr>* p_vtx_attrs = nullptr,
std::vector<MSLResourceBinding>* p_res_bindings = nullptr);
std::string compile() override;
protected:
void emit_header() override;
void emit_function_prototype(SPIRFunction &func, uint64_t return_flags) 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;
std::string builtin_to_glsl(spv::BuiltIn builtin) override;
std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member) override;
std::string constant_expression(const SPIRConstant &c) override;
size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override;
void post_parse();
void extract_builtins();
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);
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();
std::string func_type_decl(SPIRType& type);
std::string clean_func_name(std::string func_name);
std::string entry_point_args(bool append_comma);
std::string get_entry_point_name();
std::string builtin_qualifier(spv::BuiltIn builtin);
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, 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;
MSLOptions msl_options;
std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
std::vector<MSLResourceBinding> resource_bindings;
std::unordered_map<spv::BuiltIn, uint32_t> builtin_vars;
MSLResourceBinding next_metal_resource_index;
std::unordered_map<uint32_t, uint32_t> pad_type_ids_by_pad_len;
std::string stage_in_var_name = "in";
std::vector<uint32_t> stage_in_var_ids;
std::string stage_out_var_name = "out";
uint32_t stage_out_var_id = 0;
std::string sampler_name_suffix = "Smplr";
std::string qual_pos_var_name;
};
// Sorts the members of a SPIRType and associated Meta info based on the location
// and builtin decorations of the members. Members are rearranged by location,
// with all builtin members appearing a the end.
struct MemberSorterByLocation
{
void sort();
bool operator() (uint32_t mbr_idx1,uint32_t mbr_idx2);
MemberSorterByLocation(SPIRType& type, Meta& meta) : type(type), meta(meta) {}
SPIRType& type;
Meta& meta;
};
}
#endif