Skip to content

Commit

Permalink
feat: cacheAsTexture (pixijs#11031)
Browse files Browse the repository at this point in the history
* add cacheAsTexture!

* linty

* fix types

* refactor cacheAsBitmap

* Update src/scene/container/RenderGroup.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* return postrender on GPUEncoderSystem

* little lint fix

* remove double var

* add check for instruction.container in filter push

* Update getFastGlobalBounds.ts

* add cacheAsTexture!

* linty

* fix types

* refactor cacheAsBitmap

* return postrender on GPUEncoderSystem

* Update src/scene/container/RenderGroup.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* little lint fix

* remove double var

* add check for instruction.container in filter push

* Update getFastGlobalBounds.ts

* update api

* Update src/scene/container/RenderGroupSystem.ts

Co-authored-by: Zyie <[email protected]>

---------

Co-authored-by: Zyie <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 13, 2024
1 parent a6e68a6 commit fc1820c
Show file tree
Hide file tree
Showing 17 changed files with 590 additions and 95 deletions.
15 changes: 15 additions & 0 deletions src/filters/FilterSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,21 @@ export class FilterSystem implements System
{
getFastGlobalBounds(instruction.container, bounds);
}

if (instruction.container)
{
// When a container is cached as a texture, its filters need to be applied relative to its
// cached parent's coordinate space rather than world space. This transform adjustment ensures
// filters are applied in the correct coordinate system.
const renderGroup = instruction.container.renderGroup || instruction.container.parentRenderGroup;
const filterFrameTransform = renderGroup.cacheToLocalTransform;

if (filterFrameTransform)
{
bounds.applyMatrix(filterFrameTransform);
}
}

// get GLOBAL bounds of the item we are going to apply the filter to

const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source;
Expand Down
6 changes: 0 additions & 6 deletions src/rendering/renderers/gpu/buffer/UboBatch.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { Buffer } from '../../shared/buffer/Buffer';

export class UboBatch
{
private _buffer: Buffer;
public data: Float32Array;
private readonly _minUniformOffsetAlignment: number = 256;

Expand Down Expand Up @@ -58,9 +55,6 @@ export class UboBatch

public destroy()
{
this._buffer.destroy();
this._buffer = null;

this.data = null;
}
}
4 changes: 4 additions & 0 deletions src/rendering/renderers/shared/system/AbstractRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ export class AbstractRenderer<
options.transform = options.container.localTransform;
}

// lets ensure this object is a render group so we can render it!
// the renderer only likes to render - render groups.
options.container.enableRenderGroup();

this.runners.prerender.emit(options);
this.runners.renderStart.emit(options);
this.runners.render.emit(options);
Expand Down
7 changes: 5 additions & 2 deletions src/scene/SceneMixins.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ContainerChild } from './container/Container';
import type { CacheAsTextureMixin, CacheAsTextureMixinConstructor } from './container/container-mixins/cacheAsTextureMixin';
import type { ChildrenHelperMixin } from './container/container-mixins/childrenHelperMixin';
import type { EffectsMixin, EffectsMixinConstructor } from './container/container-mixins/effectsMixin';
import type { FindMixin, FindMixinConstructor } from './container/container-mixins/findMixin';
Expand All @@ -19,15 +20,17 @@ declare global
MeasureMixin,
EffectsMixin,
FindMixin,
SortMixin {}
SortMixin,
CacheAsTextureMixin {}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ContainerOptions
extends OnRenderMixinConstructor,
MeasureMixinConstructor,
EffectsMixinConstructor,
FindMixinConstructor,
SortMixinConstructor {}
SortMixinConstructor,
CacheAsTextureMixinConstructor {}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/scene/container/Container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ObservablePoint } from '../../maths/point/ObservablePoint';
import { uid } from '../../utils/data/uid';
import { deprecation, v8_0_0 } from '../../utils/logging/deprecation';
import { BigPool } from '../../utils/pool/PoolGroup';
import { cacheAsTextureMixin } from './container-mixins/cacheAsTextureMixin';
import { childrenHelperMixin } from './container-mixins/childrenHelperMixin';
import { effectsMixin } from './container-mixins/effectsMixin';
import { findMixin } from './container-mixins/findMixin';
Expand Down Expand Up @@ -1390,3 +1391,4 @@ Container.mixin(effectsMixin);
Container.mixin(findMixin);
Container.mixin(sortMixin);
Container.mixin(cullingMixin);
Container.mixin(cacheAsTextureMixin);
189 changes: 189 additions & 0 deletions src/scene/container/RenderGroup.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import { Matrix } from '../../maths/matrix/Matrix';
import { InstructionSet } from '../../rendering/renderers/shared/instructions/InstructionSet';
import { TexturePool } from '../../rendering/renderers/shared/texture/TexturePool';

import type { Instruction } from '../../rendering/renderers/shared/instructions/Instruction';
import type { Texture } from '../../rendering/renderers/shared/texture/Texture';
import type { BatchableSprite } from '../sprite/BatchableSprite';
import type { ViewContainer } from '../view/ViewContainer';
import type { Bounds } from './bounds/Bounds';
import type { Container } from './Container';

/**
* Options for caching a container as a texture.
* @memberof rendering
* @see {@link RenderGroup#textureOptions}
*/
export interface CacheAsTextureOptions
{
/**
* If true, the texture will be antialiased. This smooths out the edges of the texture.
* @default false
*/
antialias?: boolean;
/**
* The resolution of the texture. A higher resolution means a sharper texture but uses more memory.
* By default the resolution is 1 which is the same as the rendererers resolution.
*/
resolution?: number;
}

