Add existing to tracked
This commit is contained in:
+967
@@ -0,0 +1,967 @@
|
||||
import BoundingRectangle from "../Core/BoundingRectangle.js";
|
||||
import Color from "../Core/Color.js";
|
||||
import Frozen from "../Core/Frozen.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 = renderState ?? Frozen.EMPTY_OBJECT;
|
||||
const cull = rs.cull ?? Frozen.EMPTY_OBJECT;
|
||||
const polygonOffset = rs.polygonOffset ?? Frozen.EMPTY_OBJECT;
|
||||
const scissorTest = rs.scissorTest ?? Frozen.EMPTY_OBJECT;
|
||||
const scissorTestRectangle = scissorTest.rectangle ?? Frozen.EMPTY_OBJECT;
|
||||
const depthRange = rs.depthRange ?? Frozen.EMPTY_OBJECT;
|
||||
const depthTest = rs.depthTest ?? Frozen.EMPTY_OBJECT;
|
||||
const colorMask = rs.colorMask ?? Frozen.EMPTY_OBJECT;
|
||||
const blending = rs.blending ?? Frozen.EMPTY_OBJECT;
|
||||
const blendingColor = blending.color ?? Frozen.EMPTY_OBJECT;
|
||||
const stencilTest = rs.stencilTest ?? Frozen.EMPTY_OBJECT;
|
||||
const stencilTestFrontOperation =
|
||||
stencilTest.frontOperation ?? Frozen.EMPTY_OBJECT;
|
||||
const stencilTestBackOperation =
|
||||
stencilTest.backOperation ?? Frozen.EMPTY_OBJECT;
|
||||
const sampleCoverage = rs.sampleCoverage ?? Frozen.EMPTY_OBJECT;
|
||||
const viewport = rs.viewport;
|
||||
|
||||
this.frontFace = rs.frontFace ?? WindingOrder.COUNTER_CLOCKWISE;
|
||||
this.cull = {
|
||||
enabled: cull.enabled ?? false,
|
||||
face: cull.face ?? WebGLConstants.BACK,
|
||||
};
|
||||
this.lineWidth = rs.lineWidth ?? 1.0;
|
||||
this.polygonOffset = {
|
||||
enabled: polygonOffset.enabled ?? false,
|
||||
factor: polygonOffset.factor ?? 0,
|
||||
units: polygonOffset.units ?? 0,
|
||||
};
|
||||
this.scissorTest = {
|
||||
enabled: scissorTest.enabled ?? false,
|
||||
rectangle: BoundingRectangle.clone(scissorTestRectangle),
|
||||
};
|
||||
this.depthRange = {
|
||||
near: depthRange.near ?? 0,
|
||||
far: depthRange.far ?? 1,
|
||||
};
|
||||
this.depthTest = {
|
||||
enabled: depthTest.enabled ?? false,
|
||||
func: depthTest.func ?? WebGLConstants.LESS, // func, because function is a JavaScript keyword
|
||||
};
|
||||
this.colorMask = {
|
||||
red: colorMask.red ?? true,
|
||||
green: colorMask.green ?? true,
|
||||
blue: colorMask.blue ?? true,
|
||||
alpha: colorMask.alpha ?? true,
|
||||
};
|
||||
this.depthMask = rs.depthMask ?? true;
|
||||
this.stencilMask = rs.stencilMask ?? ~0;
|
||||
this.blending = {
|
||||
enabled: blending.enabled ?? false,
|
||||
color: new Color(
|
||||
blendingColor.red ?? 0.0,
|
||||
blendingColor.green ?? 0.0,
|
||||
blendingColor.blue ?? 0.0,
|
||||
blendingColor.alpha ?? 0.0,
|
||||
),
|
||||
equationRgb: blending.equationRgb ?? WebGLConstants.FUNC_ADD,
|
||||
equationAlpha: blending.equationAlpha ?? WebGLConstants.FUNC_ADD,
|
||||
functionSourceRgb: blending.functionSourceRgb ?? WebGLConstants.ONE,
|
||||
functionSourceAlpha: blending.functionSourceAlpha ?? WebGLConstants.ONE,
|
||||
functionDestinationRgb:
|
||||
blending.functionDestinationRgb ?? WebGLConstants.ZERO,
|
||||
functionDestinationAlpha:
|
||||
blending.functionDestinationAlpha ?? WebGLConstants.ZERO,
|
||||
};
|
||||
this.stencilTest = {
|
||||
enabled: stencilTest.enabled ?? false,
|
||||
frontFunction: stencilTest.frontFunction ?? WebGLConstants.ALWAYS,
|
||||
backFunction: stencilTest.backFunction ?? WebGLConstants.ALWAYS,
|
||||
reference: stencilTest.reference ?? 0,
|
||||
mask: stencilTest.mask ?? ~0,
|
||||
frontOperation: {
|
||||
fail: stencilTestFrontOperation.fail ?? WebGLConstants.KEEP,
|
||||
zFail: stencilTestFrontOperation.zFail ?? WebGLConstants.KEEP,
|
||||
zPass: stencilTestFrontOperation.zPass ?? WebGLConstants.KEEP,
|
||||
},
|
||||
backOperation: {
|
||||
fail: stencilTestBackOperation.fail ?? WebGLConstants.KEEP,
|
||||
zFail: stencilTestBackOperation.zFail ?? WebGLConstants.KEEP,
|
||||
zPass: stencilTestBackOperation.zPass ?? WebGLConstants.KEEP,
|
||||
},
|
||||
};
|
||||
this.sampleCoverage = {
|
||||
enabled: sampleCoverage.enabled ?? false,
|
||||
value: sampleCoverage.value ?? 1.0,
|
||||
invert: 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
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
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 = 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;
|
||||
Reference in New Issue
Block a user