playcanvas / playcanvas/engine
texture.lock({ level: 0 }): only upload a single mip level if locked explicitly
@mvaligursky is already working on this.
Since Feb 26, 2024.
- Dominant language
- JavaScript
- Stars
- 16.8k
- Forks
- 2k
- Avg merge
- 4h 32m
- Merged PRs (30d)
- 222
Description
I also have some questions about future directions texture.lock() API, which I would be happy to help implement.
I'm asking these in the context of #6003 - I kept that PR deliberately small, but can open a followup PR depending on your thoughts.
Apologies in advance for length!
Questions for PlayCanvas team
Question 1
As mentioned in #5754, it would be good to be able to:
- Lock an individual slice of a 2D Texture Array
- Lock a specific mip level, and:
- Only upload the explicitly locked mip level instead of all of them
I'm excited to contribute an implementation for 1), but first I would like to clarify some issues around 2) and 3).
On 3): it looks to me like we always re-upload all mip levels regardless of whether we locked a specific one.
Q1: Is this correct, or have I missed something important in webgl/webgpu-texture.js?
Question 2
We could check texture._lockedLevel and use it to only upload the specified level when a level option was passed to texture.lock().
For example in webgl-texture.js:
// Upload all existing mip levels. Initialize 0 mip anyway.
while (texture._levels[mipLevel] || mipLevel === 0) {
+ // If we have locked a single mip level, only upload that mip
+ if (texture.lockedLevel !== -1 && mipLevel !== texture.lockedLevel) {
+ mipLevel++;
+ continue;
+ }
Q2: Does this seem like a good approach? Or should we be checking / updating texture._levelsUpdated instead?
Question 3
- Currently,
texture.lockedLeveldefaults to0when noleveloption is passed. - This is because
_lockedLevel !== -1is overloaded to indicate whether a texture is locked. - With #6003, we can use
_lockedModefor this instead, and leave_lockedLevelat-1if no explicit level was specified.
The default API would have the same behavior as now:
const data = texture.lock(); // Returns mip level 0 data
/** Internally, _lockedLevel remains -1, because user didn't set it explicitly */
texture.unlock(); // Uploads all mipmaps
But when the user specifies mip level 0 explicitly:
const data = texture.lock({ level: 0 }); // Returns mip level 0 data
/** Internally, _lockedLevel is now 0, because user set it explicitly */
texture.unlock(); // Uploads only mip level 0
So for most cases of lock(), the behavior is the same, but it could break expectations in code that is passing { level: 0 } and expecting all mips to update.
Additionally, anyone relying on the private texture._lockedLevel would not longer see it set to 0 when a texture is locked in the default way. But they could switch to the public texture.lockMode instead to check the locked state.
Q3: Is this level of API/behavior change acceptable?
Thank you, and let me know if there is a better venue for implementation questions like this!
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.