rrRenderer.js (49642B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES Utilities 3 * ------------------------------------------------ 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 'use strict'; 22 goog.provide('framework.referencerenderer.rrRenderer'); 23 goog.require('framework.common.tcuTexture'); 24 goog.require('framework.common.tcuTextureUtil'); 25 goog.require('framework.delibs.debase.deMath'); 26 goog.require('framework.delibs.debase.deString'); 27 goog.require('framework.delibs.debase.deUtil'); 28 goog.require('framework.opengl.simplereference.sglrShaderProgram'); 29 goog.require('framework.referencerenderer.rrDefs'); 30 goog.require('framework.referencerenderer.rrFragmentOperations'); 31 goog.require('framework.referencerenderer.rrGenericVector'); 32 goog.require('framework.referencerenderer.rrMultisamplePixelBufferAccess'); 33 goog.require('framework.referencerenderer.rrRenderState'); 34 goog.require('framework.referencerenderer.rrShadingContext'); 35 goog.require('framework.referencerenderer.rrVertexAttrib'); 36 goog.require('framework.referencerenderer.rrVertexPacket'); 37 38 goog.scope(function() { 39 40 var rrRenderer = framework.referencerenderer.rrRenderer; 41 var rrVertexPacket = framework.referencerenderer.rrVertexPacket; 42 var rrDefs = framework.referencerenderer.rrDefs; 43 var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; 44 var deMath = framework.delibs.debase.deMath; 45 var tcuTextureUtil = framework.common.tcuTextureUtil; 46 var tcuTexture = framework.common.tcuTexture; 47 var rrRenderState = framework.referencerenderer.rrRenderState; 48 var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; 49 var rrShadingContext = framework.referencerenderer.rrShadingContext; 50 var rrGenericVector = framework.referencerenderer.rrGenericVector; 51 var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; 52 var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; 53 var deString = framework.delibs.debase.deString; 54 var deUtil = framework.delibs.debase.deUtil; 55 56 /** 57 * @enum 58 */ 59 rrRenderer.PrimitiveType = { 60 TRIANGLES: 0, //!< Separate rrRenderer.triangles 61 TRIANGLE_STRIP: 1, //!< rrRenderer.Triangle strip 62 TRIANGLE_FAN: 2, //!< rrRenderer.Triangle fan 63 64 LINES: 3, //!< Separate lines 65 LINE_STRIP: 4, //!< Line strip 66 LINE_LOOP: 5, //!< Line loop 67 68 POINTS: 6 //!< Points 69 }; 70 71 // /** 72 // * @constructor 73 // * @param {boolean} depthEnabled Is depth buffer enabled 74 // */ 75 // rrRenderer.RasterizationInternalBuffers = function(depthEnabled) { 76 // /*std::vector<rrFragmentOperations.Fragment>*/ this.fragmentPackets = []; 77 // /*std::vector<GenericVec4>*/ this.shaderOutputs = []; 78 // /*std::vector<Fragment>*/ this.shadedFragments = []; 79 // /*float**/ this.fragmentDepthBuffer = depthEnabled ? [] : null; 80 // }; 81 82 /** 83 * @constructor 84 * @param {number=} id 85 */ 86 rrRenderer.DrawContext = function(id) { 87 this.primitiveID = id || 0; 88 89 }; 90 91 /** 92 * Transform [x, y] to window (pixel) coordinates. 93 * z and w are unchanged 94 * @param {rrRenderState.RenderState} state 95 * @param {rrVertexPacket.VertexPacket} packet 96 * Wreturn {Array<number>} 97 */ 98 rrRenderer.transformGLToWindowCoords = function(state, packet) { 99 var transformed = [packet.position[0] / packet.position[3], 100 packet.position[1] / packet.position[3], 101 packet.position[2], 102 packet.position[3]]; 103 var viewport = state.viewport.rect; 104 var halfW = viewport.width / 2; 105 var halfH = viewport.height / 2; 106 var oX = viewport.left + halfW; 107 var oY = viewport.bottom + halfH; 108 109 return [ 110 transformed[0] * halfW + oX, 111 transformed[1] * halfH + oY, 112 transformed[2], 113 transformed[3] 114 ]; 115 }; 116 117 /** 118 * @constructor 119 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} colorMultisampleBuffer 120 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} depthMultisampleBuffer 121 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} stencilMultisampleBuffer 122 */ 123 rrRenderer.RenderTarget = function(colorMultisampleBuffer, depthMultisampleBuffer, stencilMultisampleBuffer) { 124 this.MAX_COLOR_BUFFERS = 4; 125 this.colorBuffers = []; 126 this.colorBuffers[0] = colorMultisampleBuffer; 127 this.depthBuffer = depthMultisampleBuffer || new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess(); 128 this.stencilBuffer = stencilMultisampleBuffer || new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess(); 129 this.numColorBuffers = 1; 130 }; 131 132 // NOTE: Program object is useless. Let's just use the sglrShaderProgram 133 // /** 134 // * @constructor 135 // * @param {rrShaders.VertexShader} vertexShader_ 136 // * @param {rrShaders.FragmentShader} fragmentShader_ 137 // */ 138 // var Program = function(vertexShader_, fragmentShader_) { 139 // this.vertexShader = vertexShader_; 140 // this.fragmentShader = fragmentShader_; 141 // }; 142 143 /** 144 * @constructor 145 * @param {ArrayBuffer} data 146 * @param {rrDefs.IndexType} type 147 * @param {number} offset 148 * @param {number=} baseVertex_ 149 */ 150 rrRenderer.DrawIndices = function(data, type, offset, baseVertex_) { 151 /** @type {ArrayBuffer} */ this.data = data; 152 /** @type {number} */ this.baseVertex = baseVertex_ || 0; 153 /** @type {rrDefs.IndexType} */ this.indexType = type; 154 /** @type {goog.NumberArray} */ this.access = null; 155 switch (type) { 156 case rrDefs.IndexType.INDEXTYPE_UINT8: this.access = new Uint8Array(data).subarray(offset); break; 157 case rrDefs.IndexType.INDEXTYPE_UINT16: this.access = new Uint16Array(data).subarray(offset / 2); break; 158 case rrDefs.IndexType.INDEXTYPE_UINT32: this.access = new Uint32Array(data).subarray(offset / 4); break; 159 default: throw new Error('Invalid type: ' + type); 160 } 161 }; 162 163 /** 164 * @return {number} 165 */ 166 rrRenderer.DrawIndices.prototype.readIndexArray = function(index) { return this.access[index]; }; 167 168 /** 169 * @constructor 170 * @param {rrRenderer.PrimitiveType} primitiveType 171 * @param {number} numElements 172 * @param {(number|rrRenderer.DrawIndices)} indices 173 */ 174 rrRenderer.PrimitiveList = function(primitiveType, numElements, indices) { 175 /** @type {rrRenderer.PrimitiveType} */ this.m_primitiveType = primitiveType; 176 /** @type {number} */ this.m_numElements = numElements; 177 if (typeof indices == 'number') { 178 // !< primitive list for drawArrays-like call 179 this.m_indices = null; 180 this.m_indexType = null; 181 this.m_baseVertex = indices; 182 } else { 183 // !< primitive list for drawElements-like call 184 this.m_indices = indices; 185 this.m_indexType = indices.indexType; 186 this.m_baseVertex = indices.baseVertex; 187 } 188 this.m_iterator = 0; 189 }; 190 191 /** 192 * @param {number} elementNdx 193 * @return {number} 194 */ 195 rrRenderer.PrimitiveList.prototype.getIndex = function(elementNdx) { 196 if (this.m_indices) { 197 var index = this.m_baseVertex + this.m_indices.readIndexArray(elementNdx); 198 if (index < 0) 199 throw new Error('Index must not be negative'); 200 201 return index; 202 } else 203 return this.m_baseVertex + elementNdx; 204 }; 205 206 /** 207 * @param {number} elementNdx 208 * @param {number} restartIndex 209 * @return {boolean} 210 */ 211 rrRenderer.PrimitiveList.prototype.isRestartIndex = function(elementNdx, restartIndex) { 212 // implicit index or explicit index (without base vertex) equals restart 213 if (this.m_indices) 214 return this.m_indices.readIndexArray(elementNdx) == restartIndex; 215 else 216 return elementNdx == restartIndex; 217 }; 218 219 /** 220 * @return {number} 221 */ 222 rrRenderer.PrimitiveList.prototype.getNumElements = function() {return this.m_numElements;}; 223 224 /** 225 * @return {rrRenderer.PrimitiveType} 226 */ 227 rrRenderer.PrimitiveList.prototype.getPrimitiveType = function() {return this.m_primitiveType;}; 228 229 /** 230 * @return {?rrDefs.IndexType} 231 */ 232 rrRenderer.PrimitiveList.prototype.getIndexType = function() {return this.m_indexType;}; 233 234 /** 235 * Generate a primitive from indices 236 * @param {boolean=} reset Restart generating primitives. Default false 237 * @return {Array<number>} 238 */ 239 rrRenderer.PrimitiveList.prototype.getNextPrimitive = function(reset) { 240 if (reset) 241 this.m_iterator = 0; 242 var result = []; 243 var i = this.m_iterator; 244 switch (this.m_primitiveType) { 245 case rrRenderer.PrimitiveType.TRIANGLES: 246 if (this.m_iterator + 3 <= this.m_numElements) { 247 result = [i, i + 1, i + 2]; 248 this.m_iterator += 3; 249 } 250 break; 251 case rrRenderer.PrimitiveType.TRIANGLE_STRIP: 252 if (this.m_iterator + 3 <= this.m_numElements) { 253 result = [i, i + 1, i + 2]; 254 this.m_iterator += 1; 255 } 256 break; 257 case rrRenderer.PrimitiveType.TRIANGLE_FAN: 258 if (this.m_iterator + 3 <= this.m_numElements) { 259 result = [0, i + 1, i + 2]; 260 this.m_iterator += 1; 261 } 262 break; 263 case rrRenderer.PrimitiveType.LINES: 264 if (this.m_iterator + 2 <= this.m_numElements) { 265 result = [i, i + 1]; 266 this.m_iterator += 2; 267 } 268 break; 269 case rrRenderer.PrimitiveType.LINE_STRIP: 270 if (this.m_iterator + 2 <= this.m_numElements) { 271 result = [i, i + 1]; 272 this.m_iterator += 1; 273 } 274 break; 275 case rrRenderer.PrimitiveType.LINE_LOOP: 276 if (this.m_iterator == this.m_numElements) 277 break; 278 if (this.m_iterator + 2 <= this.m_numElements) 279 result = [i, i + 1]; 280 else 281 result = [i, 0]; 282 this.m_iterator += 1; 283 break; 284 case rrRenderer.PrimitiveType.POINTS: 285 if (this.m_iterator == this.m_numElements) 286 break; 287 else 288 result = [i]; 289 this.m_iterator += 1; 290 break; 291 default: 292 throw new Error('Unsupported primitive type: ' + deString.enumToString(rrRenderer.PrimitiveType, this.m_primitiveType)); 293 } 294 295 return result; 296 }; 297 298 /** 299 * @param {rrRenderState.RenderState} state 300 * @param {rrRenderer.RenderTarget} renderTarget 301 * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write 302 */ 303 rrRenderer.writeFragments = function(state, renderTarget, fragments) { 304 /* TODO: Add blending, depth, stencil ... */ 305 var colorbuffer = renderTarget.colorBuffers[0].raw(); 306 for (var i = 0; i < fragments.length; i++) { 307 var fragment = fragments[i]; 308 colorbuffer.setPixel(fragment.value, 0, fragment.pixelCoord[0], fragment.pixelCoord[1]); 309 } 310 311 }; 312 313 /** 314 * @param {rrRenderState.RenderState} renderState 315 * @param {rrRenderer.RenderTarget} renderTarget 316 * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write 317 */ 318 rrRenderer.writeFragments2 = function(renderState, renderTarget, fragments) { 319 /* 320 void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess& msColorBuffer, 321 const rr::MultisamplePixelBufferAccess& msDepthBuffer, 322 const rr::MultisamplePixelBufferAccess& msStencilBuffer, 323 const Fragment* fragments, 324 int numFragments, 325 FaceType fragmentFacing, 326 const FragmentOperationState& state) 327 */ 328 329 /** @const */ var fragmentFacing = rrDefs.FaceType.FACETYPE_FRONT; 330 var colorBuffer = renderTarget.colorBuffers[0].raw(); 331 var depthBuffer = renderTarget.depthBuffer.raw(); 332 var stencilBuffer = renderTarget.stencilBuffer.raw(); 333 var state = renderState.fragOps; 334 335 var hasDepth = depthBuffer.getWidth() > 0 && depthBuffer.getHeight() > 0 && depthBuffer.getDepth() > 0; 336 var hasStencil = stencilBuffer.getWidth() > 0 && stencilBuffer.getHeight() > 0 && stencilBuffer.getDepth() > 0; 337 var doDepthTest = hasDepth && state.depthTestEnabled; 338 var doStencilTest = hasStencil && state.stencilTestEnabled; 339 340 var colorbufferClass = tcuTexture.getTextureChannelClass(colorBuffer.getFormat().type); 341 var fragmentDataType = rrGenericVector.GenericVecType.FLOAT; 342 switch (colorbufferClass) { 343 case tcuTexture.TextureChannelClass.SIGNED_INTEGER: 344 fragmentDataType = rrGenericVector.GenericVecType.INT32; 345 break; 346 case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: 347 fragmentDataType = rrGenericVector.GenericVecType.UINT32; 348 break; 349 } 350 351 if (!((!hasDepth || colorBuffer.getWidth() == depthBuffer.getWidth()) && (!hasStencil || colorBuffer.getWidth() == stencilBuffer.getWidth()))) 352 throw new Error('Attachment must have the same width'); 353 if (!((!hasDepth || colorBuffer.getHeight() == depthBuffer.getHeight()) && (!hasStencil || colorBuffer.getHeight() == stencilBuffer.getHeight()))) 354 throw new Error('Attachment must have the same height'); 355 if (!((!hasDepth || colorBuffer.getDepth() == depthBuffer.getDepth()) && (!hasStencil || colorBuffer.getDepth() == stencilBuffer.getDepth()))) 356 throw new Error('Attachment must have the same depth'); 357 358 var stencilState = state.stencilStates[fragmentFacing]; 359 var colorMaskFactor = [state.colorMask[0] ? 1 : 0, state.colorMask[1] ? 1 : 0, state.colorMask[2] ? 1 : 0, state.colorMask[3] ? 1 : 0]; 360 var colorMaskNegationFactor = [state.colorMask[0] ? false : true, state.colorMask[1] ? false : true, state.colorMask[2] ? false : true, state.colorMask[3] ? false : true]; 361 var sRGBTarget = state.sRGBEnabled && colorBuffer.getFormat().isSRGB(); 362 363 // Scissor test. 364 365 if (state.scissorTestEnabled) 366 rrFragmentOperations.executeScissorTest(fragments, state.scissorRectangle); 367 368 // Stencil test. 369 370 if (doStencilTest) { 371 rrFragmentOperations.executeStencilCompare(fragments, stencilState, state.numStencilBits, stencilBuffer); 372 rrFragmentOperations.executeStencilSFail(fragments, stencilState, state.numStencilBits, stencilBuffer); 373 } 374 375 // Depth test. 376 // \note Current value of isAlive is needed for dpPass and dpFail, so it's only updated after them and not right after depth test. 377 378 if (doDepthTest) { 379 rrFragmentOperations.executeDepthCompare(fragments, state.depthFunc, depthBuffer); 380 381 if (state.depthMask) 382 rrFragmentOperations.executeDepthWrite(fragments, depthBuffer); 383 } 384 385 // Do dpFail and dpPass stencil writes. 386 387 if (doStencilTest) 388 rrFragmentOperations.executeStencilDpFailAndPass(fragments, stencilState, state.numStencilBits, stencilBuffer); 389 390 // Kill the samples that failed depth test. 391 392 if (doDepthTest) { 393 for (var i = 0; i < fragments.length; i++) 394 fragments[i].isAlive = fragments[i].isAlive && fragments[i].depthPassed; 395 } 396 397 // Paint fragments to target 398 399 switch (fragmentDataType) { 400 case rrGenericVector.GenericVecType.FLOAT: 401 // Blend calculation - only if using blend. 402 if (state.blendMode == rrRenderState.BlendMode.STANDARD) { 403 // Put dst color to register, doing srgb-to-linear conversion if needed. 404 for (var i = 0; i < fragments.length; i++) { 405 var frag = fragments[i]; 406 if (frag.isAlive) { 407 var dstColor = colorBuffer.getPixel(0, frag.pixelCoord[0], frag.pixelCoord[1]); 408 409 /* TODO: Check frag.value and frag.value1 types */ 410 frag.clampedBlendSrcColor = deMath.clampVector(frag.value, 0, 1); 411 frag.clampedBlendSrc1Color = deMath.clampVector(frag.value1, 0, 1); 412 frag.clampedBlendDstColor = deMath.clampVector(sRGBTarget ? tcuTexture.sRGBToLinear(dstColor) : dstColor, 0, 1); 413 } 414 } 415 416 // Calculate blend factors to register. 417 rrFragmentOperations.executeBlendFactorComputeRGB(fragments, state.blendColor, state.blendRGBState); 418 rrFragmentOperations.executeBlendFactorComputeA(fragments, state.blendColor, state.blendAState); 419 420 // Compute blended color. 421 rrFragmentOperations.executeBlend(fragments, state.blendRGBState, state.blendAState); 422 } else { 423 // Not using blend - just put values to register as-is. 424 425 for (var i = 0; i < fragments.length; i++) { 426 var frag = fragments[i]; 427 if (frag.isAlive) { 428 frag.blendedRGB = deMath.swizzle(frag.value, [0, 1, 2]); 429 frag.blendedA = frag.value[3]; 430 } 431 } 432 } 433 434 // Finally, write the colors to the color buffer. 435 436 if (state.colorMask[0] && state.colorMask[1] && state.colorMask[2] && state.colorMask[3]) { 437 /* TODO: Add quick path */ 438 // if (colorBuffer.getFormat().isEqual(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))) 439 // executeRGBA8ColorWrite(fragments, colorBuffer); 440 // else 441 rrFragmentOperations.executeColorWrite(fragments, sRGBTarget, colorBuffer); 442 } else if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) 443 rrFragmentOperations.executeMaskedColorWrite(fragments, colorMaskFactor, colorMaskNegationFactor, sRGBTarget, colorBuffer); 444 break; 445 446 case rrGenericVector.GenericVecType.INT32: 447 // Write fragments 448 for (var i = 0; i < fragments.length; i++) { 449 var frag = fragments[i]; 450 if (frag.isAlive) { 451 frag.signedValue = frag.value; 452 } 453 } 454 455 if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) 456 rrFragmentOperations.executeSignedValueWrite(fragments, state.colorMask, colorBuffer); 457 break; 458 459 case rrGenericVector.GenericVecType.UINT32: 460 // Write fragments 461 for (var i = 0; i < fragments.length; i++) { 462 var frag = fragments[i]; 463 if (frag.isAlive) { 464 frag.unsignedValue = frag.value; 465 } 466 } 467 468 if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) 469 rrFragmentOperations.executeUnsignedValueWrite(fragments, state.colorMask, colorBuffer); 470 break; 471 472 default: 473 throw new Error('Unrecognized fragment data type:' + fragmentDataType); 474 } 475 }; 476 477 /** 478 * Determines the index of the corresponding vertex according to top/right conditions. 479 * @param {boolean} isTop 480 * @param {boolean} isRight 481 * @return {number} 482 */ 483 rrRenderer.getIndexOfCorner = function(isTop, isRight, vertexPackets) { 484 var x = null; 485 var y = null; 486 487 var xcriteria = isRight ? Math.max : Math.min; 488 var ycriteria = isTop ? Math.max : Math.min; 489 490 // Determine corner values 491 for (var i = 0; i < vertexPackets.length; i++) { 492 x = x != null ? xcriteria(vertexPackets[i].position[0], x) : vertexPackets[i].position[0]; 493 y = y != null ? ycriteria(vertexPackets[i].position[1], y) : vertexPackets[i].position[1]; 494 } 495 496 // Search for matching vertex 497 for (var v = 0; v < vertexPackets.length; v++) 498 if (vertexPackets[v].position[0] == x && 499 vertexPackets[v].position[1] == y) 500 return v; 501 502 throw new Error('Corner not found'); 503 }; 504 505 /** 506 * Check that point is in the clipping volume 507 * @param {number} x 508 * @param {number} y 509 * @param {number} z 510 * @param {rrRenderState.WindowRectangle} rect 511 * @return {boolean} 512 */ 513 rrRenderer.clipTest = function(x, y, z, rect) { 514 x = Math.round(x); 515 y = Math.round(y); 516 if (!deMath.deInBounds32(x, rect.left, rect.left + rect.width)) 517 return false; 518 if (!deMath.deInBounds32(y, rect.bottom, rect.bottom + rect.height)) 519 return false; 520 if (z < 0 || z > 1) 521 return false; 522 return true; 523 }; 524 525 // Rasterizer configuration 526 rrRenderer.RASTERIZER_SUBPIXEL_BITS = 8; 527 rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT = 16; 528 529 // Referenced from rrRasterizer.hpp 530 531 /** 532 * Get coverage bit value 533 * @param {number} numSamples 534 * @param {number} x 535 * @param {number} y 536 * @param {number} sampleNdx 537 * @return {number} 538 */ 539 rrRenderer.getCoverageBit = function(numSamples, x, y, sampleNdx) { 540 var maxSamples = 16; 541 assertMsgOptions(maxSamples >= rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT, 'maxSamples should not greater than ' + rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT, false, true); 542 assertMsgOptions(deMath.deInRange32(numSamples, 1, maxSamples) && deMath.deInBounds32(x, 0, 2) && deMath.deInBounds32(y, 0, 2), 'numSamples, x or y not in bound', false, true); 543 return 1 << ((x * 2 + y) * numSamples + sampleNdx); 544 }; 545 546 /** 547 * Get all sample bits for fragment 548 * @param {number} numSamples 549 * @param {number} x 550 * @param {number} y 551 * @return {number} 552 */ 553 rrRenderer.getCoverageFragmentSampleBits = function(numSamples, x, y) { 554 assertMsgOptions(deMath.deInBounds32(x, 0, 2) && deMath.deInBounds32(y, 0, 2), 'x or y is not in bound 0 to 2', false, true); 555 var fragMask = (1 << numSamples) - 1; 556 return fragMask << (x * 2 + y) * numSamples; 557 }; 558 559 /** 560 * Set coverage bit in coverage mask 561 * @param {number} mask 562 * @param {number} numSamples 563 * @param {number} x 564 * @param {number} y 565 * @param {number} sampleNdx 566 * @param {number} val 567 * @return {number} 568 */ 569 rrRenderer.setCoverageValue = function(mask, numSamples, x, y, sampleNdx, val) { 570 var bit = rrRenderer.getCoverageBit(numSamples, x, y, sampleNdx); 571 return val ? (mask | bit) : (mask & ~bit); 572 }; 573 574 /** 575 * Test if any sample for fragment is live 576 * @param {number} mask 577 * @param {number} numSamples 578 * @param {number} x 579 * @param {number} y 580 * @return {number} 581 */ 582 rrRenderer.getCoverageAnyFragmentSampleLive = function(mask, numSamples, x, y) { 583 return (mask & rrRenderer.getCoverageFragmentSampleBits(numSamples, x, y)) != 0; 584 }; 585 586 // Referenced from rrRasterizer.cpp 587 588 /** 589 * Pixel coord to sub pixel coord 590 * @param {number} v 591 * @return {number} 592 */ 593 rrRenderer.toSubpixelCoord = function(v) { 594 return Math.trunc(v * (1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) + (v < 0 ? -0.5 : 0.5)); 595 }; 596 597 /** 598 * Floor sub pixel coord to pixel coord 599 * @param {number} coord 600 * @param {boolean} fillEdge 601 * @return {number} 602 */ 603 rrRenderer.floorSubpixelToPixelCoord = function(coord, fillEdge) { 604 if (coord >= 0) 605 return Math.trunc((coord - (fillEdge ? 1 : 0)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); 606 else 607 return Math.trunc((coord - ((1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1))) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); 608 }; 609 610 /** 611 * Ceil sub pixel coord to pixel coord 612 * @param {number} coord 613 * @param {boolean} fillEdge 614 * @return {number} 615 */ 616 rrRenderer.ceilSubpixelToPixelCoord = function(coord, fillEdge) { 617 if (coord >= 0) 618 return Math.trunc((coord + (1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); 619 else 620 return Math.trunc((coord + (fillEdge ? 1 : 0)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); 621 }; 622 623 /** 624 * \brief Edge function - referenced from struct EdgeFunction in rrRasterizer.hpp 625 * 626 * Edge function can be evaluated for point P (in a fixed-point coordinates 627 * with RASTERIZER_SUBPIXEL_BITS fractional part) by computing 628 * D = a * Px + b * Py + c 629 * 630 * D will be fixed-point value where lower (RASTERIZER_SUBPIXEL_BITS * 2) bits 631 * will be fractional part. 632 * 633 * Member function evaluateEdge, reverseEdge and isInsideCCW are referenced from rrRasterizer.cpp. 634 * 635 * @param {number} a 636 * @param {number} b 637 * @param {number} c 638 * @param {boolean} inclusive 639 */ 640 rrRenderer.edgeFunction = function(a, b, c, inclusive) { 641 this.a = a; 642 this.b = b; 643 this.c = c; 644 this.inclusive = inclusive; // True if edge is inclusive according to fill rules 645 }; 646 647 /** 648 * Evaluate point (x,y) 649 * @param {number} x 650 * @param {number} y 651 * @return {number} 652 */ 653 rrRenderer.edgeFunction.prototype.evaluateEdge = function(x, y) { 654 return this.a * x + this.b * y + this.c; 655 }; 656 657 /** 658 * Reverse edge (e.g. from CCW to CW) 659 */ 660 rrRenderer.edgeFunction.prototype.reverseEdge = function () { 661 this.a = -this.a; 662 this.b = -this.b; 663 this.c = -this.c; 664 this.inclusive = !this.inclusive; 665 }; 666 667 /** 668 * Determine if a point with value edgeVal is inside the CCW region of the edge 669 * @param {number} edgeVal 670 * @return {boolean} 671 */ 672 rrRenderer.edgeFunction.prototype.isInsideCCW = function(edgeVal) { 673 return this.inclusive ? edgeVal >= 0 : edgeVal > 0; 674 }; 675 676 /** 677 * Init an edge function in counter-clockwise (CCW) orientation 678 * @param {number} horizontalFill 679 * @param {number} verticalFill 680 * @param {number} x0 681 * @param {number} y0 682 * @param {number} x1 683 * @param {number} y1 684 * @return {rrRenderer.edgeFunction} 685 */ 686 rrRenderer.initEdgeCCW = function(horizontalFill, verticalFill, x0, y0, x1, y1) { 687 var xd = x1 - x0; 688 var yd = y1 - y0; 689 var inclusive = false; 690 691 if (yd == 0) 692 inclusive = verticalFill == rrRenderState.VerticalFill.BOTTOM ? xd >= 0 : xd <= 0; 693 else 694 inclusive = horizontalFill == rrRenderState.HorizontalFill.LEFT ? yd <= 0 : yd >=0; 695 696 return new rrRenderer.edgeFunction(y0 - y1, x1 - x0, x0 * y1 - y0 * x1, inclusive); 697 }; 698 699 /** 700 * \brief Triangle rasterizer - referenced from class TriangleRasterizer in rrRasterizer.hpp 701 * 702 * Triangle rasterizer implements following features: 703 * - Rasterization using fixed-point coordinates 704 * - 1-sample rasterization (the value of numSamples always equals 1 in sglrReferenceContext) 705 * - Depth interpolation 706 * - Perspective-correct barycentric computation for interpolation 707 * - Visible face determination 708 * - Clipping - native dEQP does clipping before rasterization; see function drawBasicPrimitives 709 * in rrRenderer.cpp for more details 710 * 711 * It does not (and will not) implement following: 712 * - Triangle setup 713 * - Degenerate elimination 714 * - Coordinate transformation (inputs are in screen-space) 715 * - Culling - logic can be implemented outside by querying visible face 716 * - Scissoring - (this can be done by controlling viewport rectangle) 717 * - Any per-fragment operations 718 * 719 * @param {rrRenderState.RenderState} state 720 */ 721 rrRenderer.triangleRasterizer = function(state) { 722 this.m_viewport = state.viewport; 723 this.m_winding = state.rasterization.winding; 724 this.m_horizontalFill = state.rasterization.horizontalFill; 725 this.m_verticalFill = state.rasterization.verticalFill; 726 }; 727 728 /** 729 * Initialize triangle rasterization 730 * @param {vec} v0 Screen-space coordinates (x, y, z) and 1/w for vertex 0 731 * @param {vec} v1 Screen-space coordinates (x, y, z) and 1/w for vertex 1 732 * @param {vec} v2 Screen-space coordinates (x, y, z) and 1/w for vertex 2 733 */ 734 rrRenderer.triangleRasterizer.prototype.init = function(v0, v1, v2) { 735 this.m_v0 = v0; 736 this.m_v1 = v1; 737 this.m_v2 = v2; 738 739 // Positions in fixed-point coordinates 740 var x0 = rrRenderer.toSubpixelCoord(v0[0]); 741 var y0 = rrRenderer.toSubpixelCoord(v0[1]); 742 var x1 = rrRenderer.toSubpixelCoord(v1[0]); 743 var y1 = rrRenderer.toSubpixelCoord(v1[1]); 744 var x2 = rrRenderer.toSubpixelCoord(v2[0]); 745 var y2 = rrRenderer.toSubpixelCoord(v2[1]); 746 747 // Initialize edge functions 748 if (this.m_winding == rrRenderState.Winding.CCW) { 749 this.m_edge01 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x0, y0, x1, y1); 750 this.m_edge12 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x1, y1, x2, y2); 751 this.m_edge20 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x2, y2, x0, y0); 752 } else { 753 // Reverse edges 754 this.m_edge01 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x1, y1, x0, y0); 755 this.m_edge12 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x2, y2, x1, y1); 756 this.m_edge20 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x0, y0, x2, y2); 757 } 758 759 // Determine face 760 var s = this.m_edge01.evaluateEdge(x2, y2); 761 var positiveArea = (this.m_winding == rrRenderState.Winding.CCW ) ? s > 0 : s < 0; 762 this.m_face = positiveArea ? rrDefs.FaceType.FACETYPE_FRONT : rrDefs.FaceType.FACETYPE_BACK; 763 if (!positiveArea) { 764 // Reverse edges so that we can use CCW area tests & interpolation 765 this.m_edge01.reverseEdge(); 766 this.m_edge12.reverseEdge(); 767 this.m_edge20.reverseEdge(); 768 } 769 770 // Bounding box 771 var minX = Math.min(x0, x1, x2); 772 var maxX = Math.max(x0, x1, x2); 773 var minY = Math.min(y0, y1, y2); 774 var maxY = Math.max(y0, y1, y2); 775 776 this.m_bboxMin = []; 777 this.m_bboxMax = []; 778 this.m_bboxMin[0] = rrRenderer.floorSubpixelToPixelCoord(minX, this.m_horizontalFill == rrRenderState.HorizontalFill.LEFT); 779 this.m_bboxMin[1] = rrRenderer.floorSubpixelToPixelCoord(minY, this.m_verticalFill == rrRenderState.VerticalFill.BOTTOM); 780 this.m_bboxMax[0] = rrRenderer.ceilSubpixelToPixelCoord(maxX, this.m_horizontalFill == rrRenderState.HorizontalFill.RIGHT); 781 this.m_bboxMax[1] = rrRenderer.ceilSubpixelToPixelCoord(maxY, this.m_verticalFill == rrRenderState.VerticalFill.TOP); 782 783 // Clamp to viewport 784 var wX0 = this.m_viewport.rect.left; 785 var wY0 = this.m_viewport.rect.bottom; 786 var wX1 = wX0 + this.m_viewport.rect.width - 1; 787 var wY1 = wY0 + this.m_viewport.rect.height - 1; 788 789 this.m_bboxMin[0] = deMath.clamp(this.m_bboxMin[0], wX0, wX1); 790 this.m_bboxMin[1] = deMath.clamp(this.m_bboxMin[1], wY0, wY1); 791 this.m_bboxMax[0] = deMath.clamp(this.m_bboxMax[0], wX0, wX1); 792 this.m_bboxMax[1] = deMath.clamp(this.m_bboxMax[1], wY0, wY1); 793 794 this.m_curPos = [this.m_bboxMin[0], this.m_bboxMin[1]]; 795 }; 796 797 rrRenderer.triangleRasterizer.prototype.rasterize = function() { 798 var fragmentPackets = []; 799 var halfPixel = 1 << (rrRenderer.RASTERIZER_SUBPIXEL_BITS - 1); 800 801 // For depth interpolation; given barycentrics A, B, C = (1 - A -B) 802 // We can reformulate the usual z = z0 * A + z1 * B + z2 * C into more 803 // stable equation z = A * (z0 - z2) + B * (z1 - z2) + z2 804 var za = this.m_v0[2] - this.m_v2[2]; 805 var zb = this.m_v1[2] - this.m_v2[2]; 806 var zc = this.m_v2[2]; 807 808 var zn = this.m_viewport.zn; 809 var zf = this.m_viewport.zf; 810 var depthScale = (zf - zn) / 2; 811 var depthBias = (zf + zn) / 2; 812 813 while (this.m_curPos[1] <= this.m_bboxMax[1]) { 814 var x0 = this.m_curPos[0]; 815 var y0 = this.m_curPos[1]; 816 817 // Subpixel coords of (x0, y0), (x0 + 1, y0), (x0, y0 + 1), (x0 + 1, y0 + 1) 818 var sx0 = rrRenderer.toSubpixelCoord(x0) + halfPixel; 819 var sx1 = rrRenderer.toSubpixelCoord(x0 + 1) + halfPixel; 820 var sy0 = rrRenderer.toSubpixelCoord(y0) + halfPixel; 821 var sy1 = rrRenderer.toSubpixelCoord(y0 + 1) + halfPixel; 822 823 var sx = [sx0, sx1, sx0, sx1]; 824 var sy = [sy0, sy0, sy1, sy1]; 825 826 // Viewport test 827 var outX1 = x0 + 1 == this.m_viewport.rect.left + this.m_viewport.rect.width; 828 var outY1 = y0 + 1 == this.m_viewport.rect.bottom + this.m_viewport.rect.height; 829 830 // Coverage 831 var coverage = 0; 832 833 // Evaluate edge values 834 var e01 = []; 835 var e12 = []; 836 var e20 = []; 837 for (var i = 0; i < 4; i++) { 838 e01.push(this.m_edge01.evaluateEdge(sx[i], sy[i])); 839 e12.push(this.m_edge12.evaluateEdge(sx[i], sy[i])); 840 e20.push(this.m_edge20.evaluateEdge(sx[i], sy[i])); 841 } 842 843 // Compute coverage mask 844 coverage = rrRenderer.setCoverageValue(coverage, 1, 0, 0, 0, this.m_edge01.isInsideCCW(e01[0]) && this.m_edge12.isInsideCCW(e12[0]) && this.m_edge20.isInsideCCW(e20[0])); 845 coverage = rrRenderer.setCoverageValue(coverage, 1, 1, 0, 0, !outX1 && this.m_edge01.isInsideCCW(e01[1]) && this.m_edge12.isInsideCCW(e12[1]) && this.m_edge20.isInsideCCW(e20[1])); 846 coverage = rrRenderer.setCoverageValue(coverage, 1, 0, 1, 0, !outY1 && this.m_edge01.isInsideCCW(e01[2]) && this.m_edge12.isInsideCCW(e12[2]) && this.m_edge20.isInsideCCW(e20[2])); 847 coverage = rrRenderer.setCoverageValue(coverage, 1, 1, 1, 0, !outX1 && !outY1 && this.m_edge01.isInsideCCW(e01[3]) && this.m_edge12.isInsideCCW(e12[3]) && this.m_edge20.isInsideCCW(e20[3])); 848 849 // Advance to next location 850 this.m_curPos[0] += 2; 851 if (this.m_curPos[0] > this.m_bboxMax[0]) { 852 this.m_curPos[0] = this.m_bboxMin[0]; 853 this.m_curPos[1] += 2; 854 } 855 856 if (coverage == 0) 857 continue; // Discard 858 859 // Compute depth and barycentric coordinates 860 var edgeSum = deMath.add(deMath.add(e01, e12), e20); 861 var z0 = deMath.divide(e12, edgeSum); 862 var z1 = deMath.divide(e20, edgeSum); 863 864 var b0 = deMath.multiply(e12, [this.m_v0[3], this.m_v0[3], this.m_v0[3], this.m_v0[3]]); 865 var b1 = deMath.multiply(e20, [this.m_v1[3], this.m_v1[3], this.m_v1[3], this.m_v1[3]]); 866 var b2 = deMath.multiply(e01, [this.m_v2[3], this.m_v2[3], this.m_v2[3], this.m_v2[3]]); 867 var bSum = deMath.add(deMath.add(b0, b1), b2); 868 var barycentric0 = deMath.divide(b0, bSum); 869 var barycentric1 = deMath.divide(b1, bSum); 870 var barycentric2 = deMath.subtract(deMath.subtract([1, 1, 1, 1], barycentric0), barycentric1); 871 872 // In native dEQP, after rasterization, the pixel (x0, y0) actually represents four pixels: 873 // (x0, y0), (x0 + 1, y0), (x0, y0 + 1) and (x0 + 1, y0 + 1). 874 // The barycentrics and depths of these four pixels are to be computed after rasterization: 875 // barycentrics are computed in function shadeFragments in es3fFboTestUtil.cpp; 876 // depths are computed in function writeFragmentPackets in rrRenderer.cpp. 877 878 // In js, pixels are processed one after another, so their depths and barycentrics should be computed immediately. 879 880 // Determine if (x0, y0), (x0 + 1, y0), (x0, y0 + 1), (x0 + 1, y0 + 1) can be rendered 881 for (var fragNdx = 0; fragNdx < 4; fragNdx++) { 882 var xo = fragNdx % 2; 883 var yo = Math.trunc(fragNdx / 2); 884 var x = x0 + xo; 885 var y = y0 + yo; 886 887 // The value of numSamples always equals 1 in sglrReferenceContext. 888 if(rrRenderer.getCoverageAnyFragmentSampleLive(coverage, 1, xo, yo)) { 889 // Barycentric coordinates - referenced from function readTriangleVarying in rrShadingContext.hpp 890 var b = [barycentric0[fragNdx], barycentric1[fragNdx], barycentric2[fragNdx]]; 891 892 // Depth - referenced from writeFragmentPackets in rrRenderer.cpp 893 var depth = z0[fragNdx] * za + z1[fragNdx] * zb + zc; 894 depth = depth * depthScale + depthBias; 895 896 // Clip test 897 // Native dEQP does clipping test before rasterization. 898 if (!rrRenderer.clipTest(x, y, depth, this.m_viewport.rect)) 899 continue; 900 901 fragmentPackets.push(new rrFragmentOperations.Fragment(b, [x, y], depth)); 902 } 903 } 904 } 905 return fragmentPackets; 906 }; 907 908 /** 909 * @param {rrRenderState.RenderState} state 910 * @param {rrRenderer.RenderTarget} renderTarget 911 * @param {sglrShaderProgram.ShaderProgram} program 912 * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs 913 * @param {rrRenderer.PrimitiveType} primitive 914 * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex 915 * @param {number} count Number of indices 916 * @param {number} instanceID 917 */ 918 rrRenderer.drawTriangles = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { 919 920 /** 921 * @param {Array<rrVertexPacket.VertexPacket>} vertices 922 * @param {Array<number>} indices 923 * @return {Array<rrVertexPacket.VertexPacket>} 924 */ 925 var selectVertices = function(vertices, indices) { 926 var result = []; 927 for (var i = 0; i < indices.length; i++) 928 result.push(vertices[indices[i]]); 929 return result; 930 }; 931 932 // Referenced from native dEQP Renderer::drawInstanced() in rrRenderer.cpp 933 934 var primitives = new rrRenderer.PrimitiveList(primitive, count, first); 935 // Do not draw if nothing to draw 936 if (primitives.getNumElements() == 0) 937 return; 938 939 // Prepare transformation 940 var numVaryings = program.vertexShader.getOutputs().length; 941 var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); 942 var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); 943 var drawContext = new rrRenderer.DrawContext(); 944 drawContext.primitiveID = 0; 945 946 var numberOfVertices = primitives.getNumElements(); 947 var numVertexPackets = 0; 948 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { 949 950 // input 951 vertexPackets[numVertexPackets].instanceNdx = instanceID; 952 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); 953 954 // output 955 vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state 956 vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values 957 958 ++numVertexPackets; 959 960 } 961 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); 962 963 // Referenced from native dEQP Renderer::rasterizePrimitive() for triangle rasterization in rrRenderer.cpp 964 965 // In native dEQP, only maxFragmentPackets packets are processed per rasterize-shade-write loop; 966 // in js all packets are processed in one loop. 967 968 var rasterizer = new rrRenderer.triangleRasterizer(state); 969 970 for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { 971 var vertices = selectVertices(vertexPackets, prim); 972 973 var v0 = rrRenderer.transformGLToWindowCoords(state, vertices[0]); 974 var v1 = rrRenderer.transformGLToWindowCoords(state, vertices[1]); 975 var v2 = rrRenderer.transformGLToWindowCoords(state, vertices[2]); 976 977 rasterizer.init(v0, v1, v2); 978 979 // Culling 980 if ((state.cullMode == rrRenderState.CullMode.FRONT && rasterizer.m_face == rrDefs.FaceType.FACETYPE_FRONT) || 981 (state.cullMode == rrRenderState.CullMode.BACK && rasterizer.m_face == rrDefs.FaceType.FACETYPE_BACK)) 982 return; 983 984 /* TODO: Add Polygon Offset and Depth Clamp */ 985 986 // Compute a conservative integer bounding box for the triangle 987 var minX = Math.floor(Math.min(v0[0], v1[0], v2[0])); 988 var maxX = Math.ceil(Math.max(v0[0], v1[0], v2[0])); 989 var minY = Math.floor(Math.min(v0[1], v1[1], v2[1])); 990 var maxY = Math.ceil(Math.max(v0[1], v1[1], v2[1])); 991 992 // Shading context 993 var shadingContext = new rrShadingContext.FragmentShadingContext( 994 vertices[0].outputs, 995 vertices[1].outputs, 996 vertices[2].outputs 997 ); 998 shadingContext.setSize(maxX - minX, maxY - minY); 999 1000 // Rasterize 1001 var fragmentPackets = rasterizer.rasterize(); 1002 1003 // Shade 1004 program.shadeFragments(fragmentPackets, shadingContext); 1005 1006 // Handle fragment shader outputs 1007 rrRenderer.writeFragments2(state, renderTarget, fragmentPackets); 1008 } 1009 }; 1010 1011 /** 1012 * @param {rrRenderState.RenderState} state 1013 * @param {rrRenderer.RenderTarget} renderTarget 1014 * @param {sglrShaderProgram.ShaderProgram} program 1015 * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs 1016 * @param {rrRenderer.PrimitiveType} primitive 1017 * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex 1018 * @param {number} count Number of indices 1019 * @param {number} instanceID 1020 */ 1021 rrRenderer.drawLines = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { 1022 1023 /** 1024 * @param {Array<rrVertexPacket.VertexPacket>} vertices 1025 * @param {Array<number>} indices 1026 * @return {Array<rrVertexPacket.VertexPacket>} 1027 */ 1028 var selectVertices = function(vertices, indices) { 1029 var result = []; 1030 for (var i = 0; i < indices.length; i++) 1031 result.push(vertices[indices[i]]); 1032 return result; 1033 }; 1034 1035 var lengthSquared = function(a) { 1036 var sqSum = 0; 1037 for (var i = 0; i < a.length; i++) 1038 sqSum += a[i] * a[i]; 1039 return sqSum; 1040 }; 1041 1042 var dot = function(a, b) { 1043 var res = 0; 1044 for (var i = 0; i < a.length; i++) 1045 res += a[i] * b[i]; 1046 return res; 1047 }; 1048 1049 var rasterizeLine = function(v0, v1) { 1050 var d = [ 1051 Math.abs(v1[0] - v0[0]), 1052 Math.abs(v1[1] - v0[1])]; 1053 var xstep = v0[0] < v1[0] ? 1 : -1; 1054 var ystep = v0[1] < v1[1] ? 1 : -1; 1055 var x = v0[0]; 1056 var y = v0[1]; 1057 var offset = d[0] - d[1]; 1058 var lenV = [v1[0] - v0[0], v1[1] - v0[1]]; 1059 var lenSq = lengthSquared(lenV); 1060 1061 var packets = []; 1062 1063 while (true) { 1064 var t = dot([x - v0[0], y - v0[1]], lenV) / lenSq; 1065 var depth = (1 - t) * v0[2] + t * v1[2]; 1066 var b = [0, 0, 0]; 1067 b[0] = 1 - t; 1068 b[1] = t; 1069 1070 if (x == v1[0] && y == v1[1]) 1071 break; 1072 1073 depth = depth * depthScale + depthBias; 1074 packets.push(new rrFragmentOperations.Fragment(b, [x, y], depth)); 1075 1076 var offset2 = 2 * offset; 1077 if (offset2 > -1 * d[1]) { 1078 x += xstep; 1079 offset -= d[1]; 1080 } 1081 1082 if (offset2 < d[0]) { 1083 y += ystep; 1084 offset += d[0]; 1085 } 1086 } 1087 return packets; 1088 }; 1089 1090 var primitives = new rrRenderer.PrimitiveList(primitive, count, first); 1091 // Do not draw if nothing to draw 1092 if (primitives.getNumElements() == 0) 1093 return; 1094 1095 // Prepare transformation 1096 var numVaryings = program.vertexShader.getOutputs().length; 1097 var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); 1098 var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); 1099 var drawContext = new rrRenderer.DrawContext(); 1100 drawContext.primitiveID = 0; 1101 1102 var numberOfVertices = primitives.getNumElements(); 1103 var numVertexPackets = 0; 1104 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { 1105 1106 // input 1107 vertexPackets[numVertexPackets].instanceNdx = instanceID; 1108 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); 1109 1110 // output 1111 vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state 1112 vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values 1113 1114 ++numVertexPackets; 1115 1116 } 1117 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); 1118 1119 var zn = state.viewport.zn; 1120 var zf = state.viewport.zf; 1121 var depthScale = (zf - zn) / 2; 1122 var depthBias = (zf + zn) / 2; 1123 1124 // For each quad, we get a group of six vertex packets 1125 for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { 1126 var linePackets = selectVertices(vertexPackets, prim); 1127 1128 var v0 = rrRenderer.transformGLToWindowCoords(state, linePackets[0]); 1129 var v1 = rrRenderer.transformGLToWindowCoords(state, linePackets[1]); 1130 v0[2] = linePackets[0].position[2]; 1131 v1[2] = linePackets[1].position[2]; 1132 1133 v0[0] = Math.floor(v0[0]); 1134 v0[1] = Math.floor(v0[1]); 1135 v1[0] = Math.floor(v1[0]); 1136 v1[1] = Math.floor(v1[1]); 1137 1138 var lineWidth = state.line.lineWidth; 1139 1140 var shadingContext = new rrShadingContext.FragmentShadingContext( 1141 linePackets[0].outputs, 1142 linePackets[1].outputs, 1143 null 1144 ); 1145 var isXmajor = Math.abs(v1[0] - v0[0]) >= Math.abs(v1[1] - v0[1]); 1146 var packets = []; 1147 if (isXmajor) 1148 packets = rasterizeLine([v0[0], v0[1] - (lineWidth - 1) / 2, v0[2]], 1149 [v1[0], v1[1] - (lineWidth - 1) / 2, v1[2]]); 1150 else 1151 packets = rasterizeLine([v0[0] - (lineWidth - 1) / 2, v0[1], v0[2]], 1152 [v1[0] - (lineWidth - 1) / 2, v1[1], v1[2]]); 1153 var numPackets = packets.length; 1154 if (lineWidth > 1) 1155 for (var i = 0; i < numPackets; i++) { 1156 var p = packets[i]; 1157 for (var j = 1; j < lineWidth; j++) { 1158 var p2 = deUtil.clone(p); 1159 if (isXmajor) 1160 p2.pixelCoord[1] += j; 1161 else 1162 p2.pixelCoord[0] += j; 1163 packets.push(p2); 1164 } 1165 } 1166 1167 var clipped = []; 1168 for (var i = 0; i < packets.length; i++) { 1169 var p = packets[i]; 1170 if (rrRenderer.clipTest(p.pixelCoord[0], p.pixelCoord[1], p.sampleDepths[0], state.viewport.rect)) 1171 clipped.push(p); 1172 } 1173 program.shadeFragments(clipped, shadingContext); 1174 1175 rrRenderer.writeFragments2(state, renderTarget, clipped); 1176 } 1177 }; 1178 1179 /** 1180 * @param {rrRenderState.RenderState} state 1181 * @param {rrRenderer.RenderTarget} renderTarget 1182 * @param {sglrShaderProgram.ShaderProgram} program 1183 * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs 1184 * @param {rrRenderer.PrimitiveType} primitive 1185 * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex 1186 * @param {number} count Number of indices 1187 * @param {number} instanceID 1188 */ 1189 rrRenderer.drawPoints = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { 1190 /** 1191 * @param {Array<rrVertexPacket.VertexPacket>} vertices 1192 * @param {Array<number>} indices 1193 * @return {Array<rrVertexPacket.VertexPacket>} 1194 */ 1195 var selectVertices = function(vertices, indices) { 1196 var result = []; 1197 for (var i = 0; i < indices.length; i++) 1198 result.push(vertices[indices[i]]); 1199 return result; 1200 }; 1201 1202 var primitives = new rrRenderer.PrimitiveList(primitive, count, first); 1203 // Do not draw if nothing to draw 1204 if (primitives.getNumElements() == 0) 1205 return; 1206 1207 // Prepare transformation 1208 var numVaryings = program.vertexShader.getOutputs().length; 1209 var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); 1210 var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); 1211 var drawContext = new rrRenderer.DrawContext(); 1212 drawContext.primitiveID = 0; 1213 1214 var numberOfVertices = primitives.getNumElements(); 1215 var numVertexPackets = 0; 1216 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { 1217 1218 // input 1219 vertexPackets[numVertexPackets].instanceNdx = instanceID; 1220 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); 1221 1222 // output 1223 vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state 1224 vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values 1225 1226 ++numVertexPackets; 1227 1228 } 1229 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); 1230 1231 var zn = state.viewport.zn; 1232 var zf = state.viewport.zf; 1233 var depthScale = (zf - zn) / 2; 1234 var depthBias = (zf + zn) / 2; 1235 1236 // For each primitive, we draw a point. 1237 for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { 1238 var pointPackets = selectVertices(vertexPackets, prim); 1239 1240 var v0 = rrRenderer.transformGLToWindowCoords(state, pointPackets[0]); 1241 v0[2] = pointPackets[0].position[2]; 1242 var pointSize = pointPackets[0].pointSize; 1243 1244 var shadingContext = new rrShadingContext.FragmentShadingContext( 1245 pointPackets[0].outputs, 1246 null, 1247 null 1248 ); 1249 var packets = []; 1250 1251 var x = v0[0]; 1252 var y = v0[1]; 1253 var depth = v0[2]; 1254 var b = [1, 0, 0]; 1255 depth = depth * depthScale + depthBias; 1256 1257 for (var i = Math.floor(x - pointSize / 2); i < x + pointSize / 2; i++) { 1258 for (var j = Math.floor(y - pointSize / 2); j < y + pointSize / 2; j++) { 1259 var centerX = i + 0.5; 1260 var centerY = j + 0.5; 1261 if (Math.abs(centerX - x) <= pointSize / 2 && 1262 Math.abs(centerY - y) <= pointSize / 2 && 1263 rrRenderer.clipTest(i, j, depth, state.viewport.rect)) 1264 packets.push(new rrFragmentOperations.Fragment(b, [i, j], depth)); 1265 } 1266 } 1267 1268 program.shadeFragments(packets, shadingContext); 1269 1270 rrRenderer.writeFragments2(state, renderTarget, packets); 1271 } 1272 }; 1273 1274 });