gallium: add a cap for VIEWPORT_SUBPIXEL_BITS (v2)
[mesa.git] / src / gallium / drivers / nouveau / nvc0 / nvc0_state_validate.c
index 25a3232b48d984e9f89771d44e644086c405e65a..7ccb601193f77252beff887c790e04f258308648 100644 (file)
@@ -1,8 +1,9 @@
 
+#include "util/u_format.h"
+#include "util/u_framebuffer.h"
 #include "util/u_math.h"
 
 #include "nvc0/nvc0_context.h"
-#include "nv50/nv50_defs.xml.h"
 
 #if 0
 static void
@@ -55,16 +56,19 @@ nvc0_validate_zcull(struct nvc0_context *nvc0)
 }
 #endif
 
-static INLINE void
-nvc0_fb_set_null_rt(struct nouveau_pushbuf *push, unsigned i)
+static inline void
+nvc0_fb_set_null_rt(struct nouveau_pushbuf *push, unsigned i, unsigned layers)
 {
-   BEGIN_NVC0(push, NVC0_3D(RT_ADDRESS_HIGH(i)), 6);
+   BEGIN_NVC0(push, NVC0_3D(RT_ADDRESS_HIGH(i)), 9);
    PUSH_DATA (push, 0);
    PUSH_DATA (push, 0);
-   PUSH_DATA (push, 64);
-   PUSH_DATA (push, 0);
-   PUSH_DATA (push, NV50_SURFACE_FORMAT_NONE);
-   PUSH_DATA (push, 0);
+   PUSH_DATA (push, 64);     // width
+   PUSH_DATA (push, 0);      // height
+   PUSH_DATA (push, 0);      // format
+   PUSH_DATA (push, 0);      // tile mode
+   PUSH_DATA (push, layers); // layers
+   PUSH_DATA (push, 0);      // layer stride
+   PUSH_DATA (push, 0);      // base layer
 }
 
 static void
@@ -72,14 +76,14 @@ nvc0_validate_fb(struct nvc0_context *nvc0)
 {
     struct nouveau_pushbuf *push = nvc0->base.pushbuf;
     struct pipe_framebuffer_state *fb = &nvc0->framebuffer;
+    struct nvc0_screen *screen = nvc0->screen;
     unsigned i, ms;
     unsigned ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS1;
-    boolean serialize = FALSE;
+    unsigned nr_cbufs = fb->nr_cbufs;
+    bool serialize = false;
 
-    nouveau_bufctx_reset(nvc0->bufctx_3d, NVC0_BIND_FB);
+    nouveau_bufctx_reset(nvc0->bufctx_3d, NVC0_BIND_3D_FB);
 
-    BEGIN_NVC0(push, NVC0_3D(RT_CONTROL), 1);
-    PUSH_DATA (push, (076543210 << 4) | fb->nr_cbufs);
     BEGIN_NVC0(push, NVC0_3D(SCREEN_SCISSOR_HORIZ), 2);
     PUSH_DATA (push, fb->width << 16);
     PUSH_DATA (push, fb->height << 16);
@@ -90,7 +94,7 @@ nvc0_validate_fb(struct nvc0_context *nvc0)
         struct nouveau_bo *bo;
 
         if (!fb->cbufs[i]) {
-           nvc0_fb_set_null_rt(push, i);
+           nvc0_fb_set_null_rt(push, i, 0);
            continue;
         }
 
@@ -136,12 +140,12 @@ nvc0_validate_fb(struct nvc0_context *nvc0)
         }
 
         if (res->status & NOUVEAU_BUFFER_STATUS_GPU_READING)
-           serialize = TRUE;
+           serialize = true;
         res->status |=  NOUVEAU_BUFFER_STATUS_GPU_WRITING;
         res->status &= ~NOUVEAU_BUFFER_STATUS_GPU_READING;
 
         /* only register for writing, otherwise we'd always serialize here */
