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