Updated spirv-cross.

This commit is contained in:
Бранимир Караџић 2020-08-28 23:15:26 -07:00
parent 885a86cf56
commit c6b37def25
8 changed files with 240 additions and 112 deletions

View File

@ -306,6 +306,8 @@ void CompilerCPP::emit_resources()
string CompilerCPP::compile() string CompilerCPP::compile()
{ {
ir.fixup_reserved_names();
// Do not deal with ES-isms like precision, older extensions and such. // Do not deal with ES-isms like precision, older extensions and such.
options.es = false; options.es = false;
options.version = 450; options.version = 450;

View File

@ -328,8 +328,9 @@ public:
size_t target_capacity = buffer_capacity; size_t target_capacity = buffer_capacity;
if (target_capacity == 0) if (target_capacity == 0)
target_capacity = 1; target_capacity = 1;
if (target_capacity < N)
target_capacity = N; // Weird parens works around macro issues on Windows if NOMINMAX is not used.
target_capacity = (std::max)(target_capacity, N);
// Need to ensure there is a POT value of target capacity which is larger than count, // Need to ensure there is a POT value of target capacity which is larger than count,
// otherwise this will overflow. // otherwise this will overflow.

View File

@ -74,6 +74,8 @@ ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
source = other.source; source = other.source;
loop_iteration_depth_hard = other.loop_iteration_depth_hard; loop_iteration_depth_hard = other.loop_iteration_depth_hard;
loop_iteration_depth_soft = other.loop_iteration_depth_soft; loop_iteration_depth_soft = other.loop_iteration_depth_soft;
meta_needing_name_fixup = std::move(other.meta_needing_name_fixup);
} }
return *this; return *this;
} }
@ -106,6 +108,8 @@ ParsedIR &ParsedIR::operator=(const ParsedIR &other)
addressing_model = other.addressing_model; addressing_model = other.addressing_model;
memory_model = other.memory_model; memory_model = other.memory_model;
meta_needing_name_fixup = other.meta_needing_name_fixup;
// Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor. // Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor.
// Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group. // Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group.
ids.clear(); ids.clear();
@ -134,42 +138,146 @@ static bool is_alpha(char c)
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
} }
static bool is_alphanumeric(char c) static bool is_numeric(char c)
{ {
return is_alpha(c) || (c >= '0' && c <= '9'); return c >= '0' && c <= '9';
} }
static string ensure_valid_identifier(const string &name, bool member) static bool is_alphanumeric(char c)
{
return is_alpha(c) || is_numeric(c);
}
static bool is_valid_identifier(const string &name)
{
if (name.empty())
return true;
if (is_numeric(name[0]))
return false;
for (auto c : name)
if (!is_alphanumeric(c) && c != '_')
return false;
bool saw_underscore = false;
// Two underscores in a row is not a valid identifier either.
// Technically reserved, but it's easier to treat it as invalid.
for (auto c : name)
{
bool is_underscore = c == '_';
if (is_underscore && saw_underscore)
return false;
saw_underscore = is_underscore;
}
return true;
}
static bool is_reserved_prefix(const string &name)
{
// Generic reserved identifiers used by the implementation.
return name.compare(0, 3, "gl_", 3) == 0 ||
// Ignore this case for now, might rewrite internal code to always use spv prefix.
//name.compare(0, 11, "SPIRV_Cross", 11) == 0 ||
name.compare(0, 3, "spv", 3) == 0;
}
static bool is_reserved_identifier(const string &name, bool member, bool allow_reserved_prefixes)
{
if (!allow_reserved_prefixes && is_reserved_prefix(name))
return true;
if (member)
{
// Reserved member identifiers come in one form:
// _m[0-9]+$.
if (name.size() < 3)
return false;
if (name.compare(0, 2, "_m", 2) != 0)
return false;
size_t index = 2;
while (index < name.size() && is_numeric(name[index]))
index++;
return index == name.size();
}
else
{
// Reserved non-member identifiers come in two forms:
// _[0-9]+$, used for temporaries which map directly to a SPIR-V ID.
// _[0-9]+_, used for auxillary temporaries which derived from a SPIR-V ID.
if (name.size() < 2)
return false;
if (name[0] != '_' || !is_numeric(name[1]))
return false;
size_t index = 2;
while (index < name.size() && is_numeric(name[index]))
index++;
return index == name.size() || (index < name.size() && name[index] == '_');
}
}
bool ParsedIR::is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes)
{
return is_reserved_identifier(str, false, allow_reserved_prefixes);
}
static string make_unreserved_identifier(const string &name)
{
if (is_reserved_prefix(name))
return "_RESERVED_IDENTIFIER_FIXUP_" + name;
else
return "_RESERVED_IDENTIFIER_FIXUP" + name;
}
void ParsedIR::sanitize_underscores(std::string &str)
{
// Compact adjacent underscores to make it valid.
auto dst = str.begin();
auto src = dst;
bool saw_underscore = false;
while (src != str.end())
{
bool is_underscore = *src == '_';
if (saw_underscore && is_underscore)
{
src++;
}
else
{
if (dst != src)
*dst = *src;
dst++;
src++;
saw_underscore = is_underscore;
}
}
str.erase(dst, str.end());
}
static string ensure_valid_identifier(const string &name)
{ {
// Functions in glslangValidator are mangled with name(<mangled> stuff. // Functions in glslangValidator are mangled with name(<mangled> stuff.
// Normally, we would never see '(' in any legal identifiers, so just strip them out. // Normally, we would never see '(' in any legal identifiers, so just strip them out.
auto str = name.substr(0, name.find('(')); auto str = name.substr(0, name.find('('));
for (uint32_t i = 0; i < str.size(); i++) if (str.empty())
{ return str;
auto &c = str[i];
if (member) if (is_numeric(str[0]))
{ str[0] = '_';
// _m<num> variables are reserved by the internal implementation,
// otherwise, make sure the name is a valid identifier. for (auto &c : str)
if (i == 0) if (!is_alphanumeric(c) && c != '_')
c = is_alpha(c) ? c : '_'; c = '_';
else if (i == 2 && str[0] == '_' && str[1] == 'm')
c = is_alpha(c) ? c : '_'; ParsedIR::sanitize_underscores(str);
else
c = is_alphanumeric(c) ? c : '_';
}
else
{
// _<num> variables are reserved by the internal implementation,
// otherwise, make sure the name is a valid identifier.
if (i == 0 || (str[0] == '_' && i == 1))
c = is_alpha(c) ? c : '_';
else
c = is_alphanumeric(c) ? c : '_';
}
}
return str; return str;
} }
@ -195,35 +303,41 @@ const string &ParsedIR::get_member_name(TypeID id, uint32_t index) const
return empty_string; return empty_string;
} }
void ParsedIR::sanitize_identifier(std::string &name, bool member, bool allow_reserved_prefixes)
{
if (!is_valid_identifier(name))
name = ensure_valid_identifier(name);
if (is_reserved_identifier(name, member, allow_reserved_prefixes))
name = make_unreserved_identifier(name);
}
void ParsedIR::fixup_reserved_names()
{
for (uint32_t id : meta_needing_name_fixup)
{
auto &m = meta[id];
sanitize_identifier(m.decoration.alias, false, false);
for (auto &memb : m.members)
sanitize_identifier(memb.alias, true, false);
}
meta_needing_name_fixup.clear();
}
void ParsedIR::set_name(ID id, const string &name) void ParsedIR::set_name(ID id, const string &name)
{ {
auto &str = meta[id].decoration.alias; auto &m = meta[id];
str.clear(); m.decoration.alias = name;
if (!is_valid_identifier(name) || is_reserved_identifier(name, false, false))
if (name.empty()) meta_needing_name_fixup.insert(id);
return;
// Reserved for temporaries.
if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
return;
str = ensure_valid_identifier(name, false);
} }
void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name) void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name)
{ {
meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1)); auto &m = meta[id];
m.members.resize(max(meta[id].members.size(), size_t(index) + 1));
auto &str = meta[id].members[index].alias; m.members[index].alias = name;
str.clear(); if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false))
if (name.empty()) meta_needing_name_fixup.insert(id);
return;
// Reserved for unnamed members.
if (name[0] == '_' && name.size() >= 3 && name[1] == 'm' && isdigit(name[2]))
return;
str = ensure_valid_identifier(name, true);
} }
void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument) void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument)