-        BCTX_REFN(nvc0->bufctx_3d, FB, res, WR);
+        BCTX_REFN(nvc0->bufctx_3d, 3D_FB, res, WR);
     }
 
     if (fb->zsbuf) {
@@ -168,25 +172,38 @@ nvc0_validate_fb(struct nvc0_context *nvc0)
         ms_mode = mt->ms_mode;
 
         if (mt->base.status & NOUVEAU_BUFFER_STATUS_GPU_READING)
-           serialize = TRUE;
+           serialize = true;
         mt->base.status |=  NOUVEAU_BUFFER_STATUS_GPU_WRITING;
         mt->base.status &= ~NOUVEAU_BUFFER_STATUS_GPU_READING;
 
-        BCTX_REFN(nvc0->bufctx_3d, FB, &mt->base, WR);
+        BCTX_REFN(nvc0->bufctx_3d, 3D_FB, &mt->base, WR);
     } else {
         BEGIN_NVC0(push, NVC0_3D(ZETA_ENABLE), 1);
         PUSH_DATA (push, 0);
     }
 
+    if (nr_cbufs == 0 && !fb->zsbuf) {
+       assert(util_is_power_of_two(fb->samples));
+       assert(fb->samples <= 8);
+
+       nvc0_fb_set_null_rt(push, 0, fb->layers);
+
+       if (fb->samples > 1)
+          ms_mode = ffs(fb->samples) - 1;
+       nr_cbufs = 1;
+    }
+
+    BEGIN_NVC0(push, NVC0_3D(RT_CONTROL), 1);
+    PUSH_DATA (push, (076543210 << 4) | nr_cbufs);
     IMMED_NVC0(push, NVC0_3D(MULTISAMPLE_MODE), ms_mode);
 
     ms = 1 << ms_mode;
     BEGIN_NVC0(push, NVC0_3D(CB_SIZE), 3);
-    PUSH_DATA (push, 512);
-    PUSH_DATAh(push, nvc0->screen->uniform_bo->offset + (5 << 16) + (4 << 9));
-    PUSH_DATA (push, nvc0->screen->uniform_bo->offset + (5 << 16) + (4 << 9));
+    PUSH_DATA (push, NVC0_CB_AUX_SIZE);
+    PUSH_DATAh(push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(4));
+    PUSH_DATA (push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(4));
     BEGIN_1IC0(push, NVC0_3D(CB_POS), 1 + 2 * ms);
