From 984861bca86557661d3e6e36ebd6dfe7baf40450 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Fri, 4 Sep 2015 17:55:33 -0400 Subject: [PATCH] Added ComputeCommand specs --- Source/Renderer/ComputeCommand.js | 12 +- Source/Renderer/ComputeEngine.js | 33 +++--- Source/Renderer/VertexArray.js | 3 +- Source/Scene/ImageryLayer.js | 5 +- Source/Scene/Scene.js | 25 ++--- Source/Scene/Sun.js | 45 ++++---- Specs/Renderer/ComputeCommandSpec.js | 162 +++++++++++++++++++++++++++ Specs/Renderer/FramebufferSpec.js | 4 +- Specs/Scene/GlobeSpec.js | 12 +- 9 files changed, 227 insertions(+), 74 deletions(-) create mode 100644 Specs/Renderer/ComputeCommandSpec.js diff --git a/Source/Renderer/ComputeCommand.js b/Source/Renderer/ComputeCommand.js index 7c34d2143b21..2f17d6600a09 100644 --- a/Source/Renderer/ComputeCommand.js +++ b/Source/Renderer/ComputeCommand.js @@ -59,22 +59,22 @@ define([ this.outputTexture = options.outputTexture; /** - * Function that is called after the ComputeCommand is executed. Takes the output - * texture as its single argument. + * Function that is called immediately before the ComputeCommand is executed. Used to + * update any renderer resources. Takes the ComputeCommand as its single argument. * * @type {Function} * @default undefined */ - this.callback = options.callback; + this.preExecute = options.preExecute; /** - * Function that is called immediately before the ComputeCommand is executed. Used to - * update any renderer resources. Takes the ComputeCommand as its single argument. + * Function that is called after the ComputeCommand is executed. Takes the output + * texture as its single argument. * * @type {Function} * @default undefined */ - this.preExecutionCallback = options.preExecutionCallback; + this.postExecute = options.postExecute; /** * Whether the renderer resources will persist beyond this call. If not, they diff --git a/Source/Renderer/ComputeEngine.js b/Source/Renderer/ComputeEngine.js index e07a370d4e66..f65a56fcb490 100644 --- a/Source/Renderer/ComputeEngine.js +++ b/Source/Renderer/ComputeEngine.js @@ -97,10 +97,7 @@ define([ vertexArray = VertexArray.fromGeometry({ context : computeEngine._context, geometry : geometry, - attributeLocations : { - position : 0, - textureCoordinates : 1 - }, + attributeLocations : viewportQuadAttributeLocations, bufferUsage : BufferUsage.STATIC_DRAW, interleave : true }); @@ -111,12 +108,12 @@ define([ return vertexArray; } - function createFramebuffer(context, texture) { + function createFramebuffer(context, outputTexture) { var fbo = new Framebuffer({ context : context, - colorTextures : [texture] + colorTextures : [outputTexture], + destroyAttachments : false }); - fbo.destroyAttachments = false; return fbo; } @@ -148,8 +145,8 @@ define([ } //>>includeEnd('debug'); - if (defined(computeCommand.preExecutionCallback)) { - computeCommand.preExecutionCallback(computeCommand); + if (defined(computeCommand.preExecute)) { + computeCommand.preExecute(computeCommand); } //>>includeStart('debug', pragmas.debug); @@ -162,20 +159,21 @@ define([ } //>>includeEnd('debug'); - var texture = computeCommand.outputTexture; - var width = texture.width; - var height = texture.height; + var outputTexture = computeCommand.outputTexture; + var width = outputTexture.width; + var height = outputTexture.height; var context = this._context; var vertexArray = defined(computeCommand.vertexArray) ? computeCommand.vertexArray : getViewportQuadVertexArray(this); var shaderProgram = defined(computeCommand.shaderProgram) ? computeCommand.shaderProgram : createViewportQuadShader(context, computeCommand.fragmentShaderSource); - var framebuffer = createFramebuffer(context, texture); + var framebuffer = createFramebuffer(context, outputTexture); var renderState = createRenderState(width, height); - var uniformMap = defaultValue(computeCommand.uniformMap, defaultValue.EMPTY_OBJECT); + var uniformMap = computeCommand.uniformMap; var clearCommand = clearCommandScratch; clearCommand.framebuffer = framebuffer; clearCommand.renderState = renderState; + clearCommand.execute(context); var drawCommand = drawCommandScratch; drawCommand.vertexArray = vertexArray; @@ -183,9 +181,8 @@ define([ drawCommand.shaderProgram = shaderProgram; drawCommand.uniformMap = uniformMap; drawCommand.framebuffer = framebuffer; - - clearCommand.execute(context); drawCommand.execute(context); + framebuffer.destroy(); if (!computeCommand.persists) { @@ -195,8 +192,8 @@ define([ } } - if (defined(computeCommand.callback)) { - computeCommand.callback(texture); + if (defined(computeCommand.postExecute)) { + computeCommand.postExecute(outputTexture); } }; diff --git a/Source/Renderer/VertexArray.js b/Source/Renderer/VertexArray.js index 96bff0e30f5c..5034938dc1f0 100644 --- a/Source/Renderer/VertexArray.js +++ b/Source/Renderer/VertexArray.js @@ -185,7 +185,8 @@ define([ * context : context, * sizeInBytes : 12, * usage : BufferUsage.STATIC_DRAW - * }); * var attributes = [ + * }); + * var attributes = [ * { * index : 0, * vertexBuffer : positionBuffer, diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 12a90c660626..d19117e44deb 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -724,7 +724,6 @@ define([ * @param {Context} context The rendered context to use. * @param {Imagery} imagery The imagery instance to reproject. */ - ImageryLayer.prototype._reprojectTexture = function(context, commandList, imagery) { var texture = imagery.texture; var rectangle = imagery.rectangle; @@ -741,10 +740,10 @@ define([ owner : this, // Update render resources right before execution instead of now. // This allows different ImageryLayers to share the same vao and buffers. - preExecutionCallback : function(command) { + preExecute : function(command) { reprojectToGeographic(command, context, texture, imagery.rectangle); }, - callback : function(outputTexture) { + postExecute : function(outputTexture) { texture.destroy(); imagery.texture = outputTexture; finalizeReprojectTexture(that, context, imagery, outputTexture); diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 73b00aaac450..a783f0e3d236 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -230,10 +230,10 @@ define([ this._sunPostProcess = undefined; + this._computeCommandList = []; this._commandList = []; this._frustumCommandsList = []; this._overlayCommandList = []; - this._computeCommandList = []; this._pickFramebuffer = undefined; @@ -815,17 +815,6 @@ define([ } }, - - /** - * @memberof Scene.prototype - * @type {ComputeEngine} - */ - computeEngine : { - get : function() { - return this._computeEngine; - } - }, - /** * This property is for debugging only; it is not for production use. *

@@ -1044,9 +1033,9 @@ define([ var distances = new Interval(); function createPotentiallyVisibleSet(scene) { + var computeList = scene._computeCommandList; var commandList = scene._commandList; var overlayList = scene._overlayCommandList; - var computeList = scene._computeCommandList; var cullingVolume = scene._frameState.cullingVolume; var camera = scene._camera; @@ -1069,8 +1058,9 @@ define([ frustumCommandsList[n].indices[p] = 0; } } - overlayList.length = 0; + computeList.length = 0; + overlayList.length = 0; var near = Number.MAX_VALUE; var far = Number.MIN_VALUE; @@ -1493,7 +1483,7 @@ define([ } if (defined(sunComputeCommand)) { - sunComputeCommand.execute(scene.computeEngine); + sunComputeCommand.execute(scene._computeEngine); } if (sunVisible) { @@ -1641,11 +1631,10 @@ define([ } function executeComputeCommands(scene) { - var context = scene.context; var commandList = scene._computeCommandList; var length = commandList.length; for (var i = 0; i < length; ++i) { - commandList[i].execute(scene.computeEngine); + commandList[i].execute(scene._computeEngine); } } @@ -1727,9 +1716,9 @@ define([ var context = scene.context; us.update(context, frameState); + scene._computeCommandList.length = 0; scene._commandList.length = 0; scene._overlayCommandList.length = 0; - scene._computeCommandList.length = 0; updatePrimitives(scene); createPotentiallyVisibleSet(scene); diff --git a/Source/Scene/Sun.js b/Source/Scene/Sun.js index 990ab94340c7..59934acab0d4 100644 --- a/Source/Scene/Sun.js +++ b/Source/Scene/Sun.js @@ -86,11 +86,19 @@ define([ */ this.show = true; - this._command = new DrawCommand({ + this._drawCommand = new DrawCommand({ primitiveType : PrimitiveType.TRIANGLES, boundingVolume : new BoundingSphere(), owner : this }); + this._computeCommand = new ComputeCommand({ + persists : false, + owner : this + }); + this._commands = { + drawCommand : undefined, + computeCommand : undefined + }; this._boundingVolume = new BoundingSphere(); this._boundingVolume2D = new BoundingSphere(); @@ -195,17 +203,15 @@ define([ } }; - computeCommand = new ComputeCommand({ - fragmentShaderSource : SunTextureFS, - outputTexture : this._texture, - uniformMap : uniformMap, - owner : this - }); + computeCommand = this._computeCommand; + computeCommand.fragmentShaderSource = SunTextureFS; + computeCommand.outputTexture = this._texture; + computeCommand.uniformMap = uniformMap; } - var command = this._command; + var drawCommand = this._drawCommand; - if (!defined(command.vertexArray)) { + if (!defined(drawCommand.vertexArray)) { var attributeLocations = { direction : 0 }; @@ -242,23 +248,23 @@ define([ usage : BufferUsage.STATIC_DRAW, indexDatatype : IndexDatatype.UNSIGNED_SHORT }); - command.vertexArray = new VertexArray({ + drawCommand.vertexArray = new VertexArray({ context : context, attributes : attributes, indexBuffer : indexBuffer }); - command.shaderProgram = ShaderProgram.fromCache({ + drawCommand.shaderProgram = ShaderProgram.fromCache({ context : context, vertexShaderSource : SunVS, fragmentShaderSource : SunFS, attributeLocations : attributeLocations }); - command.renderState = RenderState.fromCache({ + drawCommand.renderState = RenderState.fromCache({ blending : BlendingState.ALPHA_BLEND }); - command.uniformMap = this._uniformMap; + drawCommand.uniformMap = this._uniformMap; } var sunPosition = context.uniformState.sunPositionWC; @@ -276,9 +282,9 @@ define([ boundingVolume2D.radius = boundingVolume.radius; if (mode === SceneMode.SCENE3D) { - BoundingSphere.clone(boundingVolume, command.boundingVolume); + BoundingSphere.clone(boundingVolume, drawCommand.boundingVolume); } else if (mode === SceneMode.COLUMBUS_VIEW) { - BoundingSphere.clone(boundingVolume2D, command.boundingVolume); + BoundingSphere.clone(boundingVolume2D, drawCommand.boundingVolume); } var position = SceneTransforms.computeActualWgs84Position(frameState, sunPosition, scratchCartesian4); @@ -302,10 +308,9 @@ define([ this._size = Math.ceil(Cartesian2.magnitude(Cartesian2.subtract(limbWC, positionWC, scratchCartesian4))); this._size = 2.0 * this._size * (1.0 + 2.0 * this._glowLengthTS); - return { - computeCommand : computeCommand, - drawCommand : command - }; + this._commands.drawCommand = drawCommand; + this._commands.computeCommand = computeCommand; + return this._commands; }; /** @@ -340,7 +345,7 @@ define([ * sun = sun && sun.destroy(); */ Sun.prototype.destroy = function() { - var command = this._command; + var command = this._drawCommand; command.vertexArray = command.vertexArray && command.vertexArray.destroy(); command.shaderProgram = command.shaderProgram && command.shaderProgram.destroy(); diff --git a/Specs/Renderer/ComputeCommandSpec.js b/Specs/Renderer/ComputeCommandSpec.js new file mode 100644 index 000000000000..79d9d0596feb --- /dev/null +++ b/Specs/Renderer/ComputeCommandSpec.js @@ -0,0 +1,162 @@ +/*global defineSuite*/ +defineSuite([ + 'Renderer/ComputeCommand', + 'Core/BoundingRectangle', + 'Core/PixelFormat', + 'Renderer/Buffer', + 'Renderer/BufferUsage', + 'Renderer/ShaderProgram', + 'Renderer/Texture', + 'Renderer/VertexArray', + 'Scene/Material', + 'Scene/ViewportQuad', + 'Specs/createScene' + ], function( + ComputeCommand, + BoundingRectangle, + PixelFormat, + Buffer, + BufferUsage, + ShaderProgram, + Texture, + VertexArray, + Material, + ViewportQuad, + createScene) { + "use strict"; + /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn*/ + + var scene; + var context; + + beforeAll(function() { + scene = createScene(); + context = scene._context; + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + afterEach(function() { + scene.primitives.removeAll(); + }); + + function CommandMockPrimitive(command) { + this.update = function(context, frameState, commandList) { + commandList.push(command); + }; + this.destroy = function() { + }; + } + + it('throws if no shader is provided', function() { + var outputTexture = new Texture({ + context : context, + width : 1, + height : 1, + pixelFormat : PixelFormat.RGBA + }); + var computeCommand = new ComputeCommand({ + outputTexture : outputTexture + }); + scene.primitives.add(new CommandMockPrimitive(computeCommand)); + + expect(function() { + scene.renderForSpecs(); + }).toThrowDeveloperError(); + }); + + it('throws if no output texture is provided', function() { + var computeCommand = new ComputeCommand({ + fragmentShaderSource : 'void main() { gl_FragColor = vec4(1.0); }' + }); + scene.primitives.add(new CommandMockPrimitive(computeCommand)); + + expect(function() { + scene.renderForSpecs(); + }).toThrowDeveloperError(); + }); + + it('renderer resources are destroyed if persists is set to false', function() { + var vertexShader = 'attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }'; + var fragmentShader = 'void main() { gl_FragColor = vec4(1.0); }'; + var shaderProgram = ShaderProgram.fromCache({ + context : context, + vertexShaderSource : vertexShader, + fragmentShaderSource : fragmentShader + }); + + var vertexArray = new VertexArray({ + context : context, + attributes : [{ + index : shaderProgram.vertexAttributes.position.index, + vertexBuffer : Buffer.createVertexBuffer({ + context : context, + typedArray : new Float32Array([0, 0, 0, 1]), + usage : BufferUsage.STATIC_DRAW + }), + componentsPerAttribute : 4 + }] + }); + + var outputTexture = new Texture({ + context : context, + width : 1, + height : 1, + pixelFormat : PixelFormat.RGBA + }); + + var computeCommand = new ComputeCommand({ + vertexArray : vertexArray, + shaderProgram : shaderProgram, + outputTexture : outputTexture + }); + + // check that resources are not destroyed when persists is true + computeCommand.persists = true; + scene.primitives.add(new CommandMockPrimitive(computeCommand)); + scene.renderForSpecs(); + context.shaderCache.destroyReleasedShaderPrograms(); + scene.primitives.removeAll(); + + expect(shaderProgram.isDestroyed()).toEqual(false); + expect(vertexArray.isDestroyed()).toEqual(false); + expect(outputTexture.isDestroyed()).toEqual(false); + + // check that resources are destroyed when persists is false + // except outputTexture which is not destroyed by the compute command + computeCommand.persists = false; + scene.primitives.add(new CommandMockPrimitive(computeCommand)); + scene.renderForSpecs(); + context.shaderCache.destroyReleasedShaderPrograms(); + scene.primitives.removeAll(); + + expect(shaderProgram.isDestroyed()).toEqual(true); + expect(vertexArray.isDestroyed()).toEqual(true); + expect(outputTexture.isDestroyed()).toEqual(false); + }); + + it('renders to a texture and draws that texture to the screen', function() { + var outputTexture = new Texture({ + context : context, + width : 1, + height : 1, + pixelFormat : PixelFormat.RGBA + }); + var computeCommand = new ComputeCommand({ + fragmentShaderSource : 'void main() { gl_FragColor = vec4(1.0); }', + outputTexture : outputTexture + }); + + var viewportQuad = new ViewportQuad(); + viewportQuad.rectangle = new BoundingRectangle(0, 0, 1, 1); + viewportQuad.material = Material.fromType(Material.ImageType); + viewportQuad.material.uniforms.image = outputTexture; + + expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); + scene.primitives.add(new CommandMockPrimitive(computeCommand)); + scene.primitives.add(viewportQuad); + expect(scene.renderForSpecs()).not.toEqual([0, 0, 0, 255]); + }); +}, 'WebGL'); diff --git a/Specs/Renderer/FramebufferSpec.js b/Specs/Renderer/FramebufferSpec.js index d3cde166fd03..770c1a654056 100644 --- a/Specs/Renderer/FramebufferSpec.js +++ b/Specs/Renderer/FramebufferSpec.js @@ -238,9 +238,9 @@ defineSuite([ // 2 of 4. Clear framebuffer color attachment to green. framebuffer = new Framebuffer({ context : context, - colorTextures : [cubeMap.positiveX] + colorTextures : [cubeMap.positiveX], + destroyAttachments : false }); - framebuffer.destroyAttachments = false; var clearCommand = new ClearCommand({ color : new Color (0.0, 1.0, 0.0, 1.0), diff --git a/Specs/Scene/GlobeSpec.js b/Specs/Scene/GlobeSpec.js index 2d7db5456bc0..7cd0a8040446 100644 --- a/Specs/Scene/GlobeSpec.js +++ b/Specs/Scene/GlobeSpec.js @@ -83,9 +83,9 @@ defineSuite([ scene.camera.viewRectangle(new Rectangle(0.0001, 0.0001, 0.0025, 0.0025), Ellipsoid.WGS84); return updateUntilDone(globe).then(function() { - scene._globe = undefined; + scene.globe.show = false; expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); - scene._globe = globe; + scene.globe.show = true; expect(scene.renderForSpecs()).not.toEqual([0, 0, 0, 255]); }); }); @@ -100,9 +100,9 @@ defineSuite([ scene.camera.viewRectangle(new Rectangle(0.0001, 0.0001, 0.0025, 0.0025), Ellipsoid.WGS84); return updateUntilDone(globe).then(function() { - scene._globe = undefined; + scene.globe.show = false; expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); - scene._globe = globe; + scene.globe.show = true; expect(scene.renderForSpecs()).not.toEqual([0, 0, 0, 255]); }); }); @@ -133,9 +133,9 @@ defineSuite([ scene.camera.viewRectangle(new Rectangle(0.0001, 0.0001, 0.0025, 0.0025), Ellipsoid.WGS84); return updateUntilDone(globe).then(function() { - scene._globe = undefined; + scene.globe.show = false; expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); - scene._globe = globe; + scene.globe.show = true; expect(scene.renderForSpecs()).not.toEqual([0, 0, 0, 255]); }); });