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

Enhancement limit WebGL textures to gl.MAX_TEXTURE_SIZE #6651

Merged
merged 11 commits into from
Mar 19, 2024

Conversation

capGoblin
Copy link
Contributor

@capGoblin capGoblin commented Dec 24, 2023

Resolves #6546

Changes:

For both Graphics and Framebuffer

  • Calculated the maximum allowed pixel dimensions using the hardware limit and pixel density.
  • Limited the dimensions to stay within these calculated maximum dimensions.

Screenshots of the change:

PR Checklist

@capGoblin capGoblin changed the title Enhancement/limit textures Enhancement limit WebGL textures to gl.MAX_TEXTURE_SIZE Dec 24, 2023
@capGoblin
Copy link
Contributor Author

Hi @davepagurek,

Here's the problem:

  • Let's say both the w and h given by the users are above the calculated maxAllowedPixelDimensions(let's say 8192), in this case, both the w and h are limited to maxAllowedPixelDimensions(8192), which works well for Graphics.
  • But for Framebuffer restricting both w and h to maxAllowedPixelDimensions(8192) results in a CONTEXT_LOST_WEBGL error.
    Oddly, the error doesn't occur when only one dimension is limited to 8192 and the other is kept below 4000.

Is there something I might have overlooked or misunderstood in this context? Any insights on what might be causing this behaviour?

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oddly, the error doesn't occur when only one dimension is limited to 8192 and the other is kept below 4000.

I think this SO answer's explanation also applies here: basically, gl.MAX_TEXTURE_SIZE is definitely the max that can be supported in any dimension, but it doesn't make any guarantees that you have enough memory to store MAX x MAX textures, or even if you can store one, that you can store a second, if you choose to make it.

Do you run into issues with framebuffers only when the main canvas is also really big? Or do they work if you have a tiny main canvas but a big framebuffer? If it's the latter, it sounds like maybe it's running into a memory limit.

In any case, I think we won't be able to prevent all context losses when trying to make a large texture, but I think the checks being done here are a helpful start!

maxTextureSize / this._pixelDensity
);
w = Math.min(w, maxAllowedPixelDimensions);
h = Math.min(h, maxAllowedPixelDimensions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to log a warning here (and in p5.Framebuffer.js) if the width/height we end up picking is different than what the user requested.

const gl = this._renderer.GL;
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
const maxAllowedPixelDimensions = Math.floor(
maxTextureSize / this._pixelDensity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, this depends on the density. I think this means that we may need to do a similar check when the user sets pixelDensity() too for complete coverage

@@ -60,6 +60,13 @@ p5.Graphics = class extends p5.Element {

if (r === constants.WEBGL) {
this._renderer = new p5.RendererGL(this.canvas, this, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want this for both graphics and the main canvas, so maybe we should do the check inside RendererGL instead?

@@ -60,6 +60,13 @@ p5.Graphics = class extends p5.Element {

if (r === constants.WEBGL) {
this._renderer = new p5.RendererGL(this.canvas, this, false);
const gl = this._renderer.GL;
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This theoretically shouldn't change over time, so maybe we can add a method to RendererGL to be something like this:

maxTextureSize() {
  if (!this._maxTextureSize) {
    this._maxTextureSize = this.GL.getParameter(this.GL.MAX_TEXTURE_SIZE);
  }
  return this._maxTextureSize;
}

getParameter calls are generally a bit slow, so we want to cache their results (or avoid calling them if there's a convenient way to do so.)

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the changes so far! I think the remaining changes would be:

  • can we also handle this in resizeCanvas (for the main canvas and p5.Graphics) and resize(forp5.Framebuffer`)?
  • can we add some unit tests for this? You can use sinon.stub(object, "method", func); to replace a system function with another function for easier testing (e.g. used here). Maybe you could use that to mock a specific max texture size (even like 100, just for testing) and make assertions on other things working accordingly

@capGoblin
Copy link
Contributor Author

capGoblin commented Jan 16, 2024

Hi @davepagurek,

I have spent significant time in debugging this. Strangely, I encountered a completely different error when running npm test, and after pushing the code, yet another error emerged.
Could you please assist me by suggesting any potential areas that might be causing this error? Also, let me know if the tests I wrote looks good to you

@capGoblin capGoblin requested a review from davepagurek January 16, 2024 17:26
myp5.createCanvas(10, 10, myp5.WEBGL);
myp5.resizeCanvas(200, 200);
assert.equal(myp5.canvas.width, 100);
assert.equal(myp5.canvas.height, 100);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think myp5.width and myp5.height should also be 100 here? I think currently they aren't because we set this.width and this.height before adjusting the sizes, but I'm not sure just yet what the expected result would be. I think I lean towards having those values also be 100 for consistency, what do you think?

@davepagurek
Copy link
Contributor

Looks like some of the tests are failing. It might be that in the test environment on CI, the max texture size is something unexpected. Does the same thing happen when you run npm run ci locally? That might help us figure out if it's something about github's test runner, or something else.

@capGoblin
Copy link
Contributor Author

Sorry forgot to add comment during the previous commit, after updating this.height and this.width to adjusted sizes, by that time I was using glStub.restore() inside afterEach(), and I did npm run test all tests passed.
Since we are only using glStub in a couple of tests made sense to use glStub.restore() inside the tests itself. So, pushed the code after this change. And the tests failed.

Now, this commit uses glStub.restore() inside afterEach(), tests passed. But why does this happen?

P.S. I just tried glStub.restore() inside the test("it resizes the graphics based on max texture size") in p5.Graphics,
I usually get this (often),

image

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm honestly not totally sure why doing it per-test doesn't work but doing it in an afterEach does. I'm glad you were able to find a fix though!

I think this is good to go now! Since we've started our release process with a beta build, I'm going to hold off on merging this until after we finish the release so that we don't accidentally introduce new bugs during the testing period. I'll merge this right after that though!

@davepagurek davepagurek mentioned this pull request Feb 22, 2024
3 tasks
@Qianqianye Qianqianye merged commit 7ba5dc2 into processing:main Mar 19, 2024
2 checks passed
@Qianqianye
Copy link
Contributor

Looks great! Thanks @capGoblin and @davepagurek!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Limit WebGL textures to gl.MAX_TEXTURE_SIZE
3 participants