/**
* A RenderGroup is a class that is responsible for I generating a set of instructions that are used to render the
* root container and its children. It also watches for any changes in that container or its children,
Expand Down Expand Up @@ -40,6 +63,58 @@ export class RenderGroup implements Instruction

private readonly _onRenderContainers: Container[] = [];

/**
* Indicates if the cached texture needs to be updated.
* @default true
*/
public textureNeedsUpdate = true;

/**
* Indicates if the container should be cached as a texture.
* @default false
*/
public isCachedAsTexture = false;

/**
* The texture used for caching the container. this is only set if isCachedAsTexture is true.
* It can only be accessed after a render pass.
* @type {Texture | undefined}
*/
public texture?: Texture;

/**
* The bounds of the cached texture.
* @type {Bounds | undefined}
* @ignore
*/
public _textureBounds?: Bounds;

/**
* The options for caching the container as a texture.
* @type {CacheAsTextureOptions}
*/
public textureOptions: CacheAsTextureOptions;

/**
* holds a reference to the batchable render sprite
* @ignore
*/
public _batchableRenderGroup: BatchableSprite;

/**
* Holds a reference to the closest parent RenderGroup that has isCachedAsTexture enabled.
* This is used to properly transform coordinates when rendering into cached textures.
* @type {RenderGroup | null}
* @ignore
*/
public _parentCacheAsTextureRenderGroup: RenderGroup;

private _inverseWorldTransform: Matrix;
private _textureOffsetInverseTransform: Matrix;
private _inverseParentTextureTransform: Matrix;

private _matrixDirty = 0b111;

public init(root: Container)
{
this.root = root;
Expand All @@ -56,6 +131,28 @@ export class RenderGroup implements Instruction
}
}

public enableCacheAsTexture(options: CacheAsTextureOptions = {}): void
{
this.textureOptions = options;
this.isCachedAsTexture = true;
this.textureNeedsUpdate = true;
}

public disableCacheAsTexture(): void
{
this.isCachedAsTexture = false;
if (this.texture)
{
TexturePool.returnTexture(this.texture);
this.texture = null;
}
}

public updateCacheTexture(): void
{
this.textureNeedsUpdate = true;
}

public reset()
{
this.renderGroupChildren.length = 0;
Expand All @@ -77,6 +174,8 @@ export class RenderGroup implements Instruction

this._onRenderContainers.length = 0;
this.renderGroupParent = null;

this.disableCacheAsTexture();
}

get localTransform()
Expand Down Expand Up @@ -243,6 +342,8 @@ export class RenderGroup implements Instruction

public destroy()
{
this.disableCacheAsTexture();

this.renderGroupParent = null;
this.root = null;
(this.childrenRenderablesToUpdate as any) = null;
Expand Down Expand Up @@ -279,4 +380,92 @@ export class RenderGroup implements Instruction

return out;
}

public invalidateMatrices()
{
this._matrixDirty = 0b111;
}

/**
* Returns the inverse of the world transform matrix.
* @returns {Matrix} The inverse of the world transform matrix.
*/
public get inverseWorldTransform()
{
if ((this._matrixDirty & 0b001) === 0) return this._inverseWorldTransform;

this._matrixDirty &= ~0b001;

// TODO - add dirty flag
this._inverseWorldTransform ||= new Matrix();

return this._inverseWorldTransform
.copyFrom(this.worldTransform)
.invert();
}

/**
* Returns the inverse of the texture offset transform matrix.
* @returns {Matrix} The inverse of the texture offset transform matrix.
*/
public get textureOffsetInverseTransform()
{
if ((this._matrixDirty & 0b010) === 0) return this._textureOffsetInverseTransform;

this._matrixDirty &= ~0b010;

this._textureOffsetInverseTransform ||= new Matrix();

// TODO shared.. bad!
return this._textureOffsetInverseTransform
.copyFrom(this.inverseWorldTransform)
.translate(
-this._textureBounds.x,
-this._textureBounds.y
);
}

/**
* Returns the inverse of the parent texture transform matrix.
* This is used to properly transform coordinates when rendering into cached textures.
* @returns {Matrix} The inverse of the parent texture transform matrix.
*/
public get inverseParentTextureTransform()
{
if ((this._matrixDirty & 0b100) === 0) return this._inverseParentTextureTransform;

this._matrixDirty &= ~0b100;

const parentCacheAsTexture = this._parentCacheAsTextureRenderGroup;

if (parentCacheAsTexture)
{
this._inverseParentTextureTransform ||= new Matrix();

// Get relative transform by removing parent's world transform
return this._inverseParentTextureTransform
.copyFrom(this.worldTransform)
.prepend(parentCacheAsTexture.inverseWorldTransform)
// Offset by texture bounds
.translate(
-parentCacheAsTexture._textureBounds.x,
-parentCacheAsTexture._textureBounds.y
);
}

return this.worldTransform;
}

/**
* Returns a matrix that transforms coordinates to the correct coordinate space of the texture being rendered to.
* This is the texture offset inverse transform of the closest parent RenderGroup that is cached as a texture.
* @returns {Matrix | null} The transform matrix for the cached texture coordinate space,
* or null if no parent is cached as texture.
*/
public get cacheToLocalTransform()
{
if (!this._parentCacheAsTextureRenderGroup) return null;

return this._parentCacheAsTextureRenderGroup.textureOffsetInverseTransform;
}
}
Loading

0 comments on commit fc1820c

Please sign in to comment.