Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Zyie committed Aug 2, 2024
1 parent 6ea6037 commit c69a4f0
Show file tree
Hide file tree
Showing 34 changed files with 2,151 additions and 14 deletions.
8 changes: 4 additions & 4 deletions packages/backend/src/assets/gpuTextures/textures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,14 @@ export class Textures {
if (!texture.resource) return;

if (!this._textures.get(texture.uid)) {
const res = this._getTextureSource(texture);
const res = this.getTextureSource(texture);
if (res) {
this._textures.set(texture.uid, res);
}
}

if (!this._gpuTextureSize.has(texture.uid)) {
const size = this._getMemorySize(texture, glTextures[texture.uid]);
const size = this.getMemorySize(texture, glTextures[texture.uid]);
if (size) {
this._gpuTextureSize.set(texture.uid, size);
}
Expand Down Expand Up @@ -129,7 +129,7 @@ export class Textures {
return data;
}

private _getTextureSource(texture: TextureSource) {
public getTextureSource(texture: TextureSource) {
if (
texture.resource instanceof ImageBitmap ||
texture.resource instanceof HTMLImageElement ||
Expand Down Expand Up @@ -175,7 +175,7 @@ export class Textures {
return canvas.toDataURL('image/png');
}

private _getMemorySize(texture: TextureSource, webTexture: GlTexture | GPUTexture): number | null {
public getMemorySize(texture: TextureSource, webTexture: GlTexture | GPUTexture): number | null {
if (!webTexture) return null;
if (Array.isArray(texture.resource) && texture.resource[0] instanceof Uint8Array) {
return texture.resource.reduce((acc, res) => acc + res.byteLength, 0);
Expand Down
18 changes: 17 additions & 1 deletion packages/backend/src/pixi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { loop } from './utils/loop';
import { Throttle } from './utils/throttle';
import { Textures } from './assets/gpuTextures/textures';
import type { TextureState } from '@devtool/frontend/pages/assets/assets';
import { Rendering } from './rendering/rendering';
import type { RenderingState } from '@devtool/frontend/pages/rendering/rendering';

/**
* PixiWrapper is a class that wraps around the PixiJS library.
Expand All @@ -31,7 +33,14 @@ class PixiWrapper {
};
public state: Omit<
DevtoolState,
'active' | 'setActive' | 'bridge' | 'setBridge' | 'chromeProxy' | 'setChromeProxy' | keyof TextureState
| 'active'
| 'setActive'
| 'bridge'
| 'setBridge'
| 'chromeProxy'
| 'setChromeProxy'
| keyof TextureState
| keyof RenderingState
> = {
version: null,
setVersion: function (version: DevtoolState['version']) {
Expand Down Expand Up @@ -88,6 +97,7 @@ class PixiWrapper {
public properties = new Properties(this);
public overlay = new Overlay(this);
public textures = new Textures(this);
public rendering = new Rendering(this);
// Private properties
private _devtools: Devtools | undefined;
private _app: Application | undefined;
Expand Down Expand Up @@ -230,6 +240,10 @@ class PixiWrapper {

public get majorVersion() {
if (this.version === '') {
if (!this.stage) {
return null;
}

// lets try and find the version
const stage = this.stage;
if (stage.effects != null && Array.isArray(stage.effects) && '_updateFlags' in stage) {
Expand Down Expand Up @@ -284,6 +298,7 @@ class PixiWrapper {
// TODO: tree: 300ms, stats: 300ms, properties: 300ms, overlay: 50ms

this.overlay.update();
this.rendering.update();
if (this._updateThrottle.shouldExecute(this.settings.throttle)) {
this.preupdate();

Expand Down Expand Up @@ -314,6 +329,7 @@ class PixiWrapper {
this.stats.init();
this.tree.init();
this.textures.init();
this.rendering.init();
this._initialized = true;
}

Expand Down
232 changes: 232 additions & 0 deletions packages/backend/src/rendering/instructions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import type {
BatchInstruction,
FilterInstruction,
GraphicsInstruction,
MaskInstruction,
MeshInstruction,
NineSliceInstruction,
TilingSpriteInstruction,
} from '@devtool/frontend/pages/rendering/instructions/Instructions';
import type {
Batch,
BlurFilter,
Filter,
GlProgram,
GpuProgram,
Graphics,
Mesh,
NineSliceSprite,
FilterInstruction as PixiFilterInstruction,
Sprite,
StencilMaskInstruction,
TilingSprite,
} from 'pixi.js';
import { getRenderableData, getStateData, getTextureData } from './renderableData';
import { PixiDevtools } from '../pixi';
import { getProgramSource } from './program';

type TextureCache = Parameters<typeof getTextureData>[1];
export function getBatchInstruction(instruction: Batch, textureCache: TextureCache): BatchInstruction {
const textures: BatchInstruction['textures'] = [];
instruction.textures.textures.forEach((texture) => {
if (texture == null) return;
const tex = getTextureData(texture, textureCache);
if (tex) {
textures.push(tex);
} else {
console.log('Texture not found', texture);
}
});

return {
type: 'batch',
action: instruction.action,
blendMode: instruction.blendMode,
size: instruction.size,
start: instruction.start,
drawCalls: 0,
drawTextures: [],
textures,
};
}

export function getGraphicsInstruction(instruction: Graphics, _textureCache: TextureCache): GraphicsInstruction {
return {
type: 'graphics',
action: 'draw',
renderable: {
...getRenderableData(instruction),
},
drawCalls: 0,
drawTextures: [],
};
}

export function getNineSliceInstruction(
instruction: NineSliceSprite,
textureCache: TextureCache,
): NineSliceInstruction {
return {
type: 'nineSlice',
action: 'draw',
renderable: {
topHeight: instruction.topHeight,
bottomHeight: instruction.bottomHeight,
leftWidth: instruction.leftWidth,
rightWidth: instruction.rightWidth,
texture: instruction.texture ? getTextureData(instruction.texture._source, textureCache) : null,
originalWidth: instruction.originalWidth,
originalHeight: instruction.originalHeight,
...getRenderableData(instruction),
},
drawCalls: 0,
drawTextures: [],
};
}

export function getMaskInstruction(instruction: StencilMaskInstruction, _textureCache: TextureCache): MaskInstruction {
const mask = instruction.mask?.mask;
return {
type: instruction.renderPipeId,
action: instruction.action,
mask: mask
? {
...getRenderableData(mask),
}
: null,
drawCalls: 0,
drawTextures: [],
};
}

export function getFilterInstruction(
instruction: PixiFilterInstruction,
textureCache: TextureCache,
): FilterInstruction {
const rendererType = PixiDevtools.renderer.name === 'webgl' ? 'webgl' : 'webgpu';
const getProgramSource = (filter: Filter, shader: 'fragment' | 'vertex'): string => {
const type = rendererType === 'webgl' ? 'glProgram' : 'gpuProgram';
let program: GlProgram | GpuProgram;

// if blur filter then you need to get the blurX/blurY shaders
if ((filter as BlurFilter).blurXFilter) {
program = (filter as BlurFilter).blurXFilter[type];
} else {
program = filter[type];
}

if (!program) {
return '';
}

const source = program[shader];

if (!source) {
return '';
}

if (typeof source === 'string') {
return source;
}

return source.source;
};

return {
type: 'filter',
action: instruction.action,
filter: instruction.filterEffect?.filters?.map((filter) => {
return {
type: filter.constructor.name,
padding: filter.padding,
resolution: filter.resolution,
antialias: filter.antialias,
blendMode: filter.blendMode,
program: {
fragment: getProgramSource(filter, 'fragment'),
vertex: getProgramSource(filter, 'vertex'),
},
state: getStateData(filter._state),
};
}),
renderables:
instruction.renderables?.map((renderable) => ({
texture: (renderable as Sprite).texture
? getTextureData((renderable as Sprite).texture.source, textureCache)
: null,
...getRenderableData(renderable),
})) || ([] as FilterInstruction['renderables']),
drawCalls: 0,
drawTextures: [],
};
}

interface PixiMeshObjectInstruction {
renderPipeId: string;
mesh: Mesh;
}

export function getMeshInstruction(
instruction: Mesh | PixiMeshObjectInstruction,
textureCache: TextureCache,
): MeshInstruction {
const mesh = (instruction as PixiMeshObjectInstruction)?.mesh || instruction;
const rendererType = PixiDevtools.renderer.name === 'webgl' ? 'webgl' : 'webgpu';
let shader = mesh.shader;
const program = {
fragment: null as string | null,
vertex: null as string | null,
};

if (!shader) {
shader = PixiDevtools.renderer.renderPipes.mesh['_adaptor']._shader;
program.fragment = getProgramSource(shader, 'fragment', rendererType);
program.vertex = getProgramSource(shader, 'vertex', rendererType);
} else {
program.fragment = getProgramSource(shader, 'fragment', rendererType);
program.vertex = getProgramSource(shader, 'vertex', rendererType);
}
return {
type: 'mesh',
action: 'draw',
renderable: {
texture: mesh.texture ? getTextureData(mesh.texture.source, textureCache) : null,
program,
state: getStateData(mesh.state),
geometry: {
indices: Array.from(mesh.geometry.indices),
positions: Array.from(mesh.geometry.positions),
uvs: Array.from(mesh.geometry.uvs),
},
...getRenderableData(mesh),
},
drawCalls: 0,
drawTextures: [],
};
}

export function getTileSpriteInstruction(
tilingSprite: TilingSprite,
textureCache: TextureCache,
): TilingSpriteInstruction {
return {
type: 'tilingSprite',
action: 'draw',
renderable: {
...getRenderableData(tilingSprite),
texture: tilingSprite.texture ? getTextureData(tilingSprite.texture.source, textureCache) : null,
tilePosition: {
x: tilingSprite.tilePosition.x,
y: tilingSprite.tilePosition.y,
},
tileScale: {
x: tilingSprite.tileScale.x,
y: tilingSprite.tileScale.y,
},
tileRotation: tilingSprite.tileRotation,
clampMargin: tilingSprite.clampMargin,
},
drawCalls: 0,
drawTextures: [],
};
}
21 changes: 21 additions & 0 deletions packages/backend/src/rendering/program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Filter, TextureShader } from 'pixi.js';

export function getProgramSource(
filter: Filter | TextureShader,
shader: 'fragment' | 'vertex',
rendererType: 'webgl' | 'webgpu',
): string {
const type = rendererType === 'webgl' ? 'glProgram' : 'gpuProgram';
const program = filter[type];
const source = program[shader];

if (!source) {
return '';
}

if (typeof source === 'string') {
return source;
}

return source.source;
}
45 changes: 45 additions & 0 deletions packages/backend/src/rendering/readPixels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { GlRenderTarget, WebGLRenderer } from 'pixi.js';

export function readGlPixels(
gl: WebGLRenderingContext,
renderer: WebGLRenderer,
canvasTextures: string[],
width: number,
height: number,
) {
// Create a buffer to hold the pixel data
// Uint8Array for 8-bit per channel (RGBA), adjust as needed
const pixels = new Uint8Array(width * height * 4);

const renterTarget = renderer.renderTarget.getRenderTarget(renderer.renderTarget.renderTarget);
const glRenterTarget = renderer.renderTarget.getGpuRenderTarget(renterTarget) as GlRenderTarget;
// Bind the framebuffer you want to read from (null for default framebuffer)
gl.bindFramebuffer(gl.FRAMEBUFFER, glRenterTarget.resolveTargetFramebuffer);

// Read the pixels
gl.readPixels(
0,
0, // Start reading from the bottom left of the framebuffer
width,
height, // The dimensions of the area you want to read
gl.RGBA, // Format of the pixel data
gl.UNSIGNED_BYTE, // Type of the pixel data
pixels, // The buffer to read the pixels into
);

// Create a 2D canvas to draw the pixels on
const canvas2d = document.createElement('canvas');
canvas2d.width = width;
canvas2d.height = height;
const ctx = canvas2d.getContext('2d')!;

// Create an ImageData object
const imageData = new ImageData(new Uint8ClampedArray(pixels), width, height);

// Draw the ImageData object to the canvas
ctx.putImageData(imageData, 0, 0);

// Convert the canvas to a data URL and set it as the src of an image element
const dataUrl = canvas2d.toDataURL('image/webp', 0.5);
canvasTextures.push(dataUrl);
}
Loading

0 comments on commit c69a4f0

Please sign in to comment.