Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright © 2012 Intel Corporation
  3.  *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a
  5.  * copy of this software and associated documentation files (the "Software"),
  6.  * to deal in the Software without restriction, including without limitation
  7.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8.  * and/or sell copies of the Software, and to permit persons to whom the
  9.  * Software is furnished to do so, subject to the following conditions:
  10.  *
  11.  * The above copyright notice and this permission notice (including the next
  12.  * paragraph) shall be included in all copies or substantial portions of the
  13.  * Software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20.  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21.  * DEALINGS IN THE SOFTWARE.
  22.  */
  23.  
  24. /**
  25.  * \file lower_ubo_reference.cpp
  26.  *
  27.  * IR lower pass to replace dereferences of variables in a uniform
  28.  * buffer object with usage of ir_binop_ubo_load expressions, each of
  29.  * which can read data up to the size of a vec4.
  30.  *
  31.  * This relieves drivers of the responsibility to deal with tricky UBO
  32.  * layout issues like std140 structures and row_major matrices on
  33.  * their own.
  34.  */
  35.  
  36. #include "ir.h"
  37. #include "ir_builder.h"
  38. #include "ir_rvalue_visitor.h"
  39. #include "main/macros.h"
  40.  
  41. using namespace ir_builder;
  42.  
  43. namespace {
  44. class lower_ubo_reference_visitor : public ir_rvalue_enter_visitor {
  45. public:
  46.    lower_ubo_reference_visitor(struct gl_shader *shader)
  47.    : shader(shader)
  48.    {
  49.    }
  50.  
  51.    void handle_rvalue(ir_rvalue **rvalue);
  52.    void emit_ubo_loads(ir_dereference *deref, ir_variable *base_offset,
  53.                        unsigned int deref_offset);
  54.    ir_expression *ubo_load(const struct glsl_type *type,
  55.                            ir_rvalue *offset);
  56.  
  57.    void *mem_ctx;
  58.    struct gl_shader *shader;
  59.    struct gl_uniform_buffer_variable *ubo_var;
  60.    unsigned uniform_block;
  61.    bool progress;
  62. };
  63.  
  64. /**
  65.  * Determine the name of the interface block field
  66.  *
  67.  * This is the name of the specific member as it would appear in the
  68.  * \c gl_uniform_buffer_variable::Name field in the shader's
  69.  * \c UniformBlocks array.
  70.  */
  71. static const char *
  72. interface_field_name(void *mem_ctx, char *base_name, ir_dereference *d)
  73. {
  74.    ir_constant *previous_index = NULL;
  75.  
  76.    while (d != NULL) {
  77.       switch (d->ir_type) {
  78.       case ir_type_dereference_variable: {
  79.          ir_dereference_variable *v = (ir_dereference_variable *) d;
  80.          if (previous_index
  81.              && v->var->is_interface_instance()
  82.              && v->var->type->is_array())
  83.             return ralloc_asprintf(mem_ctx,
  84.                                    "%s[%d]",
  85.                                    base_name,
  86.                                    previous_index->get_uint_component(0));
  87.          else
  88.             return base_name;
  89.  
  90.          break;
  91.       }
  92.  
  93.       case ir_type_dereference_record: {
  94.          ir_dereference_record *r = (ir_dereference_record *) d;
  95.  
  96.          d = r->record->as_dereference();
  97.          break;
  98.       }
  99.  
  100.       case ir_type_dereference_array: {
  101.          ir_dereference_array *a = (ir_dereference_array *) d;
  102.  
  103.          d = a->array->as_dereference();
  104.          previous_index = a->array_index->as_constant();
  105.          break;
  106.       }
  107.  
  108.       default:
  109.          assert(!"Should not get here.");
  110.          break;
  111.       }
  112.    }
  113.  
  114.    assert(!"Should not get here.");
  115.    return NULL;
  116. }
  117.  
  118. void
  119. lower_ubo_reference_visitor::handle_rvalue(ir_rvalue **rvalue)
  120. {
  121.    if (!*rvalue)
  122.       return;
  123.  
  124.    ir_dereference *deref = (*rvalue)->as_dereference();
  125.    if (!deref)
  126.       return;
  127.  
  128.    ir_variable *var = deref->variable_referenced();
  129.    if (!var || !var->is_in_uniform_block())
  130.       return;
  131.  
  132.    mem_ctx = ralloc_parent(*rvalue);
  133.  
  134.    const char *const field_name =
  135.       interface_field_name(mem_ctx, (char *) var->interface_type->name, deref);
  136.  
  137.    this->uniform_block = -1;
  138.    for (unsigned i = 0; i < shader->NumUniformBlocks; i++) {
  139.       if (strcmp(field_name, shader->UniformBlocks[i].Name) == 0) {
  140.          this->uniform_block = i;
  141.  
  142.          struct gl_uniform_block *block = &shader->UniformBlocks[i];
  143.  
  144.          this->ubo_var = var->is_interface_instance()
  145.             ? &block->Uniforms[0] : &block->Uniforms[var->location];
  146.  
  147.          break;
  148.       }
  149.    }
  150.  
  151.    assert(this->uniform_block != (unsigned) -1);
  152.  
  153.    ir_rvalue *offset = new(mem_ctx) ir_constant(0u);
  154.    unsigned const_offset = 0;
  155.    bool row_major = ubo_var->RowMajor;
  156.  
  157.    /* Calculate the offset to the start of the region of the UBO
  158.     * dereferenced by *rvalue.  This may be a variable offset if an
  159.     * array dereference has a variable index.
  160.     */
  161.    while (deref) {
  162.       switch (deref->ir_type) {
  163.       case ir_type_dereference_variable: {
  164.          const_offset += ubo_var->Offset;
  165.          deref = NULL;
  166.          break;
  167.       }
  168.  
  169.       case ir_type_dereference_array: {
  170.          ir_dereference_array *deref_array = (ir_dereference_array *)deref;
  171.          unsigned array_stride;
  172.          if (deref_array->array->type->is_matrix() && row_major) {
  173.             /* When loading a vector out of a row major matrix, the
  174.              * step between the columns (vectors) is the size of a
  175.              * float, while the step between the rows (elements of a
  176.              * vector) is handled below in emit_ubo_loads.
  177.              */
  178.             array_stride = 4;
  179.          } else if (deref_array->type->is_interface()) {
  180.             /* We're processing an array dereference of an interface instance
  181.              * array.  The thing being dereferenced *must* be a variable
  182.              * dereference because intefaces cannot be embedded an other
  183.              * types.  In terms of calculating the offsets for the lowering
  184.              * pass, we don't care about the array index.  All elements of an
  185.              * interface instance array will have the same offsets relative to
  186.              * the base of the block that backs them.
  187.              */
  188.             assert(deref_array->array->as_dereference_variable());
  189.             deref = deref_array->array->as_dereference();
  190.             break;
  191.          } else {
  192.             array_stride = deref_array->type->std140_size(row_major);
  193.             array_stride = glsl_align(array_stride, 16);
  194.          }
  195.  
  196.          ir_constant *const_index = deref_array->array_index->as_constant();
  197.          if (const_index) {
  198.             const_offset += array_stride * const_index->value.i[0];
  199.          } else {
  200.             offset = add(offset,
  201.                          mul(deref_array->array_index,
  202.                              new(mem_ctx) ir_constant(array_stride)));
  203.          }
  204.          deref = deref_array->array->as_dereference();
  205.          break;
  206.       }
  207.  
  208.       case ir_type_dereference_record: {
  209.          ir_dereference_record *deref_record = (ir_dereference_record *)deref;
  210.          const glsl_type *struct_type = deref_record->record->type;
  211.          unsigned intra_struct_offset = 0;
  212.  
  213.          unsigned max_field_align = 16;
  214.          for (unsigned int i = 0; i < struct_type->length; i++) {
  215.             const glsl_type *type = struct_type->fields.structure[i].type;
  216.             unsigned field_align = type->std140_base_alignment(row_major);
  217.             max_field_align = MAX2(field_align, max_field_align);
  218.             intra_struct_offset = glsl_align(intra_struct_offset, field_align);
  219.  
  220.             if (strcmp(struct_type->fields.structure[i].name,
  221.                        deref_record->field) == 0)
  222.                break;
  223.             intra_struct_offset += type->std140_size(row_major);
  224.          }
  225.  
  226.          const_offset = glsl_align(const_offset, max_field_align);
  227.          const_offset += intra_struct_offset;
  228.  
  229.          deref = deref_record->record->as_dereference();
  230.          break;
  231.       }
  232.       default:
  233.          assert(!"not reached");
  234.          deref = NULL;
  235.          break;
  236.       }
  237.    }
  238.  
  239.    /* Now that we've calculated the offset to the start of the
  240.     * dereference, walk over the type and emit loads into a temporary.
  241.     */
  242.    const glsl_type *type = (*rvalue)->type;
  243.    ir_variable *load_var = new(mem_ctx) ir_variable(type,
  244.                                                     "ubo_load_temp",
  245.                                                     ir_var_temporary);
  246.    base_ir->insert_before(load_var);
  247.  
  248.    ir_variable *load_offset = new(mem_ctx) ir_variable(glsl_type::uint_type,
  249.                                                        "ubo_load_temp_offset",
  250.                                                        ir_var_temporary);
  251.    base_ir->insert_before(load_offset);
  252.    base_ir->insert_before(assign(load_offset, offset));
  253.  
  254.    deref = new(mem_ctx) ir_dereference_variable(load_var);
  255.    emit_ubo_loads(deref, load_offset, const_offset);
  256.    *rvalue = deref;
  257.  
  258.    progress = true;
  259. }
  260.  
  261. ir_expression *
  262. lower_ubo_reference_visitor::ubo_load(const glsl_type *type,
  263.                                       ir_rvalue *offset)
  264. {
  265.    return new(mem_ctx)
  266.       ir_expression(ir_binop_ubo_load,
  267.                     type,
  268.                     new(mem_ctx) ir_constant(this->uniform_block),
  269.                     offset);
  270.  
  271. }
  272.  
  273. /**
  274.  * Takes LHS and emits a series of assignments into its components
  275.  * from the UBO variable at variable_offset + deref_offset.
  276.  *
  277.  * Recursively calls itself to break the deref down to the point that
  278.  * the ir_binop_ubo_load expressions generated are contiguous scalars
  279.  * or vectors.
  280.  */
  281. void
  282. lower_ubo_reference_visitor::emit_ubo_loads(ir_dereference *deref,
  283.                                             ir_variable *base_offset,
  284.                                             unsigned int deref_offset)
  285. {
  286.    if (deref->type->is_record()) {
  287.       unsigned int field_offset = 0;
  288.  
  289.       for (unsigned i = 0; i < deref->type->length; i++) {
  290.          const struct glsl_struct_field *field =
  291.             &deref->type->fields.structure[i];
  292.          ir_dereference *field_deref =
  293.             new(mem_ctx) ir_dereference_record(deref->clone(mem_ctx, NULL),
  294.                                                field->name);
  295.  
  296.          field_offset =
  297.             glsl_align(field_offset,
  298.                        field->type->std140_base_alignment(ubo_var->RowMajor));
  299.  
  300.          emit_ubo_loads(field_deref, base_offset, deref_offset + field_offset);
  301.  
  302.          field_offset += field->type->std140_size(ubo_var->RowMajor);
  303.       }
  304.       return;
  305.    }
  306.  
  307.    if (deref->type->is_array()) {
  308.       unsigned array_stride =
  309.          glsl_align(deref->type->fields.array->std140_size(ubo_var->RowMajor),
  310.                     16);
  311.  
  312.       for (unsigned i = 0; i < deref->type->length; i++) {
  313.          ir_constant *element = new(mem_ctx) ir_constant(i);
  314.          ir_dereference *element_deref =
  315.             new(mem_ctx) ir_dereference_array(deref->clone(mem_ctx, NULL),
  316.                                               element);
  317.          emit_ubo_loads(element_deref, base_offset,
  318.                         deref_offset + i * array_stride);
  319.       }
  320.       return;
  321.    }
  322.  
  323.    if (deref->type->is_matrix()) {
  324.       for (unsigned i = 0; i < deref->type->matrix_columns; i++) {
  325.          ir_constant *col = new(mem_ctx) ir_constant(i);
  326.          ir_dereference *col_deref =
  327.             new(mem_ctx) ir_dereference_array(deref->clone(mem_ctx, NULL),
  328.                                               col);
  329.  
  330.          /* std140 always rounds the stride of arrays (and matrices)
  331.           * to a vec4, so matrices are always 16 between columns/rows.
  332.           */
  333.          emit_ubo_loads(col_deref, base_offset, deref_offset + i * 16);
  334.       }
  335.       return;
  336.    }
  337.  
  338.    assert(deref->type->is_scalar() ||
  339.           deref->type->is_vector());
  340.  
  341.    if (!ubo_var->RowMajor) {
  342.       ir_rvalue *offset = add(base_offset,
  343.                               new(mem_ctx) ir_constant(deref_offset));
  344.       base_ir->insert_before(assign(deref->clone(mem_ctx, NULL),
  345.                                     ubo_load(deref->type, offset)));
  346.    } else {
  347.       /* We're dereffing a column out of a row-major matrix, so we
  348.        * gather the vector from each stored row.
  349.       */
  350.       assert(deref->type->base_type == GLSL_TYPE_FLOAT);
  351.       /* Matrices, row_major or not, are stored as if they were
  352.        * arrays of vectors of the appropriate size in std140.
  353.        * Arrays have their strides rounded up to a vec4, so the
  354.        * matrix stride is always 16.
  355.        */
  356.       unsigned matrix_stride = 16;
  357.  
  358.       for (unsigned i = 0; i < deref->type->vector_elements; i++) {
  359.          ir_rvalue *chan_offset =
  360.             add(base_offset,
  361.                 new(mem_ctx) ir_constant(deref_offset + i * matrix_stride));
  362.  
  363.          base_ir->insert_before(assign(deref->clone(mem_ctx, NULL),
  364.                                        ubo_load(glsl_type::float_type,
  365.                                                 chan_offset),
  366.                                        (1U << i)));
  367.       }
  368.    }
  369. }
  370.  
  371. } /* unnamed namespace */
  372.  
  373. void
  374. lower_ubo_reference(struct gl_shader *shader, exec_list *instructions)
  375. {
  376.    lower_ubo_reference_visitor v(shader);
  377.  
  378.    /* Loop over the instructions lowering references, because we take
  379.     * a deref of a UBO array using a UBO dereference as the index will
  380.     * produce a collection of instructions all of which have cloned
  381.     * UBO dereferences for that array index.
  382.     */
  383.    do {
  384.       v.progress = false;
  385.       visit_list_elements(&v, instructions);
  386.    } while (v.progress);
  387. }
  388.