i915: Catch cases where not all state is emitted for a new batchbuffer.
authorKeith Whitwell <keith@tungstengraphics.com>
Thu, 15 Nov 2007 09:59:33 +0000 (09:59 +0000)
committerMichel Dänzer <michel@tungstengraphics.com>
Mon, 26 Nov 2007 16:49:29 +0000 (17:49 +0100)
This could lead to incorrect rendering or even lockups.

src/mesa/drivers/dri/i915/i915_vtbl.c
src/mesa/drivers/dri/i915/intel_context.h
src/mesa/drivers/dri/i915/intel_tris.c
src/mesa/drivers/dri/intel/intel_batchbuffer.c
src/mesa/drivers/dri/intel/intel_batchbuffer.h
src/mesa/drivers/dri/intel/intel_screen.h

index 35757e17eb9c5c24ad467373fd910d298c0fdc36..5decffa1165926358c69dfb54970cc72210b18b8 100644 (file)
@@ -290,7 +290,7 @@ get_state_size(struct i915_hw_state *state)
 /* Push the state into the sarea and/or texture memory.
  */
 static void
-i915_emit_state(struct intel_context *intel)
+i915_do_emit_state(struct intel_context *intel)
 {
    struct i915_context *i915 = i915_context(&intel->ctx);
    struct i915_hw_state *state = i915->current;
@@ -307,11 +307,32 @@ i915_emit_state(struct intel_context *intel)
     */
    intel_batchbuffer_require_space(intel->batch, get_state_size(state), 0);
 
+   /* Workaround.  There are cases I haven't been able to track down
+    * where we aren't emitting a full state at the start of a new
+    * batchbuffer.  This code spots that we are on a new batchbuffer
+    * and forces a full state emit no matter what.  
+    *
+    * In the normal case state->emitted is already zero, this code is
+    * another set of checks to make sure it really is.
+    */
+   if (intel->batch->id != intel->last_state_batch_id ||
+       intel->batch->map == intel->batch->ptr) 
+   {
+      state->emitted = 0;
+      intel_batchbuffer_require_space(intel->batch, get_state_size(state), 0);
+   }
+
    /* Do this here as we may have flushed the batchbuffer above,
     * causing more state to be dirty!
     */
    dirty = get_dirty(state);
    state->emitted |= dirty;
+   assert(get_dirty(state) == 0);
+
+   if (intel->batch->id != intel->last_state_batch_id) {
+      assert(dirty & I915_UPLOAD_CTX);
+      intel->last_state_batch_id = intel->batch->id;
+   }
 
    if (INTEL_DEBUG & DEBUG_STATE)
       fprintf(stderr, "%s dirty: %x\n", __FUNCTION__, dirty);
@@ -431,9 +452,32 @@ i915_emit_state(struct intel_context *intel)
          i915_disassemble_program(state->Program, state->ProgramSize);
    }
 
+   intel->batch->dirty_state &= ~dirty;
    assert(get_dirty(state) == 0);
 }
 
+static void
+i915_emit_state(struct intel_context *intel)
+{
+   struct i915_context *i915 = i915_context(&intel->ctx);
+
+   i915_do_emit_state( intel );
+
+   /* Second chance - catch batchbuffer wrap in the middle of state
+    * emit.  This shouldn't happen but it has been observed in
+    * testing.
+    */
+   if (get_dirty( i915->current )) {
+      /* Force a full re-emit if this happens.
+       */
+      i915->current->emitted = 0;
+      i915_do_emit_state( intel );
+   }
+
+   assert(get_dirty(i915->current) == 0);
+   assert((intel->batch->dirty_state & (1<<1)) == 0);
+}
+
 static void
 i915_destroy_context(struct intel_context *intel)
 {
index 128a1a897aa694bfec7334fa6c38947ba7b79f2a..16eb6afed23b4a88bba561b4555cc7e9ea37d029 100644 (file)
@@ -145,6 +145,7 @@ struct intel_context
    dri_fence *first_swap_fence;
 
    struct intel_batchbuffer *batch;
+   GLuint last_state_batch_id;
 
    struct
    {
index 67f02cfc3a46e3f88c6f88351c81fed41574c190..4b45dc065c64ba21345d03f00abd57db9a41793f 100644 (file)
@@ -111,6 +111,9 @@ intelStartInlinePrimitive(struct intel_context *intel,
    BEGIN_BATCH(2, batch_flags);
    OUT_BATCH(0);
 
+   assert(intel->batch->id == intel->last_state_batch_id);
+   assert((intel->batch->dirty_state & (1<<1)) == 0);
+
    intel->prim.start_ptr = intel->batch->ptr;
    intel->prim.primitive = prim;
    intel->prim.flush = intel_flush_inline_primitive;
index 4e698627a369f4a6f31e644160e5080572c1a21c..3764027bff61d318148122f1d570e0fad02fb5c1 100644 (file)
@@ -85,6 +85,8 @@ intel_batchbuffer_reset(struct intel_batchbuffer *batch)
    batch->map = batch->buf->virtual;
    batch->size = intel->intelScreen->maxBatchSize;
    batch->ptr = batch->map;
+   batch->dirty_state = ~0;
+   batch->id = batch->intel->intelScreen->batch_id++;
 }
 
 struct intel_batchbuffer *
index b5c7a783a726b5fe8e75659ce8ebda8250b219f8..1bbbbde293e3a20694ec8407d44bd9cdc04e8e1a 100644 (file)
@@ -25,6 +25,9 @@ struct intel_batchbuffer
    GLubyte *ptr;
 
    GLuint size;
+
+   GLuint dirty_state;
+   GLuint id;
 };
 
 struct intel_batchbuffer *intel_batchbuffer_alloc(struct intel_context
index 3a1a969b23f297a17ad72f95cd303d2921d3a2a2..ac11431b877f49da62ae2b77cd94a244b564f9a0 100644 (file)
@@ -98,6 +98,8 @@ typedef struct
     * instead of the fake client-side memory manager.
     */
    GLboolean ttm;
+
+   unsigned batch_id;
 } intelScreenPrivate;