diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index a9480cb7d..71c21c2d4 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -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 diff --git a/packages/melonjs/src/video/rendertarget/canvasrendertarget.js b/packages/melonjs/src/video/rendertarget/canvasrendertarget.js index a93da6100..3447c413f 100644 --- a/packages/melonjs/src/video/rendertarget/canvasrendertarget.js +++ b/packages/melonjs/src/video/rendertarget/canvasrendertarget.js @@ -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 */ @@ -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 @@ -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` diff --git a/packages/melonjs/src/video/rendertarget/rendertarget.ts b/packages/melonjs/src/video/rendertarget/rendertarget.ts index 83d1bd57d..eb7528b18 100644 --- a/packages/melonjs/src/video/rendertarget/rendertarget.ts +++ b/packages/melonjs/src/video/rendertarget/rendertarget.ts @@ -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 { @@ -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 {} @@ -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)