#include "glsl_symbol_table.h"
#include "ir.h"
#include "ir_optimization.h"
namespace {
class lower_packed_varyings_visitor
{
public:
lower_packed_varyings_visitor(void *mem_ctx, unsigned locations_used,
ir_variable_mode mode,
unsigned gs_input_vertices,
exec_list *out_instructions);
void run(exec_list *instructions);
private:
ir_assignment *bitwise_assign_pack(ir_rvalue *lhs, ir_rvalue *rhs);
ir_assignment *bitwise_assign_unpack(ir_rvalue *lhs, ir_rvalue *rhs);
unsigned lower_rvalue(ir_rvalue *rvalue, unsigned fine_location,
ir_variable *unpacked_var, const char *name,
bool gs_input_toplevel, unsigned vertex_index);
unsigned lower_arraylike(ir_rvalue *rvalue, unsigned array_size,
unsigned fine_location,
ir_variable *unpacked_var, const char *name,
bool gs_input_toplevel, unsigned vertex_index);
ir_dereference *get_packed_varying_deref(unsigned location,
ir_variable *unpacked_var,
const char *name,
unsigned vertex_index);
bool needs_lowering(ir_variable *var);
void * const mem_ctx;
const unsigned locations_used;
ir_variable **packed_varyings;
const ir_variable_mode mode;
const unsigned gs_input_vertices;
exec_list *out_instructions;
};
}
lower_packed_varyings_visitor::lower_packed_varyings_visitor(
void *mem_ctx, unsigned locations_used, ir_variable_mode mode,
unsigned gs_input_vertices, exec_list *out_instructions)
: mem_ctx(mem_ctx),
locations_used(locations_used),
packed_varyings((ir_variable **)
rzalloc_array_size(mem_ctx, sizeof(*packed_varyings),
locations_used)),
mode(mode),
gs_input_vertices(gs_input_vertices),
out_instructions(out_instructions)
{
}
void
lower_packed_varyings_visitor::run(exec_list *instructions)
{
foreach_in_list(ir_instruction, node, instructions) {
ir_variable *var = node->as_variable();
if (var == NULL)
continue;
if (var->data.mode != this->mode ||
var->data.location < VARYING_SLOT_VAR0 ||
!this->needs_lowering(var))
continue;
assert(var->data.interpolation == INTERP_QUALIFIER_FLAT ||
!var->type->contains_integer());
assert(var->data.mode != ir_var_temporary);
var->data.mode = ir_var_auto;
ir_dereference_variable *deref
= new(this->mem_ctx) ir_dereference_variable(var);
this->lower_rvalue(deref, var->data.location * 4 + var->data.location_frac, var,
var->name, this->gs_input_vertices != 0, 0);
}
}
ir_assignment *
lower_packed_varyings_visitor::bitwise_assign_pack(ir_rvalue *lhs,
ir_rvalue *rhs)
{
if (lhs->type->base_type != rhs->type->base_type) {
assert(lhs->type->base_type == GLSL_TYPE_INT);
switch (rhs->type->base_type) {
case GLSL_TYPE_UINT:
rhs = new(this->mem_ctx)
ir_expression(ir_unop_u2i, lhs->type, rhs);
break;
case GLSL_TYPE_FLOAT:
rhs = new(this->mem_ctx)
ir_expression(ir_unop_bitcast_f2i, lhs->type, rhs);
break;
default:
assert(!"Unexpected type conversion while lowering varyings");
break;
}
}
return new(this->mem_ctx) ir_assignment(lhs, rhs);
}
ir_assignment *
lower_packed_varyings_visitor::bitwise_assign_unpack(ir_rvalue *lhs,
ir_rvalue *rhs)
{
if (lhs->type->base_type != rhs->type->base_type) {
assert(rhs->type->base_type == GLSL_TYPE_INT);
switch (lhs->type->base_type) {
case GLSL_TYPE_UINT:
rhs = new(this->mem_ctx)
ir_expression(ir_unop_i2u, lhs->type, rhs);
break;
case GLSL_TYPE_FLOAT:
rhs = new(this->mem_ctx)
ir_expression(ir_unop_bitcast_i2f, lhs->type, rhs);
break;
default:
assert(!"Unexpected type conversion while lowering varyings");
break;
}
}
return new(this->mem_ctx) ir_assignment(lhs, rhs);
}
unsigned
lower_packed_varyings_visitor::lower_rvalue(ir_rvalue *rvalue,
unsigned fine_location,
ir_variable *unpacked_var,
const char *name,
bool gs_input_toplevel,
unsigned vertex_index)
{
assert(!gs_input_toplevel || rvalue->type->is_array());
if (rvalue->type->is_record()) {
for (unsigned i = 0; i < rvalue->type->length; i++) {
if (i != 0)
rvalue = rvalue->clone(this->mem_ctx, NULL);
const char *field_name = rvalue->type->fields.structure[i].name;
ir_dereference_record *dereference_record = new(this->mem_ctx)
ir_dereference_record(rvalue, field_name);
char *deref_name
= ralloc_asprintf(this->mem_ctx, "%s.%s", name, field_name);
fine_location = this->lower_rvalue(dereference_record, fine_location,
unpacked_var, deref_name, false,
vertex_index);
}
return fine_location;
} else if (rvalue->type->is_array()) {
return this->lower_arraylike(rvalue, rvalue->type->array_size(),
fine_location, unpacked_var, name,
gs_input_toplevel, vertex_index);
} else if (rvalue->type->is_matrix()) {
return this->lower_arraylike(rvalue, rvalue->type->matrix_columns,
fine_location, unpacked_var, name,
false, vertex_index);
} else if (rvalue->type->vector_elements + fine_location % 4 > 4) {
unsigned left_components = 4 - fine_location % 4;
unsigned right_components
= rvalue->type->vector_elements - left_components;
unsigned left_swizzle_values[4] = { 0, 0, 0, 0 };
unsigned right_swizzle_values[4] = { 0, 0, 0, 0 };
char left_swizzle_name[4] = { 0, 0, 0, 0 };
char right_swizzle_name[4] = { 0, 0, 0, 0 };
for (unsigned i = 0; i < left_components; i++) {
left_swizzle_values[i] = i;
left_swizzle_name[i] = "xyzw"[i];
}
for (unsigned i = 0; i < right_components; i++) {
right_swizzle_values[i] = i + left_components;
right_swizzle_name[i] = "xyzw"[i + left_components];
}
ir_swizzle *left_swizzle = new(this->mem_ctx)
ir_swizzle(rvalue, left_swizzle_values, left_components);
ir_swizzle *right_swizzle = new(this->mem_ctx)
ir_swizzle(rvalue->clone(this->mem_ctx, NULL), right_swizzle_values,
right_components);
char *left_name
= ralloc_asprintf(this->mem_ctx, "%s.%s", name, left_swizzle_name);
char *right_name
= ralloc_asprintf(this->mem_ctx, "%s.%s", name, right_swizzle_name);
fine_location = this->lower_rvalue(left_swizzle, fine_location,
unpacked_var, left_name, false,
vertex_index);
return this->lower_rvalue(right_swizzle, fine_location, unpacked_var,
right_name, false, vertex_index);
} else {
unsigned swizzle_values[4] = { 0, 0, 0, 0 };
unsigned components = rvalue->type->vector_elements;
unsigned location = fine_location / 4;
unsigned location_frac = fine_location % 4;
for (unsigned i = 0; i < components; ++i)
swizzle_values[i] = i + location_frac;
ir_dereference *packed_deref =
this->get_packed_varying_deref(location, unpacked_var, name,
vertex_index);
ir_swizzle *swizzle = new(this->mem_ctx)
ir_swizzle(packed_deref, swizzle_values, components);
if (this->mode == ir_var_shader_out) {
ir_assignment *assignment
= this->bitwise_assign_pack(swizzle, rvalue);
this->out_instructions->push_tail(assignment);
} else {
ir_assignment *assignment
= this->bitwise_assign_unpack(rvalue, swizzle);
this->out_instructions->push_tail(assignment);
}
return fine_location + components;
}
}
unsigned
lower_packed_varyings_visitor::lower_arraylike(ir_rvalue *rvalue,
unsigned array_size,
unsigned fine_location,
ir_variable *unpacked_var,
const char *name,
bool gs_input_toplevel,
unsigned vertex_index)
{
for (unsigned i = 0; i < array_size; i++) {
if (i != 0)
rvalue = rvalue->clone(this->mem_ctx, NULL);
ir_constant *constant = new(this->mem_ctx) ir_constant(i);
ir_dereference_array *dereference_array = new(this->mem_ctx)
ir_dereference_array(rvalue, constant);
if (gs_input_toplevel) {
(void) this->lower_rvalue(dereference_array, fine_location,
unpacked_var, name, false, i);
} else {
char *subscripted_name
= ralloc_asprintf(this->mem_ctx, "%s[%d]", name, i);
fine_location =
this->lower_rvalue(dereference_array, fine_location,
unpacked_var, subscripted_name,
false, vertex_index);
}
}
return fine_location;
}
ir_dereference *
lower_packed_varyings_visitor::get_packed_varying_deref(
unsigned location, ir_variable *unpacked_var, const char *name,
unsigned vertex_index)
{
unsigned slot = location - VARYING_SLOT_VAR0;
assert(slot < locations_used);
if (this->packed_varyings[slot] == NULL) {
char *packed_name = ralloc_asprintf(this->mem_ctx, "packed:%s", name);
const glsl_type *packed_type;
if (unpacked_var->data.interpolation == INTERP_QUALIFIER_FLAT)
packed_type = glsl_type::ivec4_type;
else
packed_type = glsl_type::vec4_type;
if (this->gs_input_vertices != 0) {
packed_type =
glsl_type::get_array_instance(packed_type,
this->gs_input_vertices);
}
ir_variable *packed_var = new(this->mem_ctx)
ir_variable(packed_type, packed_name, this->mode, (glsl_precision)unpacked_var->data.precision);
if (this->gs_input_vertices != 0) {
packed_var->data.max_array_access = this->gs_input_vertices - 1;
}
packed_var->data.centroid = unpacked_var->data.centroid;
packed_var->data.sample = unpacked_var->data.sample;
packed_var->data.interpolation = unpacked_var->data.interpolation;
packed_var->data.location = location;
unpacked_var->insert_before(packed_var);
this->packed_varyings[slot] = packed_var;
} else {
if (this->gs_input_vertices == 0 || vertex_index == 0) {
ralloc_asprintf_append((char **) &this->packed_varyings[slot]->name,
",%s", name);
}
}
ir_dereference *deref = new(this->mem_ctx)
ir_dereference_variable(this->packed_varyings[slot]);
if (this->gs_input_vertices != 0) {
ir_constant *constant = new(this->mem_ctx) ir_constant(vertex_index);
deref = new(this->mem_ctx) ir_dereference_array(deref, constant);
}
return deref;
}
bool
lower_packed_varyings_visitor::needs_lowering(ir_variable *var)
{
if (var->data.explicit_location)
return false;
const glsl_type *type = var->type;
if (this->gs_input_vertices != 0) {
assert(type->is_array());
type = type->element_type();
}
if (type->is_array())
type = type->fields.array;
if (type->vector_elements == 4)
return false;
return true;
}
class lower_packed_varyings_gs_splicer : public ir_hierarchical_visitor
{
public:
explicit lower_packed_varyings_gs_splicer(void *mem_ctx,
const exec_list *instructions);
virtual ir_visitor_status visit_leave(ir_emit_vertex *ev);
private:
void * const mem_ctx;
const exec_list *instructions;
};
lower_packed_varyings_gs_splicer::lower_packed_varyings_gs_splicer(
void *mem_ctx, const exec_list *instructions)
: mem_ctx(mem_ctx), instructions(instructions)
{
}
ir_visitor_status
lower_packed_varyings_gs_splicer::visit_leave(ir_emit_vertex *ev)
{
foreach_in_list(ir_instruction, ir, this->instructions) {
ev->insert_before(ir->clone(this->mem_ctx, NULL));
}
return visit_continue;
}
void
lower_packed_varyings(void *mem_ctx, unsigned locations_used,
ir_variable_mode mode, unsigned gs_input_vertices,
gl_shader *shader)
{
exec_list *instructions = shader->ir;
ir_function *main_func = shader->symbols->get_function("main");
exec_list void_parameters;
ir_function_signature *main_func_sig
= main_func->matching_signature(NULL, &void_parameters, false);
exec_list new_instructions;
lower_packed_varyings_visitor visitor(mem_ctx, locations_used, mode,
gs_input_vertices, &new_instructions);
visitor.run(instructions);
if (mode == ir_var_shader_out) {
if (shader->Stage == MESA_SHADER_GEOMETRY) {
lower_packed_varyings_gs_splicer splicer(mem_ctx, &new_instructions);
splicer.run(instructions);
} else {
main_func_sig->body.append_list(&new_instructions);
}
} else {
main_func_sig->body.head->insert_before(&new_instructions);
}
}