radeonsi: add an si_set_rw_shader_buffer convenience function
[mesa.git] / src / gallium / drivers / radeonsi / si_state_viewport.c
index a96eb8adc1349358ce1143fa0697f96ca614bbf1..76c56447eb03b8a0147ec6cc88935f76e08fd52f 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2012 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,9 +22,7 @@
  * USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#include "si_pipe.h"
-#include "sid.h"
-#include "radeon/r600_cs.h"
+#include "si_build_pm4.h"
 #include "util/u_viewport.h"
 #include "tgsi/tgsi_scan.h"
 
@@ -45,7 +44,7 @@ static void si_set_scissor_states(struct pipe_context *pctx,
                return;
 
        ctx->scissors.dirty_mask |= ((1 << num_scissors) - 1) << start_slot;
-       si_mark_atom_dirty(ctx, &ctx->scissors.atom);
+       si_mark_atom_dirty(ctx, &ctx->atoms.s.scissors);
 }
 
 /* Since the guard band disables clipping, we have to clip per-pixel
@@ -63,13 +62,6 @@ static void si_get_scissor_from_viewport(struct si_context *ctx,
        maxx = vp->scale[0] + vp->translate[0];
        maxy = vp->scale[1] + vp->translate[1];
 
-       /* r600_draw_rectangle sets this. Disable the scissor. */
-       if (minx == -1 && miny == -1 && maxx == 1 && maxy == 1) {
-               scissor->minx = scissor->miny = 0;
-               scissor->maxx = scissor->maxy = SI_MAX_SCISSOR;
-               return;
-       }
-
        /* Handle inverted viewports. */
        if (minx > maxx) {
                tmp = minx;
@@ -115,10 +107,11 @@ static void si_scissor_make_union(struct si_signed_scissor *out,
        out->miny = MIN2(out->miny, in->miny);
        out->maxx = MAX2(out->maxx, in->maxx);
        out->maxy = MAX2(out->maxy, in->maxy);
+       out->quant_mode = MIN2(out->quant_mode, in->quant_mode);
 }
 
 static void si_emit_one_scissor(struct si_context *ctx,
-                               struct radeon_winsys_cs *cs,
+                               struct radeon_cmdbuf *cs,
                                struct si_signed_scissor *vp_scissor,
                                struct pipe_scissor_state *scissor)
 {
@@ -134,6 +127,18 @@ static void si_emit_one_scissor(struct si_context *ctx,
        if (scissor)
                si_clip_scissor(&final, scissor);
 
+       /* Workaround for a hw bug on SI that occurs when PA_SU_HARDWARE_-
+        * SCREEN_OFFSET != 0 and any_scissor.BR_X/Y <= 0.
+        */
+       if (ctx->chip_class == SI && (final.maxx == 0 || final.maxy == 0)) {
+               radeon_emit(cs, S_028250_TL_X(1) |
+                               S_028250_TL_Y(1) |
+                               S_028250_WINDOW_OFFSET_DISABLE(1));
+               radeon_emit(cs, S_028254_BR_X(1) |
+                               S_028254_BR_Y(1));
+               return;
+       }
+
        radeon_emit(cs, S_028250_TL_X(final.minx) |
                        S_028250_TL_Y(final.miny) |
                        S_028250_WINDOW_OFFSET_DISABLE(1));
@@ -141,27 +146,67 @@ static void si_emit_one_scissor(struct si_context *ctx,
                        S_028254_BR_Y(final.maxy));
 }
 
-/* the range is [-MAX, MAX] */
-#define GET_MAX_VIEWPORT_RANGE(rctx) (32768)
-
-static void si_emit_guardband(struct si_context *ctx,
-                             struct si_signed_scissor *vp_as_scissor)
+static void si_emit_guardband(struct si_context *ctx)
 {
-       struct radeon_winsys_cs *cs = ctx->b.gfx.cs;
+       const struct si_state_rasterizer *rs = ctx->queued.named.rasterizer;
+       struct si_signed_scissor vp_as_scissor;
        struct pipe_viewport_state vp;
        float left, top, right, bottom, max_range, guardband_x, guardband_y;
        float discard_x, discard_y;
 
+       if (ctx->vs_writes_viewport_index) {
+               /* Shaders can draw to any viewport. Make a union of all
+                * viewports. */
+               vp_as_scissor = ctx->viewports.as_scissor[0];
+               for (unsigned i = 1; i < SI_MAX_VIEWPORTS; i++) {
+                       si_scissor_make_union(&vp_as_scissor,
+                                             &ctx->viewports.as_scissor[i]);
+               }
+       } else {
+               vp_as_scissor = ctx->viewports.as_scissor[0];
+       }
+
+       /* Blits don't set the viewport state. The vertex shader determines
+        * the viewport size by scaling the coordinates, so we don't know
+        * how large the viewport is. Assume the worst case.
+        */
+       if (ctx->vs_disables_clipping_viewport)
+               vp_as_scissor.quant_mode = SI_QUANT_MODE_16_8_FIXED_POINT_1_256TH;
+
+       /* Determine the optimal hardware screen offset to center the viewport
+        * within the viewport range in order to maximize the guardband size.
+        */
+       int hw_screen_offset_x = (vp_as_scissor.maxx + vp_as_scissor.minx) / 2;
+       int hw_screen_offset_y = (vp_as_scissor.maxy + vp_as_scissor.miny) / 2;
+
+       const unsigned hw_screen_offset_max = 8176;
+       /* SI-CI need to align the offset to an ubertile consisting of all SEs. */
+       const unsigned hw_screen_offset_alignment =
+               ctx->chip_class >= VI ? 16 : MAX2(ctx->screen->se_tile_repeat, 16);
+
+       hw_screen_offset_x = CLAMP(hw_screen_offset_x, 0, hw_screen_offset_max);
+       hw_screen_offset_y = CLAMP(hw_screen_offset_y, 0, hw_screen_offset_max);
+
+       /* Align the screen offset by dropping the low bits. */
+       hw_screen_offset_x &= ~(hw_screen_offset_alignment - 1);
+       hw_screen_offset_y &= ~(hw_screen_offset_alignment - 1);
+
+       /* Apply the offset to center the viewport and maximize the guardband. */
+       vp_as_scissor.minx -= hw_screen_offset_x;
+       vp_as_scissor.maxx -= hw_screen_offset_x;
+       vp_as_scissor.miny -= hw_screen_offset_y;
+       vp_as_scissor.maxy -= hw_screen_offset_y;
+
        /* Reconstruct the viewport transformation from the scissor. */
-       vp.translate[0] = (vp_as_scissor->minx + vp_as_scissor->maxx) / 2.0;
-       vp.translate[1] = (vp_as_scissor->miny + vp_as_scissor->maxy) / 2.0;
-       vp.scale[0] = vp_as_scissor->maxx - vp.translate[0];
-       vp.scale[1] = vp_as_scissor->maxy - vp.translate[1];
+       vp.translate[0] = (vp_as_scissor.minx + vp_as_scissor.maxx) / 2.0;
+       vp.translate[1] = (vp_as_scissor.miny + vp_as_scissor.maxy) / 2.0;
+       vp.scale[0] = vp_as_scissor.maxx - vp.translate[0];
+       vp.scale[1] = vp_as_scissor.maxy - vp.translate[1];
 
        /* Treat a 0x0 viewport as 1x1 to prevent division by zero. */
-       if (vp_as_scissor->minx == vp_as_scissor->maxx)
+       if (vp_as_scissor.minx == vp_as_scissor.maxx)
                vp.scale[0] = 0.5;
-       if (vp_as_scissor->miny == vp_as_scissor->maxy)
+       if (vp_as_scissor.miny == vp_as_scissor.maxy)
                vp.scale[1] = 0.5;
 
        /* Find the biggest guard band that is inside the supported viewport
@@ -171,9 +216,11 @@ static void si_emit_guardband(struct si_context *ctx,
         * This is done by applying the inverse viewport transformation
         * on the viewport limits to get those limits in clip space.
         *
-        * Use a limit one pixel smaller to allow for some precision error.
+        * The viewport range is [-max_viewport_size/2, max_viewport_size/2].
         */
-       max_range = GET_MAX_VIEWPORT_RANGE(ctx) - 1;
+       static unsigned max_viewport_size[] = {65535, 16383, 4095};
+       assert(vp_as_scissor.quant_mode < ARRAY_SIZE(max_viewport_size));
+       max_range = max_viewport_size[vp_as_scissor.quant_mode] / 2;
        left   = (-max_range - vp.translate[0]) / vp.scale[0];
        right  = ( max_range - vp.translate[0]) / vp.scale[0];
        top    = (-max_range - vp.translate[1]) / vp.scale[1];
@@ -187,11 +234,9 @@ static void si_emit_guardband(struct si_context *ctx,
        discard_x = 1.0;
        discard_y = 1.0;
 
-       if (unlikely(ctx->current_rast_prim < PIPE_PRIM_TRIANGLES) &&
-           ctx->queued.named.rasterizer) {
+       if (unlikely(util_prim_is_points_or_lines(ctx->current_rast_prim))) {
                /* When rendering wide points or lines, we need to be more
                 * conservative about when to discard them entirely. */
-               const struct si_state_rasterizer *rs = ctx->queued.named.rasterizer;
                float pixels;
 
                if (ctx->current_rast_prim == PIPE_PRIM_POINTS)
@@ -209,27 +254,34 @@ static void si_emit_guardband(struct si_context *ctx,
                discard_y = MIN2(discard_y, guardband_y);
        }
 
-       /* If any of the GB registers is updated, all of them must be updated. */
-       radeon_set_context_reg_seq(cs, R_028BE8_PA_CL_GB_VERT_CLIP_ADJ, 4);
-
-       radeon_emit(cs, fui(guardband_y)); /* R_028BE8_PA_CL_GB_VERT_CLIP_ADJ */
-       radeon_emit(cs, fui(discard_y));   /* R_028BEC_PA_CL_GB_VERT_DISC_ADJ */
-       radeon_emit(cs, fui(guardband_x)); /* R_028BF0_PA_CL_GB_HORZ_CLIP_ADJ */
-       radeon_emit(cs, fui(discard_x));   /* R_028BF4_PA_CL_GB_HORZ_DISC_ADJ */
+       /* If any of the GB registers is updated, all of them must be updated.
+        * R_028BE8_PA_CL_GB_VERT_CLIP_ADJ, R_028BEC_PA_CL_GB_VERT_DISC_ADJ
+        * R_028BF0_PA_CL_GB_HORZ_CLIP_ADJ, R_028BF4_PA_CL_GB_HORZ_DISC_ADJ
+        */
+       unsigned initial_cdw = ctx->gfx_cs->current.cdw;
+       radeon_opt_set_context_reg4(ctx, R_028BE8_PA_CL_GB_VERT_CLIP_ADJ,
+                                   SI_TRACKED_PA_CL_GB_VERT_CLIP_ADJ,
+                                   fui(guardband_y), fui(discard_y),
+                                   fui(guardband_x), fui(discard_x));
+       radeon_opt_set_context_reg(ctx, R_028234_PA_SU_HARDWARE_SCREEN_OFFSET,
+                                  SI_TRACKED_PA_SU_HARDWARE_SCREEN_OFFSET,
+                                  S_028234_HW_SCREEN_OFFSET_X(hw_screen_offset_x >> 4) |
+                                  S_028234_HW_SCREEN_OFFSET_Y(hw_screen_offset_y >> 4));
+       radeon_opt_set_context_reg(ctx, R_028BE4_PA_SU_VTX_CNTL,
+                                  SI_TRACKED_PA_SU_VTX_CNTL,
+                                  S_028BE4_PIX_CENTER(rs->half_pixel_center) |
+                                  S_028BE4_QUANT_MODE(V_028BE4_X_16_8_FIXED_POINT_1_256TH +
+                                                      vp_as_scissor.quant_mode));
+       if (initial_cdw != ctx->gfx_cs->current.cdw)
+               ctx->context_roll_counter++;
 }
 
-static void si_emit_scissors(struct r600_common_context *rctx, struct r600_atom *atom)
+static void si_emit_scissors(struct si_context *ctx)
 {
-       struct si_context *ctx = (struct si_context *)rctx;
-       struct radeon_winsys_cs *cs = ctx->b.gfx.cs;
+       struct radeon_cmdbuf *cs = ctx->gfx_cs;
        struct pipe_scissor_state *states = ctx->scissors.states;
        unsigned mask = ctx->scissors.dirty_mask;
-       bool scissor_enabled = false;
-       struct si_signed_scissor max_vp_scissor;
-       int i;
-
-       if (ctx->queued.named.rasterizer)
-               scissor_enabled = ctx->queued.named.rasterizer->scissor_enable;
+       bool scissor_enabled = ctx->queued.named.rasterizer->scissor_enable;
 
        /* The simple case: Only 1 viewport is active. */
        if (!ctx->vs_writes_viewport_index) {
@@ -240,17 +292,10 @@ static void si_emit_scissors(struct r600_common_context *rctx, struct r600_atom
 
                radeon_set_context_reg_seq(cs, R_028250_PA_SC_VPORT_SCISSOR_0_TL, 2);
                si_emit_one_scissor(ctx, cs, vp, scissor_enabled ? &states[0] : NULL);
-               si_emit_guardband(ctx, vp);
                ctx->scissors.dirty_mask &= ~1; /* clear one bit */
                return;
        }
 
-       /* Shaders can draw to any viewport. Make a union of all viewports. */
-       max_vp_scissor = ctx->viewports.as_scissor[0];
-       for (i = 1; i < SI_MAX_VIEWPORTS; i++)
-               si_scissor_make_union(&max_vp_scissor,
-                                     &ctx->viewports.as_scissor[i]);
-
        while (mask) {
                int start, count, i;
 
@@ -263,7 +308,6 @@ static void si_emit_scissors(struct r600_common_context *rctx, struct r600_atom
                                            scissor_enabled ? &states[i] : NULL);
                }
        }
-       si_emit_guardband(ctx, &max_vp_scissor);
        ctx->scissors.dirty_mask = 0;
 }
 
@@ -278,24 +322,48 @@ static void si_set_viewport_states(struct pipe_context *pctx,
 
        for (i = 0; i < num_viewports; i++) {
                unsigned index = start_slot + i;
+               struct si_signed_scissor *scissor = &ctx->viewports.as_scissor[index];
 
                ctx->viewports.states[index] = state[i];
-               si_get_scissor_from_viewport(ctx, &state[i],
-                                            &ctx->viewports.as_scissor[index]);
+
+               si_get_scissor_from_viewport(ctx, &state[i], scissor);
+
+               unsigned w = scissor->maxx - scissor->minx;
+               unsigned h = scissor->maxy - scissor->miny;
+               unsigned max_extent = MAX2(w, h);
+
+               /* Determine the best quantization mode (subpixel precision),
+                * but also leave enough space for the guardband.
+                *
+                * Note that primitive binning requires QUANT_MODE == 16_8 on Vega10
+                * and Raven1. What we do depends on the chip:
+                * - Vega10: Never use primitive binning.
+                * - Raven1: Always use QUANT_MODE == 16_8.
+                */
+               if (ctx->family == CHIP_RAVEN)
+                       max_extent = 16384; /* Use QUANT_MODE == 16_8. */
+
+               if (max_extent <= 1024) /* 4K scanline area for guardband */
+                       scissor->quant_mode = SI_QUANT_MODE_12_12_FIXED_POINT_1_4096TH;
+               else if (max_extent <= 4096) /* 16K scanline area for guardband */
+                       scissor->quant_mode = SI_QUANT_MODE_14_10_FIXED_POINT_1_1024TH;
+               else /* 64K scanline area for guardband */
+                       scissor->quant_mode = SI_QUANT_MODE_16_8_FIXED_POINT_1_256TH;
        }
 
        mask = ((1 << num_viewports) - 1) << start_slot;
        ctx->viewports.dirty_mask |= mask;
        ctx->viewports.depth_range_dirty_mask |= mask;
        ctx->scissors.dirty_mask |= mask;
-       si_mark_atom_dirty(ctx, &ctx->viewports.atom);
-       si_mark_atom_dirty(ctx, &ctx->scissors.atom);
+       si_mark_atom_dirty(ctx, &ctx->atoms.s.viewports);
+       si_mark_atom_dirty(ctx, &ctx->atoms.s.guardband);
+       si_mark_atom_dirty(ctx, &ctx->atoms.s.scissors);
 }
 
 static void si_emit_one_viewport(struct si_context *ctx,
                                 struct pipe_viewport_state *state)
 {
-       struct radeon_winsys_cs *cs = ctx->b.gfx.cs;
+       struct radeon_cmdbuf *cs = ctx->gfx_cs;
 
        radeon_emit(cs, fui(state->scale[0]));
        radeon_emit(cs, fui(state->translate[0]));
@@ -307,7 +375,7 @@ static void si_emit_one_viewport(struct si_context *ctx,
 
 static void si_emit_viewports(struct si_context *ctx)
 {
-       struct radeon_winsys_cs *cs = ctx->b.gfx.cs;
+       struct radeon_cmdbuf *cs = ctx->gfx_cs;
        struct pipe_viewport_state *states = ctx->viewports.states;
        unsigned mask = ctx->viewports.dirty_mask;
 
@@ -335,23 +403,34 @@ static void si_emit_viewports(struct si_context *ctx)
        ctx->viewports.dirty_mask = 0;
 }
 
+static inline void
+si_viewport_zmin_zmax(const struct pipe_viewport_state *vp, bool halfz,
+                     bool window_space_position, float *zmin, float *zmax)
+{
+       if (window_space_position) {
+               *zmin = 0;
+               *zmax = 1;
+               return;
+       }
+       util_viewport_zmin_zmax(vp, halfz, zmin, zmax);
+}
+
 static void si_emit_depth_ranges(struct si_context *ctx)
 {
-       struct radeon_winsys_cs *cs = ctx->b.gfx.cs;
+       struct radeon_cmdbuf *cs = ctx->gfx_cs;
        struct pipe_viewport_state *states = ctx->viewports.states;
        unsigned mask = ctx->viewports.depth_range_dirty_mask;
-       bool clip_halfz = false;
+       bool clip_halfz = ctx->queued.named.rasterizer->clip_halfz;
+       bool window_space = ctx->vs_disables_clipping_viewport;
        float zmin, zmax;
 
-       if (ctx->queued.named.rasterizer)
-               clip_halfz = ctx->queued.named.rasterizer->clip_halfz;
-
        /* The simple case: Only 1 viewport is active. */
        if (!ctx->vs_writes_viewport_index) {
                if (!(mask & 1))
                        return;
 
-               util_viewport_zmin_zmax(&states[0], clip_halfz, &zmin, &zmax);
+               si_viewport_zmin_zmax(&states[0], clip_halfz, window_space,
+                                     &zmin, &zmax);
 
                radeon_set_context_reg_seq(cs, R_0282D0_PA_SC_VPORT_ZMIN_0, 2);
                radeon_emit(cs, fui(zmin));
@@ -368,7 +447,8 @@ static void si_emit_depth_ranges(struct si_context *ctx)
                radeon_set_context_reg_seq(cs, R_0282D0_PA_SC_VPORT_ZMIN_0 +
                                           start * 4 * 2, count * 2);
                for (i = start; i < start+count; i++) {
-                       util_viewport_zmin_zmax(&states[i], clip_halfz, &zmin, &zmax);
+                       si_viewport_zmin_zmax(&states[i], clip_halfz, window_space,
+                                             &zmin, &zmax);
                        radeon_emit(cs, fui(zmin));
                        radeon_emit(cs, fui(zmax));
                }
@@ -376,21 +456,23 @@ static void si_emit_depth_ranges(struct si_context *ctx)
        ctx->viewports.depth_range_dirty_mask = 0;
 }
 
-static void si_emit_viewport_states(struct r600_common_context *rctx,
-                                   struct r600_atom *atom)
+static void si_emit_viewport_states(struct si_context *ctx)
 {
-       struct si_context *ctx = (struct si_context *)rctx;
        si_emit_viewports(ctx);
        si_emit_depth_ranges(ctx);
 }
 
 /**
+ * This reacts to 2 state changes:
+ * - VS.writes_viewport_index
+ * - VS output position in window space (enable/disable)
+ *
  * Normally, we only emit 1 viewport and 1 scissor if no shader is using
  * the VIEWPORT_INDEX output, and emitting the other viewports and scissors
  * is delayed. When a shader with VIEWPORT_INDEX appears, this should be
  * called to emit the rest.
  */
-void si_update_vs_writes_viewport_index(struct si_context *ctx)
+void si_update_vs_viewport_state(struct si_context *ctx)
 {
        struct tgsi_shader_info *info = si_get_vs_info(ctx);
        bool vs_window_space;
@@ -405,30 +487,119 @@ void si_update_vs_writes_viewport_index(struct si_context *ctx)
        if (ctx->vs_disables_clipping_viewport != vs_window_space) {
                ctx->vs_disables_clipping_viewport = vs_window_space;
                ctx->scissors.dirty_mask = (1 << SI_MAX_VIEWPORTS) - 1;
-               si_mark_atom_dirty(ctx, &ctx->scissors.atom);
+               ctx->viewports.depth_range_dirty_mask = (1 << SI_MAX_VIEWPORTS) - 1;
+               si_mark_atom_dirty(ctx, &ctx->atoms.s.scissors);
+               si_mark_atom_dirty(ctx, &ctx->atoms.s.viewports);
        }
 
        /* Viewport index handling. */
+       if (ctx->vs_writes_viewport_index == info->writes_viewport_index)
+               return;
+
+       /* This changes how the guardband is computed. */
        ctx->vs_writes_viewport_index = info->writes_viewport_index;
+       si_mark_atom_dirty(ctx, &ctx->atoms.s.guardband);
+
        if (!ctx->vs_writes_viewport_index)
                return;
 
        if (ctx->scissors.dirty_mask)
-           si_mark_atom_dirty(ctx, &ctx->scissors.atom);
+           si_mark_atom_dirty(ctx, &ctx->atoms.s.scissors);
 
        if (ctx->viewports.dirty_mask ||
            ctx->viewports.depth_range_dirty_mask)
-           si_mark_atom_dirty(ctx, &ctx->viewports.atom);
+           si_mark_atom_dirty(ctx, &ctx->atoms.s.viewports);
+}
+
+static void si_emit_window_rectangles(struct si_context *sctx)
+{
+       /* There are four clipping rectangles. Their corner coordinates are inclusive.
+        * Every pixel is assigned a number from 0 and 15 by setting bits 0-3 depending
+        * on whether the pixel is inside cliprects 0-3, respectively. For example,
+        * if a pixel is inside cliprects 0 and 1, but outside 2 and 3, it is assigned
+        * the number 3 (binary 0011).
+        *
+        * If CLIPRECT_RULE & (1 << number), the pixel is rasterized.
+        */
+       struct radeon_cmdbuf *cs = sctx->gfx_cs;
+       static const unsigned outside[4] = {
+               /* outside rectangle 0 */
+               V_02820C_OUT |
+               V_02820C_IN_1 |
+               V_02820C_IN_2 |
+               V_02820C_IN_21 |
+               V_02820C_IN_3 |
+               V_02820C_IN_31 |
+               V_02820C_IN_32 |
+               V_02820C_IN_321,
+               /* outside rectangles 0, 1 */
+               V_02820C_OUT |
+               V_02820C_IN_2 |
+               V_02820C_IN_3 |
+               V_02820C_IN_32,
+               /* outside rectangles 0, 1, 2 */
+               V_02820C_OUT |
+               V_02820C_IN_3,
+               /* outside rectangles 0, 1, 2, 3 */
+               V_02820C_OUT,
+       };
+       const unsigned disabled = 0xffff; /* all inside and outside cases */
+       unsigned num_rectangles = sctx->num_window_rectangles;
+       struct pipe_scissor_state *rects = sctx->window_rectangles;
+       unsigned rule;
+
+       assert(num_rectangles <= 4);
+
+       if (num_rectangles == 0)
+               rule = disabled;
+       else if (sctx->window_rectangles_include)
+               rule = ~outside[num_rectangles - 1];
+       else
+               rule = outside[num_rectangles - 1];
+
+       radeon_opt_set_context_reg(sctx, R_02820C_PA_SC_CLIPRECT_RULE,
+                                  SI_TRACKED_PA_SC_CLIPRECT_RULE, rule);
+       if (num_rectangles == 0)
+               return;
+
+       radeon_set_context_reg_seq(cs, R_028210_PA_SC_CLIPRECT_0_TL,
+                                  num_rectangles * 2);
+       for (unsigned i = 0; i < num_rectangles; i++) {
+               radeon_emit(cs, S_028210_TL_X(rects[i].minx) |
+                               S_028210_TL_Y(rects[i].miny));
+               radeon_emit(cs, S_028214_BR_X(rects[i].maxx) |
+                               S_028214_BR_Y(rects[i].maxy));
+       }
+}
+
+static void si_set_window_rectangles(struct pipe_context *ctx,
+                                    boolean include,
+                                    unsigned num_rectangles,
+                                    const struct pipe_scissor_state *rects)
+{
+       struct si_context *sctx = (struct si_context *)ctx;
+
+       sctx->num_window_rectangles = num_rectangles;
+       sctx->window_rectangles_include = include;
+       if (num_rectangles) {
+               memcpy(sctx->window_rectangles, rects,
+                      sizeof(*rects) * num_rectangles);
+       }
+
+       si_mark_atom_dirty(sctx, &sctx->atoms.s.window_rectangles);
 }
 
 void si_init_viewport_functions(struct si_context *ctx)
 {
-       ctx->scissors.atom.emit = si_emit_scissors;
-       ctx->viewports.atom.emit = si_emit_viewport_states;
+       ctx->atoms.s.guardband.emit = si_emit_guardband;
+       ctx->atoms.s.scissors.emit = si_emit_scissors;
+       ctx->atoms.s.viewports.emit = si_emit_viewport_states;
+       ctx->atoms.s.window_rectangles.emit = si_emit_window_rectangles;
 
-       ctx->scissors.atom.num_dw = (2 + 16 * 2) + 6;
-       ctx->viewports.atom.num_dw = 2 + 16 * 6;
+       ctx->b.set_scissor_states = si_set_scissor_states;
+       ctx->b.set_viewport_states = si_set_viewport_states;
+       ctx->b.set_window_rectangles = si_set_window_rectangles;
 
-       ctx->b.b.set_scissor_states = si_set_scissor_states;
-       ctx->b.b.set_viewport_states = si_set_viewport_states;
+       for (unsigned i = 0; i < 16; i++)
+               ctx->viewports.as_scissor[i].quant_mode = SI_QUANT_MODE_16_8_FIXED_POINT_1_256TH;
 }