Skip to content

Commit

Permalink
Optimized the key generation for StencilParameters (#5491)
Browse files Browse the repository at this point in the history
* Optimized the key generation for StencilParameters

* Update src/platform/graphics/stencil-parameters.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Co-authored-by: Will Eastcott <will@playcanvas.com>
  • Loading branch information
3 people committed Jul 20, 2023
1 parent 90f4533 commit ddc0fda
Showing 1 changed file with 114 additions and 28 deletions.
142 changes: 114 additions & 28 deletions src/platform/graphics/stencil-parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ const stringIds = new StringIds();
* Holds stencil test settings.
*/
class StencilParameters {
/** @type {number} */
_func;

/** @type {number} */
_ref;

/** @type {number} */
_fail;

/** @type {number} */
_zfail;

/** @type {number} */
_zpass;

/** @type {number} */
_readMask;

/** @type {number} */
_writeMask;

/** @type {boolean} */
_dirty = true;

/** @type {number} */
_key;

/**
* A comparison function that decides if the pixel should be written, based on the current
* stencil buffer value, reference value, and mask value. Can be:
Expand All @@ -22,14 +49,28 @@ class StencilParameters {
*
* @type {number}
*/
func;
set func(value) {
this._func = value;
this._dirty = true;
}

get func() {
return this._func;
}

/**
* Sets stencil test reference value used in comparisons.
*
* @type {number}
*/
ref;
set ref(value) {
this._ref = value;
this._dirty = true;
}

get ref() {
return this._ref;
}

/**
* Operation to perform if stencil test is failed. Can be:
Expand All @@ -47,61 +88,104 @@ class StencilParameters {
*
* @type {number}
*/
fail;
set fail(value) {
this._fail = value;
this._dirty = true;
}

get fail() {
return this._fail;
}

/**
* Operation to perform if depth test is failed. Accepts the same values as `fail`.
*
* @type {number}
*/
zfail;
set zfail(value) {
this._zfail = value;
this._dirty = true;
}

get zfail() {
return this._zfail;
}

/**
* Operation to perform if both stencil and depth test are passed. Accepts the same values as
* `fail`.
*
* @type {number}
*/
zpass;
set zpass(value) {
this._zpass = value;
this._dirty = true;
}

get zpass() {
return this._zpass;
}

/**
* Mask applied to stencil buffer value and reference value before comparison.
*
* @type {number}
*/
readMask;
set readMask(value) {
this._readMask = value;
this._dirty = true;
}

get readMask() {
return this._readMask;
}

/**
* A bit mask applied to the stencil value, when written.
*
* @type {number}
*/
writeMask;
set writeMask(value) {
this._writeMask = value;
this._dirty = true;
}

get writeMask() {
return this._writeMask;
}

/**
* Create a new StencilParameters instance.
*
* @param {object} [options] - Options object to configure the stencil parameters.
*/
constructor(options = {}) {
this.func = options.func ?? FUNC_ALWAYS;
this.ref = options.ref ?? 0;
this.readMask = options.readMask ?? 0xFF;
this.writeMask = options.writeMask ?? 0xFF;
this._func = options.func ?? FUNC_ALWAYS;
this._ref = options.ref ?? 0;
this._readMask = options.readMask ?? 0xFF;
this._writeMask = options.writeMask ?? 0xFF;

this._fail = options.fail ?? STENCILOP_KEEP; // keep == 0
this._zfail = options.zfail ?? STENCILOP_KEEP;
this._zpass = options.zpass ?? STENCILOP_KEEP;

// Evaluate key here. This evaluates the key for the DEFAULT instance, which is important,
// as during rendering it gets copied and the key would get evaluated each time.
this._evalKey();
}

this.fail = options.fail ?? STENCILOP_KEEP; // keep == 0
this.zfail = options.zfail ?? STENCILOP_KEEP;
this.zpass = options.zpass ?? STENCILOP_KEEP;
_evalKey() {
const { _func, _ref, _fail, _zfail, _zpass, _readMask, _writeMask } = this;
const key = `${_func},${_ref},${_fail},${_zfail},${_zpass},${_readMask},${_writeMask}`;
this._key = stringIds.get(key);
this._dirty = false;
}

// TODO: we could store the key as a property and only update it when the parameters change,
// by using a dirty flag. But considering stencil is used rarely, this can be done at a later
// stage. This function is only called when the stencil state is enabled. We could also use
// BitField to store the parameters and to speed up the key generation.
get key() {
const { func, ref, fail, zfail, zpass, readMask, writeMask } = this;
const key = `${func},${ref},${fail},${zfail},${zpass},${readMask},${writeMask}`;
return stringIds.get(key);
if (this._dirty) {
this._evalKey();
}
return this._key;
}

/**
Expand All @@ -111,13 +195,15 @@ class StencilParameters {
* @returns {StencilParameters} Self for chaining.
*/
copy(rhs) {
this.func = rhs.func;
this.ref = rhs.ref;
this.readMask = rhs.readMask;
this.writeMask = rhs.writeMask;
this.fail = rhs.fail;
this.zfail = rhs.zfail;
this.zpass = rhs.zpass;
this._func = rhs._func;
this._ref = rhs._ref;
this._readMask = rhs._readMask;
this._writeMask = rhs._writeMask;
this._fail = rhs._fail;
this._zfail = rhs._zfail;
this._zpass = rhs._zpass;
this._dirty = rhs._dirty;
this._key = rhs._key;
return this;
}

Expand Down

0 comments on commit ddc0fda

Please sign in to comment.