1/**********************************************************
2 * Copyright 2008-2009 VMware, Inc.  All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 **********************************************************/
25
26#include "util/u_inlines.h"
27#include "pipe/p_defines.h"
28#include "util/u_math.h"
29#include "util/format/u_format.h"
30
31#include "svga_context.h"
32#include "svga_state.h"
33#include "svga_cmd.h"
34#include "svga_debug.h"
35#include "svga_screen.h"
36#include "svga_surface.h"
37#include "svga_resource_texture.h"
38
39
40/*
41 * flush our command buffer after the 8th distinct render target
42 *
43 * This helps improve the surface cache behaviour in the face of the
44 * large number of single-use render targets generated by EXA and the xorg
45 * state tracker.  Without this we can reference hundreds of individual
46 * render targets from a command buffer, which leaves little scope for
47 * sharing or reuse of those targets.
48 */
49#define MAX_RT_PER_BATCH 8
50
51
52
53static enum pipe_error
54emit_fb_vgpu9(struct svga_context *svga)
55{
56   struct svga_screen *svgascreen = svga_screen(svga->pipe.screen);
57   const struct pipe_framebuffer_state *curr = &svga->curr.framebuffer;
58   struct pipe_framebuffer_state *hw = &svga->state.hw_clear.framebuffer;
59   boolean reemit = svga->rebind.flags.rendertargets;
60   unsigned i;
61   enum pipe_error ret;
62
63   assert(!svga_have_vgpu10(svga));
64
65   /*
66    * We need to reemit non-null surface bindings, even when they are not
67    * dirty, to ensure that the resources are paged in.
68    */
69
70   for (i = 0; i < svgascreen->max_color_buffers; i++) {
71      if ((curr->cbufs[i] != hw->cbufs[i]) || (reemit && hw->cbufs[i])) {
72         if (svga->curr.nr_fbs++ > MAX_RT_PER_BATCH)
73            return PIPE_ERROR_OUT_OF_MEMORY;
74
75         /* Check to see if we need to propagate the render target surface */
76         if (hw->cbufs[i] && svga_surface_needs_propagation(hw->cbufs[i]))
77            svga_propagate_surface(svga, hw->cbufs[i], TRUE);
78
79         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_COLOR0 + i,
80                                      curr->cbufs[i]);
81         if (ret != PIPE_OK)
82            return ret;
83
84         pipe_surface_reference(&hw->cbufs[i], curr->cbufs[i]);
85      }
86
87      /* Set the rendered-to flag */
88      struct pipe_surface *s = curr->cbufs[i];
89      if (s) {
90         svga_set_texture_rendered_to(svga_texture(s->texture));
91      }
92   }
93
94   if ((curr->zsbuf != hw->zsbuf) || (reemit && hw->zsbuf)) {
95      ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_DEPTH, curr->zsbuf);
96      if (ret != PIPE_OK)
97         return ret;
98
99      /* Check to see if we need to propagate the depth stencil surface */
100      if (hw->zsbuf && svga_surface_needs_propagation(hw->zsbuf))
101         svga_propagate_surface(svga, hw->zsbuf, TRUE);
102
103      if (curr->zsbuf &&
104          util_format_is_depth_and_stencil(curr->zsbuf->format)) {
105         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL,
106                                      curr->zsbuf);
107         if (ret != PIPE_OK)
108            return ret;
109      }
110      else {
111         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, NULL);
112         if (ret != PIPE_OK)
113            return ret;
114      }
115
116      pipe_surface_reference(&hw->zsbuf, curr->zsbuf);
117
118      /* Set the rendered-to flag */
119      struct pipe_surface *s = curr->zsbuf;
120      if (s) {
121         svga_set_texture_rendered_to(svga_texture(s->texture));
122      }
123   }
124
125   return PIPE_OK;
126}
127
128
129/*
130 * Rebind rendertargets.
131 *
132 * Similar to emit_framebuffer, but without any state checking/update.
133 *
134 * Called at the beginning of every new command buffer to ensure that
135 * non-dirty rendertargets are properly paged-in.
136 */
137static enum pipe_error
138svga_reemit_framebuffer_bindings_vgpu9(struct svga_context *svga)
139{
140   struct svga_screen *svgascreen = svga_screen(svga->pipe.screen);
141   struct pipe_framebuffer_state *hw = &svga->state.hw_clear.framebuffer;
142   unsigned i;
143   enum pipe_error ret;
144
145   assert(!svga_have_vgpu10(svga));
146
147   for (i = 0; i < svgascreen->max_color_buffers; i++) {
148      if (hw->cbufs[i]) {
149         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_COLOR0 + i,
150                                      hw->cbufs[i]);
151         if (ret != PIPE_OK) {
152            return ret;
153         }
154      }
155   }
156
157   if (hw->zsbuf) {
158      ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_DEPTH, hw->zsbuf);
159      if (ret != PIPE_OK) {
160         return ret;
161      }
162
163      if (hw->zsbuf &&
164          util_format_is_depth_and_stencil(hw->zsbuf->format)) {
165         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, hw->zsbuf);
166         if (ret != PIPE_OK) {
167            return ret;
168         }
169      }
170      else {
171         ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, NULL);
172         if (ret != PIPE_OK) {
173            return ret;
174         }
175      }
176   }
177
178   return PIPE_OK;
179}
180
181
182
183static enum pipe_error
184emit_fb_vgpu10(struct svga_context *svga)
185{
186   const struct svga_screen *ss = svga_screen(svga->pipe.screen);
187   struct pipe_surface *rtv[SVGA3D_MAX_RENDER_TARGETS];
188   struct pipe_surface *dsv;
189   struct pipe_framebuffer_state *curr = &svga->curr.framebuffer;
190   struct pipe_framebuffer_state *hw = &svga->state.hw_clear.framebuffer;
191   const unsigned num_color = MAX2(curr->nr_cbufs, hw->nr_cbufs);
192   int last_rtv = -1;
193   unsigned i;
194   enum pipe_error ret = PIPE_OK;
195
196   assert(svga_have_vgpu10(svga));
197
198   /* Reset the has_backed_views flag.
199    * The flag is set in svga_validate_surface_view() if
200    * a backed surface view is used.
201    */
202   svga->state.hw_draw.has_backed_views = FALSE;
203
204   /* Setup render targets array.  Note that we loop over the max of the
205    * number of previously bound buffers and the new buffers to unbind
206    * any previously bound buffers when the new number of buffers is less
207    * than the old number of buffers.
208    */
209   for (i = 0; i < num_color; i++) {
210      if (curr->cbufs[i]) {
211         struct pipe_surface *s = curr->cbufs[i];
212
213         if (curr->cbufs[i] != hw->cbufs[i]) {
214            rtv[i] = svga_validate_surface_view(svga, svga_surface(s));
215            if (rtv[i] == NULL) {
216               return PIPE_ERROR_OUT_OF_MEMORY;
217            }
218         } else {
219           rtv[i] = svga->state.hw_clear.rtv[i];
220         }
221
222         assert(svga_surface(rtv[i])->view_id != SVGA3D_INVALID_ID);
223         last_rtv = i;
224
225         /* Set the rendered-to flag */
226         svga_set_texture_rendered_to(svga_texture(s->texture));
227      }
228      else {
229         rtv[i] = NULL;
230      }
231   }
232
233   /* Setup depth stencil view */
234   if (curr->zsbuf) {
235      struct pipe_surface *s = curr->zsbuf;
236
237      if (curr->zsbuf != hw->zsbuf) {
238         dsv = svga_validate_surface_view(svga, svga_surface(curr->zsbuf));
239         if (!dsv) {
240            return PIPE_ERROR_OUT_OF_MEMORY;
241         }
242      } else {
243         dsv = svga->state.hw_clear.dsv;
244      }
245
246      /* Set the rendered-to flag */
247      svga_set_texture_rendered_to(svga_texture(s->texture));
248   }
249   else {
250      dsv = NULL;
251   }
252
253   /* avoid emitting redundant SetRenderTargets command */
254   if ((num_color != svga->state.hw_clear.num_rendertargets) ||
255       (dsv != svga->state.hw_clear.dsv) ||
256       memcmp(rtv, svga->state.hw_clear.rtv, num_color * sizeof(rtv[0]))) {
257
258      ret = SVGA3D_vgpu10_SetRenderTargets(svga->swc, num_color, rtv, dsv);
259      if (ret != PIPE_OK)
260         return ret;
261
262      /* number of render targets sent to the device, not including trailing
263       * unbound render targets.
264       */
265      for (i = 0; i < ss->max_color_buffers; i++) {
266         if (hw->cbufs[i] != curr->cbufs[i]) {
267            /* propagate the backed view surface before unbinding it */
268            if (hw->cbufs[i] && svga_surface(hw->cbufs[i])->backed) {
269               svga_propagate_surface(svga,
270                                      &svga_surface(hw->cbufs[i])->backed->base,
271                                      TRUE);
272            }
273            else if (svga->state.hw_clear.rtv[i] != hw->cbufs[i] &&
274                     svga->state.hw_clear.rtv[i]) {
275               /* Free the alternate surface view when it is unbound.  */
276               svga->pipe.surface_destroy(&svga->pipe, svga->state.hw_clear.rtv[i]);
277            }
278            pipe_surface_reference(&hw->cbufs[i], curr->cbufs[i]);
279         }
280      }
281      svga->state.hw_clear.num_rendertargets = last_rtv + 1;
282      memcpy(svga->state.hw_clear.rtv, rtv, num_color * sizeof(rtv[0]));
283      hw->nr_cbufs = curr->nr_cbufs;
284
285      if (hw->zsbuf != curr->zsbuf) {
286         /* propagate the backed view surface before unbinding it */
287         if (hw->zsbuf && svga_surface(hw->zsbuf)->backed) {
288            svga_propagate_surface(svga,
289                                   &svga_surface(hw->zsbuf)->backed->base,
290                                   TRUE);
291         }
292         else if (svga->state.hw_clear.dsv != hw->zsbuf && svga->state.hw_clear.dsv) {
293            /* Free the alternate surface view when it is unbound.  */
294            svga->pipe.surface_destroy(&svga->pipe, svga->state.hw_clear.dsv);
295         }
296         pipe_surface_reference(&hw->zsbuf, curr->zsbuf);
297      }
298      svga->state.hw_clear.dsv = dsv;
299   }
300
301   return ret;
302}
303
304
305static enum pipe_error
306emit_framebuffer(struct svga_context *svga, uint64_t dirty)
307{
308   if (svga_have_vgpu10(svga)) {
309      return emit_fb_vgpu10(svga);
310   }
311   else {
312      return emit_fb_vgpu9(svga);
313   }
314}
315
316
317/*
318 * Rebind rendertargets.
319 *
320 * Similar to emit_framebuffer, but without any state checking/update.
321 *
322 * Called at the beginning of every new command buffer to ensure that
323 * non-dirty rendertargets are properly paged-in.
324 */
325enum pipe_error
326svga_reemit_framebuffer_bindings(struct svga_context *svga)
327{
328   enum pipe_error ret;
329
330   assert(svga->rebind.flags.rendertargets);
331
332   if (svga_have_vgpu10(svga)) {
333      ret = emit_fb_vgpu10(svga);
334   }
335   else {
336      ret = svga_reemit_framebuffer_bindings_vgpu9(svga);
337   }
338
339   svga->rebind.flags.rendertargets = FALSE;
340
341   return ret;
342}
343
344
345/*
346 * Send a private allocation command to page in rendertargets resource.
347 */
348enum pipe_error
349svga_rebind_framebuffer_bindings(struct svga_context *svga)
350{
351   struct svga_hw_clear_state *hw = &svga->state.hw_clear;
352   unsigned i;
353   enum pipe_error ret;
354
355   assert(svga_have_vgpu10(svga));
356
357   if (!svga->rebind.flags.rendertargets)
358      return PIPE_OK;
359
360   for (i = 0; i < hw->num_rendertargets; i++) {
361      if (hw->rtv[i]) {
362         ret = svga->swc->resource_rebind(svga->swc,
363                                          svga_surface(hw->rtv[i])->handle,
364                                          NULL,
365                                          SVGA_RELOC_WRITE);
366         if (ret != PIPE_OK)
367            return ret;
368      }
369   }
370
371   if (hw->dsv) {
372      ret = svga->swc->resource_rebind(svga->swc,
373                                       svga_surface(hw->dsv)->handle,
374                                       NULL,
375                                       SVGA_RELOC_WRITE);
376      if (ret != PIPE_OK)
377         return ret;
378   }
379
380   svga->rebind.flags.rendertargets = 0;
381
382   return PIPE_OK;
383}
384
385
386struct svga_tracked_state svga_hw_framebuffer =
387{
388   "hw framebuffer state",
389   SVGA_NEW_FRAME_BUFFER,
390   emit_framebuffer
391};
392
393
394
395
396/***********************************************************************
397 */
398
399static void
400get_viewport_prescale(struct svga_context *svga,
401                      struct pipe_viewport_state *viewport,
402                      SVGA3dViewport *vp,
403                      struct svga_prescale *prescale)
404{
405   SVGA3dRect rect;
406
407   /* Not sure if this state is relevant with POSITIONT.  Probably
408    * not, but setting to 0,1 avoids some state pingponging.
409    */
410   float range_min = 0.0;
411   float range_max = 1.0;
412   float flip = -1.0;
413   boolean degenerate = FALSE;
414   boolean invertY = FALSE;
415
416   float fb_width = (float) svga->curr.framebuffer.width;
417   float fb_height = (float) svga->curr.framebuffer.height;
418
419   float fx =        viewport->scale[0] * -1.0f + viewport->translate[0];
420   float fy = flip * viewport->scale[1] * -1.0f + viewport->translate[1];
421   float fw =        viewport->scale[0] * 2.0f;
422   float fh = flip * viewport->scale[1] * 2.0f;
423
424   memset(prescale, 0, sizeof(*prescale));
425
426   /* Examine gallium viewport transformation and produce a screen
427    * rectangle and possibly vertex shader pre-transformation to
428    * get the same results.
429    */
430
431   SVGA_DBG(DEBUG_VIEWPORT,
432            "\ninitial %f,%f %fx%f\n",
433            fx,
434            fy,
435            fw,
436            fh);
437
438   prescale->scale[0] = 1.0;
439   prescale->scale[1] = 1.0;
440   prescale->scale[2] = 1.0;
441   prescale->scale[3] = 1.0;
442   prescale->translate[0] = 0;
443   prescale->translate[1] = 0;
444   prescale->translate[2] = 0;
445   prescale->translate[3] = 0;
446
447   /* Enable prescale to adjust vertex positions to match
448      VGPU10 convention only if rasterization is enabled.
449    */
450   if (svga->curr.rast && svga->curr.rast->templ.rasterizer_discard) {
451      degenerate = TRUE;
452      goto out;
453   } else {
454      prescale->enabled = TRUE;
455   }
456
457   if (fw < 0) {
458      prescale->scale[0] *= -1.0f;
459      prescale->translate[0] += -fw;
460      fw = -fw;
461      fx = viewport->scale[0] * 1.0f + viewport->translate[0];
462   }
463
464   if (fh < 0.0) {
465      if (svga_have_vgpu10(svga)) {
466         /* floating point viewport params below */
467         prescale->translate[1] = fh + fy * 2.0f;
468      }
469      else {
470         /* integer viewport params below */
471         prescale->translate[1] = fh - 1.0f + fy * 2.0f;
472      }
473      fh = -fh;
474      fy -= fh;
475      prescale->scale[1] = -1.0f;
476      invertY = TRUE;
477   }
478
479   if (fx < 0) {
480      prescale->translate[0] += fx;
481      prescale->scale[0] *= fw / (fw + fx);
482      fw += fx;
483      fx = 0.0f;
484   }
485
486   if (fy < 0) {
487      if (invertY) {
488         prescale->translate[1] -= fy;
489      }
490      else {
491         prescale->translate[1] += fy;
492      }
493      prescale->scale[1] *= fh / (fh + fy);
494      fh += fy;
495      fy = 0.0f;
496   }
497
498   if (fx + fw > fb_width) {
499      prescale->scale[0] *= fw / (fb_width - fx);
500      prescale->translate[0] -= fx * (fw / (fb_width - fx));
501      prescale->translate[0] += fx;
502      fw = fb_width - fx;
503   }
504
505   if (fy + fh > fb_height) {
506      prescale->scale[1] *= fh / (fb_height - fy);
507      if (invertY) {
508         float in = fb_height - fy;       /* number of vp pixels inside view */
509         float out = fy + fh - fb_height; /* number of vp pixels out of view */
510         prescale->translate[1] += fy * out / in;
511      }
512      else {
513         prescale->translate[1] -= fy * (fh / (fb_height - fy));
514         prescale->translate[1] += fy;
515      }
516      fh = fb_height - fy;
517   }
518
519   if (fw < 0 || fh < 0) {
520      fw = fh = fx = fy = 0;
521      degenerate = TRUE;
522      goto out;
523   }
524
525   /* D3D viewport is integer space.  Convert fx,fy,etc. to
526    * integers.
527    *
528    * TODO: adjust pretranslate correct for any subpixel error
529    * introduced converting to integers.
530    */
531   rect.x = (uint32) fx;
532   rect.y = (uint32) fy;
533   rect.w = (uint32) fw;
534   rect.h = (uint32) fh;
535
536   SVGA_DBG(DEBUG_VIEWPORT,
537            "viewport error %f,%f %fx%f\n",
538            fabs((float)rect.x - fx),
539            fabs((float)rect.y - fy),
540            fabs((float)rect.w - fw),
541            fabs((float)rect.h - fh));
542
543   SVGA_DBG(DEBUG_VIEWPORT,
544            "viewport %d,%d %dx%d\n",
545            rect.x,
546            rect.y,
547            rect.w,
548            rect.h);
549
550   /* Finally, to get GL rasterization rules, need to tweak the
551    * screen-space coordinates slightly relative to D3D which is
552    * what hardware implements natively.
553    */
554   if (svga->curr.rast && svga->curr.rast->templ.half_pixel_center) {
555      float adjust_x = 0.0;
556      float adjust_y = 0.0;
557
558      if (svga_have_vgpu10(svga)) {
559         /* Normally, we don't have to do any sub-pixel coordinate
560          * adjustments for VGPU10.  But when we draw wide points with
561          * a GS we need an X adjustment in order to be conformant.
562          */
563         if (svga->curr.reduced_prim == PIPE_PRIM_POINTS &&
564             svga->curr.rast->pointsize > 1.0f) {
565            adjust_x = 0.5;
566         }
567      }
568      else {
569         /* Use (-0.5, -0.5) bias for all prim types.
570          * Regarding line rasterization, this does not seem to satisfy
571          * the Piglit gl-1.0-ortho-pos test but it generally produces
572          * results identical or very similar to VGPU10.
573          */
574         adjust_x = -0.5;
575         adjust_y = -0.5;
576      }
577
578      if (invertY)
579         adjust_y = -adjust_y;
580
581      prescale->translate[0] += adjust_x;
582      prescale->translate[1] += adjust_y;
583      prescale->translate[2] = 0.5; /* D3D clip space */
584      prescale->scale[2]     = 0.5; /* D3D clip space */
585   }
586
587   range_min = viewport->scale[2] * -1.0f + viewport->translate[2];
588   range_max = viewport->scale[2] *  1.0f + viewport->translate[2];
589
590   /* D3D (and by implication SVGA) doesn't like dealing with zmax
591    * less than zmin.  Detect that case, flip the depth range and
592    * invert our z-scale factor to achieve the same effect.
593    */
594   if (range_min > range_max) {
595      float range_tmp;
596      range_tmp = range_min;
597      range_min = range_max;
598      range_max = range_tmp;
599      prescale->scale[2] = -prescale->scale[2];
600   }
601
602   /* If zmin is less than 0, clamp zmin to 0 and adjust the prescale.
603    * zmin can be set to -1 when viewport->scale[2] is set to 1 and
604    * viewport->translate[2] is set to 0 in the blit code.
605    */
606   if (range_min < 0.0f) {
607      range_min = -0.5f * viewport->scale[2] + 0.5f + viewport->translate[2];
608      range_max = 0.5f * viewport->scale[2] + 0.5f + viewport->translate[2];
609      prescale->scale[2] *= 2.0f;
610      prescale->translate[2] -= 0.5f;
611   }
612
613   /* Clamp depth range, making sure it's between 0 and 1 */
614   range_min = CLAMP(range_min, 0.0f, 1.0f);
615   range_max = CLAMP(range_max, 0.0f, 1.0f);
616
617   if (prescale->enabled) {
618      float H[2];
619      float J[2];
620      int i;
621
622      SVGA_DBG(DEBUG_VIEWPORT,
623               "prescale %f,%f %fx%f\n",
624               prescale->translate[0],
625               prescale->translate[1],
626               prescale->scale[0],
627               prescale->scale[1]);
628
629      H[0] = (float)rect.w / 2.0f;
630      H[1] = -(float)rect.h / 2.0f;
631      J[0] = (float)rect.x + (float)rect.w / 2.0f;
632      J[1] = (float)rect.y + (float)rect.h / 2.0f;
633
634      SVGA_DBG(DEBUG_VIEWPORT,
635               "H %f,%f\n"
636               "J %fx%f\n",
637               H[0],
638               H[1],
639               J[0],
640               J[1]);
641
642      /* Adjust prescale to take into account the fact that it is
643       * going to be applied prior to the perspective divide and
644       * viewport transformation.
645       *
646       * Vwin = H(Vc/Vc.w) + J
647       *
648       * We want to tweak Vwin with scale and translation from above,
649       * as in:
650       *
651       * Vwin' = S Vwin + T
652       *
653       * But we can only modify the values at Vc.  Plugging all the
654       * above together, and rearranging, eventually we get:
655       *
656       *   Vwin' = H(Vc'/Vc'.w) + J
657       * where:
658       *   Vc' = SVc + KVc.w
659       *   K = (T + (S-1)J) / H
660       *
661       * Overwrite prescale.translate with values for K:
662       */
663      for (i = 0; i < 2; i++) {
664         prescale->translate[i] = ((prescale->translate[i] +
665                                   (prescale->scale[i] - 1.0f) * J[i]) / H[i]);
666      }
667
668      SVGA_DBG(DEBUG_VIEWPORT,
669               "clipspace %f,%f %fx%f\n",
670               prescale->translate[0],
671               prescale->translate[1],
672               prescale->scale[0],
673               prescale->scale[1]);
674   }
675
676out:
677   if (degenerate) {
678      rect.x = 0;
679      rect.y = 0;
680      rect.w = 1;
681      rect.h = 1;
682      prescale->enabled = FALSE;
683   }
684
685   vp->x = (float) rect.x;
686   vp->y = (float) rect.y;
687   vp->width = (float) rect.w;
688   vp->height = (float) rect.h;
689   vp->minDepth = range_min;
690   vp->maxDepth = range_max;
691}
692
693
694static enum pipe_error
695emit_viewport( struct svga_context *svga,
696               uint64_t dirty )
697{
698   struct svga_screen *svgascreen = svga_screen(svga->pipe.screen);
699   SVGA3dViewport viewports[SVGA3D_DX_MAX_VIEWPORTS];
700   struct svga_prescale prescale[SVGA3D_DX_MAX_VIEWPORTS];
701   unsigned i;
702   enum pipe_error ret;
703   unsigned max_viewports = svgascreen->max_viewports;
704
705   for (i = 0; i < max_viewports; i++) {
706      get_viewport_prescale(svga, &svga->curr.viewport[i],
707                            &viewports[i], &prescale[i]);
708   }
709
710   if (memcmp(viewports, svga->state.hw_clear.viewports,
711              max_viewports * sizeof viewports[0]) != 0) {
712
713      if (!svga_have_vgpu10(svga)) {
714         SVGA3dRect rect;
715         SVGA3dViewport *vp = &viewports[0];
716
717         rect.x = (uint32)vp->x;
718         rect.y = (uint32)vp->y;
719         rect.w = (uint32)vp->width;
720         rect.h = (uint32)vp->height;
721
722         ret = SVGA3D_SetViewport(svga->swc, &rect);
723         if (ret != PIPE_OK)
724            return ret;
725
726         ret = SVGA3D_SetZRange(svga->swc, vp->minDepth, vp->maxDepth);
727         if (ret != PIPE_OK)
728            return ret;
729
730         svga->state.hw_clear.viewport = rect;
731         svga->state.hw_clear.depthrange.zmin = vp->minDepth;
732         svga->state.hw_clear.depthrange.zmax = vp->maxDepth;
733      }
734      else {
735         ret = SVGA3D_vgpu10_SetViewports(svga->swc, max_viewports,
736                                          viewports);
737         if (ret != PIPE_OK)
738            return ret;
739      }
740      memcpy(svga->state.hw_clear.viewports, viewports,
741             max_viewports * sizeof viewports[0]);
742   }
743
744   if (memcmp(prescale, svga->state.hw_clear.prescale,
745              max_viewports * sizeof prescale[0]) != 0) {
746      svga->dirty |= SVGA_NEW_PRESCALE;
747      memcpy(svga->state.hw_clear.prescale, prescale,
748             max_viewports * sizeof prescale[0]);
749
750      /*
751       * Determine number of unique prescales. This is to minimize the
752       * if check needed in the geometry shader to identify the prescale
753       * for the specified viewport.
754       */
755      unsigned last_prescale = SVGA3D_DX_MAX_VIEWPORTS - 1;
756      unsigned i;
757      for (i = SVGA3D_DX_MAX_VIEWPORTS-1; i > 0; i--) {
758         if (memcmp(&svga->state.hw_clear.prescale[i],
759                    &svga->state.hw_clear.prescale[i-1],
760                    sizeof svga->state.hw_clear.prescale[0])) {
761            break;
762         }
763         last_prescale--;
764      }
765      svga->state.hw_clear.num_prescale = last_prescale + 1;
766   }
767
768   return PIPE_OK;
769}
770
771
772struct svga_tracked_state svga_hw_viewport =
773{
774   "hw viewport state",
775   ( SVGA_NEW_FRAME_BUFFER |
776     SVGA_NEW_VIEWPORT |
777     SVGA_NEW_RAST |
778     SVGA_NEW_REDUCED_PRIMITIVE ),
779   emit_viewport
780};
781
782
783/***********************************************************************
784 * Scissor state
785 */
786static enum pipe_error
787emit_scissor_rect( struct svga_context *svga,
788                   uint64_t dirty )
789{
790   struct svga_screen *svgascreen = svga_screen(svga->pipe.screen);
791   const struct pipe_scissor_state *scissor = svga->curr.scissor;
792   unsigned max_viewports = svgascreen->max_viewports;
793   enum pipe_error ret;
794
795   if (memcmp(&svga->state.hw_clear.scissors[0], scissor,
796              max_viewports * sizeof *scissor) != 0) {
797
798      if (svga_have_vgpu10(svga)) {
799         SVGASignedRect rect[SVGA3D_DX_MAX_VIEWPORTS];
800         unsigned i;
801
802         for (i = 0; i < max_viewports; i++) {
803            rect[i].left = scissor[i].minx;
804            rect[i].top = scissor[i].miny;
805            rect[i].right = scissor[i].maxx;
806            rect[i].bottom = scissor[i].maxy;
807         }
808
809         ret = SVGA3D_vgpu10_SetScissorRects(svga->swc, max_viewports, rect);
810      }
811      else {
812         SVGA3dRect rect;
813
814         rect.x = scissor[0].minx;
815         rect.y = scissor[0].miny;
816         rect.w = scissor[0].maxx - scissor[0].minx; /* + 1 ?? */
817         rect.h = scissor[0].maxy - scissor[0].miny; /* + 1 ?? */
818
819         ret = SVGA3D_SetScissorRect(svga->swc, &rect);
820      }
821
822      if (ret != PIPE_OK)
823         return ret;
824
825      memcpy(svga->state.hw_clear.scissors, scissor,
826             max_viewports * sizeof *scissor);
827   }
828
829   return PIPE_OK;
830}
831
832struct svga_tracked_state svga_hw_scissor =
833{
834   "hw scissor state",
835   SVGA_NEW_SCISSOR,
836   emit_scissor_rect
837};
838
839
840/***********************************************************************
841 * Userclip state
842 */
843
844static enum pipe_error
845emit_clip_planes( struct svga_context *svga,
846                  uint64_t dirty )
847{
848   unsigned i;
849   enum pipe_error ret;
850
851   /* TODO: just emit directly from svga_set_clip_state()?
852    */
853   for (i = 0; i < SVGA3D_MAX_CLIP_PLANES; i++) {
854      /* need to express the plane in D3D-style coordinate space.
855       * GL coords get converted to D3D coords with the matrix:
856       * [ 1  0  0  0 ]
857       * [ 0 -1  0  0 ]
858       * [ 0  0  2  0 ]
859       * [ 0  0 -1  1 ]
860       * Apply that matrix to our plane equation, and invert Y.
861       */
862      float a = svga->curr.clip.ucp[i][0];
863      float b = svga->curr.clip.ucp[i][1];
864      float c = svga->curr.clip.ucp[i][2];
865      float d = svga->curr.clip.ucp[i][3];
866      float plane[4];
867
868      plane[0] = a;
869      plane[1] = b;
870      plane[2] = 2.0f * c;
871      plane[3] = d - c;
872
873      if (svga_have_vgpu10(svga)) {
874         //debug_printf("XXX emit DX10 clip plane\n");
875         ret = PIPE_OK;
876      }
877      else {
878         ret = SVGA3D_SetClipPlane(svga->swc, i, plane);
879         if (ret != PIPE_OK)
880            return ret;
881      }
882   }
883
884   return PIPE_OK;
885}
886
887
888struct svga_tracked_state svga_hw_clip_planes =
889{
890   "hw viewport state",
891   SVGA_NEW_CLIP,
892   emit_clip_planes
893};
894