defineSuite([ 'Core/BoundingRectangle', 'Core/Color', 'Core/ComponentDatatype', 'Core/IndexDatatype', 'Core/PrimitiveType', 'Core/WebGLConstants', 'Core/WindingOrder', 'Renderer/Buffer', 'Renderer/BufferUsage', 'Renderer/ClearCommand', 'Renderer/ContextLimits', 'Renderer/DrawCommand', 'Renderer/RenderState', 'Renderer/ShaderProgram', 'Renderer/VertexArray', 'Scene/BlendingState', 'Specs/createContext' ], 'Renderer/Draw', function( BoundingRectangle, Color, ComponentDatatype, IndexDatatype, PrimitiveType, WebGLConstants, WindingOrder, Buffer, BufferUsage, ClearCommand, ContextLimits, DrawCommand, RenderState, ShaderProgram, VertexArray, BlendingState, createContext) { 'use strict'; var context; var sp; var va; beforeAll(function() { context = createContext(); }); afterAll(function() { context.destroyForSpecs(); }); afterEach(function() { sp = sp && sp.destroy(); va = va && va.destroy(); }); it('draws a white point', function() { var fs = 'void main() { gl_FragColor = vec4(1.0); }'; expect({ context : context, fragmentShader : fs }).contextToRender(); }); it('draws a white point with an index buffer', function() { // Use separate context to work around IE 11.0.9 bug var context = createContext(); var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); // Two indices instead of one is a workaround for NVIDIA: // http://www.khronos.org/message_boards/viewtopic.php?f=44&t=3719 var indexBuffer = Buffer.createIndexBuffer({ context : context, typedArray : new Uint16Array([0, 0]), usage : BufferUsage.STATIC_DRAW, indexDatatype : IndexDatatype.UNSIGNED_SHORT }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }], indexBuffer : indexBuffer }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); sp = sp.destroy(); va = va.destroy(); context.destroyForSpecs(); }); it('draws a red point with two vertex buffers', function() { var vs = 'attribute vec4 position;' + 'attribute mediump float intensity;' + 'varying mediump float fs_intensity;' + 'void main() {' + ' gl_PointSize = 1.0; ' + ' gl_Position = position;' + ' fs_intensity = intensity;' + '}'; var fs = 'varying mediump float fs_intensity; void main() { gl_FragColor = vec4(fs_intensity, 0.0, 0.0, 1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0, intensity : 1 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }, { index : 1, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 1 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); }); it('draws a red point with one interleaved vertex buffers', function() { var vs = 'attribute vec4 position;' + 'attribute mediump float intensity;' + 'varying mediump float fs_intensity;' + 'void main() {' + ' gl_PointSize = 1.0; ' + ' gl_Position = position;' + ' fs_intensity = intensity;' + '}'; var fs = 'varying mediump float fs_intensity; void main() { gl_FragColor = vec4(fs_intensity, 0.0, 0.0, 1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0, intensity : 1 } }); var stride = 5 * Float32Array.BYTES_PER_ELEMENT; var vertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1, 1]), usage : BufferUsage.STATIC_DRAW }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : vertexBuffer, componentsPerAttribute : 4, offsetInBytes : 0, strideInBytes : stride }, { index : 1, vertexBuffer : vertexBuffer, componentsPerAttribute : 1, offsetInBytes : 4 * Float32Array.BYTES_PER_ELEMENT, strideInBytes : stride }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); }); it('draws with scissor test', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Render point - fails scissor test var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ scissorTest : { enabled : true, rectangle : new BoundingRectangle(1, 1, 0, 0) } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Render point - passes scissor test command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ scissorTest : { enabled : true, rectangle : new BoundingRectangle(0, 0, 1, 1) } }) }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with color mask', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Render point - blue color mask var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ colorMask : { red : true, green : false, blue : false, alpha : false } }) }); command.execute(context); expect(context).toReadPixels([255, 0, 0, 255]); // 3 of 3: Render point - red color mask (blue channel not touched) command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ colorMask : { red : false, green : false, blue : true, alpha : false } }) }); command.execute(context); expect(context).toReadPixels([255, 0, 255, 255]); }); it('draws with additive blending', function() { var cxt = createContext({ webgl : { alpha : true } }); var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(0.5); }'; var sp = ShaderProgram.fromCache({ context : cxt, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); var va = new VertexArray({ context : cxt, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : cxt, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(cxt); expect(cxt).toReadPixels([0, 0, 0, 0]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ blending : { enabled : true, equationRgb : WebGLConstants.FUNC_ADD, // Optional, default equationAlpha : WebGLConstants.FUNC_ADD, // Optional, default functionSourceRgb : WebGLConstants.ONE, // Optional, default functionSourceAlpha : WebGLConstants.ONE, // Optional, default functionDestinationRgb : WebGLConstants.ONE, functionDestinationAlpha : WebGLConstants.ONE } }) }); // 2 of 3: Blend: 0 + 0.5 command.execute(cxt); expect({ context : cxt, epsilon : 1 }).toReadPixels([127, 127, 127, 127]); // 3 of 3: Blend: 0.5 + 0.5 command.execute(cxt); expect({ context : cxt, epsilon : 1 }).toReadPixels([254, 254, 254, 254]); sp.destroy(); va.destroy(); cxt.destroyForSpecs(); }); it('draws with alpha blending', function() { var cxt = createContext({ webgl : { alpha : true } }); var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 0.5); }'; var sp = ShaderProgram.fromCache({ context : cxt, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); var va = new VertexArray({ context : cxt, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : cxt, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(cxt); expect(cxt).toReadPixels([0, 0, 0, 0]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ blending : { enabled : true, equationRgb : WebGLConstants.FUNC_ADD, equationAlpha : WebGLConstants.FUNC_SUBTRACT, // does not actually matter functionSourceRgb : WebGLConstants.SRC_ALPHA, functionSourceAlpha : WebGLConstants.ONE, // Don't blend alpha functionDestinationRgb : WebGLConstants.ONE_MINUS_SRC_ALPHA, functionDestinationAlpha : WebGLConstants.ZERO } }) }); // 2 of 3: Blend: RGB: (255 * 0.5) + (0 * 0.5), Alpha: 0.5 + 0 command.execute(cxt); expect({ context : cxt, epsilon : 1 }).toReadPixels([127, 127, 127, 127]); // 3 of 3: Blend: RGB: (255 * 0.5) + (127 * 0.5), Alpha: 0.5 + 0 command.execute(cxt); expect({ context : cxt, epsilon : 2 }).toReadPixels([191, 191, 191, 127]); sp.destroy(); va.destroy(); cxt.destroyForSpecs(); }); it('draws with blend color', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ blending : { enabled : true, color : { red : 0.5, green : 0.5, blue : 0.5, alpha : 0.5 }, equationRgb : WebGLConstants.FUNC_SUBTRACT, equationAlpha : WebGLConstants.FUNC_SUBTRACT, functionSourceRgb : WebGLConstants.CONSTANT_COLOR, functionSourceAlpha : WebGLConstants.ONE, functionDestinationRgb : WebGLConstants.ZERO, functionDestinationAlpha : WebGLConstants.ZERO } }) }); // 2 of 3: Blend: RGB: 255 - 127, Alpha: 255 - (255 - 255) // Epsilon of 1 because ANGLE gives 127 and desktop GL gives 128. command.execute(context); expect({ context : context, epsilon : 1 }).toReadPixels([128, 128, 128, 255]); }); it('draws with culling', function() { var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Cull front faces - nothing is drawn var command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ cull : { enabled : true, face : WebGLConstants.FRONT } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Cull back faces - nothing is culled command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ cull : { enabled : true, face : WebGLConstants.BACK } }) }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with front face winding order', function() { var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); // 1 of 3: Clear to black ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 3: Cull back faces with opposite winding order - nothing is drawn var command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ frontFace : WindingOrder.CLOCKWISE, cull : { enabled : true, face : WebGLConstants.BACK } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 3: Cull back faces with correct winding order - nothing is culled command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ frontFace : WindingOrder.COUNTER_CLOCKWISE, cull : { enabled : true, face : WebGLConstants.BACK } }) }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with the depth test', function() { var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); var command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ depthTest : { enabled : true, func : WebGLConstants.LEQUAL } }) }); // 1 of 2. Triangle fan passes the depth test. var clearCommand = new ClearCommand({ color : new Color (0.0, 0.0, 0.0, 0.0), depth : 1.0 }); clearCommand.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); // 2 of 2. Triangle fan fails the depth test. clearCommand.color = new Color (0.0, 0.0, 0.0, 0.0); clearCommand.depth = 0.0; clearCommand.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); }); it('draws with depth range', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(gl_DepthRange.near, gl_DepthRange.far, 0.0, 1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ depthRange : { near : 0.25, far : 0.75 } }) }); command.execute(context); expect(context).toReadPixels([64, 191, 0, 255]); }); it('draws with line width', function() { var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.LINES, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ lineWidth : ContextLimits.maximumAliasedLineWidth // May only be 1. }) }); command.execute(context); // I believe different GL implementations are allowed to AA // in different ways (or at least that is what we see in practice), // so verify it at least rendered something. expect(context).notToReadPixels([0, 0, 0, 255]); }); it('draws with polygon offset', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ polygonOffset : { enabled : true, factor : 1, units : 1 } }) }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with sample coverage', function() { if (!context.antialias) { // Sample coverage requires antialiasing. return; } var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ sampleCoverage : { enabled : true, value : 0, invert : false } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ sampleCoverage : { enabled : false } }) }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with stencil test (front)', function() { if (context.stencilBits === 0) { return; } var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); var rs = RenderState.fromCache({ stencilTest : { enabled : true, frontFunction : WebGLConstants.EQUAL, reference : 1, mask : 1 } }); // 1 of 4. Clear, including stencil ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 4. Render where stencil is set - nothing is drawn var command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : rs }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 4. Render to stencil only, increment command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ colorMask : { red : false, green : false, blue : false, alpha : false }, stencilTest : { enabled : true, frontOperation : { zPass : WebGLConstants.INCR } } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 4 of 4. Render where stencil is set command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : rs }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with stencil test (back)', function() { if (context.stencilBits === 0) { return; } var vs = 'attribute vec4 position; void main() { gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([-1000, -1000, 0, 1, 1000, -1000, 0, 1, -1000, 1000, 0, 1, 1000, 1000, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); var rs = RenderState.fromCache({ frontFace : WindingOrder.CLOCKWISE, stencilTest : { enabled : true, backFunction : WebGLConstants.NOTEQUAL, reference : 0 } }); // 1 of 4. Clear, including stencil ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 2 of 4. Render where stencil is set - nothing is drawn var command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : rs }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 3 of 4. Render to stencil only, increment command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : RenderState.fromCache({ frontFace : WindingOrder.CLOCKWISE, colorMask : { red : false, green : false, blue : false, alpha : false }, stencilTest : { enabled : true, backOperation : { zPass : WebGLConstants.INVERT } } }) }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // 4 of 4. Render where stencil is set command = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLE_STRIP, shaderProgram : sp, vertexArray : va, renderState : rs }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws with an offset and count', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, -1, 0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); // The first point in the vertex buffer does not generate any pixels var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, offset : 0, count : 1, shaderProgram : sp, vertexArray : va }); command.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, offset : 1, count : 1, shaderProgram : sp, vertexArray : va }); command.execute(context); expect(context).toReadPixels([255, 255, 255, 255]); }); it('draws two instances of a point with different per-instance colors', function() { if (context.instancedArrays) { var vs = 'attribute vec4 position;' + 'attribute vec4 color;' + 'varying vec4 v_color;' + 'void main() {' + ' gl_PointSize = 1.0; ' + ' gl_Position = position;' + ' v_color = color;' + '}'; var fs = 'varying vec4 v_color; void main() { gl_FragColor = v_color; }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0, color: 1 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }, { index : 1, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255]), usage : BufferUsage.STATIC_DRAW }), componentDatatype : ComponentDatatype.UNSIGNED_BYTE, componentsPerAttribute : 4, normalize : true, instanceDivisor : 1 }] }); ClearCommand.ALL.execute(context); expect(context).toReadPixels([0, 0, 0, 255]); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, instanceCount : 2, renderState : RenderState.fromCache({ blending : BlendingState.ADDITIVE_BLEND }) }); command.execute(context); expect(context).toReadPixels([255, 255, 0, 255]); } }); it('fails to draw (missing command)', function() { expect(function() { context.draw(); }).toThrowDeveloperError(); }); it('fails to draw (missing shaderProgram)', function() { expect(function() { context.draw({ primitiveType : PrimitiveType.POINTS }); }).toThrowDeveloperError(); }); it('fails to draw (missing primitiveType)', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs }); expect(function() { context.draw({ shaderProgram : sp }); }).toThrowDeveloperError(); }); it('fails to draw (primitiveType)', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs }); expect(function() { context.draw({ primitiveType : 'invalid value', shaderProgram : sp }); }).toThrowDeveloperError(); }); it('fails to draw (missing vertexArray)', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs }); expect(function() { context.draw({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp }); }).toThrowDeveloperError(); }); it('fails to draw (negative offset)', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs }); expect(function() { context.draw({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : new VertexArray({ context : context }), offset : -1, count : 1 }); }).toThrowDeveloperError(); }); it('throws if instanceCount is less than one', function() { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, instanceCount : -1 }); expect(function() { command.execute(context); }).toThrowDeveloperError(); }); it('throws when instanceCount is greater than one and the instanced arrays extension is not supported', function() { if (!context.instancedArrays) { var vs = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = ShaderProgram.fromCache({ context : context, vertexShaderSource : vs, fragmentShaderSource : fs, attributeLocations: { position: 0 } }); va = new VertexArray({ context : context, attributes : [{ index : 0, vertexBuffer : Buffer.createVertexBuffer({ context : context, typedArray : new Float32Array([0, 0, 0, 1]), usage : BufferUsage.STATIC_DRAW }), componentsPerAttribute : 4 }] }); var command = new DrawCommand({ primitiveType : PrimitiveType.POINTS, shaderProgram : sp, vertexArray : va, instanceCount : 2 }); expect(function() { command.execute(context); }).toThrowDeveloperError(); } }); }, 'WebGL');