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 brw_lower_texture_gradients.cpp
26 */
27
28#include "glsl/ir.h"
29#include "glsl/ir_builder.h"
30#include "program/prog_instruction.h"
31
32using namespace ir_builder;
33
34class lower_texture_grad_visitor : public ir_hierarchical_visitor {
35public:
36   lower_texture_grad_visitor()
37   {
38      progress = false;
39   }
40
41   ir_visitor_status visit_leave(ir_texture *ir);
42
43
44   bool progress;
45
46private:
47   void emit(ir_variable *, ir_rvalue *);
48};
49
50/**
51 * Emit a variable declaration and an assignment to initialize it.
52 */
53void
54lower_texture_grad_visitor::emit(ir_variable *var, ir_rvalue *value)
55{
56   base_ir->insert_before(var);
57   base_ir->insert_before(assign(var, value));
58}
59
60static const glsl_type *
61txs_type(const glsl_type *type)
62{
63   unsigned dims;
64   switch (type->sampler_dimensionality) {
65   case GLSL_SAMPLER_DIM_1D:
66      dims = 1;
67      break;
68   case GLSL_SAMPLER_DIM_2D:
69   case GLSL_SAMPLER_DIM_RECT:
70   case GLSL_SAMPLER_DIM_CUBE:
71      dims = 2;
72      break;
73   case GLSL_SAMPLER_DIM_3D:
74      dims = 3;
75      break;
76   default:
77      assert(!"Should not get here: invalid sampler dimensionality");
78   }
79
80   if (type->sampler_array)
81      dims++;
82
83   return glsl_type::get_instance(GLSL_TYPE_INT, dims, 1);
84}
85
86ir_visitor_status
87lower_texture_grad_visitor::visit_leave(ir_texture *ir)
88{
89   /* Only lower textureGrad with shadow samplers */
90   if (ir->op != ir_txd || !ir->shadow_comparitor)
91      return visit_continue;
92
93   void *mem_ctx = ralloc_parent(ir);
94
95   const glsl_type *grad_type = ir->lod_info.grad.dPdx->type;
96
97   /* Use textureSize() to get the width and height of LOD 0; swizzle away
98    * the depth/number of array slices.
99    */
100   ir_texture *txs = new(mem_ctx) ir_texture(ir_txs);
101   txs->set_sampler(ir->sampler->clone(mem_ctx, NULL),
102		    txs_type(ir->sampler->type));
103   txs->lod_info.lod = new(mem_ctx) ir_constant(0);
104   ir_variable *size =
105      new(mem_ctx) ir_variable(grad_type, "size", ir_var_temporary);
106   if (ir->sampler->type->sampler_dimensionality == GLSL_SAMPLER_DIM_CUBE) {
107      base_ir->insert_before(size);
108      base_ir->insert_before(assign(size, expr(ir_unop_i2f, txs), WRITEMASK_XY));
109      base_ir->insert_before(assign(size, new(mem_ctx) ir_constant(1.0f), WRITEMASK_Z));
110   } else {
111      emit(size, expr(ir_unop_i2f,
112                      swizzle_for_size(txs, grad_type->vector_elements)));
113   }
114
115   /* Scale the gradients by width and height.  Effectively, the incoming
116    * gradients are s'(x,y), t'(x,y), and r'(x,y) from equation 3.19 in the
117    * GL 3.0 spec; we want u'(x,y), which is w_t * s'(x,y).
118    */
119   ir_variable *dPdx =
120      new(mem_ctx) ir_variable(grad_type, "dPdx", ir_var_temporary);
121   emit(dPdx, mul(size, ir->lod_info.grad.dPdx));
122
123   ir_variable *dPdy =
124      new(mem_ctx) ir_variable(grad_type, "dPdy", ir_var_temporary);
125   emit(dPdy, mul(size, ir->lod_info.grad.dPdy));
126
127   /* Calculate rho from equation 3.20 of the GL 3.0 specification. */
128   ir_rvalue *rho;
129   if (dPdx->type->is_scalar()) {
130      rho = expr(ir_binop_max, expr(ir_unop_abs, dPdx),
131			       expr(ir_unop_abs, dPdy));
132   } else {
133      rho = expr(ir_binop_max, expr(ir_unop_sqrt, dot(dPdx, dPdx)),
134			       expr(ir_unop_sqrt, dot(dPdy, dPdy)));
135   }
136
137   /* lambda_base = log2(rho).  We're ignoring GL state biases for now. */
138   ir->op = ir_txl;
139   ir->lod_info.lod = expr(ir_unop_log2, rho);
140
141   progress = true;
142   return visit_continue;
143}
144
145extern "C" {
146
147bool
148brw_lower_texture_gradients(struct exec_list *instructions)
149{
150   lower_texture_grad_visitor v;
151
152   visit_list_elements(&v, instructions);
153
154   return v.progress;
155}
156
157}
158