i915: Drop broken front_buffer_reading/drawing optimization
[mesa.git] / src / mesa / drivers / dri / i915 / i915_program.c
index c6c6434023223eb622ea3bb1b7a34a3ec2389299..64b0b4ddb03dab613afcb428ac9accadcc363b91 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  * 
- * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2003 VMware, Inc.
  * All Rights Reserved.
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -18,7 +18,7 @@
  * 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 NON-INFRINGEMENT.
- * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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.
@@ -27,9 +27,9 @@
 
 #include <strings.h>
 
-#include "glheader.h"
-#include "macros.h"
-#include "enums.h"
+#include "main/glheader.h"
+#include "main/macros.h"
+#include "main/enums.h"
 
 #include "tnl/t_context.h"
 #include "intel_batchbuffer.h"
@@ -130,6 +130,7 @@ i915_emit_decl(struct i915_fragment_program *p,
    *(p->decl++) = (D0_DCL | D0_DEST(reg) | d0_flags);
    *(p->decl++) = D1_MBZ;
    *(p->decl++) = D2_MBZ;
+   assert(p->decl <= p->declarations + ARRAY_SIZE(p->declarations));
 
    p->nr_decl_insn++;
    return reg;
@@ -186,44 +187,98 @@ i915_emit_arith(struct i915_fragment_program * p,
       p->utemp_flag = old_utemp_flag;   /* restore */
    }
 
+   if (p->csr >= p->program + ARRAY_SIZE(p->program)) {
+      i915_program_error(p, "Program contains too many instructions");
+      return UREG_BAD;
+   }
+
    *(p->csr++) = (op | A0_DEST(dest) | mask | saturate | A0_SRC0(src0));
    *(p->csr++) = (A1_SRC0(src0) | A1_SRC1(src1));
    *(p->csr++) = (A2_SRC1(src1) | A2_SRC2(src2));
 
+   if (GET_UREG_TYPE(dest) == REG_TYPE_R)
+      p->register_phases[GET_UREG_NR(dest)] = p->nr_tex_indirect;
+
    p->nr_alu_insn++;
    return dest;
 }
 
