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