Skip to content

Commit

Permalink
fix: [#3047] Animation glitch cause by uninitialized state (#3048)
Browse files Browse the repository at this point in the history
Closes #3047

After the performance update to `ImageRenderer` in v0.29.2 there was uninitialized shared state that leaks between draw calls, which `Raster`s are especially susceptible to based on the call pattern.
  • Loading branch information
eonarheim authored May 5, 2024
1 parent 9439441 commit 8058188
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- Fixed animation glitch caused by uninitialized state in `ImageRenderer`
- Fixed issue where `ex.Loader.suppressPlayButton = true` did not work. Only using the `ex.Engine({suppressPlayButton: true})` worked

### Updates
Expand Down
12 changes: 12 additions & 0 deletions sandbox/tests/polygon-rendering/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Polygon Animation Rendering</title>
</head>
<body>
<script src="../../lib/excalibur.js"></script>
<script src="index.js"></script>
</body>
</html>
72 changes: 72 additions & 0 deletions sandbox/tests/polygon-rendering/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
var engine = new ex.Engine({
width: 500,
height: 400
});

var image = new ex.ImageSource('test.png');
var spriteSheet = ex.SpriteSheet.fromImageSource({
image: image,
grid: {
rows: 1,
columns: 4,
spriteWidth: 100,
spriteHeight: 100
}
});

// Set up the moving object
// var mover = new ex.Actor({ x: 100, y: 0 });
// mover.anchor = ex.vec(0, 0);
// mover.actions.repeatForever((ctx) => ctx.moveTo(ex.vec(100, 300), 50).moveTo(ex.vec(100, 0), 50));
// mover.graphics.add('up', spriteSheet.sprites[0]);

var down = ex.Animation.fromSpriteSheet(spriteSheet, [0, 1, 2, 3], 500);

// mover.graphics.add('down', down);
// mover.onPreUpdate = () => {
// if (mover.vel.y < 0) {
// mover.graphics.use('up');
// }
// if (mover.vel.y > 0) {
// mover.graphics.use('down');
// }
// };

// set up the hexagon object
// var static1 = new ex.Actor({ x: 350, y: 100 });
var polygon1 = new ex.Polygon({
points: [ex.vec(50, 0), ex.vec(150, 0), ex.vec(200, 86), ex.vec(150, 172), ex.vec(50, 172), ex.vec(0, 86)],
color: ex.Color.fromRGB(0, 255, 0)
});
// static1.graphics.use(polygon1);

// set up the rectangle object
// var static2 = new ex.Actor({ x: 300, y: 300 });
var rect = new ex.Rectangle({ width: 100, height: 100, color: ex.Color.fromRGB(255, 0, 0) });
// static2.graphics.use(rect);

// set up the circle object
// var static3 = new ex.Actor({ x: 400, y: 300 });
var circle = new ex.Circle({ radius: 50, color: ex.Color.fromRGB(0, 0, 255) });
// static3.graphics.use(circle);

var loader = new ex.Loader([image]);

engine.currentScene.onPostDraw = (ctx) => {
down.draw(ctx, 100, 100);
polygon1.draw(ctx, 350, 100);
rect.draw(ctx, 300, 300);
circle.draw(ctx, 400, 300);

down.tick(500, 1);
down.draw(ctx, 100, 100);
polygon1.draw(ctx, 350, 100);
rect.draw(ctx, 300, 300);
circle.draw(ctx, 400, 300);
};

// engine.add(mover);
// engine.add(static1);
// engine.add(static2);
// engine.add(static3);
engine.start(loader);
Binary file added sandbox/tests/polygon-rendering/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/engine/Graphics/Context/image-renderer/image-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ export class ImageRenderer implements RendererPlugin {

let width = maybeImageWidth || swidth || 0;
let height = maybeImageHeight || sheight || 0;
this._view[0] = 0;
this._view[1] = 0;
this._view[2] = swidth ?? maybeImageWidth ?? 0;
this._view[3] = sheight ?? maybeImageHeight ?? 0;
this._dest[0] = sx ?? 1;
Expand Down
55 changes: 55 additions & 0 deletions src/spec/ExcaliburGraphicsContextSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,5 +1053,60 @@ describe('The ExcaliburGraphicsContext', () => {
}).not.toThrow();
sut.dispose();
});

it('can handle rendering rasters in succession', async () => {
// arrange
const image = new ex.ImageSource('src/spec/images/ExcaliburGraphicsContextSpec/test.png');
await image.load();
const spriteSheet = ex.SpriteSheet.fromImageSource({
image: image,
grid: {
rows: 1,
columns: 4,
spriteWidth: 100,
spriteHeight: 100
}
});

const down = ex.Animation.fromSpriteSheet(spriteSheet, [0, 1, 2, 3], 500);
const polygon = new ex.Polygon({
points: [ex.vec(50, 0), ex.vec(150, 0), ex.vec(200, 86), ex.vec(150, 172), ex.vec(50, 172), ex.vec(0, 86)],
color: ex.Color.fromRGB(0, 255, 0)
});
const rect = new ex.Rectangle({ width: 100, height: 100, color: ex.Color.fromRGB(255, 0, 0) });
const circle = new ex.Circle({ radius: 50, color: ex.Color.fromRGB(0, 0, 255) });

const canvasElement = testCanvasElement;
canvasElement.width = 500;
canvasElement.height = 400;
const sut = new ex.ExcaliburGraphicsContextWebGL({
canvasElement: canvasElement,
context: testContext,
enableTransparency: false,
snapToPixel: true,
backgroundColor: ex.Color.White
});

// act

sut.clear();

down.draw(sut, 100, 100);
polygon.draw(sut, 350, 100);
rect.draw(sut, 300, 300);
circle.draw(sut, 400, 300);

down.tick(500, 1);
down.draw(sut, 100, 100);
polygon.draw(sut, 350, 100);
rect.draw(sut, 300, 300);
circle.draw(sut, 400, 300);

sut.flush();

// assert
await expectAsync(canvasElement).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/rasters.png');
sut.dispose();
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8058188

Please sign in to comment.