+static GLuint get_free_rreg (struct i915_fragment_program *p, 
+                             GLuint live_regs)
+{
+    int bit = ffs(~live_regs);
+    if (!bit) {
+        i915_program_error(p, "Can't find free R reg");
+        return UREG_BAD;
+    }
+    return UREG(REG_TYPE_R, bit - 1);
+}
+
 GLuint i915_emit_texld( struct i915_fragment_program *p,
+                       GLuint live_regs,               
                        GLuint dest,
                        GLuint destmask,
                        GLuint sampler,
                        GLuint coord,
                        GLuint op )
 {
-   if (coord != UREG(GET_UREG_TYPE(coord), GET_UREG_NR(coord))) {
-      /* No real way to work around this in the general case - need to
-       * allocate and declare a new temporary register (a utemp won't
-       * do).  Will fallback for now.
-       */
-      i915_program_error(p, "Can't (yet) swizzle TEX arguments");
-      return 0;
-   }
-
-   /* Don't worry about saturate as we only support  
+    if (coord != UREG(GET_UREG_TYPE(coord), GET_UREG_NR(coord))) {
+        /* With the help of the "needed registers" table created earlier, pick
+         * a register we can MOV the swizzled TC to (since TEX doesn't support
+         * swizzled sources) */
+        GLuint swizCoord = get_free_rreg(p, live_regs);
+        if (swizCoord == UREG_BAD) 
+            return 0;
+
+        i915_emit_arith( p, A0_MOV, swizCoord, A0_DEST_CHANNEL_ALL, 0, coord, 0, 0 );
+        coord = swizCoord;
+    }
+
+   /* Don't worry about saturate as we only support texture formats
+    * that are always in the 0..1 range.
     */
    if (destmask != A0_DEST_CHANNEL_ALL) {
       GLuint tmp = i915_get_utemp(p);
-      i915_emit_texld( p, tmp, A0_DEST_CHANNEL_ALL, sampler, coord, op );
+      i915_emit_texld( p, 0, tmp, A0_DEST_CHANNEL_ALL, sampler, coord, op );
       i915_emit_arith( p, A0_MOV, dest, destmask, 0, tmp, 0, 0 );
       return dest;
    }
    else {
       assert(GET_UREG_TYPE(dest) != REG_TYPE_CONST);
-      assert(dest = UREG(GET_UREG_TYPE(dest), GET_UREG_NR(dest)));
+      assert(dest == UREG(GET_UREG_TYPE(dest), GET_UREG_NR(dest)));
+      /* Can't use unsaved temps for coords, as the phase boundary would result
+       * in the contents becoming undefined.
+       */
+      assert(GET_UREG_TYPE(coord) != REG_TYPE_U);
+
+      if ((GET_UREG_TYPE(coord) != REG_TYPE_R) &&
+          (GET_UREG_TYPE(coord) != REG_TYPE_OC) &&
+          (GET_UREG_TYPE(coord) != REG_TYPE_OD) &&
+          (GET_UREG_TYPE(coord) != REG_TYPE_T)) {
+          GLuint  tmpCoord = get_free_rreg(p, live_regs);
+          
+          if (tmpCoord == UREG_BAD) 
+              return 0;
+
+          i915_emit_arith(p, A0_MOV, tmpCoord, A0_DEST_CHANNEL_ALL, 0, coord, 0, 0);
+          coord = tmpCoord;
+      }
 
-      if (GET_UREG_TYPE(coord) != REG_TYPE_T) {
+      /* Output register being oC or oD defines a phase boundary */
+      if (GET_UREG_TYPE(dest) == REG_TYPE_OC ||
+         GET_UREG_TYPE(dest) == REG_TYPE_OD)
         p->nr_tex_indirect++;
+
+      /* Reading from an r# register whose contents depend on output of the
+       * current phase defines a phase boundary.
+       */
+      if (GET_UREG_TYPE(coord) == REG_TYPE_R &&
+         p->register_phases[GET_UREG_NR(coord)] == p->nr_tex_indirect)
+        p->nr_tex_indirect++;
+
+      if (p->csr >= p->program + ARRAY_SIZE(p->program)) {
+        i915_program_error(p, "Program contains too many instructions");
+        return UREG_BAD;
       }
 
       *(p->csr++) = (op | 
@@ -233,6 +288,9 @@ GLuint i915_emit_texld( struct i915_fragment_program *p,
       *(p->csr++) = T1_ADDRESS_REG( coord );
       *(p->csr++) = T2_MBZ;
 
+      if (GET_UREG_TYPE(dest) == REG_TYPE_R)
+        p->register_phases[GET_UREG_NR(dest)] = p->nr_tex_indirect;
+
       p->nr_tex_insn++;
       return dest;
    }
@@ -264,7 +322,7 @@ i915_emit_const1f(struct i915_fragment_program * p, GLfloat c0)
       }
    }
 
-   fprintf(stderr, "%s: out of constants\n", __FUNCTION__);
+   fprintf(stderr, "%s: out of constants\n", __func__);
    p->error = 1;
    return 0;
 }
@@ -301,7 +359,7 @@ i915_emit_const2f(struct i915_fragment_program * p, GLfloat c0, GLfloat c1)
       }
    }
 
-   fprintf(stderr, "%s: out of constants\n", __FUNCTION__);
+   fprintf(stderr, "%s: out of constants\n", __func__);
    p->error = 1;
    return 0;
 }
@@ -333,7 +391,7 @@ i915_emit_const4f(struct i915_fragment_program * p,
       }
    }
 
-   fprintf(stderr, "%s: out of constants\n", __FUNCTION__);
+   fprintf(stderr, "%s: out of constants\n", __func__);
    p->error = 1;
    return 0;
 }
@@ -372,17 +430,28 @@ i915_emit_param4fv(struct i915_fragment_program * p, const GLfloat * values)
       }
    }
 