View File

@ -208,6 +208,12 @@ public:
void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set);
void fixup_reserved_names();
static void sanitize_underscores(std::string &str);
static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes);
static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes);
private: private:
template <typename T> template <typename T>
T &get(uint32_t id) T &get(uint32_t id)
@ -225,6 +231,8 @@ private:
mutable uint32_t loop_iteration_depth_soft = 0; mutable uint32_t loop_iteration_depth_soft = 0;
std::string empty_string; std::string empty_string;
Bitset cleared_bitset; Bitset cleared_bitset;
std::unordered_set<uint32_t> meta_needing_name_fixup;
}; };
} // namespace SPIRV_CROSS_NAMESPACE } // namespace SPIRV_CROSS_NAMESPACE

View File

@ -145,32 +145,6 @@ static BufferPackingStandard packing_to_substruct_packing(BufferPackingStandard
} }
} }
// Sanitizes underscores for GLSL where multiple underscores in a row are not allowed.
string CompilerGLSL::sanitize_underscores(const string &str)
{
string res;
res.reserve(str.size());
bool last_underscore = false;
for (auto c : str)
{
if (c == '_')
{
if (last_underscore)
continue;
res += c;
last_underscore = true;
}
else
{
res += c;
last_underscore = false;
}
}
return res;
}
void CompilerGLSL::init() void CompilerGLSL::init()
{ {
if (ir.source.known) if (ir.source.known)
@ -529,6 +503,8 @@ void CompilerGLSL::find_static_extensions()
string CompilerGLSL::compile() string CompilerGLSL::compile()
{ {
ir.fixup_reserved_names();
if (options.vulkan_semantics) if (options.vulkan_semantics)
backend.allow_precision_qualifiers = true; backend.allow_precision_qualifiers = true;
else else
@ -2150,7 +2126,7 @@ void CompilerGLSL::emit_flattened_io_block_member(const std::string &basename, c
// Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row, // Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row,
// which is not allowed. // which is not allowed.
flattened_name = sanitize_underscores(flattened_name); ParsedIR::sanitize_underscores(flattened_name);
uint32_t last_index = indices.back(); uint32_t last_index = indices.back();
@ -2693,6 +2669,23 @@ bool CompilerGLSL::should_force_emit_builtin_block(StorageClass storage)
return should_force; return should_force;
} }
void CompilerGLSL::fixup_implicit_builtin_block_names()
{
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
auto &type = this->get<SPIRType>(var.basetype);
bool block = has_decoration(type.self, DecorationBlock);
if ((var.storage == StorageClassOutput || var.storage == StorageClassInput) && block &&
is_builtin_variable(var))
{
// Make sure the array has a supported name in the code.
if (var.storage == StorageClassOutput)
set_name(var.self, "gl_out");
else if (var.storage == StorageClassInput)
set_name(var.self, "gl_in");
}
});
}
void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model) void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model)
{ {
Bitset emitted_builtins; Bitset emitted_builtins;
@ -2874,12 +2867,6 @@ void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionMo
if (builtin_array) if (builtin_array)
{ {
// Make sure the array has a supported name in the code.
if (storage == StorageClassOutput)
set_name(block_var->self, "gl_out");
else if (storage == StorageClassInput)
set_name(block_var->self, "gl_in");
if (model == ExecutionModelTessellationControl && storage == StorageClassOutput) if (model == ExecutionModelTessellationControl && storage == StorageClassOutput)
end_scope_decl(join(to_name(block_var->self), "[", get_entry_point().output_vertices, "]")); end_scope_decl(join(to_name(block_var->self), "[", get_entry_point().output_vertices, "]"));
else else
@ -2936,6 +2923,18 @@ void CompilerGLSL::emit_resources()
if (!pls_inputs.empty() || !pls_outputs.empty()) if (!pls_inputs.empty() || !pls_outputs.empty())
emit_pls(); emit_pls();
switch (execution.model)
{
case ExecutionModelGeometry:
case ExecutionModelTessellationControl:
case ExecutionModelTessellationEvaluation:
fixup_implicit_builtin_block_names();
break;
default:
break;
}
// Emit custom gl_PerVertex for SSO compatibility. // Emit custom gl_PerVertex for SSO compatibility.
if (options.separate_shader_objects && !options.es && execution.model != ExecutionModelFragment) if (options.separate_shader_objects && !options.es && execution.model != ExecutionModelFragment)
{ {
@ -7793,7 +7792,9 @@ void CompilerGLSL::prepare_access_chain_for_scalar_access(std::string &, const S
string CompilerGLSL::to_flattened_struct_member(const string &basename, const SPIRType &type, uint32_t index) string CompilerGLSL::to_flattened_struct_member(const string &basename, const SPIRType &type, uint32_t index)
{ {
return sanitize_underscores(join(basename, "_", to_member_name(type, index))); auto ret = join(basename, "_", to_member_name(type, index));
ParsedIR::sanitize_underscores(ret);
return ret;
} }
string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
@ -7837,7 +7838,9 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
} }
auto basename = to_flattened_access_chain_expression(base); auto basename = to_flattened_access_chain_expression(base);
return sanitize_underscores(join(basename, "_", chain)); auto ret = join(basename, "_", chain);
ParsedIR::sanitize_underscores(ret);
return ret;
} }
else else
{ {
@ -7895,7 +7898,8 @@ void CompilerGLSL::store_flattened_struct(const string &basename, uint32_t rhs_i
for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++)
{ {
sub_indices.back() = i; sub_indices.back() = i;
auto lhs = sanitize_underscores(join(basename, "_", to_member_name(*member_type, i))); auto lhs = join(basename, "_", to_member_name(*member_type, i));
ParsedIR::sanitize_underscores(lhs);
if (get<SPIRType>(member_type->member_types[i]).basetype == SPIRType::Struct) if (get<SPIRType>(member_type->member_types[i]).basetype == SPIRType::Struct)
{ {
@ -11435,13 +11439,7 @@ void CompilerGLSL::add_member_name(SPIRType &type, uint32_t index)
if (name.empty()) if (name.empty())
return; return;
// Reserved for temporaries. ParsedIR::sanitize_identifier(name, true, true);
if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
{
name.clear();
return;
}
update_name_cache(type.member_name_cache, name); update_name_cache(type.member_name_cache, name);
} }
} }
@ -12167,16 +12165,13 @@ void CompilerGLSL::add_variable(unordered_set<string> &variables_primary,
if (name.empty()) if (name.empty())
return; return;
// Reserved for temporaries. ParsedIR::sanitize_underscores(name);
if (name[0] == '_' && name.size() >= 2 && isdigit(name[1])) if (ParsedIR::is_globally_reserved_identifier(name, true))
{ {
name.clear(); name.clear();
return; return;
} }
// Avoid double underscores.
name = sanitize_underscores(name);
update_name_cache(variables_primary, variables_secondary, name); update_name_cache(variables_primary, variables_secondary, name);
} }

View File

@ -487,6 +487,7 @@ protected:
void emit_buffer_reference_block(SPIRType &type, bool forward_declaration); void emit_buffer_reference_block(SPIRType &type, bool forward_declaration);
void emit_buffer_block_legacy(const SPIRVariable &var); void emit_buffer_block_legacy(const SPIRVariable &var);
void emit_buffer_block_flattened(const SPIRVariable &type); void emit_buffer_block_flattened(const SPIRVariable &type);
void fixup_implicit_builtin_block_names();
void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model);
bool should_force_emit_builtin_block(spv::StorageClass storage); bool should_force_emit_builtin_block(spv::StorageClass storage);
void emit_push_constant_block_vulkan(const SPIRVariable &var); void emit_push_constant_block_vulkan(const SPIRVariable &var);
@ -757,8 +758,6 @@ protected:
virtual void declare_undefined_values(); virtual void declare_undefined_values();
static std::string sanitize_underscores(const std::string &str);
bool can_use_io_location(spv::StorageClass storage, bool block); bool can_use_io_location(spv::StorageClass storage, bool block);
const Instruction *get_next_instruction_in_block(const Instruction &instr); const Instruction *get_next_instruction_in_block(const Instruction &instr);
static uint32_t mask_relevant_memory_semantics(uint32_t semantics); static uint32_t mask_relevant_memory_semantics(uint32_t semantics);

View File

@ -971,7 +971,9 @@ std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClas
auto &var = get<SPIRVariable>(num_workgroups_builtin); auto &var = get<SPIRVariable>(num_workgroups_builtin);
auto &type = get<SPIRType>(var.basetype); auto &type = get<SPIRType>(var.basetype);
return sanitize_underscores(join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0))); auto ret = join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0));
ParsedIR::sanitize_underscores(ret);
return ret;
} }
case BuiltInPointCoord: case BuiltInPointCoord:
// Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set. // Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set.
@ -2076,7 +2078,9 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
add_member_name(type, i); add_member_name(type, i);
auto backup_name = get_member_name(type.self, i); auto backup_name = get_member_name(type.self, i);
auto member_name = to_member_name(type, i); auto member_name = to_member_name(type, i);
set_member_name(type.self, i, sanitize_underscores(join(to_name(var.self), "_", member_name))); member_name = join(to_name(var.self), "_", member_name);
ParsedIR::sanitize_underscores(member_name);
set_member_name(type.self, i, member_name);
emit_struct_member(type, member, i, ""); emit_struct_member(type, member, i, "");
set_member_name(type.self, i, backup_name); set_member_name(type.self, i, backup_name);
i++; i++;
@ -2157,8 +2161,9 @@ void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
add_member_name(type, constant_index); add_member_name(type, constant_index);
auto backup_name = get_member_name(type.self, i); auto backup_name = get_member_name(type.self, i);
auto member_name = to_member_name(type, i); auto member_name = to_member_name(type, i);
set_member_name(type.self, constant_index, member_name = join(to_name(var.self), "_", member_name);
sanitize_underscores(join(to_name(var.self), "_", member_name))); ParsedIR::sanitize_underscores(member_name);
set_member_name(type.self, constant_index, member_name);
emit_struct_member(type, member, i, "", layout.start); emit_struct_member(type, member, i, "", layout.start);
set_member_name(type.self, constant_index, backup_name); set_member_name(type.self, constant_index, backup_name);
@ -5590,6 +5595,8 @@ void CompilerHLSL::validate_shader_model()
string CompilerHLSL::compile() string CompilerHLSL::compile()
{ {
ir.fixup_reserved_names();
// Do not deal with ES-isms like precision, older extensions and such. // Do not deal with ES-isms like precision, older extensions and such.
options.es = false; options.es = false;
options.version = 450; options.version = 450;

View File

@ -589,7 +589,7 @@ void CompilerMSL::build_implicit_builtins()
uint_type_ptr_out.pointer = true; uint_type_ptr_out.pointer = true;
uint_type_ptr_out.parent_type = get_uint_type_id(); uint_type_ptr_out.parent_type = get_uint_type_id();
uint_type_ptr_out.storage = StorageClassOutput; uint_type_ptr_out.storage = StorageClassOutput;
auto &ptr_out_type = set<SPIRType>(offset, uint_type_ptr_out); auto &ptr_out_type = set<SPIRType>(offset, uint_type_ptr_out);
ptr_out_type.self = get_uint_type_id(); ptr_out_type.self = get_uint_type_id();
set<SPIRVariable>(var_id, offset, StorageClassOutput); set<SPIRVariable>(var_id, offset, StorageClassOutput);
@ -1028,6 +1028,8 @@ void CompilerMSL::emit_entry_point_declarations()
string CompilerMSL::compile() string CompilerMSL::compile()
{ {
ir.fixup_reserved_names();
// Do not deal with GLES-isms like precision, older extensions and such. // Do not deal with GLES-isms like precision, older extensions and such.
options.vulkan_semantics = true; options.vulkan_semantics = true;
options.es = false; options.es = false;
@ -2684,7 +2686,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &", statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &",
input_buffer_var_name, "[min(", to_expression(builtin_invocation_id_id), ".x / ", input_buffer_var_name, "[min(", to_expression(builtin_invocation_id_id), ".x / ",
get_entry_point().output_vertices, get_entry_point().output_vertices,
", spvIndirectParams[1] - 1) * spvIndirectParams[0]];"); ", spvIndirectParams[1] - 1) * spvIndirectParams[0]];");
} }
else else
{ {
@ -10555,7 +10557,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
else if (var.storage == StorageClassOutput && is_builtin_variable(var)) else if (var.storage == StorageClassOutput && is_builtin_variable(var))
{ {
if (bi_type == BuiltInSampleMask && get_execution_model() == ExecutionModelFragment && if (bi_type == BuiltInSampleMask && get_execution_model() == ExecutionModelFragment &&
msl_options.additional_fixed_sample_mask != 0xffffffff) msl_options.additional_fixed_sample_mask != 0xffffffff)
{ {
// If the additional fixed sample mask was set, we need to adjust the sample_mask // If the additional fixed sample mask was set, we need to adjust the sample_mask
// output to reflect that. If the shader outputs the sample_mask itself too, we need // output to reflect that. If the shader outputs the sample_mask itself too, we need
@ -10563,15 +10565,15 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
if (does_shader_write_sample_mask) if (does_shader_write_sample_mask)
{ {
entry_func.fixup_hooks_out.push_back([=]() { entry_func.fixup_hooks_out.push_back([=]() {
statement(to_expression(builtin_sample_mask_id), " &= ", statement(to_expression(builtin_sample_mask_id),
msl_options.additional_fixed_sample_mask, ";"); " &= ", msl_options.additional_fixed_sample_mask, ";");
}); });
} }
else else
{ {
entry_func.fixup_hooks_out.push_back([=]() { entry_func.fixup_hooks_out.push_back([=]() {
statement(to_expression(builtin_sample_mask_id), " = ", statement(to_expression(builtin_sample_mask_id), " = ",
msl_options.additional_fixed_sample_mask, ";"); msl_options.additional_fixed_sample_mask, ";");
}); });
} }
} }