2040 lines
54 KiB
C++
2040 lines
54 KiB
C++
/*
|
|
* Copyright © 2014 Unity Technologies
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "ir_print_metal_visitor.h"
|
|
#include "ir_visitor.h"
|
|
#include "glsl_types.h"
|
|
#include "glsl_parser_extras.h"
|
|
#include "ir_unused_structs.h"
|
|
#include "loop_analysis.h"
|
|
#include "program/hash_table.h"
|
|
#include <math.h>
|
|
|
|
|
|
static void print_type(string_buffer& buffer, ir_instruction* ir, const glsl_type *t, bool arraySize);
|
|
static void print_type_post(string_buffer& buffer, const glsl_type *t, bool arraySize);
|
|
|
|
|
|
struct ga_entry_metal : public exec_node
|
|
{
|
|
ga_entry_metal(ir_instruction* ir)
|
|
{
|
|
assert(ir);
|
|
this->ir = ir;
|
|
}
|
|
ir_instruction* ir;
|
|
};
|
|
struct gconst_entry_metal : public exec_node
|
|
{
|
|
gconst_entry_metal(ir_constant* ir, unsigned id)
|
|
{
|
|
assert(ir);
|
|
this->ir = ir;
|
|
this->id = id;
|
|
}
|
|
ir_constant* ir;
|
|
unsigned id;
|
|
};
|
|
|
|
|
|
struct global_print_tracker_metal
|
|
{
|
|
global_print_tracker_metal ()
|
|
{
|
|
mem_ctx = ralloc_context(0);
|
|
var_counter = 0;
|
|
var_hash = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
|
|
const_counter = 0;
|
|
const_hash = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
|
|
main_function_done = false;
|
|
}
|
|
|
|
~global_print_tracker_metal()
|
|
{
|
|
hash_table_dtor (var_hash);
|
|
hash_table_dtor (const_hash);
|
|
ralloc_free(mem_ctx);
|
|
}
|
|
|
|
unsigned var_counter;
|
|
hash_table* var_hash;
|
|
exec_list global_assignements;
|
|
|
|
unsigned const_counter;
|
|
hash_table* const_hash;
|
|
exec_list global_constants;
|
|
|
|
void* mem_ctx;
|
|
bool main_function_done;
|
|
};
|
|
|
|
|
|
struct metal_print_context
|
|
{
|
|
metal_print_context(char* buffer)
|
|
: str(buffer)
|
|
, prefixStr(ralloc_strdup(buffer, ""))
|
|
, inputStr(ralloc_strdup(buffer, ""))
|
|
, outputStr(ralloc_strdup(buffer, ""))
|
|
, inoutStr(ralloc_strdup(buffer, ""))
|
|
, uniformStr(ralloc_strdup(buffer, ""))
|
|
, paramsStr(ralloc_strdup(buffer, ""))
|
|
, typedeclStr(ralloc_strdup(buffer, ""))
|
|
, writingParams(false)
|
|
, matrixCastsDone(false)
|
|
, matrixConstructorsDone(false)
|
|
, shadowSamplerDone(false)
|
|
, textureCounter(0)
|
|
, attributeCounter(0)
|
|
, uniformLocationCounter(0)
|
|
, colorCounter(0)
|
|
{
|
|
}
|
|
|
|
string_buffer str;
|
|
string_buffer prefixStr;
|
|
string_buffer inputStr;
|
|
string_buffer outputStr;
|
|
string_buffer inoutStr;
|
|
string_buffer uniformStr;
|
|
string_buffer paramsStr;
|
|
string_buffer typedeclStr;
|
|
bool writingParams;
|
|
bool matrixCastsDone;
|
|
bool matrixConstructorsDone;
|
|
bool shadowSamplerDone;
|
|
int textureCounter;
|
|
int attributeCounter;
|
|
int uniformLocationCounter;
|
|
int colorCounter;
|
|
};
|
|
|
|
|
|
class ir_print_metal_visitor : public ir_visitor {
|
|
public:
|
|
ir_print_metal_visitor(metal_print_context& ctx_, string_buffer& buf, global_print_tracker_metal* globals_, PrintGlslMode mode_, const _mesa_glsl_parse_state* state_)
|
|
: ctx(ctx_)
|
|
, buffer(buf)
|
|
, loopstate(NULL)
|
|
, inside_loop_body(false)
|
|
, inside_lhs(false)
|
|
, skipped_this_ir(false)
|
|
, previous_skipped(false)
|
|
, mode_whole(mode_)
|
|
{
|
|
indentation = 0;
|
|
expression_depth = 0;
|
|
globals = globals_;
|
|
mode = mode_;
|
|
state = state_;
|
|
}
|
|
|
|
virtual ~ir_print_metal_visitor()
|
|
{
|
|
}
|
|
|
|
|
|
void indent(void);
|
|
void newline_indent();
|
|
void end_statement_line();
|
|
void newline_deindent();
|
|
void print_var_name (ir_variable* v);
|
|
|
|
virtual void visit(ir_variable *);
|
|
virtual void visit(ir_function_signature *);
|
|
virtual void visit(ir_function *);
|
|
virtual void visit(ir_expression *);
|
|
virtual void visit(ir_texture *);
|
|
virtual void visit(ir_swizzle *);
|
|
virtual void visit(ir_dereference_variable *);
|
|
virtual void visit(ir_dereference_array *);
|
|
virtual void visit(ir_dereference_record *);
|
|
virtual void visit(ir_assignment *);
|
|
virtual void visit(ir_constant *);
|
|
virtual void visit(ir_call *);
|
|
virtual void visit(ir_return *);
|
|
virtual void visit(ir_discard *);
|
|
virtual void visit(ir_if *);
|
|
virtual void visit(ir_loop *);
|
|
virtual void visit(ir_loop_jump *);
|
|
virtual void visit(ir_precision_statement *);
|
|
virtual void visit(ir_typedecl_statement *);
|
|
virtual void visit(ir_emit_vertex *);
|
|
virtual void visit(ir_end_primitive *);
|
|
|
|
void emit_assignment_part (ir_dereference* lhs, ir_rvalue* rhs, unsigned write_mask, ir_rvalue* dstIndex);
|
|
bool can_emit_canonical_for (loop_variable_state *ls);
|
|
bool emit_canonical_for (ir_loop* ir);
|
|
|
|
metal_print_context& ctx;
|
|
int indentation;
|
|
int expression_depth;
|
|
string_buffer& buffer;
|
|
global_print_tracker_metal* globals;
|
|
const _mesa_glsl_parse_state* state;
|
|
PrintGlslMode mode;
|
|
const PrintGlslMode mode_whole;
|
|
loop_state* loopstate;
|
|
bool inside_loop_body;
|
|
bool inside_lhs;
|
|
bool skipped_this_ir;
|
|
bool previous_skipped;
|
|
};
|
|
|
|
|
|
char*
|
|
_mesa_print_ir_metal(exec_list *instructions,
|
|
struct _mesa_glsl_parse_state *state,
|
|
char* buffer, PrintGlslMode mode, int* outUniformsSize)
|
|
{
|
|
metal_print_context ctx(buffer);
|
|
|
|
// includes, prefix etc.
|
|
ctx.prefixStr.asprintf_append ("#include <metal_stdlib>\n");
|
|
ctx.prefixStr.asprintf_append ("#pragma clang diagnostic ignored \"-Wparentheses-equality\"\n");
|
|
ctx.prefixStr.asprintf_append ("using namespace metal;\n");
|
|
|
|
ctx.inputStr.asprintf_append("struct xlatMtlShaderInput {\n");
|
|
ctx.outputStr.asprintf_append("struct xlatMtlShaderOutput {\n");
|
|
ctx.uniformStr.asprintf_append("struct xlatMtlShaderUniform {\n");
|
|
|
|
// remove unused struct declarations
|
|
do_remove_unused_typedecls(instructions);
|
|
|
|
global_print_tracker_metal gtracker;
|
|
|
|
loop_state* ls = analyze_loop_variables(instructions);
|
|
if (ls->loop_found)
|
|
set_loop_controls(instructions, ls);
|
|
|
|
foreach_in_list(ir_instruction, ir, instructions)
|
|
{
|
|
string_buffer* strOut = &ctx.str;
|
|
ctx.writingParams = false;
|
|
if (ir->ir_type == ir_type_variable)
|
|
{
|
|
ir_variable *var = static_cast<ir_variable*>(ir);
|
|
|
|
// skip gl_ variables if they aren't used/assigned
|
|
if (strstr(var->name, "gl_") == var->name)
|
|
{
|
|
if (NULL == strstr(var->name, "gl_FragData_") ) {
|
|
if (!var->data.used && !var->data.assigned)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (var->data.mode == ir_var_uniform)
|
|
{
|
|
if (var->type->is_sampler())
|
|
{
|
|
strOut = &ctx.paramsStr;
|
|
ctx.writingParams = true;
|
|
strOut->asprintf_append ("\n , ");
|
|
}
|
|
else
|
|
strOut = &ctx.uniformStr;
|
|
}
|
|
if (var->data.mode == ir_var_system_value)
|
|
{
|
|
strOut = &ctx.paramsStr;
|
|
ctx.writingParams = true;
|
|
strOut->asprintf_append ("\n , ");
|
|
}
|
|
if (var->data.mode == ir_var_shader_in)
|
|
strOut = &ctx.inputStr;
|
|
if (var->data.mode == ir_var_shader_out)
|
|
strOut = &ctx.outputStr;
|
|
if (var->data.mode == ir_var_shader_inout)
|
|
strOut = &ctx.inoutStr;
|
|
}
|
|
|
|
if (ir->ir_type == ir_type_typedecl) {
|
|
strOut = &ctx.typedeclStr;
|
|
}
|
|
|
|
ir_print_metal_visitor v (ctx, *strOut, >racker, mode, state);
|
|
v.loopstate = ls;
|
|
|
|
ir->accept(&v);
|
|
if (ir->ir_type != ir_type_function && !v.skipped_this_ir)
|
|
{
|
|
if (!ctx.writingParams)
|
|
strOut->asprintf_append (";\n");
|
|
}
|
|
}
|
|
|
|
delete ls;
|
|
|
|
// append inout variables to both input & output structs
|
|
if (!ctx.inoutStr.empty())
|
|
{
|
|
ctx.inputStr.asprintf_append("%s", ctx.inoutStr.c_str());
|
|
ctx.outputStr.asprintf_append("%s", ctx.inoutStr.c_str());
|
|
}
|
|
ctx.inputStr.asprintf_append("};\n");
|
|
ctx.outputStr.asprintf_append("};\n");
|
|
ctx.uniformStr.asprintf_append("};\n");
|
|
|
|
// emit global array/struct constants
|
|
|
|
ctx.prefixStr.asprintf_append("%s", ctx.typedeclStr.c_str());
|
|
foreach_in_list_safe(gconst_entry_metal, node, >racker.global_constants)
|
|
{
|
|
ir_constant* c = node->ir;
|
|
|
|
ir_print_metal_visitor v (ctx, ctx.prefixStr, >racker, mode, state);
|
|
|
|
v.buffer.asprintf_append ("constant ");
|
|
print_type(v.buffer, c, c->type, false);
|
|
v.buffer.asprintf_append (" _xlat_mtl_const%i", (int)((gconst_entry_metal*)node)->id);
|
|
print_type_post(v.buffer, c->type, false);
|
|
v.buffer.asprintf_append (" = {");
|
|
|
|
if (c->type->is_array())
|
|
{
|
|
for (unsigned i = 0; i < c->type->length; i++)
|
|
{
|
|
if (i != 0)
|
|
v.buffer.asprintf_append (", ");
|
|
c->get_array_element(i)->accept(&v);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(c->type->is_record());
|
|
bool first = true;
|
|
foreach_in_list(ir_constant, inst, &c->components)
|
|
{
|
|
if (!first)
|
|
v.buffer.asprintf_append (", ");
|
|
first = false;
|
|
inst->accept(&v);
|
|
}
|
|
}
|
|
v.buffer.asprintf_append ("};\n");
|
|
}
|
|
|
|
|
|
ctx.prefixStr.asprintf_append("%s", ctx.inputStr.c_str());
|
|
ctx.prefixStr.asprintf_append("%s", ctx.outputStr.c_str());
|
|
ctx.prefixStr.asprintf_append("%s", ctx.uniformStr.c_str());
|
|
ctx.prefixStr.asprintf_append("%s", ctx.str.c_str());
|
|
|
|
*outUniformsSize = ctx.uniformLocationCounter;
|
|
|
|
return ralloc_strdup(buffer, ctx.prefixStr.c_str());
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::indent(void)
|
|
{
|
|
if (previous_skipped)
|
|
return;
|
|
previous_skipped = false;
|
|
for (int i = 0; i < indentation; i++)
|
|
buffer.asprintf_append (" ");
|
|
}
|
|
|
|
void ir_print_metal_visitor::end_statement_line()
|
|
{
|
|
if (!skipped_this_ir)
|
|
buffer.asprintf_append(";\n");
|
|
previous_skipped = skipped_this_ir;
|
|
skipped_this_ir = false;
|
|
}
|
|
|
|
void ir_print_metal_visitor::newline_indent()
|
|
{
|
|
if (expression_depth % 4 == 0)
|
|
{
|
|
++indentation;
|
|
buffer.asprintf_append ("\n");
|
|
indent();
|
|
}
|
|
}
|
|
void ir_print_metal_visitor::newline_deindent()
|
|
{
|
|
if (expression_depth % 4 == 0)
|
|
{
|
|
--indentation;
|
|
buffer.asprintf_append ("\n");
|
|
indent();
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::print_var_name (ir_variable* v)
|
|
{
|
|
uintptr_t id = (uintptr_t)hash_table_find (globals->var_hash, v);
|
|
if (!id && v->data.mode == ir_var_temporary)
|
|
{
|
|
id = ++globals->var_counter;
|
|
hash_table_insert (globals->var_hash, (void*)id, v);
|
|
}
|
|
if (id)
|
|
{
|
|
if (v->data.mode == ir_var_temporary)
|
|
buffer.asprintf_append ("tmpvar_%d", (int)id);
|
|
else
|
|
buffer.asprintf_append ("%s_%d", v->name, (int)id);
|
|
}
|
|
else
|
|
{
|
|
buffer.asprintf_append ("%s", v->name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void print_type_precision(string_buffer& buffer, const glsl_type *t, glsl_precision prec, bool arraySize)
|
|
{
|
|
const bool halfPrec = (prec == glsl_precision_medium || prec == glsl_precision_low);
|
|
|
|
const char* typeName = t->name;
|
|
// scalars
|
|
if (!strcmp(typeName, "float"))
|
|
typeName = halfPrec ? "half" : "float";
|
|
else if (!strcmp(typeName, "int"))
|
|
typeName = halfPrec ? "short" : "int";
|
|
// vectors
|
|
else if (!strcmp(typeName, "vec2"))
|
|
typeName = halfPrec ? "half2" : "float2";
|
|
else if (!strcmp(typeName, "vec3"))
|
|
typeName = halfPrec ? "half3" : "float3";
|
|
else if (!strcmp(typeName, "vec4"))
|
|
typeName = halfPrec ? "half4" : "float4";
|
|
else if (!strcmp(typeName, "ivec2"))
|
|
typeName = halfPrec ? "short2" : "int2";
|
|
else if (!strcmp(typeName, "ivec3"))
|
|
typeName = halfPrec ? "short3" : "int3";
|
|
else if (!strcmp(typeName, "ivec4"))
|
|
typeName = halfPrec ? "short4" : "int4";
|
|
else if (!strcmp(typeName, "bvec2"))
|
|
typeName = "bool2";
|
|
else if (!strcmp(typeName, "bvec3"))
|
|
typeName = "bool3";
|
|
else if (!strcmp(typeName, "bvec4"))
|
|
typeName = "bool4";
|
|
// matrices
|
|
else if (!strcmp(typeName, "mat2"))
|
|
typeName = halfPrec ? "half2x2" : "float2x2";
|
|
else if (!strcmp(typeName, "mat3"))
|
|
typeName = halfPrec ? "half3x3" : "float3x3";
|
|
else if (!strcmp(typeName, "mat4"))
|
|
typeName = halfPrec ? "half4x4" : "float4x4";
|
|
// non-square matrices
|
|
else if (!strcmp(typeName, "mat2x2"))
|
|
typeName = halfPrec ? "half2x2" : "float2x2";
|
|
else if (!strcmp(typeName, "mat2x3"))
|
|
typeName = halfPrec ? "half2x3" : "float2x3";
|
|
else if (!strcmp(typeName, "mat2x4"))
|
|
typeName = halfPrec ? "half2x4" : "float2x4";
|
|
else if (!strcmp(typeName, "mat3x2"))
|
|
typeName = halfPrec ? "half3x2" : "float3x2";
|
|
else if (!strcmp(typeName, "mat3x3"))
|
|
typeName = halfPrec ? "half3x3" : "float3x3";
|
|
else if (!strcmp(typeName, "mat3x4"))
|
|
typeName = halfPrec ? "half3x4" : "float3x4";
|
|
else if (!strcmp(typeName, "mat4x2"))
|
|
typeName = halfPrec ? "half4x2" : "float4x2";
|
|
else if (!strcmp(typeName, "mat4x3"))
|
|
typeName = halfPrec ? "half4x3" : "float4x3";
|
|
else if (!strcmp(typeName, "mat4x4"))
|
|
typeName = halfPrec ? "half4x4" : "float4x4";
|
|
// samplers
|
|
else if (!strcmp(typeName, "sampler2D"))
|
|
typeName = halfPrec ? "texture2d<half>" : "texture2d<float>";
|
|
else if (!strcmp(typeName, "samplerCube"))
|
|
typeName = halfPrec ? "texturecube<half>" : "texturecube<float>";
|
|
else if (!strcmp(typeName, "sampler3D"))
|
|
typeName = halfPrec ? "texture3d<half>" : "texture3d<float>";
|
|
else if (!strcmp(typeName, "sampler2DShadow"))
|
|
typeName = "depth2d<float>"; // depth type must always be float
|
|
else if (!strcmp(typeName, "samplerCubeShadow"))
|
|
typeName = "depthcube<float>"; // depth type must always be float
|
|
else if (!strcmp(typeName, "sampler2DArray"))
|
|
typeName = halfPrec ? "texture2d_array<half>" : "texture2d_array<float>";
|
|
|
|
if (t->base_type == GLSL_TYPE_ARRAY) {
|
|
print_type_precision(buffer, t->fields.array, prec, true);
|
|
if (arraySize)
|
|
buffer.asprintf_append ("[%u]", t->length);
|
|
} else if ((t->base_type == GLSL_TYPE_STRUCT)
|
|
&& (strncmp("gl_", typeName, 3) != 0)) {
|
|
buffer.asprintf_append ("%s", typeName);
|
|
} else {
|
|
buffer.asprintf_append ("%s", typeName);
|
|
}
|
|
}
|
|
|
|
|
|
static void print_type(string_buffer& buffer, ir_instruction* ir, const glsl_type *t, bool arraySize)
|
|
{
|
|
glsl_precision prec = precision_from_ir(ir);
|
|
if (prec == glsl_precision_low)
|
|
prec = glsl_precision_medium; // Metal does not have low precision; treat as medium
|
|
print_type_precision(buffer, t, prec, arraySize);
|
|
}
|
|
|
|
|
|
static void print_type_post(string_buffer& buffer, const glsl_type *t, bool arraySize)
|
|
{
|
|
if (t->base_type == GLSL_TYPE_ARRAY) {
|
|
if (!arraySize)
|
|
buffer.asprintf_append ("[%u]", t->length);
|
|
}
|
|
}
|
|
|
|
static void get_metal_type_size(const glsl_type* type, glsl_precision prec, int& size, int& alignment)
|
|
{
|
|
if (prec == glsl_precision_undefined)
|
|
prec = glsl_precision_high;
|
|
if (prec == glsl_precision_low)
|
|
prec = glsl_precision_medium;
|
|
const bool half = (prec == glsl_precision_medium);
|
|
|
|
const int asize = type->is_array() ? type->length : 1;
|
|
if (type->is_array())
|
|
type = type->element_type();
|
|
|
|
if (type->base_type == GLSL_TYPE_UINT || type->base_type == GLSL_TYPE_INT || type->base_type == GLSL_TYPE_FLOAT)
|
|
{
|
|
size = half ? 2 : 4;
|
|
}
|
|
else if (type->base_type == GLSL_TYPE_BOOL)
|
|
{
|
|
size = 1;
|
|
}
|
|
else
|
|
{
|
|
size = 0;
|
|
}
|
|
alignment = MAX2(size,1);
|
|
|
|
int vsize = type->vector_elements;
|
|
// float3 etc in Metal has both sizeof and alignment same as float4
|
|
if (vsize == 3)
|
|
vsize = 4;
|
|
|
|
size *= vsize;
|
|
alignment *= vsize;
|
|
|
|
const int msize = type->matrix_columns;
|
|
size *= msize;
|
|
|
|
size *= asize;
|
|
}
|
|
|
|
void ir_print_metal_visitor::visit(ir_variable *ir)
|
|
{
|
|
const char *const cent = (ir->data.centroid) ? "centroid " : "";
|
|
const char *const inv = (ir->data.invariant) ? "invariant " : "";
|
|
const char *const mode[ir_var_mode_count] = { "", " ", " ", " ", " ", "in ", "out ", "inout ", "", "", "" };
|
|
|
|
const char *const interp[] = { "", "smooth ", "flat ", "noperspective " };
|
|
|
|
// give an id to any variable defined in a function that is not an uniform
|
|
if ((this->mode == kPrintGlslNone && ir->data.mode != ir_var_uniform))
|
|
{
|
|
uintptr_t id = (uintptr_t)hash_table_find (globals->var_hash, ir);
|
|
if (id == 0)
|
|
{
|
|
id = ++globals->var_counter;
|
|
hash_table_insert (globals->var_hash, (void*)id, ir);
|
|
}
|
|
}
|
|
|
|
// auto/temp variables in global scope are postponed to main function
|
|
if (this->mode != kPrintGlslNone && (ir->data.mode == ir_var_auto || ir->data.mode == ir_var_temporary))
|
|
{
|
|
assert (!this->globals->main_function_done);
|
|
this->globals->global_assignements.push_tail (new(this->globals->mem_ctx) ga_entry_metal(ir));
|
|
skipped_this_ir = true;
|
|
return;
|
|
}
|
|
|
|
// if this is a loop induction variable, do not print it
|
|
// (will be printed inside loop body)
|
|
if (!inside_loop_body)
|
|
{
|
|
loop_variable_state* inductor_state = loopstate->get_for_inductor(ir);
|
|
if (inductor_state && inductor_state->private_induction_variable_count == 1 &&
|
|
can_emit_canonical_for(inductor_state))
|
|
{
|
|
skipped_this_ir = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
buffer.asprintf_append ("%s%s%s%s",
|
|
cent, inv, interp[ir->data.interpolation], mode[ir->data.mode]);
|
|
print_type(buffer, ir, ir->type, false);
|
|
buffer.asprintf_append (" ");
|
|
print_var_name (ir);
|
|
print_type_post(buffer, ir->type, false);
|
|
|
|
// special built-in variables
|
|
if (!strcmp(ir->name, "gl_FragDepth"))
|
|
buffer.asprintf_append (" [[depth(any)]]");
|
|
else if (!strcmp(ir->name, "gl_FragCoord"))
|
|
buffer.asprintf_append (" [[position]]");
|
|
else if (!strcmp(ir->name, "gl_FrontFacing"))
|
|
buffer.asprintf_append (" [[front_facing]]");
|
|
else if (!strcmp(ir->name, "gl_PointCoord"))
|
|
buffer.asprintf_append (" [[point_coord]]");
|
|
else if (!strcmp(ir->name, "gl_PointSize"))
|
|
buffer.asprintf_append (" [[point_size]]");
|
|
else if (!strcmp(ir->name, "gl_Position"))
|
|
buffer.asprintf_append (" [[position]]");
|
|
else if (!strcmp(ir->name, "gl_VertexID"))
|
|
buffer.asprintf_append (" [[vertex_id]]");
|
|
else if (!strcmp(ir->name, "gl_InstanceID"))
|
|
buffer.asprintf_append (" [[instance_id]]");
|
|
|
|
// vertex shader input attribute?
|
|
if (this->mode_whole == kPrintGlslVertex && ir->data.mode == ir_var_shader_in)
|
|
{
|
|
buffer.asprintf_append (" [[attribute(%i)]]", ctx.attributeCounter);
|
|
ir->data.explicit_location = 1;
|
|
ir->data.location = ctx.attributeCounter;
|
|
++ctx.attributeCounter;
|
|
}
|
|
|
|
// fragment shader output?
|
|
if (this->mode_whole == kPrintGlslFragment && (ir->data.mode == ir_var_shader_out || ir->data.mode == ir_var_shader_inout))
|
|
{
|
|
if (!ir->data.explicit_location)
|
|
{
|
|
ir->data.explicit_location = 1;
|
|
ir->data.location = FRAG_RESULT_DATA0 + ctx.colorCounter;
|
|
++ctx.colorCounter;
|
|
}
|
|
|
|
if (ir->data.explicit_location)
|
|
{
|
|
const int binding_base = (int)FRAG_RESULT_DATA0;
|
|
const int location = ir->data.location - binding_base;
|
|
if (location >= 0 && !ir->type->is_array())
|
|
buffer.asprintf_append (" [[color(%d)]]", location);
|
|
}
|
|
}
|
|
|
|
// uniform texture?
|
|
if (ir->data.mode == ir_var_uniform && ctx.writingParams)
|
|
{
|
|
buffer.asprintf_append (" [[texture(%i)]]", ctx.textureCounter);
|
|
buffer.asprintf_append (", sampler _mtlsmp_%s [[sampler(%i)]]", ir->name, ctx.textureCounter);
|
|
ir->data.explicit_location = 1;
|
|
ir->data.location = ctx.textureCounter;
|
|
++ctx.textureCounter;
|
|
}
|
|
// regular uniform?
|
|
if (ir->data.mode == ir_var_uniform && !ctx.writingParams)
|
|
{
|
|
int size, align;
|
|
get_metal_type_size(ir->type, (glsl_precision)ir->data.precision, size, align);
|
|
|
|
int loc = ctx.uniformLocationCounter;
|
|
loc = (loc + align-1) & ~(align-1); // align it
|
|
|
|
ir->data.explicit_location = 1;
|
|
ir->data.location = loc;
|
|
|
|
loc += size;
|
|
ctx.uniformLocationCounter = loc;
|
|
}
|
|
|
|
if (ir->constant_value &&
|
|
ir->data.mode != ir_var_shader_in &&
|
|
ir->data.mode != ir_var_shader_out &&
|
|
ir->data.mode != ir_var_shader_inout &&
|
|
ir->data.mode != ir_var_function_in &&
|
|
ir->data.mode != ir_var_function_out &&
|
|
ir->data.mode != ir_var_function_inout)
|
|
{
|
|
buffer.asprintf_append (" = ");
|
|
visit (ir->constant_value);
|
|
}
|
|
|
|
if ((ir->data.mode == ir_var_auto || ir->data.mode == ir_var_temporary) && (ir->type->matrix_columns == 1)) {
|
|
switch (ir->type->base_type) {
|
|
case GLSL_TYPE_INT:
|
|
case GLSL_TYPE_FLOAT:
|
|
buffer.asprintf_append (" = 0");
|
|
break;
|
|
case GLSL_TYPE_BOOL:
|
|
buffer.asprintf_append (" = false");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_function_signature *ir)
|
|
{
|
|
const bool isMain = (strcmp(ir->function()->name, "main") == 0);
|
|
|
|
if (!isMain)
|
|
{
|
|
print_type(buffer, ir, ir->return_type, true);
|
|
buffer.asprintf_append (" %s (", ir->function_name());
|
|
|
|
if (!ir->parameters.is_empty())
|
|
{
|
|
buffer.asprintf_append ("\n");
|
|
|
|
indentation++; previous_skipped = false;
|
|
bool first = true;
|
|
foreach_in_list(ir_variable, inst, &ir->parameters)
|
|
{
|
|
if (!first)
|
|
buffer.asprintf_append (",\n");
|
|
indent();
|
|
inst->accept(this);
|
|
first = false;
|
|
}
|
|
indentation--;
|
|
|
|
buffer.asprintf_append ("\n");
|
|
indent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this->mode_whole == kPrintGlslFragment)
|
|
buffer.asprintf_append ("fragment ");
|
|
if (this->mode_whole == kPrintGlslVertex)
|
|
buffer.asprintf_append ("vertex ");
|
|
buffer.asprintf_append ("xlatMtlShaderOutput xlatMtlMain (xlatMtlShaderInput _mtl_i [[stage_in]], constant xlatMtlShaderUniform& _mtl_u [[buffer(0)]]");
|
|
if (!ctx.paramsStr.empty())
|
|
{
|
|
buffer.asprintf_append ("%s", ctx.paramsStr.c_str());
|
|
}
|
|
}
|
|
|
|
if (ir->body.is_empty())
|
|
{
|
|
buffer.asprintf_append (");\n");
|
|
return;
|
|
}
|
|
|
|
buffer.asprintf_append (")\n");
|
|
|
|
indent();
|
|
buffer.asprintf_append ("{\n");
|
|
indentation++; previous_skipped = false;
|
|
|
|
if (isMain)
|
|
{
|
|
// output struct
|
|
indent(); buffer.asprintf_append ("xlatMtlShaderOutput _mtl_o;\n");
|
|
|
|
// insert postponed global assigments and variable declarations
|
|
assert (!globals->main_function_done);
|
|
globals->main_function_done = true;
|
|
foreach_in_list(ga_entry_metal, node, &globals->global_assignements)
|
|
{
|
|
ir_instruction* as = node->ir;
|
|
as->accept(this);
|
|
buffer.asprintf_append(";\n");
|
|
}
|
|
}
|
|
|
|
foreach_in_list(ir_instruction, inst, &ir->body) {
|
|
indent();
|
|
inst->accept(this);
|
|
end_statement_line();
|
|
}
|
|
|
|
if (isMain)
|
|
{
|
|
// return stuff
|
|
indent(); buffer.asprintf_append ("return _mtl_o;\n");
|
|
}
|
|
|
|
indentation--;
|
|
indent();
|
|
buffer.asprintf_append ("}\n");
|
|
}
|
|
|
|
void ir_print_metal_visitor::visit(ir_function *ir)
|
|
{
|
|
bool found_non_builtin_proto = false;
|
|
|
|
foreach_in_list(ir_function_signature, sig, &ir->signatures) {
|
|
if (!sig->is_builtin())
|
|
found_non_builtin_proto = true;
|
|
}
|
|
if (!found_non_builtin_proto)
|
|
return;
|
|
|
|
PrintGlslMode oldMode = this->mode;
|
|
this->mode = kPrintGlslNone;
|
|
|
|
foreach_in_list(ir_function_signature, sig, &ir->signatures) {
|
|
indent();
|
|
sig->accept(this);
|
|
buffer.asprintf_append ("\n");
|
|
}
|
|
|
|
this->mode = oldMode;
|
|
|
|
indent();
|
|
}
|
|
|
|
|
|
static const char *const operator_glsl_strs[] = {
|
|
"~",
|
|
"!",
|
|
"-",
|
|
"abs",
|
|
"sign",
|
|
"1.0/",
|
|
"rsqrt",
|
|
"sqrt",
|
|
"normalize",
|
|
"exp",
|
|
"log",
|
|
"exp2",
|
|
"log2",
|
|
"int", // f2i
|
|
"int", // f2u
|
|
"float", // i2f
|
|
"bool", // f2b
|
|
"float", // b2f
|
|
"bool", // i2b
|
|
"int", // b2i
|
|
"float", // u2f
|
|
"int", // i2u
|
|
"int", // u2i
|
|
"as_type_", // bit i2f
|
|
"as_type_", // bit f2i
|
|
"as_type_", // bit u2f
|
|
"as_type_", // bit f2u
|
|
"any",
|
|
"trunc",
|
|
"ceil",
|
|
"floor",
|
|
"fract",
|
|
"rint",
|
|
"sin",
|
|
"cos",
|
|
"fast::sin", // reduced
|
|
"fast::cos", // reduced
|
|
"dfdx",
|
|
"dfdx", // coarse
|
|
"dfdx", // fine
|
|
"dfdy",
|
|
"dfdy", // coarse
|
|
"dfdy", // fine
|
|
"packSnorm2x16",
|
|
"packSnorm4x8",
|
|
"packUnorm2x16",
|
|
"packUnorm4x8",
|
|
"packHalf2x16",
|
|
"unpackSnorm2x16",
|
|
"unpackSnorm4x8",
|
|
"unpackUnorm2x16",
|
|
"unpackUnorm4x8",
|
|
"unpackHalf2x16",
|
|
"unpackHalf2x16_splitX_TODO",
|
|
"unpackHalf2x16_splitY_TODO",
|
|
"bitfieldReverse",
|
|
"bitCount",
|
|
"findMSB",
|
|
"findLSB",
|
|
"saturate",
|
|
"noise",
|
|
"interpolateAtCentroid_TODO",
|
|
"+",
|
|
"-",
|
|
"*",
|
|
"*_imul_high_TODO",
|
|
"/",
|
|
"carry_TODO",
|
|
"borrow_TODO",
|
|
"fmod",
|
|
"<",
|
|
">",
|
|
"<=",
|
|
">=",
|
|
"==",
|
|
"!=",
|
|
"==",
|
|
"!=",
|
|
"<<",
|
|
">>",
|
|
"&",
|
|
"^",
|
|
"|",
|
|
"&&",
|
|
"^^",
|
|
"||",
|
|
"dot",
|
|
"min",
|
|
"max",
|
|
"pow",
|
|
"packHalf2x16_split_TODO",
|
|
"bfm_TODO",
|
|
"uboloadTODO",
|
|
"ldexp_TODO",
|
|
"vectorExtract_TODO",
|
|
"interpolateAtOffset_TODO",
|
|
"interpolateAtSample_TODO",
|
|
"fma",
|
|
"clamp",
|
|
"mix",
|
|
"csel_TODO",
|
|
"bfi_TODO",
|
|
"bitfield_extract_TODO",
|
|
"vector_insert_TODO",
|
|
"bitfield_insert_TODO",
|
|
"vectorTODO",
|
|
};
|
|
|
|
|
|
static bool is_binop_func_like(ir_expression_operation op, const glsl_type* type)
|
|
{
|
|
if (op == ir_binop_mod ||
|
|
(op >= ir_binop_dot && op <= ir_binop_pow))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool is_different_precision(glsl_precision a, glsl_precision b)
|
|
{
|
|
// Tread "undefined" as high precision
|
|
if (a == glsl_precision_undefined)
|
|
a = glsl_precision_high;
|
|
if (b == glsl_precision_undefined)
|
|
b = glsl_precision_high;
|
|
// Metal does not have "low" precision; treat as medium
|
|
if (a == glsl_precision_low)
|
|
a = glsl_precision_medium;
|
|
if (b == glsl_precision_low)
|
|
b = glsl_precision_medium;
|
|
|
|
return a != b;
|
|
}
|
|
|
|
|
|
static void print_cast(string_buffer& buffer, glsl_precision prec, ir_rvalue* ir)
|
|
{
|
|
buffer.asprintf_append ("(");
|
|
print_type_precision(buffer, ir->type, prec, false);
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_expression *ir)
|
|
{
|
|
++this->expression_depth;
|
|
newline_indent();
|
|
|
|
glsl_precision arg_prec = glsl_precision_undefined;
|
|
if (ir->operands[0])
|
|
arg_prec = higher_precision(arg_prec, ir->operands[0]->get_precision());
|
|
if (ir->operands[1])
|
|
arg_prec = higher_precision(arg_prec, ir->operands[1]->get_precision());
|
|
if (ir->operands[2])
|
|
arg_prec = higher_precision(arg_prec, ir->operands[2]->get_precision());
|
|
glsl_precision res_prec = ir->get_precision();
|
|
|
|
bool op0cast = ir->operands[0] && is_different_precision(arg_prec, ir->operands[0]->get_precision());
|
|
bool op1cast = ir->operands[1] && is_different_precision(arg_prec, ir->operands[1]->get_precision());
|
|
bool op2cast = ir->operands[2] && is_different_precision(arg_prec, ir->operands[2]->get_precision());
|
|
const bool op0matrix = ir->operands[0] && ir->operands[0]->type->is_matrix();
|
|
const bool op1matrix = ir->operands[1] && ir->operands[1]->type->is_matrix();
|
|
bool op0castTo1 = false;
|
|
bool op1castTo0 = false;
|
|
|
|
// Metal does not support matrix precision casts, so when any of the arguments is a matrix,
|
|
// take precision from it. This isn't fully robust now, but oh well.
|
|
if (op0cast && op0matrix && !op1cast)
|
|
{
|
|
op0cast = false;
|
|
arg_prec = ir->operands[0]->get_precision();
|
|
op1cast = ir->operands[1] && is_different_precision(arg_prec, ir->operands[1]->get_precision());
|
|
}
|
|
if (op1cast && op1matrix && !op0cast)
|
|
{
|
|
op1cast = false;
|
|
arg_prec = ir->operands[1]->get_precision();
|
|
op0cast = ir->operands[0] && is_different_precision(arg_prec, ir->operands[0]->get_precision());
|
|
}
|
|
|
|
// Metal does not have matrix+scalar and matrix-scalar operations; we need to create matrices
|
|
// out of the non-matrix argument.
|
|
if (ir->operation == ir_binop_add || ir->operation == ir_binop_sub)
|
|
{
|
|
if (op0matrix && !op1matrix)
|
|
{
|
|
op1cast = true;
|
|
op1castTo0 = true;
|
|
}
|
|
if (op1matrix && !op0matrix)
|
|
{
|
|
op0cast = true;
|
|
op0castTo1 = true;
|
|
}
|
|
if (op1castTo0 || op0castTo1)
|
|
{
|
|
if (!ctx.matrixConstructorsDone)
|
|
{
|
|
ctx.prefixStr.asprintf_append(
|
|
"inline float4x4 _xlinit_float4x4(float v) { return float4x4(float4(v), float4(v), float4(v), float4(v)); }\n"
|
|
"inline float3x3 _xlinit_float3x3(float v) { return float3x3(float3(v), float3(v), float3(v)); }\n"
|
|
"inline float2x2 _xlinit_float2x2(float v) { return float2x2(float2(v), float2(v)); }\n"
|
|
"inline half4x4 _xlinit_half4x4(half v) { return half4x4(half4(v), half4(v), half4(v), half4(v)); }\n"
|
|
"inline half3x3 _xlinit_half3x3(half v) { return half3x3(half3(v), half3(v), half3(v)); }\n"
|
|
"inline half2x2 _xlinit_half2x2(half v) { return half2x2(half2(v), half2(v)); }\n"
|
|
);
|
|
ctx.matrixConstructorsDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool rescast = is_different_precision(arg_prec, res_prec) && !ir->type->is_boolean();
|
|
if (rescast)
|
|
{
|
|
buffer.asprintf_append ("(");
|
|
print_cast (buffer, res_prec, ir);
|
|
}
|
|
|
|
if (ir->get_num_operands() == 1)
|
|
{
|
|
if (op0cast)
|
|
print_cast (buffer, arg_prec, ir->operands[0]);
|
|
if (ir->operation >= ir_unop_f2i && ir->operation <= ir_unop_u2i) {
|
|
print_type(buffer, ir, ir->type, true);
|
|
buffer.asprintf_append ("(");
|
|
} else if (ir->operation >= ir_unop_bitcast_i2f && ir->operation <= ir_unop_bitcast_f2u) {
|
|
buffer.asprintf_append("as_type<");
|
|
print_type(buffer, ir, ir->type, true);
|
|
buffer.asprintf_append(">(");
|
|
} else if (ir->operation == ir_unop_rcp) {
|
|
const bool halfCast = (arg_prec == glsl_precision_medium || arg_prec == glsl_precision_low);
|
|
buffer.asprintf_append (halfCast ? "((half)1.0/(" : "(1.0/(");
|
|
} else {
|
|
switch(ir->operation) {
|
|
case ir_unop_dFdy:
|
|
case ir_unop_dFdy_coarse:
|
|
case ir_unop_dFdy_fine:
|
|
buffer.asprintf_append ("%s(-", operator_glsl_strs[ir->operation]);
|
|
break;
|
|
|
|
default:
|
|
buffer.asprintf_append ("%s(", operator_glsl_strs[ir->operation]);
|
|
break;
|
|
}
|
|
}
|
|
if (ir->operands[0])
|
|
ir->operands[0]->accept(this);
|
|
buffer.asprintf_append (")");
|
|
if (ir->operation == ir_unop_rcp) {
|
|
buffer.asprintf_append (")");
|
|
}
|
|
}
|
|
else if (ir->operation == ir_binop_vector_extract)
|
|
{
|
|
// a[b]
|
|
|
|
if (ir->operands[0])
|
|
ir->operands[0]->accept(this);
|
|
buffer.asprintf_append ("[");
|
|
if (ir->operands[1])
|
|
ir->operands[1]->accept(this);
|
|
buffer.asprintf_append ("]");
|
|
}
|
|
else if (is_binop_func_like(ir->operation, ir->type))
|
|
{
|
|
// binary operation that must be printed like a function, "foo(a,b)"
|
|
if (ir->operation == ir_binop_mod)
|
|
{
|
|
buffer.asprintf_append ("(");
|
|
print_type(buffer, ir, ir->type, true);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
buffer.asprintf_append ("%s (", operator_glsl_strs[ir->operation]);
|
|
|
|
if (ir->operands[0])
|
|
{
|
|
if (op0cast)
|
|
print_cast (buffer, arg_prec, ir->operands[0]);
|
|
ir->operands[0]->accept(this);
|
|
}
|
|
buffer.asprintf_append (", ");
|
|
if (ir->operands[1])
|
|
{
|
|
if (op1cast)
|
|
print_cast (buffer, arg_prec, ir->operands[1]);
|
|
ir->operands[1]->accept(this);
|
|
}
|
|
buffer.asprintf_append (")");
|
|
if (ir->operation == ir_binop_mod)
|
|
buffer.asprintf_append ("))");
|
|
}
|
|
else if (ir->get_num_operands() == 2 && ir->operation == ir_binop_div && op0matrix && !op1matrix)
|
|
{
|
|
// "matrix/scalar" - Metal does not have it, so print multiply by inverse instead
|
|
buffer.asprintf_append ("(");
|
|
ir->operands[0]->accept(this);
|
|
const bool halfCast = (arg_prec == glsl_precision_medium || arg_prec == glsl_precision_low);
|
|
buffer.asprintf_append (halfCast ? " * (1.0h/half(" : " * (1.0/(");
|
|
ir->operands[1]->accept(this);
|
|
buffer.asprintf_append (")))");
|
|
}
|
|
else if (ir->get_num_operands() == 2)
|
|
{
|
|
// regular binary operator
|
|
buffer.asprintf_append ("(");
|
|
if (ir->operands[0])
|
|
{
|
|
if (op0castTo1)
|
|
{
|
|
buffer.asprintf_append ("_xlinit_");
|
|
print_type_precision(buffer, ir->operands[1]->type, arg_prec, false);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
else if (op0cast)
|
|
{
|
|
print_cast (buffer, arg_prec, ir->operands[0]);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
ir->operands[0]->accept(this);
|
|
if (op0castTo1 || op0cast)
|
|
{
|
|
buffer.asprintf_append (")");
|
|
}
|
|
}
|
|
|
|
buffer.asprintf_append (" %s ", operator_glsl_strs[ir->operation]);
|
|
|
|
if (ir->operands[1])
|
|
{
|
|
if (op1castTo0)
|
|
{
|
|
buffer.asprintf_append ("_xlinit_");
|
|
print_type_precision(buffer, ir->operands[0]->type, arg_prec, false);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
else if (op1cast)
|
|
{
|
|
print_cast (buffer, arg_prec, ir->operands[1]);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
ir->operands[1]->accept(this);
|
|
if (op1castTo0 || op1cast)
|
|
{
|
|
buffer.asprintf_append (")");
|
|
}
|
|
}
|
|
buffer.asprintf_append (")");
|
|
}
|
|
else
|
|
{
|
|
// ternary op
|
|
buffer.asprintf_append ("%s (", operator_glsl_strs[ir->operation]);
|
|
if (ir->operands[0])
|
|
{
|
|
if (op0cast)
|
|
print_cast (buffer, arg_prec, ir->operands[0]);
|
|
ir->operands[0]->accept(this);
|
|
}
|
|
buffer.asprintf_append (", ");
|
|
if (ir->operands[1])
|
|
{
|
|
if (op1cast)
|
|
print_cast (buffer, arg_prec, ir->operands[1]);
|
|
ir->operands[1]->accept(this);
|
|
}
|
|
buffer.asprintf_append (", ");
|
|
if (ir->operands[2])
|
|
{
|
|
if (op2cast)
|
|
print_cast (buffer, arg_prec, ir->operands[2]);
|
|
ir->operands[2]->accept(this);
|
|
}
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
if (rescast)
|
|
{
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
|
|
newline_deindent();
|
|
--this->expression_depth;
|
|
}
|
|
|
|
static int tex_sampler_dim_size[] = {
|
|
1, 2, 3, 3, 2, 2, 2,
|
|
};
|
|
|
|
|
|
static void print_texture_uv (ir_print_metal_visitor* vis, ir_texture* ir, bool is_shadow, bool is_proj, bool is_array, int uv_dim, int sampler_uv_dim)
|
|
{
|
|
if (!is_shadow)
|
|
{
|
|
if (!is_proj && !is_array)
|
|
{
|
|
// regular UV
|
|
vis->buffer.asprintf_append (sampler_uv_dim == 3 ? "(float3)(" : "(float2)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (")");
|
|
}
|
|
else if (is_array)
|
|
{
|
|
// array sample
|
|
vis->buffer.asprintf_append ("(float2)((");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").xy), (uint)((");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").z)");
|
|
}
|
|
else
|
|
{
|
|
// regular projected
|
|
vis->buffer.asprintf_append (sampler_uv_dim == 3 ? "((float3)(" : "((float2)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (sampler_uv_dim == 3 ? ").xyz / (float)(" : ").xy / (float)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (uv_dim == 4 ? ").w)" : ").z)");
|
|
}
|
|
}
|
|
else if (is_shadow)
|
|
{
|
|
// Note that on metal sample_compare works differently than shadow2DEXT on GLES:
|
|
// it does not clamp neither the pixel value nor compare value to the [0.0, 1.0] range. To
|
|
// preserve same behavior we're clamping the argument explicitly.
|
|
if (!is_proj)
|
|
{
|
|
// regular shadow
|
|
vis->buffer.asprintf_append (uv_dim == 4 ? "(float3)(" : "(float2)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (uv_dim == 4 ? ").xyz, (" : ").xy, saturate((float)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (uv_dim == 4 ? ").w" : ").z)");
|
|
}
|
|
else
|
|
{
|
|
// projected shadow
|
|
vis->buffer.asprintf_append ("(float2)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").xy / (float)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").w, saturate((float)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").z / (float)(");
|
|
ir->coordinate->accept(vis);
|
|
vis->buffer.asprintf_append (").w)");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ir_print_metal_visitor::visit(ir_texture *ir)
|
|
{
|
|
if (ir->op == ir_txs)
|
|
{
|
|
ir->sampler->accept(this);
|
|
buffer.asprintf_append (".get_width(");
|
|
ir->lod_info.lod->accept(this);
|
|
buffer.asprintf_append ("), ");
|
|
ir->sampler->accept(this);
|
|
buffer.asprintf_append (".get_height(");
|
|
ir->lod_info.lod->accept(this);
|
|
buffer.asprintf_append (")");
|
|
return;
|
|
}
|
|
glsl_sampler_dim sampler_dim = (glsl_sampler_dim)ir->sampler->type->sampler_dimensionality;
|
|
const bool is_shadow = ir->sampler->type->sampler_shadow;
|
|
const bool is_array = ir->sampler->type->sampler_array;
|
|
const glsl_type* uv_type = ir->coordinate->type;
|
|
const int uv_dim = uv_type->vector_elements;
|
|
int sampler_uv_dim = tex_sampler_dim_size[sampler_dim];
|
|
if (is_shadow)
|
|
sampler_uv_dim += 1;
|
|
const bool is_proj = (uv_dim > sampler_uv_dim) && !is_array;
|
|
|
|
// Construct as the expected return type of shadow2D as sample_compare returns a scalar
|
|
if (is_shadow)
|
|
{
|
|
buffer.asprintf_append("float4(");
|
|
}
|
|
|
|
// texture name & call to sample
|
|
ir->sampler->accept(this);
|
|
if (is_shadow)
|
|
{
|
|
// For shadow sampling, Metal right now needs a hardcoded sampler state :|
|
|
if (!ctx.shadowSamplerDone)
|
|
{
|
|
ctx.prefixStr.asprintf_append("constexpr sampler _mtl_xl_shadow_sampler(address::clamp_to_edge, filter::linear, compare_func::less_equal);\n");
|
|
ctx.shadowSamplerDone = true;
|
|
}
|
|
buffer.asprintf_append (".sample_compare(_mtl_xl_shadow_sampler");
|
|
}
|
|
else
|
|
{
|
|
buffer.asprintf_append (".sample(_mtlsmp_");
|
|
ir->sampler->accept(this);
|
|
}
|
|
buffer.asprintf_append (", ");
|
|
|
|
// texture coordinate
|
|
print_texture_uv (this, ir, is_shadow, is_proj, is_array, uv_dim, sampler_uv_dim);
|
|
|
|
// lod bias
|
|
if (ir->op == ir_txb)
|
|
{
|
|
buffer.asprintf_append (", bias(");
|
|
ir->lod_info.bias->accept(this);
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
// lod
|
|
if (ir->op == ir_txl)
|
|
{
|
|
buffer.asprintf_append (", level(");
|
|
ir->lod_info.lod->accept(this);
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
// grad
|
|
if (ir->op == ir_txd)
|
|
{
|
|
if (sampler_dim == GLSL_SAMPLER_DIM_CUBE)
|
|
buffer.asprintf_append (", gradientcube((float3)(");
|
|
else
|
|
buffer.asprintf_append (", gradient2d((float2)(");
|
|
|
|
ir->lod_info.grad.dPdx->accept(this);
|
|
|
|
if (sampler_dim == GLSL_SAMPLER_DIM_CUBE)
|
|
buffer.asprintf_append ("), (float3)(");
|
|
else
|
|
buffer.asprintf_append ("), (float2)(");
|
|
|
|
ir->lod_info.grad.dPdy->accept(this);
|
|
buffer.asprintf_append ("))");
|
|
}
|
|
|
|
//@TODO: texelFetch
|
|
//@TODO: projected
|
|
//@TODO: shadowmaps
|
|
//@TODO: pixel offsets
|
|
|
|
buffer.asprintf_append (")");
|
|
|
|
// Close float4 cast
|
|
if (is_shadow)
|
|
{
|
|
buffer.asprintf_append(")");
|
|
}
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_swizzle *ir)
|
|
{
|
|
const unsigned swiz[4] = {
|
|
ir->mask.x,
|
|
ir->mask.y,
|
|
ir->mask.z,
|
|
ir->mask.w,
|
|
};
|
|
|
|
if (ir->val->type == glsl_type::float_type || ir->val->type == glsl_type::int_type)
|
|
{
|
|
if (ir->mask.num_components != 1)
|
|
{
|
|
print_type(buffer, ir, ir->type, true);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
}
|
|
|
|
ir->val->accept(this);
|
|
|
|
if (ir->val->type == glsl_type::float_type || ir->val->type == glsl_type::int_type)
|
|
{
|
|
if (ir->mask.num_components != 1)
|
|
{
|
|
buffer.asprintf_append (")");
|
|
}
|
|
return;
|
|
}
|
|
|
|
buffer.asprintf_append (".");
|
|
for (unsigned i = 0; i < ir->mask.num_components; i++) {
|
|
buffer.asprintf_append ("%c", "xyzw"[swiz[i]]);
|
|
}
|
|
}
|
|
|
|
static void print_var_inout (string_buffer& buf, ir_variable* var, bool insideLHS)
|
|
{
|
|
if (var->data.mode == ir_var_shader_in)
|
|
buf.asprintf_append ("_mtl_i.");
|
|
if (var->data.mode == ir_var_shader_out)
|
|
buf.asprintf_append ("_mtl_o.");
|
|
if (var->data.mode == ir_var_uniform && !var->type->is_sampler())
|
|
buf.asprintf_append ("_mtl_u.");
|
|
if (var->data.mode == ir_var_shader_inout)
|
|
buf.asprintf_append (insideLHS ? "_mtl_o." : "_mtl_i.");
|
|
}
|
|
|
|
void ir_print_metal_visitor::visit(ir_dereference_variable *ir)
|
|
{
|
|
ir_variable *var = ir->variable_referenced();
|
|
print_var_inout(buffer, var, this->inside_lhs);
|
|
print_var_name (var);
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_dereference_array *ir)
|
|
{
|
|
ir->array->accept(this);
|
|
buffer.asprintf_append ("[");
|
|
ir->array_index->accept(this);
|
|
buffer.asprintf_append ("]");
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_dereference_record *ir)
|
|
{
|
|
ir->record->accept(this);
|
|
buffer.asprintf_append (".%s", ir->field);
|
|
}
|
|
|
|
|
|
|
|
|
|
void ir_print_metal_visitor::emit_assignment_part (ir_dereference* lhs, ir_rvalue* rhs, unsigned write_mask, ir_rvalue* dstIndex)
|
|
{
|
|
const bool prev_lhs_flag = this->inside_lhs;
|
|
this->inside_lhs = true;
|
|
|
|
lhs->accept(this);
|
|
|
|
this->inside_lhs = prev_lhs_flag;
|
|
|
|
const glsl_type* lhsType = lhs->type;
|
|
if (dstIndex)
|
|
{
|
|
// if dst index is a constant, then emit a swizzle
|
|
ir_constant* dstConst = dstIndex->as_constant();
|
|
if (dstConst)
|
|
{
|
|
const char* comps = "xyzw";
|
|
char comp = comps[dstConst->get_int_component(0)];
|
|
buffer.asprintf_append (".%c", comp);
|
|
}
|
|
else
|
|
{
|
|
buffer.asprintf_append ("[");
|
|
dstIndex->accept(this);
|
|
buffer.asprintf_append ("]");
|
|
}
|
|
|
|
if (lhsType->matrix_columns <= 1 && lhsType->vector_elements > 1)
|
|
lhsType = glsl_type::get_instance(lhsType->base_type, 1, 1);
|
|
}
|
|
|
|
char mask[5];
|
|
unsigned j = 0;
|
|
const glsl_type* rhsType = rhs->type;
|
|
if (!dstIndex && lhsType->matrix_columns <= 1 && lhsType->vector_elements > 1 && write_mask != (1<<lhsType->vector_elements)-1)
|
|
{
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
if ((write_mask & (1 << i)) != 0) {
|
|
mask[j] = "xyzw"[i];
|
|
j++;
|
|
}
|
|
}
|
|
lhsType = glsl_type::get_instance(lhsType->base_type, j, 1);
|
|
}
|
|
mask[j] = '\0';
|
|
bool hasWriteMask = false;
|
|
if (mask[0])
|
|
{
|
|
buffer.asprintf_append (".%s", mask);
|
|
hasWriteMask = true;
|
|
}
|
|
|
|
buffer.asprintf_append (" = ");
|
|
|
|
const bool typeMismatch = !dstIndex && (lhsType != rhsType);
|
|
|
|
const bool precMismatch = is_different_precision (lhs->get_precision(), rhs->get_precision());
|
|
const bool addSwizzle = hasWriteMask && typeMismatch;
|
|
if (typeMismatch || precMismatch)
|
|
{
|
|
if (!addSwizzle)
|
|
{
|
|
if (lhsType->is_matrix())
|
|
{
|
|
// Metal does not have matrix precision casts right now, so emit workaround
|
|
// functions that would do that.
|
|
if (!ctx.matrixCastsDone)
|
|
{
|
|
ctx.prefixStr.asprintf_append(
|
|
"inline float4x4 _xlcast_float4x4(half4x4 v) { return float4x4(float4(v[0]), float4(v[1]), float4(v[2]), float4(v[3])); }\n"
|
|
"inline float3x3 _xlcast_float3x3(half3x3 v) { return float3x3(float3(v[0]), float3(v[1]), float3(v[2])); }\n"
|
|
"inline float2x2 _xlcast_float2x2(half2x2 v) { return float2x2(float2(v[0]), float2(v[1])); }\n"
|
|
"inline half4x4 _xlcast_half4x4(float4x4 v) { return half4x4(half4(v[0]), half4(v[1]), half4(v[2]), half4(v[3])); }\n"
|
|
"inline half3x3 _xlcast_half3x3(float3x3 v) { return half3x3(half3(v[0]), half3(v[1]), half3(v[2])); }\n"
|
|
"inline half2x2 _xlcast_half2x2(float2x2 v) { return half2x2(half2(v[0]), half2(v[1])); }\n"
|
|
);
|
|
ctx.matrixCastsDone = true;
|
|
}
|
|
buffer.asprintf_append ("_xlcast_");
|
|
}
|
|
print_type(buffer, lhs, lhsType, true);
|
|
}
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
|
|
rhs->accept(this);
|
|
|
|
if (typeMismatch || precMismatch)
|
|
{
|
|
buffer.asprintf_append (")");
|
|
if (addSwizzle)
|
|
buffer.asprintf_append (".%s", mask);
|
|
}
|
|
}
|
|
|
|
|
|
// Try to print (X = X + const) as (X += const), mostly to satisfy
|
|
// OpenGL ES 2.0 loop syntax restrictions.
|
|
static bool try_print_increment (ir_print_metal_visitor* vis, ir_assignment* ir)
|
|
{
|
|
if (ir->condition)
|
|
return false;
|
|
|
|
// Needs to be + on rhs
|
|
ir_expression* rhsOp = ir->rhs->as_expression();
|
|
if (!rhsOp || rhsOp->operation != ir_binop_add)
|
|
return false;
|
|
|
|
// Needs to write to whole variable
|
|
ir_variable* lhsVar = ir->whole_variable_written();
|
|
if (lhsVar == NULL)
|
|
return false;
|
|
|
|
// Types must match
|
|
if (ir->lhs->type != ir->rhs->type)
|
|
return false;
|
|
|
|
// Type must be scalar
|
|
if (!ir->lhs->type->is_scalar())
|
|
return false;
|
|
|
|
// rhs0 must be variable deref, same one as lhs
|
|
ir_dereference_variable* rhsDeref = rhsOp->operands[0]->as_dereference_variable();
|
|
if (rhsDeref == NULL)
|
|
return false;
|
|
if (lhsVar != rhsDeref->var)
|
|
return false;
|
|
|
|
// rhs1 must be a constant
|
|
ir_constant* rhsConst = rhsOp->operands[1]->as_constant();
|
|
if (!rhsConst)
|
|
return false;
|
|
|
|
// print variable name
|
|
const bool prev_lhs_flag = vis->inside_lhs;
|
|
vis->inside_lhs = true;
|
|
|
|
ir->lhs->accept (vis);
|
|
|
|
vis->inside_lhs = prev_lhs_flag;
|
|
|
|
|
|
// print ++ or +=const
|
|
if (ir->lhs->type->base_type <= GLSL_TYPE_INT && rhsConst->is_one())
|
|
{
|
|
vis->buffer.asprintf_append ("++");
|
|
}
|
|
else
|
|
{
|
|
vis->buffer.asprintf_append(" += ");
|
|
rhsConst->accept (vis);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void ir_print_metal_visitor::visit(ir_assignment *ir)
|
|
{
|
|
// if this is a loop induction variable initial assignment, and we aren't inside loop body:
|
|
// do not print it (will be printed when inside loop body)
|
|
if (!inside_loop_body)
|
|
{
|
|
ir_variable* whole_var = ir->whole_variable_written();
|
|
if (!ir->condition && whole_var)
|
|
{
|
|
loop_variable_state* inductor_state = loopstate->get_for_inductor(whole_var);
|
|
if (inductor_state && inductor_state->private_induction_variable_count == 1 &&
|
|
can_emit_canonical_for(inductor_state))
|
|
{
|
|
skipped_this_ir = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// assignments in global scope are postponed to main function
|
|
if (this->mode != kPrintGlslNone)
|
|
{
|
|
assert (!this->globals->main_function_done);
|
|
this->globals->global_assignements.push_tail (new(this->globals->mem_ctx) ga_entry_metal(ir));
|
|
buffer.asprintf_append ("//"); // for the ; that will follow (ugly, I know)
|
|
return;
|
|
}
|
|
|
|
// if RHS is ir_triop_vector_insert, then we have to do some special dance. If source expression is:
|
|
// dst = vector_insert (a, b, idx)
|
|
// then emit it like:
|
|
// dst = a;
|
|
// dst.idx = b;
|
|
ir_expression* rhsOp = ir->rhs->as_expression();
|
|
if (rhsOp && rhsOp->operation == ir_triop_vector_insert)
|
|
{
|
|
// skip assignment if lhs and rhs would be the same
|
|
bool skip_assign = false;
|
|
ir_dereference_variable* lhsDeref = ir->lhs->as_dereference_variable();
|
|
ir_dereference_variable* rhsDeref = rhsOp->operands[0]->as_dereference_variable();
|
|
if (lhsDeref && rhsDeref)
|
|
{
|
|
if (lhsDeref->var == rhsDeref->var)
|
|
skip_assign = true;
|
|
}
|
|
|
|
if (!skip_assign)
|
|
{
|
|
emit_assignment_part(ir->lhs, rhsOp->operands[0], ir->write_mask, NULL);
|
|
buffer.asprintf_append ("; ");
|
|
}
|
|
emit_assignment_part(ir->lhs, rhsOp->operands[1], ir->write_mask, rhsOp->operands[2]);
|
|
return;
|
|
}
|
|
|
|
if (try_print_increment (this, ir))
|
|
return;
|
|
|
|
if (ir->condition)
|
|
{
|
|
ir->condition->accept(this);
|
|
buffer.asprintf_append (" ");
|
|
}
|
|
|
|
emit_assignment_part (ir->lhs, ir->rhs, ir->write_mask, NULL);
|
|
}
|
|
|
|
void ir_print_metal_visitor::visit(ir_constant *ir)
|
|
{
|
|
const glsl_type* type = ir->type;
|
|
|
|
// hoist array & struct constants into global scope
|
|
if (type->is_array() || type->is_record())
|
|
{
|
|
size_t id = (size_t)hash_table_find(globals->const_hash, ir);
|
|
if (id == 0)
|
|
{
|
|
id = ++globals->const_counter;
|
|
hash_table_insert (globals->const_hash, (void*)id, ir);
|
|
globals->global_constants.push_tail(new(globals->mem_ctx) gconst_entry_metal(ir,id));
|
|
}
|
|
buffer.asprintf_append("_xlat_mtl_const%i", (int)id);
|
|
return;
|
|
}
|
|
|
|
if (type == glsl_type::float_type)
|
|
{
|
|
print_float (buffer, ir->value.f[0]);
|
|
return;
|
|
}
|
|
else if (type == glsl_type::int_type)
|
|
{
|
|
buffer.asprintf_append ("%d", ir->value.i[0]);
|
|
return;
|
|
}
|
|
else if (type == glsl_type::uint_type)
|
|
{
|
|
buffer.asprintf_append ("%u", ir->value.u[0]);
|
|
return;
|
|
}
|
|
|
|
const glsl_type *const base_type = ir->type->get_base_type();
|
|
|
|
print_type(buffer, ir, type, true);
|
|
buffer.asprintf_append ("(");
|
|
|
|
// should be dealt with above
|
|
assert(!ir->type->is_array());
|
|
assert(!ir->type->is_record());
|
|
bool first = true;
|
|
|
|
// Metal needs matrices to be constructed from vectors, not from a bunch of scalars.
|
|
// So instead of printing mat2(1,2,3,4) like in glsl, we have to print float2x2(float2(1,2), float2(3,4))
|
|
// here.
|
|
const bool mtx = ir->type->is_matrix();
|
|
const glsl_type* vec_type = NULL; // matrix column type
|
|
if (mtx)
|
|
vec_type = glsl_type::get_instance (ir->type->base_type, ir->type->vector_elements, 1);
|
|
|
|
for (unsigned i = 0; i < ir->type->components(); i++)
|
|
{
|
|
if (!first)
|
|
{
|
|
if (mtx && (i % ir->type->matrix_columns == 0))
|
|
buffer.asprintf_append (")");
|
|
buffer.asprintf_append (", ");
|
|
}
|
|
first = false;
|
|
|
|
if (mtx && (i % ir->type->matrix_columns == 0))
|
|
{
|
|
print_type(buffer, ir, vec_type, true);
|
|
buffer.asprintf_append ("(");
|
|
}
|
|
|
|
switch (base_type->base_type) {
|
|
case GLSL_TYPE_UINT: buffer.asprintf_append ("%u", ir->value.u[i]); break;
|
|
case GLSL_TYPE_INT: buffer.asprintf_append ("%d", ir->value.i[i]); break;
|
|
case GLSL_TYPE_FLOAT: print_float(buffer, ir->value.f[i]); break;
|
|
case GLSL_TYPE_BOOL: buffer.asprintf_append ("%d", ir->value.b[i]); break;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
if (mtx)
|
|
buffer.asprintf_append (")");
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_call *ir)
|
|
{
|
|
// calls in global scope are postponed to main function
|
|
if (this->mode != kPrintGlslNone)
|
|
{
|
|
assert (!this->globals->main_function_done);
|
|
this->globals->global_assignements.push_tail (new(this->globals->mem_ctx) ga_entry_metal(ir));
|
|
buffer.asprintf_append ("//"); // for the ; that will follow (ugly, I know)
|
|
return;
|
|
}
|
|
|
|
if (ir->return_deref)
|
|
{
|
|
visit(ir->return_deref);
|
|
buffer.asprintf_append (" = ");
|
|
}
|
|
|
|
buffer.asprintf_append ("%s (", ir->callee_name());
|
|
bool first = true;
|
|
foreach_in_list(ir_instruction, inst, &ir->actual_parameters) {
|
|
if (!first)
|
|
buffer.asprintf_append (", ");
|
|
inst->accept(this);
|
|
first = false;
|
|
}
|
|
buffer.asprintf_append (")");
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_return *ir)
|
|
{
|
|
buffer.asprintf_append ("return");
|
|
|
|
ir_rvalue *const value = ir->get_value();
|
|
if (value) {
|
|
buffer.asprintf_append (" ");
|
|
value->accept(this);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_discard *ir)
|
|
{
|
|
buffer.asprintf_append ("discard_fragment()");
|
|
|
|
if (ir->condition != NULL) {
|
|
buffer.asprintf_append (" TODO ");
|
|
ir->condition->accept(this);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_if *ir)
|
|
{
|
|
buffer.asprintf_append ("if (");
|
|
ir->condition->accept(this);
|
|
|
|
buffer.asprintf_append (") {\n");
|
|
indentation++; previous_skipped = false;
|
|
|
|
|
|
foreach_in_list(ir_instruction, inst, &ir->then_instructions) {
|
|
indent();
|
|
inst->accept(this);
|
|
end_statement_line();
|
|
}
|
|
|
|
indentation--;
|
|
indent();
|
|
buffer.asprintf_append ("}");
|
|
|
|
if (!ir->else_instructions.is_empty())
|
|
{
|
|
buffer.asprintf_append (" else {\n");
|
|
indentation++; previous_skipped = false;
|
|
|
|
foreach_in_list(ir_instruction, inst, &ir->else_instructions) {
|
|
indent();
|
|
inst->accept(this);
|
|
end_statement_line();
|
|
}
|
|
indentation--;
|
|
indent();
|
|
buffer.asprintf_append ("}");
|
|
}
|
|
}
|
|
|
|
|
|
bool ir_print_metal_visitor::can_emit_canonical_for (loop_variable_state *ls)
|
|
{
|
|
if (ls == NULL)
|
|
return false;
|
|
|
|
if (ls->induction_variables.is_empty())
|
|
return false;
|
|
|
|
if (ls->terminators.is_empty())
|
|
return false;
|
|
|
|
// only support for loops with one terminator condition
|
|
int terminatorCount = ls->terminators.length();
|
|
if (terminatorCount != 1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ir_print_metal_visitor::emit_canonical_for (ir_loop* ir)
|
|
{
|
|
loop_variable_state* const ls = this->loopstate->get(ir);
|
|
|
|
if (!can_emit_canonical_for(ls))
|
|
return false;
|
|
|
|
hash_table* terminator_hash = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
|
|
hash_table* induction_hash = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
|
|
|
|
buffer.asprintf_append("for (");
|
|
inside_loop_body = true;
|
|
|
|
// emit loop induction variable declarations.
|
|
// only for loops with single induction variable, to avoid cases of different types of them
|
|
if (ls->private_induction_variable_count == 1)
|
|
{
|
|
foreach_in_list(loop_variable, indvar, &ls->induction_variables)
|
|
{
|
|
if (!this->loopstate->get_for_inductor(indvar->var))
|
|
continue;
|
|
|
|
ir_variable* var = indvar->var;
|
|
print_type(buffer, var, var->type, false);
|
|
buffer.asprintf_append (" ");
|
|
print_var_inout(buffer, var, true);
|
|
print_var_name (var);
|
|
print_type_post(buffer, var->type, false);
|
|
if (indvar->initial_value)
|
|
{
|
|
buffer.asprintf_append (" = ");
|
|
indvar->initial_value->accept(this);
|
|
}
|
|
}
|
|
}
|
|
buffer.asprintf_append("; ");
|
|
|
|
// emit loop terminating conditions
|
|
foreach_in_list(loop_terminator, term, &ls->terminators)
|
|
{
|
|
hash_table_insert(terminator_hash, term, term->ir);
|
|
|
|
// IR has conditions in the form of "if (x) break",
|
|
// whereas for loop needs them negated, in the form
|
|
// if "while (x) continue the loop".
|
|
// See if we can print them using syntax that reads nice.
|
|
bool handled = false;
|
|
ir_expression* term_expr = term->ir->condition->as_expression();
|
|
if (term_expr)
|
|
{
|
|
// Binary comparison conditions
|
|
const char* termOp = NULL;
|
|
switch (term_expr->operation)
|
|
{
|
|
case ir_binop_less: termOp = ">="; break;
|
|
case ir_binop_greater: termOp = "<="; break;
|
|
case ir_binop_lequal: termOp = ">"; break;
|
|
case ir_binop_gequal: termOp = "<"; break;
|
|
case ir_binop_equal: termOp = "!="; break;
|
|
case ir_binop_nequal: termOp = "=="; break;
|
|
default: break;
|
|
}
|
|
if (termOp != NULL)
|
|
{
|
|
term_expr->operands[0]->accept(this);
|
|
buffer.asprintf_append(" %s ", termOp);
|
|
term_expr->operands[1]->accept(this);
|
|
handled = true;
|
|
}
|
|
|
|
// Unary logic not
|
|
if (!handled && term_expr->operation == ir_unop_logic_not)
|
|
{
|
|
term_expr->operands[0]->accept(this);
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
// More complex condition, print as "!(x)"
|
|
if (!handled)
|
|
{
|
|
buffer.asprintf_append("!(");
|
|
term->ir->condition->accept(this);
|
|
buffer.asprintf_append(")");
|
|
}
|
|
}
|
|
buffer.asprintf_append("; ");
|
|
|
|
// emit loop induction variable updates
|
|
bool first = true;
|
|
foreach_in_list(loop_variable, indvar, &ls->induction_variables)
|
|
{
|
|
hash_table_insert(induction_hash, indvar, indvar->first_assignment);
|
|
if (!first)
|
|
buffer.asprintf_append(", ");
|
|
visit(indvar->first_assignment);
|
|
first = false;
|
|
}
|
|
buffer.asprintf_append(") {\n");
|
|
|
|
inside_loop_body = false;
|
|
|
|
// emit loop body
|
|
indentation++; previous_skipped = false;
|
|
foreach_in_list(ir_instruction, inst, &ir->body_instructions) {
|
|
// skip termination & induction statements,
|
|
// they are part of "for" clause
|
|
if (hash_table_find(terminator_hash, inst))
|
|
continue;
|
|
if (hash_table_find(induction_hash, inst))
|
|
continue;
|
|
|
|
indent();
|
|
inst->accept(this);
|
|
end_statement_line();
|
|
}
|
|
indentation--;
|
|
|
|
indent();
|
|
buffer.asprintf_append("}");
|
|
|
|
hash_table_dtor (terminator_hash);
|
|
hash_table_dtor (induction_hash);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_loop *ir)
|
|
{
|
|
if (emit_canonical_for(ir))
|
|
return;
|
|
|
|
buffer.asprintf_append ("while (true) {\n");
|
|
indentation++; previous_skipped = false;
|
|
foreach_in_list(ir_instruction, inst, &ir->body_instructions) {
|
|
indent();
|
|
inst->accept(this);
|
|
end_statement_line();
|
|
}
|
|
indentation--;
|
|
indent();
|
|
buffer.asprintf_append ("}");
|
|
}
|
|
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_loop_jump *ir)
|
|
{
|
|
buffer.asprintf_append ("%s", ir->is_break() ? "break" : "continue");
|
|
}
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_precision_statement *ir)
|
|
{
|
|
}
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_typedecl_statement *ir)
|
|
{
|
|
const glsl_type *const s = ir->type_decl;
|
|
buffer.asprintf_append ("struct %s {\n", s->name);
|
|
|
|
for (unsigned j = 0; j < s->length; j++) {
|
|
buffer.asprintf_append (" ");
|
|
//if (state->es_shader)
|
|
// buffer.asprintf_append ("%s", get_precision_string(s->fields.structure[j].precision)); //@TODO
|
|
print_type_precision(buffer, s->fields.structure[j].type, s->fields.structure[j].precision, false);
|
|
buffer.asprintf_append (" %s", s->fields.structure[j].name);
|
|
print_type_post(buffer, s->fields.structure[j].type, false);
|
|
buffer.asprintf_append (";\n");
|
|
}
|
|
buffer.asprintf_append ("}");
|
|
}
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_emit_vertex *ir)
|
|
{
|
|
buffer.asprintf_append ("emit-vertex-TODO");
|
|
}
|
|
|
|
void
|
|
ir_print_metal_visitor::visit(ir_end_primitive *ir)
|
|
{
|
|
buffer.asprintf_append ("end-primitive-TODO");
|
|
}
|