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.
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
;
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
73 // * @param {boolean} depthEnabled Is depth buffer enabled
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;
86 rrRenderer
.DrawContext = function(id
) {
87 this.primitiveID
= id
|| 0;
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],
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
;
110 transformed
[0] * halfW
+ oX
,
111 transformed
[1] * halfH
+ oY
,
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
135 // * @param {rrShaders.VertexShader} vertexShader_
136 // * @param {rrShaders.FragmentShader} fragmentShader_
138 // var Program = function(vertexShader_, fragmentShader_) {
139 // this.vertexShader = vertexShader_;
140 // this.fragmentShader = fragmentShader_;
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;
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
);
166 rrRenderer
.DrawIndices
.prototype.readIndexArray = function(index
) { return this.access
[index
]; };
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
;
183 // !< primitive list for drawElements-like call
184 this.m_indices
= indices
;
185 this.m_indexType
= indices
.indexType
;
186 this.m_baseVertex
= indices
.baseVertex
;
192 * @param {number} elementNdx
195 rrRenderer
.PrimitiveList
.prototype.getIndex = function(elementNdx
) {
196 if (this.m_indices
) {
197 var index
= this.m_baseVertex
+ this.m_indices
.readIndexArray(elementNdx
);
199 throw new Error('Index must not be negative');
203 return this.m_baseVertex
+ elementNdx
;
207 * @param {number} elementNdx
208 * @param {number} restartIndex
211 rrRenderer
.PrimitiveList
.prototype.isRestartIndex = function(elementNdx
, restartIndex
) {
212 // implicit index or explicit index (without base vertex) equals restart
214 return this.m_indices
.readIndexArray(elementNdx
) == restartIndex
;
216 return elementNdx
== restartIndex
;
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
) {
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;
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;
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;
263 case rrRenderer
.PrimitiveType
.LINES
:
264 if (this.m_iterator
+ 2 <= this.m_numElements
) {
266 this.m_iterator
+= 2;
269 case rrRenderer
.PrimitiveType
.LINE_STRIP
:
270 if (this.m_iterator
+ 2 <= this.m_numElements
) {
272 this.m_iterator
+= 1;
275 case rrRenderer
.PrimitiveType
.LINE_LOOP
:
276 if (this.m_iterator
== this.m_numElements
)
278 if (this.m_iterator
+ 2 <= this.m_numElements
)
282 this.m_iterator
+= 1;
284 case rrRenderer
.PrimitiveType
.POINTS
:
285 if (this.m_iterator
== this.m_numElements
)
289 this.m_iterator
+= 1;
292 throw new Error('Unsupported primitive type: ' + deString
.enumToString(rrRenderer
.PrimitiveType
, this.m_primitiveType
));
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,
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
;
346 case tcuTexture
.TextureChannelClass
.UNSIGNED_INTEGER
:
347 fragmentDataType
= rrGenericVector
.GenericVecType
.UINT32
;
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();
365 if (state
.scissorTestEnabled
)
366 rrFragmentOperations
.executeScissorTest(fragments
, state
.scissorRectangle
);
371 rrFragmentOperations
.executeStencilCompare(fragments
, stencilState
, state
.numStencilBits
, stencilBuffer
);
372 rrFragmentOperations
.executeStencilSFail(fragments
, stencilState
, state
.numStencilBits
, stencilBuffer
);
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.
379 rrFragmentOperations
.executeDepthCompare(fragments
, state
.depthFunc
, depthBuffer
);
382 rrFragmentOperations
.executeDepthWrite(fragments
, depthBuffer
);
385 // Do dpFail and dpPass stencil writes.
388 rrFragmentOperations
.executeStencilDpFailAndPass(fragments
, stencilState
, state
.numStencilBits
, stencilBuffer
);
390 // Kill the samples that failed depth test.
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
];
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
);
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
];
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);
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
);
446 case rrGenericVector
.GenericVecType
.INT32
:
448 for (var i
= 0; i
< fragments
.length
; i
++) {
449 var frag
= fragments
[i
];
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
);
459 case rrGenericVector
.GenericVecType
.UINT32
:
461 for (var i
= 0; i
< fragments
.length
; i
++) {
462 var frag
= fragments
[i
];
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
);
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
483 rrRenderer
.getIndexOfCorner = function(isTop
, isRight
, vertexPackets
) {
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
)
502 throw new Error('Corner not found');
506 * Check that point is in the clipping volume
510 * @param {rrRenderState.WindowRectangle} rect
513 rrRenderer
.clipTest = function(x
, y
, z
, rect
) {
516 if (!deMath
.deInBounds32(x
, rect
.left
, rect
.left
+ rect
.width
))
518 if (!deMath
.deInBounds32(y
, rect
.bottom
, rect
.bottom
+ rect
.height
))
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
536 * @param {number} sampleNdx
539 rrRenderer
.getCoverageBit = function(numSamples
, x
, y
, sampleNdx
) {
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
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
565 * @param {number} sampleNdx
566 * @param {number} val
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
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
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
603 rrRenderer
.floorSubpixelToPixelCoord = function(coord
, fillEdge
) {
605 return Math
.trunc((coord
- (fillEdge
? 1 : 0)) >> rrRenderer
.RASTERIZER_SUBPIXEL_BITS
);
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
616 rrRenderer
.ceilSubpixelToPixelCoord = function(coord
, fillEdge
) {
618 return Math
.trunc((coord
+ (1 << rrRenderer
.RASTERIZER_SUBPIXEL_BITS
) - (fillEdge
? 0 : 1)) >> rrRenderer
.RASTERIZER_SUBPIXEL_BITS
);
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.
638 * @param {boolean} inclusive
640 rrRenderer
.edgeFunction = function(a
, b
, c
, inclusive
) {
644 this.inclusive
= inclusive
; // True if edge is inclusive according to fill rules
648 * Evaluate point (x,y)
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 () {
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
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
684 * @return {rrRenderer.edgeFunction}
686 rrRenderer
.initEdgeCCW = function(horizontalFill
, verticalFill
, x0
, y0
, x1
, y1
) {
689 var inclusive
= false;
692 inclusive
= verticalFill
== rrRenderState
.VerticalFill
.BOTTOM
? xd
>= 0 : xd
<= 0;
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:
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
) {
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
);
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
);
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
;
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();
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
);
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
);
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
];
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
;
833 // Evaluate edge values
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;
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);
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
;
897 // Native dEQP does clipping test before rasterization.
898 if (!rrRenderer
.clipTest(x
, y
, depth
, this.m_viewport
.rect
))
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
) {
927 for (var i
= 0; i
< indices
.length
; i
++)
928 result
.push(vertices
[indices
[i
]]);
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)
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
) {
951 vertexPackets
[numVertexPackets
].instanceNdx
= instanceID
;
952 vertexPackets
[numVertexPackets
].vertexNdx
= primitives
.getIndex(elementNdx
);
955 vertexPackets
[numVertexPackets
].pointSize
= state
.point
.pointSize
; // default value from the current state
956 vertexPackets
[numVertexPackets
].position
= [0, 0, 0, 0]; // no undefined values
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
);
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
))
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]));
993 var shadingContext
= new rrShadingContext
.FragmentShadingContext(
998 shadingContext
.setSize(maxX
- minX
, maxY
- minY
);
1001 var fragmentPackets
= rasterizer
.rasterize();
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
) {
1030 for (var i
= 0; i
< indices
.length
; i
++)
1031 result
.push(vertices
[indices
[i
]]);
1035 var lengthSquared = function(a
) {
1037 for (var i
= 0; i
< a
.length
; i
++)
1038 sqSum
+= a
[i
] * a
[i
];
1042 var dot = function(a
, b
) {
1044 for (var i
= 0; i
< a
.length
; i
++)
1049 var rasterizeLine = function(v0
, v1
) {
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;
1057 var offset
= d
[0] - d
[1];
1058 var lenV
= [v1
[0] - v0
[0], v1
[1] - v0
[1]];
1059 var lenSq
= lengthSquared(lenV
);
1064 var t
= dot([x
- v0
[0], y
- v0
[1]], lenV
) / lenSq
;
1065 var depth
= (1 - t
) * v0
[2] + t
* v1
[2];
1070 if (x
== v1
[0] && y
== v1
[1])
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]) {
1082 if (offset2
< d
[0]) {
1090 var primitives
= new rrRenderer
.PrimitiveList(primitive
, count
, first
);
1091 // Do not draw if nothing to draw
1092 if (primitives
.getNumElements() == 0)
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
) {
1107 vertexPackets
[numVertexPackets
].instanceNdx
= instanceID
;
1108 vertexPackets
[numVertexPackets
].vertexNdx
= primitives
.getIndex(elementNdx
);
1111 vertexPackets
[numVertexPackets
].pointSize
= state
.point
.pointSize
; // default value from the current state
1112 vertexPackets
[numVertexPackets
].position
= [0, 0, 0, 0]; // no undefined values
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
,
1145 var isXmajor
= Math
.abs(v1
[0] - v0
[0]) >= Math
.abs(v1
[1] - v0
[1]);
1148 packets
= rasterizeLine([v0
[0], v0
[1] - (lineWidth
- 1) / 2, v0
[2]],
1149 [v1
[0], v1
[1] - (lineWidth
- 1) / 2, v1
[2]]);
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
;
1155 for (var i
= 0; i
< numPackets
; i
++) {
1157 for (var j
= 1; j
< lineWidth
; j
++) {
1158 var p2
= deUtil
.clone(p
);
1160 p2
.pixelCoord
[1] += j
;
1162 p2
.pixelCoord
[0] += j
;
1168 for (var i
= 0; i
< packets
.length
; i
++) {
1170 if (rrRenderer
.clipTest(p
.pixelCoord
[0], p
.pixelCoord
[1], p
.sampleDepths
[0], state
.viewport
.rect
))
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
) {
1197 for (var i
= 0; i
< indices
.length
; i
++)
1198 result
.push(vertices
[indices
[i
]]);
1202 var primitives
= new rrRenderer
.PrimitiveList(primitive
, count
, first
);
1203 // Do not draw if nothing to draw
1204 if (primitives
.getNumElements() == 0)
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
) {
1219 vertexPackets
[numVertexPackets
].instanceNdx
= instanceID
;
1220 vertexPackets
[numVertexPackets
].vertexNdx
= primitives
.getIndex(elementNdx
);
1223 vertexPackets
[numVertexPackets
].pointSize
= state
.point
.pointSize
; // default value from the current state
1224 vertexPackets
[numVertexPackets
].position
= [0, 0, 0, 0]; // no undefined values
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
,
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
);