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 41static enum pipe_error 42emit_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 */ 106enum pipe_error 107svga_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 151struct 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 164static enum pipe_error 165emit_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 408out: 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 446struct 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 */ 460static enum pipe_error 461emit_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 476struct 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 488static enum pipe_error 489emit_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 526struct svga_tracked_state svga_hw_clip_planes = 527{ 528 "hw viewport state", 529 SVGA_NEW_CLIP, 530 emit_clip_planes 531}; 532