-    PUSH_DATA (push, 256 + 128);
+    PUSH_DATA (push, NVC0_CB_AUX_SAMPLE_INFO);
     for (i = 0; i < ms; i++) {
        float xy[2];
        nvc0->base.pipe.get_sample_position(&nvc0->base.pipe, ms, i, xy);
@@ -239,7 +256,7 @@ nvc0_validate_scissor(struct nvc0_context *nvc0)
    int i;
    struct nouveau_pushbuf *push = nvc0->base.pushbuf;
 
-   if (!(nvc0->dirty & NVC0_NEW_SCISSOR) &&
+   if (!(nvc0->dirty_3d & NVC0_NEW_3D_SCISSOR) &&
       nvc0->rast->pipe.scissor == nvc0->state.scissor)
       return;
 
@@ -309,22 +326,46 @@ nvc0_validate_viewport(struct nvc0_context *nvc0)
    nvc0->viewports_dirty = 0;
 }
 
-static INLINE void
+static void
+nvc0_validate_window_rects(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+   bool enable = nvc0->window_rect.rects > 0 || nvc0->window_rect.inclusive;
+   int i;
+
+   IMMED_NVC0(push, NVC0_3D(CLIP_RECTS_EN), enable);
+   if (!enable)
+      return;
+
+   IMMED_NVC0(push, NVC0_3D(CLIP_RECTS_MODE), !nvc0->window_rect.inclusive);
+   BEGIN_NVC0(push, NVC0_3D(CLIP_RECT_HORIZ(0)), NVC0_MAX_WINDOW_RECTANGLES * 2);
+   for (i = 0; i < nvc0->window_rect.rects; i++) {
+      struct pipe_scissor_state *s = &nvc0->window_rect.rect[i];
+      PUSH_DATA(push, (s->maxx << 16) | s->minx);
+      PUSH_DATA(push, (s->maxy << 16) | s->miny);
+   }
+   for (; i < NVC0_MAX_WINDOW_RECTANGLES; i++) {
+      PUSH_DATA(push, 0);
+      PUSH_DATA(push, 0);
+   }
+}
+
+static inline void
 nvc0_upload_uclip_planes(struct nvc0_context *nvc0, unsigned s)
 {
    struct nouveau_pushbuf *push = nvc0->base.pushbuf;
-   struct nouveau_bo *bo = nvc0->screen->uniform_bo;
+   struct nvc0_screen *screen = nvc0->screen;
 
    BEGIN_NVC0(push, NVC0_3D(CB_SIZE), 3);
-   PUSH_DATA (push, 512);
-   PUSH_DATAh(push, bo->offset + (5 << 16) + (s << 9));
-   PUSH_DATA (push, bo->offset + (5 << 16) + (s << 9));
+   PUSH_DATA (push, NVC0_CB_AUX_SIZE);
+   PUSH_DATAh(push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(s));
+   PUSH_DATA (push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(s));
    BEGIN_1IC0(push, NVC0_3D(CB_POS), PIPE_MAX_CLIP_PLANES * 4 + 1);
-   PUSH_DATA (push, 256);
+   PUSH_DATA (push, NVC0_CB_AUX_UCP_INFO);
    PUSH_DATAp(push, &nvc0->clip.ucp[0][0], PIPE_MAX_CLIP_PLANES * 4);
 }
 
-static INLINE void
+static inline void
 nvc0_check_program_ucps(struct nvc0_context *nvc0,
                         struct nvc0_program *vp, uint8_t mask)
 {
@@ -339,7 +380,7 @@ nvc0_check_program_ucps(struct nvc0_context *nvc0,
       nvc0_vertprog_validate(nvc0);
    else
    if (likely(vp == nvc0->gmtyprog))
-      nvc0_vertprog_validate(nvc0);
+      nvc0_gmtyprog_validate(nvc0);
    else
       nvc0_tevlprog_validate(nvc0);
 }
@@ -367,11 +408,12 @@ nvc0_validate_clip(struct nvc0_context *nvc0)
    if (clip_enable && vp->vp.num_ucps < PIPE_MAX_CLIP_PLANES)
       nvc0_check_program_ucps(nvc0, vp, clip_enable);
 
-   if (nvc0->dirty & (NVC0_NEW_CLIP | (NVC0_NEW_VERTPROG << stage)))
+   if (nvc0->dirty_3d & (NVC0_NEW_3D_CLIP | (NVC0_NEW_3D_VERTPROG << stage)))
       if (vp->vp.num_ucps > 0 && vp->vp.num_ucps <= PIPE_MAX_CLIP_PLANES)
          nvc0_upload_uclip_planes(nvc0, stage);
 
    clip_enable &= vp->vp.clip_enable;
+   clip_enable |= vp->vp.cull_enable;
 
    if (nvc0->state.clip_enable != clip_enable) {
       nvc0->state.clip_enable = clip_enable;
@@ -424,7 +466,7 @@ nvc0_constbufs_validate(struct nvc0_context *nvc0)
 
          if (nvc0->constbuf[s][i].user) {
             struct nouveau_bo *bo = nvc0->screen->uniform_bo;
-            const unsigned base = s << 16;
+            const unsigned base = NVC0_CB_USR_INFO(s);
             const unsigned size = nvc0->constbuf[s][0].size;
             assert(i == 0); /* we really only want OpenGL uniforms here */
             assert(nvc0->constbuf[s][0].u.data);
@@ -439,7 +481,7 @@ nvc0_constbufs_validate(struct nvc0_context *nvc0)
                BEGIN_NVC0(push, NVC0_3D(CB_BIND(s)), 1);
                PUSH_DATA (push, (0 << 4) | 1);
             }
-            nvc0_cb_push(&nvc0->base, bo, NOUVEAU_BO_VRAM,
+            nvc0_cb_bo_push(&nvc0->base, bo, NV_VRAM_DOMAIN(&nvc0->screen->base),
                          base, nvc0->state.uniform_buffer_bound[s],
                          0, (size + 3) / 4,
                          nvc0->constbuf[s][0].u.data);
@@ -454,7 +496,10 @@ nvc0_constbufs_validate(struct nvc0_context *nvc0)
                BEGIN_NVC0(push, NVC0_3D(CB_BIND(s)), 1);
                PUSH_DATA (push, (i << 4) | 1);
 
-               BCTX_REFN(nvc0->bufctx_3d, CB(s, i), res, RD);
+               BCTX_REFN(nvc0->bufctx_3d, 3D_CB(s, i), res, RD);
+
+               nvc0->cb_dirty = 1; /* Force cache flush for UBO. */
+               res->cb_bindings[s] |= 1 << i;
             } else {
                BEGIN_NVC0(push, NVC0_3D(CB_BIND(s)), 1);
                PUSH_DATA (push, (i << 4) | 0);
@@ -464,6 +509,50 @@ nvc0_constbufs_validate(struct nvc0_context *nvc0)
          }
       }
    }
+
+   if (nvc0->screen->base.class_3d < NVE4_3D_CLASS) {
+      /* Invalidate all COMPUTE constbufs because they are aliased with 3D. */
+      nvc0->dirty_cp |= NVC0_NEW_CP_CONSTBUF;
+      nvc0->constbuf_dirty[5] |= nvc0->constbuf_valid[5];
+      nvc0->state.uniform_buffer_bound[5] = 0;
+   }
+}
+
+static void
+nvc0_validate_buffers(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+   struct nvc0_screen *screen = nvc0->screen;
+   int i, s;
+
+   for (s = 0; s < 5; s++) {
+      BEGIN_NVC0(push, NVC0_3D(CB_SIZE), 3);
+      PUSH_DATA (push, NVC0_CB_AUX_SIZE);
+      PUSH_DATAh(push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(s));
+      PUSH_DATA (push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(s));
+      BEGIN_1IC0(push, NVC0_3D(CB_POS), 1 + 4 * NVC0_MAX_BUFFERS);
+      PUSH_DATA (push, NVC0_CB_AUX_BUF_INFO(0));
+      for (i = 0; i < NVC0_MAX_BUFFERS; i++) {
+         if (nvc0->buffers[s][i].buffer) {
+            struct nv04_resource *res =
+               nv04_resource(nvc0->buffers[s][i].buffer);
+            PUSH_DATA (push, res->address + nvc0->buffers[s][i].buffer_offset);
+            PUSH_DATAh(push, res->address + nvc0->buffers[s][i].buffer_offset);
+            PUSH_DATA (push, nvc0->buffers[s][i].buffer_size);
+            PUSH_DATA (push, 0);
+            BCTX_REFN(nvc0->bufctx_3d, 3D_BUF, res, RDWR);
+            util_range_add(&res->valid_buffer_range,
+                           nvc0->buffers[s][i].buffer_offset,
+                           nvc0->buffers[s][i].buffer_size);
+         } else {
+            PUSH_DATA (push, 0);
+            PUSH_DATA (push, 0);
+            PUSH_DATA (push, 0);
+            PUSH_DATA (push, 0);
+         }
+      }
+   }
+
 }
 
 static void
