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 
30 #include "svga_context.h"
31 #include "svga_state.h"
32 #include "svga_cmd.h"
33 #include "svga_debug.h"
34 
35 
36 /***********************************************************************
37  * Hardware state update
38  */
39 
40 
41 static enum pipe_error
emit_framebuffer(struct svga_context * svga,unsigned dirty)42 emit_framebuffer( struct svga_context *svga,
43                   unsigned dirty )
44 {
45    const struct pipe_framebuffer_state *curr = &svga->curr.framebuffer;
46    struct pipe_framebuffer_state *hw = &svga->state.hw_clear.framebuffer;
47    boolean reemit = svga->rebind.rendertargets;
48    unsigned i;
49    enum pipe_error ret;
50 
51    /*
52     * We need to reemit non-null surface bindings, even when they are not
53     * dirty, to ensure that the resources are paged in.
54     */
55 
56    for(i = 0; i < PIPE_MAX_COLOR_BUFS; ++i) {
57       if (curr->cbufs[i] != hw->cbufs[i] ||
58           (reemit && hw->cbufs[i])) {
59          if (svga->curr.nr_fbs++ > 8)
60             return PIPE_ERROR_OUT_OF_MEMORY;
61 
62          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_COLOR0 + i, 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 
70 
71    if (curr->zsbuf != hw->zsbuf ||
72        (reemit && hw->zsbuf)) {
73       ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_DEPTH, curr->zsbuf);
74       if (ret != PIPE_OK)
75          return ret;
76 
77       if (curr->zsbuf &&
78           curr->zsbuf->format == PIPE_FORMAT_S8_UINT_Z24_UNORM) {
79          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, curr->zsbuf);
80          if (ret != PIPE_OK)
81             return ret;
82       }
83       else {
84          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, NULL);
85          if (ret != PIPE_OK)
86             return ret;
87       }
88 
89       pipe_surface_reference(&hw->zsbuf, curr->zsbuf);
90    }
91 
92    svga->rebind.rendertargets = FALSE;
93 
94    return PIPE_OK;
95 }
96 
97 
98 /*
99  * Rebind rendertargets.
100  *
101  * Similar to emit_framebuffer, but without any state checking/update.
102  *
103  * Called at the beginning of every new command buffer to ensure that
104  * non-dirty rendertargets are properly paged-in.
105  */
106 enum pipe_error
svga_reemit_framebuffer_bindings(struct svga_context * svga)107 svga_reemit_framebuffer_bindings(struct svga_context *svga)
108 {
109    struct pipe_framebuffer_state *hw = &svga->state.hw_clear.framebuffer;
110    unsigned i;
111    enum pipe_error ret;
112 
113    assert(svga->rebind.rendertargets);
114 
115    for (i = 0; i < MIN2(PIPE_MAX_COLOR_BUFS, 8); ++i) {
116       if (hw->cbufs[i]) {
117          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_COLOR0 + i, hw->cbufs[i]);
118          if (ret != PIPE_OK) {
119             return ret;
120          }
121       }
122    }
123 
124    if (hw->zsbuf) {
125       ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_DEPTH, hw->zsbuf);
126       if (ret != PIPE_OK) {
127          return ret;
128       }
129 
130       if (hw->zsbuf &&
131           hw->zsbuf->format == PIPE_FORMAT_S8_UINT_Z24_UNORM) {
132          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, hw->zsbuf);
133          if (ret != PIPE_OK) {
134             return ret;
135          }
136       }
137       else {
138          ret = SVGA3D_SetRenderTarget(svga->swc, SVGA3D_RT_STENCIL, NULL);
139          if (ret != PIPE_OK) {
140             return ret;
141          }
142       }
143    }
144 
145    svga->rebind.rendertargets = FALSE;
146 
147    return PIPE_OK;
148 }
149 
150 
151 struct svga_tracked_state svga_hw_framebuffer =
152 {
153    "hw framebuffer state",
154    SVGA_NEW_FRAME_BUFFER,
155    emit_framebuffer
156 };
157 
158 
159 
160 
161 /***********************************************************************
162  */
163 
164 static enum pipe_error
emit_viewport(struct svga_context * svga,unsigned dirty)165 emit_viewport( struct svga_context *svga,
166                unsigned dirty )
167 {
168    const struct pipe_viewport_state *viewport = &svga->curr.viewport;
169    struct svga_prescale prescale;
170    SVGA3dRect rect;
171    /* Not sure if this state is relevant with POSITIONT.  Probably
172     * not, but setting to 0,1 avoids some state pingponging.
173     */
174    float range_min = 0.0;
175    float range_max = 1.0;
176    float flip = -1.0;
177    boolean degenerate = FALSE;
178    boolean invertY = FALSE;
179    enum pipe_error ret;
180 
181    float fb_width = svga->curr.framebuffer.width;
182    float fb_height = svga->curr.framebuffer.height;
183 
184    float fx =        viewport->scale[0] * -1.0 + viewport->translate[0];
185    float fy = flip * viewport->scale[1] * -1.0 + viewport->translate[1];
186    float fw =        viewport->scale[0] * 2;
187    float fh = flip * viewport->scale[1] * 2;
188 
189    memset( &prescale, 0, sizeof(prescale) );
190 
191    /* Examine gallium viewport transformation and produce a screen
192     * rectangle and possibly vertex shader pre-transformation to
193     * get the same results.
194     */
195 
196    SVGA_DBG(DEBUG_VIEWPORT,
197             "\ninitial %f,%f %fx%f\n",
198             fx,
199             fy,
200             fw,
201             fh);
202 
203    prescale.scale[0] = 1.0;
204    prescale.scale[1] = 1.0;
205    prescale.scale[2] = 1.0;
206    prescale.scale[3] = 1.0;
207    prescale.translate[0] = 0;
208    prescale.translate[1] = 0;
209    prescale.translate[2] = 0;
210    prescale.translate[3] = 0;
211    prescale.enabled = TRUE;
212 
213 
214 
215    if (fw < 0) {
216       prescale.scale[0] *= -1.0;
217       prescale.translate[0] += -fw;
218       fw = -fw;
219       fx =        viewport->scale[0] * 1.0 + viewport->translate[0];
220    }
221 
222    if (fh < 0.0) {
223       prescale.translate[1] = fh - 1 + fy * 2;
224       fh = -fh;
225       fy -= fh;
226       prescale.scale[1] = -1.0;
227       invertY = TRUE;
228    }
229 
230    if (fx < 0) {
231       prescale.translate[0] += fx;
232       prescale.scale[0] *= fw / (fw + fx);
233       fw += fx;
234       fx = 0;
235    }
236 
237    if (fy < 0) {
238       if (invertY) {
239          prescale.translate[1] -= fy;
240       }
241       else {
242          prescale.translate[1] += fy;
243       }
244       prescale.scale[1] *= fh / (fh + fy);
245       fh += fy;
246       fy = 0;
247    }
248 
249    if (fx + fw > fb_width) {
250       prescale.scale[0] *= fw / (fb_width - fx);
251       prescale.translate[0] -= fx * (fw / (fb_width - fx));
252       prescale.translate[0] += fx;
253       fw = fb_width - fx;
254 
255    }
256 
257    if (fy + fh > fb_height) {
258       prescale.scale[1] *= fh / (fb_height - fy);
259       if (invertY) {
260          float in = fb_height - fy;       /* number of vp pixels inside view */
261          float out = fy + fh - fb_height; /* number of vp pixels out of view */
262          prescale.translate[1] += fy * out / in;
263       }
264       else {
265          prescale.translate[1] -= fy * (fh / (fb_height - fy));
266          prescale.translate[1] += fy;
267       }
268       fh = fb_height - fy;
269    }
270 
271    if (fw < 0 || fh < 0) {
272       fw = fh = fx = fy = 0;
273       degenerate = TRUE;
274       goto out;
275    }
276 
277 
278    /* D3D viewport is integer space.  Convert fx,fy,etc. to
279     * integers.
280     *
281     * TODO: adjust pretranslate correct for any subpixel error
282     * introduced converting to integers.
283     */
284    rect.x = fx;
285    rect.y = fy;
286    rect.w = fw;
287    rect.h = fh;
288 
289    SVGA_DBG(DEBUG_VIEWPORT,
290             "viewport error %f,%f %fx%f\n",
291             fabs((float)rect.x - fx),
292             fabs((float)rect.y - fy),
293             fabs((float)rect.w - fw),
294             fabs((float)rect.h - fh));
295 
296    SVGA_DBG(DEBUG_VIEWPORT,
297             "viewport %d,%d %dx%d\n",
298             rect.x,
299             rect.y,
300             rect.w,
301             rect.h);
302 
303 
304    /* Finally, to get GL rasterization rules, need to tweak the
305     * screen-space coordinates slightly relative to D3D which is
306     * what hardware implements natively.
307     */
308    if (svga->curr.rast->templ.gl_rasterization_rules) {
309       float adjust_x = 0.0;
310       float adjust_y = 0.0;
311 
312       switch (svga->curr.reduced_prim) {
313       case PIPE_PRIM_LINES:
314          adjust_x = -0.5;
315          adjust_y = 0;
316          break;
317       case PIPE_PRIM_POINTS:
318       case PIPE_PRIM_TRIANGLES:
319          adjust_x = -0.5;
320          adjust_y = -0.5;
321          break;
322       }
323 
324       if (invertY)
325          adjust_y = -adjust_y;
326 
327       prescale.translate[0] += adjust_x;
328       prescale.translate[1] += adjust_y;
329       prescale.translate[2] = 0.5; /* D3D clip space */
330       prescale.scale[2]     = 0.5; /* D3D clip space */
331    }
332 
333 
334    range_min = viewport->scale[2] * -1.0 + viewport->translate[2];
335    range_max = viewport->scale[2] *  1.0 + viewport->translate[2];
336 
337    /* D3D (and by implication SVGA) doesn't like dealing with zmax
338     * less than zmin.  Detect that case, flip the depth range and
339     * invert our z-scale factor to achieve the same effect.
340     */
341    if (range_min > range_max) {
342       float range_tmp;
343       range_tmp = range_min;
344       range_min = range_max;
345       range_max = range_tmp;
346       prescale.scale[2]     = -prescale.scale[2];
347    }
348 
349    if (prescale.enabled) {
350       float H[2];
351       float J[2];
352       int i;
353 
354       SVGA_DBG(DEBUG_VIEWPORT,
355                "prescale %f,%f %fx%f\n",
356                prescale.translate[0],
357                prescale.translate[1],
358                prescale.scale[0],
359                prescale.scale[1]);
360 
361       H[0] = (float)rect.w / 2.0;
362       H[1] = -(float)rect.h / 2.0;
363       J[0] = (float)rect.x + (float)rect.w / 2.0;
364       J[1] = (float)rect.y + (float)rect.h / 2.0;
365 
366       SVGA_DBG(DEBUG_VIEWPORT,
367                "H %f,%f\n"
368                "J %fx%f\n",
369                H[0],
370                H[1],
371                J[0],
372                J[1]);
373 
374       /* Adjust prescale to take into account the fact that it is
375        * going to be applied prior to the perspective divide and
376        * viewport transformation.
377        *
378        * Vwin = H(Vc/Vc.w) + J
379        *
380        * We want to tweak Vwin with scale and translation from above,
381        * as in:
382        *
383        * Vwin' = S Vwin + T
384        *
385        * But we can only modify the values at Vc.  Plugging all the
386        * above together, and rearranging, eventually we get:
387        *
388        *   Vwin' = H(Vc'/Vc'.w) + J
389        * where:
390        *   Vc' = SVc + KVc.w
391        *   K = (T + (S-1)J) / H
392        *
393        * Overwrite prescale.translate with values for K:
394        */
395       for (i = 0; i < 2; i++) {
396          prescale.translate[i] = ((prescale.translate[i] +
397                                    (prescale.scale[i] - 1.0) * J[i]) / H[i]);
398       }
399 
400       SVGA_DBG(DEBUG_VIEWPORT,
401                "clipspace %f,%f %fx%f\n",
402                prescale.translate[0],
403                prescale.translate[1],
404                prescale.scale[0],
405                prescale.scale[1]);
406    }
407 
408 out:
409    if (degenerate) {
410       rect.x = 0;
411       rect.y = 0;
412       rect.w = 1;
413       rect.h = 1;
414       prescale.enabled = FALSE;
415    }
416 
417    if (memcmp(&rect, &svga->state.hw_clear.viewport, sizeof(rect)) != 0) {
418       ret = SVGA3D_SetViewport(svga->swc, &rect);
419       if(ret != PIPE_OK)
420          return ret;
421 
422       memcpy(&svga->state.hw_clear.viewport, &rect, sizeof(rect));
423       assert(sizeof(rect) == sizeof(svga->state.hw_clear.viewport));
424    }
425 
426    if (svga->state.hw_clear.depthrange.zmin != range_min ||
427        svga->state.hw_clear.depthrange.zmax != range_max)
428    {
429       ret = SVGA3D_SetZRange(svga->swc, range_min, range_max );
430       if(ret != PIPE_OK)
431          return ret;
432 
433       svga->state.hw_clear.depthrange.zmin = range_min;
434       svga->state.hw_clear.depthrange.zmax = range_max;
435    }
436 
437    if (memcmp(&prescale, &svga->state.hw_clear.prescale, sizeof prescale) != 0) {
438       svga->dirty |= SVGA_NEW_PRESCALE;
439       svga->state.hw_clear.prescale = prescale;
440    }
441 
442    return PIPE_OK;
443 }
444 
445 
446 struct svga_tracked_state svga_hw_viewport =
447 {
448    "hw viewport state",
449    ( SVGA_NEW_FRAME_BUFFER |
450      SVGA_NEW_VIEWPORT |
451      SVGA_NEW_RAST |
452      SVGA_NEW_REDUCED_PRIMITIVE ),
453    emit_viewport
454 };
455 
456 
457 /***********************************************************************
458  * Scissor state
459  */
460 static enum pipe_error
emit_scissor_rect(struct svga_context * svga,unsigned dirty)461 emit_scissor_rect( struct svga_context *svga,
462                    unsigned dirty )
463 {
464    const struct pipe_scissor_state *scissor = &svga->curr.scissor;
465    SVGA3dRect rect;
466 
467    rect.x = scissor->minx;
468    rect.y = scissor->miny;
469    rect.w = scissor->maxx - scissor->minx; /* + 1 ?? */
470    rect.h = scissor->maxy - scissor->miny; /* + 1 ?? */
471 
472    return SVGA3D_SetScissorRect(svga->swc, &rect);
473 }
474 
475 
476 struct svga_tracked_state svga_hw_scissor =
477 {
478    "hw scissor state",
479    SVGA_NEW_SCISSOR,
480    emit_scissor_rect
481 };
482 
483 
484 /***********************************************************************
485  * Userclip state
486  */
487 
488 static enum pipe_error
emit_clip_planes(struct svga_context * svga,unsigned dirty)489 emit_clip_planes( struct svga_context *svga,
490                   unsigned dirty )
491 {
492    unsigned i;
493    enum pipe_error ret;
494 
495    /* TODO: just emit directly from svga_set_clip_state()?
496     */
497    for (i = 0; i < SVGA3D_MAX_CLIP_PLANES; i++) {
498       /* need to express the plane in D3D-style coordinate space.
499        * GL coords get converted to D3D coords with the matrix:
500        * [ 1  0  0  0 ]
501        * [ 0 -1  0  0 ]
502        * [ 0  0  2  0 ]
503        * [ 0  0 -1  1 ]
504        * Apply that matrix to our plane equation, and invert Y.
505        */
506       float a = svga->curr.clip.ucp[i][0];
507       float b = svga->curr.clip.ucp[i][1];
508       float c = svga->curr.clip.ucp[i][2];
509       float d = svga->curr.clip.ucp[i][3];
510       float plane[4];
511 
512       plane[0] = a;
513       plane[1] = b;
514       plane[2] = 2.0f * c;
515       plane[3] = d - c;
516 
517       ret = SVGA3D_SetClipPlane(svga->swc, i, plane);
518       if(ret != PIPE_OK)
519          return ret;
520    }
521 
522    return PIPE_OK;
523 }
524 
525 
526 struct svga_tracked_state svga_hw_clip_planes =
527 {
528    "hw viewport state",
529    SVGA_NEW_CLIP,
530    emit_clip_planes
531 };
532