Bug 1945965 – remove new tab April Fools logo. r=home-newtab-reviewers,reemhamz
[gecko.git] / dom / canvas / test / webgl-conf / checkout / deqp / framework / referencerenderer / rrRenderer.js
blob4d5752b2c49edbbf5c6bea5109315fa4e98b5849
1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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');
38 goog.scope(function() {
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;
56 /**
57 * @enum
59 rrRenderer.PrimitiveType = {
60 TRIANGLES: 0, //!< Separate rrRenderer.triangles
61 TRIANGLE_STRIP: 1, //!< rrRenderer.Triangle strip
62 TRIANGLE_FAN: 2, //!< rrRenderer.Triangle fan
64 LINES: 3, //!< Separate lines
65 LINE_STRIP: 4, //!< Line strip
66 LINE_LOOP: 5, //!< Line loop
68 POINTS: 6 //!< Points
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 // };
82 /**
83 * @constructor
84 * @param {number=} id
86 rrRenderer.DrawContext = function(id) {
87 this.primitiveID = id || 0;
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>}
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;
109 return [
110 transformed[0] * halfW + oX,
111 transformed[1] * halfH + oY,
112 transformed[2],
113 transformed[3]
118 * @constructor
119 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} colorMultisampleBuffer
120 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} depthMultisampleBuffer
121 * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} stencilMultisampleBuffer
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;
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 // };
144 * @constructor
145 * @param {ArrayBuffer} data
146 * @param {rrDefs.IndexType} type
147 * @param {number} offset
148 * @param {number=} baseVertex_
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);
164 * @return {number}
166 rrRenderer.DrawIndices.prototype.readIndexArray = function(index) { return this.access[index]; };
169 * @constructor
170 * @param {rrRenderer.PrimitiveType} primitiveType
171 * @param {number} numElements
172 * @param {(number|rrRenderer.DrawIndices)} indices
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;
188 this.m_iterator = 0;
192 * @param {number} elementNdx
193 * @return {number}
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');
201 return index;
202 } else
203 return this.m_baseVertex + elementNdx;
207 * @param {number} elementNdx
208 * @param {number} restartIndex
209 * @return {boolean}
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;
220 * @return {number}
222 rrRenderer.PrimitiveList.prototype.getNumElements = function() {return this.m_numElements;};
225 * @return {rrRenderer.PrimitiveType}
227 rrRenderer.PrimitiveList.prototype.getPrimitiveType = function() {return this.m_primitiveType;};
230 * @return {?rrDefs.IndexType}
232 rrRenderer.PrimitiveList.prototype.getIndexType = function() {return this.m_indexType;};
235 * Generate a primitive from indices
236 * @param {boolean=} reset Restart generating primitives. Default false
237 * @return {Array<number>}
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;
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;
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;
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;
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;
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));
295 return result;
299 * @param {rrRenderState.RenderState} state
300 * @param {rrRenderer.RenderTarget} renderTarget
301 * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write
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]);
314 * @param {rrRenderState.RenderState} renderState
315 * @param {rrRenderer.RenderTarget} renderTarget
316 * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write
318 rrRenderer.writeFragments2 = function(renderState, renderTarget, fragments) {
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)
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;
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;
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;
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');
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();
363 // Scissor test.
365 if (state.scissorTestEnabled)
366 rrFragmentOperations.executeScissorTest(fragments, state.scissorRectangle);
368 // Stencil test.
370 if (doStencilTest) {
371 rrFragmentOperations.executeStencilCompare(fragments, stencilState, state.numStencilBits, stencilBuffer);
372 rrFragmentOperations.executeStencilSFail(fragments, stencilState, state.numStencilBits, stencilBuffer);
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.
378 if (doDepthTest) {
379 rrFragmentOperations.executeDepthCompare(fragments, state.depthFunc, depthBuffer);
381 if (state.depthMask)
382 rrFragmentOperations.executeDepthWrite(fragments, depthBuffer);
385 // Do dpFail and dpPass stencil writes.
387 if (doStencilTest)
388 rrFragmentOperations.executeStencilDpFailAndPass(fragments, stencilState, state.numStencilBits, stencilBuffer);
390 // Kill the samples that failed depth test.
392 if (doDepthTest) {
393 for (var i = 0; i < fragments.length; i++)
394 fragments[i].isAlive = fragments[i].isAlive && fragments[i].depthPassed;
397 // Paint fragments to target
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]);
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);
416 // Calculate blend factors to register.
417 rrFragmentOperations.executeBlendFactorComputeRGB(fragments, state.blendColor, state.blendRGBState);
418 rrFragmentOperations.executeBlendFactorComputeA(fragments, state.blendColor, state.blendAState);
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.
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];
434 // Finally, write the colors to the color buffer.
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;
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;
455 if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
456 rrFragmentOperations.executeSignedValueWrite(fragments, state.colorMask, colorBuffer);
457 break;
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;
468 if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
469 rrFragmentOperations.executeUnsignedValueWrite(fragments, state.colorMask, colorBuffer);
470 break;
472 default:
473 throw new Error('Unrecognized fragment data type:' + fragmentDataType);
478 * Determines the index of the corresponding vertex according to top/right conditions.
479 * @param {boolean} isTop
480 * @param {boolean} isRight
481 * @return {number}
483 rrRenderer.getIndexOfCorner = function(isTop, isRight, vertexPackets) {
484 var x = null;
485 var y = null;
487 var xcriteria = isRight ? Math.max : Math.min;
488 var ycriteria = isTop ? Math.max : Math.min;
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];
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;
502 throw new Error('Corner not found');
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}
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;
525 // Rasterizer configuration
526 rrRenderer.RASTERIZER_SUBPIXEL_BITS = 8;
527 rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT = 16;
529 // Referenced from rrRasterizer.hpp
532 * Get coverage bit value
533 * @param {number} numSamples
534 * @param {number} x
535 * @param {number} y
536 * @param {number} sampleNdx
537 * @return {number}
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);
547 * Get all sample bits for fragment
548 * @param {number} numSamples
549 * @param {number} x
550 * @param {number} y
551 * @return {number}
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;
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}
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);
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}
582 rrRenderer.getCoverageAnyFragmentSampleLive = function(mask, numSamples, x, y) {
583 return (mask & rrRenderer.getCoverageFragmentSampleBits(numSamples, x, y)) != 0;
586 // Referenced from rrRasterizer.cpp
589 * Pixel coord to sub pixel coord
590 * @param {number} v
591 * @return {number}
593 rrRenderer.toSubpixelCoord = function(v) {
594 return Math.trunc(v * (1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) + (v < 0 ? -0.5 : 0.5));
598 * Floor sub pixel coord to pixel coord
599 * @param {number} coord
600 * @param {boolean} fillEdge
601 * @return {number}
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);
611 * Ceil sub pixel coord to pixel coord
612 * @param {number} coord
613 * @param {boolean} fillEdge
614 * @return {number}
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);
624 * \brief Edge function - referenced from struct EdgeFunction in rrRasterizer.hpp
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
630 * D will be fixed-point value where lower (RASTERIZER_SUBPIXEL_BITS * 2) bits
631 * will be fractional part.
633 * Member function evaluateEdge, reverseEdge and isInsideCCW are referenced from rrRasterizer.cpp.
635 * @param {number} a
636 * @param {number} b
637 * @param {number} c
638 * @param {boolean} inclusive
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
648 * Evaluate point (x,y)
649 * @param {number} x
650 * @param {number} y
651 * @return {number}
653 rrRenderer.edgeFunction.prototype.evaluateEdge = function(x, y) {
654 return this.a * x + this.b * y + this.c;
658 * Reverse edge (e.g. from CCW to CW)
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;
668 * Determine if a point with value edgeVal is inside the CCW region of the edge
669 * @param {number} edgeVal
670 * @return {boolean}
672 rrRenderer.edgeFunction.prototype.isInsideCCW = function(edgeVal) {
673 return this.inclusive ? edgeVal >= 0 : edgeVal > 0;
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}
686 rrRenderer.initEdgeCCW = function(horizontalFill, verticalFill, x0, y0, x1, y1) {
687 var xd = x1 - x0;
688 var yd = y1 - y0;
689 var inclusive = false;
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;
696 return new rrRenderer.edgeFunction(y0 - y1, x1 - x0, x0 * y1 - y0 * x1, inclusive);
700 * \brief Triangle rasterizer - referenced from class TriangleRasterizer in rrRasterizer.hpp
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
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
719 * @param {rrRenderState.RenderState} state
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;
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
734 rrRenderer.triangleRasterizer.prototype.init = function(v0, v1, v2) {
735 this.m_v0 = v0;
736 this.m_v1 = v1;
737 this.m_v2 = v2;
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]);
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);
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();
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);
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);
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;
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);
794 this.m_curPos = [this.m_bboxMin[0], this.m_bboxMin[1]];
797 rrRenderer.triangleRasterizer.prototype.rasterize = function() {
798 var fragmentPackets = [];
799 var halfPixel = 1 << (rrRenderer.RASTERIZER_SUBPIXEL_BITS - 1);
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];
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;
813 while (this.m_curPos[1] <= this.m_bboxMax[1]) {
814 var x0 = this.m_curPos[0];
815 var y0 = this.m_curPos[1];
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;
823 var sx = [sx0, sx1, sx0, sx1];
824 var sy = [sy0, sy0, sy1, sy1];
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;
830 // Coverage
831 var coverage = 0;
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]));
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]));
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;
856 if (coverage == 0)
857 continue; // Discard
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);
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);
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.
878 // In js, pixels are processed one after another, so their depths and barycentrics should be computed immediately.
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;
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]];
892 // Depth - referenced from writeFragmentPackets in rrRenderer.cpp
893 var depth = z0[fragNdx] * za + z1[fragNdx] * zb + zc;
894 depth = depth * depthScale + depthBias;
896 // Clip test
897 // Native dEQP does clipping test before rasterization.
898 if (!rrRenderer.clipTest(x, y, depth, this.m_viewport.rect))
899 continue;
901 fragmentPackets.push(new rrFragmentOperations.Fragment(b, [x, y], depth));
905 return fragmentPackets;
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
918 rrRenderer.drawTriangles = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) {
921 * @param {Array<rrVertexPacket.VertexPacket>} vertices
922 * @param {Array<number>} indices
923 * @return {Array<rrVertexPacket.VertexPacket>}
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;
932 // Referenced from native dEQP Renderer::drawInstanced() in rrRenderer.cpp
934 var primitives = new rrRenderer.PrimitiveList(primitive, count, first);
935 // Do not draw if nothing to draw
936 if (primitives.getNumElements() == 0)
937 return;
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;
946 var numberOfVertices = primitives.getNumElements();
947 var numVertexPackets = 0;
948 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) {
950 // input
951 vertexPackets[numVertexPackets].instanceNdx = instanceID;
952 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx);
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
958 ++numVertexPackets;
961 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets);
963 // Referenced from native dEQP Renderer::rasterizePrimitive() for triangle rasterization in rrRenderer.cpp
965 // In native dEQP, only maxFragmentPackets packets are processed per rasterize-shade-write loop;
966 // in js all packets are processed in one loop.
968 var rasterizer = new rrRenderer.triangleRasterizer(state);
970 for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) {
971 var vertices = selectVertices(vertexPackets, prim);
973 var v0 = rrRenderer.transformGLToWindowCoords(state, vertices[0]);
974 var v1 = rrRenderer.transformGLToWindowCoords(state, vertices[1]);
975 var v2 = rrRenderer.transformGLToWindowCoords(state, vertices[2]);
977 rasterizer.init(v0, v1, v2);
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;
984 /* TODO: Add Polygon Offset and Depth Clamp */
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]));
992 // Shading context
993 var shadingContext = new rrShadingContext.FragmentShadingContext(
994 vertices[0].outputs,
995 vertices[1].outputs,
996 vertices[2].outputs
998 shadingContext.setSize(maxX - minX, maxY - minY);
1000 // Rasterize
1001 var fragmentPackets = rasterizer.rasterize();
1003 // Shade
1004 program.shadeFragments(fragmentPackets, shadingContext);
1006 // Handle fragment shader outputs
1007 rrRenderer.writeFragments2(state, renderTarget, fragmentPackets);
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
1021 rrRenderer.drawLines = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) {
1024 * @param {Array<rrVertexPacket.VertexPacket>} vertices
1025 * @param {Array<number>} indices
1026 * @return {Array<rrVertexPacket.VertexPacket>}
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;
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;
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;
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);
1061 var packets = [];
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;
1070 if (x == v1[0] && y == v1[1])
1071 break;
1073 depth = depth * depthScale + depthBias;
1074 packets.push(new rrFragmentOperations.Fragment(b, [x, y], depth));
1076 var offset2 = 2 * offset;
1077 if (offset2 > -1 * d[1]) {
1078 x += xstep;
1079 offset -= d[1];
1082 if (offset2 < d[0]) {
1083 y += ystep;
1084 offset += d[0];
1087 return packets;
1090 var primitives = new rrRenderer.PrimitiveList(primitive, count, first);
1091 // Do not draw if nothing to draw
1092 if (primitives.getNumElements() == 0)
1093 return;
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;
1102 var numberOfVertices = primitives.getNumElements();
1103 var numVertexPackets = 0;
1104 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) {
1106 // input
1107 vertexPackets[numVertexPackets].instanceNdx = instanceID;
1108 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx);
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
1114 ++numVertexPackets;
1117 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets);
1119 var zn = state.viewport.zn;
1120 var zf = state.viewport.zf;
1121 var depthScale = (zf - zn) / 2;
1122 var depthBias = (zf + zn) / 2;
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);
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];
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]);
1138 var lineWidth = state.line.lineWidth;
1140 var shadingContext = new rrShadingContext.FragmentShadingContext(
1141 linePackets[0].outputs,
1142 linePackets[1].outputs,
1143 null
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);
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);
1173 program.shadeFragments(clipped, shadingContext);
1175 rrRenderer.writeFragments2(state, renderTarget, clipped);
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
1189 rrRenderer.drawPoints = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) {
1191 * @param {Array<rrVertexPacket.VertexPacket>} vertices
1192 * @param {Array<number>} indices
1193 * @return {Array<rrVertexPacket.VertexPacket>}
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;
1202 var primitives = new rrRenderer.PrimitiveList(primitive, count, first);
1203 // Do not draw if nothing to draw
1204 if (primitives.getNumElements() == 0)
1205 return;
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;
1214 var numberOfVertices = primitives.getNumElements();
1215 var numVertexPackets = 0;
1216 for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) {
1218 // input
1219 vertexPackets[numVertexPackets].instanceNdx = instanceID;
1220 vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx);
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
1226 ++numVertexPackets;
1229 program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets);
1231 var zn = state.viewport.zn;
1232 var zf = state.viewport.zf;
1233 var depthScale = (zf - zn) / 2;
1234 var depthBias = (zf + zn) / 2;
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);
1240 var v0 = rrRenderer.transformGLToWindowCoords(state, pointPackets[0]);
1241 v0[2] = pointPackets[0].position[2];
1242 var pointSize = pointPackets[0].pointSize;
1244 var shadingContext = new rrShadingContext.FragmentShadingContext(
1245 pointPackets[0].outputs,
1246 null,
1247 null
1249 var packets = [];
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;
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));
1268 program.shadeFragments(packets, shadingContext);
1270 rrRenderer.writeFragments2(state, renderTarget, packets);