Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized the key generation for StencilParameters #5491

Merged
merged 2 commits into from
Jul 20, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 they key would get evaluated each time.
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved
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