lima/gpir: Always schedule complex2 and *_impl right after complex1
[mesa.git] / src / gallium / drivers / nouveau / nv50 / nv50_state_validate.c
index 95592533bb3f3068630bf15ac38c51612513e6c6..fcf2228c17875d81cba62b140042dada084415e9 100644 (file)
@@ -1,14 +1,15 @@
 
+#include "util/u_viewport.h"
+
 #include "nv50/nv50_context.h"
-#include "nv50/nv50_defs.xml.h"
 
-static INLINE void
+static inline void
 nv50_fb_set_null_rt(struct nouveau_pushbuf *push, unsigned i)
 {
    BEGIN_NV04(push, NV50_3D(RT_ADDRESS_HIGH(i)), 4);
    PUSH_DATA (push, 0);
    PUSH_DATA (push, 0);
-   PUSH_DATA (push, NV50_SURFACE_FORMAT_NONE);
+   PUSH_DATA (push, 0);
    PUSH_DATA (push, 0);
    BEGIN_NV04(push, NV50_3D(RT_HORIZ(i)), 2);
    PUSH_DATA (push, 64);
@@ -24,7 +25,7 @@ nv50_validate_fb(struct nv50_context *nv50)
    unsigned ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1;
    uint32_t array_size = 0xffff, array_mode = 0;
 
-   nouveau_bufctx_reset(nv50->bufctx_3d, NV50_BIND_FB);
+   nouveau_bufctx_reset(nv50->bufctx_3d, NV50_BIND_3D_FB);
 
    BEGIN_NV04(push, NV50_3D(RT_CONTROL), 1);
    PUSH_DATA (push, (076543210 << 4) | fb->nr_cbufs);
@@ -54,10 +55,12 @@ nv50_validate_fb(struct nv50_context *nv50)
       assert(mt->layout_3d || !array_mode || array_size == 1);
 
       BEGIN_NV04(push, NV50_3D(RT_ADDRESS_HIGH(i)), 5);
-      PUSH_DATAh(push, bo->offset + sf->offset);
-      PUSH_DATA (push, bo->offset + sf->offset);
+      PUSH_DATAh(push, mt->base.address + sf->offset);
+      PUSH_DATA (push, mt->base.address + sf->offset);
       PUSH_DATA (push, nv50_format_table[sf->base.format].rt);
       if (likely(nouveau_bo_memtype(bo))) {
+         assert(sf->base.texture->target != PIPE_BUFFER);
+
          PUSH_DATA (push, mt->level[sf->base.u.tex.level].tile_mode);
          PUSH_DATA (push, mt->layer_stride >> 2);
          BEGIN_NV04(push, NV50_3D(RT_HORIZ(i)), 2);
@@ -65,6 +68,7 @@ nv50_validate_fb(struct nv50_context *nv50)
          PUSH_DATA (push, sf->height);
          BEGIN_NV04(push, NV50_3D(RT_ARRAY_MODE), 1);
          PUSH_DATA (push, array_mode | array_size);
+         nv50->rt_array_mode = array_mode | array_size;
       } else {
          PUSH_DATA (push, 0);
          PUSH_DATA (push, 0);
@@ -81,23 +85,22 @@ nv50_validate_fb(struct nv50_context *nv50)
       ms_mode = mt->ms_mode;
 
       if (mt->base.status & NOUVEAU_BUFFER_STATUS_GPU_READING)
-         nv50->state.rt_serialize = TRUE;
+         nv50->state.rt_serialize = true;
       mt->base.status |= NOUVEAU_BUFFER_STATUS_GPU_WRITING;
       mt->base.status &= ~NOUVEAU_BUFFER_STATUS_GPU_READING;
 
       /* only register for writing, otherwise we'd always serialize here */
-      BCTX_REFN(nv50->bufctx_3d, FB, &mt->base, WR);
+      BCTX_REFN(nv50->bufctx_3d, 3D_FB, &mt->base, WR);
    }
 
    if (fb->zsbuf) {
       struct nv50_miptree *mt = nv50_miptree(fb->zsbuf->texture);
       struct nv50_surface *sf = nv50_surface(fb->zsbuf);
-      struct nouveau_bo *bo = mt->base.bo;
       int unk = mt->base.base.target == PIPE_TEXTURE_3D || sf->depth == 1;
 
       BEGIN_NV04(push, NV50_3D(ZETA_ADDRESS_HIGH), 5);
-      PUSH_DATAh(push, bo->offset + sf->offset);
-      PUSH_DATA (push, bo->offset + sf->offset);
+      PUSH_DATAh(push, mt->base.address + sf->offset);
+      PUSH_DATA (push, mt->base.address + sf->offset);
       PUSH_DATA (push, nv50_format_table[fb->zsbuf->format].rt);
       PUSH_DATA (push, mt->level[sf->base.u.tex.level].tile_mode);
       PUSH_DATA (push, mt->layer_stride >> 2);
@@ -111,11 +114,11 @@ nv50_validate_fb(struct nv50_context *nv50)
       ms_mode = mt->ms_mode;
 
       if (mt->base.status & NOUVEAU_BUFFER_STATUS_GPU_READING)
-         nv50->state.rt_serialize = TRUE;
+         nv50->state.rt_serialize = true;
       mt->base.status |= NOUVEAU_BUFFER_STATUS_GPU_WRITING;
       mt->base.status &= ~NOUVEAU_BUFFER_STATUS_GPU_READING;
 
-      BCTX_REFN(nv50->bufctx_3d, FB, &mt->base, WR);
+      BCTX_REFN(nv50->bufctx_3d, 3D_FB, &mt->base, WR);
    } else {
       BEGIN_NV04(push, NV50_3D(ZETA_ENABLE), 1);
       PUSH_DATA (push, 0);
@@ -124,9 +127,23 @@ nv50_validate_fb(struct nv50_context *nv50)
    BEGIN_NV04(push, NV50_3D(MULTISAMPLE_MODE), 1);
    PUSH_DATA (push, ms_mode);
 
+   /* Only need to initialize the first viewport, which is used for clears */
    BEGIN_NV04(push, NV50_3D(VIEWPORT_HORIZ(0)), 2);
    PUSH_DATA (push, fb->width << 16);
    PUSH_DATA (push, fb->height << 16);
+
+   if (nv50->screen->tesla->oclass >= NVA3_3D_CLASS) {
+      unsigned ms = 1 << ms_mode;
+      BEGIN_NV04(push, NV50_3D(CB_ADDR), 1);
+      PUSH_DATA (push, (NV50_CB_AUX_SAMPLE_OFFSET << (8 - 2)) | NV50_CB_AUX);
+      BEGIN_NI04(push, NV50_3D(CB_DATA(0)), 2 * ms);
+      for (i = 0; i < ms; i++) {
+         float xy[2];
+         nv50->base.pipe.get_sample_position(&nv50->base.pipe, ms, i, xy);
+         PUSH_DATAf(push, xy[0]);
+         PUSH_DATAf(push, xy[1]);
+      }
+   }
 }
 
 static void
@@ -167,42 +184,64 @@ static void
 nv50_validate_scissor(struct nv50_context *nv50)
 {
    struct nouveau_pushbuf *push = nv50->base.pushbuf;
-   struct pipe_scissor_state *s = &nv50->scissor;
 #ifdef NV50_SCISSORS_CLIPPING
-   struct pipe_viewport_state *vp = &nv50->viewport;
-   int minx, maxx, miny, maxy;
+   int minx, maxx, miny, maxy, i;
+   bool rast_scissor = nv50->rast ? nv50->rast->pipe.scissor : false;
 
-   if (!(nv50->dirty &
-         (NV50_NEW_SCISSOR | NV50_NEW_VIEWPORT | NV50_NEW_FRAMEBUFFER)) &&
-       nv50->state.scissor == nv50->rast->pipe.scissor)
+   if (!(nv50->dirty_3d &
+         (NV50_NEW_3D_SCISSOR | NV50_NEW_3D_VIEWPORT | NV50_NEW_3D_FRAMEBUFFER)) &&
+       nv50->state.scissor == rast_scissor)
       return;
-   nv50->state.scissor = nv50->rast->pipe.scissor;
 
-   if (nv50->state.scissor) {
-      minx = s->minx;
-      maxx = s->maxx;
-      miny = s->miny;
-      maxy = s->maxy;
-   } else {
-      minx = 0;
-      maxx = nv50->framebuffer.width;
-      miny = 0;
-      maxy = nv50->framebuffer.height;
-   }
+   if (nv50->state.scissor != rast_scissor)
+      nv50->scissors_dirty = (1 << NV50_MAX_VIEWPORTS) - 1;
+
+   nv50->state.scissor = rast_scissor;
+
+   if ((nv50->dirty_3d & NV50_NEW_3D_FRAMEBUFFER) && !nv50->state.scissor)
+      nv50->scissors_dirty = (1 << NV50_MAX_VIEWPORTS) - 1;
+
+   for (i = 0; i < NV50_MAX_VIEWPORTS; i++) {
+      struct pipe_scissor_state *s = &nv50->scissors[i];
+      struct pipe_viewport_state *vp = &nv50->viewports[i];
+
+      if (!(nv50->scissors_dirty & (1 << i)) &&
+          !(nv50->viewports_dirty & (1 << i)))
+         continue;
+
+      if (nv50->state.scissor) {
+         minx = s->minx;
+         maxx = s->maxx;
+         miny = s->miny;
+         maxy = s->maxy;
+      } else {
+         minx = 0;
+         maxx = nv50->framebuffer.width;
+         miny = 0;
+         maxy = nv50->framebuffer.height;
+      }
+
+      minx = MAX2(minx, (int)(vp->translate[0] - fabsf(vp->scale[0])));
+      maxx = MIN2(maxx, (int)(vp->translate[0] + fabsf(vp->scale[0])));
+      miny = MAX2(miny, (int)(vp->translate[1] - fabsf(vp->scale[1])));
+      maxy = MIN2(maxy, (int)(vp->translate[1] + fabsf(vp->scale[1])));
 
-   minx = MAX2(minx, (int)(vp->translate[0] - fabsf(vp->scale[0])));
-   maxx = MIN2(maxx, (int)(vp->translate[0] + fabsf(vp->scale[0])));
-   miny = MAX2(miny, (int)(vp->translate[1] - fabsf(vp->scale[1])));
-   maxy = MIN2(maxy, (int)(vp->translate[1] + fabsf(vp->scale[1])));
+      minx = MIN2(minx, 8192);
+      maxx = MAX2(maxx, 0);
+      miny = MIN2(miny, 8192);
+      maxy = MAX2(maxy, 0);
 
-   BEGIN_NV04(push, NV50_3D(SCISSOR_HORIZ(0)), 2);
-   PUSH_DATA (push, (maxx << 16) | minx);
-   PUSH_DATA (push, (maxy << 16) | miny);
+      BEGIN_NV04(push, NV50_3D(SCISSOR_HORIZ(i)), 2);
+      PUSH_DATA (push, (maxx << 16) | minx);
+      PUSH_DATA (push, (maxy << 16) | miny);
 #else
-   BEGIN_NV04(push, NV50_3D(SCISSOR_HORIZ(0)), 2);
-   PUSH_DATA (push, (s->maxx << 16) | s->minx);
-   PUSH_DATA (push, (s->maxy << 16) | s->miny);
+      BEGIN_NV04(push, NV50_3D(SCISSOR_HORIZ(i)), 2);
+      PUSH_DATA (push, (s->maxx << 16) | s->minx);
+      PUSH_DATA (push, (s->maxy << 16) | s->miny);
 #endif
+   }
+
+   nv50->scissors_dirty = 0;
 }
 
 static void
@@ -210,27 +249,67 @@ nv50_validate_viewport(struct nv50_context *nv50)
 {
    struct nouveau_pushbuf *push = nv50->base.pushbuf;
    float zmin, zmax;
+   int i;
 
-   BEGIN_NV04(push, NV50_3D(VIEWPORT_TRANSLATE_X(0)), 3);
-   PUSH_DATAf(push, nv50->viewport.translate[0]);
-   PUSH_DATAf(push, nv50->viewport.translate[1]);
-   PUSH_DATAf(push, nv50->viewport.translate[2]);
-   BEGIN_NV04(push, NV50_3D(VIEWPORT_SCALE_X(0)), 3);
-   PUSH_DATAf(push, nv50->viewport.scale[0]);
-   PUSH_DATAf(push, nv50->viewport.scale[1]);
-   PUSH_DATAf(push, nv50->viewport.scale[2]);
+   for (i = 0; i < NV50_MAX_VIEWPORTS; i++) {
+      struct pipe_viewport_state *vpt = &nv50->viewports[i];
 
-   zmin = nv50->viewport.translate[2] - fabsf(nv50->viewport.scale[2]);
-   zmax = nv50->viewport.translate[2] + fabsf(nv50->viewport.scale[2]);
+      if (!(nv50->viewports_dirty & (1 << i)))
+         continue;
+
+      BEGIN_NV04(push, NV50_3D(VIEWPORT_TRANSLATE_X(i)), 3);
+      PUSH_DATAf(push, vpt->translate[0]);
+      PUSH_DATAf(push, vpt->translate[1]);
+      PUSH_DATAf(push, vpt->translate[2]);
+      BEGIN_NV04(push, NV50_3D(VIEWPORT_SCALE_X(i)), 3);
+      PUSH_DATAf(push, vpt->scale[0]);
+      PUSH_DATAf(push, vpt->scale[1]);
+      PUSH_DATAf(push, vpt->scale[2]);
+
+      /* If the halfz setting ever changes, the viewports will also get
+       * updated. The rast will get updated before the validate function has a
+       * chance to hit, so we can just use it directly without an atom
+       * dependency.
+       */
+      util_viewport_zmin_zmax(vpt, nv50->rast->pipe.clip_halfz, &zmin, &zmax);
 
 #ifdef NV50_SCISSORS_CLIPPING
-   BEGIN_NV04(push, NV50_3D(DEPTH_RANGE_NEAR(0)), 2);
-   PUSH_DATAf(push, zmin);
-   PUSH_DATAf(push, zmax);
+      BEGIN_NV04(push, NV50_3D(DEPTH_RANGE_NEAR(i)), 2);
+      PUSH_DATAf(push, zmin);
+      PUSH_DATAf(push, zmax);
 #endif
+   }
+
+   nv50->viewports_dirty = 0;
+}
+
+static void
+nv50_validate_window_rects(struct nv50_context *nv50)
+{
+   struct nouveau_pushbuf *push = nv50->base.pushbuf;
+   bool enable = nv50->window_rect.rects > 0 || nv50->window_rect.inclusive;
+   int i;
+
+   BEGIN_NV04(push, NV50_3D(CLIP_RECTS_EN), 1);
+   PUSH_DATA (push, enable);
+   if (!enable)
+      return;
+
+   BEGIN_NV04(push, NV50_3D(CLIP_RECTS_MODE), 1);
+   PUSH_DATA (push, !nv50->window_rect.inclusive);
+   BEGIN_NV04(push, NV50_3D(CLIP_RECT_HORIZ(0)), NV50_MAX_WINDOW_RECTANGLES * 2);
+   for (i = 0; i < nv50->window_rect.rects; i++) {
+      struct pipe_scissor_state *s = &nv50->window_rect.rect[i];
+      PUSH_DATA(push, (s->maxx << 16) | s->minx);
+      PUSH_DATA(push, (s->maxy << 16) | s->miny);
+   }
+   for (; i < NV50_MAX_WINDOW_RECTANGLES; i++) {
+      PUSH_DATA(push, 0);
+      PUSH_DATA(push, 0);
+   }
 }
 
-static INLINE void
+static inline void
 nv50_check_program_ucps(struct nv50_context *nv50,
                         struct nv50_program *vp, uint8_t mask)
 {
@@ -242,23 +321,40 @@ nv50_check_program_ucps(struct nv50_context *nv50,
 
    vp->vp.clpd_nr = n;
    if (likely(vp == nv50->vertprog)) {
-      nv50->dirty |= NV50_NEW_VERTPROG;
+      nv50->dirty_3d |= NV50_NEW_3D_VERTPROG;
       nv50_vertprog_validate(nv50);
    } else {
-      nv50->dirty |= NV50_NEW_GMTYPROG;
+      nv50->dirty_3d |= NV50_NEW_3D_GMTYPROG;
       nv50_gmtyprog_validate(nv50);
    }
    nv50_fp_linkage_validate(nv50);
 }
 
+/* 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
+ * nv50_validate_fb, otherwise that will override the RT count setting.
+ */
+static void
+nv50_validate_derived_2(struct nv50_context *nv50)
+{
+   struct nouveau_pushbuf *push = nv50->base.pushbuf;
+
+   if (nv50->zsa && nv50->zsa->pipe.alpha.enabled &&
+       nv50->framebuffer.nr_cbufs == 0) {
+      nv50_fb_set_null_rt(push, 0);
+      BEGIN_NV04(push, NV50_3D(RT_CONTROL), 1);
+      PUSH_DATA (push, (076543210 << 4) | 1);
+   }
+}
+
 static void
 nv50_validate_clip(struct nv50_context *nv50)
 {
    struct nouveau_pushbuf *push = nv50->base.pushbuf;
    struct nv50_program *vp;
-   uint8_t clip_enable;
+   uint8_t clip_enable = nv50->rast->pipe.clip_plane_enable;
 
-   if (nv50->dirty & NV50_NEW_CLIP) {
+   if (nv50->dirty_3d & NV50_NEW_3D_CLIP) {
       BEGIN_NV04(push, NV50_3D(CB_ADDR), 1);
       PUSH_DATA (push, (NV50_CB_AUX_UCP_OFFSET << 8) | NV50_CB_AUX);
       BEGIN_NI04(push, NV50_3D(CB_DATA(0)), PIPE_MAX_CLIP_PLANES * 4);
@@ -269,13 +365,20 @@ nv50_validate_clip(struct nv50_context *nv50)
    if (likely(!vp))
       vp = nv50->vertprog;
 
-   clip_enable = nv50->rast->pipe.clip_plane_enable;
+   if (clip_enable)
+      nv50_check_program_ucps(nv50, vp, clip_enable);
+
+   clip_enable &= vp->vp.clip_enable;
+   clip_enable |= vp->vp.cull_enable;
 
    BEGIN_NV04(push, NV50_3D(CLIP_DISTANCE_ENABLE), 1);
    PUSH_DATA (push, clip_enable);
 
-   if (clip_enable)
-      nv50_check_program_ucps(nv50, vp, clip_enable);
+   if (nv50->state.clip_mode != vp->vp.clip_mode) {
+      nv50->state.clip_mode = vp->vp.clip_mode;
+      BEGIN_NV04(push, NV50_3D(CLIP_DISTANCE_MODE), 1);
+      PUSH_DATA (push, vp->vp.clip_mode);
+   }
 }
 
 static void
@@ -325,6 +428,23 @@ nv50_validate_sample_mask(struct nv50_context *nv50)
    PUSH_DATA (push, mask[3]);
 }
 
+static void
+nv50_validate_min_samples(struct nv50_context *nv50)
+{
+   struct nouveau_pushbuf *push = nv50->base.pushbuf;
+   int samples;
+
+   if (nv50->screen->tesla->oclass < NVA3_3D_CLASS)
+      return;
+
+   samples = util_next_power_of_two(nv50->min_samples);
+   if (samples > 1)
+      samples |= NVA3_3D_SAMPLE_SHADING_ENABLE;
+
+   BEGIN_NV04(push, SUBC_3D(NVA3_3D_SAMPLE_SHADING), 1);
+   PUSH_DATA (push, samples);
+}
+
 static void
 nv50_switch_pipe_context(struct nv50_context *ctx_to)
 {
@@ -332,72 +452,85 @@ nv50_switch_pipe_context(struct nv50_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;
+
+   ctx_to->constbuf_dirty[0] =
+   ctx_to->constbuf_dirty[1] =
+   ctx_to->constbuf_dirty[2] = (1 << NV50_MAX_PIPE_CONSTBUFS) - 1;
 
    if (!ctx_to->vertex)
-      ctx_to->dirty &= ~(NV50_NEW_VERTEX | NV50_NEW_ARRAYS);
+      ctx_to->dirty_3d &= ~(NV50_NEW_3D_VERTEX | NV50_NEW_3D_ARRAYS);
 
    if (!ctx_to->vertprog)
-      ctx_to->dirty &= ~NV50_NEW_VERTPROG;
+      ctx_to->dirty_3d &= ~NV50_NEW_3D_VERTPROG;
    if (!ctx_to->fragprog)
-      ctx_to->dirty &= ~NV50_NEW_FRAGPROG;
+      ctx_to->dirty_3d &= ~NV50_NEW_3D_FRAGPROG;
 
    if (!ctx_to->blend)
-      ctx_to->dirty &= ~NV50_NEW_BLEND;
+      ctx_to->dirty_3d &= ~NV50_NEW_3D_BLEND;
    if (!ctx_to->rast)
 #ifdef NV50_SCISSORS_CLIPPING
-      ctx_to->dirty &= ~(NV50_NEW_RASTERIZER | NV50_NEW_SCISSOR);
+      ctx_to->dirty_3d &= ~(NV50_NEW_3D_RASTERIZER | NV50_NEW_3D_SCISSOR);
 #else
-      ctx_to->dirty &= ~NV50_NEW_RASTERIZER;
+      ctx_to->dirty_3d &= ~NV50_NEW_3D_RASTERIZER;
 #endif
    if (!ctx_to->zsa)
-      ctx_to->dirty &= ~NV50_NEW_ZSA;
+      ctx_to->dirty_3d &= ~NV50_NEW_3D_ZSA;
 
    ctx_to->screen->cur_ctx = ctx_to;
 }
 
-static struct state_validate {
-    void (*func)(struct nv50_context *);
-    uint32_t states;
-} validate_list[] = {
-    { nv50_validate_fb,            NV50_NEW_FRAMEBUFFER },
-    { nv50_validate_blend,         NV50_NEW_BLEND },
-    { nv50_validate_zsa,           NV50_NEW_ZSA },
-    { nv50_validate_sample_mask,   NV50_NEW_SAMPLE_MASK },
-    { nv50_validate_rasterizer,    NV50_NEW_RASTERIZER },
-    { nv50_validate_blend_colour,  NV50_NEW_BLEND_COLOUR },
-    { nv50_validate_stencil_ref,   NV50_NEW_STENCIL_REF },
-    { nv50_validate_stipple,       NV50_NEW_STIPPLE },
+static struct nv50_state_validate
+validate_list_3d[] = {
+    { nv50_validate_fb,            NV50_NEW_3D_FRAMEBUFFER },
+    { nv50_validate_blend,         NV50_NEW_3D_BLEND },
+    { nv50_validate_zsa,           NV50_NEW_3D_ZSA },
+    { nv50_validate_sample_mask,   NV50_NEW_3D_SAMPLE_MASK },
+    { nv50_validate_rasterizer,    NV50_NEW_3D_RASTERIZER },
+    { nv50_validate_blend_colour,  NV50_NEW_3D_BLEND_COLOUR },
+    { nv50_validate_stencil_ref,   NV50_NEW_3D_STENCIL_REF },
+    { nv50_validate_stipple,       NV50_NEW_3D_STIPPLE },
 #ifdef NV50_SCISSORS_CLIPPING
-    { nv50_validate_scissor,       NV50_NEW_SCISSOR | NV50_NEW_VIEWPORT |
-                                   NV50_NEW_RASTERIZER |
-                                   NV50_NEW_FRAMEBUFFER },
+    { nv50_validate_scissor,       NV50_NEW_3D_SCISSOR | NV50_NEW_3D_VIEWPORT |
+                                   NV50_NEW_3D_RASTERIZER |
+                                   NV50_NEW_3D_FRAMEBUFFER },
 #else
-    { nv50_validate_scissor,       NV50_NEW_SCISSOR },
+    { nv50_validate_scissor,       NV50_NEW_3D_SCISSOR },
 #endif
-    { nv50_validate_viewport,      NV50_NEW_VIEWPORT },
-    { nv50_vertprog_validate,      NV50_NEW_VERTPROG },
-    { nv50_gmtyprog_validate,      NV50_NEW_GMTYPROG },
-    { nv50_fragprog_validate,      NV50_NEW_FRAGPROG },
-    { nv50_fp_linkage_validate,    NV50_NEW_FRAGPROG | NV50_NEW_VERTPROG |
-                                   NV50_NEW_GMTYPROG | NV50_NEW_RASTERIZER },
-    { nv50_gp_linkage_validate,    NV50_NEW_GMTYPROG | NV50_NEW_VERTPROG },
-    { nv50_validate_derived_rs,    NV50_NEW_FRAGPROG | NV50_NEW_RASTERIZER |
-                                   NV50_NEW_VERTPROG | NV50_NEW_GMTYPROG },
-    { nv50_validate_clip,          NV50_NEW_CLIP | NV50_NEW_RASTERIZER |
-                                   NV50_NEW_VERTPROG | NV50_NEW_GMTYPROG },
-    { nv50_constbufs_validate,     NV50_NEW_CONSTBUF },
-    { nv50_validate_textures,      NV50_NEW_TEXTURES },
-    { nv50_validate_samplers,      NV50_NEW_SAMPLERS },
-    { nv50_stream_output_validate, NV50_NEW_STRMOUT |
-                                   NV50_NEW_VERTPROG | NV50_NEW_GMTYPROG },
-    { nv50_vertex_arrays_validate, NV50_NEW_VERTEX | NV50_NEW_ARRAYS }
+    { nv50_validate_viewport,      NV50_NEW_3D_VIEWPORT },
+    { nv50_validate_window_rects,  NV50_NEW_3D_WINDOW_RECTS },
+    { nv50_vertprog_validate,      NV50_NEW_3D_VERTPROG },
+    { nv50_gmtyprog_validate,      NV50_NEW_3D_GMTYPROG },
+    { nv50_fragprog_validate,      NV50_NEW_3D_FRAGPROG | NV50_NEW_3D_RASTERIZER |
+                                   NV50_NEW_3D_MIN_SAMPLES | NV50_NEW_3D_ZSA |
+                                   NV50_NEW_3D_FRAMEBUFFER},
+    { nv50_fp_linkage_validate,    NV50_NEW_3D_FRAGPROG | NV50_NEW_3D_VERTPROG |
+                                   NV50_NEW_3D_GMTYPROG | NV50_NEW_3D_RASTERIZER },
+    { nv50_gp_linkage_validate,    NV50_NEW_3D_GMTYPROG | NV50_NEW_3D_VERTPROG },
+    { nv50_validate_derived_rs,    NV50_NEW_3D_FRAGPROG | NV50_NEW_3D_RASTERIZER |
+                                   NV50_NEW_3D_VERTPROG | NV50_NEW_3D_GMTYPROG },
+    { nv50_validate_derived_2,     NV50_NEW_3D_ZSA | NV50_NEW_3D_FRAMEBUFFER },
+    { nv50_validate_clip,          NV50_NEW_3D_CLIP | NV50_NEW_3D_RASTERIZER |
+                                   NV50_NEW_3D_VERTPROG | NV50_NEW_3D_GMTYPROG },
+    { nv50_constbufs_validate,     NV50_NEW_3D_CONSTBUF },
+    { nv50_validate_textures,      NV50_NEW_3D_TEXTURES },
+    { nv50_validate_samplers,      NV50_NEW_3D_SAMPLERS },
+    { nv50_stream_output_validate, NV50_NEW_3D_STRMOUT |
+                                   NV50_NEW_3D_VERTPROG | NV50_NEW_3D_GMTYPROG },
+    { nv50_vertex_arrays_validate, NV50_NEW_3D_VERTEX | NV50_NEW_3D_ARRAYS },
+    { nv50_validate_min_samples,   NV50_NEW_3D_MIN_SAMPLES },
 };
-#define validate_list_len (sizeof(validate_list) / sizeof(validate_list[0]))
 
-boolean
-nv50_state_validate(struct nv50_context *nv50, uint32_t mask, unsigned words)
+bool
+nv50_state_validate(struct nv50_context *nv50, uint32_t mask,
+                    struct nv50_state_validate *validate_list, int size,
+                    uint32_t *dirty, struct nouveau_bufctx *bufctx)
 {
    uint32_t state_mask;
    int ret;
@@ -406,31 +539,43 @@ nv50_state_validate(struct nv50_context *nv50, uint32_t mask, unsigned words)
    if (nv50->screen->cur_ctx != nv50)
       nv50_switch_pipe_context(nv50);
 
-   state_mask = nv50->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 nv50_state_validate *validate = &validate_list[i];
 
          if (state_mask & validate->states)
             validate->func(nv50);
       }
-      nv50->dirty &= ~state_mask;
+      *dirty &= ~state_mask;
 
       if (nv50->state.rt_serialize) {
-         nv50->state.rt_serialize = FALSE;
+         nv50->state.rt_serialize = false;
          BEGIN_NV04(nv50->base.pushbuf, SUBC_3D(NV50_GRAPH_SERIALIZE), 1);
          PUSH_DATA (nv50->base.pushbuf, 0);
       }
 
-      nv50_bufctx_fence(nv50->bufctx_3d, FALSE);
+      nv50_bufctx_fence(bufctx, false);
    }
-   nouveau_pushbuf_bufctx(nv50->base.pushbuf, nv50->bufctx_3d);
+   nouveau_pushbuf_bufctx(nv50->base.pushbuf, bufctx);
    ret = nouveau_pushbuf_validate(nv50->base.pushbuf);
 
+   return !ret;
+}
+
+bool
+nv50_state_validate_3d(struct nv50_context *nv50, uint32_t mask)
+{
+   bool ret;
+
+   ret = nv50_state_validate(nv50, mask, validate_list_3d,
+                             ARRAY_SIZE(validate_list_3d), &nv50->dirty_3d,
+                             nv50->bufctx_3d);
+
    if (unlikely(nv50->state.flushed)) {
-      nv50->state.flushed = FALSE;
-      nv50_bufctx_fence(nv50->bufctx_3d, TRUE);
+      nv50->state.flushed = false;
+      nv50_bufctx_fence(nv50->bufctx_3d, true);
    }
-   return !ret;
+   return ret;
 }