tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });