Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/melonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
- Audio: a sprite region declared looping (`[start, duration, true]`) loops when played with `audio.play(name, { sprite })`. It played only once unless `loop: true` was passed as well
- Text: a label whose text is empty or only spaces no longer blanks the whole frame on WebGPU. Its canvas was sized 0 pixels wide, which WebGPU refuses to upload
- Typings: `TextureAtlas#getAnimationSettings()` is typed as the settings it returns, so they pass to a `Sprite` constructor from TypeScript without a cast. It returned `object`, and `Sprite3d` typed the named animations an aseprite atlas carries as an array
- Typings: `CanvasRenderTarget#destroy(renderer)` is typed and documented with its `renderer`, which frees the copy of the canvas uploaded to the GPU as well. It was hidden, so only the parent's `destroy()` showed, and TypeScript refused the argument

### Performance
- Effects: sprites drawn one after the other through the same `ShaderEffect` share one draw call, as plain sprites do, even from different frames of an atlas. A uniform set to a new value draws the sprites queued before it first, so each sprite still draws with the values it was given; a `screen_texture` effect still draws each sprite on its own
Expand Down
19 changes: 11 additions & 8 deletions packages/melonjs/src/video/rendertarget/canvasrendertarget.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,12 @@ function createContext(canvas, attributes) {
}

/**
* A 2D canvas render target for offscreen texture generation.
* Used by Text, Gradient, Light2d and Particle systems to render into
* a canvas that is then uploaded as a WebGL texture.
* A 2D canvas render target: draw into it once and reuse it, as a texture,
* a baked background or a fog map. Make one and keep it rather than making
* a new one each time. The engine uses it too, for text, gradients,
* particle textures and tinted images. Drawn with the WebGL or WebGPU
* renderer, its canvas is uploaded to the GPU, and uploaded again after
* {@link CanvasRenderTarget#invalidate}.
* @augments RenderTarget
* @category Rendering
*/
Expand Down Expand Up @@ -204,7 +207,7 @@ class CanvasRenderTarget extends RenderTarget {

/**
* Returns an ImageData object representing the underlying pixel data for a specified portion of this canvas texture.
* (Note: when using getImageData(), it is highly recommended to use the `willReadFrequently` attribute when creatimg the corresponding canvas texture)
* (Note: when using getImageData(), it is highly recommended to use the `willReadFrequently` attribute when creating the corresponding canvas texture)
* @param {number} x - The x-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted
* @param {number} y - The y-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted
* @param {number} width - The width of the rectangle from which the ImageData will be extracted. Positive values are to the right, and negative to the left
Expand Down Expand Up @@ -332,10 +335,10 @@ class CanvasRenderTarget extends RenderTarget {
}

/**
* Destroy this canvas render target and release associated GPU resources.
* @param {Renderer} [renderer] - the renderer to clean up WebGL resources from
* @ignore
* @internal
* Release this render target: its canvas, and the copy of it the
* renderer uploaded to the GPU. It must not be used afterwards.
* @param {Renderer} [renderer] - the renderer it was drawn with, so the
* copy uploaded to the GPU is freed too
*/
destroy(renderer) {
// Look up the cached texture by SOURCE identity (the `canvas`
Expand Down
28 changes: 12 additions & 16 deletions packages/melonjs/src/video/rendertarget/rendertarget.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/**
* Abstract base class for offscreen render targets.
* Provides a renderer-agnostic interface for binding, clearing, resizing,
* and reading back pixel data from an offscreen surface.
* Concrete implementations:
* - {@link WebGLRenderTarget} — WebGL framebuffer object (FBO) for post-processing
* - {@link CanvasRenderTarget} — 2D canvas surface for texture generation
* The base class of an offscreen render target: a surface the engine can
* draw into, clear, resize and read back, whatever the renderer.
*
* A future WebGPU implementation would extend this class with
* GPUTexture / GPURenderPassDescriptor management.
* The one to create yourself is {@link CanvasRenderTarget}, a 2D canvas:
* draw something into it once (a texture, a background, a fog map) and
* reuse it every frame. The WebGL and WebGPU renderers keep render targets
* of their own for post effects (a framebuffer, a GPU texture); those are
* internal.
* @category Rendering
*/
export default abstract class RenderTarget {
Expand All @@ -24,15 +23,13 @@ export default abstract class RenderTarget {
/**
* Bind this render target as the active draw destination.
* All subsequent draw calls will render into this target until {@link unbind} is called.
* No-op by default — subclasses override for GPU render targets
* (a WebGL FBO today, a `GPURenderPassEncoder` once the WebGPU
* renderer lands).
* No-op by default: only the GPU renderers' own targets override it.
*/
bind(): void {}

/**
* Unbind this render target, restoring the default (screen) output.
* No-op by default — subclasses override for GPU render targets.
* No-op by default: only the GPU renderers' own targets override it.
*/
unbind(): void {}

Expand Down Expand Up @@ -67,10 +64,9 @@ export default abstract class RenderTarget {
*
* The portable form, and the one anything backend-agnostic should use.
* Synchronous readback is NOT portable: WebGPU can only map a buffer
* asynchronously, so a render target of that backend cannot answer at all
* until a frame has been awaited. The two backends that CAN read back
* synchronously offer `getImageData()` as an extra, but it is not part of
* this contract and does not exist on every target.
* asynchronously, so its render targets cannot answer until a frame has
* been awaited. A {@link CanvasRenderTarget} also offers
* {@link CanvasRenderTarget#getImageData}, which answers at once.
* @param x - x coordinate of the top-left corner (default 0)
* @param y - y coordinate of the top-left corner (default 0)
* @param width - width of the area to read (default full width)
Expand Down
Loading