Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/webgl/p5.RendererGL.Immediate.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,23 @@ p5.RendererGL.prototype._processVertices = function(mode) {
this.immediateMode.shapeMode !== constants.LINES;

if (shouldTess) {
// libtess can't handle >65k vertices and will freeze the browser
const vertexCount = this.immediateMode.geometry.vertices.length;
const MAX_SAFE_TESSELATION_VERTICES = 50000;

if (vertexCount > MAX_SAFE_TESSELATION_VERTICES) {
p5._friendlyError(
'p5.js WebGL: Tessellation warning',
`Attempting to tessellate a shape with ${vertexCount} vertices. ` +
`Tessellation of shapes with more than ${MAX_SAFE_TESSELATION_VERTICES} vertices ` +
'may cause the browser to freeze. Consider reducing the number of vertices ' +
'or using a different shape mode (e.g., TRIANGLES, TRIANGLE_STRIP) instead of TESS.'
);
// skip tessellation to prevent freeze, use TRIANGLE_FAN as fallback
this.immediateMode.shapeMode = constants.TRIANGLE_FAN;
return;
}

this._tesselateShape();
}
};
Expand Down
67 changes: 67 additions & 0 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,73 @@ suite('p5.RendererGL', function() {
done();
});

test('TESS mode warns and prevents freeze for >50k vertices', function(done) {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
var friendlyErrorSpy = sinon.spy(p5, '_friendlyError');

renderer.beginShape(myp5.TESS);
for (let i = 0; i < 60000; i++) {
renderer.vertex(
Math.random() * 10 - 5,
Math.random() * 10 - 5,
Math.random() * 10 - 5
);
}
renderer.endShape();

assert.isTrue(
friendlyErrorSpy.called,
'p5._friendlyError should be called for large vertex count'
);

const warningCall = friendlyErrorSpy.getCalls().find(call =>
call.args[0] && call.args[0].includes('Tessellation warning')
);
assert.isDefined(
warningCall,
'Tessellation warning should be shown'
);

assert.equal(
renderer.immediateMode.shapeMode,
myp5.TRIANGLE_FAN,
'Shape mode should be changed to TRIANGLE_FAN when tessellation is skipped'
);

friendlyErrorSpy.restore();
done();
});

test('TESS mode works normally for <50k vertices', function(done) {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);
var friendlyErrorSpy = sinon.spy(p5, '_friendlyError');

// use a simple shape that tessellates quickly
renderer.beginShape(myp5.TESS);
renderer.vertex(-10, -10, 0);
renderer.vertex(10, -10, 0);
renderer.vertex(10, 10, 0);
renderer.vertex(-10, 10, 0);
renderer.endShape(myp5.CLOSE);

const warningCall = friendlyErrorSpy.getCalls().find(call =>
call.args[0] && call.args[0].includes('Tessellation warning')
);
assert.isUndefined(
warningCall,
'No tessellation warning should be shown for <50k vertices'
);

assert.equal(
renderer.immediateMode.shapeMode,
myp5.TRIANGLES,
'Shape mode should be TRIANGLES after normal tessellation'
);

friendlyErrorSpy.restore();
done();
});

test('TESS does not affect stroke colors', function(done) {
var renderer = myp5.createCanvas(10, 10, myp5.WEBGL);

Expand Down