-   fprintf(stderr, "%s: out of constants\n", __FUNCTION__);
+   fprintf(stderr, "%s: out of constants\n", __func__);
    p->error = 1;
    return 0;
 }
 
-
-
+/* Warning the user about program errors seems to be quite valuable, from
+ * our bug reports.  It unfortunately means piglit reporting errors
+ * when we fall back to software due to an unsupportable program, though.
+ */
 void
-i915_program_error(struct i915_fragment_program *p, const char *msg)
+i915_program_error(struct i915_fragment_program *p, const char *fmt, ...)
 {
-   _mesa_problem(NULL, "i915_program_error: %s", msg);
+   if (unlikely((INTEL_DEBUG & (DEBUG_WM | DEBUG_PERF)) != 0)) {
+      va_list args;
+
+      fprintf(stderr, "i915_program_error: ");
+      va_start(args, fmt);
+      vfprintf(stderr, fmt, args);
+      va_end(args);
+
+      fprintf(stderr, "\n");
+   }
    p->error = 1;
 }
 
@@ -390,14 +459,15 @@ i915_program_error(struct i915_fragment_program *p, const char *msg)
 void
 i915_init_program(struct i915_context *i915, struct i915_fragment_program *p)
 {
-   GLcontext *ctx = &i915->intel.ctx;
+   struct gl_context *ctx = &i915->intel.ctx;
 
    p->translated = 0;
    p->params_uptodate = 0;
    p->on_hardware = 0;
    p->error = 0;
 
-   p->nr_tex_indirect = 1;      /* correct? */
+   memset(&p->register_phases, 0, sizeof(p->register_phases));
+   p->nr_tex_indirect = 1;
    p->nr_tex_insn = 0;
    p->nr_alu_insn = 0;
    p->nr_decl_insn = 0;
@@ -426,17 +496,25 @@ i915_fini_program(struct i915_fragment_program *p)
    GLuint program_size = p->csr - p->program;
    GLuint decl_size = p->decl - p->declarations;
 
-   if (p->nr_tex_indirect > I915_MAX_TEX_INDIRECT)
-      i915_program_error(p, "Exceeded max nr indirect texture lookups");
+   if (p->nr_tex_indirect > I915_MAX_TEX_INDIRECT) {
+      i915_program_error(p, "Exceeded max nr indirect texture lookups "
+                        "(%d out of %d)",
+                        p->nr_tex_indirect, I915_MAX_TEX_INDIRECT);
+   }
 
-   if (p->nr_tex_insn > I915_MAX_TEX_INSN)
-      i915_program_error(p, "Exceeded max TEX instructions");
+   if (p->nr_tex_insn > I915_MAX_TEX_INSN) {
+      i915_program_error(p, "Exceeded max TEX instructions (%d out of %d)",
+                        p->nr_tex_insn, I915_MAX_TEX_INSN);
+   }
 
    if (p->nr_alu_insn > I915_MAX_ALU_INSN)
-      i915_program_error(p, "Exceeded max ALU instructions");
+      i915_program_error(p, "Exceeded max ALU instructions (%d out of %d)",
+                        p->nr_alu_insn, I915_MAX_ALU_INSN);
 
-   if (p->nr_decl_insn > I915_MAX_DECL_INSN)
-      i915_program_error(p, "Exceeded max DECL instructions");
+   if (p->nr_decl_insn > I915_MAX_DECL_INSN) {
+      i915_program_error(p, "Exceeded max DECL instructions (%d out of %d)",
+                        p->nr_decl_insn, I915_MAX_DECL_INSN);
+   }
 
    if (p->error) {
       p->FragProg.Base.NumNativeInstructions = 0;
@@ -463,7 +541,8 @@ i915_upload_program(struct i915_context *i915,
    GLuint program_size = p->csr - p->program;
    GLuint decl_size = p->decl - p->declarations;
 
-   FALLBACK(&i915->intel, I915_FALLBACK_PROGRAM, p->error);
+   if (p->error)
+      return;
 
    /* Could just go straight to the batchbuffer from here:
     */