@@ -493,37 +582,47 @@ nvc0_validate_min_samples(struct nvc0_context *nvc0)
    int samples;
 
    samples = util_next_power_of_two(nvc0->min_samples);
-   if (samples > 1)
+   if (samples > 1) {
+      // If we're using the incoming sample mask and doing sample shading, we
+      // have to do sample shading "to the max", otherwise there's no way to
+      // tell which sets of samples are covered by the current invocation.
+      if (nvc0->fragprog->fp.sample_mask_in)
+         samples = util_framebuffer_get_num_samples(&nvc0->framebuffer);
       samples |= NVC0_3D_SAMPLE_SHADING_ENABLE;
+   }
 
    IMMED_NVC0(push, NVC0_3D(SAMPLE_SHADING), samples);
 }
 
-void
-nvc0_validate_global_residents(struct nvc0_context *nvc0,
-                               struct nouveau_bufctx *bctx, int bin)
+static void
+nvc0_validate_driverconst(struct nvc0_context *nvc0)
 {
-   unsigned i;
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+   struct nvc0_screen *screen = nvc0->screen;
+   int i;
 
-   for (i = 0; i < nvc0->global_residents.size / sizeof(struct pipe_resource *);
-        ++i) {
-      struct pipe_resource *res = *util_dynarray_element(
-         &nvc0->global_residents, struct pipe_resource *, i);
-      if (res)
-         nvc0_add_resident(bctx, bin, nv04_resource(res), NOUVEAU_BO_RDWR);
+   for (i = 0; i < 5; ++i) {
+      BEGIN_NVC0(push, NVC0_3D(CB_SIZE), 3);
+      PUSH_DATA (push, NVC0_CB_AUX_SIZE);
+      PUSH_DATAh(push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(i));
+      PUSH_DATA (push, screen->uniform_bo->offset + NVC0_CB_AUX_INFO(i));
+      BEGIN_NVC0(push, NVC0_3D(CB_BIND(i)), 1);
+      PUSH_DATA (push, (15 << 4) | 1);
    }
+
+   nvc0->dirty_cp |= NVC0_NEW_CP_DRIVERCONST;
 }
 
 static void
-nvc0_validate_derived_1(struct nvc0_context *nvc0)
+nvc0_validate_fp_zsa_rast(struct nvc0_context *nvc0)
 {
    struct nouveau_pushbuf *push = nvc0->base.pushbuf;
-   boolean rasterizer_discard;
+   bool rasterizer_discard;
 
    if (nvc0->rast && nvc0->rast->pipe.rasterizer_discard) {
-      rasterizer_discard = TRUE;
+      rasterizer_discard = true;
    } else {
-      boolean zs = nvc0->zsa &&
+      bool zs = nvc0->zsa &&
          (nvc0->zsa->pipe.depth.enabled || nvc0->zsa->pipe.stencil[0].enabled);
       rasterizer_discard = !zs &&
          (!nvc0->fragprog || !nvc0->fragprog->hdr[18]);
@@ -535,6 +634,73 @@ nvc0_validate_derived_1(struct nvc0_context *nvc0)
    }
 }
 
+/* alpha test is disabled if there are no color RTs, so make sure we have at
+ * least one if alpha test is enabled. Note that this must run after
+ * nvc0_validate_fb, otherwise that will override the RT count setting.
+ */
+static void
+nvc0_validate_zsa_fb(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+
+   if (nvc0->zsa && nvc0->zsa->pipe.alpha.enabled &&
+       nvc0->framebuffer.zsbuf &&
+       nvc0->framebuffer.nr_cbufs == 0) {
+      nvc0_fb_set_null_rt(push, 0, 0);
+      BEGIN_NVC0(push, NVC0_3D(RT_CONTROL), 1);
+      PUSH_DATA (push, (076543210 << 4) | 1);
+   }
+}
+
+static void
+nvc0_validate_blend_fb(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+   struct pipe_framebuffer_state *fb = &nvc0->framebuffer;
+   uint32_t ms = 0;
+
+   if ((!fb->nr_cbufs || !fb->cbufs[0] ||
+        !util_format_is_pure_integer(fb->cbufs[0]->format)) && nvc0->blend) {
+      if (nvc0->blend->pipe.alpha_to_coverage)
+         ms |= NVC0_3D_MULTISAMPLE_CTRL_ALPHA_TO_COVERAGE;
+      if (nvc0->blend->pipe.alpha_to_one)
+         ms |= NVC0_3D_MULTISAMPLE_CTRL_ALPHA_TO_ONE;
+   }
+
+   BEGIN_NVC0(push, NVC0_3D(MULTISAMPLE_CTRL), 1);
+   PUSH_DATA (push, ms);
+}
+
+static void
+nvc0_validate_rast_fb(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+   struct pipe_framebuffer_state *fb = &nvc0->framebuffer;
+   struct pipe_rasterizer_state *rast = &nvc0->rast->pipe;
+
+   if (!rast)
+      return;
+
+   if (rast->offset_units_unscaled) {
+      BEGIN_NVC0(push, NVC0_3D(POLYGON_OFFSET_UNITS), 1);
+      if (fb->zsbuf && fb->zsbuf->format == PIPE_FORMAT_Z16_UNORM)
+         PUSH_DATAf(push, rast->offset_units * (1 << 16));
+      else
+         PUSH_DATAf(push, rast->offset_units * (1 << 24));
+   }
+}
+
+
+static void
+nvc0_validate_tess_state(struct nvc0_context *nvc0)
+{
+   struct nouveau_pushbuf *push = nvc0->base.pushbuf;
+
+   BEGIN_NVC0(push, NVC0_3D(TESS_LEVEL_OUTER(0)), 6);
+   PUSH_DATAp(push, nvc0->default_tess_outer, 4);
+   PUSH_DATAp(push, nvc0->default_tess_inner, 2);
+}
+
 static void
 nvc0_switch_pipe_context(struct nvc0_context *ctx_to)
 {
@@ -543,76 +709,92 @@ nvc0_switch_pipe_context(struct nvc0_context *ctx_to)
 
    if (ctx_from)
       ctx_to->state = ctx_from->state;
+   else
+      ctx_to->state = ctx_to->screen->save_state;
 
-   ctx_to->dirty = ~0;
+   ctx_to->dirty_3d = ~0;
+   ctx_to->dirty_cp = ~0;
    ctx_to->viewports_dirty = ~0;
    ctx_to->scissors_dirty = ~0;
 
-   for (s = 0; s < 5; ++s) {
+   for (s = 0; s < 6; ++s) {
       ctx_to->samplers_dirty[s] = ~0;
       ctx_to->textures_dirty[s] = ~0;
       ctx_to->constbuf_dirty[s] = (1 << NVC0_MAX_PIPE_CONSTBUFS) - 1;
+      ctx_to->buffers_dirty[s]  = ~0;
+      ctx_to->images_dirty[s]   = ~0;
    }
 
+   /* Reset tfb as the shader that owns it may have been deleted. */
+   ctx_to->state.tfb = NULL;
+
    if (!ctx_to->vertex)
-      ctx_to->dirty &= ~(NVC0_NEW_VERTEX | NVC0_NEW_ARRAYS);
+      ctx_to->dirty_3d &= ~(NVC0_NEW_3D_VERTEX | NVC0_NEW_3D_ARRAYS);
    if (!ctx_to->idxbuf.buffer)
-      ctx_to->dirty &= ~NVC0_NEW_IDXBUF;
+      ctx_to->dirty_3d &= ~NVC0_NEW_3D_IDXBUF;
 
    if (!ctx_to->vertprog)
-      ctx_to->dirty &= ~NVC0_NEW_VERTPROG;
+      ctx_to->dirty_3d &= ~NVC0_NEW_3D_VERTPROG;
    if (!ctx_to->fragprog)
-      ctx_to->dirty &= ~NVC0_NEW_FRAGPROG;
+      ctx_to->dirty_3d &= ~NVC0_NEW_3D_FRAGPROG;
 
    if (!ctx_to->blend)
-      ctx_to->dirty &= ~NVC0_NEW_BLEND;
+      ctx_to->dirty_3d &= ~NVC0_NEW_3D_BLEND;
    if (!ctx_to->rast)
-      ctx_to->dirty &= ~(NVC0_NEW_RASTERIZER | NVC0_NEW_SCISSOR);
+      ctx_to->dirty_3d &= ~(NVC0_NEW_3D_RASTERIZER | NVC0_NEW_3D_SCISSOR);
    if (!ctx_to->zsa)
-      ctx_to->dirty &= ~NVC0_NEW_ZSA;
+      ctx_to->dirty_3d &= ~NVC0_NEW_3D_ZSA;
 
    ctx_to->screen->cur_ctx = ctx_to;
 }
 
-static struct state_validate {
-    void (*func)(struct nvc0_context *);
-    uint32_t states;
-} validate_list[] = {
-    { nvc0_validate_fb,            NVC0_NEW_FRAMEBUFFER },
-    { nvc0_validate_blend,         NVC0_NEW_BLEND },
-    { nvc0_validate_zsa,           NVC0_NEW_ZSA },
-    { nvc0_validate_sample_mask,   NVC0_NEW_SAMPLE_MASK },
-    { nvc0_validate_rasterizer,    NVC0_NEW_RASTERIZER },
-    { nvc0_validate_blend_colour,  NVC0_NEW_BLEND_COLOUR },
-    { nvc0_validate_stencil_ref,   NVC0_NEW_STENCIL_REF },
-    { nvc0_validate_stipple,       NVC0_NEW_STIPPLE },
-    { nvc0_validate_scissor,       NVC0_NEW_SCISSOR | NVC0_NEW_RASTERIZER },
-    { nvc0_validate_viewport,      NVC0_NEW_VIEWPORT },
-    { nvc0_vertprog_validate,      NVC0_NEW_VERTPROG },
-    { nvc0_tctlprog_validate,      NVC0_NEW_TCTLPROG },
-    { nvc0_tevlprog_validate,      NVC0_NEW_TEVLPROG },
-    { nvc0_gmtyprog_validate,      NVC0_NEW_GMTYPROG },
-    { nvc0_fragprog_validate,      NVC0_NEW_FRAGPROG },
-    { nvc0_validate_derived_1,     NVC0_NEW_FRAGPROG | NVC0_NEW_ZSA |
-                                   NVC0_NEW_RASTERIZER },
-    { nvc0_validate_clip,          NVC0_NEW_CLIP | NVC0_NEW_RASTERIZER |
-                                   NVC0_NEW_VERTPROG |
-                                   NVC0_NEW_TEVLPROG |
-                                   NVC0_NEW_GMTYPROG },
-    { nvc0_constbufs_validate,     NVC0_NEW_CONSTBUF },
-    { nvc0_validate_textures,      NVC0_NEW_TEXTURES },
-    { nvc0_validate_samplers,      NVC0_NEW_SAMPLERS },
-    { nve4_set_tex_handles,        NVC0_NEW_TEXTURES | NVC0_NEW_SAMPLERS },
-    { nvc0_vertex_arrays_validate, NVC0_NEW_VERTEX | NVC0_NEW_ARRAYS },
-    { nvc0_validate_surfaces,      NVC0_NEW_SURFACES },
-    { nvc0_idxbuf_validate,        NVC0_NEW_IDXBUF },
-    { nvc0_tfb_validate,           NVC0_NEW_TFB_TARGETS | NVC0_NEW_GMTYPROG },
-    { nvc0_validate_min_samples,   NVC0_NEW_MIN_SAMPLES },
+static struct nvc0_state_validate
+validate_list_3d[] = {
+    { nvc0_validate_fb,            NVC0_NEW_3D_FRAMEBUFFER },
+    { nvc0_validate_blend,         NVC0_NEW_3D_BLEND },
+    { nvc0_validate_zsa,           NVC0_NEW_3D_ZSA },
+    { nvc0_validate_sample_mask,   NVC0_NEW_3D_SAMPLE_MASK },
+    { nvc0_validate_rasterizer,    NVC0_NEW_3D_RASTERIZER },
+    { nvc0_validate_blend_colour,  NVC0_NEW_3D_BLEND_COLOUR },
+    { nvc0_validate_stencil_ref,   NVC0_NEW_3D_STENCIL_REF },
+    { nvc0_validate_stipple,       NVC0_NEW_3D_STIPPLE },
+    { nvc0_validate_scissor,       NVC0_NEW_3D_SCISSOR | NVC0_NEW_3D_RASTERIZER },
+    { nvc0_validate_viewport,      NVC0_NEW_3D_VIEWPORT },
+    { nvc0_validate_window_rects,  NVC0_NEW_3D_WINDOW_RECTS },
+    { nvc0_vertprog_validate,      NVC0_NEW_3D_VERTPROG },
+    { nvc0_tctlprog_validate,      NVC0_NEW_3D_TCTLPROG },
+    { nvc0_tevlprog_validate,      NVC0_NEW_3D_TEVLPROG },
+    { nvc0_validate_tess_state,    NVC0_NEW_3D_TESSFACTOR },
+    { nvc0_gmtyprog_validate,      NVC0_NEW_3D_GMTYPROG },
+    { nvc0_validate_min_samples,   NVC0_NEW_3D_MIN_SAMPLES |
+                                   NVC0_NEW_3D_FRAGPROG |
+                                   NVC0_NEW_3D_FRAMEBUFFER },
+    { nvc0_fragprog_validate,      NVC0_NEW_3D_FRAGPROG | NVC0_NEW_3D_RASTERIZER },
+    { nvc0_validate_fp_zsa_rast,   NVC0_NEW_3D_FRAGPROG | NVC0_NEW_3D_ZSA |
+                                   NVC0_NEW_3D_RASTERIZER },
+    { nvc0_validate_zsa_fb,        NVC0_NEW_3D_ZSA | NVC0_NEW_3D_FRAMEBUFFER },
+    { nvc0_validate_blend_fb,      NVC0_NEW_3D_BLEND | NVC0_NEW_3D_FRAMEBUFFER },
+    { nvc0_validate_rast_fb,       NVC0_NEW_3D_RASTERIZER | NVC0_NEW_3D_FRAMEBUFFER },
+    { nvc0_validate_clip,          NVC0_NEW_3D_CLIP | NVC0_NEW_3D_RASTERIZER |
+                                   NVC0_NEW_3D_VERTPROG |
+                                   NVC0_NEW_3D_TEVLPROG |
+                                   NVC0_NEW_3D_GMTYPROG },
+    { nvc0_constbufs_validate,     NVC0_NEW_3D_CONSTBUF },
+    { nvc0_validate_textures,      NVC0_NEW_3D_TEXTURES },
+    { nvc0_validate_samplers,      NVC0_NEW_3D_SAMPLERS },
+    { nve4_set_tex_handles,        NVC0_NEW_3D_TEXTURES | NVC0_NEW_3D_SAMPLERS },
+    { nvc0_vertex_arrays_validate, NVC0_NEW_3D_VERTEX | NVC0_NEW_3D_ARRAYS },
+    { nvc0_validate_surfaces,      NVC0_NEW_3D_SURFACES },
+    { nvc0_validate_buffers,       NVC0_NEW_3D_BUFFERS },
+    { nvc0_idxbuf_validate,        NVC0_NEW_3D_IDXBUF },
+    { nvc0_tfb_validate,           NVC0_NEW_3D_TFB_TARGETS | NVC0_NEW_3D_GMTYPROG },
+    { nvc0_validate_driverconst,   NVC0_NEW_3D_DRIVERCONST },
 };
-#define validate_list_len (sizeof(validate_list) / sizeof(validate_list[0]))
 
-boolean
-nvc0_state_validate(struct nvc0_context *nvc0, uint32_t mask, unsigned words)
+bool
+nvc0_state_validate(struct nvc0_context *nvc0, uint32_t mask,
+                    struct nvc0_state_validate *validate_list, int size,
+                    uint32_t *dirty, struct nouveau_bufctx *bufctx)
 {
    uint32_t state_mask;
    int ret;
@@ -621,26 +803,38 @@ nvc0_state_validate(struct nvc0_context *nvc0, uint32_t mask, unsigned words)
    if (nvc0->screen->cur_ctx != nvc0)
       nvc0_switch_pipe_context(nvc0);
 
-   state_mask = nvc0->dirty & mask;
+   state_mask = *dirty & mask;
 
    if (state_mask) {
-      for (i = 0; i < validate_list_len; ++i) {
-         struct state_validate *validate = &validate_list[i];
+      for (i = 0; i < size; ++i) {
+         struct nvc0_state_validate *validate = &validate_list[i];
 
          if (state_mask & validate->states)
             validate->func(nvc0);
       }
-      nvc0->dirty &= ~state_mask;
+      *dirty &= ~state_mask;
 
-      nvc0_bufctx_fence(nvc0, nvc0->bufctx_3d, FALSE);
+      nvc0_bufctx_fence(nvc0, bufctx, false);
    }
 
-   nouveau_pushbuf_bufctx(nvc0->base.pushbuf, nvc0->bufctx_3d);
+   nouveau_pushbuf_bufctx(nvc0->base.pushbuf, bufctx);
    ret = nouveau_pushbuf_validate(nvc0->base.pushbuf);
 
+   return !ret;
+}
+
+bool
+nvc0_state_validate_3d(struct nvc0_context *nvc0, uint32_t mask)
+{
+   bool ret;
+
+   ret = nvc0_state_validate(nvc0, mask, validate_list_3d,
+                             ARRAY_SIZE(validate_list_3d), &nvc0->dirty_3d,
+                             nvc0->bufctx_3d);
+
    if (unlikely(nvc0->state.flushed)) {
-      nvc0->state.flushed = FALSE;
-      nvc0_bufctx_fence(nvc0, nvc0->bufctx_3d, TRUE);
+      nvc0->state.flushed = false;
+      nvc0_bufctx_fence(nvc0, nvc0->bufctx_3d, true);
    }
-   return !ret;
+   return ret;
 }