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