123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996 |
- import BoundingRectangle from "../Core/BoundingRectangle.js";
- import Color from "../Core/Color.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import WebGLConstants from "../Core/WebGLConstants.js";
- import WindingOrder from "../Core/WindingOrder.js";
- import ContextLimits from "./ContextLimits.js";
- import freezeRenderState from "./freezeRenderState.js";
- function validateBlendEquation(blendEquation) {
- return (
- blendEquation === WebGLConstants.FUNC_ADD ||
- blendEquation === WebGLConstants.FUNC_SUBTRACT ||
- blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT ||
- blendEquation === WebGLConstants.MIN ||
- blendEquation === WebGLConstants.MAX
- );
- }
- function validateBlendFunction(blendFunction) {
- return (
- blendFunction === WebGLConstants.ZERO ||
- blendFunction === WebGLConstants.ONE ||
- blendFunction === WebGLConstants.SRC_COLOR ||
- blendFunction === WebGLConstants.ONE_MINUS_SRC_COLOR ||
- blendFunction === WebGLConstants.DST_COLOR ||
- blendFunction === WebGLConstants.ONE_MINUS_DST_COLOR ||
- blendFunction === WebGLConstants.SRC_ALPHA ||
- blendFunction === WebGLConstants.ONE_MINUS_SRC_ALPHA ||
- blendFunction === WebGLConstants.DST_ALPHA ||
- blendFunction === WebGLConstants.ONE_MINUS_DST_ALPHA ||
- blendFunction === WebGLConstants.CONSTANT_COLOR ||
- blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_COLOR ||
- blendFunction === WebGLConstants.CONSTANT_ALPHA ||
- blendFunction === WebGLConstants.ONE_MINUS_CONSTANT_ALPHA ||
- blendFunction === WebGLConstants.SRC_ALPHA_SATURATE
- );
- }
- function validateCullFace(cullFace) {
- return (
- cullFace === WebGLConstants.FRONT ||
- cullFace === WebGLConstants.BACK ||
- cullFace === WebGLConstants.FRONT_AND_BACK
- );
- }
- function validateDepthFunction(depthFunction) {
- return (
- depthFunction === WebGLConstants.NEVER ||
- depthFunction === WebGLConstants.LESS ||
- depthFunction === WebGLConstants.EQUAL ||
- depthFunction === WebGLConstants.LEQUAL ||
- depthFunction === WebGLConstants.GREATER ||
- depthFunction === WebGLConstants.NOTEQUAL ||
- depthFunction === WebGLConstants.GEQUAL ||
- depthFunction === WebGLConstants.ALWAYS
- );
- }
- function validateStencilFunction(stencilFunction) {
- return (
- stencilFunction === WebGLConstants.NEVER ||
- stencilFunction === WebGLConstants.LESS ||
- stencilFunction === WebGLConstants.EQUAL ||
- stencilFunction === WebGLConstants.LEQUAL ||
- stencilFunction === WebGLConstants.GREATER ||
- stencilFunction === WebGLConstants.NOTEQUAL ||
- stencilFunction === WebGLConstants.GEQUAL ||
- stencilFunction === WebGLConstants.ALWAYS
- );
- }
- function validateStencilOperation(stencilOperation) {
- return (
- stencilOperation === WebGLConstants.ZERO ||
- stencilOperation === WebGLConstants.KEEP ||
- stencilOperation === WebGLConstants.REPLACE ||
- stencilOperation === WebGLConstants.INCR ||
- stencilOperation === WebGLConstants.DECR ||
- stencilOperation === WebGLConstants.INVERT ||
- stencilOperation === WebGLConstants.INCR_WRAP ||
- stencilOperation === WebGLConstants.DECR_WRAP
- );
- }
- /**
- * @private
- */
- function RenderState(renderState) {
- const rs = defaultValue(renderState, defaultValue.EMPTY_OBJECT);
- const cull = defaultValue(rs.cull, defaultValue.EMPTY_OBJECT);
- const polygonOffset = defaultValue(
- rs.polygonOffset,
- defaultValue.EMPTY_OBJECT
- );
- const scissorTest = defaultValue(rs.scissorTest, defaultValue.EMPTY_OBJECT);
- const scissorTestRectangle = defaultValue(
- scissorTest.rectangle,
- defaultValue.EMPTY_OBJECT
- );
- const depthRange = defaultValue(rs.depthRange, defaultValue.EMPTY_OBJECT);
- const depthTest = defaultValue(rs.depthTest, defaultValue.EMPTY_OBJECT);
- const colorMask = defaultValue(rs.colorMask, defaultValue.EMPTY_OBJECT);
- const blending = defaultValue(rs.blending, defaultValue.EMPTY_OBJECT);
- const blendingColor = defaultValue(blending.color, defaultValue.EMPTY_OBJECT);
- const stencilTest = defaultValue(rs.stencilTest, defaultValue.EMPTY_OBJECT);
- const stencilTestFrontOperation = defaultValue(
- stencilTest.frontOperation,
- defaultValue.EMPTY_OBJECT
- );
- const stencilTestBackOperation = defaultValue(
- stencilTest.backOperation,
- defaultValue.EMPTY_OBJECT
- );
- const sampleCoverage = defaultValue(
- rs.sampleCoverage,
- defaultValue.EMPTY_OBJECT
- );
- const viewport = rs.viewport;
- this.frontFace = defaultValue(rs.frontFace, WindingOrder.COUNTER_CLOCKWISE);
- this.cull = {
- enabled: defaultValue(cull.enabled, false),
- face: defaultValue(cull.face, WebGLConstants.BACK),
- };
- this.lineWidth = defaultValue(rs.lineWidth, 1.0);
- this.polygonOffset = {
- enabled: defaultValue(polygonOffset.enabled, false),
- factor: defaultValue(polygonOffset.factor, 0),
- units: defaultValue(polygonOffset.units, 0),
- };
- this.scissorTest = {
- enabled: defaultValue(scissorTest.enabled, false),
- rectangle: BoundingRectangle.clone(scissorTestRectangle),
- };
- this.depthRange = {
- near: defaultValue(depthRange.near, 0),
- far: defaultValue(depthRange.far, 1),
- };
- this.depthTest = {
- enabled: defaultValue(depthTest.enabled, false),
- func: defaultValue(depthTest.func, WebGLConstants.LESS), // func, because function is a JavaScript keyword
- };
- this.colorMask = {
- red: defaultValue(colorMask.red, true),
- green: defaultValue(colorMask.green, true),
- blue: defaultValue(colorMask.blue, true),
- alpha: defaultValue(colorMask.alpha, true),
- };
- this.depthMask = defaultValue(rs.depthMask, true);
- this.stencilMask = defaultValue(rs.stencilMask, ~0);
- this.blending = {
- enabled: defaultValue(blending.enabled, false),
- color: new Color(
- defaultValue(blendingColor.red, 0.0),
- defaultValue(blendingColor.green, 0.0),
- defaultValue(blendingColor.blue, 0.0),
- defaultValue(blendingColor.alpha, 0.0)
- ),
- equationRgb: defaultValue(blending.equationRgb, WebGLConstants.FUNC_ADD),
- equationAlpha: defaultValue(
- blending.equationAlpha,
- WebGLConstants.FUNC_ADD
- ),
- functionSourceRgb: defaultValue(
- blending.functionSourceRgb,
- WebGLConstants.ONE
- ),
- functionSourceAlpha: defaultValue(
- blending.functionSourceAlpha,
- WebGLConstants.ONE
- ),
- functionDestinationRgb: defaultValue(
- blending.functionDestinationRgb,
- WebGLConstants.ZERO
- ),
- functionDestinationAlpha: defaultValue(
- blending.functionDestinationAlpha,
- WebGLConstants.ZERO
- ),
- };
- this.stencilTest = {
- enabled: defaultValue(stencilTest.enabled, false),
- frontFunction: defaultValue(
- stencilTest.frontFunction,
- WebGLConstants.ALWAYS
- ),
- backFunction: defaultValue(stencilTest.backFunction, WebGLConstants.ALWAYS),
- reference: defaultValue(stencilTest.reference, 0),
- mask: defaultValue(stencilTest.mask, ~0),
- frontOperation: {
- fail: defaultValue(stencilTestFrontOperation.fail, WebGLConstants.KEEP),
- zFail: defaultValue(stencilTestFrontOperation.zFail, WebGLConstants.KEEP),
- zPass: defaultValue(stencilTestFrontOperation.zPass, WebGLConstants.KEEP),
- },
- backOperation: {
- fail: defaultValue(stencilTestBackOperation.fail, WebGLConstants.KEEP),
- zFail: defaultValue(stencilTestBackOperation.zFail, WebGLConstants.KEEP),
- zPass: defaultValue(stencilTestBackOperation.zPass, WebGLConstants.KEEP),
- },
- };
- this.sampleCoverage = {
- enabled: defaultValue(sampleCoverage.enabled, false),
- value: defaultValue(sampleCoverage.value, 1.0),
- invert: defaultValue(sampleCoverage.invert, false),
- };
- this.viewport = defined(viewport)
- ? new BoundingRectangle(
- viewport.x,
- viewport.y,
- viewport.width,
- viewport.height
- )
- : undefined;
- //>>includeStart('debug', pragmas.debug);
- if (
- this.lineWidth < ContextLimits.minimumAliasedLineWidth ||
- this.lineWidth > ContextLimits.maximumAliasedLineWidth
- ) {
- throw new DeveloperError(
- "renderState.lineWidth is out of range. Check minimumAliasedLineWidth and maximumAliasedLineWidth."
- );
- }
- if (!WindingOrder.validate(this.frontFace)) {
- throw new DeveloperError("Invalid renderState.frontFace.");
- }
- if (!validateCullFace(this.cull.face)) {
- throw new DeveloperError("Invalid renderState.cull.face.");
- }
- if (
- this.scissorTest.rectangle.width < 0 ||
- this.scissorTest.rectangle.height < 0
- ) {
- throw new DeveloperError(
- "renderState.scissorTest.rectangle.width and renderState.scissorTest.rectangle.height must be greater than or equal to zero."
- );
- }
- if (this.depthRange.near > this.depthRange.far) {
- // WebGL specific - not an error in GL ES
- throw new DeveloperError(
- "renderState.depthRange.near can not be greater than renderState.depthRange.far."
- );
- }
- if (this.depthRange.near < 0) {
- // Would be clamped by GL
- throw new DeveloperError(
- "renderState.depthRange.near must be greater than or equal to zero."
- );
- }
- if (this.depthRange.far > 1) {
- // Would be clamped by GL
- throw new DeveloperError(
- "renderState.depthRange.far must be less than or equal to one."
- );
- }
- if (!validateDepthFunction(this.depthTest.func)) {
- throw new DeveloperError("Invalid renderState.depthTest.func.");
- }
- if (
- this.blending.color.red < 0.0 ||
- this.blending.color.red > 1.0 ||
- this.blending.color.green < 0.0 ||
- this.blending.color.green > 1.0 ||
- this.blending.color.blue < 0.0 ||
- this.blending.color.blue > 1.0 ||
- this.blending.color.alpha < 0.0 ||
- this.blending.color.alpha > 1.0
- ) {
- // Would be clamped by GL
- throw new DeveloperError(
- "renderState.blending.color components must be greater than or equal to zero and less than or equal to one."
- );
- }
- if (!validateBlendEquation(this.blending.equationRgb)) {
- throw new DeveloperError("Invalid renderState.blending.equationRgb.");
- }
- if (!validateBlendEquation(this.blending.equationAlpha)) {
- throw new DeveloperError("Invalid renderState.blending.equationAlpha.");
- }
- if (!validateBlendFunction(this.blending.functionSourceRgb)) {
- throw new DeveloperError("Invalid renderState.blending.functionSourceRgb.");
- }
- if (!validateBlendFunction(this.blending.functionSourceAlpha)) {
- throw new DeveloperError(
- "Invalid renderState.blending.functionSourceAlpha."
- );
- }
- if (!validateBlendFunction(this.blending.functionDestinationRgb)) {
- throw new DeveloperError(
- "Invalid renderState.blending.functionDestinationRgb."
- );
- }
- if (!validateBlendFunction(this.blending.functionDestinationAlpha)) {
- throw new DeveloperError(
- "Invalid renderState.blending.functionDestinationAlpha."
- );
- }
- if (!validateStencilFunction(this.stencilTest.frontFunction)) {
- throw new DeveloperError("Invalid renderState.stencilTest.frontFunction.");
- }
- if (!validateStencilFunction(this.stencilTest.backFunction)) {
- throw new DeveloperError("Invalid renderState.stencilTest.backFunction.");
- }
- if (!validateStencilOperation(this.stencilTest.frontOperation.fail)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.frontOperation.fail."
- );
- }
- if (!validateStencilOperation(this.stencilTest.frontOperation.zFail)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.frontOperation.zFail."
- );
- }
- if (!validateStencilOperation(this.stencilTest.frontOperation.zPass)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.frontOperation.zPass."
- );
- }
- if (!validateStencilOperation(this.stencilTest.backOperation.fail)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.backOperation.fail."
- );
- }
- if (!validateStencilOperation(this.stencilTest.backOperation.zFail)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.backOperation.zFail."
- );
- }
- if (!validateStencilOperation(this.stencilTest.backOperation.zPass)) {
- throw new DeveloperError(
- "Invalid renderState.stencilTest.backOperation.zPass."
- );
- }
- if (defined(this.viewport)) {
- if (this.viewport.width < 0) {
- throw new DeveloperError(
- "renderState.viewport.width must be greater than or equal to zero."
- );
- }
- if (this.viewport.height < 0) {
- throw new DeveloperError(
- "renderState.viewport.height must be greater than or equal to zero."
- );
- }
- if (this.viewport.width > ContextLimits.maximumViewportWidth) {
- throw new DeveloperError(
- `renderState.viewport.width must be less than or equal to the maximum viewport width (${ContextLimits.maximumViewportWidth.toString()}). Check maximumViewportWidth.`
- );
- }
- if (this.viewport.height > ContextLimits.maximumViewportHeight) {
- throw new DeveloperError(
- `renderState.viewport.height must be less than or equal to the maximum viewport height (${ContextLimits.maximumViewportHeight.toString()}). Check maximumViewportHeight.`
- );
- }
- }
- //>>includeEnd('debug');
- this.id = 0;
- this._applyFunctions = [];
- }
- let nextRenderStateId = 0;
- let renderStateCache = {};
- /**
- * Validates and then finds or creates an immutable render state, which defines the pipeline
- * state for a {@link DrawCommand} or {@link ClearCommand}. All inputs states are optional. Omitted states
- * use the defaults shown in the example below.
- *
- * @param {Object} [renderState] The states defining the render state as shown in the example below.
- *
- * @exception {RuntimeError} renderState.lineWidth is out of range.
- * @exception {DeveloperError} Invalid renderState.frontFace.
- * @exception {DeveloperError} Invalid renderState.cull.face.
- * @exception {DeveloperError} scissorTest.rectangle.width and scissorTest.rectangle.height must be greater than or equal to zero.
- * @exception {DeveloperError} renderState.depthRange.near can't be greater than renderState.depthRange.far.
- * @exception {DeveloperError} renderState.depthRange.near must be greater than or equal to zero.
- * @exception {DeveloperError} renderState.depthRange.far must be less than or equal to zero.
- * @exception {DeveloperError} Invalid renderState.depthTest.func.
- * @exception {DeveloperError} renderState.blending.color components must be greater than or equal to zero and less than or equal to one
- * @exception {DeveloperError} Invalid renderState.blending.equationRgb.
- * @exception {DeveloperError} Invalid renderState.blending.equationAlpha.
- * @exception {DeveloperError} Invalid renderState.blending.functionSourceRgb.
- * @exception {DeveloperError} Invalid renderState.blending.functionSourceAlpha.
- * @exception {DeveloperError} Invalid renderState.blending.functionDestinationRgb.
- * @exception {DeveloperError} Invalid renderState.blending.functionDestinationAlpha.
- * @exception {DeveloperError} Invalid renderState.stencilTest.frontFunction.
- * @exception {DeveloperError} Invalid renderState.stencilTest.backFunction.
- * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.fail.
- * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zFail.
- * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zPass.
- * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.fail.
- * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zFail.
- * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zPass.
- * @exception {DeveloperError} renderState.viewport.width must be greater than or equal to zero.
- * @exception {DeveloperError} renderState.viewport.width must be less than or equal to the maximum viewport width.
- * @exception {DeveloperError} renderState.viewport.height must be greater than or equal to zero.
- * @exception {DeveloperError} renderState.viewport.height must be less than or equal to the maximum viewport height.
- *
- *
- * @example
- * const defaults = {
- * frontFace : WindingOrder.COUNTER_CLOCKWISE,
- * cull : {
- * enabled : false,
- * face : CullFace.BACK
- * },
- * lineWidth : 1,
- * polygonOffset : {
- * enabled : false,
- * factor : 0,
- * units : 0
- * },
- * scissorTest : {
- * enabled : false,
- * rectangle : {
- * x : 0,
- * y : 0,
- * width : 0,
- * height : 0
- * }
- * },
- * depthRange : {
- * near : 0,
- * far : 1
- * },
- * depthTest : {
- * enabled : false,
- * func : DepthFunction.LESS
- * },
- * colorMask : {
- * red : true,
- * green : true,
- * blue : true,
- * alpha : true
- * },
- * depthMask : true,
- * stencilMask : ~0,
- * blending : {
- * enabled : false,
- * color : {
- * red : 0.0,
- * green : 0.0,
- * blue : 0.0,
- * alpha : 0.0
- * },
- * equationRgb : BlendEquation.ADD,
- * equationAlpha : BlendEquation.ADD,
- * functionSourceRgb : BlendFunction.ONE,
- * functionSourceAlpha : BlendFunction.ONE,
- * functionDestinationRgb : BlendFunction.ZERO,
- * functionDestinationAlpha : BlendFunction.ZERO
- * },
- * stencilTest : {
- * enabled : false,
- * frontFunction : StencilFunction.ALWAYS,
- * backFunction : StencilFunction.ALWAYS,
- * reference : 0,
- * mask : ~0,
- * frontOperation : {
- * fail : StencilOperation.KEEP,
- * zFail : StencilOperation.KEEP,
- * zPass : StencilOperation.KEEP
- * },
- * backOperation : {
- * fail : StencilOperation.KEEP,
- * zFail : StencilOperation.KEEP,
- * zPass : StencilOperation.KEEP
- * }
- * },
- * sampleCoverage : {
- * enabled : false,
- * value : 1.0,
- * invert : false
- * }
- * };
- *
- * const rs = RenderState.fromCache(defaults);
- *
- * @see DrawCommand
- * @see ClearCommand
- *
- * @private
- */
- RenderState.fromCache = function (renderState) {
- const partialKey = JSON.stringify(renderState);
- let cachedState = renderStateCache[partialKey];
- if (defined(cachedState)) {
- ++cachedState.referenceCount;
- return cachedState.state;
- }
- // Cache miss. Fully define render state and try again.
- let states = new RenderState(renderState);
- const fullKey = JSON.stringify(states);
- cachedState = renderStateCache[fullKey];
- if (!defined(cachedState)) {
- states.id = nextRenderStateId++;
- //>>includeStart('debug', pragmas.debug);
- states = freezeRenderState(states);
- //>>includeEnd('debug');
- cachedState = {
- referenceCount: 0,
- state: states,
- };
- // Cache full render state. Multiple partially defined render states may map to this.
- renderStateCache[fullKey] = cachedState;
- }
- ++cachedState.referenceCount;
- // Cache partial render state so we can skip validation on a cache hit for a partially defined render state
- renderStateCache[partialKey] = {
- referenceCount: 1,
- state: cachedState.state,
- };
- return cachedState.state;
- };
- /**
- * @private
- */
- RenderState.removeFromCache = function (renderState) {
- const states = new RenderState(renderState);
- const fullKey = JSON.stringify(states);
- const fullCachedState = renderStateCache[fullKey];
- // decrement partial key reference count
- const partialKey = JSON.stringify(renderState);
- const cachedState = renderStateCache[partialKey];
- if (defined(cachedState)) {
- --cachedState.referenceCount;
- if (cachedState.referenceCount === 0) {
- // remove partial key
- delete renderStateCache[partialKey];
- // decrement full key reference count
- if (defined(fullCachedState)) {
- --fullCachedState.referenceCount;
- }
- }
- }
- // remove full key if reference count is zero
- if (defined(fullCachedState) && fullCachedState.referenceCount === 0) {
- delete renderStateCache[fullKey];
- }
- };
- /**
- * This function is for testing purposes only.
- * @private
- */
- RenderState.getCache = function () {
- return renderStateCache;
- };
- /**
- * This function is for testing purposes only.
- * @private
- */
- RenderState.clearCache = function () {
- renderStateCache = {};
- };
- function enableOrDisable(gl, glEnum, enable) {
- if (enable) {
- gl.enable(glEnum);
- } else {
- gl.disable(glEnum);
- }
- }
- function applyFrontFace(gl, renderState) {
- gl.frontFace(renderState.frontFace);
- }
- function applyCull(gl, renderState) {
- const cull = renderState.cull;
- const enabled = cull.enabled;
- enableOrDisable(gl, gl.CULL_FACE, enabled);
- if (enabled) {
- gl.cullFace(cull.face);
- }
- }
- function applyLineWidth(gl, renderState) {
- gl.lineWidth(renderState.lineWidth);
- }
- function applyPolygonOffset(gl, renderState) {
- const polygonOffset = renderState.polygonOffset;
- const enabled = polygonOffset.enabled;
- enableOrDisable(gl, gl.POLYGON_OFFSET_FILL, enabled);
- if (enabled) {
- gl.polygonOffset(polygonOffset.factor, polygonOffset.units);
- }
- }
- function applyScissorTest(gl, renderState, passState) {
- const scissorTest = renderState.scissorTest;
- const enabled = defined(passState.scissorTest)
- ? passState.scissorTest.enabled
- : scissorTest.enabled;
- enableOrDisable(gl, gl.SCISSOR_TEST, enabled);
- if (enabled) {
- const rectangle = defined(passState.scissorTest)
- ? passState.scissorTest.rectangle
- : scissorTest.rectangle;
- gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
- }
- }
- function applyDepthRange(gl, renderState) {
- const depthRange = renderState.depthRange;
- gl.depthRange(depthRange.near, depthRange.far);
- }
- function applyDepthTest(gl, renderState) {
- const depthTest = renderState.depthTest;
- const enabled = depthTest.enabled;
- enableOrDisable(gl, gl.DEPTH_TEST, enabled);
- if (enabled) {
- gl.depthFunc(depthTest.func);
- }
- }
- function applyColorMask(gl, renderState) {
- const colorMask = renderState.colorMask;
- gl.colorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
- }
- function applyDepthMask(gl, renderState) {
- gl.depthMask(renderState.depthMask);
- }
- function applyStencilMask(gl, renderState) {
- gl.stencilMask(renderState.stencilMask);
- }
- function applyBlendingColor(gl, color) {
- gl.blendColor(color.red, color.green, color.blue, color.alpha);
- }
- function applyBlending(gl, renderState, passState) {
- const blending = renderState.blending;
- const enabled = defined(passState.blendingEnabled)
- ? passState.blendingEnabled
- : blending.enabled;
- enableOrDisable(gl, gl.BLEND, enabled);
- if (enabled) {
- applyBlendingColor(gl, blending.color);
- gl.blendEquationSeparate(blending.equationRgb, blending.equationAlpha);
- gl.blendFuncSeparate(
- blending.functionSourceRgb,
- blending.functionDestinationRgb,
- blending.functionSourceAlpha,
- blending.functionDestinationAlpha
- );
- }
- }
- function applyStencilTest(gl, renderState) {
- const stencilTest = renderState.stencilTest;
- const enabled = stencilTest.enabled;
- enableOrDisable(gl, gl.STENCIL_TEST, enabled);
- if (enabled) {
- const frontFunction = stencilTest.frontFunction;
- const backFunction = stencilTest.backFunction;
- const reference = stencilTest.reference;
- const mask = stencilTest.mask;
- // Section 6.8 of the WebGL spec requires the reference and masks to be the same for
- // front- and back-face tests. This call prevents invalid operation errors when calling
- // stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this.
- gl.stencilFunc(frontFunction, reference, mask);
- gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask);
- gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask);
- const frontOperation = stencilTest.frontOperation;
- const frontOperationFail = frontOperation.fail;
- const frontOperationZFail = frontOperation.zFail;
- const frontOperationZPass = frontOperation.zPass;
- gl.stencilOpSeparate(
- gl.FRONT,
- frontOperationFail,
- frontOperationZFail,
- frontOperationZPass
- );
- const backOperation = stencilTest.backOperation;
- const backOperationFail = backOperation.fail;
- const backOperationZFail = backOperation.zFail;
- const backOperationZPass = backOperation.zPass;
- gl.stencilOpSeparate(
- gl.BACK,
- backOperationFail,
- backOperationZFail,
- backOperationZPass
- );
- }
- }
- function applySampleCoverage(gl, renderState) {
- const sampleCoverage = renderState.sampleCoverage;
- const enabled = sampleCoverage.enabled;
- enableOrDisable(gl, gl.SAMPLE_COVERAGE, enabled);
- if (enabled) {
- gl.sampleCoverage(sampleCoverage.value, sampleCoverage.invert);
- }
- }
- const scratchViewport = new BoundingRectangle();
- function applyViewport(gl, renderState, passState) {
- let viewport = defaultValue(renderState.viewport, passState.viewport);
- if (!defined(viewport)) {
- viewport = scratchViewport;
- viewport.width = passState.context.drawingBufferWidth;
- viewport.height = passState.context.drawingBufferHeight;
- }
- passState.context.uniformState.viewport = viewport;
- gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
- }
- RenderState.apply = function (gl, renderState, passState) {
- applyFrontFace(gl, renderState);
- applyCull(gl, renderState);
- applyLineWidth(gl, renderState);
- applyPolygonOffset(gl, renderState);
- applyDepthRange(gl, renderState);
- applyDepthTest(gl, renderState);
- applyColorMask(gl, renderState);
- applyDepthMask(gl, renderState);
- applyStencilMask(gl, renderState);
- applyStencilTest(gl, renderState);
- applySampleCoverage(gl, renderState);
- applyScissorTest(gl, renderState, passState);
- applyBlending(gl, renderState, passState);
- applyViewport(gl, renderState, passState);
- };
- function createFuncs(previousState, nextState) {
- const funcs = [];
- if (previousState.frontFace !== nextState.frontFace) {
- funcs.push(applyFrontFace);
- }
- if (
- previousState.cull.enabled !== nextState.cull.enabled ||
- previousState.cull.face !== nextState.cull.face
- ) {
- funcs.push(applyCull);
- }
- if (previousState.lineWidth !== nextState.lineWidth) {
- funcs.push(applyLineWidth);
- }
- if (
- previousState.polygonOffset.enabled !== nextState.polygonOffset.enabled ||
- previousState.polygonOffset.factor !== nextState.polygonOffset.factor ||
- previousState.polygonOffset.units !== nextState.polygonOffset.units
- ) {
- funcs.push(applyPolygonOffset);
- }
- if (
- previousState.depthRange.near !== nextState.depthRange.near ||
- previousState.depthRange.far !== nextState.depthRange.far
- ) {
- funcs.push(applyDepthRange);
- }
- if (
- previousState.depthTest.enabled !== nextState.depthTest.enabled ||
- previousState.depthTest.func !== nextState.depthTest.func
- ) {
- funcs.push(applyDepthTest);
- }
- if (
- previousState.colorMask.red !== nextState.colorMask.red ||
- previousState.colorMask.green !== nextState.colorMask.green ||
- previousState.colorMask.blue !== nextState.colorMask.blue ||
- previousState.colorMask.alpha !== nextState.colorMask.alpha
- ) {
- funcs.push(applyColorMask);
- }
- if (previousState.depthMask !== nextState.depthMask) {
- funcs.push(applyDepthMask);
- }
- if (previousState.stencilMask !== nextState.stencilMask) {
- funcs.push(applyStencilMask);
- }
- if (
- previousState.stencilTest.enabled !== nextState.stencilTest.enabled ||
- previousState.stencilTest.frontFunction !==
- nextState.stencilTest.frontFunction ||
- previousState.stencilTest.backFunction !==
- nextState.stencilTest.backFunction ||
- previousState.stencilTest.reference !== nextState.stencilTest.reference ||
- previousState.stencilTest.mask !== nextState.stencilTest.mask ||
- previousState.stencilTest.frontOperation.fail !==
- nextState.stencilTest.frontOperation.fail ||
- previousState.stencilTest.frontOperation.zFail !==
- nextState.stencilTest.frontOperation.zFail ||
- previousState.stencilTest.backOperation.fail !==
- nextState.stencilTest.backOperation.fail ||
- previousState.stencilTest.backOperation.zFail !==
- nextState.stencilTest.backOperation.zFail ||
- previousState.stencilTest.backOperation.zPass !==
- nextState.stencilTest.backOperation.zPass
- ) {
- funcs.push(applyStencilTest);
- }
- if (
- previousState.sampleCoverage.enabled !== nextState.sampleCoverage.enabled ||
- previousState.sampleCoverage.value !== nextState.sampleCoverage.value ||
- previousState.sampleCoverage.invert !== nextState.sampleCoverage.invert
- ) {
- funcs.push(applySampleCoverage);
- }
- return funcs;
- }
- RenderState.partialApply = function (
- gl,
- previousRenderState,
- renderState,
- previousPassState,
- passState,
- clear
- ) {
- if (previousRenderState !== renderState) {
- // When a new render state is applied, instead of making WebGL calls for all the states or first
- // comparing the states one-by-one with the previous state (basically a linear search), we take
- // advantage of RenderState's immutability, and store a dynamically populated sparse data structure
- // containing functions that make the minimum number of WebGL calls when transitioning from one state
- // to the other. In practice, this works well since state-to-state transitions generally only require a
- // few WebGL calls, especially if commands are stored by state.
- let funcs = renderState._applyFunctions[previousRenderState.id];
- if (!defined(funcs)) {
- funcs = createFuncs(previousRenderState, renderState);
- renderState._applyFunctions[previousRenderState.id] = funcs;
- }
- const len = funcs.length;
- for (let i = 0; i < len; ++i) {
- funcs[i](gl, renderState);
- }
- }
- const previousScissorTest = defined(previousPassState.scissorTest)
- ? previousPassState.scissorTest
- : previousRenderState.scissorTest;
- const scissorTest = defined(passState.scissorTest)
- ? passState.scissorTest
- : renderState.scissorTest;
- // Our scissor rectangle can get out of sync with the GL scissor rectangle on clears.
- // Seems to be a problem only on ANGLE. See https://github.com/CesiumGS/cesium/issues/2994
- if (previousScissorTest !== scissorTest || clear) {
- applyScissorTest(gl, renderState, passState);
- }
- const previousBlendingEnabled = defined(previousPassState.blendingEnabled)
- ? previousPassState.blendingEnabled
- : previousRenderState.blending.enabled;
- const blendingEnabled = defined(passState.blendingEnabled)
- ? passState.blendingEnabled
- : renderState.blending.enabled;
- if (
- previousBlendingEnabled !== blendingEnabled ||
- (blendingEnabled && previousRenderState.blending !== renderState.blending)
- ) {
- applyBlending(gl, renderState, passState);
- }
- if (
- previousRenderState !== renderState ||
- previousPassState !== passState ||
- previousPassState.context !== passState.context
- ) {
- applyViewport(gl, renderState, passState);
- }
- };
- RenderState.getState = function (renderState) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(renderState)) {
- throw new DeveloperError("renderState is required.");
- }
- //>>includeEnd('debug');
- return {
- frontFace: renderState.frontFace,
- cull: {
- enabled: renderState.cull.enabled,
- face: renderState.cull.face,
- },
- lineWidth: renderState.lineWidth,
- polygonOffset: {
- enabled: renderState.polygonOffset.enabled,
- factor: renderState.polygonOffset.factor,
- units: renderState.polygonOffset.units,
- },
- scissorTest: {
- enabled: renderState.scissorTest.enabled,
- rectangle: BoundingRectangle.clone(renderState.scissorTest.rectangle),
- },
- depthRange: {
- near: renderState.depthRange.near,
- far: renderState.depthRange.far,
- },
- depthTest: {
- enabled: renderState.depthTest.enabled,
- func: renderState.depthTest.func,
- },
- colorMask: {
- red: renderState.colorMask.red,
- green: renderState.colorMask.green,
- blue: renderState.colorMask.blue,
- alpha: renderState.colorMask.alpha,
- },
- depthMask: renderState.depthMask,
- stencilMask: renderState.stencilMask,
- blending: {
- enabled: renderState.blending.enabled,
- color: Color.clone(renderState.blending.color),
- equationRgb: renderState.blending.equationRgb,
- equationAlpha: renderState.blending.equationAlpha,
- functionSourceRgb: renderState.blending.functionSourceRgb,
- functionSourceAlpha: renderState.blending.functionSourceAlpha,
- functionDestinationRgb: renderState.blending.functionDestinationRgb,
- functionDestinationAlpha: renderState.blending.functionDestinationAlpha,
- },
- stencilTest: {
- enabled: renderState.stencilTest.enabled,
- frontFunction: renderState.stencilTest.frontFunction,
- backFunction: renderState.stencilTest.backFunction,
- reference: renderState.stencilTest.reference,
- mask: renderState.stencilTest.mask,
- frontOperation: {
- fail: renderState.stencilTest.frontOperation.fail,
- zFail: renderState.stencilTest.frontOperation.zFail,
- zPass: renderState.stencilTest.frontOperation.zPass,
- },
- backOperation: {
- fail: renderState.stencilTest.backOperation.fail,
- zFail: renderState.stencilTest.backOperation.zFail,
- zPass: renderState.stencilTest.backOperation.zPass,
- },
- },
- sampleCoverage: {
- enabled: renderState.sampleCoverage.enabled,
- value: renderState.sampleCoverage.value,
- invert: renderState.sampleCoverage.invert,
- },
- viewport: defined(renderState.viewport)
- ? BoundingRectangle.clone(renderState.viewport)
- : undefined,
- };
- };
- export default RenderState;
|