diff --git a/.eslintignore b/.eslintignore index 19eeadf09..588f8e433 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,4 +6,5 @@ dist node_modules rollup.config.js site +rust __tests__ diff --git a/__tests__/integration/__node__tests__/webgl/material.spec.js b/__tests__/integration/__node__tests__/webgl/material.spec.js index 1ee79384e..3676116a9 100644 --- a/__tests__/integration/__node__tests__/webgl/material.spec.js +++ b/__tests__/integration/__node__tests__/webgl/material.spec.js @@ -81,7 +81,7 @@ describe('Render custom material with g-webgl', () => { bufferGeometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + frequency: VertexBufferFrequency.VERTEX, attributes: [ { format: Format.F32_RGB, diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/circle.png b/__tests__/integration/__node__tests__/webgl/snapshots/circle.png index a0f6ae4e3..84955bdff 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/circle.png and b/__tests__/integration/__node__tests__/webgl/snapshots/circle.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/d3-barchart.png b/__tests__/integration/__node__tests__/webgl/snapshots/d3-barchart.png index 3adaf86ed..1a295272f 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/d3-barchart.png and b/__tests__/integration/__node__tests__/webgl/snapshots/d3-barchart.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/d3-linechart.png b/__tests__/integration/__node__tests__/webgl/snapshots/d3-linechart.png index eb7cefeb0..08c329a86 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/d3-linechart.png and b/__tests__/integration/__node__tests__/webgl/snapshots/d3-linechart.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/d3-scatterchart.png b/__tests__/integration/__node__tests__/webgl/snapshots/d3-scatterchart.png index bdf8686af..aabcfb488 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/d3-scatterchart.png and b/__tests__/integration/__node__tests__/webgl/snapshots/d3-scatterchart.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/ellipse.png b/__tests__/integration/__node__tests__/webgl/snapshots/ellipse.png index 5298bc423..c60e01c06 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/ellipse.png and b/__tests__/integration/__node__tests__/webgl/snapshots/ellipse.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/rect.png b/__tests__/integration/__node__tests__/webgl/snapshots/rect.png index 4b289d902..25e06aa8f 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/rect.png and b/__tests__/integration/__node__tests__/webgl/snapshots/rect.png differ diff --git a/__tests__/integration/__node__tests__/webgl/snapshots/text.png b/__tests__/integration/__node__tests__/webgl/snapshots/text.png index 623e05b4b..77a860333 100644 Binary files a/__tests__/integration/__node__tests__/webgl/snapshots/text.png and b/__tests__/integration/__node__tests__/webgl/snapshots/text.png differ diff --git a/packages/g-mobile-webgl/CHANGELOG.md b/packages/g-mobile-webgl/CHANGELOG.md index 0b36e3c89..dddfd0727 100644 --- a/packages/g-mobile-webgl/CHANGELOG.md +++ b/packages/g-mobile-webgl/CHANGELOG.md @@ -1,5 +1,14 @@ # @antv/g-mobile-webgl +## 0.9.20 + +### Patch Changes + +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + - @antv/g-plugin-webgl-device@1.9.17 + ## 0.9.19 ### Patch Changes diff --git a/packages/g-mobile-webgl/package.json b/packages/g-mobile-webgl/package.json index 6f5bf2559..f4c14595e 100644 --- a/packages/g-mobile-webgl/package.json +++ b/packages/g-mobile-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-mobile-webgl", - "version": "0.9.19", + "version": "0.9.20", "description": "A renderer implemented by WebGL1/2 in mobile environment", "keywords": [ "antv", diff --git a/packages/g-plugin-3d/CHANGELOG.md b/packages/g-plugin-3d/CHANGELOG.md index 124b12034..0123766b8 100644 --- a/packages/g-plugin-3d/CHANGELOG.md +++ b/packages/g-plugin-3d/CHANGELOG.md @@ -1,5 +1,15 @@ # @antv/g-plugin-3d +## 1.9.17 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- 568ec0f4: Export Device API in webgl & webgpu. +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + ## 1.9.16 ### Patch Changes diff --git a/packages/g-plugin-3d/package.json b/packages/g-plugin-3d/package.json index 3812ce328..3f7379611 100644 --- a/packages/g-plugin-3d/package.json +++ b/packages/g-plugin-3d/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-3d", - "version": "1.9.16", + "version": "1.9.17", "description": "Provide 3D extension for G", "keywords": [ "antv", diff --git a/packages/g-plugin-3d/src/geometries/ProceduralGeometry.ts b/packages/g-plugin-3d/src/geometries/ProceduralGeometry.ts index 96fafb90f..d1ac44507 100644 --- a/packages/g-plugin-3d/src/geometries/ProceduralGeometry.ts +++ b/packages/g-plugin-3d/src/geometries/ProceduralGeometry.ts @@ -4,7 +4,7 @@ import { Format, VertexAttributeBufferIndex, VertexAttributeLocation, - VertexBufferFrequency, + VertexStepMode, } from '@antv/g-plugin-device-renderer'; import type { Device } from '@antv/g-plugin-device-renderer'; import { mat4, vec3, vec4 } from 'gl-matrix'; @@ -139,7 +139,7 @@ export abstract class ProceduralGeometry< this.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -152,7 +152,7 @@ export abstract class ProceduralGeometry< this.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.NORMAL, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -165,7 +165,7 @@ export abstract class ProceduralGeometry< this.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.UV, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, diff --git a/packages/g-plugin-3d/src/index.ts b/packages/g-plugin-3d/src/index.ts index 4aa3be34c..81ad52e7f 100644 --- a/packages/g-plugin-3d/src/index.ts +++ b/packages/g-plugin-3d/src/index.ts @@ -10,7 +10,7 @@ import { Fog, FogType, Format, - FrontFaceMode, + FrontFace, GL, Light, Material, @@ -24,7 +24,7 @@ import { TextureUsage, VertexAttributeBufferIndex, VertexAttributeLocation, - VertexBufferFrequency, + VertexStepMode, WrapMode, } from '@antv/g-plugin-device-renderer'; @@ -42,7 +42,7 @@ export { Fog, FogType, Format, - FrontFaceMode, + FrontFace, Light, Material, Mesh, @@ -56,7 +56,7 @@ export { GL, VertexAttributeBufferIndex, VertexAttributeLocation, - VertexBufferFrequency, + VertexStepMode, WrapMode, }; diff --git a/packages/g-plugin-3d/src/materials/MeshBasicMaterial.ts b/packages/g-plugin-3d/src/materials/MeshBasicMaterial.ts index 3d6629157..afccf710c 100644 --- a/packages/g-plugin-3d/src/materials/MeshBasicMaterial.ts +++ b/packages/g-plugin-3d/src/materials/MeshBasicMaterial.ts @@ -62,7 +62,7 @@ export class MeshBasicMaterial< super(device, { vertexShader: vert, fragmentShader: frag, - cullMode: CullMode.Back, + cullMode: CullMode.BACK, ...props, }); diff --git a/packages/g-plugin-3d/src/materials/PointMaterial.ts b/packages/g-plugin-3d/src/materials/PointMaterial.ts index 22a38b202..789545b95 100644 --- a/packages/g-plugin-3d/src/materials/PointMaterial.ts +++ b/packages/g-plugin-3d/src/materials/PointMaterial.ts @@ -1,4 +1,8 @@ -import type { IMaterial, Texture, Device } from '@antv/g-plugin-device-renderer'; +import type { + IMaterial, + Texture, + Device, +} from '@antv/g-plugin-device-renderer'; import { Material, CullMode } from '@antv/g-plugin-device-renderer'; import vert from '../shaders/point.vert'; import frag from '../shaders/point.frag'; @@ -57,7 +61,7 @@ export class PointMaterial extends Material { super(device, { vertexShader: vert, fragmentShader: frag, - cullMode: CullMode.Back, + cullMode: CullMode.BACK, ...props, }); diff --git a/packages/g-plugin-device-renderer/CHANGELOG.md b/packages/g-plugin-device-renderer/CHANGELOG.md index 64ce5df54..e68806374 100644 --- a/packages/g-plugin-device-renderer/CHANGELOG.md +++ b/packages/g-plugin-device-renderer/CHANGELOG.md @@ -1,5 +1,12 @@ # @antv/g-plugin-device-renderer +## 1.9.17 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- 568ec0f4: Export Device API in webgl & webgpu. + ## 1.9.16 ### Patch Changes diff --git a/packages/g-plugin-device-renderer/package.json b/packages/g-plugin-device-renderer/package.json index 29a8d46a7..61e1a7edb 100644 --- a/packages/g-plugin-device-renderer/package.json +++ b/packages/g-plugin-device-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-device-renderer", - "version": "1.9.16", + "version": "1.9.17", "description": "A G plugin of renderer implementation with GPUDevice", "keywords": [ "antv", diff --git a/packages/g-plugin-device-renderer/src/PickingPlugin.ts b/packages/g-plugin-device-renderer/src/PickingPlugin.ts index 6afe7657c..e95c54284 100644 --- a/packages/g-plugin-device-renderer/src/PickingPlugin.ts +++ b/packages/g-plugin-device-renderer/src/PickingPlugin.ts @@ -316,12 +316,12 @@ export class PickingPlugin implements RenderingPlugin { depthWrite: true, }, { - rgbBlendMode: BlendMode.Add, - rgbBlendSrcFactor: BlendFactor.One, - rgbBlendDstFactor: BlendFactor.Zero, - alphaBlendMode: BlendMode.Add, - alphaBlendSrcFactor: BlendFactor.One, - alphaBlendDstFactor: BlendFactor.Zero, + rgbBlendMode: BlendMode.ADD, + rgbBlendSrcFactor: BlendFactor.ONE, + rgbBlendDstFactor: BlendFactor.ZERO, + alphaBlendMode: BlendMode.ADD, + alphaBlendSrcFactor: BlendFactor.ONE, + alphaBlendDstFactor: BlendFactor.ZERO, }, ), ); diff --git a/packages/g-plugin-device-renderer/src/RenderGraphPlugin.ts b/packages/g-plugin-device-renderer/src/RenderGraphPlugin.ts index 1da6ae365..0b8a0ed79 100644 --- a/packages/g-plugin-device-renderer/src/RenderGraphPlugin.ts +++ b/packages/g-plugin-device-renderer/src/RenderGraphPlugin.ts @@ -235,6 +235,8 @@ export class RenderGraphPlugin implements RenderingPlugin { renderingService.hooks.destroy.tap(RenderGraphPlugin.tag, () => { this.renderHelper.destroy(); + this.batchManager.destroy(); + this.texturePool.destroy(); canvas.removeEventListener(ElementEvent.MOUNTED, handleMounted); canvas.removeEventListener(ElementEvent.UNMOUNTED, handleUnmounted); @@ -246,6 +248,9 @@ export class RenderGraphPlugin implements RenderingPlugin { ElementEvent.BOUNDS_CHANGED, handleBoundsChanged, ); + + this.device.destroy(); + this.device.checkForLeaks(); }); /** @@ -355,12 +360,12 @@ export class RenderGraphPlugin implements RenderingPlugin { blendConstant: TransparentBlack, }, { - rgbBlendMode: BlendMode.Add, - alphaBlendMode: BlendMode.Add, - rgbBlendSrcFactor: BlendFactor.SrcAlpha, - alphaBlendSrcFactor: BlendFactor.One, - rgbBlendDstFactor: BlendFactor.OneMinusSrcAlpha, - alphaBlendDstFactor: BlendFactor.OneMinusSrcAlpha, + rgbBlendMode: BlendMode.ADD, + alphaBlendMode: BlendMode.ADD, + rgbBlendSrcFactor: BlendFactor.SRC_ALPHA, + alphaBlendSrcFactor: BlendFactor.ONE, + rgbBlendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + alphaBlendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, }, ), ); diff --git a/packages/g-plugin-device-renderer/src/TexturePool.ts b/packages/g-plugin-device-renderer/src/TexturePool.ts index edc5d4d01..22de91948 100644 --- a/packages/g-plugin-device-renderer/src/TexturePool.ts +++ b/packages/g-plugin-device-renderer/src/TexturePool.ts @@ -50,8 +50,8 @@ export class TexturePool { height: 1, depth: 1, numLevels: 1, - dimension: TextureDimension.n2D, - usage: TextureUsage.Sampled, + dimension: TextureDimension.TEXTURE_2D, + usage: TextureUsage.SAMPLED, pixelStore: { unpackFlipY: false, }, @@ -64,7 +64,7 @@ export class TexturePool { } if (!isString(src)) { - texture.setImageData(src); + texture.setImageData([src]); texture.emit(TextureEvent.LOADED); this.context.renderingService.dirtify(); } else { @@ -81,7 +81,7 @@ export class TexturePool { if (image) { image.onload = () => { const onSuccess = (bitmap: ImageBitmap | HTMLImageElement) => { - this.textureCache[id].setImageData(bitmap); + this.textureCache[id].setImageData([bitmap]); this.textureCache[id].emit(TextureEvent.LOADED); this.context.renderingService.dirtify(); if (successCallback) { diff --git a/packages/g-plugin-device-renderer/src/drawcalls/Image.ts b/packages/g-plugin-device-renderer/src/drawcalls/Image.ts index 227871af0..a400a71a6 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/Image.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/Image.ts @@ -4,7 +4,7 @@ import { VertexAttributeBufferIndex, VertexAttributeLocation, } from './Instanced'; -import { Format, VertexBufferFrequency } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import frag from '../shader/image.frag'; import vert from '../shader/image.vert'; import { enumToObject } from '../utils'; @@ -79,7 +79,7 @@ export class ImageDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RG, @@ -92,7 +92,7 @@ export class ImageDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: ImageVertexAttributeBufferIndex.PACKED_STYLE, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, @@ -106,7 +106,7 @@ export class ImageDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.UV, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, diff --git a/packages/g-plugin-device-renderer/src/drawcalls/Instanced.ts b/packages/g-plugin-device-renderer/src/drawcalls/Instanced.ts index 55022102b..b880db49d 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/Instanced.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/Instanced.ts @@ -12,7 +12,7 @@ import type { LightPool } from '../LightPool'; import type { Fog } from '../lights'; import type { Material } from '../materials'; import { MaterialEvent, ShaderMaterial } from '../materials'; -import type { BindingLayoutSamplerDescriptor, InputState } from '../platform'; +import type { BindingLayoutSamplerDescriptor } from '../platform'; import { ChannelWriteMask, CompareMode, @@ -21,7 +21,7 @@ import { MipFilterMode, StencilOp, TexFilterMode, - VertexBufferFrequency, + VertexStepMode, WrapMode, } from '../platform'; import type { RenderInst, RenderInstUniform, RenderHelper } from '../render'; @@ -34,8 +34,6 @@ import { import type { BatchContext } from '../renderer'; import type { Batch } from '../renderer/Batch'; import { RENDER_ORDER_SCALE } from '../renderer/Batch'; -import type { ProgramDescriptorSimpleWithOrig } from '../shader/compiler'; -import { preprocessProgramObj_GLSL } from '../shader/compiler'; import type { TexturePool } from '../TexturePool'; import { compareDefines, definedProps, enumToObject } from '../utils/enum'; import { packUint8ToFloat } from '../utils/compression'; @@ -127,16 +125,13 @@ export abstract class Instanced { clipPath: DisplayObject; clipPathTarget: DisplayObject; - private inputState: InputState; private program: DeviceProgram = new DeviceProgram(); - private programDescriptorSimpleWithOrig?: ProgramDescriptorSimpleWithOrig; geometryDirty = true; /** * the same material maybe shared between different canvases */ materialDirty = true; - private inputStateDirty = true; /** * texture mappings */ @@ -391,7 +386,7 @@ export abstract class Instanced { // this.bufferGeometry.setVertexBuffer({ // bufferIndex: 4, // byteStride: 4 * (3 * 3), - // frequency: VertexBufferFrequency.PerInstance, + // stepMode: VertexStepMode.INSTANCE, // attributes: [ // { // format: Format.F32_RGB, @@ -422,7 +417,7 @@ export abstract class Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.MODEL_MATRIX, byteStride: 4 * (4 * 4), - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, @@ -455,7 +450,7 @@ export abstract class Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.PACKED_COLOR, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, @@ -470,7 +465,7 @@ export abstract class Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.PACKED_STYLE, byteStride: 4 * 8, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, @@ -491,7 +486,7 @@ export abstract class Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.PICKING_COLOR, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, @@ -508,9 +503,6 @@ export abstract class Instanced { if (this.geometry) { this.geometry.destroy(); } - if (this.inputState) { - this.inputState.destroy(); - } } applyRenderInst(renderInst: RenderInst, objects: DisplayObject[]) { @@ -523,13 +515,13 @@ export abstract class Instanced { this.material.stencilWrite = true; // @see https://open.gl/depthstencils this.material.depthWrite = false; - this.material.stencilCompare = CompareMode.Always; - this.material.stencilPassOp = StencilOp.Replace; + this.material.stencilCompare = CompareMode.ALWAYS; + this.material.stencilPassOp = StencilOp.REPLACE; } else { this.material.stencilWrite = false; this.material.depthWrite = true; - this.material.stencilCompare = CompareMode.Equal; - this.material.stencilPassOp = StencilOp.Keep; + this.material.stencilCompare = CompareMode.EQUAL; + this.material.stencilPassOp = StencilOp.KEEP; } } else { this.material.stencilWrite = false; @@ -573,11 +565,11 @@ export abstract class Instanced { 'Material Texture ' + key, ); mapping.sampler = this.renderHelper.getCache().createSampler({ - wrapS: WrapMode.Clamp, - wrapT: WrapMode.Clamp, - minFilter: TexFilterMode.Point, - magFilter: TexFilterMode.Bilinear, - mipFilter: MipFilterMode.Linear, + wrapS: WrapMode.CLAMP, + wrapT: WrapMode.CLAMP, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.BILINEAR, + mipFilter: MipFilterMode.LINEAR, minLOD: 0, maxLOD: 0, }); @@ -625,11 +617,6 @@ export abstract class Instanced { // build shaders this.program.vert = this.material.vertexShader; this.program.frag = this.material.fragmentShader; - // use cached program - this.programDescriptorSimpleWithOrig = preprocessProgramObj_GLSL( - this.context.device, - this.program, - ); this.material.programDirty = false; this.materialDirty = false; } @@ -637,7 +624,6 @@ export abstract class Instanced { if (this.material.geometryDirty) { // wireframe 需要额外生成 geometry 重心坐标 this.geometryDirty = true; - this.inputStateDirty = true; this.material.geometryDirty = false; } @@ -657,41 +643,28 @@ export abstract class Instanced { // sync to internal Geometry this.geometryDirty = false; this.geometry.dirty = false; - this.inputStateDirty = true; } // cached input layout - const inputLayout = this.renderHelper - .getCache() - .createInputLayout(this.geometry.inputLayoutDescriptor); - const program = this.renderHelper .getCache() - .createProgramSimple(this.programDescriptorSimpleWithOrig); + .createProgramSimple(this.program); + const inputLayout = this.renderHelper.getCache().createInputLayout({ + ...this.geometry.inputLayoutDescriptor, + program, + }); const useIndexes = !!this.geometry.indexBuffer; - // prevent rebinding VAO too many times - if (this.inputStateDirty) { - if (this.inputState) { - this.inputState.destroy(); - } - this.inputState = this.context.device.createInputState( - inputLayout, - this.geometry.vertexBuffers.map((buffer) => ({ - buffer, - byteOffset: 0, - })), - useIndexes - ? { buffer: this.geometry.indexBuffer, byteOffset: 0 } - : null, - program, - ); - this.inputStateDirty = false; - } - renderInst.renderPipelineDescriptor.topology = this.geometry.drawMode; renderInst.setProgram(program); - renderInst.setInputLayoutAndState(inputLayout, this.inputState); + renderInst.setVertexInput( + inputLayout, + this.geometry.vertexBuffers.map((buffer) => ({ + buffer, + byteOffset: 0, + })), + useIndexes ? { buffer: this.geometry.indexBuffer, byteOffset: 0 } : null, + ); this.renderer.beforeUploadUBO(renderInst, this); // upload uniform buffer object @@ -966,7 +939,7 @@ export abstract class Instanced { // continue; // } - const { frequency, byteStride } = + const { stepMode, byteStride } = geometry.inputLayoutDescriptor.vertexBufferDescriptors[i]; const descriptor = @@ -980,7 +953,7 @@ export abstract class Instanced { geometry.setVertexBuffer({ bufferIndex, byteStride, - frequency, + stepMode, attributes: [ { format, @@ -1006,7 +979,7 @@ export abstract class Instanced { geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.BARYCENTRIC, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -1120,8 +1093,8 @@ export abstract class Instanced { { // should not affect color buffer when drawing stencil channelWriteMask: this.material.stencilWrite - ? ChannelWriteMask.None - : ChannelWriteMask.AllChannels, + ? ChannelWriteMask.NONE + : ChannelWriteMask.ALL, // channelWriteMask: ChannelWriteMask.AllChannels, rgbBlendState: { ...currentAttachmentsState.rgbBlendState, @@ -1242,11 +1215,11 @@ export abstract class Instanced { 'Fill Texture' + this.id, ); fillMapping.sampler = this.renderHelper.getCache().createSampler({ - wrapS: WrapMode.Clamp, - wrapT: WrapMode.Clamp, - minFilter: TexFilterMode.Point, - magFilter: TexFilterMode.Bilinear, - mipFilter: MipFilterMode.Linear, + wrapS: WrapMode.CLAMP, + wrapT: WrapMode.CLAMP, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.BILINEAR, + mipFilter: MipFilterMode.LINEAR, minLOD: 0, maxLOD: 0, }); diff --git a/packages/g-plugin-device-renderer/src/drawcalls/InstancedFill.ts b/packages/g-plugin-device-renderer/src/drawcalls/InstancedFill.ts index 2f642d51b..ffb533ce7 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/InstancedFill.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/InstancedFill.ts @@ -4,7 +4,7 @@ import { VertexAttributeBufferIndex, VertexAttributeLocation, } from './Instanced'; -import { Format, VertexBufferFrequency } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import meshFrag from '../shader/mesh.frag'; import meshVert from '../shader/mesh.vert'; import { updateBuffer } from './InstancedPath'; @@ -127,7 +127,7 @@ export class InstancedFillDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -141,7 +141,7 @@ export class InstancedFillDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.UV, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, diff --git a/packages/g-plugin-device-renderer/src/drawcalls/InstancedLine.ts b/packages/g-plugin-device-renderer/src/drawcalls/InstancedLine.ts index cfa88f1ab..99afa450f 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/InstancedLine.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/InstancedLine.ts @@ -11,7 +11,7 @@ import type { ParsedPolylineStyleProps, } from '@antv/g-lite'; import { DisplayObject, Shape, isDisplayObject } from '@antv/g-lite'; -import { Format, VertexBufferFrequency } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import frag from '../shader/instanced-line.frag'; import vert from '../shader/instanced-line.vert'; import { enumToObject } from '../utils/enum'; @@ -291,7 +291,7 @@ export class InstancedLineDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 5, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGB, @@ -311,7 +311,7 @@ export class InstancedLineDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: InstancedLineVertexAttributeBufferIndex.POINT, byteStride: 4 * (3 + 3), - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGB, @@ -332,7 +332,7 @@ export class InstancedLineDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: InstancedLineVertexAttributeBufferIndex.CAP, byteStride: 4 * 1, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_R, @@ -346,7 +346,7 @@ export class InstancedLineDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: InstancedLineVertexAttributeBufferIndex.DASH, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, diff --git a/packages/g-plugin-device-renderer/src/drawcalls/InstancedPath.ts b/packages/g-plugin-device-renderer/src/drawcalls/InstancedPath.ts index 019b78f54..294a584b4 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/InstancedPath.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/InstancedPath.ts @@ -17,7 +17,7 @@ import { import { mat4 } from 'gl-matrix'; import { arcToCubic } from '@antv/util'; import earcut from 'earcut'; -import { Format, VertexBufferFrequency } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import frag from '../shader/line.frag'; import vert from '../shader/line.vert'; import { enumToObject } from '../utils/enum'; @@ -222,7 +222,7 @@ export class InstancedPathDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: LineVertexAttributeBufferIndex.PACKED, byteStride: 4 * (4 + 4 + 4 + 4), - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGB, @@ -260,7 +260,7 @@ export class InstancedPathDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: LineVertexAttributeBufferIndex.VERTEX_NUM, byteStride: 4 * 1, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_R, @@ -275,7 +275,7 @@ export class InstancedPathDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: LineVertexAttributeBufferIndex.TRAVEL, byteStride: 4 * 1, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_R, @@ -294,7 +294,7 @@ export class InstancedPathDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: LineVertexAttributeBufferIndex.DASH, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RGBA, diff --git a/packages/g-plugin-device-renderer/src/drawcalls/SDF.ts b/packages/g-plugin-device-renderer/src/drawcalls/SDF.ts index 4bfecaa80..d30aaf146 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/SDF.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/SDF.ts @@ -8,7 +8,7 @@ import type { ParsedRectStyleProps, } from '@antv/g-lite'; import { Shape } from '@antv/g-lite'; -import { Format, VertexBufferFrequency } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import frag from '../shader/sdf.frag'; import vert from '../shader/sdf.vert'; import { enumToObject } from '../utils/enum'; @@ -92,7 +92,7 @@ export class SDFDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 4, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, @@ -112,7 +112,7 @@ export class SDFDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: SDFVertexAttributeBufferIndex.PACKED_STYLE, byteStride: 4 * 6, - frequency: VertexBufferFrequency.PerInstance, + stepMode: VertexStepMode.INSTANCE, attributes: [ { format: Format.F32_RG, diff --git a/packages/g-plugin-device-renderer/src/drawcalls/Text.ts b/packages/g-plugin-device-renderer/src/drawcalls/Text.ts index 26a1e9d0f..b90957d63 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/Text.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/Text.ts @@ -6,7 +6,7 @@ import type { } from '@antv/g-lite'; import { isCSSRGB } from '@antv/g-lite'; import { mat4 } from 'gl-matrix'; -import { CullMode, Format, VertexBufferFrequency } from '../platform'; +import { CullMode, Format, VertexStepMode } from '../platform'; import { RENDER_ORDER_SCALE } from '../renderer/Batch'; import frag from '../shader/text.frag'; import vert from '../shader/text.vert'; @@ -178,7 +178,7 @@ export class TextDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: TextVertexAttributeBufferIndex.INSTANCED, byteStride: 4 * (4 * 4 + 4 + 4 + 4 + 4), // 32 - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGBA, @@ -227,7 +227,7 @@ export class TextDrawcall extends Instanced { this.geometry.setVertexBuffer({ bufferIndex: TextVertexAttributeBufferIndex.TEX, byteStride: 4 * (2 + 2), - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, @@ -247,7 +247,7 @@ export class TextDrawcall extends Instanced { protected createMaterial(objects: DisplayObject[]): void { this.material.vertexShader = vert; this.material.fragmentShader = frag; - this.material.cullMode = CullMode.Back; + this.material.cullMode = CullMode.BACK; this.material.defines = { ...this.material.defines, ...enumToObject(TextVertexAttributeLocation), diff --git a/packages/g-plugin-device-renderer/src/drawcalls/symbol/GlyphManager.ts b/packages/g-plugin-device-renderer/src/drawcalls/symbol/GlyphManager.ts index 2281ae49f..7941554f8 100644 --- a/packages/g-plugin-device-renderer/src/drawcalls/symbol/GlyphManager.ts +++ b/packages/g-plugin-device-renderer/src/drawcalls/symbol/GlyphManager.ts @@ -175,7 +175,7 @@ export class GlyphManager { }, immutable: false, }); - this.glyphAtlasTexture.setImageData([data], 0); + this.glyphAtlasTexture.setImageData([data]); } } diff --git a/packages/g-plugin-device-renderer/src/geometries/BufferGeometry.ts b/packages/g-plugin-device-renderer/src/geometries/BufferGeometry.ts index dcaaf9c41..fab8e03cf 100644 --- a/packages/g-plugin-device-renderer/src/geometries/BufferGeometry.ts +++ b/packages/g-plugin-device-renderer/src/geometries/BufferGeometry.ts @@ -5,7 +5,7 @@ import type { Buffer, Device, InputLayoutDescriptor, - VertexBufferFrequency, + VertexStepMode, } from '../platform'; import { BufferFrequencyHint, @@ -18,7 +18,7 @@ export function makeDataBuffer( device: Device, usage: BufferUsage, data: ArrayBufferLike, - hint = BufferFrequencyHint.Static, + hint = BufferFrequencyHint.STATIC, ): Buffer { const buffer = device.createBuffer({ viewOrSize: data.byteLength, @@ -34,7 +34,7 @@ export type IndicesArray = number[] | Int32Array | Uint32Array | Uint16Array; export interface GeometryVertexBufferDescriptor { bufferIndex: number; byteStride: number; - frequency: VertexBufferFrequency; + stepMode: VertexStepMode; attributes: { format: Format; bufferByteOffset: number; @@ -68,7 +68,7 @@ export class BufferGeometry extends EventEmitter { /** * 绘制模式 */ - drawMode: PrimitiveTopology = PrimitiveTopology.Triangles; + drawMode: PrimitiveTopology = PrimitiveTopology.TRIANGLES; /** * 存放 Attribute Buffer 列表 @@ -90,7 +90,8 @@ export class BufferGeometry extends EventEmitter { inputLayoutDescriptor: InputLayoutDescriptor = { vertexBufferDescriptors: [], vertexAttributeDescriptors: [], - indexBufferFormat: Format.U32_R, + indexBufferFormat: null, + program: null, }; vertexCount = 0; @@ -136,15 +137,17 @@ export class BufferGeometry extends EventEmitter { this.indices = indices; + this.inputLayoutDescriptor.indexBufferFormat = Format.U32_R; + return this; } setVertexBuffer(descriptor: GeometryVertexBufferDescriptor) { - const { bufferIndex, byteStride, frequency, attributes, data } = descriptor; + const { bufferIndex, byteStride, stepMode, attributes, data } = descriptor; this.inputLayoutDescriptor.vertexBufferDescriptors[bufferIndex] = { byteStride, - frequency, + stepMode, }; this.vertices[bufferIndex] = data; @@ -158,7 +161,6 @@ export class BufferGeometry extends EventEmitter { if (existed) { existed.format = format; existed.bufferByteOffset = bufferByteOffset; - existed.byteStride = byteStride; existed.divisor = divisor; } else { this.inputLayoutDescriptor.vertexAttributeDescriptors.push({ @@ -166,7 +168,6 @@ export class BufferGeometry extends EventEmitter { bufferIndex, bufferByteOffset, location, - byteStride, divisor, }); } @@ -182,7 +183,7 @@ export class BufferGeometry extends EventEmitter { this.device, BufferUsage.VERTEX, data.buffer, - BufferFrequencyHint.Dynamic, + BufferFrequencyHint.DYNAMIC, ); this.vertexBuffers[bufferIndex] = buffer; diff --git a/packages/g-plugin-device-renderer/src/materials/Material.ts b/packages/g-plugin-device-renderer/src/materials/Material.ts index 5a15716f5..b61a74aa8 100644 --- a/packages/g-plugin-device-renderer/src/materials/Material.ts +++ b/packages/g-plugin-device-renderer/src/materials/Material.ts @@ -10,7 +10,7 @@ import type { CullMode, Device, Format, - FrontFaceMode, + FrontFace, StencilOp, Texture, } from '../platform'; @@ -38,7 +38,7 @@ export interface IMaterial { stencilPassOp: StencilOp; stencilRef: number; - frontFace: FrontFaceMode; + frontFace: FrontFace; // @see https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/polygonOffset polygonOffset: boolean; diff --git a/packages/g-plugin-device-renderer/src/passes/Copy.ts b/packages/g-plugin-device-renderer/src/passes/Copy.ts index 891bbf544..2a1d09e22 100644 --- a/packages/g-plugin-device-renderer/src/passes/Copy.ts +++ b/packages/g-plugin-device-renderer/src/passes/Copy.ts @@ -1,5 +1,5 @@ // import { BufferGeometry } from '../geometries'; -// import { Format, InputLayout, InputState, VertexBufferFrequency } from '../platform'; +// import { Format, InputLayout, InputState, VertexStepMode } from '../platform'; // import { fullscreenMegaState, nArray } from '../platform/utils'; import { DeviceProgram } from '../render/DeviceProgram'; // import { RGAttachmentSlot, RGGraphBuilder } from '../render/interfaces'; @@ -54,7 +54,7 @@ export class CopyProgram extends DeviceProgram { // geometry.setVertexBuffer({ // bufferIndex: 0, // byteStride: 4 * 2, -// frequency: VertexBufferFrequency.PerVertex, +// stepMode: VertexStepMode.VERTEX, // attributes: [ // { // format: Format.F32_RG, diff --git a/packages/g-plugin-device-renderer/src/passes/FXAA.ts b/packages/g-plugin-device-renderer/src/passes/FXAA.ts index b102dc31a..5ffde2303 100644 --- a/packages/g-plugin-device-renderer/src/passes/FXAA.ts +++ b/packages/g-plugin-device-renderer/src/passes/FXAA.ts @@ -1,6 +1,6 @@ import { BufferGeometry } from '../geometries'; -import type { InputLayout, InputState } from '../platform'; -import { Format, VertexBufferFrequency } from '../platform'; +import type { InputLayout } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import { fullscreenMegaState, nArray } from '../platform/utils'; import { DeviceProgram } from '../render/DeviceProgram'; import type { RGGraphBuilder } from '../render/interfaces'; @@ -32,7 +32,6 @@ interface RenderInput { const textureMapping = nArray(1, () => new TextureMapping()); let geometry: BufferGeometry; -let inputState: InputState; let inputLayout: InputLayout; export function pushFXAAPass( @@ -68,7 +67,7 @@ export function pushFXAAPass( ); const fxaaProgram = new FXAAProgram(); - const program = renderHelper.renderCache.createProgram(fxaaProgram); + const program = renderHelper.renderCache.createProgramSimple(fxaaProgram); renderInst.setProgram(program); @@ -77,7 +76,7 @@ export function pushFXAAPass( geometry.setVertexBuffer({ bufferIndex: 0, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, @@ -94,24 +93,21 @@ export function pushFXAAPass( inputLayout = renderHelper .getCache() .createInputLayout(geometry.inputLayoutDescriptor); + } - inputState = renderHelper.getDevice().createInputState( + pass.exec((passRenderer, scope) => { + textureMapping[0].texture = scope.getResolveTextureForID( + mainColorResolveTextureID, + ); + renderInst.setSamplerBindingsFromTextureMappings(textureMapping); + renderInst.setVertexInput( inputLayout, geometry.vertexBuffers.map((buffer) => ({ buffer, byteOffset: 0, })), null, - program, ); - } - - pass.exec((passRenderer, scope) => { - textureMapping[0].texture = scope.getResolveTextureForID( - mainColorResolveTextureID, - ); - renderInst.setSamplerBindingsFromTextureMappings(textureMapping); - renderInst.setInputLayoutAndState(inputLayout, inputState); renderInst.drawOnPass(renderHelper.renderCache, passRenderer); }); }); diff --git a/packages/g-plugin-device-renderer/src/passes/PostProcessing.ts b/packages/g-plugin-device-renderer/src/passes/PostProcessing.ts index b65772272..b0e2e7514 100644 --- a/packages/g-plugin-device-renderer/src/passes/PostProcessing.ts +++ b/packages/g-plugin-device-renderer/src/passes/PostProcessing.ts @@ -1,6 +1,6 @@ import { BufferGeometry } from '../geometries'; -import type { InputLayout, InputState, Program } from '../platform'; -import { Format, VertexBufferFrequency } from '../platform'; +import type { InputLayout, Program } from '../platform'; +import { Format, VertexStepMode } from '../platform'; import { fullscreenMegaState } from '../platform/utils'; import type { DeviceProgram, TextureMapping, RenderHelper } from '../render'; import { fillVec4 } from '../render'; @@ -15,19 +15,20 @@ export class PostProcessing { protected debugName: string; protected geometry: BufferGeometry; - protected inputState: InputState; protected inputLayout: InputLayout; protected textureMappings: TextureMapping[] = []; protected program: Program; init() { - const program = this.renderHelper.renderCache.createProgram(this.deviceProgram); + this.program = this.renderHelper.renderCache.createProgramSimple( + this.deviceProgram, + ); this.geometry = new BufferGeometry(this.renderHelper.getDevice()); this.geometry.setVertexBuffer({ bufferIndex: 0, byteStride: 4 * 2, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RG, @@ -44,24 +45,19 @@ export class PostProcessing { this.inputLayout = this.renderHelper .getCache() .createInputLayout(this.geometry.inputLayoutDescriptor); - - this.inputState = this.renderHelper.getDevice().createInputState( - this.inputLayout, - this.geometry.vertexBuffers.map((buffer) => ({ - buffer, - byteOffset: 0, - })), - null, - program, - ); } - build(builder: RGGraphBuilder, renderInput: RenderInput, mainColorTargetID: number) { + build( + builder: RGGraphBuilder, + renderInput: RenderInput, + mainColorTargetID: number, + ) { builder.pushPass((pass) => { pass.setDebugName(this.debugName); pass.attachRenderTargetID(RGAttachmentSlot.Color0, mainColorTargetID); - const mainColorResolveTextureID = builder.resolveRenderTarget(mainColorTargetID); + const mainColorResolveTextureID = + builder.resolveRenderTarget(mainColorTargetID); pass.attachResolveTexture(mainColorResolveTextureID); const renderInst = this.renderHelper.renderInstManager.newRenderInst(); @@ -76,14 +72,32 @@ export class PostProcessing { // since gl_VertexID is not available in GLSL 100, we need to use a geometry const offs = renderInst.allocateUniformBuffer(0, 4); const d = renderInst.mapUniformBufferF32(0); - fillVec4(d, offs, 1.0 / renderInput.backbufferWidth, 1.0 / renderInput.backbufferHeight); + fillVec4( + d, + offs, + 1.0 / renderInput.backbufferWidth, + 1.0 / renderInput.backbufferHeight, + ); renderInst.setProgram(this.program); pass.exec((passRenderer, scope) => { - this.textureMappings[0].texture = scope.getResolveTextureForID(mainColorResolveTextureID); + this.textureMappings[0].texture = scope.getResolveTextureForID( + mainColorResolveTextureID, + ); renderInst.setSamplerBindingsFromTextureMappings(this.textureMappings); - renderInst.setInputLayoutAndState(this.inputLayout, this.inputState); + + // this.inputState = this.renderHelper.getDevice().createInput( + // this.inputLayout, + // this.geometry.vertexBuffers.map((buffer) => ({ + // buffer, + // byteOffset: 0, + // })), + // null, + // program, + // ); + + // renderInst.setVertexInput(this.inputLayout, this.inputState); renderInst.drawOnPass(this.renderHelper.renderCache, passRenderer); }); }); @@ -92,6 +106,5 @@ export class PostProcessing { destroy() { this.geometry.destroy(); this.inputLayout.destroy(); - this.inputState.destroy(); } } diff --git a/packages/g-plugin-device-renderer/src/platform/format.ts b/packages/g-plugin-device-renderer/src/platform/format.ts index e25b388cd..199670c21 100644 --- a/packages/g-plugin-device-renderer/src/platform/format.ts +++ b/packages/g-plugin-device-renderer/src/platform/format.ts @@ -21,6 +21,7 @@ export enum FormatTypeFlags { // Special-case packed texture formats. U16_PACKED_5551 = 0x61, + U16_PACKED_565, // Depth/stencil texture formats. D24 = 0x81, @@ -217,6 +218,11 @@ export enum Format { FormatCompFlags.RGBA, FormatFlags.Normalized, ), + U16_RGB_565 = makeFormat( + FormatTypeFlags.U16_PACKED_565, + FormatCompFlags.RGB, + FormatFlags.Normalized, + ), // Compressed BC1 = makeFormat( diff --git a/packages/g-plugin-device-renderer/src/platform/interfaces.ts b/packages/g-plugin-device-renderer/src/platform/interfaces.ts index 692c78b20..bb44577f4 100644 --- a/packages/g-plugin-device-renderer/src/platform/interfaces.ts +++ b/packages/g-plugin-device-renderer/src/platform/interfaces.ts @@ -10,7 +10,6 @@ export enum ResourceType { Program, Bindings, InputLayout, - InputState, RenderPipeline, ComputePipeline, Readback, @@ -37,7 +36,7 @@ export interface Buffer extends ResourceBase { export interface Texture extends ResourceBase { type: ResourceType.Texture; setImageData: ( - data: TexImageSource | ArrayBufferView[], + data: (TexImageSource | ArrayBufferView)[], firstMipLevel?: number, ) => void; } @@ -49,6 +48,7 @@ export interface Sampler extends ResourceBase { } export interface Program extends ResourceBase { type: ResourceType.Program; + setUniformsLegacy: (uniforms: Record) => void; } export interface Bindings extends ResourceBase { type: ResourceType.Bindings; @@ -56,9 +56,6 @@ export interface Bindings extends ResourceBase { export interface InputLayout extends ResourceBase { type: ResourceType.InputLayout; } -export interface InputState extends ResourceBase { - type: ResourceType.InputState; -} export interface RenderPipeline extends ResourceBase { type: ResourceType.RenderPipeline; } @@ -112,73 +109,141 @@ export type Resource = | Program | Bindings | InputLayout - | InputState | RenderPipeline | ComputePipeline | Readback; export enum CompareMode { - Never = GL.NEVER, - Less = GL.LESS, - Equal = GL.EQUAL, - LessEqual = GL.LEQUAL, - Greater = GL.GREATER, - NotEqual = GL.NOTEQUAL, - GreaterEqual = GL.GEQUAL, - Always = GL.ALWAYS, + NEVER = GL.NEVER, + LESS = GL.LESS, + EQUAL = GL.EQUAL, + LEQUAL = GL.LEQUAL, + GREATER = GL.GREATER, + NOTEQUAL = GL.NOTEQUAL, + GEQUAL = GL.GEQUAL, + ALWAYS = GL.ALWAYS, } -export enum FrontFaceMode { +export enum FrontFace { CCW = GL.CCW, CW = GL.CW, } export enum CullMode { - None, - Front, - Back, - FrontAndBack, + NONE, + FRONT, + BACK, + FRONT_AND_BACK, } +/** + * Blend factor RGBA components. + * @see https://www.w3.org/TR/webgpu/#enumdef-gpublendfactor + */ export enum BlendFactor { - Zero = GL.ZERO, - One = GL.ONE, - Src = GL.SRC_COLOR, - OneMinusSrc = GL.ONE_MINUS_SRC_COLOR, - Dst = GL.DST_COLOR, - OneMinusDst = GL.ONE_MINUS_DST_COLOR, - SrcAlpha = GL.SRC_ALPHA, - OneMinusSrcAlpha = GL.ONE_MINUS_SRC_ALPHA, - DstAlpha = GL.DST_ALPHA, - OneMinusDstAlpha = GL.ONE_MINUS_DST_ALPHA, + /** + * (0, 0, 0, 0) + */ + ZERO = GL.ZERO, + /** + * (1, 1, 1, 1) + */ + ONE = GL.ONE, + /** + * (Rsrc, Gsrc, Bsrc, Asrc) + */ + SRC = GL.SRC_COLOR, + /** + * (1 - Rsrc, 1 - Gsrc, 1 - Bsrc, 1 - Asrc) + */ + ONE_MINUS_SRC = GL.ONE_MINUS_SRC_COLOR, + /** + * (Rdst, Gdst, Bdst, Adst) + */ + DST = GL.DST_COLOR, + /** + * (1 - Rdst, 1 - Gdst, 1 - Bdst, 1 - Adst) + */ + ONE_MINUS_DST = GL.ONE_MINUS_DST_COLOR, + /** + * (Asrc, Asrc, Asrc, Asrc) + */ + SRC_ALPHA = GL.SRC_ALPHA, + /** + * (1 - Asrc, 1 - Asrc, 1 - Asrc, 1 - Asrc) + */ + ONE_MINUS_SRC_ALPHA = GL.ONE_MINUS_SRC_ALPHA, + /** + * (Adst, Adst, Adst, Adst) + */ + DST_ALPHA = GL.DST_ALPHA, + /** + * (1 - Adst, 1 - Adst, 1 - Adst, 1 - Adst) + */ + ONE_MINUS_DST_ALPHA = GL.ONE_MINUS_DST_ALPHA, + /** + * (Rconst, Gconst, Bconst, Aconst) + */ + CONST = GL.CONSTANT_COLOR, + /** + * (1 - Rconst, 1 - Gconst, 1 - Bconst, 1 - Aconst) + */ + ONE_MINUS_CONSTANT = GL.ONE_MINUS_CONSTANT_COLOR, + /** + * (min(Asrc, 1 - Adst), min(Asrc, 1 - Adst), min(Asrc, 1 - Adst), 1) + */ + SRC_ALPHA_SATURATE = GL.SRC_ALPHA_SATURATE, } +/** + * Defines the algorithm used to combine source and destination blend factors. + * @see https://www.w3.org/TR/webgpu/#enumdef-gpublendoperation + */ export enum BlendMode { - Add = GL.FUNC_ADD, - Subtract = GL.FUNC_SUBTRACT, - ReverseSubtract = GL.FUNC_REVERSE_SUBTRACT, + /** + * RGBAsrc × RGBAsrcFactor + RGBAdst × RGBAdstFactor + */ + ADD = GL.FUNC_ADD, + /** + * RGBAsrc × RGBAsrcFactor - RGBAdst × RGBAdstFactor + */ + SUBSTRACT = GL.FUNC_SUBTRACT, + /** + * RGBAdst × RGBAdstFactor - RGBAsrc × RGBAsrcFactor + */ + REVERSE_SUBSTRACT = GL.FUNC_REVERSE_SUBTRACT, + // TODO: WebGL 1 should use ext + // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquation#parameters + /** + * min(RGBAsrc, RGBAdst) + */ + MIN = GL.MIN, + /** + * max(RGBAsrc, RGBAdst) + */ + MAX = GL.MAX, } export enum WrapMode { - Clamp, - Repeat, - Mirror, + CLAMP, + REPEAT, + MIRROR, } export enum TexFilterMode { - Point, - Bilinear, + POINT, + BILINEAR, } export enum MipFilterMode { - NoMip, - Nearest, - Linear, + NO_MIP, + NEAREST, + LINEAR, } export enum PrimitiveTopology { - Points, - Triangles, - TriangleStrip, - Lines, - LineStrip, + POINTS, + TRIANGLES, + TRIANGLE_STRIP, + LINES, + LINE_STRIP, } /** @@ -207,13 +272,16 @@ export enum BufferUsage { } export enum BufferFrequencyHint { - Static = 0x01, - Dynamic = 0x02, + STATIC = 0x01, + DYNAMIC = 0x02, } -export enum VertexBufferFrequency { - PerVertex = 0x01, - PerInstance = 0x02, +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpuvertexstepmode + */ +export enum VertexStepMode { + VERTEX = 0x01, + INSTANCE = 0x02, } export enum TextureEvent { @@ -221,42 +289,44 @@ export enum TextureEvent { } export enum TextureDimension { - n2D, - n2DArray, - n3D, - Cube, + TEXTURE_2D, + TEXTURE_2D_ARRAY, + TEXTURE_3D, + TEXTURE_CUBE_MAP, } export enum TextureUsage { - Sampled = 0x01, - RenderTarget = 0x02, + SAMPLED = 0x01, + RENDER_TARGET = 0x02, } export enum ChannelWriteMask { - None = 0x00, - Red = 0x01, - Green = 0x02, - Blue = 0x04, - Alpha = 0x08, - + NONE = 0x00, + RED = 0x01, + GREEN = 0x02, + BLUE = 0x04, + ALPHA = 0x08, RGB = 0x07, - AllChannels = 0x0f, + ALL = 0x0f, } +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpustenciloperation + */ export enum StencilOp { - Keep = GL.KEEP, - Zero = GL.ZERO, - Replace = GL.REPLACE, - Invert = GL.INVERT, - IncrementClamp = GL.INCR, - DecrementClamp = GL.DECR, - IncrementWrap = GL.INCR_WRAP, - DecrementWrap = GL.DECR_WRAP, + KEEP = GL.KEEP, + ZERO = GL.ZERO, + REPLACE = GL.REPLACE, + INVERT = GL.INVERT, + INCREMENT_CLAMP = GL.INCR, + DECREMENT_CLAMP = GL.DECR, + INCREMENT_WRAP = GL.INCR_WRAP, + DECREMENT_WRAP = GL.DECR_WRAP, } export interface VertexBufferDescriptor { buffer: Buffer; - byteOffset: number; + byteOffset?: number; } export type IndexBufferDescriptor = VertexBufferDescriptor; @@ -266,22 +336,24 @@ export interface VertexAttributeDescriptor { format: Format; bufferIndex: number; bufferByteOffset: number; - byteStride?: number; divisor?: number; } export interface InputLayoutBufferDescriptor { byteStride: number; - frequency: VertexBufferFrequency; + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpuvertexbufferlayout-stepmode + */ + stepMode: VertexStepMode; } export interface TextureDescriptor { - dimension: TextureDimension; + dimension?: TextureDimension; pixelFormat: Format; width: number; height: number; - depth: number; - numLevels: number; + depth?: number; + numLevels?: number; usage: TextureUsage; immutable?: boolean; pixelStore?: Partial<{ @@ -297,9 +369,9 @@ export function makeTextureDescriptor2D( height: number, numLevels: number, ): TextureDescriptor { - const dimension = TextureDimension.n2D, + const dimension = TextureDimension.TEXTURE_2D, depth = 1; - const usage = TextureUsage.Sampled; + const usage = TextureUsage.SAMPLED; return { dimension, pixelFormat, width, height, depth, numLevels, usage }; } @@ -320,7 +392,7 @@ export interface RenderTargetDescriptor { pixelFormat: Format; width: number; height: number; - sampleCount: number; + sampleCount?: number; texture?: Texture; } @@ -332,7 +404,6 @@ export interface BufferBinding { export interface SamplerBinding { texture: Texture | null; sampler: Sampler | null; - lateBinding: string | null; } export enum SamplerFormatKind { @@ -370,6 +441,23 @@ export interface BindingsDescriptor { storageBufferBindings?: BufferBinding[]; } +/** + * Support the following shaderStage: vertex | fragment | compute. + */ +export interface ProgramDescriptor { + vertex?: { + glsl?: string; + wgsl?: string; + }; + fragment?: { + glsl?: string; + wgsl?: string; + }; + compute?: { + wgsl: string; + }; +} + export interface ProgramDescriptorSimple { vert?: string; frag?: string; @@ -378,15 +466,14 @@ export interface ProgramDescriptorSimple { preprocessedCompute?: string; } -export interface ProgramDescriptor extends ProgramDescriptorSimple { - ensurePreprocessed: (vendorInfo: VendorInfo) => void; - associate: (device: Device, program: Program) => void; -} - export interface InputLayoutDescriptor { vertexBufferDescriptors: (InputLayoutBufferDescriptor | null)[]; vertexAttributeDescriptors: VertexAttributeDescriptor[]; indexBufferFormat: Format | null; + /** + * Read attributes from linked program. + */ + program: Program; } export interface ChannelBlendState { @@ -396,39 +483,39 @@ export interface ChannelBlendState { } export interface AttachmentState { - channelWriteMask: ChannelWriteMask; + channelWriteMask?: ChannelWriteMask; rgbBlendState: ChannelBlendState; alphaBlendState: ChannelBlendState; } export interface MegaStateDescriptor { attachmentsState: AttachmentState[]; - blendConstant: Color; - depthCompare: CompareMode; - depthWrite: boolean; - stencilCompare: CompareMode; - stencilWrite: boolean; - stencilPassOp: StencilOp; - stencilRef: number; - cullMode: CullMode; - frontFace: FrontFaceMode; - polygonOffset: boolean; + blendConstant?: Color; + depthCompare?: CompareMode; + depthWrite?: boolean; + stencilCompare?: CompareMode; + stencilWrite?: boolean; + stencilPassOp?: StencilOp; + stencilRef?: number; + cullMode?: CullMode; + frontFace?: FrontFace; + polygonOffset?: boolean; } export interface PipelineDescriptor { - bindingLayouts: BindingLayoutDescriptor[]; + bindingLayouts?: BindingLayoutDescriptor[]; inputLayout: InputLayout | null; program: Program; } export interface RenderPipelineDescriptor extends PipelineDescriptor { - topology: PrimitiveTopology; - megaStateDescriptor: MegaStateDescriptor; + topology?: PrimitiveTopology; + megaStateDescriptor?: MegaStateDescriptor; // Attachment data. colorAttachmentFormats: (Format | null)[]; - depthStencilAttachmentFormat: Format | null; - sampleCount: number; + depthStencilAttachmentFormat?: Format | null; + sampleCount?: number; } export type ComputePipelineDescriptor = PipelineDescriptor; @@ -442,25 +529,25 @@ export interface Color { export interface RenderPassDescriptor { colorAttachment: (RenderTarget | null)[]; - colorAttachmentLevel: number[]; - colorClearColor: (Color | 'load')[]; + colorAttachmentLevel?: number[]; + colorClearColor?: (Color | 'load')[]; colorResolveTo: (Texture | null)[]; - colorResolveToLevel: number[]; - colorStore: boolean[]; - depthStencilAttachment: RenderTarget | null; - depthStencilResolveTo: Texture | null; - depthStencilStore: boolean; - depthClearValue: number | 'load'; - stencilClearValue: number | 'load'; - - // Query system. - occlusionQueryPool: QueryPool | null; + colorResolveToLevel?: number[]; + colorStore?: boolean[]; + depthStencilAttachment?: RenderTarget | null; + depthStencilResolveTo?: Texture | null; + depthStencilStore?: boolean; + depthClearValue?: number | 'load'; + stencilClearValue?: number | 'load'; + occlusionQueryPool?: QueryPool | null; } export interface DeviceLimits { uniformBufferWordAlignment: number; uniformBufferMaxPageWordSize: number; readonly supportedSampleCounts: number[]; + occlusionQueriesRecommended: boolean; + computeShadersSupported: boolean; } export interface DebugGroup { @@ -472,13 +559,13 @@ export interface DebugGroup { } export enum ViewportOrigin { - LowerLeft, - UpperLeft, + LOWER_LEFT, + UPPER_LEFT, } export enum ClipSpaceNearZ { - NegativeOne, - Zero, + NEGATIVE_ONE, + ZERO, } export interface VendorInfo { @@ -505,7 +592,16 @@ export interface SwapChain { getOnscreenTexture: () => Texture; } -export interface RenderPass { +/** + * @see https://www.w3.org/TR/webgpu/#debug-markers + */ +interface DebugCommandsMixin { + pushDebugGroup: (groupLabel: string) => void; + popDebugGroup: () => void; + insertDebugMarker: (markerLabel: string) => void; +} + +export interface RenderPass extends DebugCommandsMixin { // State management. setViewport: (x: number, y: number, w: number, h: number) => void; setScissor: (x: number, y: number, w: number, h: number) => void; @@ -515,28 +611,47 @@ export interface RenderPass { bindings: Bindings, dynamicByteOffsets: number[], ) => void; - setInputState: (inputState: InputState | null) => void; + setVertexInput: ( + inputLayout: InputLayout | null, + buffers: (VertexBufferDescriptor | null)[] | null, + indexBuffer: IndexBufferDescriptor | null, + ) => void; setStencilRef: (value: number) => void; // Draw commands. - draw: (vertexCount: number, firstVertex: number) => void; - drawIndexed: (indexCount: number, firstIndex: number) => void; - drawIndexedInstanced: ( + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-draw + */ + draw: ( + vertexCount: number, + instanceCount?: number, + firstVertex?: number, + firstInstance?: number, + ) => void; + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindexed + */ + drawIndexed: ( indexCount: number, - firstIndex: number, - instanceCount: number, + instanceCount?: number, + firstIndex?: number, + baseVertex?: number, + firstInstance?: number, ) => void; + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindirect + */ + drawIndirect: (indirectBuffer: Buffer, indirectOffset: number) => void; // Query system. beginOcclusionQuery: (dstOffs: number) => void; endOcclusionQuery: (dstOffs: number) => void; - - // Debug. - beginDebugGroup: (name: string) => void; - endDebugGroup: () => void; } -export interface ComputePass { +/** + * @see https://www.w3.org/TR/webgpu/#compute-passes + */ +export interface ComputePass extends DebugCommandsMixin { setPipeline: (pipeline: ComputePipeline) => void; setBindings: ( bindingLayoutIndex: number, @@ -544,12 +659,20 @@ export interface ComputePass { dynamicByteOffsets: number[], ) => void; /** - * @see https://www.w3.org/TR/webgpu/#compute-pass-encoder-dispatch + * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatchworkgroups + */ + dispatchWorkgroups: ( + workgroupCountX: number, + workgroupCountY?: number, + workgroupCountZ?: number, + ) => void; + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatchworkgroupsindirect */ - dispatch: (x: number, y?: number, z?: number) => void; - // Debug. - beginDebugGroup: (name: string) => void; - endDebugGroup: () => void; + dispatchWorkgroupsIndirect: ( + indirectBuffer: Buffer, + indirectOffset: number, + ) => void; } export enum QueryPoolType { @@ -580,17 +703,10 @@ export interface Device { createRenderTarget: (descriptor: RenderTargetDescriptor) => RenderTarget; createRenderTargetFromTexture: (texture: Texture) => RenderTarget; createProgram: (program: ProgramDescriptor) => Program; - createProgramSimple: (program: ProgramDescriptorSimple) => Program; createBindings: (bindingsDescriptor: BindingsDescriptor) => Bindings; createInputLayout: ( inputLayoutDescriptor: InputLayoutDescriptor, ) => InputLayout; - createInputState: ( - inputLayout: InputLayout, - buffers: (VertexBufferDescriptor | null)[], - indexBuffer: IndexBufferDescriptor | null, - program?: Program, - ) => InputState; createRenderPipeline: ( descriptor: RenderPipelineDescriptor, ) => RenderPipeline; @@ -606,6 +722,7 @@ export interface Device { beginFrame(): void; endFrame(): void; submitPass: (pass: RenderPass | ComputePass) => void; + destroy(): void; // Render pipeline compilation control. pipelineQueryReady: (o: RenderPipeline) => boolean; @@ -636,7 +753,5 @@ export interface Device { setResourceName: (o: Resource, s: string) => void; setResourceLeakCheck: (o: Resource, v: boolean) => void; checkForLeaks: () => void; - programPatched: (o: Program, descriptor: ProgramDescriptorSimple) => void; - pushDebugGroup: (debugGroup: DebugGroup) => void; - popDebugGroup: () => void; + programPatched: (o: Program, descriptor: ProgramDescriptor) => void; } diff --git a/packages/g-plugin-device-renderer/src/platform/utils/depth.ts b/packages/g-plugin-device-renderer/src/platform/utils/depth.ts index 373aa769d..1282f2fd1 100644 --- a/packages/g-plugin-device-renderer/src/platform/utils/depth.ts +++ b/packages/g-plugin-device-renderer/src/platform/utils/depth.ts @@ -32,14 +32,14 @@ export function reverseDepthForCompareMode( ): CompareMode { if (isDepthReversed) { switch (compareMode) { - case CompareMode.Less: - return CompareMode.Greater; - case CompareMode.LessEqual: - return CompareMode.GreaterEqual; - case CompareMode.GreaterEqual: - return CompareMode.LessEqual; - case CompareMode.Greater: - return CompareMode.Less; + case CompareMode.LESS: + return CompareMode.GREATER; + case CompareMode.LEQUAL: + return CompareMode.GEQUAL; + case CompareMode.GEQUAL: + return CompareMode.LEQUAL; + case CompareMode.GREATER: + return CompareMode.LESS; default: return compareMode; } @@ -48,7 +48,10 @@ export function reverseDepthForCompareMode( } } -export function reverseDepthForClearValue(n: number, isDepthReversed = IsDepthReversed): number { +export function reverseDepthForClearValue( + n: number, + isDepthReversed = IsDepthReversed, +): number { if (isDepthReversed) { return 1.0 - n; } else { @@ -56,7 +59,10 @@ export function reverseDepthForClearValue(n: number, isDepthReversed = IsDepthRe } } -export function reverseDepthForDepthOffset(n: number, isDepthReversed = IsDepthReversed): number { +export function reverseDepthForDepthOffset( + n: number, + isDepthReversed = IsDepthReversed, +): number { if (isDepthReversed) { return -n; } else { @@ -71,9 +77,9 @@ export function compareDepthValues( isDepthReversed = IsDepthReversed, ): boolean { op = reverseDepthForCompareMode(op, isDepthReversed); - if (op === CompareMode.Less) return a < b; - else if (op === CompareMode.LessEqual) return a <= b; - else if (op === CompareMode.Greater) return a > b; - else if (op === CompareMode.GreaterEqual) return a >= b; + if (op === CompareMode.LESS) return a < b; + else if (op === CompareMode.LEQUAL) return a <= b; + else if (op === CompareMode.GREATER) return a > b; + else if (op === CompareMode.GEQUAL) return a >= b; else throw new Error('whoops'); } diff --git a/packages/g-plugin-device-renderer/src/platform/utils/hash.ts b/packages/g-plugin-device-renderer/src/platform/utils/hash.ts index b2c421b51..1b9d86e2a 100644 --- a/packages/g-plugin-device-renderer/src/platform/utils/hash.ts +++ b/packages/g-plugin-device-renderer/src/platform/utils/hash.ts @@ -202,7 +202,7 @@ export function inputLayoutBufferDescriptorEquals( ): boolean { if (isNil(a)) return isNil(b); if (isNil(b)) return false; - return a.byteStride === b.byteStride && a.frequency === b.frequency; + return a.byteStride === b.byteStride && a.stepMode === b.stepMode; } export function inputLayoutDescriptorEquals( @@ -226,6 +226,7 @@ export function inputLayoutDescriptorEquals( ) ) return false; + if (!programEquals(a.program, b.program)) return false; return true; } @@ -251,12 +252,11 @@ export function samplerBindingCopy( ): SamplerBinding { const sampler = a.sampler; const texture = a.texture; - const lateBinding = a.lateBinding; - return { sampler, texture, lateBinding }; + return { sampler, texture }; } export function samplerBindingNew(): SamplerBinding { - return { sampler: null, texture: null, lateBinding: null }; + return { sampler: null, texture: null }; } export function bufferBindingCopy(a: Readonly): BufferBinding { @@ -335,14 +335,12 @@ export function vertexAttributeDescriptorCopy( const format = a.format; const bufferIndex = a.bufferIndex; const bufferByteOffset = a.bufferByteOffset; - const byteStride = a.byteStride; const divisor = a.divisor; return { location, format, bufferIndex, bufferByteOffset, - byteStride, divisor, }; } @@ -352,8 +350,8 @@ export function inputLayoutBufferDescriptorCopy( ): InputLayoutBufferDescriptor | null { if (!isNil(a)) { const byteStride = a.byteStride; - const frequency = a.frequency; - return { byteStride, frequency }; + const stepMode = a.stepMode; + return { byteStride, stepMode }; } else { return a; } @@ -371,9 +369,11 @@ export function inputLayoutDescriptorCopy( inputLayoutBufferDescriptorCopy, ); const indexBufferFormat = a.indexBufferFormat; + const program = a.program; return { vertexAttributeDescriptors, vertexBufferDescriptors, indexBufferFormat, + program, }; } diff --git a/packages/g-plugin-device-renderer/src/platform/utils/states.ts b/packages/g-plugin-device-renderer/src/platform/utils/states.ts index f89308138..503cce010 100644 --- a/packages/g-plugin-device-renderer/src/platform/utils/states.ts +++ b/packages/g-plugin-device-renderer/src/platform/utils/states.ts @@ -10,7 +10,7 @@ import { ChannelWriteMask, CompareMode, CullMode, - FrontFaceMode, + FrontFace, SamplerFormatKind, StencilOp, TextureDimension, @@ -217,15 +217,15 @@ export function copyAttachmentStateFromSimple( } const defaultBlendState: ChannelBlendState = { - blendMode: BlendMode.Add, - blendSrcFactor: BlendFactor.One, - blendDstFactor: BlendFactor.Zero, + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.ONE, + blendDstFactor: BlendFactor.ZERO, }; export const defaultMegaState: MegaStateDescriptor = { attachmentsState: [ { - channelWriteMask: ChannelWriteMask.RGB, + channelWriteMask: ChannelWriteMask.ALL, rgbBlendState: defaultBlendState, alphaBlendState: defaultBlendState, }, @@ -233,15 +233,15 @@ export const defaultMegaState: MegaStateDescriptor = { blendConstant: colorNewCopy(TransparentBlack), depthWrite: true, - depthCompare: CompareMode.LessEqual, + depthCompare: CompareMode.LEQUAL, // depthCompare: reverseDepthForCompareMode(CompareMode.LessEqual), // stencilCompare: CompareMode.Never, - stencilCompare: CompareMode.Always, + stencilCompare: CompareMode.ALWAYS, stencilWrite: false, - stencilPassOp: StencilOp.Keep, + stencilPassOp: StencilOp.KEEP, stencilRef: 0, - cullMode: CullMode.None, - frontFace: FrontFaceMode.CCW, + cullMode: CullMode.NONE, + frontFace: FrontFace.CCW, polygonOffset: false, }; @@ -255,7 +255,7 @@ export function makeMegaState( } export const fullscreenMegaState = makeMegaState( - { depthCompare: CompareMode.Always, depthWrite: false }, + { depthCompare: CompareMode.ALWAYS, depthWrite: false }, defaultMegaState, ); @@ -278,5 +278,5 @@ export function setAttachmentStateSimple( export const defaultBindingLayoutSamplerDescriptor: BindingLayoutSamplerDescriptor = { formatKind: SamplerFormatKind.Float, - dimension: TextureDimension.n2D, + dimension: TextureDimension.TEXTURE_2D, }; diff --git a/packages/g-plugin-device-renderer/src/render/DeviceProgram.ts b/packages/g-plugin-device-renderer/src/render/DeviceProgram.ts index f7b9a73b6..65f00eb45 100644 --- a/packages/g-plugin-device-renderer/src/render/DeviceProgram.ts +++ b/packages/g-plugin-device-renderer/src/render/DeviceProgram.ts @@ -1,8 +1,5 @@ import { isNil } from '@antv/util'; -import type { VendorInfo } from '../platform'; import { assert, nullify } from '../platform/utils'; -import type { ShaderFeatureMap } from '../shader/compiler'; -import { preprocessShader_GLSL } from '../shader/compiler'; export class DeviceProgram { name = '(unnamed)'; @@ -16,9 +13,6 @@ export class DeviceProgram { vert = ''; frag = ''; defines: Record = {}; - features: ShaderFeatureMap = { - MRT: true, - }; definesChanged(): void { this.preprocessedVert = ''; @@ -50,23 +44,4 @@ export class DeviceProgram { if (str !== null) assert(str === '1'); return str !== null; } - - ensurePreprocessed(vendorInfo: VendorInfo): void { - if (this.preprocessedVert === '') { - this.preprocessedVert = preprocessShader_GLSL( - vendorInfo, - 'vert', - this.both + this.vert, - this.defines, - this.features, - ); - this.preprocessedFrag = preprocessShader_GLSL( - vendorInfo, - 'frag', - this.both + this.frag, - this.defines, - this.features, - ); - } - } } diff --git a/packages/g-plugin-device-renderer/src/render/DynamicUniformBuffer.ts b/packages/g-plugin-device-renderer/src/render/DynamicUniformBuffer.ts index 5aa198230..52ba9aa10 100644 --- a/packages/g-plugin-device-renderer/src/render/DynamicUniformBuffer.ts +++ b/packages/g-plugin-device-renderer/src/render/DynamicUniformBuffer.ts @@ -114,7 +114,7 @@ export class DynamicUniformBuffer { // in bytes length viewOrSize: this.currentBufferWordSize * 4, usage: BufferUsage.UNIFORM, - hint: BufferFrequencyHint.Dynamic, + hint: BufferFrequencyHint.DYNAMIC, }); } diff --git a/packages/g-plugin-device-renderer/src/render/RenderCache.ts b/packages/g-plugin-device-renderer/src/render/RenderCache.ts index ec87935fe..cc816c203 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderCache.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderCache.ts @@ -15,7 +15,6 @@ import type { RenderPipelineDescriptor, Sampler, SamplerDescriptor, - VendorInfo, } from '../platform'; import { assert } from '../platform/utils'; import { @@ -27,6 +26,8 @@ import { renderPipelineDescriptorEquals, samplerDescriptorEquals, } from '../platform/utils/hash'; +import { preprocessProgramObj_GLSL } from '../shader/compiler'; +import { DeviceProgram } from './DeviceProgram'; import { hashCodeNumberFinish, hashCodeNumberUpdate, @@ -133,10 +134,6 @@ function bindingsDescriptorHash(a: BindingsDescriptor): number { return hashCodeNumberFinish(hash); } -interface ProgramDescriptor extends ProgramDescriptorSimple { - ensurePreprocessed: (vendorInfo: VendorInfo) => void; -} - export class RenderCache { device: Device; @@ -195,25 +192,50 @@ export class RenderCache { return inputLayout; } - createProgramSimple( - programDescriptorSimple: ProgramDescriptorSimple, - ): Program { - let program = this.programCache.get(programDescriptorSimple); + createProgramSimple(deviceProgram: DeviceProgram): Program { + const { vert, frag, preprocessedFrag, preprocessedVert } = deviceProgram; + + let program = null; + if (preprocessedVert && preprocessedFrag) { + program = this.programCache.get({ + vert, + frag, + preprocessedFrag, + preprocessedVert, + }); + } + if (program === null) { - const descriptorCopy = programDescriptorSimpleCopy( - programDescriptorSimple, + const { preprocessedVert, preprocessedFrag } = preprocessProgramObj_GLSL( + this.device, + deviceProgram, + ); + deviceProgram.preprocessedVert = preprocessedVert; + deviceProgram.preprocessedFrag = preprocessedFrag; + + const descriptorCopy = programDescriptorSimpleCopy(deviceProgram); + + program = this.device['createProgramSimple']( + { + vertex: { + glsl: preprocessedVert, + }, + fragment: { + glsl: preprocessedFrag, + }, + }, + vert, ); - program = this.device.createProgramSimple(descriptorCopy); this.programCache.add(descriptorCopy, program); } return program; } - createProgram(programDescriptor: ProgramDescriptor): Program { - programDescriptor.ensurePreprocessed(this.device.queryVendorInfo()); - return this.createProgramSimple(programDescriptor); - } + // createProgram(programDescriptor: ProgramDescriptor): Program { + // programDescriptor.ensurePreprocessed(this.device.queryVendorInfo()); + // return this.createProgramSimple(programDescriptor); + // } createSampler(descriptor: SamplerDescriptor): Sampler { let sampler = this.samplerCache.get(descriptor); diff --git a/packages/g-plugin-device-renderer/src/render/RenderGraph.ts b/packages/g-plugin-device-renderer/src/render/RenderGraph.ts index 544f82a92..0586fdb03 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderGraph.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderGraph.ts @@ -572,7 +572,7 @@ export class RenderGraph implements RGGraphBuilder { this.currentPass = pass; const renderPass = this.device.createRenderPass(pass.descriptor); - renderPass.beginDebugGroup(pass.debugName); + renderPass.pushDebugGroup(pass.debugName); renderPass.setViewport( pass.viewportX, @@ -583,7 +583,7 @@ export class RenderGraph implements RGGraphBuilder { if (pass.execFunc !== null) pass.execFunc(renderPass, this); - renderPass.endDebugGroup(); + renderPass.popDebugGroup(); this.device.submitPass(renderPass); if (pass.postFunc !== null) pass.postFunc(this); diff --git a/packages/g-plugin-device-renderer/src/render/RenderGraphPass.ts b/packages/g-plugin-device-renderer/src/render/RenderGraphPass.ts index 379ea5735..aecb4e601 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderGraphPass.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderGraphPass.ts @@ -88,13 +88,6 @@ export class RenderGraphPass implements IRenderGraphPass { this.descriptor.occlusionQueryPool = queryPool; } - resolveToExternalTexture( - attachmentSlot: RGAttachmentSlot, - texture: Texture, - ): void { - this.resolveTextureOutputExternalTextures[attachmentSlot] = texture; - } - exec(func: PassExecFunc): void { assert(this.execFunc === null); this.execFunc = func; diff --git a/packages/g-plugin-device-renderer/src/render/RenderInst.ts b/packages/g-plugin-device-renderer/src/render/RenderInst.ts index ca31f9fa4..6e0ebf44c 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderInst.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderInst.ts @@ -3,13 +3,14 @@ import type { BindingLayoutDescriptor, BindingsDescriptor, Device, + IndexBufferDescriptor, InputLayout, - InputState, MegaStateDescriptor, Program, RenderPass, RenderPipelineDescriptor, SamplerBinding, + VertexBufferDescriptor, } from '../platform'; import { PrimitiveTopology } from '../platform'; import { @@ -65,7 +66,8 @@ export class RenderInst { private dynamicUniformBufferByteOffsets: number[] = nArray(4, () => 0); flags: RenderInstFlags = 0; - private inputState: InputState | null = null; + private vertexBuffers: (VertexBufferDescriptor | null)[] | null = null; + private indexBuffer: IndexBufferDescriptor | null = null; private drawStart = 0; private drawCount = 0; private drawInstanceCount = 0; @@ -76,7 +78,7 @@ export class RenderInst { inputLayout: null, megaStateDescriptor: copyMegaState(defaultMegaState), program: null!, - topology: PrimitiveTopology.Triangles, + topology: PrimitiveTopology.TRIANGLES, colorAttachmentFormats: [], depthStencilAttachmentFormat: null, sampleCount: 1, @@ -92,7 +94,8 @@ export class RenderInst { reset(): void { this.sortKey = 0; this.flags = RenderInstFlags.AllowSkippingIfPipelineNotReady; - this.inputState = null; + this.vertexBuffers = null; + this.indexBuffer = null; this.renderPipelineDescriptor.inputLayout = null; } @@ -125,12 +128,13 @@ export class RenderInst { o.renderPipelineDescriptor.depthStencilAttachmentFormat; this.renderPipelineDescriptor.sampleCount = o.renderPipelineDescriptor.sampleCount; - this.inputState = o.inputState; this.uniformBuffer = o.uniformBuffer; this.uniforms = [...o.uniforms]; this.drawCount = o.drawCount; this.drawStart = o.drawStart; this.drawInstanceCount = o.drawInstanceCount; + this.vertexBuffers = o.vertexBuffers; + this.indexBuffer = o.indexBuffer; this.flags = (this.flags & ~RenderInstFlags.InheritedFlags) | (o.flags & RenderInstFlags.InheritedFlags); @@ -155,6 +159,17 @@ export class RenderInst { o.dynamicUniformBufferByteOffsets[i]; } + validate(): void { + // Validate uniform buffer bindings. + for (let i = 0; i < this.bindingDescriptors.length; i++) { + const bd = this.bindingDescriptors[i]; + for (let j = 0; j < bd.bindingLayout.numUniformBuffers; j++) + assert(bd.uniformBufferBindings[j].wordCount > 0); + } + + assert(this.drawCount > 0); + } + /** * Set the {@see Program} that this render inst will render with. This is part of the automatic * pipeline building facilities. At render time, a pipeline will be automatically and constructed from @@ -184,15 +199,17 @@ export class RenderInst { } /** - * Sets both the {@see InputLayout} and {@see InputState} to be used by this render instance. + * Sets the vertex input configuration to be used by this render instance. * The {@see InputLayout} is used to construct the pipeline as part of the automatic pipeline building - * facilities, while {@see InputState} is used for the render. + * facilities, while the {@see VertexBufferDescriptor} and {@see IndexBufferDescriptor} is used for the render. */ - setInputLayoutAndState( + setVertexInput( inputLayout: InputLayout | null, - inputState: InputState | null, + vertexBuffers: (VertexBufferDescriptor | null)[] | null, + indexBuffer: IndexBufferDescriptor | null, ): void { - this.inputState = inputState; + this.vertexBuffers = vertexBuffers; + this.indexBuffer = indexBuffer; this.renderPipelineDescriptor.inputLayout = inputLayout; } @@ -221,7 +238,6 @@ export class RenderInst { this.bindingDescriptors[0].samplerBindings.push({ sampler: null, texture: null, - lateBinding: null, }); } @@ -415,62 +431,14 @@ export class RenderInst { if (binding === undefined || binding === null) { dst.texture = null; dst.sampler = null; - dst.lateBinding = null; continue; } dst.texture = binding.texture; dst.sampler = binding.sampler; - dst.lateBinding = binding.lateBinding; } } - hasLateSamplerBinding(name: string): boolean { - for ( - let i = 0; - i < this.bindingDescriptors[0].samplerBindings.length; - i++ - ) { - const dst = this.bindingDescriptors[0].samplerBindings[i]; - if (dst.lateBinding === name) return true; - } - - return false; - } - - /** - * Resolve a previously registered "late bound" sampler binding for the given {@param name} to the provided - * {@param binding}, as registered through {@see setSamplerBindingsFromTextureMappings}. - * - * This is intended to be called by high-level code, and is especially helpful when juggling render targets - * for framebuffer effects. - */ - resolveLateSamplerBinding( - name: string, - binding: SamplerBinding | null, - ): void { - for ( - let i = 0; - i < this.bindingDescriptors[0].samplerBindings.length; - i++ - ) { - const dst = this.bindingDescriptors[0].samplerBindings[i]; - if (dst.lateBinding === name) { - if (binding === null) { - dst.texture = null; - dst.sampler = null; - } else { - assert(binding.lateBinding === null); - dst.texture = binding.texture; - if (binding.sampler !== null) { - dst.sampler = binding.sampler; - } - } - - dst.lateBinding = null; - } - } - } /** * Sets whether this render inst should be skipped if the render pipeline isn't ready. * @@ -546,7 +514,11 @@ export class RenderInst { passRenderer.setPipeline(gfxPipeline); - passRenderer.setInputState(this.inputState); + passRenderer.setVertexInput( + this.renderPipelineDescriptor.inputLayout, + this.vertexBuffers, + this.indexBuffer, + ); // upload uniforms for ( @@ -564,7 +536,7 @@ export class RenderInst { uniforms.forEach(({ name, value }) => { uniformsMap[name] = value; }); - (this.renderPipelineDescriptor.program as any).setUniforms(uniformsMap); + this.renderPipelineDescriptor.program.setUniformsLegacy(uniformsMap); }); } @@ -579,17 +551,21 @@ export class RenderInst { this.dynamicUniformBufferByteOffsets, ); - if (this.drawInstanceCount > 1) { - assert(!!(this.flags & RenderInstFlags.Indexed)); - passRenderer.drawIndexedInstanced( + if (this.flags & RenderInstFlags.Indexed) { + passRenderer.drawIndexed( this.drawCount, - this.drawStart, this.drawInstanceCount, + this.drawStart, + 0, + 0, ); - } else if (this.flags & RenderInstFlags.Indexed) { - passRenderer.drawIndexed(this.drawCount, this.drawStart); } else { - passRenderer.draw(this.drawCount, this.drawStart); + passRenderer.draw( + this.drawCount, + this.drawInstanceCount, + this.drawStart, + 0, + ); } return true; diff --git a/packages/g-plugin-device-renderer/src/render/RenderInstList.ts b/packages/g-plugin-device-renderer/src/render/RenderInstList.ts index 1735ee28c..1a29a015c 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderInstList.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderInstList.ts @@ -1,4 +1,4 @@ -import type { RenderPass, SamplerBinding } from '../platform'; +import type { RenderPass } from '../platform'; import { spliceBisectRight } from '../platform/utils'; import type { RenderCache } from './RenderCache'; import type { RenderInst } from './RenderInst'; @@ -58,26 +58,12 @@ export class RenderInstList { } submitRenderInst(renderInst: RenderInst): void { + // TODO: drawCount = 0 + // renderInst.validate(); renderInst.flags |= RenderInstFlags.Draw; this.insertSorted(renderInst); } - hasLateSamplerBinding(name: string): boolean { - for (let i = 0; i < this.renderInsts.length; i++) - if (this.renderInsts[i].hasLateSamplerBinding(name)) { - return true; - } - return false; - } - - /** - * Resolve sampler bindings for all render insts on this render inst list. - */ - resolveLateSamplerBinding(name: string, binding: SamplerBinding): void { - for (let i = 0; i < this.renderInsts.length; i++) - this.renderInsts[i].resolveLateSamplerBinding(name, binding); - } - ensureSorted(): void { if (this.usePostSort) { if (this.renderInsts.length !== 0) @@ -89,19 +75,16 @@ export class RenderInstList { drawOnPassRendererNoReset( cache: RenderCache, passRenderer: RenderPass, - ): number { + ): void { this.ensureSorted(); - let numDrawn = 0; if (this.executionOrder === RenderInstExecutionOrder.Forwards) { for (let i = 0; i < this.renderInsts.length; i++) - if (this.renderInsts[i].drawOnPass(cache, passRenderer)) numDrawn++; + this.renderInsts[i].drawOnPass(cache, passRenderer); } else { for (let i = this.renderInsts.length - 1; i >= 0; i--) - if (this.renderInsts[i].drawOnPass(cache, passRenderer)) numDrawn++; + this.renderInsts[i].drawOnPass(cache, passRenderer); } - - return numDrawn; } reset(): void { diff --git a/packages/g-plugin-device-renderer/src/render/RenderTarget.ts b/packages/g-plugin-device-renderer/src/render/RenderTarget.ts index 526aaedd0..b6744c76f 100644 --- a/packages/g-plugin-device-renderer/src/render/RenderTarget.ts +++ b/packages/g-plugin-device-renderer/src/render/RenderTarget.ts @@ -6,7 +6,7 @@ import type { RGRenderTargetDescription } from './RenderTargetDescription'; export class RGRenderTarget { debugName: string; - readonly dimension = TextureDimension.n2D; + readonly dimension = TextureDimension.TEXTURE_2D; readonly depth = 1; readonly numLevels = 1; @@ -14,7 +14,7 @@ export class RGRenderTarget { width = 0; height = 0; sampleCount = 0; - usage: TextureUsage = TextureUsage.RenderTarget; + usage: TextureUsage = TextureUsage.RENDER_TARGET; immutable = true; needsClear = true; @@ -63,9 +63,6 @@ export class RGRenderTarget { } destroy(): void { - if (this.texture !== null) { - this.texture.destroy(); - } this.attachment.destroy(); } } diff --git a/packages/g-plugin-device-renderer/src/render/SingleSampledTexture.ts b/packages/g-plugin-device-renderer/src/render/SingleSampledTexture.ts index d0dd4d180..36c17d7c3 100644 --- a/packages/g-plugin-device-renderer/src/render/SingleSampledTexture.ts +++ b/packages/g-plugin-device-renderer/src/render/SingleSampledTexture.ts @@ -6,10 +6,10 @@ import { assert } from '../platform/utils'; // Whenever we need to resolve a multi-sampled render target to a single-sampled texture, // we record an extra single-sampled texture here. export class SingleSampledTexture { - readonly dimension = TextureDimension.n2D; + readonly dimension = TextureDimension.TEXTURE_2D; readonly depth = 1; readonly numLevels = 1; - readonly usage = TextureUsage.RenderTarget; + readonly usage = TextureUsage.RENDER_TARGET; pixelFormat: Format; width = 0; diff --git a/packages/g-plugin-device-renderer/src/render/TextureHolder.ts b/packages/g-plugin-device-renderer/src/render/TextureHolder.ts index 490379d43..dc7f756e6 100644 --- a/packages/g-plugin-device-renderer/src/render/TextureHolder.ts +++ b/packages/g-plugin-device-renderer/src/render/TextureHolder.ts @@ -12,7 +12,6 @@ export interface TextureOverride { sampler?: Sampler; width: number; height: number; - lateBinding?: string; } export interface TextureBase { @@ -25,9 +24,6 @@ export class TextureMapping { name: string; texture: Texture | null = null; sampler: Sampler | null = null; - lateBinding: string | null = null; - // These are not used when binding to samplers, and are conveniences for custom behavior. - // TODO(jstpierre): Are any of these really worth anything? width = 0; height = 0; lodBias = 0; @@ -38,7 +34,6 @@ export class TextureMapping { reset(): void { this.texture = null; this.sampler = null; - this.lateBinding = null; this.width = 0; this.height = 0; this.lodBias = 0; @@ -48,7 +43,6 @@ export class TextureMapping { copy(other: TextureMapping): void { this.texture = other.texture; this.sampler = other.sampler; - this.lateBinding = other.lateBinding; this.width = other.width; this.height = other.height; this.lodBias = other.lodBias; diff --git a/packages/g-plugin-device-renderer/src/render/TopologyHelpers.ts b/packages/g-plugin-device-renderer/src/render/TopologyHelpers.ts deleted file mode 100644 index 29c6d82fc..000000000 --- a/packages/g-plugin-device-renderer/src/render/TopologyHelpers.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { assert } from '../platform/utils'; - -export enum Topology { - TRIANGLES, - TRISTRIP, - TRIFAN, - QUADS, - QUADSTRIP, -} - -export function convertToTriangles( - dstBuffer: Uint16Array | Uint32Array, - dstOffs: number, - topology: Topology, - indexBuffer: Uint16Array | Uint32Array, -): void { - assert( - dstOffs + getTriangleIndexCountForTopologyIndexCount(topology, indexBuffer.length) <= - dstBuffer.length, - ); - - let dst = dstOffs; - if (topology === Topology.QUADS) { - for (let i = 0; i < indexBuffer.length; i += 4) { - dstBuffer[dst++] = indexBuffer[i + 0]; - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 2]; - dstBuffer[dst++] = indexBuffer[i + 0]; - dstBuffer[dst++] = indexBuffer[i + 2]; - dstBuffer[dst++] = indexBuffer[i + 3]; - } - } else if (topology === Topology.TRISTRIP) { - for (let i = 0; i < indexBuffer.length - 2; i++) { - if (i % 2 === 0) { - dstBuffer[dst++] = indexBuffer[i + 0]; - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 2]; - } else { - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 0]; - dstBuffer[dst++] = indexBuffer[i + 2]; - } - } - } else if (topology === Topology.TRIFAN) { - for (let i = 0; i < indexBuffer.length - 2; i++) { - dstBuffer[dst++] = indexBuffer[0]; - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 2]; - } - } else if (topology === Topology.QUADSTRIP) { - for (let i = 0; i < indexBuffer.length - 2; i += 2) { - dstBuffer[dst++] = indexBuffer[i + 0]; - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 2]; - dstBuffer[dst++] = indexBuffer[i + 2]; - dstBuffer[dst++] = indexBuffer[i + 1]; - dstBuffer[dst++] = indexBuffer[i + 3]; - } - } else if (topology === Topology.TRIANGLES) { - dstBuffer.set(indexBuffer, dstOffs); - } -} - -export function convertToTrianglesRange( - dstBuffer: Uint16Array | Uint32Array | number[], - dstOffs: number, - topology: Topology, - baseVertex: number, - numVertices: number, -): void { - assert( - dstOffs + getTriangleIndexCountForTopologyIndexCount(topology, numVertices) <= dstBuffer.length, - ); - - let dst = dstOffs; - if (topology === Topology.QUADS) { - for (let i = 0; i < numVertices; i += 4) { - dstBuffer[dst++] = baseVertex + i + 0; - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 2; - dstBuffer[dst++] = baseVertex + i + 2; - dstBuffer[dst++] = baseVertex + i + 3; - dstBuffer[dst++] = baseVertex + i + 0; - } - } else if (topology === Topology.TRISTRIP) { - for (let i = 0; i < numVertices - 2; i++) { - if (i % 2 === 0) { - dstBuffer[dst++] = baseVertex + i + 0; - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 2; - } else { - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 0; - dstBuffer[dst++] = baseVertex + i + 2; - } - } - } else if (topology === Topology.TRIFAN) { - for (let i = 0; i < numVertices - 2; i++) { - dstBuffer[dst++] = baseVertex + 0; - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 2; - } - } else if (topology === Topology.QUADSTRIP) { - for (let i = 0; i < numVertices - 2; i += 2) { - dstBuffer[dst++] = baseVertex + i + 0; - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 2; - dstBuffer[dst++] = baseVertex + i + 2; - dstBuffer[dst++] = baseVertex + i + 1; - dstBuffer[dst++] = baseVertex + i + 3; - } - } else if (topology === Topology.TRIANGLES) { - for (let i = 0; i < numVertices; i++) dstBuffer[dst++] = baseVertex + i; - } -} - -export function convertToTriangleIndexBuffer( - topology: Topology, - indexBuffer: Uint16Array, -): Uint16Array { - if (topology === Topology.TRIANGLES) return indexBuffer; - - const newSize = getTriangleIndexCountForTopologyIndexCount(topology, indexBuffer.length); - const newBuffer = new Uint16Array(newSize); - convertToTriangles(newBuffer, 0, topology, indexBuffer); - - return newBuffer; -} - -function range(start: number, length: number): Uint16Array { - const r = new Uint16Array(length); - for (let i = 0; i < length; i++) r[i] = start + i; - return r; -} - -export function makeTriangleIndexBuffer( - topology: Topology, - baseVertex: number, - numVertices: number, -): Uint16Array { - return convertToTriangleIndexBuffer(topology, range(baseVertex, numVertices)); -} - -export function getTriangleCountForTopologyIndexCount( - topology: Topology, - indexCount: number, -): number { - switch (topology) { - case Topology.TRIANGLES: - // One triangle per every three indexes. - return indexCount / 3; - case Topology.TRISTRIP: - case Topology.TRIFAN: - // One triangle per index, minus the first two. - return indexCount - 2; - case Topology.QUADS: - // Two triangles per four indices. - return 2 * (indexCount / 4); - case Topology.QUADSTRIP: - // Two triangles per two indexes, minus the first two. - return 2 * (indexCount - 2); - } -} - -export function getTriangleIndexCountForTopologyIndexCount( - topology: Topology, - indexCount: number, -): number { - // Three indexes per triangle. - return 3 * getTriangleCountForTopologyIndexCount(topology, indexCount); -} - -export function filterDegenerateTriangleIndexBuffer(indexData: Uint16Array): Uint16Array { - assert(indexData.length % 3 === 0); - const dst = new Uint16Array(indexData.length); - let dstIdx = 0; - - for (let i = 0; i < indexData.length; i += 3) { - const i0 = indexData[i + 0]; - const i1 = indexData[i + 1]; - const i2 = indexData[i + 2]; - - const isDegenerate = i0 === i1 || i1 === i2 || i2 === i0; - if (!isDegenerate) { - dst[dstIdx++] = i0; - dst[dstIdx++] = i1; - dst[dstIdx++] = i2; - } - } - - return dst.slice(0, dstIdx); -} diff --git a/packages/g-plugin-device-renderer/src/render/utils/projection.ts b/packages/g-plugin-device-renderer/src/render/utils/projection.ts index 242249309..37f03e705 100644 --- a/packages/g-plugin-device-renderer/src/render/utils/projection.ts +++ b/packages/g-plugin-device-renderer/src/render/utils/projection.ts @@ -1,14 +1,48 @@ import { mat4 } from 'gl-matrix'; import { ClipSpaceNearZ } from '../../platform'; -const mtxOpenGLFromD3D = mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, -1, 1); +const mtxOpenGLFromD3D = mat4.fromValues( + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + -1, + 1, +); // Converts a projection matrix from D3D-style Z range [0, 1] to OpenGL-style Z range [-1, 1] function projectionMatrixOpenGLFromD3D(m: mat4): void { mat4.mul(m, mtxOpenGLFromD3D, m); } -const mtxD3DFromOpenGL = mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1); +const mtxD3DFromOpenGL = mat4.fromValues( + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0.5, + 0, + 0, + 0, + 0.5, + 1, +); // Converts a projection matrix from OpenGL-style Z range [-1, 1] to D3D-style Z range [0, 1] function projectionMatrixD3DFromOpenGL(m: mat4): void { @@ -46,6 +80,6 @@ export function projectionMatrixConvertClipSpaceNearZ( ): void { if (dst === src) return; - if (dst === ClipSpaceNearZ.NegativeOne) projectionMatrixOpenGLFromD3D(m); - else if (dst === ClipSpaceNearZ.Zero) projectionMatrixD3DFromOpenGL(m); + if (dst === ClipSpaceNearZ.NEGATIVE_ONE) projectionMatrixOpenGLFromD3D(m); + else if (dst === ClipSpaceNearZ.ZERO) projectionMatrixD3DFromOpenGL(m); } diff --git a/packages/g-plugin-device-renderer/src/renderer/BatchManager.ts b/packages/g-plugin-device-renderer/src/renderer/BatchManager.ts index 56dfe091d..2bd1a63f7 100644 --- a/packages/g-plugin-device-renderer/src/renderer/BatchManager.ts +++ b/packages/g-plugin-device-renderer/src/renderer/BatchManager.ts @@ -49,6 +49,14 @@ export class BatchManager { private stencilRefCache: Record = {}; + destroy() { + this.drawcalls.forEach((drawcall) => { + drawcall.destroy(); + }); + this.drawcalls = []; + this.pendingUpdatePatches = {}; + } + render(list: RenderInstList, isPicking = false) { if (!isPicking) { this.updatePendingPatches(); @@ -155,7 +163,13 @@ export class BatchManager { } if (mesh.objects.length === 0) { - this.drawcalls.splice(this.drawcalls.indexOf(mesh), 1); + const deletedDrawcalls = this.drawcalls.splice( + this.drawcalls.indexOf(mesh), + 1, + ); + deletedDrawcalls.forEach((deletedDrawcall) => { + deletedDrawcall.destroy(); + }); } } }); diff --git a/packages/g-plugin-device-renderer/src/shader/compiler.ts b/packages/g-plugin-device-renderer/src/shader/compiler.ts index ecd1fcb49..5e338ea5b 100644 --- a/packages/g-plugin-device-renderer/src/shader/compiler.ts +++ b/packages/g-plugin-device-renderer/src/shader/compiler.ts @@ -1,6 +1,7 @@ -import type { Device, ProgramDescriptorSimple, VendorInfo } from '../platform'; +import type { Device, VendorInfo } from '../platform'; import { ClipSpaceNearZ, ViewportOrigin } from '../platform'; import { assert } from '../platform/utils'; +import { DeviceProgram } from '../render'; const ES100_REPLACEMENTS: [RegExp, string][] = [ // In GLSL 1.00 ES these functions are provided by an extension @@ -12,9 +13,6 @@ const ES100_REPLACEMENTS: [RegExp, string][] = [ [/\btextureLod\(/g, 'texture2DLodEXT('], ]; -export type ShaderFeature = 'MRT' | 'PICKING'; -export type ShaderFeatureMap = Partial>; - function defineStr(k: string, v: string): string { return `#define ${k} ${v}`; } @@ -129,12 +127,35 @@ export function getUniforms(vert: string) { return uniformNames; } +function parseBinding(layout: string | undefined): number | null { + if (layout === undefined) return null; + + const g = /binding\s*=\s*(\d+)/.exec(layout); + if (g !== null) { + const bindingNum = parseInt(g[1], 10); + if (!Number.isNaN(bindingNum)) return bindingNum; + } + + return null; +} + +function getSeparateSamplerTypes( + combinedSamplerType: string, +): [string, string] { + let samplerType = ``, + textureType = combinedSamplerType; + if (combinedSamplerType.endsWith(`Shadow`)) { + textureType = textureType.slice(0, -6); + samplerType = `Shadow`; + } + return [textureType, samplerType]; +} + export function preprocessShader_GLSL( vendorInfo: VendorInfo, type: 'vert' | 'frag', source: string, defines: Record | null = null, - features: ShaderFeatureMap | null = null, ): string { const isGLSL100 = vendorInfo.glslVersion === '#version 100'; // const supportMRT = vendorInfo.supportMRT && !!features.MRT; @@ -165,81 +186,115 @@ export function preprocessShader_GLSL( let rest = lines.filter((line) => !line.startsWith('precision')).join('\n'); let extraDefines = ''; - if (vendorInfo.viewportOrigin === ViewportOrigin.UpperLeft) { + if (vendorInfo.viewportOrigin === ViewportOrigin.UPPER_LEFT) { extraDefines += `${defineStr(`VIEWPORT_ORIGIN_TL`, `1`)}\n`; } - if (vendorInfo.clipSpaceNearZ === ClipSpaceNearZ.Zero) { + if (vendorInfo.clipSpaceNearZ === ClipSpaceNearZ.ZERO) { extraDefines += `${defineStr(`CLIPSPACE_NEAR_ZERO`, `1`)}\n`; } if (vendorInfo.explicitBindingLocations) { let set = 0, - binding = 0, + implicitBinding = 0, location = 0; rest = rest.replace( /^(layout\((.*)\))?\s*uniform(.+{)$/gm, (substr, cap, layout, rest) => { const layout2 = layout ? `${layout}, ` : ``; - return `layout(${layout2}set = ${set}, binding = ${binding++}) uniform ${rest}`; + return `layout(${layout2}set = ${set}, binding = ${implicitBinding++}) uniform ${rest}`; }, ); // XXX(jstpierre): WebGPU now binds UBOs and textures in different sets as a porting hack, hrm... set++; - binding = 0; + implicitBinding = 0; assert(vendorInfo.separateSamplerTextures); - rest = rest.replace(/uniform sampler2D (.*);/g, (substr, samplerName) => { - // Can't have samplers in vertex for some reason. - return type === 'frag' - ? ` -layout(set = ${set}, binding = ${binding++}) uniform texture2D T_${samplerName}; -layout(set = ${set}, binding = ${binding++}) uniform sampler S_${samplerName}; -` - : ''; - }); + rest = rest.replace( + /^(layout\((.*)\))?\s*uniform sampler(\w+) (.*);/gm, + (substr, cap, layout, combinedSamplerType, samplerName) => { + let binding = parseBinding(layout); + if (binding === null) binding = implicitBinding++; + + const [textureType, samplerType] = + getSeparateSamplerTypes(combinedSamplerType); + return type === 'frag' + ? ` +layout(set = ${set}, binding = ${ + binding * 2 + 0 + }) uniform texture${textureType} T_${samplerName}; +layout(set = ${set}, binding = ${ + binding * 2 + 1 + }) uniform sampler${samplerType} S_${samplerName};`.trim() + : ''; + }, + ); rest = rest.replace( - type === 'frag' ? /^\s*\b\s*(varying|in)\b/gm : /^\s*\b(varying|out)\b/gm, + type === 'frag' ? /^\b(varying|in)\b/gm : /^\b(varying|out)\b/gm, (substr, tok) => { return `layout(location = ${location++}) ${tok}`; }, ); + /** + * @see https://github.com/gfx-rs/naga/issues/1994 + */ extraDefines += `${defineStr(`gl_VertexID`, `gl_VertexIndex`)}\n`; + extraDefines += `${defineStr(`gl_InstanceID`, `gl_InstanceIndex`)}\n`; } if (vendorInfo.separateSamplerTextures) { - rest = rest.replace(/\bPD_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `texture2D T_P_${samplerName}, sampler S_P_${samplerName}`; - }); + rest = rest.replace( + /\bPD_SAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + const [textureType, samplerType] = + getSeparateSamplerTypes(combinedSamplerType); + return `texture${textureType} T_P_${samplerName}, sampler${samplerType} S_P_${samplerName}`; + }, + ); - rest = rest.replace(/\bPU_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `SAMPLER_2D(P_${samplerName})`; - }); + rest = rest.replace( + /\bPP_SAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + return `T_${samplerName}, S_${samplerName}`; + }, + ); - rest = rest.replace(/\bPP_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `T_${samplerName}, S_${samplerName}`; - }); + rest = rest.replace( + /\bSAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + return `sampler${combinedSamplerType}(T_${samplerName}, S_${samplerName})`; + }, + ); - rest = rest.replace(/\bSAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `sampler2D(T_${samplerName}, S_${samplerName})`; + rest = rest.replace(/\bTEXTURE\((.*?)\)/g, (substr, samplerName) => { + return `T_${samplerName}`; }); } else { - rest = rest.replace(/\bPD_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `sampler2D P_${samplerName}`; - }); + rest = rest.replace( + /\bPD_SAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + return `sampler${combinedSamplerType} P_${samplerName}`; + }, + ); - rest = rest.replace(/\bPU_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return `SAMPLER_2D(P_${samplerName})`; - }); + rest = rest.replace( + /\bPP_SAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + return samplerName; + }, + ); - rest = rest.replace(/\bPP_SAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { - return samplerName; - }); + rest = rest.replace( + /\bSAMPLER_(\w+)\((.*?)\)/g, + (substr, combinedSamplerType, samplerName) => { + return samplerName; + }, + ); - rest = rest.replace(/\bSAMPLER_2D\((.*?)\)/g, (substr, samplerName) => { + rest = rest.replace(/\bTEXTURE\((.*?)\)/g, (substr, samplerName) => { return samplerName; }); } @@ -372,10 +427,11 @@ ${rest} return concat; } -export interface ProgramDescriptorSimpleWithOrig - extends ProgramDescriptorSimple { +export interface ProgramDescriptorSimpleWithOrig { vert: string; frag: string; + preprocessedVert: string; + preprocessedFrag: string; } export function preprocessProgram_GLSL( @@ -383,46 +439,28 @@ export function preprocessProgram_GLSL( vert: string, frag: string, defines: Record | null = null, - features: ShaderFeatureMap | null = null, ): ProgramDescriptorSimpleWithOrig { const preprocessedVert = preprocessShader_GLSL( vendorInfo, 'vert', vert, defines, - features, ); const preprocessedFrag = preprocessShader_GLSL( vendorInfo, 'frag', frag, defines, - features, ); return { vert, frag, preprocessedVert, preprocessedFrag }; } -export interface ProgramObjBag { - both?: string; - vert: string; - frag: string; - defines?: Record; - features?: ShaderFeatureMap; -} - export function preprocessProgramObj_GLSL( device: Device, - obj: ProgramObjBag, + obj: DeviceProgram, ): ProgramDescriptorSimpleWithOrig { const defines = obj.defines !== undefined ? obj.defines : null; - const features = obj.features !== undefined ? obj.features : null; const vert = obj.both !== undefined ? obj.both + obj.vert : obj.vert; const frag = obj.both !== undefined ? obj.both + obj.frag : obj.frag; - return preprocessProgram_GLSL( - device.queryVendorInfo(), - vert, - frag, - defines, - features, - ); + return preprocessProgram_GLSL(device.queryVendorInfo(), vert, frag, defines); } diff --git a/packages/g-plugin-device-renderer/src/shader/sdf.frag b/packages/g-plugin-device-renderer/src/shader/sdf.frag index 92de1c5ac..6c59a60ee 100644 --- a/packages/g-plugin-device-renderer/src/shader/sdf.frag +++ b/packages/g-plugin-device-renderer/src/shader/sdf.frag @@ -4,7 +4,7 @@ #pragma glslify: import('@antv/g-shader-components/uv.declaration.frag') #pragma glslify: import('@antv/g-shader-components/map.declaration.frag') -in vec3 v_Data; +in vec2 v_Data; in vec2 v_Radius; in vec4 v_StylePacked3; @@ -23,24 +23,25 @@ void main() { bool omitStroke = v_StylePacked3.z == 1.0; - float antialiasblur = v_Data.z; - float antialiased_blur = -max(0.0, antialiasblur); vec2 r = (v_Radius - (omitStroke ? 0.0 : u_StrokeWidth)) / v_Radius.y; float wh = v_Radius.x / v_Radius.y; + float dist = length(v_Data); + float antialiased_blur = -fwidth(dist); + float outer_df; float inner_df; // 'circle', 'ellipse', 'rect' if (shape == 0) { - outer_df = sdCircle(v_Data.xy, 1.0); - inner_df = sdCircle(v_Data.xy, r.x); + outer_df = sdCircle(v_Data, 1.0); + inner_df = sdCircle(v_Data, r.x); } else if (shape == 1) { - outer_df = sdEllipsoidApproximated(v_Data.xy, vec2(wh, 1.0)); - inner_df = sdEllipsoidApproximated(v_Data.xy, r); + outer_df = sdEllipsoidApproximated(v_Data, vec2(wh, 1.0)); + inner_df = sdEllipsoidApproximated(v_Data, r); } else if (shape == 2) { bool useRadius = v_StylePacked3.y > epsilon; - outer_df = sdRoundedBox(v_Data.xy, vec2(wh, 1.0), useRadius ? (v_StylePacked3.y + u_StrokeWidth / 2.0) / v_Radius.y : 0.0); - inner_df = sdRoundedBox(v_Data.xy, r, useRadius ? (v_StylePacked3.y - u_StrokeWidth / 2.0) / v_Radius.y : 0.0); + outer_df = sdRoundedBox(v_Data, vec2(wh, 1.0), useRadius ? (v_StylePacked3.y + u_StrokeWidth / 2.0) / v_Radius.y : 0.0); + inner_df = sdRoundedBox(v_Data, r, useRadius ? (v_StylePacked3.y - u_StrokeWidth / 2.0) / v_Radius.y : 0.0); } float opacity_t = smoothstep(0.0, antialiased_blur, outer_df); diff --git a/packages/g-plugin-device-renderer/src/shader/sdf.vert b/packages/g-plugin-device-renderer/src/shader/sdf.vert index 6ffbe991c..8cab3266b 100644 --- a/packages/g-plugin-device-renderer/src/shader/sdf.vert +++ b/packages/g-plugin-device-renderer/src/shader/sdf.vert @@ -13,7 +13,7 @@ layout(location = SIZE) in vec2 a_Size; out vec2 v_Uv; #endif -out vec3 v_Data; +out vec2 v_Data; out vec2 v_Radius; out vec4 v_StylePacked3; @@ -31,7 +31,6 @@ void main() { bool omitStroke = a_StylePacked3.z == 1.0; vec2 radius = a_Size + vec2(omitStroke ? 0.0 : strokeWidth / 2.0); vec2 offset = (a_Extrude + vec2(1.0) - 2.0 * u_Anchor.xy) * a_Size + a_Extrude * vec2(omitStroke ? 0.0 : strokeWidth / 2.0); - float antialiasblur = 1.0 / radius.y; bool isBillboard = a_StylePacked3.w > 0.5; if (isBillboard) { @@ -43,6 +42,6 @@ void main() { } v_Radius = radius; - v_Data = vec3(a_Extrude * radius / radius.y, antialiasblur); + v_Data = vec2(a_Extrude * radius / radius.y); v_StylePacked3 = a_StylePacked3; } \ No newline at end of file diff --git a/packages/g-plugin-device-renderer/src/shader/text.frag b/packages/g-plugin-device-renderer/src/shader/text.frag index 2699f5a7f..2f954e8b8 100644 --- a/packages/g-plugin-device-renderer/src/shader/text.frag +++ b/packages/g-plugin-device-renderer/src/shader/text.frag @@ -7,8 +7,6 @@ uniform sampler2D u_SDFMap; #define SDF_PX 8.0 -in float v_GammaScale; - out vec4 outputColor; void main() { @@ -16,20 +14,17 @@ void main() { float dist = texture(SAMPLER_2D(u_SDFMap), v_Uv).a; - float EDGE_GAMMA = 0.105 / u_DevicePixelRatio; float fontScale = u_FontSize / 24.0; - highp float gamma = EDGE_GAMMA / (fontScale * u_GammaScale); lowp vec4 color = u_Color; lowp float buff = (256.0 - 64.0) / 256.0; float opacity = u_FillOpacity; if (u_HasStroke > 0.5 && u_StrokeWidth > 0.0) { color = u_StrokeColor; - gamma = (u_StrokeBlur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_GammaScale); buff = (6.0 - u_StrokeWidth / fontScale / 2.0) / SDF_PX; opacity = u_StrokeOpacity; } - highp float gamma_scaled = gamma * v_GammaScale; + highp float gamma_scaled = fwidth(dist); highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist); opacity *= alpha * u_Opacity; diff --git a/packages/g-plugin-device-renderer/src/shader/text.vert b/packages/g-plugin-device-renderer/src/shader/text.vert index a833c93bb..7e314f0bc 100644 --- a/packages/g-plugin-device-renderer/src/shader/text.vert +++ b/packages/g-plugin-device-renderer/src/shader/text.vert @@ -9,7 +9,6 @@ layout(location = TEX) in vec2 a_Tex; layout(location = OFFSET) in vec2 a_Offset; out vec2 v_Uv; -out float v_GammaScale; void main() { #pragma glslify: import('@antv/g-shader-components/batch.vert') @@ -25,9 +24,7 @@ void main() { float rotation = a_StylePacked2.w; bool isSizeAttenuation = a_StylePacked2.z > 0.5; gl_Position = billboard(offset, rotation, isSizeAttenuation, u_ProjectionMatrix, u_ViewMatrix, u_ModelMatrix); - v_GammaScale = 1.0; } else { gl_Position = project(vec4((a_Offset) * fontScale + bufferOffset, u_ZIndex, 1.0), u_ProjectionMatrix, u_ViewMatrix, u_ModelMatrix); - v_GammaScale = gl_Position.w; } } \ No newline at end of file diff --git a/packages/g-plugin-gpgpu/CHANGELOG.md b/packages/g-plugin-gpgpu/CHANGELOG.md index 32a37f0f2..103b8f000 100644 --- a/packages/g-plugin-gpgpu/CHANGELOG.md +++ b/packages/g-plugin-gpgpu/CHANGELOG.md @@ -1,5 +1,13 @@ # @antv/g-plugin-gpgpu +## 1.9.20 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- 568ec0f4: Export Device API in webgl & webgpu. + - @antv/g-webgpu@1.9.20 + ## 1.9.19 ### Patch Changes diff --git a/packages/g-plugin-gpgpu/package.json b/packages/g-plugin-gpgpu/package.json index 62fe678cf..916de6e95 100644 --- a/packages/g-plugin-gpgpu/package.json +++ b/packages/g-plugin-gpgpu/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-gpgpu", - "version": "1.9.19", + "version": "1.9.20", "description": "A G plugin for GPGPU based on WebGPU", "keywords": [ "webgpu", diff --git a/packages/g-plugin-gpgpu/src/Kernel.ts b/packages/g-plugin-gpgpu/src/Kernel.ts index 0676d8cae..c34340e90 100644 --- a/packages/g-plugin-gpgpu/src/Kernel.ts +++ b/packages/g-plugin-gpgpu/src/Kernel.ts @@ -88,8 +88,10 @@ export class Kernel { } } - const program = this.device.createProgramSimple({ - preprocessedCompute: this.bundle.shaders[target], + const program = this.device.createProgram({ + compute: { + wgsl: this.bundle.shaders[target], + }, }); this.computePipeline = this.device.createComputePipeline({ @@ -172,7 +174,7 @@ export class Kernel { // fixed bind group 0 computePass.setBindings(0, bindings, []); - computePass.dispatch(...dispatchParams); + computePass.dispatchWorkgroups(...dispatchParams); this.device.submitPass(computePass); } diff --git a/packages/g-plugin-webgl-device/CHANGELOG.md b/packages/g-plugin-webgl-device/CHANGELOG.md index 5322b5c52..51460faf8 100644 --- a/packages/g-plugin-webgl-device/CHANGELOG.md +++ b/packages/g-plugin-webgl-device/CHANGELOG.md @@ -1,5 +1,15 @@ # @antv/g-plugin-webgl-device +## 1.9.17 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- 568ec0f4: Export Device API in webgl & webgpu. +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + ## 1.9.16 ### Patch Changes diff --git a/packages/g-plugin-webgl-device/package.json b/packages/g-plugin-webgl-device/package.json index f3fff6437..15f017b8d 100644 --- a/packages/g-plugin-webgl-device/package.json +++ b/packages/g-plugin-webgl-device/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-webgl-device", - "version": "1.9.16", + "version": "1.9.17", "description": "A G plugin implements GPUDevice interface with WebGL API", "keywords": [ "antv", diff --git a/packages/g-plugin-webgl-device/src/index.ts b/packages/g-plugin-webgl-device/src/index.ts index 480036a08..7ee949a50 100644 --- a/packages/g-plugin-webgl-device/src/index.ts +++ b/packages/g-plugin-webgl-device/src/index.ts @@ -2,6 +2,8 @@ import { AbstractRendererPlugin } from '@antv/g-lite'; import type { WebGLRendererPluginOptions } from './interfaces'; import { WebGLDeviceContribution } from './WebGLDeviceContribution'; +export { WebGLDeviceContribution }; + export class Plugin extends AbstractRendererPlugin { name = 'webgl-device'; constructor(private options: Partial) { diff --git a/packages/g-plugin-webgl-device/src/platform/Buffer.ts b/packages/g-plugin-webgl-device/src/platform/Buffer.ts index aa3c4feea..f217e3f60 100644 --- a/packages/g-plugin-webgl-device/src/platform/Buffer.ts +++ b/packages/g-plugin-webgl-device/src/platform/Buffer.ts @@ -5,7 +5,7 @@ import { align, } from '@antv/g-plugin-device-renderer'; import { - assert, + // assert, BufferUsage, ResourceType, } from '@antv/g-plugin-device-renderer'; @@ -39,7 +39,7 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { }) { super({ id, device }); - const { viewOrSize, usage, hint } = descriptor; + const { viewOrSize, usage, hint = BufferFrequencyHint.STATIC } = descriptor; const { uniformBufferMaxPageByteSize, gl } = device; const isStorageTexture = usage & BufferUsage.STORAGE; @@ -56,7 +56,7 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { // numLevels: 1, // immutable: true, // }); - // texture.setImageData([new Uint8Array(4 * depth)], 0); + // texture.setImageData([new Uint8Array(4 * depth)]); // Create later. return this; @@ -85,7 +85,7 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { let pageByteSize: number; if (isUBO) { - assert(byteSize % uniformBufferMaxPageByteSize === 0); + // assert(byteSize % uniformBufferMaxPageByteSize === 0); let byteSizeLeft = byteSize; while (byteSizeLeft > 0) { this.gl_buffer_pages.push( @@ -111,12 +111,17 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { this.usage = usage; this.gl_target = translateBufferUsageToTarget(usage); + // init data + if (!isNumber(viewOrSize)) { + this.setSubData(0, new Uint8Array(viewOrSize.buffer)); + } + if (!isUBO) { if (isWebGL2(gl)) { - gl.bindVertexArray(this.device.currentBoundVAO); + gl.bindVertexArray(this.device['currentBoundVAO']); } else { device.OES_vertex_array_object.bindVertexArrayOES( - this.device.currentBoundVAO, + this.device['currentBoundVAO'], ); } } @@ -130,28 +135,28 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { ): void { const gl = this.device.gl; const { - gl_target, - byteSize: dstByteSize, + // gl_target, + // byteSize: dstByteSize, pageByteSize: dstPageByteSize, } = this; // Account for setSubData being called with a dstByteOffset that is beyond the end of the buffer. - if (isWebGL2(gl) && gl_target === gl.UNIFORM_BUFFER) { - // Manually check asserts for speed. - if (!(dstByteOffset % dstPageByteSize === 0)) - throw new Error( - `Assert fail: (dstByteOffset [${dstByteOffset}] % dstPageByteSize [${dstPageByteSize}]) === 0`, - ); - if (!(byteSize % dstPageByteSize === 0)) - throw new Error( - `Assert fail: (byteSize [${byteSize}] % dstPageByteSize [${dstPageByteSize}]) === 0`, - ); - } - if (!(dstByteOffset + byteSize <= dstByteSize)) { - throw new Error( - `Assert fail: (dstByteOffset [${dstByteOffset}] + byteSize [${byteSize}]) <= dstByteSize [${dstByteSize}], gl_target ${gl_target}`, - ); - // exceed, need to recreate - } + // if (isWebGL2(gl) && gl_target === gl.UNIFORM_BUFFER) { + // // Manually check asserts for speed. + // if (!(dstByteOffset % dstPageByteSize === 0)) + // throw new Error( + // `Assert fail: (dstByteOffset [${dstByteOffset}] % dstPageByteSize [${dstPageByteSize}]) === 0`, + // ); + // if (!(byteSize % dstPageByteSize === 0)) + // throw new Error( + // `Assert fail: (byteSize [${byteSize}] % dstPageByteSize [${dstPageByteSize}]) === 0`, + // ); + // } + // if (!(dstByteOffset + byteSize <= dstByteSize)) { + // throw new Error( + // `Assert fail: (dstByteOffset [${dstByteOffset}] + byteSize [${byteSize}]) <= dstByteSize [${dstByteSize}], gl_target ${gl_target}`, + // ); + // // exceed, need to recreate + // } const virtBufferByteOffsetEnd = dstByteOffset + byteSize; let virtBufferByteOffset = dstByteOffset; @@ -160,7 +165,12 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindBuffer#parameters const target = isWebGL2(gl) ? gl.COPY_WRITE_BUFFER : this.gl_target; - gl.bindBuffer(target, getPlatformBuffer(this, virtBufferByteOffset)); + const buffer = getPlatformBuffer(this, virtBufferByteOffset); + // @ts-ignore + if (buffer.ubo) { + return; + } + gl.bindBuffer(target, buffer); // only WebGL2 support srcOffset & length // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferSubData @@ -182,7 +192,7 @@ export class Buffer_GL extends ResourceBase_GL implements Buffer { virtBufferByteOffset += dstPageByteSize; physBufferByteOffset = 0; srcByteOffset += dstPageByteSize; - this.device.debugGroupStatisticsBufferUpload(); + this.device['debugGroupStatisticsBufferUpload'](); } } diff --git a/packages/g-plugin-webgl-device/src/platform/ComputePass.ts b/packages/g-plugin-webgl-device/src/platform/ComputePass.ts index 689753462..a1826e737 100644 --- a/packages/g-plugin-webgl-device/src/platform/ComputePass.ts +++ b/packages/g-plugin-webgl-device/src/platform/ComputePass.ts @@ -1,4 +1,5 @@ import type { + Buffer, Bindings, ComputePass, ComputePipeline, @@ -7,13 +8,16 @@ import type { // import type { ComputePipeline_GL } from './ComputePipeline'; export class ComputePass_GL implements ComputePass { - beginDebugGroup: (name: string) => void; - endDebugGroup: () => void; - /** * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatch */ - dispatch(x: number, y?: number, z?: number): void {} + dispatchWorkgroups( + workgroupCountX: number, + workgroupCountY?: number, + workgroupCountZ?: number, + ) {} + + dispatchWorkgroupsIndirect(indirectBuffer: Buffer, indirectOffset: number) {} finish() { // this.gpuComputePassEncoder.end(); @@ -42,4 +46,8 @@ export class ComputePass_GL implements ComputePass { // const bindings = bindings_ as Bindings_WebGPU; // this.gpuComputePassEncoder.setBindGroup(bindingLayoutIndex, bindings.gpuBindGroup[0]); } + + pushDebugGroup(name: string) {} + popDebugGroup() {} + insertDebugMarker(markerLabel: string) {} } diff --git a/packages/g-plugin-webgl-device/src/platform/Device.ts b/packages/g-plugin-webgl-device/src/platform/Device.ts index 5a36b9368..84fb21cf7 100644 --- a/packages/g-plugin-webgl-device/src/platform/Device.ts +++ b/packages/g-plugin-webgl-device/src/platform/Device.ts @@ -5,6 +5,7 @@ import { BindingsDescriptor, Buffer, BufferDescriptor, + BufferFrequencyHint, ComputePass, ComputePipeline, ComputePipelineDescriptor, @@ -14,12 +15,10 @@ import { IndexBufferDescriptor, InputLayout, InputLayoutDescriptor, - InputState, MegaStateDescriptor, PlatformFramebuffer, Program, ProgramDescriptor, - ProgramDescriptorSimple, QueryPool, QueryPoolType, Readback, @@ -38,6 +37,7 @@ import { TransparentWhite, VendorInfo, VertexBufferDescriptor, + preprocessShader_GLSL, } from '@antv/g-plugin-device-renderer'; import { assert, @@ -63,19 +63,17 @@ import { makeDataBuffer, nullify, prependLineNo, - preprocessProgramObj_GLSL, PrimitiveTopology, ResourceType, SamplerFormatKind, TextureDimension, TextureUsage, - VertexBufferFrequency, + VertexStepMode, ViewportOrigin, } from '@antv/g-plugin-device-renderer'; import { Bindings_GL } from './Bindings'; import { Buffer_GL } from './Buffer'; import { InputLayout_GL } from './InputLayout'; -import { InputState_GL } from './InputState'; import type { BindingLayoutSamplerDescriptor_GL, EXT_texture_compression_rgtc, @@ -106,6 +104,7 @@ import { isWebGL2, } from './utils'; import { ComputePass_GL } from './ComputePass'; +import { isNil } from '@antv/util'; // This is a workaround for ANGLE not supporting UBOs greater than 64kb (the limit of D3D). // https://bugs.chromium.org/p/angleproject/issues/detail?id=3388 @@ -146,10 +145,10 @@ export class Device_GL implements SwapChain, Device { // Device private currentActiveTexture: GLenum | null = null; - currentBoundVAO: WebGLVertexArrayObject | null = null; + private currentBoundVAO: WebGLVertexArrayObject | null = null; private currentProgram: Program_GL | null = null; - resourceCreationTracker: ResourceCreationTracker | null = null; + private resourceCreationTracker: ResourceCreationTracker | null = null; private resourceUniqueId = 0; // Cached GL driver state @@ -161,12 +160,12 @@ export class Device_GL implements SwapChain, Device { private currentDepthStencilResolveTo: Texture_GL | null = null; private currentSampleCount = -1; private currentPipeline: RenderPipeline_GL; - private currentInputState: InputState_GL; + private currentIndexBufferByteOffset: number | null = null; private currentMegaState: MegaStateDescriptor = copyMegaState(defaultMegaState); private currentSamplers: (WebGLSampler | null)[] = []; - currentTextures: (WebGLTexture | null)[] = []; + private currentTextures: (WebGLTexture | null)[] = []; private currentUniformBuffers: Buffer[] = []; private currentUniformBufferByteOffsets: number[] = []; @@ -187,28 +186,30 @@ export class Device_GL implements SwapChain, Device { * use DRAW_FRAMEBUFFER in WebGL2 */ private renderPassDrawFramebuffer: WebGLFramebuffer; - readbackFramebuffer: WebGLFramebuffer; + private readbackFramebuffer: WebGLFramebuffer; private fallbackTexture2D: WebGLTexture; private fallbackTexture2DDepth: WebGLTexture; private fallbackTexture2DArray: WebGLTexture; private fallbackTexture3D: WebGLTexture; private fallbackTextureCube: WebGLTexture; + private fallbackVertexBuffer: Buffer; // VendorInfo readonly platformString: string; readonly glslVersion: string; readonly explicitBindingLocations = false; readonly separateSamplerTextures = false; - readonly viewportOrigin = ViewportOrigin.LowerLeft; - readonly clipSpaceNearZ = ClipSpaceNearZ.NegativeOne; - + readonly viewportOrigin = ViewportOrigin.LOWER_LEFT; + readonly clipSpaceNearZ = ClipSpaceNearZ.NEGATIVE_ONE; readonly supportMRT: boolean = false; private inBlitRenderPass = false; private blitRenderPipeline: RenderPipeline; - private blitInputState: InputState; + private blitInputLayout: InputLayout; + private blitVertexBuffer: Buffer; private blitBindings: Bindings; + private blitProgram: Program_GL; // GLimits /** @@ -220,6 +221,7 @@ export class Device_GL implements SwapChain, Device { supportedSampleCounts: number[] = []; maxVertexAttribs: number; occlusionQueriesRecommended = false; + computeShadersSupported = false; gl: WebGLRenderingContext | WebGL2RenderingContext; @@ -292,9 +294,9 @@ export class Device_GL implements SwapChain, Device { width: 0, height: 0, depth: 1, - dimension: TextureDimension.n2D, + dimension: TextureDimension.TEXTURE_2D, numLevels: 1, - usage: TextureUsage.RenderTarget, + usage: TextureUsage.RENDER_TARGET, pixelFormat: this.contextAttributes.alpha === false ? Format.U8_RGB_RT @@ -326,13 +328,18 @@ export class Device_GL implements SwapChain, Device { ); this.fallbackTexture2D = this.createFallbackTexture( - TextureDimension.n2D, + TextureDimension.TEXTURE_2D, SamplerFormatKind.Float, ); this.fallbackTexture2DDepth = this.createFallbackTexture( - TextureDimension.n2D, + TextureDimension.TEXTURE_2D, SamplerFormatKind.Depth, ); + this.fallbackVertexBuffer = this.createBuffer({ + viewOrSize: 1, + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.STATIC, + }); if (isWebGL2(gl)) { // this.fallbackTexture2DArray = this.createFallbackTexture( @@ -350,10 +357,10 @@ export class Device_GL implements SwapChain, Device { } // Adjust for GL defaults. - this.currentMegaState.depthCompare = CompareMode.Less; + this.currentMegaState.depthCompare = CompareMode.LESS; this.currentMegaState.depthWrite = false; this.currentMegaState.attachmentsState[0].channelWriteMask = - ChannelWriteMask.AllChannels; + ChannelWriteMask.ALL; // always have depth test enabled. gl.enable(gl.DEPTH_TEST); @@ -370,11 +377,29 @@ export class Device_GL implements SwapChain, Device { } } + destroy() { + if (this.blitBindings) { + this.blitBindings.destroy(); + } + if (this.blitInputLayout) { + this.blitInputLayout.destroy(); + } + if (this.blitRenderPipeline) { + this.blitRenderPipeline.destroy(); + } + if (this.blitVertexBuffer) { + this.blitVertexBuffer.destroy(); + } + if (this.blitProgram) { + this.blitProgram.destroy(); + } + } + private createFallbackTexture( dimension: TextureDimension, formatKind: SamplerFormatKind, ): WebGLTexture { - const depth = dimension === TextureDimension.Cube ? 6 : 1; + const depth = dimension === TextureDimension.TEXTURE_CUBE_MAP ? 6 : 1; // const supportDepthTexture = // isWebGL2(this.gl) || (!isWebGL2(this.gl) && !!this.WEBGL_depth_texture); const pixelFormat = @@ -385,7 +410,7 @@ export class Device_GL implements SwapChain, Device { const texture = this.createTexture({ dimension, pixelFormat, - usage: TextureUsage.Sampled, + usage: TextureUsage.SAMPLED, width: 1, height: 1, depth, @@ -408,7 +433,7 @@ export class Device_GL implements SwapChain, Device { // ) if (formatKind === SamplerFormatKind.Float) { - texture.setImageData([new Uint8Array(4 * depth)], 0); + texture.setImageData([new Uint8Array(4 * depth)]); } return getPlatformTexture(texture); } @@ -484,7 +509,10 @@ export class Device_GL implements SwapChain, Device { //#region Device // @see https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html - translateTextureInternalFormat(fmt: Format): GLenum { + translateTextureInternalFormat( + fmt: Format, + isRenderbufferStorage = false, + ): GLenum { switch (fmt) { case Format.ALPHA: return GL.ALPHA; @@ -515,7 +543,15 @@ export class Device_GL implements SwapChain, Device { return GL.SRGB8; case Format.U8_RGBA_NORM: case Format.U8_RGBA_RT: - return isWebGL2(this.gl) ? GL.RGBA8 : GL.RGBA; + // WebGL1 renderbuffer only support RGBA4 RGB565 RGB5_A1 + // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/renderbufferStorage#parameters + // But texImage2D allows RGBA + // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D + return isWebGL2(this.gl) + ? GL.RGBA8 + : isRenderbufferStorage + ? GL.RGBA4 + : GL.RGBA; case Format.U8_RGBA_SRGB: case Format.U8_RGBA_RT_SRGB: return GL.SRGB8_ALPHA8; @@ -527,16 +563,16 @@ export class Device_GL implements SwapChain, Device { return this.EXT_texture_norm16.RG16_EXT; case Format.U16_RGBA_NORM: return this.EXT_texture_norm16.RGBA16_EXT; - // case Format.U16_RGBA_5551: - // return GL.RGB5_A1; + case Format.U16_RGBA_5551: + return GL.RGB5_A1; + case Format.U16_RGB_565: + return GL.RGB565; case Format.U32_R: return GL.R32UI; case Format.S8_RGBA_NORM: return GL.RGBA8_SNORM; case Format.S8_RG_NORM: return GL.RG8_SNORM; - case Format.U16_RGBA_5551: - return GL.UNSIGNED_SHORT_5_5_5_1; case Format.BC1: return this.WEBGL_compressed_texture_s3tc.COMPRESSED_RGBA_S3TC_DXT1_EXT; case Format.BC1_SRGB: @@ -770,16 +806,38 @@ export class Device_GL implements SwapChain, Device { } createProgram(descriptor: ProgramDescriptor): Program_GL { - descriptor.ensurePreprocessed(this.queryVendorInfo()); - return this.createProgramSimple(descriptor); + const rawVertexGLSL = descriptor.vertex?.glsl; + // preprocess GLSL first + if (descriptor.vertex.glsl) { + descriptor.vertex.glsl = preprocessShader_GLSL( + this.queryVendorInfo(), + 'vert', + descriptor.vertex.glsl, + ); + } + if (descriptor.fragment.glsl) { + descriptor.fragment.glsl = preprocessShader_GLSL( + this.queryVendorInfo(), + 'frag', + descriptor.fragment.glsl, + ); + } + return this.createProgramSimple(descriptor, rawVertexGLSL); } - createProgramSimple(descriptor: ProgramDescriptor): Program_GL { - return new Program_GL({ - id: this.getNextUniqueId(), - device: this, - descriptor, - }); + private createProgramSimple( + descriptor: ProgramDescriptor, + rawVertexGLSL: string, + ): Program_GL { + const program = new Program_GL( + { + id: this.getNextUniqueId(), + device: this, + descriptor, + }, + rawVertexGLSL, + ); + return program; } createBindings(descriptor: BindingsDescriptor): Bindings { @@ -798,24 +856,6 @@ export class Device_GL implements SwapChain, Device { }); } - createInputState( - _inputLayout: InputLayout, - vertexBuffers: (VertexBufferDescriptor | null)[], - indexBufferBinding: IndexBufferDescriptor | null, - program: Program, - ): InputState { - const inputLayout = _inputLayout as InputLayout_GL; - - return new InputState_GL({ - id: this.getNextUniqueId(), - device: this, - inputLayout, - vertexBuffers, - indexBufferBinding, - program: program as Program_GL, - }); - } - createRenderPipeline(descriptor: RenderPipelineDescriptor): RenderPipeline { return new RenderPipeline_GL({ id: this.getNextUniqueId(), @@ -856,10 +896,44 @@ export class Device_GL implements SwapChain, Device { }); } + private formatRenderPassDescriptor(descriptor: RenderPassDescriptor) { + const { colorAttachment } = descriptor; + + descriptor.depthClearValue = descriptor.depthClearValue ?? 'load'; + descriptor.stencilClearValue = descriptor.stencilClearValue ?? 'load'; + + for (let i = 0; i < colorAttachment.length; i++) { + if (!descriptor.colorAttachmentLevel) { + descriptor.colorAttachmentLevel = []; + } + descriptor.colorAttachmentLevel[i] = + descriptor.colorAttachmentLevel[i] ?? 0; + + if (!descriptor.colorResolveToLevel) { + descriptor.colorResolveToLevel = []; + } + descriptor.colorResolveToLevel[i] = + descriptor.colorResolveToLevel[i] ?? 0; + + if (!descriptor.colorClearColor) { + descriptor.colorClearColor = []; + } + descriptor.colorClearColor[i] = descriptor.colorClearColor[i] ?? 'load'; + + if (!descriptor.colorStore) { + descriptor.colorStore = []; + } + descriptor.colorStore[i] = descriptor.colorStore[i] ?? false; + } + } + createRenderPass(descriptor: RenderPassDescriptor): RenderPass { assert(this.currentRenderPassDescriptor === null); this.currentRenderPassDescriptor = descriptor; + // Format renderpass descriptor + this.formatRenderPassDescriptor(descriptor); + const { colorAttachment, colorAttachmentLevel, @@ -1082,8 +1156,8 @@ export class Device_GL implements SwapChain, Device { } else if (o.type === ResourceType.RenderTarget) { const { gl_renderbuffer } = o as RenderTarget_GL; if (gl_renderbuffer !== null) assignPlatformName(gl_renderbuffer, name); - } else if (o.type === ResourceType.InputState) { - assignPlatformName((o as InputState_GL).vao, name); + } else if (o.type === ResourceType.InputLayout) { + assignPlatformName((o as InputLayout_GL).vao, name); } } @@ -1097,15 +1171,21 @@ export class Device_GL implements SwapChain, Device { this.resourceCreationTracker.checkForLeaks(); } - pushDebugGroup(debugGroup: DebugGroup): void { - this.debugGroupStack.push(debugGroup); - } + pushDebugGroup(name: string): void {} - popDebugGroup(): void { - this.debugGroupStack.pop(); - } + popDebugGroup(): void {} + + insertDebugMarker(markerLabel: string) {} - programPatched(o: Program, descriptor: ProgramDescriptorSimple): void { + // pushDebugGroup(debugGroup: DebugGroup): void { + // this.debugGroupStack.push(debugGroup); + // } + + // popDebugGroup(): void { + // this.debugGroupStack.pop(); + // } + + programPatched(o: Program, descriptor: ProgramDescriptor): void { assert(this.shaderDebug); // const program = o as Program_GL; @@ -1141,7 +1221,7 @@ export class Device_GL implements SwapChain, Device { this.debugGroupStack[i].drawCallCount += count; } - debugGroupStatisticsBufferUpload(count = 1): void { + private debugGroupStatisticsBufferUpload(count = 1): void { for (let i = this.debugGroupStack.length - 1; i >= 0; i--) this.debugGroupStack[i].bufferUploadCount += count; } @@ -1177,17 +1257,14 @@ export class Device_GL implements SwapChain, Device { const descriptor = program.descriptor; if ( - !this.reportShaderError( - program.gl_shader_vert, - descriptor.preprocessedVert, - ) + !this.reportShaderError(program.gl_shader_vert, descriptor.vertex.glsl) ) return; if ( !this.reportShaderError( program.gl_shader_frag, - descriptor.preprocessedFrag, + descriptor.fragment.glsl, ) ) return; @@ -1312,7 +1389,7 @@ export class Device_GL implements SwapChain, Device { } } - if (this.currentDepthStencilAttachment !== null) { + if (this.currentDepthStencilAttachment) { if (sampleCount === -1) { sampleCount = this.currentDepthStencilAttachment.sampleCount; width = this.currentDepthStencilAttachment.width; @@ -1444,7 +1521,7 @@ export class Device_GL implements SwapChain, Device { if (this.currentDepthStencilResolveTo !== depthStencilResolveTo) { this.currentDepthStencilResolveTo = depthStencilResolveTo as Texture_GL; - if (depthStencilResolveTo !== null) { + if (depthStencilResolveTo) { this.resolveDepthStencilAttachmentsChanged = true; } } @@ -1461,10 +1538,7 @@ export class Device_GL implements SwapChain, Device { if (this.OES_draw_buffers_indexed !== null) { const attachment = this.currentMegaState.attachmentsState[slot]; - if ( - attachment && - attachment.channelWriteMask !== ChannelWriteMask.AllChannels - ) { + if (attachment && attachment.channelWriteMask !== ChannelWriteMask.ALL) { this.OES_draw_buffers_indexed.colorMaskiOES( slot, true, @@ -1472,16 +1546,13 @@ export class Device_GL implements SwapChain, Device { true, true, ); - attachment.channelWriteMask = ChannelWriteMask.AllChannels; + attachment.channelWriteMask = ChannelWriteMask.ALL; } } else { const attachment = this.currentMegaState.attachmentsState[0]; - if ( - attachment && - attachment.channelWriteMask !== ChannelWriteMask.AllChannels - ) { + if (attachment && attachment.channelWriteMask !== ChannelWriteMask.ALL) { gl.colorMask(true, true, true, true); - attachment.channelWriteMask = ChannelWriteMask.AllChannels; + attachment.channelWriteMask = ChannelWriteMask.ALL; } } @@ -1497,13 +1568,13 @@ export class Device_GL implements SwapChain, Device { } private setRenderPassParametersClearDepthStencil( - depthClearValue: number | 'load', - stencilClearValue: number | 'load', + depthClearValue: number | 'load' = 'load', + stencilClearValue: number | 'load' = 'load', ): void { const gl = this.gl; if (depthClearValue !== 'load') { - assert(this.currentDepthStencilAttachment !== null); + assert(!!this.currentDepthStencilAttachment); // GL clears obey the masks... bad API or worst API? if (!this.currentMegaState.depthWrite) { gl.depthMask(true); @@ -1517,7 +1588,7 @@ export class Device_GL implements SwapChain, Device { } } if (stencilClearValue !== 'load') { - assert(this.currentDepthStencilAttachment !== null); + assert(!!this.currentDepthStencilAttachment); if (!this.currentMegaState.stencilWrite) { gl.enable(gl.STENCIL_TEST); gl.stencilMask(0xff); @@ -1668,10 +1739,10 @@ export class Device_GL implements SwapChain, Device { ) { dbi.colorMaskiOES( i, - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Red), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Green), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Blue), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Alpha), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.RED), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.GREEN), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.BLUE), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.ALPHA), ); currentAttachmentState.channelWriteMask = newAttachmentState.channelWriteMask; @@ -1747,10 +1818,10 @@ export class Device_GL implements SwapChain, Device { newAttachmentState.channelWriteMask ) { gl.colorMask( - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Red), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Green), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Blue), - !!(newAttachmentState.channelWriteMask & ChannelWriteMask.Alpha), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.RED), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.GREEN), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.BLUE), + !!(newAttachmentState.channelWriteMask & ChannelWriteMask.ALPHA), ); currentAttachmentState.channelWriteMask = newAttachmentState.channelWriteMask; @@ -1850,12 +1921,12 @@ export class Device_GL implements SwapChain, Device { currentMegaState.depthCompare = newMegaState.depthCompare; } - if (currentMegaState.depthWrite !== newMegaState.depthWrite) { + if (!!currentMegaState.depthWrite !== !!newMegaState.depthWrite) { gl.depthMask(newMegaState.depthWrite); currentMegaState.depthWrite = newMegaState.depthWrite; } - if (currentMegaState.stencilWrite !== newMegaState.stencilWrite) { + if (!!currentMegaState.stencilWrite !== !!newMegaState.stencilWrite) { // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/stencilMask gl.stencilMask(newMegaState.stencilWrite ? 0xff : 0x00); currentMegaState.stencilWrite = newMegaState.stencilWrite; @@ -1875,17 +1946,17 @@ export class Device_GL implements SwapChain, Device { } if (currentMegaState.cullMode !== newMegaState.cullMode) { - if (currentMegaState.cullMode === CullMode.None) { + if (currentMegaState.cullMode === CullMode.NONE) { gl.enable(gl.CULL_FACE); - } else if (newMegaState.cullMode === CullMode.None) { + } else if (newMegaState.cullMode === CullMode.NONE) { gl.disable(gl.CULL_FACE); } - if (newMegaState.cullMode === CullMode.Back) { + if (newMegaState.cullMode === CullMode.BACK) { gl.cullFace(gl.BACK); - } else if (newMegaState.cullMode === CullMode.Front) { + } else if (newMegaState.cullMode === CullMode.FRONT) { gl.cullFace(gl.FRONT); - } else if (newMegaState.cullMode === CullMode.FrontAndBack) { + } else if (newMegaState.cullMode === CullMode.FRONT_AND_BACK) { gl.cullFace(gl.FRONT_AND_BACK); } currentMegaState.cullMode = newMegaState.cullMode; @@ -1914,7 +1985,7 @@ export class Device_GL implements SwapChain, Device { assert(attachment.pixelFormat === pipeline.colorAttachmentFormats[i]); } - if (this.currentDepthStencilAttachment !== null) { + if (this.currentDepthStencilAttachment) { assert( this.currentDepthStencilAttachment.pixelFormat === pipeline.depthStencilAttachmentFormat, @@ -1944,7 +2015,7 @@ export class Device_GL implements SwapChain, Device { const deviceProgram = program.descriptor; const uniformBlocks = findall( - deviceProgram.preprocessedVert, + deviceProgram.vertex.glsl, /uniform (\w+) {([^]*?)}/g, ); @@ -1961,7 +2032,7 @@ export class Device_GL implements SwapChain, Device { } const samplers = findall( - deviceProgram.preprocessedVert, + deviceProgram.vertex.glsl, /^uniform .*sampler\S+ (\w+);\s* \/\/ BINDING=(\d+)$/gm, ); for (let i = 0; i < samplers.length; i++) { @@ -1974,17 +2045,70 @@ export class Device_GL implements SwapChain, Device { } } - setInputState(inputState_: InputState | null): void { - const inputState = inputState_ as InputState_GL; - this.currentInputState = inputState; - if (this.currentInputState !== null) { + setVertexInput( + inputLayout_: InputLayout | null, + vertexBuffers: (VertexBufferDescriptor | null)[] | null, + indexBuffer: IndexBufferDescriptor | null, + ): void { + if (inputLayout_ !== null) { + assert(this.currentPipeline.inputLayout === inputLayout_); + const inputLayout = inputLayout_ as InputLayout_GL; + + this.bindVAO(inputLayout.vao); + + const gl = this.gl; + for (let i = 0; i < inputLayout.vertexAttributeDescriptors.length; i++) { + const attr = inputLayout.vertexAttributeDescriptors[i]; + + // find location by name in WebGL1 + const location = isWebGL2(gl) + ? attr.location + : inputLayout.program.attributes[attr.location]?.location; + + if (!isNil(location)) { + const vertexBuffer = vertexBuffers![attr.bufferIndex]; + + if (vertexBuffer === null) continue; + + const format = inputLayout.vertexBufferFormats[i]; + + gl.bindBuffer( + gl.ARRAY_BUFFER, + getPlatformBuffer(vertexBuffer.buffer), + ); + + const bufferOffset = + (vertexBuffer.byteOffset || 0) + attr.bufferByteOffset; + + const inputLayoutBuffer = + inputLayout.vertexBufferDescriptors[attr.bufferIndex]!; + gl.vertexAttribPointer( + location, + format.size, + format.type, + format.normalized, + inputLayoutBuffer.byteStride, + bufferOffset, + ); + } + } + assert( - this.currentPipeline.inputLayout === this.currentInputState.inputLayout, + (indexBuffer !== null) === (inputLayout.indexBufferFormat !== null), ); - this.bindVAO(this.currentInputState.vao); + if (indexBuffer !== null) { + const buffer = indexBuffer.buffer as Buffer_GL; + assert(buffer.usage === BufferUsage.INDEX); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, getPlatformBuffer(buffer)); + this.currentIndexBufferByteOffset = indexBuffer.byteOffset; + } else { + this.currentIndexBufferByteOffset = null; + } } else { assert(this.currentPipeline.inputLayout === null); + assert(indexBuffer === null); this.bindVAO(null); + this.currentIndexBufferByteOffset = 0; } } @@ -1996,58 +2120,86 @@ export class Device_GL implements SwapChain, Device { this.applyStencil(); } - draw(count: number, firstVertex: number): void { + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-draw + */ + draw( + vertexCount: number, + instanceCount?: number, + firstVertex?: number, + firstInstance?: number, + ) { const gl = this.gl; const pipeline = this.currentPipeline; - gl.drawArrays(pipeline.drawMode, firstVertex, count); - this.debugGroupStatisticsDrawCall(); - this.debugGroupStatisticsTriangles(count / 3); - } + if (instanceCount) { + const params: [number, number, number, number] = [ + pipeline.drawMode, + firstVertex || 0, + vertexCount, + instanceCount, + ]; + if (isWebGL2(gl)) { + gl.drawArraysInstanced(...params); + } else { + this.ANGLE_instanced_arrays.drawArraysInstancedANGLE(...params); + } + } else { + gl.drawArrays(pipeline.drawMode, firstVertex, vertexCount); + } - drawIndexed(count: number, firstIndex: number): void { - const gl = this.gl; - const pipeline = this.currentPipeline; - const inputState = this.currentInputState; - const byteOffset = - assertExists(inputState.indexBufferByteOffset) + - firstIndex * assertExists(inputState.indexBufferCompByteSize); - gl.drawElements( - pipeline.drawMode, - count, - assertExists(inputState.indexBufferType), - byteOffset, - ); this.debugGroupStatisticsDrawCall(); - this.debugGroupStatisticsTriangles(count / 3); + this.debugGroupStatisticsTriangles( + (vertexCount / 3) * Math.max(instanceCount, 1), + ); } - - drawIndexedInstanced( - count: number, - firstIndex: number, - instanceCount: number, - ): void { + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindexed + */ + drawIndexed( + indexCount: number, + instanceCount?: number, + firstIndex?: number, + baseVertex?: number, + firstInstance?: number, + ) { const gl = this.gl; - const pipeline = this.currentPipeline; - const inputState = this.currentInputState; + const pipeline = this.currentPipeline, + inputLayout = assertExists(pipeline.inputLayout); const byteOffset = - assertExists(inputState.indexBufferByteOffset) + - firstIndex * assertExists(inputState.indexBufferCompByteSize); - - const params: [number, number, number, number, number] = [ - pipeline.drawMode, - count, - assertExists(inputState.indexBufferType), - byteOffset, - instanceCount, - ]; - if (isWebGL2(gl)) { - gl.drawElementsInstanced(...params); + assertExists(this.currentIndexBufferByteOffset) + + firstIndex * inputLayout.indexBufferCompByteSize!; + if (instanceCount) { + const params: [number, number, number, number, number] = [ + pipeline.drawMode, + indexCount, + inputLayout.indexBufferType!, + byteOffset, + instanceCount, + ]; + if (isWebGL2(gl)) { + gl.drawElementsInstanced(...params); + } else { + this.ANGLE_instanced_arrays.drawElementsInstancedANGLE(...params); + } } else { - this.ANGLE_instanced_arrays.drawElementsInstancedANGLE(...params); + gl.drawElements( + pipeline.drawMode, + indexCount, + inputLayout.indexBufferType!, + byteOffset, + ); } this.debugGroupStatisticsDrawCall(); - this.debugGroupStatisticsTriangles((count / 3) * instanceCount); + this.debugGroupStatisticsTriangles( + (indexCount / 3) * Math.max(instanceCount, 1), + ); + } + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindirect + */ + drawIndirect(indirectBuffer: Buffer, indirectOffset: number) { + // TODO } beginOcclusionQuery(dstOffs: number): void { @@ -2068,10 +2220,6 @@ export class Device_GL implements SwapChain, Device { } } - beginDebugGroup(name: string): void {} - - endDebugGroup(): void {} - pipelineQueryReady(o: RenderPipeline): boolean { const pipeline = o as RenderPipeline_GL; return this.queryProgramReady(pipeline.program); @@ -2161,7 +2309,7 @@ export class Device_GL implements SwapChain, Device { didUnbindDraw = true; } - if (!this.currentRenderPassDescriptor!.colorStore[i]) { + if (!this.currentRenderPassDescriptor.colorStore[i]) { if (!didBindRead) { gl.bindFramebuffer( isWebGL2(gl) ? GL.READ_FRAMEBUFFER : GL.FRAMEBUFFER, @@ -2193,11 +2341,11 @@ export class Device_GL implements SwapChain, Device { this.resolveColorAttachmentsChanged = false; const depthStencilResolveFrom = this.currentDepthStencilAttachment; - if (depthStencilResolveFrom !== null) { + if (depthStencilResolveFrom) { const depthStencilResolveTo = this.currentDepthStencilResolveTo; let didBindRead = false; - if (depthStencilResolveTo !== null) { + if (depthStencilResolveTo) { assert( depthStencilResolveFrom.width === depthStencilResolveTo.width && depthStencilResolveFrom.height === depthStencilResolveTo.height, @@ -2301,7 +2449,7 @@ export class Device_GL implements SwapChain, Device { } private applyStencil(): void { - if (this.currentStencilRef === null) { + if (isNil(this.currentStencilRef)) { return; } this.gl.stencilFunc( @@ -2332,14 +2480,23 @@ export class Device_GL implements SwapChain, Device { resolveTo: Texture_GL, ) { if (!this.blitRenderPipeline) { - const vertexBuffer = makeDataBuffer( + const program = new CopyProgram(); + this.blitProgram = this.createProgram({ + vertex: { + glsl: program.vert, + }, + fragment: { + glsl: program.frag, + }, + }); + this.blitVertexBuffer = makeDataBuffer( this, BufferUsage.VERTEX | BufferUsage.COPY_DST, new Float32Array([-4, -4, 4, -4, 0, 4]).buffer, ); - const inputLayout = this.createInputLayout({ + this.blitInputLayout = this.createInputLayout({ vertexBufferDescriptors: [ - { byteStride: 4 * 2, frequency: VertexBufferFrequency.PerVertex }, + { byteStride: 4 * 2, stepMode: VertexStepMode.VERTEX }, ], vertexAttributeDescriptors: [ { @@ -2350,47 +2507,34 @@ export class Device_GL implements SwapChain, Device { }, ], indexBufferFormat: null, + program: this.blitProgram, }); const bindingLayouts: BindingLayoutDescriptor[] = [ { numSamplers: 1, numUniformBuffers: 0 }, ]; - const program = this.createProgram({ - ...preprocessProgramObj_GLSL(this, new CopyProgram()), - ensurePreprocessed: () => {}, - associate: () => {}, - }); - this.blitInputState = this.createInputState( - inputLayout, - [{ buffer: vertexBuffer, byteOffset: 0 }], - null, - program, - ); this.blitRenderPipeline = this.createRenderPipeline({ - topology: PrimitiveTopology.Triangles, + topology: PrimitiveTopology.TRIANGLES, sampleCount: 1, - program, + program: this.blitProgram, bindingLayouts, colorAttachmentFormats: [Format.U8_RGBA_RT], depthStencilAttachmentFormat: null, - inputLayout, - // megaStateDescriptor: copyMegaState(defaultMegaState), - megaStateDescriptor: this.currentMegaState, + inputLayout: this.blitInputLayout, + megaStateDescriptor: copyMegaState(defaultMegaState), }); - // const colorTexture = this.currentColorAttachments[0].texture; this.blitBindings = this.createBindings({ bindingLayout: bindingLayouts[0], samplerBindings: [ { sampler: null, texture: resolveFrom.texture, - lateBinding: null, }, ], uniformBufferBindings: [], }); - program.setUniforms({ + this.blitProgram.setUniformsLegacy({ u_Texture: resolveFrom, }); } @@ -2403,23 +2547,18 @@ export class Device_GL implements SwapChain, Device { const blitRenderPass = this.createRenderPass({ colorAttachment: [resolveFrom], - colorResolveToLevel: [0], colorResolveTo: [resolveTo], colorClearColor: [TransparentWhite], - colorStore: [true], - colorAttachmentLevel: [0], - depthStencilAttachment: null, - depthStencilResolveTo: null, - depthStencilStore: true, - depthClearValue: 'load', - stencilClearValue: 'load', - occlusionQueryPool: null, }); const { width, height } = this.getCanvas() as HTMLCanvasElement; blitRenderPass.setPipeline(this.blitRenderPipeline); blitRenderPass.setBindings(0, this.blitBindings, [0]); - blitRenderPass.setInputState(this.blitInputState); + blitRenderPass.setVertexInput( + this.blitInputLayout, + [{ buffer: this.blitVertexBuffer }], + null, + ); blitRenderPass.setViewport(0, 0, width, height); // disable blending for blit diff --git a/packages/g-plugin-webgl-device/src/platform/Growable.ts b/packages/g-plugin-webgl-device/src/platform/Growable.ts deleted file mode 100644 index c29ec268e..000000000 --- a/packages/g-plugin-webgl-device/src/platform/Growable.ts +++ /dev/null @@ -1,27 +0,0 @@ -type ArrayBufferView2 = Float32Array | Uint32Array; - -export class Growable { - public b: T; - public i: number; - public o: number; - - constructor(public m: (n: number) => T, public a: number = 0x400) { - this.i = this.a; - this.b = m(this.i); - this.o = 0; - } - - public r() { - this.o = 0; - } - - public n(v: number) { - if (this.o + 1 > this.b.length) { - const b = this.m(this.b.length + this.a); - b.set(this.b); - this.b = b; - } - - this.b[this.o++] = v; - } -} diff --git a/packages/g-plugin-webgl-device/src/platform/InputLayout.ts b/packages/g-plugin-webgl-device/src/platform/InputLayout.ts index 28082ce47..c4e2f7905 100644 --- a/packages/g-plugin-webgl-device/src/platform/InputLayout.ts +++ b/packages/g-plugin-webgl-device/src/platform/InputLayout.ts @@ -1,19 +1,40 @@ -import { Format, ResourceType, assert } from '@antv/g-plugin-device-renderer'; +import { + Format, + ResourceType, + VertexStepMode, + assert, + assertExists, + getFormatCompByteSize, +} from '@antv/g-plugin-device-renderer'; import type { InputLayout, InputLayoutBufferDescriptor, InputLayoutDescriptor, VertexAttributeDescriptor, } from '@antv/g-plugin-device-renderer'; +import { isNil } from '@antv/util'; import type { Device_GL } from './Device'; import { ResourceBase_GL } from './ResourceBase'; +import { + getPlatformBuffer, + isFormatSizedInteger, + isWebGL2, + translateIndexFormat, + translateVertexFormat, +} from './utils'; +import { Program_GL } from './Program'; export class InputLayout_GL extends ResourceBase_GL implements InputLayout { type: ResourceType.InputLayout = ResourceType.InputLayout; vertexAttributeDescriptors: VertexAttributeDescriptor[]; vertexBufferDescriptors: (InputLayoutBufferDescriptor | null)[]; + vertexBufferFormats: ReturnType[]; indexBufferFormat: Format | null; + indexBufferType: GLenum | null; + indexBufferCompByteSize: number | null; + vao: WebGLVertexArrayObject; + program: Program_GL; constructor({ id, @@ -26,15 +47,114 @@ export class InputLayout_GL extends ResourceBase_GL implements InputLayout { }) { super({ id, device }); - const { vertexAttributeDescriptors, vertexBufferDescriptors, indexBufferFormat } = descriptor; + const { + vertexAttributeDescriptors, + vertexBufferDescriptors, + indexBufferFormat, + program, + } = descriptor; assert( indexBufferFormat === Format.U16_R || indexBufferFormat === Format.U32_R || indexBufferFormat === null, ); + const indexBufferType = + indexBufferFormat !== null + ? translateIndexFormat(indexBufferFormat) + : null; + const indexBufferCompByteSize = + indexBufferFormat !== null + ? getFormatCompByteSize(indexBufferFormat) + : null; + + const gl = this.device.gl; + const vao = this.device.ensureResourceExists( + isWebGL2(gl) + ? gl.createVertexArray() + : device.OES_vertex_array_object.createVertexArrayOES(), + ); + if (isWebGL2(gl)) { + gl.bindVertexArray(vao); + } else { + device.OES_vertex_array_object.bindVertexArrayOES(vao); + } + + gl.bindBuffer( + gl.ARRAY_BUFFER, + getPlatformBuffer(this.device['fallbackVertexBuffer']), + ); + + const vertexBufferFormats = []; + for (let i = 0; i < vertexAttributeDescriptors.length; i++) { + const attr = vertexAttributeDescriptors[i]; + + const { format, divisor = 1, bufferIndex } = attr; + // find location by name in WebGL1 + const location = isWebGL2(gl) + ? attr.location + : (program as Program_GL).attributes[attr.location]?.location; + + const vertexFormat = translateVertexFormat(format); + vertexBufferFormats.push(vertexFormat); + + if (!isNil(location)) { + if (isFormatSizedInteger(format)) { + // See https://groups.google.com/d/msg/angleproject/yQb5DaCzcWg/Ova0E3wcAQAJ for more info. + // console.warn("Vertex format uses sized integer types; this will cause a shader recompile on ANGLE platforms"); + // debugger; + } + + const { size, type, normalized } = vertexFormat; + + const inputLayoutBuffer = assertExists( + vertexBufferDescriptors[bufferIndex], + ); + + gl.vertexAttribPointer(location, size, type, normalized, 0, 0); + + if (inputLayoutBuffer.stepMode === VertexStepMode.INSTANCE) { + if (isWebGL2(gl)) { + // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/vertexAttribDivisor + gl.vertexAttribDivisor(location, divisor); + } else { + device.ANGLE_instanced_arrays.vertexAttribDivisorANGLE( + location, + divisor, + ); + } + } + + gl.enableVertexAttribArray(location); + } + } + + if (isWebGL2(gl)) { + gl.bindVertexArray(null); + } else { + device.OES_vertex_array_object.bindVertexArrayOES(null); + } this.vertexAttributeDescriptors = vertexAttributeDescriptors; this.vertexBufferDescriptors = vertexBufferDescriptors; + this.vao = vao; + this.vertexBufferFormats = vertexBufferFormats; this.indexBufferFormat = indexBufferFormat; + this.indexBufferType = indexBufferType; + this.indexBufferCompByteSize = indexBufferCompByteSize; + this.program = program as Program_GL; + } + + destroy() { + super.destroy(); + if (this.device['currentBoundVAO'] === this.vao) { + if (isWebGL2(this.device.gl)) { + this.device.gl.bindVertexArray(null); + this.device.gl.deleteVertexArray(this.vao); + } else { + this.device.OES_vertex_array_object.bindVertexArrayOES(null); + this.device.OES_vertex_array_object.deleteVertexArrayOES(this.vao); + } + this.device['currentBoundVAO'] = null; + } } } diff --git a/packages/g-plugin-webgl-device/src/platform/InputState.ts b/packages/g-plugin-webgl-device/src/platform/InputState.ts deleted file mode 100644 index 3071938a7..000000000 --- a/packages/g-plugin-webgl-device/src/platform/InputState.ts +++ /dev/null @@ -1,154 +0,0 @@ -import type { - IndexBufferDescriptor, - InputState, - VertexBufferDescriptor, -} from '@antv/g-plugin-device-renderer'; -import { - assert, - assertExists, - BufferUsage, - getFormatCompByteSize, - ResourceType, - VertexBufferFrequency, -} from '@antv/g-plugin-device-renderer'; -import { isNil } from '@antv/util'; -import type { Buffer_GL } from './Buffer'; -import type { Device_GL } from './Device'; -import type { InputLayout_GL } from './InputLayout'; -import type { Program_GL } from './Program'; -import { ResourceBase_GL } from './ResourceBase'; -import { - getPlatformBuffer, - isFormatSizedInteger, - isWebGL2, - translateIndexFormat, - translateVertexFormat, -} from './utils'; - -export class InputState_GL extends ResourceBase_GL implements InputState { - type: ResourceType.InputState = ResourceType.InputState; - - vao: WebGLVertexArrayObject; - indexBufferByteOffset: number | null; - indexBufferType: GLenum | null; - indexBufferCompByteSize: number | null; - inputLayout: InputLayout_GL; - vertexBuffers: (VertexBufferDescriptor | null)[]; - - constructor({ - id, - device, - inputLayout, - vertexBuffers, - indexBufferBinding, - program, - }: { - id: number; - device: Device_GL; - inputLayout: InputLayout_GL; - vertexBuffers: (VertexBufferDescriptor | null)[]; - indexBufferBinding: IndexBufferDescriptor | null; - program: Program_GL; - }) { - super({ id, device }); - - const gl = this.device.gl; - const vao = this.device.ensureResourceExists( - isWebGL2(gl) ? gl.createVertexArray() : device.OES_vertex_array_object.createVertexArrayOES(), - ); - - if (isWebGL2(gl)) { - gl.bindVertexArray(vao); - } else { - device.OES_vertex_array_object.bindVertexArrayOES(vao); - } - device.currentBoundVAO = vao; - - for (let i = 0; i < inputLayout.vertexAttributeDescriptors.length; i++) { - const attr = inputLayout.vertexAttributeDescriptors[i]; - - const { format, divisor = 1, byteStride, bufferByteOffset, bufferIndex } = attr; - // find location by name in WebGL1 - const location = isWebGL2(gl) ? attr.location : program.attributes[attr.location]?.location; - - if (!isNil(location)) { - if (isFormatSizedInteger(format)) { - // See https://groups.google.com/d/msg/angleproject/yQb5DaCzcWg/Ova0E3wcAQAJ for more info. - // console.warn("Vertex format uses sized integer types; this will cause a shader recompile on ANGLE platforms"); - // debugger; - } - - const { size, type, normalized } = translateVertexFormat(format); - const vertexBuffer = vertexBuffers[bufferIndex]; - if (vertexBuffer === null) continue; - - const inputLayoutBuffer = assertExists(inputLayout.vertexBufferDescriptors[bufferIndex]); - - const buffer = vertexBuffer.buffer as Buffer_GL; - assert(!!(buffer.usage & BufferUsage.VERTEX)); - gl.bindBuffer(gl.ARRAY_BUFFER, getPlatformBuffer(vertexBuffer.buffer)); - - const bufferOffset = vertexBuffer.byteOffset + bufferByteOffset; - gl.vertexAttribPointer( - location, - size, - type, - normalized, - byteStride || inputLayoutBuffer.byteStride, - bufferOffset, - ); - - if (inputLayoutBuffer.frequency === VertexBufferFrequency.PerInstance) { - if (isWebGL2(gl)) { - // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/vertexAttribDivisor - gl.vertexAttribDivisor(location, divisor); - } else { - device.ANGLE_instanced_arrays.vertexAttribDivisorANGLE(location, divisor); - } - } - - gl.enableVertexAttribArray(location); - } - } - - let indexBufferType: GLenum | null = null; - let indexBufferCompByteSize: number | null = null; - let indexBufferByteOffset: number | null = null; - if (indexBufferBinding !== null) { - const buffer = indexBufferBinding.buffer as Buffer_GL; - assert(!!(buffer.usage & BufferUsage.INDEX)); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, getPlatformBuffer(indexBufferBinding.buffer)); - indexBufferType = translateIndexFormat(assertExists(inputLayout.indexBufferFormat)); - indexBufferCompByteSize = getFormatCompByteSize(inputLayout.indexBufferFormat!); - indexBufferByteOffset = indexBufferBinding.byteOffset; - } - - if (isWebGL2(gl)) { - gl.bindVertexArray(null); - } else { - device.OES_vertex_array_object.bindVertexArrayOES(null); - } - device.currentBoundVAO = null; - - this.vao = vao; - this.indexBufferByteOffset = indexBufferByteOffset; - this.indexBufferType = indexBufferType; - this.indexBufferCompByteSize = indexBufferCompByteSize; - this.inputLayout = inputLayout; - this.vertexBuffers = vertexBuffers; - } - - destroy() { - super.destroy(); - if (this.device.currentBoundVAO === this.vao) { - if (isWebGL2(this.device.gl)) { - this.device.gl.bindVertexArray(null); - this.device.gl.deleteVertexArray(this.vao); - } else { - this.device.OES_vertex_array_object.bindVertexArrayOES(null); - this.device.OES_vertex_array_object.deleteVertexArrayOES(this.vao); - } - this.device.currentBoundVAO = null; - } - } -} diff --git a/packages/g-plugin-webgl-device/src/platform/Program.ts b/packages/g-plugin-webgl-device/src/platform/Program.ts index cd84fc352..24980546c 100644 --- a/packages/g-plugin-webgl-device/src/platform/Program.ts +++ b/packages/g-plugin-webgl-device/src/platform/Program.ts @@ -1,6 +1,6 @@ import type { Program, - ProgramDescriptorSimple, + ProgramDescriptor, } from '@antv/g-plugin-device-renderer'; import { assert, @@ -9,7 +9,6 @@ import { getUniformSetter, parseUniformName, ResourceType, - preprocessShader_GLSL, } from '@antv/g-plugin-device-renderer'; import { isNil } from '@antv/util'; import type { Device_GL } from './Device'; @@ -17,20 +16,20 @@ import { ResourceBase_GL } from './ResourceBase'; import { Texture_GL } from './Texture'; import { isWebGL2 } from './utils'; -const quadVert = ` -layout(location = 0) in vec2 a_Position; +// const quadVert = ` +// layout(location = 0) in vec2 a_Position; -out vec2 v_TexCoord; +// out vec2 v_TexCoord; -void main() { - v_TexCoord = 0.5 * (a_Position + 1.0); - gl_Position = vec4(a_Position, 0., 1.); +// void main() { +// v_TexCoord = 0.5 * (a_Position + 1.0); +// gl_Position = vec4(a_Position, 0., 1.); - #ifdef VIEWPORT_ORIGIN_TL - v_TexCoord.y = 1.0 - v_TexCoord.y; - #endif -} -`; +// #ifdef VIEWPORT_ORIGIN_TL +// v_TexCoord.y = 1.0 - v_TexCoord.y; +// #endif +// } +// `; export enum ProgramCompileState_GL { NeedsCompile, @@ -46,7 +45,7 @@ export class Program_GL extends ResourceBase_GL implements Program { gl_shader_vert: WebGLShader | null; gl_shader_frag: WebGLShader | null; compileState: ProgramCompileState_GL; - descriptor: ProgramDescriptorSimple; + descriptor: ProgramDescriptor; uniformSetters: Record = {}; attributes: { @@ -56,15 +55,18 @@ export class Program_GL extends ResourceBase_GL implements Program { size: number; }[] = []; - constructor({ - id, - device, - descriptor, - }: { - id: number; - device: Device_GL; - descriptor: ProgramDescriptorSimple; - }) { + constructor( + { + id, + device, + descriptor, + }: { + id: number; + device: Device_GL; + descriptor: ProgramDescriptor; + }, + private rawVertexGLSL: string, + ) { super({ id, device }); const gl = this.device.gl; @@ -94,25 +96,25 @@ export class Program_GL extends ResourceBase_GL implements Program { if (this.gl_shader_vert !== null) gl.deleteShader(this.gl_shader_vert); if (this.gl_shader_frag !== null) gl.deleteShader(this.gl_shader_frag); - if (descriptor.preprocessedCompute) { - this.gl_shader_vert = this.compileShader( - preprocessShader_GLSL(this.device.queryVendorInfo(), 'vert', quadVert), - gl.VERTEX_SHADER, - ); - this.gl_shader_frag = this.compileShader( - descriptor.preprocessedCompute, - gl.FRAGMENT_SHADER, - ); - } else { - this.gl_shader_vert = this.compileShader( - descriptor.preprocessedVert, - gl.VERTEX_SHADER, - ); - this.gl_shader_frag = this.compileShader( - descriptor.preprocessedFrag, - gl.FRAGMENT_SHADER, - ); - } + // if (descriptor.compute) { + // this.gl_shader_vert = this.compileShader( + // preprocessShader_GLSL(this.device.queryVendorInfo(), 'vert', quadVert), + // gl.VERTEX_SHADER, + // ); + // this.gl_shader_frag = this.compileShader( + // descriptor.preprocessedCompute, + // gl.FRAGMENT_SHADER, + // ); + // } else { + this.gl_shader_vert = this.compileShader( + descriptor.vertex.glsl, + gl.VERTEX_SHADER, + ); + this.gl_shader_frag = this.compileShader( + descriptor.fragment.glsl, + gl.FRAGMENT_SHADER, + ); + // } gl.attachShader(this.gl_program, this.gl_shader_vert); gl.attachShader(this.gl_program, this.gl_shader_frag); @@ -132,8 +134,12 @@ export class Program_GL extends ResourceBase_GL implements Program { const gl = this.device.gl; const count = gl.getProgramParameter(this.gl_program, gl.ACTIVE_ATTRIBUTES); - const defines = getDefines(this.descriptor.preprocessedVert); - const locations = getAttributeLocations(this.descriptor.vert, defines); + const defines = getDefines(this.descriptor.vertex.glsl); + const locations = getAttributeLocations( + // Use raw GLSL + this.rawVertexGLSL, + defines, + ); for (let index = 0; index < count; index++) { const { name, type, size } = gl.getActiveAttrib(this.gl_program, index); const location = gl.getAttribLocation(this.gl_program, name); @@ -187,7 +193,7 @@ export class Program_GL extends ResourceBase_GL implements Program { return shader; } - setUniforms(uniforms: Record = {}) { + setUniformsLegacy(uniforms: Record = {}) { const gl = this.device.gl; if (!isWebGL2(gl)) { diff --git a/packages/g-plugin-webgl-device/src/platform/Readback.ts b/packages/g-plugin-webgl-device/src/platform/Readback.ts index 4ca2786f7..ea19d6308 100644 --- a/packages/g-plugin-webgl-device/src/platform/Readback.ts +++ b/packages/g-plugin-webgl-device/src/platform/Readback.ts @@ -103,7 +103,10 @@ export class Readback_GL extends ResourceBase_GL implements Readback { gl.bufferData(gl.PIXEL_PACK_BUFFER, length, gl.STREAM_READ); gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - gl.bindFramebuffer(GL.READ_FRAMEBUFFER, this.device.readbackFramebuffer); + gl.bindFramebuffer( + GL.READ_FRAMEBUFFER, + this.device['readbackFramebuffer'], + ); gl.framebufferTexture2D( GL.READ_FRAMEBUFFER, GL.COLOR_ATTACHMENT0, @@ -133,7 +136,7 @@ export class Readback_GL extends ResourceBase_GL implements Readback { length, ); } else { - gl.bindFramebuffer(GL.FRAMEBUFFER, this.device.readbackFramebuffer); + gl.bindFramebuffer(GL.FRAMEBUFFER, this.device['readbackFramebuffer']); gl.framebufferTexture2D( GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, @@ -164,7 +167,7 @@ export class Readback_GL extends ResourceBase_GL implements Readback { const texture = t as Texture_GL; const gl_type = this.device.translateTextureType(texture.pixelFormat); - gl.bindFramebuffer(GL.FRAMEBUFFER, this.device.readbackFramebuffer); + gl.bindFramebuffer(GL.FRAMEBUFFER, this.device['readbackFramebuffer']); gl.framebufferTexture2D( GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, diff --git a/packages/g-plugin-webgl-device/src/platform/RenderPipeline.ts b/packages/g-plugin-webgl-device/src/platform/RenderPipeline.ts index d430cc2ad..3403493fb 100644 --- a/packages/g-plugin-webgl-device/src/platform/RenderPipeline.ts +++ b/packages/g-plugin-webgl-device/src/platform/RenderPipeline.ts @@ -9,6 +9,9 @@ import { defaultBindingLayoutSamplerDescriptor, ResourceType, assert, + PrimitiveTopology, + defaultMegaState, + copyMegaState, } from '@antv/g-plugin-device-renderer'; import type { Device_GL } from './Device'; import type { InputLayout_GL } from './InputLayout'; @@ -31,7 +34,10 @@ export interface BindingLayouts_GL { bindingLayoutTables: BindingLayoutTable_GL[]; } -export class RenderPipeline_GL extends ResourceBase_GL implements RenderPipeline { +export class RenderPipeline_GL + extends ResourceBase_GL + implements RenderPipeline +{ type: ResourceType.RenderPipeline = ResourceType.RenderPipeline; bindingLayouts: BindingLayouts_GL; @@ -57,22 +63,31 @@ export class RenderPipeline_GL extends ResourceBase_GL implements RenderPipeline super({ id, device }); this.bindingLayouts = this.createBindingLayouts(descriptor.bindingLayouts); - this.drawMode = translatePrimitiveTopology(descriptor.topology); + this.drawMode = translatePrimitiveTopology( + descriptor.topology ?? PrimitiveTopology.TRIANGLES, + ); this.program = descriptor.program as Program_GL; this.inputLayout = descriptor.inputLayout as InputLayout_GL | null; - this.megaState = descriptor.megaStateDescriptor; + this.megaState = { + ...copyMegaState(defaultMegaState), + ...descriptor.megaStateDescriptor, + }; + this.colorAttachmentFormats = descriptor.colorAttachmentFormats.slice(); this.depthStencilAttachmentFormat = descriptor.depthStencilAttachmentFormat; - this.sampleCount = descriptor.sampleCount; + this.sampleCount = descriptor.sampleCount ?? 1; } - private createBindingLayouts(bindingLayouts: BindingLayoutDescriptor[]): BindingLayouts_GL { + private createBindingLayouts( + bindingLayouts: BindingLayoutDescriptor[] = [], + ): BindingLayouts_GL { let firstUniformBuffer = 0; let firstSampler = 0; const bindingLayoutTables: BindingLayoutTable_GL[] = []; for (let i = 0; i < bindingLayouts.length; i++) { - const { numUniformBuffers, numSamplers, samplerEntries } = bindingLayouts[i]; + const { numUniformBuffers, numSamplers, samplerEntries } = + bindingLayouts[i]; const bindingSamplerEntries: BindingLayoutSamplerDescriptor_GL[] = []; @@ -82,9 +97,14 @@ export class RenderPipeline_GL extends ResourceBase_GL implements RenderPipeline for (let j = 0; j < numSamplers; j++) { const samplerEntry = - samplerEntries !== undefined ? samplerEntries[j] : defaultBindingLayoutSamplerDescriptor; + samplerEntries !== undefined + ? samplerEntries[j] + : defaultBindingLayoutSamplerDescriptor; const { dimension, formatKind } = samplerEntry; - bindingSamplerEntries.push({ gl_target: translateTextureDimension(dimension), formatKind }); + bindingSamplerEntries.push({ + gl_target: translateTextureDimension(dimension), + formatKind, + }); } bindingLayoutTables.push({ diff --git a/packages/g-plugin-webgl-device/src/platform/RenderTarget.ts b/packages/g-plugin-webgl-device/src/platform/RenderTarget.ts index acd786a88..f0bc76902 100644 --- a/packages/g-plugin-webgl-device/src/platform/RenderTarget.ts +++ b/packages/g-plugin-webgl-device/src/platform/RenderTarget.ts @@ -31,7 +31,7 @@ export class RenderTarget_GL extends ResourceBase_GL implements RenderTarget { const gl = this.device.gl; - const { pixelFormat, width, height, sampleCount, texture } = descriptor; + const { pixelFormat, width, height, sampleCount = 1, texture } = descriptor; let useRenderbuffer = false; // @see https://blog.tojicode.com/2012/07/using-webgldepthtexture.html @@ -41,8 +41,8 @@ export class RenderTarget_GL extends ResourceBase_GL implements RenderTarget { !isWebGL2(gl) && !device.WEBGL_depth_texture ) { - // texture.destroy(); - // texture = null; + texture.destroy(); + this.texture = null; useRenderbuffer = true; } @@ -54,7 +54,10 @@ export class RenderTarget_GL extends ResourceBase_GL implements RenderTarget { ); gl.bindRenderbuffer(gl.RENDERBUFFER, this.gl_renderbuffer); - const gl_format = this.device.translateTextureInternalFormat(pixelFormat); + const gl_format = this.device.translateTextureInternalFormat( + pixelFormat, + true, + ); if (isWebGL2(gl)) { // @see https://github.com/shrekshao/MoveWebGL1EngineToWebGL2/blob/master/Move-a-WebGL-1-Engine-To-WebGL-2-Blog-2.md#multisampled-renderbuffers @@ -81,5 +84,8 @@ export class RenderTarget_GL extends ResourceBase_GL implements RenderTarget { if (this.gl_renderbuffer !== null) { this.device.gl.deleteRenderbuffer(this.gl_renderbuffer); } + if (this.texture) { + this.texture.destroy(); + } } } diff --git a/packages/g-plugin-webgl-device/src/platform/ResourceBase.ts b/packages/g-plugin-webgl-device/src/platform/ResourceBase.ts index e5735aa44..5b3926938 100644 --- a/packages/g-plugin-webgl-device/src/platform/ResourceBase.ts +++ b/packages/g-plugin-webgl-device/src/platform/ResourceBase.ts @@ -22,16 +22,16 @@ export class ResourceBase_GL this.id = id; this.device = device; - if (this.device.resourceCreationTracker !== null) { - this.device.resourceCreationTracker.trackResourceCreated( + if (this.device['resourceCreationTracker'] !== null) { + this.device['resourceCreationTracker'].trackResourceCreated( this as unknown as Resource, ); } } destroy() { - if (this.device.resourceCreationTracker !== null) { - this.device.resourceCreationTracker.trackResourceDestroyed( + if (this.device['resourceCreationTracker'] !== null) { + this.device['resourceCreationTracker'].trackResourceDestroyed( this as unknown as Resource, ); } diff --git a/packages/g-plugin-webgl-device/src/platform/Sampler.ts b/packages/g-plugin-webgl-device/src/platform/Sampler.ts index ec2be461a..5ac72efc0 100644 --- a/packages/g-plugin-webgl-device/src/platform/Sampler.ts +++ b/packages/g-plugin-webgl-device/src/platform/Sampler.ts @@ -6,10 +6,18 @@ import { assert, isPowerOfTwo, } from '@antv/g-plugin-device-renderer'; -import type { Sampler, SamplerDescriptor } from '@antv/g-plugin-device-renderer'; +import type { + Sampler, + SamplerDescriptor, +} from '@antv/g-plugin-device-renderer'; import type { Device_GL } from './Device'; import { ResourceBase_GL } from './ResourceBase'; -import { getPlatformSampler, isWebGL2, translateFilterMode, translateWrapMode } from './utils'; +import { + getPlatformSampler, + isWebGL2, + translateFilterMode, + translateWrapMode, +} from './utils'; /** * In WebGL 1 texture image data and sampling information are both stored in texture objects @@ -36,8 +44,16 @@ export class Sampler_GL extends ResourceBase_GL implements Sampler { if (isWebGL2(gl)) { const gl_sampler = this.device.ensureResourceExists(gl.createSampler()); - gl.samplerParameteri(gl_sampler, GL.TEXTURE_WRAP_S, translateWrapMode(descriptor.wrapS)); - gl.samplerParameteri(gl_sampler, GL.TEXTURE_WRAP_T, translateWrapMode(descriptor.wrapT)); + gl.samplerParameteri( + gl_sampler, + GL.TEXTURE_WRAP_S, + translateWrapMode(descriptor.wrapS), + ); + gl.samplerParameteri( + gl_sampler, + GL.TEXTURE_WRAP_T, + translateWrapMode(descriptor.wrapT), + ); gl.samplerParameteri( gl_sampler, GL.TEXTURE_WRAP_R, @@ -51,7 +67,7 @@ export class Sampler_GL extends ResourceBase_GL implements Sampler { gl.samplerParameteri( gl_sampler, GL.TEXTURE_MAG_FILTER, - translateFilterMode(descriptor.magFilter, MipFilterMode.NoMip), + translateFilterMode(descriptor.magFilter, MipFilterMode.NO_MIP), ); if (descriptor.minLOD !== undefined) { @@ -61,16 +77,27 @@ export class Sampler_GL extends ResourceBase_GL implements Sampler { gl.samplerParameterf(gl_sampler, GL.TEXTURE_MAX_LOD, descriptor.maxLOD); } if (descriptor.compareMode !== undefined) { - gl.samplerParameteri(gl_sampler, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); - gl.samplerParameteri(gl_sampler, gl.TEXTURE_COMPARE_FUNC, descriptor.compareMode); + gl.samplerParameteri( + gl_sampler, + gl.TEXTURE_COMPARE_MODE, + gl.COMPARE_REF_TO_TEXTURE, + ); + gl.samplerParameteri( + gl_sampler, + gl.TEXTURE_COMPARE_FUNC, + descriptor.compareMode, + ); } const maxAnisotropy = descriptor.maxAnisotropy ?? 1; - if (maxAnisotropy > 1 && this.device.EXT_texture_filter_anisotropic !== null) { + if ( + maxAnisotropy > 1 && + this.device.EXT_texture_filter_anisotropic !== null + ) { assert( - descriptor.minFilter === TexFilterMode.Bilinear && - descriptor.magFilter === TexFilterMode.Bilinear && - descriptor.mipFilter === MipFilterMode.Linear, + descriptor.minFilter === TexFilterMode.BILINEAR && + descriptor.magFilter === TexFilterMode.BILINEAR && + descriptor.mipFilter === MipFilterMode.LINEAR, ); gl.samplerParameterf( gl_sampler, @@ -100,13 +127,21 @@ export class Sampler_GL extends ResourceBase_GL implements Sampler { translateFilterMode(descriptor.minFilter, descriptor.mipFilter), ); } - gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, translateWrapMode(descriptor.wrapS)); - gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, translateWrapMode(descriptor.wrapT)); + gl.texParameteri( + GL.TEXTURE_2D, + GL.TEXTURE_WRAP_S, + translateWrapMode(descriptor.wrapS), + ); + gl.texParameteri( + GL.TEXTURE_2D, + GL.TEXTURE_WRAP_T, + translateWrapMode(descriptor.wrapT), + ); gl.texParameteri( gl_target, GL.TEXTURE_MAG_FILTER, - translateFilterMode(descriptor.magFilter, MipFilterMode.NoMip), + translateFilterMode(descriptor.magFilter, MipFilterMode.NO_MIP), ); // if (descriptor.minLOD !== undefined) { @@ -117,11 +152,14 @@ export class Sampler_GL extends ResourceBase_GL implements Sampler { // } const maxAnisotropy = descriptor.maxAnisotropy ?? 1; - if (maxAnisotropy > 1 && this.device.EXT_texture_filter_anisotropic !== null) { + if ( + maxAnisotropy > 1 && + this.device.EXT_texture_filter_anisotropic !== null + ) { assert( - descriptor.minFilter === TexFilterMode.Bilinear && - descriptor.magFilter === TexFilterMode.Bilinear && - descriptor.mipFilter === MipFilterMode.Linear, + descriptor.minFilter === TexFilterMode.BILINEAR && + descriptor.magFilter === TexFilterMode.BILINEAR && + descriptor.mipFilter === MipFilterMode.LINEAR, ); gl.texParameteri( gl_target, diff --git a/packages/g-plugin-webgl-device/src/platform/Texture.ts b/packages/g-plugin-webgl-device/src/platform/Texture.ts index 3db8b9eea..f577d77a7 100644 --- a/packages/g-plugin-webgl-device/src/platform/Texture.ts +++ b/packages/g-plugin-webgl-device/src/platform/Texture.ts @@ -14,7 +14,11 @@ import { } from '@antv/g-plugin-device-renderer'; import type { Device_GL } from './Device'; import { ResourceBase_GL } from './ResourceBase'; -import { getPlatformTexture, isWebGL2 } from './utils'; +import { + getPlatformTexture, + // isTextureFormatCompressed, + isWebGL2, +} from './utils'; export class Texture_GL extends ResourceBase_GL implements Texture { type: ResourceType.Texture = ResourceType.Texture; @@ -49,11 +53,19 @@ export class Texture_GL extends ResourceBase_GL implements Texture { }) { super({ id, device }); + // Default values. + descriptor = { + dimension: TextureDimension.TEXTURE_2D, + depth: 1, + numLevels: 1, + ...descriptor, + }; + const gl = this.device.gl; let gl_target: GLenum; let gl_texture: WebGLTexture; const numLevels = this.clampNumLevels(descriptor); - this.immutable = !!descriptor.immutable; + this.immutable = descriptor.immutable ?? true; this.pixelStore = descriptor.pixelStore; this.pixelFormat = descriptor.pixelFormat; this.formatKind = getFormatSamplerKind(descriptor.pixelFormat); @@ -70,11 +82,11 @@ export class Texture_GL extends ResourceBase_GL implements Texture { descriptor.pixelFormat, ); this.device.setActiveTexture(gl.TEXTURE0); - this.device.currentTextures[0] = null; + this.device['currentTextures'][0] = null; this.preprocessImage(); - if (descriptor.dimension === TextureDimension.n2D) { + if (descriptor.dimension === TextureDimension.TEXTURE_2D) { gl_target = GL.TEXTURE_2D; gl.bindTexture(gl_target, gl_texture); if (this.immutable) { @@ -103,6 +115,11 @@ export class Texture_GL extends ResourceBase_GL implements Texture { !device.WEBGL_depth_texture ) { } else { + // if (!isWebGL2(gl)) { + // if (internalformat === GL.RGBA4) { + // internalformat = GL.RGBA; + // } + // } // @see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D gl.texImage2D( gl_target, @@ -141,7 +158,7 @@ export class Texture_GL extends ResourceBase_GL implements Texture { } assert(descriptor.depth === 1); - } else if (descriptor.dimension === TextureDimension.n2DArray) { + } else if (descriptor.dimension === TextureDimension.TEXTURE_2D_ARRAY) { gl_target = GL.TEXTURE_2D_ARRAY; gl.bindTexture(gl_target, gl_texture); @@ -156,7 +173,7 @@ export class Texture_GL extends ResourceBase_GL implements Texture { descriptor.depth, ); } - } else if (descriptor.dimension === TextureDimension.n3D) { + } else if (descriptor.dimension === TextureDimension.TEXTURE_3D) { gl_target = GL.TEXTURE_3D; gl.bindTexture(gl_target, gl_texture); @@ -170,7 +187,7 @@ export class Texture_GL extends ResourceBase_GL implements Texture { descriptor.depth, ); } - } else if (descriptor.dimension === TextureDimension.Cube) { + } else if (descriptor.dimension === TextureDimension.TEXTURE_CUBE_MAP) { gl_target = GL.TEXTURE_CUBE_MAP; gl.bindTexture(gl_target, gl_texture); if (isWebGL2(gl)) { @@ -193,19 +210,32 @@ export class Texture_GL extends ResourceBase_GL implements Texture { this.numLevels = numLevels; } - setImageData(data: TexImageSource | ArrayBufferView[], level: number) { + setImageData( + levelDatas: (TexImageSource | ArrayBufferView)[], + firstMipLevel = 0, + ) { + // const maxMipLevel = Math.min( + // firstMipLevel + levelDatas.length, + // this.numLevels, + // ); + const gl = this.device.gl; // const isCompressed = isTextureFormatCompressed(this.pixelFormat); - // const is3D = this.gl_target === GL.TEXTURE_3D || this.gl_target === GL.TEXTURE_2D_ARRAY; + // const is3D = + // this.gl_target === GL.TEXTURE_3D || + // this.gl_target === GL.TEXTURE_2D_ARRAY; // const isCube = this.gl_target === GL.TEXTURE_CUBE_MAP; - const isArray = Array.isArray(data); + // @ts-ignore + const isTypedArray = levelDatas[0].buffer; this.device.setActiveTexture(gl.TEXTURE0); - this.device.currentTextures[0] = null; + this.device['currentTextures'][0] = null; + + const data = levelDatas[0]; let width: number; let height: number; - if (isArray) { + if (isTypedArray) { width = this.width; height = this.height; } else { @@ -221,16 +251,29 @@ export class Texture_GL extends ResourceBase_GL implements Texture { } gl.bindTexture(this.gl_target, this.gl_texture); - // let w = this.width; - // let h = this.height; - // let d = this.depth; - // const maxMipLevel = Math.min(firstMipLevel + levelDatasSize, this.numLevels); const gl_format = this.device.translateTextureFormat(this.pixelFormat); const gl_type = this.device.translateTextureType(this.pixelFormat); this.preprocessImage(); + // for (let i = 0, levelDatasIdx = 0; i < maxMipLevel; i++) { + // if (i >= firstMipLevel) { + // const levelData = levelDatas[levelDatasIdx++] as Uint8Array; + // const sliceElementSize = levelData.length / this.depth; + + // if (isCube) { + + // } else if (is3D) { + + // } else if (isArray) { + + // } else { + + // } + // } + // } + if (this.immutable) { // must use texSubImage2D instead of texImage2D, since texture is immutable // @see https://stackoverflow.com/questions/56123201/unity-plugin-texture-is-immutable?rq=1 @@ -245,7 +288,7 @@ export class Texture_GL extends ResourceBase_GL implements Texture { gl_format, gl_type, // @ts-ignore - isArray ? data[0] : data, + data, ); } else { if (isWebGL2(gl)) { @@ -261,11 +304,11 @@ export class Texture_GL extends ResourceBase_GL implements Texture { gl_format, // TODO: can be different with gl_format gl_type, // @ts-ignore - isArray ? data[0] : data, + data, ); } else { // WebGL1: upload Array & Image separately - if (isArray) { + if (isTypedArray) { (gl as WebGLRenderingContext).texImage2D( this.gl_target, 0, @@ -275,7 +318,8 @@ export class Texture_GL extends ResourceBase_GL implements Texture { 0, gl_format, gl_type, - data[0], + // @ts-ignore + data, ); } else { (gl as WebGLRenderingContext).texImage2D( @@ -302,7 +346,7 @@ export class Texture_GL extends ResourceBase_GL implements Texture { private clampNumLevels(descriptor: TextureDescriptor): number { if ( - descriptor.dimension === TextureDimension.n2DArray && + descriptor.dimension === TextureDimension.TEXTURE_2D_ARRAY && descriptor.depth > 1 ) { const typeFlags: FormatTypeFlags = getFormatTypeFlags( diff --git a/packages/g-plugin-webgl-device/src/platform/utils.ts b/packages/g-plugin-webgl-device/src/platform/utils.ts index 07aa3fc4c..8b7244b5c 100644 --- a/packages/g-plugin-webgl-device/src/platform/utils.ts +++ b/packages/g-plugin-webgl-device/src/platform/utils.ts @@ -83,9 +83,9 @@ export function isFormatSizedInteger(fmt: Format): boolean { export function translateBufferHint(hint: BufferFrequencyHint): GLenum { switch (hint) { - case BufferFrequencyHint.Static: + case BufferFrequencyHint.STATIC: return GL.STATIC_DRAW; - case BufferFrequencyHint.Dynamic: + case BufferFrequencyHint.DYNAMIC: return GL.DYNAMIC_DRAW; } } @@ -104,15 +104,15 @@ export function translatePrimitiveTopology( topology: PrimitiveTopology, ): GLenum { switch (topology) { - case PrimitiveTopology.Triangles: + case PrimitiveTopology.TRIANGLES: return GL.TRIANGLES; - case PrimitiveTopology.Points: + case PrimitiveTopology.POINTS: return GL.POINTS; - case PrimitiveTopology.TriangleStrip: + case PrimitiveTopology.TRIANGLE_STRIP: return GL.TRIANGLE_STRIP; - case PrimitiveTopology.Lines: + case PrimitiveTopology.LINES: return GL.LINES; - case PrimitiveTopology.LineStrip: + case PrimitiveTopology.LINE_STRIP: return GL.LINE_STRIP; default: throw new Error('Unknown primitive topology mode'); @@ -185,11 +185,11 @@ export function translateIndexFormat(format: Format): GLenum { export function translateWrapMode(wrapMode: WrapMode): GLenum { switch (wrapMode) { - case WrapMode.Clamp: + case WrapMode.CLAMP: return GL.CLAMP_TO_EDGE; - case WrapMode.Repeat: + case WrapMode.REPEAT: return GL.REPEAT; - case WrapMode.Mirror: + case WrapMode.MIRROR: return GL.MIRRORED_REPEAT; default: throw new Error('whoops'); @@ -200,25 +200,25 @@ export function translateFilterMode( filter: TexFilterMode, mipFilter: MipFilterMode, ): GLenum { - if (mipFilter === MipFilterMode.Linear && filter === TexFilterMode.Bilinear) { + if (mipFilter === MipFilterMode.LINEAR && filter === TexFilterMode.BILINEAR) { return GL.LINEAR_MIPMAP_LINEAR; } - if (mipFilter === MipFilterMode.Linear && filter === TexFilterMode.Point) { + if (mipFilter === MipFilterMode.LINEAR && filter === TexFilterMode.POINT) { return GL.NEAREST_MIPMAP_LINEAR; } if ( - mipFilter === MipFilterMode.Nearest && - filter === TexFilterMode.Bilinear + mipFilter === MipFilterMode.NEAREST && + filter === TexFilterMode.BILINEAR ) { return GL.LINEAR_MIPMAP_NEAREST; } - if (mipFilter === MipFilterMode.Nearest && filter === TexFilterMode.Point) { + if (mipFilter === MipFilterMode.NEAREST && filter === TexFilterMode.POINT) { return GL.NEAREST_MIPMAP_NEAREST; } - if (mipFilter === MipFilterMode.NoMip && filter === TexFilterMode.Bilinear) { + if (mipFilter === MipFilterMode.NO_MIP && filter === TexFilterMode.BILINEAR) { return GL.LINEAR; } - if (mipFilter === MipFilterMode.NoMip && filter === TexFilterMode.Point) { + if (mipFilter === MipFilterMode.NO_MIP && filter === TexFilterMode.POINT) { return GL.NEAREST; } throw new Error('Unknown texture filter mode'); @@ -259,9 +259,9 @@ export function findall(haystack: string, needle: RegExp): RegExpExecArray[] { export function isBlendStateNone(blendState: ChannelBlendState): boolean { return ( - blendState.blendMode == BlendMode.Add && - blendState.blendSrcFactor == BlendFactor.One && - blendState.blendDstFactor === BlendFactor.Zero + blendState.blendMode == BlendMode.ADD && + blendState.blendSrcFactor == BlendFactor.ONE && + blendState.blendDstFactor === BlendFactor.ZERO ); } @@ -275,10 +275,12 @@ export function translateQueryPoolType(type: QueryPoolType): GLenum { } export function translateTextureDimension(dimension: TextureDimension): GLenum { - if (dimension === TextureDimension.n2D) return GL.TEXTURE_2D; - else if (dimension === TextureDimension.n2DArray) return GL.TEXTURE_2D_ARRAY; - else if (dimension === TextureDimension.Cube) return GL.TEXTURE_CUBE_MAP; - else if (dimension === TextureDimension.n3D) return GL.TEXTURE_3D; + if (dimension === TextureDimension.TEXTURE_2D) return GL.TEXTURE_2D; + else if (dimension === TextureDimension.TEXTURE_2D_ARRAY) + return GL.TEXTURE_2D_ARRAY; + else if (dimension === TextureDimension.TEXTURE_CUBE_MAP) + return GL.TEXTURE_CUBE_MAP; + else if (dimension === TextureDimension.TEXTURE_3D) return GL.TEXTURE_3D; else throw new Error('whoops'); } diff --git a/packages/g-plugin-webgpu-device/CHANGELOG.md b/packages/g-plugin-webgpu-device/CHANGELOG.md index c41a0ddb1..cf69c492b 100644 --- a/packages/g-plugin-webgpu-device/CHANGELOG.md +++ b/packages/g-plugin-webgpu-device/CHANGELOG.md @@ -1,5 +1,15 @@ # @antv/g-plugin-webgpu-device +## 1.9.17 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- 568ec0f4: Export Device API in webgl & webgpu. +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + ## 1.9.16 ### Patch Changes diff --git a/packages/g-plugin-webgpu-device/package.json b/packages/g-plugin-webgpu-device/package.json index 6ca675972..b90280169 100644 --- a/packages/g-plugin-webgpu-device/package.json +++ b/packages/g-plugin-webgpu-device/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-webgpu-device", - "version": "1.9.16", + "version": "1.9.17", "description": "A G plugin implements GPUDevice interface with WebGPU API", "keywords": [ "antv", diff --git a/packages/g-plugin-webgpu-device/src/index.ts b/packages/g-plugin-webgpu-device/src/index.ts index 408e0db71..c696d7f18 100644 --- a/packages/g-plugin-webgpu-device/src/index.ts +++ b/packages/g-plugin-webgpu-device/src/index.ts @@ -1,6 +1,8 @@ import { AbstractRendererPlugin, GlobalRuntime } from '@antv/g-lite'; import type { WebGPUDeviceOptions } from './interfaces'; import { WebGPUDeviceContribution } from './WebGPUDeviceContribution'; + +export { WebGPUDeviceContribution }; export class Plugin extends AbstractRendererPlugin { name = 'webgpu-device'; constructor(private options: Partial = {}) { diff --git a/packages/g-plugin-webgpu-device/src/platform/Bindings.ts b/packages/g-plugin-webgpu-device/src/platform/Bindings.ts index 5d9859255..0a3583e32 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Bindings.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Bindings.ts @@ -36,7 +36,7 @@ export class Bindings_WebGPU extends ResourceBase_WebGPU implements Bindings { const { pipeline, bindingLayout } = descriptor; assert(!!pipeline); - const bindGroupLayout = this.device._createBindGroupLayout(bindingLayout); + const bindGroupLayout = this.device['createBindGroupLayout'](bindingLayout); // entries orders: Storage(read-only storage) Uniform Sampler const gpuBindGroupEntries: GPUBindGroupEntry[][] = [[], []]; @@ -83,7 +83,7 @@ export class Bindings_WebGPU extends ResourceBase_WebGPU implements Bindings { const texture = binding.texture !== null ? binding.texture - : this.device.getFallbackTexture(samplerEntry); + : this.device['getFallbackTexture'](samplerEntry); assert(samplerEntry.dimension === (texture as Texture_WebGPU).dimension); assert( samplerEntry.formatKind === @@ -98,7 +98,7 @@ export class Bindings_WebGPU extends ResourceBase_WebGPU implements Bindings { const sampler = binding.sampler !== null ? binding.sampler - : this.device.getFallbackSampler(samplerEntry); + : this.device['getFallbackSampler'](samplerEntry); const gpuSampler = getPlatformSampler(sampler); gpuBindGroupEntries[1].push({ binding: numBindings++, diff --git a/packages/g-plugin-webgpu-device/src/platform/ComputePass.ts b/packages/g-plugin-webgpu-device/src/platform/ComputePass.ts index 10e60f6c4..47287100d 100644 --- a/packages/g-plugin-webgpu-device/src/platform/ComputePass.ts +++ b/packages/g-plugin-webgpu-device/src/platform/ComputePass.ts @@ -1,9 +1,11 @@ import type { + Buffer, Bindings, ComputePass, ComputePipeline, } from '@antv/g-plugin-device-renderer'; import { assert, assertExists } from '@antv/g-plugin-device-renderer'; +import type { Buffer_WebGPU } from './Buffer'; import type { Bindings_WebGPU } from './Bindings'; import type { ComputePipeline_WebGPU } from './ComputePipeline'; @@ -13,11 +15,28 @@ export class ComputePass_WebGPU implements ComputePass { private gpuComputePassEncoder: GPUComputePassEncoder | null = null; /** - * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatch + * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatchworkgroups */ - dispatch(x: number, y?: number, z?: number): void { - this.gpuComputePassEncoder.dispatchWorkgroups(x, y, z); - // TODO: dispatchIndirect read from GPUBuffer + dispatchWorkgroups( + workgroupCountX: number, + workgroupCountY?: number, + workgroupCountZ?: number, + ): void { + this.gpuComputePassEncoder.dispatchWorkgroups( + workgroupCountX, + workgroupCountY, + workgroupCountZ, + ); + } + + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpucomputepassencoder-dispatchworkgroupsindirect + */ + dispatchWorkgroupsIndirect(indirectBuffer: Buffer, indirectOffset: number) { + this.gpuComputePassEncoder.dispatchWorkgroupsIndirect( + (indirectBuffer as Buffer_WebGPU).gpuBuffer, + indirectOffset, + ); } finish() { @@ -56,17 +75,15 @@ export class ComputePass_WebGPU implements ComputePass { ); } - public beginDebugGroup(name: string): void { - // FIREFOX MISSING - if (this.gpuComputePassEncoder!.pushDebugGroup === undefined) return; - - this.gpuComputePassEncoder!.pushDebugGroup(name); + pushDebugGroup(name: string): void { + this.gpuComputePassEncoder.pushDebugGroup(name); } - public endDebugGroup(): void { - // FIREFOX MISSING - if (this.gpuComputePassEncoder!.popDebugGroup === undefined) return; + popDebugGroup(): void { + this.gpuComputePassEncoder.popDebugGroup(); + } - this.gpuComputePassEncoder!.popDebugGroup(); + insertDebugMarker(markerLabel: string) { + this.gpuComputePassEncoder.insertDebugMarker(markerLabel); } } diff --git a/packages/g-plugin-webgpu-device/src/platform/Device.ts b/packages/g-plugin-webgpu-device/src/platform/Device.ts index 3d5830d69..2e54180a6 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Device.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Device.ts @@ -8,16 +8,12 @@ import type { ComputePass, ComputePipeline, ComputePipelineDescriptor, - DebugGroup, Device, DeviceLimits, - IndexBufferDescriptor, InputLayout, InputLayoutDescriptor, - InputState, Program, ProgramDescriptor, - ProgramDescriptorSimple, QueryPool, QueryPoolType, Readback, @@ -34,7 +30,6 @@ import type { Texture, TextureDescriptor, VendorInfo, - VertexBufferDescriptor, } from '@antv/g-plugin-device-renderer'; import { assert, @@ -53,6 +48,10 @@ import { ViewportOrigin, WrapMode, CompareMode, + preprocessShader_GLSL, + defaultMegaState, + PrimitiveTopology, + copyMegaState, } from '@antv/g-plugin-device-renderer'; import type { glsl_compile as glsl_compile_ } from '../../../../rust/pkg/glsl_wgsl_compiler'; import { Bindings_WebGPU } from './Bindings'; @@ -61,7 +60,6 @@ import { ComputePass_WebGPU } from './ComputePass'; import { ComputePipeline_WebGPU } from './ComputePipeline'; import { GPUTextureUsage } from './constants'; import { InputLayout_WebGPU } from './InputLayout'; -import { InputState_WebGPU } from './InputState'; import type { Attachment_WebGPU, BindGroupLayout, @@ -112,8 +110,8 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { private fallbackTexture2DArray: Texture_WebGPU; private fallbackTexture3D: Texture_WebGPU; private fallbackTextureCube: Texture_WebGPU; - fallbackSamplerFiltering: Sampler; - fallbackSamplerComparison: Sampler; + private fallbackSamplerFiltering: Sampler; + private fallbackSamplerComparison: Sampler; private featureTextureCompressionBC = false; // VendorInfo @@ -121,15 +119,15 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { readonly glslVersion = `#version 440`; readonly explicitBindingLocations = true; readonly separateSamplerTextures = true; - readonly viewportOrigin = ViewportOrigin.UpperLeft; - readonly clipSpaceNearZ = ClipSpaceNearZ.Zero; + readonly viewportOrigin = ViewportOrigin.UPPER_LEFT; + readonly clipSpaceNearZ = ClipSpaceNearZ.ZERO; readonly supportsSyncPipelineCompilation: boolean = false; readonly supportMRT: boolean = true; device: GPUDevice; private canvas: HTMLCanvasElement | OffscreenCanvas; - canvasContext: GPUCanvasContext; - glsl_compile: typeof glsl_compile_; + private canvasContext: GPUCanvasContext; + private glsl_compile: typeof glsl_compile_; constructor( adapter: GPUAdapter, @@ -144,32 +142,47 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { this.glsl_compile = glsl_compile; this.fallbackTexture2D = this.createFallbackTexture( - TextureDimension.n2D, + TextureDimension.TEXTURE_2D, SamplerFormatKind.Float, ); + this.setResourceName(this.fallbackTexture2D, 'Fallback Texture2D'); + this.fallbackTexture2DDepth = this.createFallbackTexture( - TextureDimension.n2D, + TextureDimension.TEXTURE_2D, SamplerFormatKind.Depth, ); + this.setResourceName( + this.fallbackTexture2DDepth, + 'Fallback Depth Texture2D', + ); + this.fallbackTexture2DArray = this.createFallbackTexture( - TextureDimension.n2DArray, + TextureDimension.TEXTURE_2D_ARRAY, SamplerFormatKind.Float, ); + this.setResourceName( + this.fallbackTexture2DArray, + 'Fallback Texture2DArray', + ); + this.fallbackTexture3D = this.createFallbackTexture( - TextureDimension.n3D, + TextureDimension.TEXTURE_3D, SamplerFormatKind.Float, ); + this.setResourceName(this.fallbackTexture3D, 'Fallback Texture3D'); + this.fallbackTextureCube = this.createFallbackTexture( - TextureDimension.Cube, + TextureDimension.TEXTURE_CUBE_MAP, SamplerFormatKind.Float, ); + this.setResourceName(this.fallbackTextureCube, 'Fallback TextureCube'); this.fallbackSamplerFiltering = this.createSampler({ - wrapS: WrapMode.Repeat, - wrapT: WrapMode.Repeat, - minFilter: TexFilterMode.Point, - magFilter: TexFilterMode.Point, - mipFilter: MipFilterMode.Nearest, + wrapS: WrapMode.REPEAT, + wrapT: WrapMode.REPEAT, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.POINT, + mipFilter: MipFilterMode.NEAREST, }); this.setResourceName( this.fallbackSamplerFiltering, @@ -177,16 +190,16 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { ); this.fallbackSamplerComparison = this.createSampler({ - wrapS: WrapMode.Repeat, - wrapT: WrapMode.Repeat, - minFilter: TexFilterMode.Point, - magFilter: TexFilterMode.Point, - mipFilter: MipFilterMode.Nearest, - compareMode: CompareMode.Always, + wrapS: WrapMode.REPEAT, + wrapT: WrapMode.REPEAT, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.POINT, + mipFilter: MipFilterMode.NEAREST, + compareMode: CompareMode.ALWAYS, }); this.setResourceName( - this.fallbackSamplerFiltering, - 'Fallback Sampler Filtering', + this.fallbackSamplerComparison, + 'Fallback Sampler Comparison Filtering', ); // Firefox doesn't support GPUDevice.features yet... @@ -212,6 +225,8 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { }); } + destroy(): void {} + // SwapChain configureSwapChain(width: number, height: number): void { if (this.swapChainWidth === width && this.swapChainHeight === height) @@ -233,7 +248,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { width: this.swapChainWidth, height: this.swapChainHeight, depth: 0, - dimension: TextureDimension.n2D, + dimension: TextureDimension.TEXTURE_2D, numLevels: 1, usage: this.swapChainTextureUsage, }, @@ -245,6 +260,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { texture.gpuTexture = gpuTexture; texture.gpuTextureView = gpuTextureView; texture.name = 'Onscreen'; + this.setResourceName(texture, 'Onscreen Texture'); return texture; } @@ -315,11 +331,12 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { device: this, descriptor: { ...descriptor, - dimension: TextureDimension.n2D, + dimension: TextureDimension.TEXTURE_2D, numLevels: 1, depth: 1, - usage: TextureUsage.RenderTarget, + usage: TextureUsage.RENDER_TARGET, }, + sampleCount: descriptor.sampleCount, }) as unknown as Attachment_WebGPU; texture.depthOrArrayLayers = 1; @@ -351,7 +368,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { width, height, depth: depthOrArrayLayers, - dimension: TextureDimension.n2D, + dimension: TextureDimension.TEXTURE_2D, numLevels, usage, }, @@ -366,11 +383,22 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { } createProgram(descriptor: ProgramDescriptor): Program { - descriptor.ensurePreprocessed(this); - return this.createProgramSimple(descriptor); - } + // preprocess GLSL first + if (descriptor.vertex?.glsl) { + descriptor.vertex.glsl = preprocessShader_GLSL( + this.queryVendorInfo(), + 'vert', + descriptor.vertex.glsl, + ); + } + if (descriptor.fragment?.glsl) { + descriptor.fragment.glsl = preprocessShader_GLSL( + this.queryVendorInfo(), + 'frag', + descriptor.fragment.glsl, + ); + } - createProgramSimple(descriptor: ProgramDescriptorSimple): Program { return new Program_WebGPU({ id: this.getNextUniqueId(), device: this, @@ -401,7 +429,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { texture.depthOrArrayLayers = descriptor.depthOrArrayLayers; texture.numLevels = mipLevelCount; texture.usage = usage; - texture.sampleCount = 1; + texture.sampleCount = descriptor.sampleCount; if (!skipCreate) { const gpuTexture = this.device.createTexture({ @@ -409,6 +437,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { mipLevelCount, format, dimension, + sampleCount: descriptor.sampleCount, usage, }); const gpuTextureView = gpuTexture.createView(); @@ -429,14 +458,15 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { getFallbackTexture(samplerEntry: BindingLayoutSamplerDescriptor): Texture { const dimension = samplerEntry.dimension, formatKind = samplerEntry.formatKind; - if (dimension === TextureDimension.n2D) + if (dimension === TextureDimension.TEXTURE_2D) return formatKind === SamplerFormatKind.Depth ? this.fallbackTexture2DDepth : this.fallbackTexture2D; - else if (dimension === TextureDimension.n2DArray) + else if (dimension === TextureDimension.TEXTURE_2D_ARRAY) return this.fallbackTexture2DArray; - else if (dimension === TextureDimension.n3D) return this.fallbackTexture3D; - else if (dimension === TextureDimension.Cube) + else if (dimension === TextureDimension.TEXTURE_3D) + return this.fallbackTexture3D; + else if (dimension === TextureDimension.TEXTURE_CUBE_MAP) return this.fallbackTextureCube; else throw new Error('whoops'); } @@ -445,13 +475,13 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { dimension: TextureDimension, formatKind: SamplerFormatKind, ): Texture_WebGPU { - const depth = dimension === TextureDimension.Cube ? 6 : 1; + const depth = dimension === TextureDimension.TEXTURE_CUBE_MAP ? 6 : 1; const pixelFormat = formatKind === SamplerFormatKind.Float ? Format.U8_RGBA_NORM : Format.D24; return this.createTexture({ dimension, pixelFormat, - usage: TextureUsage.Sampled, + usage: TextureUsage.SAMPLED, width: 1, height: 1, depth, @@ -475,21 +505,6 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { }); } - createInputState( - inputLayout: InputLayout, - vertexBuffers: (VertexBufferDescriptor | null)[], - indexBuffer: IndexBufferDescriptor | null, - ): InputState { - // InputState is a GL-only thing, as VAOs suck. We emulate it with a VAO-alike here. - return new InputState_WebGPU({ - id: this.getNextUniqueId(), - device: this, - inputLayout, - vertexBuffers, - indexBuffer, - }); - } - createComputePipeline( descriptor: ComputePipelineDescriptor, ): ComputePipeline { @@ -522,7 +537,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { }); } - _createBindGroupLayout( + private createBindGroupLayout( bindingLayout: BindingLayoutDescriptor, ): BindGroupLayout { let gpuBindGroupLayout = this.bindGroupLayoutCache.get(bindingLayout); @@ -585,12 +600,15 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { ): GPUPipelineLayout { const bindGroupLayouts = bindingLayouts.flatMap( (bindingLayout) => - this._createBindGroupLayout(bindingLayout).gpuBindGroupLayout, + this.createBindGroupLayout(bindingLayout).gpuBindGroupLayout, ); return this.device.createPipelineLayout({ bindGroupLayouts }); } - _createRenderPipeline(renderPipeline: RenderPipeline_WebGPU, async = false) { + private createRenderPipelineInternal( + renderPipeline: RenderPipeline_WebGPU, + async = false, + ) { // if (this.device.createRenderPipelineAsync === undefined) { // async = false; // } @@ -610,9 +628,14 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { fragmentStage = program.fragmentStage; if (vertexStage === null || fragmentStage === null) return; - const layout = this._createPipelineLayout(descriptor.bindingLayouts); + descriptor.megaStateDescriptor = { + ...copyMegaState(defaultMegaState), + ...descriptor.megaStateDescriptor, + }; + + const layout = this._createPipelineLayout(descriptor.bindingLayouts || []); const primitive = translatePrimitiveState( - descriptor.topology, + descriptor.topology ?? PrimitiveTopology.TRIANGLES, descriptor.megaStateDescriptor, ); const targets = translateTargets( @@ -685,11 +708,6 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { }); } - destroyRenderTarget(o: RenderTarget): void { - const attachment = o as unknown as Attachment_WebGPU; - attachment.gpuTexture.destroy(); - } - createRenderPass(renderPassDescriptor: RenderPassDescriptor): RenderPass { let pass = this.renderPassPool.pop(); if (pass === undefined) { @@ -757,12 +775,16 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { } queryLimits(): DeviceLimits { - // TODO: GPUAdapter.limits + // GPUAdapter.limits // @see https://www.w3.org/TR/webgpu/#gpu-supportedlimits return { - uniformBufferMaxPageWordSize: 0x1000, - uniformBufferWordAlignment: 0x40, + uniformBufferMaxPageWordSize: + this.device.limits.maxUniformBufferBindingSize >>> 2, + uniformBufferWordAlignment: + this.device.limits.minUniformBufferOffsetAlignment >>> 2, supportedSampleCounts: [1], + occlusionQueriesRecommended: true, + computeShadersSupported: true, }; } @@ -790,6 +812,7 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { } queryPlatformAvailable(): boolean { + // TODO: should listen to lost event return true; } @@ -836,10 +859,6 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { programPatched(o: Program): void {} - pushDebugGroup(debugGroup: DebugGroup): void {} - - popDebugGroup(): void {} - pipelineQueryReady(o: RenderPipeline): boolean { const renderPipeline = o as RenderPipeline_WebGPU; return renderPipeline.gpuRenderPipeline !== null; @@ -847,6 +866,6 @@ export class Device_WebGPU implements SwapChain, IDevice_WebGPU { pipelineForceReady(o: RenderPipeline): void { const renderPipeline = o as RenderPipeline_WebGPU; - this._createRenderPipeline(renderPipeline, false); + this.createRenderPipelineInternal(renderPipeline, false); } } diff --git a/packages/g-plugin-webgpu-device/src/platform/InputLayout.ts b/packages/g-plugin-webgpu-device/src/platform/InputLayout.ts index 695343dfa..1776a8062 100644 --- a/packages/g-plugin-webgpu-device/src/platform/InputLayout.ts +++ b/packages/g-plugin-webgpu-device/src/platform/InputLayout.ts @@ -7,7 +7,7 @@ import type { IDevice_WebGPU } from './interfaces'; import { ResourceBase_WebGPU } from './ResourceBase'; import { translateIndexFormat, - translateVertexBufferFrequency, + translateVertexStepMode, translateVertexFormat, } from './utils'; @@ -50,7 +50,7 @@ export class InputLayout_WebGPU descriptor.vertexBufferDescriptors[attr.bufferIndex], ); const arrayStride = b.byteStride; - const stepMode = translateVertexBufferFrequency(b.frequency); + const stepMode = translateVertexStepMode(b.stepMode); const attributes: GPUVertexAttribute[] = [attribute]; buffers[attr.bufferIndex] = { arrayStride, stepMode, attributes }; } diff --git a/packages/g-plugin-webgpu-device/src/platform/InputState.ts b/packages/g-plugin-webgpu-device/src/platform/InputState.ts deleted file mode 100644 index a40d9493e..000000000 --- a/packages/g-plugin-webgpu-device/src/platform/InputState.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { - IndexBufferDescriptor, - InputLayout, - InputState, - VertexBufferDescriptor, -} from '@antv/g-plugin-device-renderer'; -import { ResourceType } from '@antv/g-plugin-device-renderer'; -import type { IDevice_WebGPU } from './interfaces'; -import { ResourceBase_WebGPU } from './ResourceBase'; - -export class InputState_WebGPU extends ResourceBase_WebGPU implements InputState { - type: ResourceType.InputState = ResourceType.InputState; - - inputLayout: InputLayout; - vertexBuffers: (VertexBufferDescriptor | null)[]; - indexBuffer: IndexBufferDescriptor | null; - - constructor({ - id, - device, - inputLayout, - vertexBuffers, - indexBuffer, - }: { - id: number; - device: IDevice_WebGPU; - inputLayout: InputLayout; - vertexBuffers: (VertexBufferDescriptor | null)[]; - indexBuffer: IndexBufferDescriptor | null; - }) { - super({ id, device }); - - this.inputLayout = inputLayout; - this.vertexBuffers = vertexBuffers; - this.indexBuffer = indexBuffer; - } -} diff --git a/packages/g-plugin-webgpu-device/src/platform/Program.ts b/packages/g-plugin-webgpu-device/src/platform/Program.ts index 863bfff78..216ac18cf 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Program.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Program.ts @@ -1,6 +1,6 @@ import type { Program, - ProgramDescriptorSimple, + ProgramDescriptor, } from '@antv/g-plugin-device-renderer'; import { ResourceType } from '@antv/g-plugin-device-renderer'; import type { Device_WebGPU } from './Device'; @@ -9,7 +9,7 @@ import { ResourceBase_WebGPU } from './ResourceBase'; export class Program_WebGPU extends ResourceBase_WebGPU implements Program { type: ResourceType.Program = ResourceType.Program; - descriptor: ProgramDescriptorSimple; + descriptor: ProgramDescriptor; vertexStage: GPUProgrammableStage | null = null; fragmentStage: GPUProgrammableStage | null = null; computeStage: GPUProgrammableStage | null = null; @@ -21,48 +21,48 @@ export class Program_WebGPU extends ResourceBase_WebGPU implements Program { }: { id: number; device: IDevice_WebGPU; - descriptor: ProgramDescriptorSimple; + descriptor: ProgramDescriptor; }) { super({ id, device }); this.descriptor = descriptor; - if (descriptor.preprocessedVert) { - this.vertexStage = this.createShaderStage( - descriptor.preprocessedVert, - 'vertex', - ); + if (descriptor.vertex) { + this.vertexStage = this.createShaderStage(descriptor.vertex, 'vertex'); } - if (descriptor.preprocessedFrag) { + if (descriptor.fragment) { this.fragmentStage = this.createShaderStage( - descriptor.preprocessedFrag, + descriptor.fragment, 'fragment', ); } - if (descriptor.preprocessedCompute) { - // FIXME: Only support WGSL now - this.computeStage = this.createShaderStage( - descriptor.preprocessedCompute, - 'compute', - ); + if (descriptor.compute) { + // Only support WGSL now + this.computeStage = this.createShaderStage(descriptor.compute, 'compute'); } } + setUniformsLegacy(uniforms: Record = {}) {} + private createShaderStage( - sourceText: string, + shader: { + glsl?: string; + wgsl?: string; + }, shaderStage: 'vertex' | 'fragment' | 'compute', ): GPUProgrammableStage { const validationEnabled = false; - let code = sourceText; - if (shaderStage !== 'compute') { + // Use user-defined WGSL first. + let code = shader.wgsl; + if (!code) { try { - code = (this.device as Device_WebGPU).glsl_compile( - sourceText, + code = (this.device as Device_WebGPU)['glsl_compile']( + shader.glsl, shaderStage, validationEnabled, ); } catch (e) { - console.error(e, sourceText); + console.error(e, shader.glsl); throw new Error('whoops'); } } diff --git a/packages/g-plugin-webgpu-device/src/platform/QueryPool.ts b/packages/g-plugin-webgpu-device/src/platform/QueryPool.ts index 07c86e123..c49d6695b 100644 --- a/packages/g-plugin-webgpu-device/src/platform/QueryPool.ts +++ b/packages/g-plugin-webgpu-device/src/platform/QueryPool.ts @@ -49,6 +49,7 @@ export class QueryPool_WebGPU extends ResourceBase_WebGPU implements QueryPool { } destroy(): void { + super.destroy(); this.querySet.destroy(); this.resolveBuffer.destroy(); this.cpuBuffer.destroy(); diff --git a/packages/g-plugin-webgpu-device/src/platform/Readback.ts b/packages/g-plugin-webgpu-device/src/platform/Readback.ts index 0f8c6298e..9648ec000 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Readback.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Readback.ts @@ -48,7 +48,7 @@ export class Readback_WebGPU extends ResourceBase_WebGPU implements Readback { const buffer = this.device.createBuffer({ usage: BufferUsage.STORAGE | BufferUsage.MAP_READ | BufferUsage.COPY_DST, - hint: BufferFrequencyHint.Static, + hint: BufferFrequencyHint.STATIC, viewOrSize: size, }) as Buffer_WebGPU; @@ -137,7 +137,7 @@ export class Readback_WebGPU extends ResourceBase_WebGPU implements Readback { gpuReadBuffer = this.device.createBuffer({ usage: BufferUsage.STORAGE | BufferUsage.MAP_READ | BufferUsage.COPY_DST, - hint: BufferFrequencyHint.Static, + hint: BufferFrequencyHint.STATIC, viewOrSize: size, }) as Buffer_WebGPU; diff --git a/packages/g-plugin-webgpu-device/src/platform/RenderPass.ts b/packages/g-plugin-webgpu-device/src/platform/RenderPass.ts index ded9da5c8..a0f5db22a 100644 --- a/packages/g-plugin-webgpu-device/src/platform/RenderPass.ts +++ b/packages/g-plugin-webgpu-device/src/platform/RenderPass.ts @@ -1,9 +1,12 @@ import { Bindings, - InputState, + Buffer, + IndexBufferDescriptor, + InputLayout, RenderPass, RenderPassDescriptor, RenderPipeline, + VertexBufferDescriptor, } from '@antv/g-plugin-device-renderer'; import { assert, @@ -15,7 +18,6 @@ import { isNil } from '@antv/util'; import type { Bindings_WebGPU } from './Bindings'; import { GPUTextureUsage } from './constants'; import type { InputLayout_WebGPU } from './InputLayout'; -import type { InputState_WebGPU } from './InputState'; import type { Attachment_WebGPU, TextureShared_WebGPU } from './interfaces'; import type { RenderPipeline_WebGPU } from './RenderPipeline'; import type { Texture_WebGPU } from './Texture'; @@ -88,8 +90,9 @@ export class RenderPass_WebGPU implements RenderPass { this.gfxColorAttachment[i] = colorAttachment; this.gfxColorResolveTo[i] = colorResolveTo; - this.gfxColorAttachmentLevel[i] = descriptor.colorAttachmentLevel[i]; - this.gfxColorResolveToLevel[i] = descriptor.colorResolveToLevel[i]; + this.gfxColorAttachmentLevel[i] = + descriptor.colorAttachmentLevel?.[i] || 0; + this.gfxColorResolveToLevel[i] = descriptor.colorResolveToLevel?.[i] || 0; if (colorAttachment !== null) { if (this.gpuColorAttachments[i] === undefined) { @@ -99,16 +102,18 @@ export class RenderPass_WebGPU implements RenderPass { const dstAttachment = this.gpuColorAttachments[i]; dstAttachment.view = this.getTextureView( colorAttachment, - this.gfxColorAttachmentLevel[i], + this.gfxColorAttachmentLevel?.[i] || 0, ); - const clearColor = descriptor.colorClearColor[i]; + const clearColor = descriptor.colorClearColor?.[i] ?? 'load'; if (clearColor === 'load') { dstAttachment.loadOp = 'load'; } else { dstAttachment.loadOp = 'clear'; dstAttachment.clearValue = clearColor; } - dstAttachment.storeOp = descriptor.colorStore[i] ? 'store' : 'discard'; + dstAttachment.storeOp = descriptor.colorStore?.[i] + ? 'store' + : 'discard'; dstAttachment.resolveTarget = undefined; if (colorResolveTo !== null) { if (colorAttachment.sampleCount > 1) { @@ -134,7 +139,7 @@ export class RenderPass_WebGPU implements RenderPass { this.gfxDepthStencilResolveTo = descriptor.depthStencilResolveTo as Texture_WebGPU; - if (descriptor.depthStencilAttachment !== null) { + if (descriptor.depthStencilAttachment) { const dsAttachment = descriptor.depthStencilAttachment as unknown as Attachment_WebGPU; const dstAttachment = this.gpuDepthStencilAttachment; @@ -190,10 +195,11 @@ export class RenderPass_WebGPU implements RenderPass { this.gpuRenderPassDescriptor.depthStencilAttachment = undefined; } - this.gpuRenderPassDescriptor.occlusionQuerySet = - descriptor.occlusionQueryPool !== null - ? getPlatformQuerySet(descriptor.occlusionQueryPool) - : undefined; + this.gpuRenderPassDescriptor.occlusionQuerySet = !isNil( + descriptor.occlusionQueryPool, + ) + ? getPlatformQuerySet(descriptor.occlusionQueryPool) + : undefined; } beginRenderPass(renderPassDescriptor: RenderPassDescriptor): void { @@ -218,23 +224,24 @@ export class RenderPass_WebGPU implements RenderPass { this.gpuRenderPassEncoder.setPipeline(gpuRenderPipeline); } - setInputState(inputState_: InputState | null): void { - if (inputState_ === null) return; + setVertexInput( + inputLayout_: InputLayout | null, + vertexBuffers: (VertexBufferDescriptor | null)[] | null, + indexBuffer: IndexBufferDescriptor | null, + ): void { + if (inputLayout_ === null) return; - const inputState = inputState_ as InputState_WebGPU; - if (inputState.indexBuffer !== null) { - const inputLayout = inputState.inputLayout as InputLayout_WebGPU; - const indexBuffer = inputState.indexBuffer; + const inputLayout = inputLayout_ as InputLayout_WebGPU; + if (indexBuffer !== null) this.gpuRenderPassEncoder.setIndexBuffer( getPlatformBuffer(indexBuffer.buffer), assertExists(inputLayout.indexFormat), indexBuffer.byteOffset, ); - } - for (let i = 0; i < inputState.vertexBuffers.length; i++) { - const b = inputState.vertexBuffers[i]; - if (isNil(b)) continue; + for (let i = 0; i < vertexBuffers!.length; i++) { + const b = vertexBuffers![i]; + if (b === null) continue; this.gpuRenderPassEncoder.setVertexBuffer( i, getPlatformBuffer(b.buffer), @@ -266,27 +273,46 @@ export class RenderPass_WebGPU implements RenderPass { this.gpuRenderPassEncoder.setStencilReference(ref); } - draw(vertexCount: number, firstVertex: number): void { - this.gpuRenderPassEncoder.draw(vertexCount, 1, firstVertex, 0); - } - - drawIndexed(indexCount: number, firstIndex: number): void { - this.gpuRenderPassEncoder.drawIndexed(indexCount, 1, firstIndex, 0, 0); + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-draw + */ + draw( + vertexCount: number, + instanceCount?: number, + firstVertex?: number, + firstInstance?: number, + ) { + this.gpuRenderPassEncoder.draw( + vertexCount, + instanceCount, + firstVertex, + firstInstance, + ); } - - drawIndexedInstanced( + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindexed + */ + drawIndexed( indexCount: number, - firstIndex: number, - instanceCount: number, - ): void { + instanceCount?: number, + firstIndex?: number, + baseVertex?: number, + firstInstance?: number, + ) { this.gpuRenderPassEncoder.drawIndexed( indexCount, instanceCount, firstIndex, - 0, - 0, + baseVertex, + firstInstance, ); } + /** + * @see https://www.w3.org/TR/webgpu/#dom-gpurendercommandsmixin-drawindirect + */ + drawIndirect(indirectBuffer: Buffer, indirectOffset: number) { + // TODO + } beginOcclusionQuery(dstOffs: number): void { this.gpuRenderPassEncoder.beginOcclusionQuery(dstOffs); @@ -296,20 +322,18 @@ export class RenderPass_WebGPU implements RenderPass { this.gpuRenderPassEncoder.endOcclusionQuery(); } - beginDebugGroup(name: string): void { - // FIREFOX MISSING - if (this.gpuRenderPassEncoder.pushDebugGroup === undefined) return; - + pushDebugGroup(name: string): void { this.gpuRenderPassEncoder.pushDebugGroup(name); } - endDebugGroup(): void { - // FIREFOX MISSING - if (this.gpuRenderPassEncoder.popDebugGroup === undefined) return; - + popDebugGroup(): void { this.gpuRenderPassEncoder.popDebugGroup(); } + insertDebugMarker(markerLabel: string) { + this.gpuRenderPassEncoder.insertDebugMarker(markerLabel); + } + finish(): GPUCommandBuffer { this.gpuRenderPassEncoder.end(); this.gpuRenderPassEncoder = null; @@ -333,10 +357,7 @@ export class RenderPass_WebGPU implements RenderPass { } } - if ( - this.gfxDepthStencilAttachment !== null && - this.gfxDepthStencilResolveTo !== null - ) { + if (this.gfxDepthStencilAttachment && this.gfxDepthStencilResolveTo) { if (this.gfxDepthStencilAttachment.sampleCount > 1) { // TODO(jstpierre): MSAA depth resolve (requires shader) } else { diff --git a/packages/g-plugin-webgpu-device/src/platform/RenderPipeline.ts b/packages/g-plugin-webgpu-device/src/platform/RenderPipeline.ts index 2095608d6..e2597954e 100644 --- a/packages/g-plugin-webgpu-device/src/platform/RenderPipeline.ts +++ b/packages/g-plugin-webgpu-device/src/platform/RenderPipeline.ts @@ -28,7 +28,7 @@ export class RenderPipeline_WebGPU super({ id, device }); this.descriptor = descriptor; - this.device._createRenderPipeline(this, false); + this.device['createRenderPipelineInternal'](this, false); } getBindGroupLayout(index: number) { diff --git a/packages/g-plugin-webgpu-device/src/platform/Sampler.ts b/packages/g-plugin-webgpu-device/src/platform/Sampler.ts index 11fa0ce53..281833483 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Sampler.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Sampler.ts @@ -36,16 +36,16 @@ export class Sampler_WebGPU extends ResourceBase_WebGPU implements Sampler { const lodMinClamp = descriptor.minLOD; const lodMaxClamp = - descriptor.mipFilter === MipFilterMode.NoMip + descriptor.mipFilter === MipFilterMode.NO_MIP ? descriptor.minLOD : descriptor.maxLOD; const maxAnisotropy = descriptor.maxAnisotropy ?? 1; if (maxAnisotropy > 1) assert( - descriptor.minFilter === TexFilterMode.Bilinear && - descriptor.magFilter === TexFilterMode.Bilinear && - descriptor.mipFilter === MipFilterMode.Linear, + descriptor.minFilter === TexFilterMode.BILINEAR && + descriptor.magFilter === TexFilterMode.BILINEAR && + descriptor.mipFilter === MipFilterMode.LINEAR, ); this.gpuSampler = this.device.device.createSampler({ diff --git a/packages/g-plugin-webgpu-device/src/platform/Texture.ts b/packages/g-plugin-webgpu-device/src/platform/Texture.ts index ca7641002..8f16886f2 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Texture.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Texture.ts @@ -1,4 +1,4 @@ -import type { +import { Format, Texture, TextureDescriptor, @@ -8,8 +8,6 @@ import { ResourceType } from '@antv/g-plugin-device-renderer'; import type { IDevice_WebGPU, TextureShared_WebGPU } from './interfaces'; import { ResourceBase_WebGPU } from './ResourceBase'; -// @see https://toji.dev/webgpu-best-practices/img-textures - export class Texture_WebGPU extends ResourceBase_WebGPU implements TextureShared_WebGPU, Texture @@ -32,65 +30,114 @@ export class Texture_WebGPU device, descriptor, skipCreate, + sampleCount, }: { id: number; device: IDevice_WebGPU; descriptor: TextureDescriptor; skipCreate?: boolean; + sampleCount?: number; }) { super({ id, device }); this.device.createTextureShared( { pixelFormat: descriptor.pixelFormat, - dimension: descriptor.dimension, + dimension: descriptor.dimension ?? TextureDimension.TEXTURE_2D, width: descriptor.width, height: descriptor.height, - depthOrArrayLayers: descriptor.depth, - numLevels: descriptor.numLevels, + depthOrArrayLayers: descriptor.depth ?? 1, + numLevels: descriptor.numLevels ?? 1, usage: descriptor.usage, - sampleCount: 1, + sampleCount: sampleCount ?? 1, }, this, skipCreate, ); } - setImageData(data: TexImageSource | ArrayBufferView[], level: number) { - // @see https://www.w3.org/TR/webgpu/#image-copies - // @see https://www.w3.org/TR/webgpu/#dom-gpuqueue-copyexternalimagetotexture - // const isArray = Array.isArray(data); - // if (!isArray) { - // // if (this.gpuTexture) { - // // this.gpuTexture.destroy(); - // // } - // const textureDescriptor: GPUTextureDescriptor = { - // // Unlike in WebGL, the size of our texture must be set at texture creation time. - // // This means we have to wait until the image is loaded to create the texture, since we won't - // // know the size until then. - // size: { width: data.width, height: data.height }, - // format: 'rgba8unorm', - // usage: - // GPUTextureUsage.TEXTURE_BINDING | - // GPUTextureUsage.COPY_DST | - // GPUTextureUsage.RENDER_ATTACHMENT, - // }; - // const texture = this.device.device.createTexture(textureDescriptor); - // this.gpuTexture = texture; - // this.gpuTextureView = texture.createView(); - // this.width = data.width; - // this.height = data.height; - // this.device.device.queue.copyExternalImageToTexture( - // { source: data }, - // { texture }, - // textureDescriptor.size, - // ); - // } else { - // // TODO: support ArrayBufferView[] - // } + private textureFromImageBitmapOrCanvas( + device: GPUDevice, + sources: (ImageBitmap | HTMLCanvasElement | OffscreenCanvas)[], + depthOrArrayLayers: number, + ): [GPUTexture, number, number] { + const width = sources[0].width; + const height = sources[0].height; + const textureDescriptor: GPUTextureDescriptor = { + // Unlike in WebGL, the size of our texture must be set at texture creation time. + // This means we have to wait until the image is loaded to create the texture, since we won't + // know the size until then. + size: { width, height, depthOrArrayLayers }, + format: 'rgba8unorm', + usage: + GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.RENDER_ATTACHMENT, + }; + const texture = device.createTexture(textureDescriptor); + + for (let i = 0; i < sources.length; i++) { + const source = sources[0]; + device.queue.copyExternalImageToTexture( + { source }, + { texture, origin: [0, 0, i] }, + [width, height], + ); + } + + return [texture, width, height]; + } + + private isImageBitmapOrCanvases( + datas: (TexImageSource | ArrayBufferView)[], + ): datas is (ImageBitmap | HTMLCanvasElement | OffscreenCanvas)[] { + const data = datas[0]; + return ( + data instanceof ImageBitmap || + data instanceof HTMLCanvasElement || + data instanceof OffscreenCanvas + ); + } + + private isVideo( + datas: (TexImageSource | ArrayBufferView)[], + ): datas is HTMLVideoElement[] { + const data = datas[0]; + return data instanceof HTMLVideoElement; + } + + /** + * @see https://toji.dev/webgpu-best-practices/img-textures + */ + setImageData(datas: (TexImageSource | ArrayBufferView)[], firstMipLevel = 0) { + const { device } = this.device; + let texture: GPUTexture; + let width: number; + let height: number; + + if (this.isImageBitmapOrCanvases(datas)) { + [texture, width, height] = this.textureFromImageBitmapOrCanvas( + device, + datas, + this.depthOrArrayLayers, + ); + } else if (this.isVideo(datas)) { + // @see https://toji.dev/webgpu-best-practices/img-textures#creating-a-texture-from-an-htmlvideoelement-video-tag + texture = device.importExternalTexture({ + source: datas[0], + }) as unknown as GPUTexture; + } else { + // TODO: support ArrayBufferView[] + } + + this.width = width; + this.height = height; + this.gpuTexture = texture; + this.gpuTextureView = texture.createView(); } destroy() { + super.destroy(); // @see https://www.w3.org/TR/webgpu/#dom-gputexture-destroy this.gpuTexture.destroy(); } diff --git a/packages/g-plugin-webgpu-device/src/platform/interfaces.ts b/packages/g-plugin-webgpu-device/src/platform/interfaces.ts index 61c189f0e..ff1db8ac5 100644 --- a/packages/g-plugin-webgpu-device/src/platform/interfaces.ts +++ b/packages/g-plugin-webgpu-device/src/platform/interfaces.ts @@ -3,15 +3,8 @@ import type { TextureDimension, TextureUsage, RenderTarget, - RenderPipeline, - Texture, - Sampler, Device, - BindingLayoutSamplerDescriptor, - BindingLayoutDescriptor, - Buffer, } from '@antv/g-plugin-device-renderer'; -import { BufferDescriptor } from '@antv/g-plugin-device-renderer'; export interface TextureSharedDescriptor { dimension: TextureDimension; @@ -46,22 +39,9 @@ export interface BindGroupLayout { export interface IDevice_WebGPU extends Device { device: GPUDevice; - getFallbackSampler: (samplerEntry: BindingLayoutSamplerDescriptor) => Sampler; - getFallbackTexture: (samplerEntry: BindingLayoutSamplerDescriptor) => Texture; - createBuffer: (descriptor: BufferDescriptor) => Buffer; createTextureShared: ( descriptor: TextureSharedDescriptor, texture: TextureShared_WebGPU, skipCreate: boolean, ) => void; - _createRenderPipeline: ( - renderPipeline: RenderPipeline, - async?: boolean, - ) => void; - _createBindGroupLayout: ( - bindingLayout: BindingLayoutDescriptor, - ) => BindGroupLayout; - // ensureRenderPipeline: (renderPipeline: RenderPipeline) => void; - // createBindGroupLayout(bindingLayout: Partial): BindGroupLayout; - // createPipelineLayout(bindingLayouts: BindingLayoutDescriptor[]): GPUPipelineLayout; } diff --git a/packages/g-plugin-webgpu-device/src/platform/utils.ts b/packages/g-plugin-webgpu-device/src/platform/utils.ts index 9f820495b..10cb2d26a 100644 --- a/packages/g-plugin-webgpu-device/src/platform/utils.ts +++ b/packages/g-plugin-webgpu-device/src/platform/utils.ts @@ -16,11 +16,11 @@ import { TextureDimension, PrimitiveTopology, CullMode, - FrontFaceMode, + FrontFace, BlendFactor, BlendMode, CompareMode, - VertexBufferFrequency, + VertexStepMode, TextureUsage, QueryPoolType, SamplerFormatKind, @@ -33,15 +33,19 @@ import { import type { Buffer_WebGPU } from './Buffer'; import type { Sampler_WebGPU } from './Sampler'; import type { QueryPool_WebGPU } from './QueryPool'; +import { isNil } from '@antv/util'; +/** + * @see https://developer.mozilla.org/en-US/docs/Web/API/GPUTexture/usage#value + */ export function translateTextureUsage( usage: TextureUsage, ): GPUTextureUsageFlags { let gpuUsage: GPUTextureUsageFlags = 0; - if (usage & TextureUsage.Sampled) + if (usage & TextureUsage.SAMPLED) gpuUsage |= GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; - if (usage & TextureUsage.RenderTarget) + if (usage & TextureUsage.RENDER_TARGET) gpuUsage |= GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | @@ -84,10 +88,10 @@ export function translateTextureFormat(format: Format): GPUTextureFormat { export function translateTextureDimension( dimension: TextureDimension, ): GPUTextureDimension { - if (dimension === TextureDimension.n2D) return '2d'; - else if (dimension === TextureDimension.Cube) return '2d'; - else if (dimension === TextureDimension.n2DArray) return '2d'; - else if (dimension === TextureDimension.n3D) return '3d'; + if (dimension === TextureDimension.TEXTURE_2D) return '2d'; + else if (dimension === TextureDimension.TEXTURE_CUBE_MAP) return '2d'; + else if (dimension === TextureDimension.TEXTURE_2D_ARRAY) return '2d'; + else if (dimension === TextureDimension.TEXTURE_3D) return '3d'; else throw new Error('whoops'); } @@ -103,22 +107,23 @@ export function translateBufferUsage(usage_: BufferUsage): GPUBufferUsageFlags { } export function translateWrapMode(wrapMode: WrapMode): GPUAddressMode { - if (wrapMode === WrapMode.Clamp) return 'clamp-to-edge'; - else if (wrapMode === WrapMode.Repeat) return 'repeat'; - else if (wrapMode === WrapMode.Mirror) return 'mirror-repeat'; + if (wrapMode === WrapMode.CLAMP) return 'clamp-to-edge'; + else if (wrapMode === WrapMode.REPEAT) return 'repeat'; + else if (wrapMode === WrapMode.MIRROR) return 'mirror-repeat'; else throw new Error('whoops'); } export function translateMinMagFilter(texFilter: TexFilterMode): GPUFilterMode { - if (texFilter === TexFilterMode.Bilinear) return 'linear'; - else if (texFilter === TexFilterMode.Point) return 'nearest'; + if (texFilter === TexFilterMode.BILINEAR) return 'linear'; + else if (texFilter === TexFilterMode.POINT) return 'nearest'; else throw new Error('whoops'); } +// @see https://www.w3.org/TR/webgpu/#enumdef-gpumipmapfiltermode export function translateMipFilter(mipFilter: MipFilterMode): GPUFilterMode { - if (mipFilter === MipFilterMode.Linear) return 'linear'; - else if (mipFilter === MipFilterMode.Nearest) return 'nearest'; - else if (mipFilter === MipFilterMode.NoMip) return 'nearest'; + if (mipFilter === MipFilterMode.LINEAR) return 'linear'; + else if (mipFilter === MipFilterMode.NEAREST) return 'nearest'; + else if (mipFilter === MipFilterMode.NO_MIP) return 'nearest'; else throw new Error('whoops'); } @@ -143,10 +148,10 @@ export function translateBindGroupSamplerBinding( function translateViewDimension( dimension: TextureDimension, ): GPUTextureViewDimension { - if (dimension === TextureDimension.n2D) return '2d'; - else if (dimension === TextureDimension.n2DArray) return '2d-array'; - else if (dimension === TextureDimension.n3D) return '3d'; - else if (dimension === TextureDimension.Cube) return 'cube'; + if (dimension === TextureDimension.TEXTURE_2D) return '2d'; + else if (dimension === TextureDimension.TEXTURE_2D_ARRAY) return '2d-array'; + else if (dimension === TextureDimension.TEXTURE_3D) return '3d'; + else if (dimension === TextureDimension.TEXTURE_CUBE_MAP) return 'cube'; else throw new Error('whoops'); } @@ -179,36 +184,44 @@ export function translateQueryPoolType(type: QueryPoolType): GPUQueryType { else throw new Error('whoops'); } -// @see https://www.w3.org/TR/webgpu/#primitive-state +/** + * @see https://www.w3.org/TR/webgpu/#primitive-state + */ export function translateTopology( topology: PrimitiveTopology, ): GPUPrimitiveTopology { switch (topology) { - case PrimitiveTopology.Triangles: + case PrimitiveTopology.TRIANGLES: return 'triangle-list'; - case PrimitiveTopology.Points: + case PrimitiveTopology.POINTS: return 'point-list'; - case PrimitiveTopology.TriangleStrip: + case PrimitiveTopology.TRIANGLE_STRIP: return 'triangle-strip'; - case PrimitiveTopology.Lines: + case PrimitiveTopology.LINES: return 'line-list'; - case PrimitiveTopology.LineStrip: + case PrimitiveTopology.LINE_STRIP: return 'line-strip'; default: throw new Error('Unknown primitive topology mode'); } } +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpucullmode + */ export function translateCullMode(cullMode: CullMode): GPUCullMode { - if (cullMode === CullMode.None) return 'none'; - else if (cullMode === CullMode.Front) return 'front'; - else if (cullMode === CullMode.Back) return 'back'; + if (cullMode === CullMode.NONE) return 'none'; + else if (cullMode === CullMode.FRONT) return 'front'; + else if (cullMode === CullMode.BACK) return 'back'; else throw new Error('whoops'); } -export function translateFrontFace(frontFaceMode: FrontFaceMode): GPUFrontFace { - if (frontFaceMode === FrontFaceMode.CCW) return 'ccw'; - else if (frontFaceMode === FrontFaceMode.CW) return 'cw'; +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpufrontface + */ +export function translateFrontFace(frontFaceMode: FrontFace): GPUFrontFace { + if (frontFaceMode === FrontFace.CCW) return 'ccw'; + else if (frontFaceMode === FrontFace.CW) return 'cw'; else throw new Error('whoops'); } @@ -223,26 +236,39 @@ export function translatePrimitiveState( }; } +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpublendfactor + */ export function translateBlendFactor(factor: BlendFactor): GPUBlendFactor { - if (factor === BlendFactor.Zero) return 'zero'; - else if (factor === BlendFactor.One) return 'one'; - else if (factor === BlendFactor.Src) return 'src'; - else if (factor === BlendFactor.OneMinusSrc) return 'one-minus-src'; - else if (factor === BlendFactor.Dst) return 'dst'; - else if (factor === BlendFactor.OneMinusDst) return 'one-minus-dst'; - else if (factor === BlendFactor.SrcAlpha) return 'src-alpha'; - else if (factor === BlendFactor.OneMinusSrcAlpha) + if (factor === BlendFactor.ZERO) return 'zero'; + else if (factor === BlendFactor.ONE) return 'one'; + else if (factor === BlendFactor.SRC) return 'src'; + else if (factor === BlendFactor.ONE_MINUS_SRC) return 'one-minus-src'; + else if (factor === BlendFactor.DST) return 'dst'; + else if (factor === BlendFactor.ONE_MINUS_DST) return 'one-minus-dst'; + else if (factor === BlendFactor.SRC_ALPHA) return 'src-alpha'; + else if (factor === BlendFactor.ONE_MINUS_SRC_ALPHA) return 'one-minus-src-alpha'; - else if (factor === BlendFactor.DstAlpha) return 'dst-alpha'; - else if (factor === BlendFactor.OneMinusDstAlpha) + else if (factor === BlendFactor.DST_ALPHA) return 'dst-alpha'; + else if (factor === BlendFactor.ONE_MINUS_DST_ALPHA) return 'one-minus-dst-alpha'; + else if (factor === BlendFactor.CONST) return 'constant'; + else if (factor === BlendFactor.ONE_MINUS_CONSTANT) + return 'one-minus-constant'; + else if (factor === BlendFactor.SRC_ALPHA_SATURATE) + return 'src-alpha-saturated'; else throw new Error('whoops'); } +/** + * @see https://www.w3.org/TR/webgpu/#enumdef-gpublendoperation + */ export function translateBlendMode(mode: BlendMode): GPUBlendOperation { - if (mode === BlendMode.Add) return 'add'; - else if (mode === BlendMode.Subtract) return 'subtract'; - else if (mode === BlendMode.ReverseSubtract) return 'reverse-subtract'; + if (mode === BlendMode.ADD) return 'add'; + else if (mode === BlendMode.SUBSTRACT) return 'subtract'; + else if (mode === BlendMode.REVERSE_SUBSTRACT) return 'reverse-subtract'; + else if (mode === BlendMode.MIN) return 'min'; + else if (mode === BlendMode.MAX) return 'max'; else throw new Error('whoops'); } @@ -256,9 +282,9 @@ function translateBlendComponent(ch: ChannelBlendState): GPUBlendComponent { function blendComponentIsNil(ch: ChannelBlendState): boolean { return ( - ch.blendMode === BlendMode.Add && - ch.blendSrcFactor === BlendFactor.One && - ch.blendDstFactor === BlendFactor.Zero + ch.blendMode === BlendMode.ADD && + ch.blendSrcFactor === BlendFactor.ONE && + ch.blendDstFactor === BlendFactor.ZERO ); } @@ -298,17 +324,18 @@ export function translateTargets( }); } +// @see https://www.w3.org/TR/webgpu/#enumdef-gpucomparefunction export function translateCompareMode( compareMode: CompareMode, ): GPUCompareFunction { - if (compareMode === CompareMode.Never) return 'never'; - else if (compareMode === CompareMode.Less) return 'less'; - else if (compareMode === CompareMode.Equal) return 'equal'; - else if (compareMode === CompareMode.LessEqual) return 'less-equal'; - else if (compareMode === CompareMode.Greater) return 'greater'; - else if (compareMode === CompareMode.NotEqual) return 'not-equal'; - else if (compareMode === CompareMode.GreaterEqual) return 'greater-equal'; - else if (compareMode === CompareMode.Always) return 'always'; + if (compareMode === CompareMode.NEVER) return 'never'; + else if (compareMode === CompareMode.LESS) return 'less'; + else if (compareMode === CompareMode.EQUAL) return 'equal'; + else if (compareMode === CompareMode.LEQUAL) return 'less-equal'; + else if (compareMode === CompareMode.GREATER) return 'greater'; + else if (compareMode === CompareMode.NOTEQUAL) return 'not-equal'; + else if (compareMode === CompareMode.GEQUAL) return 'greater-equal'; + else if (compareMode === CompareMode.ALWAYS) return 'always'; else throw new Error('whoops'); } @@ -316,11 +343,11 @@ export function translateDepthStencilState( format: Format | null, megaStateDescriptor: MegaStateDescriptor, ): GPUDepthStencilState | undefined { - if (format === null) return undefined; + if (isNil(format)) return undefined; return { format: translateTextureFormat(format), - depthWriteEnabled: megaStateDescriptor.depthWrite, + depthWriteEnabled: !!megaStateDescriptor.depthWrite, depthCompare: translateCompareMode(megaStateDescriptor.depthCompare), depthBias: megaStateDescriptor.polygonOffset ? 1 : 0, depthBiasSlopeScale: megaStateDescriptor.polygonOffset ? 1 : 0, @@ -336,11 +363,11 @@ export function translateIndexFormat( else throw new Error('whoops'); } -export function translateVertexBufferFrequency( - frequency: VertexBufferFrequency, +export function translateVertexStepMode( + stepMode: VertexStepMode, ): GPUVertexStepMode { - if (frequency === VertexBufferFrequency.PerVertex) return 'vertex'; - else if (frequency === VertexBufferFrequency.PerInstance) return 'instance'; + if (stepMode === VertexStepMode.VERTEX) return 'vertex'; + else if (stepMode === VertexStepMode.INSTANCE) return 'instance'; else throw new Error('whoops'); } diff --git a/packages/g-web-components/CHANGELOG.md b/packages/g-web-components/CHANGELOG.md index e95a5130c..51dd8bc0e 100644 --- a/packages/g-web-components/CHANGELOG.md +++ b/packages/g-web-components/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-web-components +## 1.9.20 + +### Patch Changes + +- @antv/g-webgl@1.9.20 + ## 1.9.19 ### Patch Changes diff --git a/packages/g-web-components/package.json b/packages/g-web-components/package.json index 1e99680d3..d680b4686 100644 --- a/packages/g-web-components/package.json +++ b/packages/g-web-components/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-web-components", - "version": "1.9.19", + "version": "1.9.20", "description": "A declarative usage for G implemented with WebComponents", "keywords": [ "antv", diff --git a/packages/g-webgl/CHANGELOG.md b/packages/g-webgl/CHANGELOG.md index 5a0a9a5d6..0ab424deb 100644 --- a/packages/g-webgl/CHANGELOG.md +++ b/packages/g-webgl/CHANGELOG.md @@ -1,5 +1,14 @@ # @antv/g-webgl +## 1.9.20 + +### Patch Changes + +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + - @antv/g-plugin-webgl-device@1.9.17 + ## 1.9.19 ### Patch Changes diff --git a/packages/g-webgl/package.json b/packages/g-webgl/package.json index 537db0e3c..c8b69b0da 100644 --- a/packages/g-webgl/package.json +++ b/packages/g-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-webgl", - "version": "1.9.19", + "version": "1.9.20", "description": "A renderer implemented by WebGL1/2", "keywords": [ "antv", diff --git a/packages/g-webgpu/CHANGELOG.md b/packages/g-webgpu/CHANGELOG.md index c1daaf216..f37763779 100644 --- a/packages/g-webgpu/CHANGELOG.md +++ b/packages/g-webgpu/CHANGELOG.md @@ -1,5 +1,14 @@ # @antv/g-webgpu +## 1.9.20 + +### Patch Changes + +- Updated dependencies [c54cc6fb] +- Updated dependencies [568ec0f4] + - @antv/g-plugin-device-renderer@1.9.17 + - @antv/g-plugin-webgpu-device@1.9.17 + ## 1.9.19 ### Patch Changes diff --git a/packages/g-webgpu/package.json b/packages/g-webgpu/package.json index c2b9e7709..3d3a4d875 100644 --- a/packages/g-webgpu/package.json +++ b/packages/g-webgpu/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-webgpu", - "version": "1.9.19", + "version": "1.9.20", "description": "A renderer implemented by WebGPU", "keywords": [ "antv", diff --git a/packages/react-g/CHANGELOG.md b/packages/react-g/CHANGELOG.md index 5dec67283..713fe310e 100644 --- a/packages/react-g/CHANGELOG.md +++ b/packages/react-g/CHANGELOG.md @@ -1,5 +1,12 @@ # @antv/react-g +## 1.10.15 + +### Patch Changes + +- c54cc6fb: Antialiasing SDF & Text. +- cc629c5f: Remove console.log + ## 1.10.14 ### Patch Changes diff --git a/packages/react-g/package.json b/packages/react-g/package.json index 3e1559b97..faea1993d 100644 --- a/packages/react-g/package.json +++ b/packages/react-g/package.json @@ -1,6 +1,6 @@ { "name": "@antv/react-g", - "version": "1.10.14", + "version": "1.10.15", "description": "react render for @antv/g", "keywords": [ "react", diff --git a/packages/react-g/src/reconciler.ts b/packages/react-g/src/reconciler.ts index a49090452..b1d51b05d 100644 --- a/packages/react-g/src/reconciler.ts +++ b/packages/react-g/src/reconciler.ts @@ -19,7 +19,6 @@ import type { Type, UpdatePayload, } from './types'; -import { log } from './util/debug'; export const reconcilor = ReactReconciler< Type, @@ -65,7 +64,7 @@ export const reconcilor = ReactReconciler< }) as unknown as Element; // @ts-ignore bindShapeEvent(props, instance); - log('createInstance ', type, instance); + // log('createInstance ', type, instance); // @ts-ignore return instance; }, @@ -73,7 +72,7 @@ export const reconcilor = ReactReconciler< parentInstance: Instance, child: Instance | TextInstance, ): void { - log('appendInitialChild', parentInstance, child); + // log('appendInitialChild', parentInstance, child); parentInstance.appendChild(child); }, finalizeInitialChildren( @@ -140,14 +139,14 @@ export const reconcilor = ReactReconciler< // (optional) // ------------------- appendChild(parentInstance: Instance, child: Instance | TextInstance): void { - log('appendChild'); + // log('appendChild'); parentInstance.appendChild(child); }, appendChildToContainer( container: Container, child: Instance | TextInstance, ): void { - log('appendChildToContainer', container, child); + // log('appendChildToContainer', container, child); container.appendChild(child); }, commitTextUpdate( @@ -169,7 +168,7 @@ export const reconcilor = ReactReconciler< newProps: Props, internalInstanceHandle: OpaqueHandle, ): void { - log('commitUpdate', instance, newProps); + // log('commitUpdate', instance, newProps); updateProps(instance, newProps, oldProps); }, insertBefore( @@ -187,7 +186,7 @@ export const reconcilor = ReactReconciler< container.insertBefore(child, beforeChild); }, removeChild(parentInstance: Instance, child: Instance | TextInstance): void { - log('removeChild', parentInstance, child); + // log('removeChild', parentInstance, child); parentInstance.removeChild(child); }, removeChildFromContainer( @@ -353,7 +352,7 @@ reconcilor.injectIntoDevTools({ rendererPackageName: 'react-g', rendererConfig: { getInspectorDataForViewTag: (tag: number) => { - console.log(tag); + // console.log(tag); }, }, }); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 35fd360e7..d73df4f9e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4,15 +4,15 @@ version = 3 [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bit-set" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] @@ -25,15 +25,15 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "cfg-if" @@ -47,6 +47,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "glsl-wgsl-compiler" version = "0.1.0" @@ -59,49 +65,40 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" [[package]] name = "indexmap" -version = "1.8.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.113" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.14" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memory_units" @@ -111,8 +108,8 @@ checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" [[package]] name = "naga" -version = "0.9.0" -source = "git+https://github.com/gfx-rs/naga#cc985396dadc95039a0f3d5cfc818a6b4d5eba6b" +version = "0.13.0" +source = "git+https://github.com/gfx-rs/naga#a0eb1f54629133a05f1bd0c82c28c15e0e3e61a2" dependencies = [ "bit-set", "bitflags", @@ -121,19 +118,24 @@ dependencies = [ "num-traits", "pp-rs", "rustc-hash", - "termcolor", "thiserror", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "pp-rs" version = "0.2.1" @@ -145,18 +147,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -169,55 +171,52 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "syn" -version = "1.0.86" +version = "2.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" dependencies = [ "proc-macro2", "quote", - "unicode-xid", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", + "unicode-ident", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -225,13 +224,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -240,9 +239,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -250,9 +249,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -263,15 +262,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -305,15 +304,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/rust/pkg/glsl_wgsl_compiler.d.ts b/rust/pkg/glsl_wgsl_compiler.d.ts index ac1707841..e59dfc333 100644 --- a/rust/pkg/glsl_wgsl_compiler.d.ts +++ b/rust/pkg/glsl_wgsl_compiler.d.ts @@ -1,48 +1,41 @@ /* tslint:disable */ /* eslint-disable */ /** - * @param {string} source - * @param {string} stage - * @param {boolean} validation_enabled - * @returns {string} - */ -export function glsl_compile( - source: string, - stage: string, - validation_enabled: boolean, -): string; +* @param {string} source +* @param {string} stage +* @param {boolean} validation_enabled +* @returns {string} +*/ +export function glsl_compile(source: string, stage: string, validation_enabled: boolean): string; -export type InitInput = - | RequestInfo - | URL - | Response - | BufferSource - | WebAssembly.Module; +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; export interface InitOutput { readonly memory: WebAssembly.Memory; - readonly glsl_compile: ( - a: number, - b: number, - c: number, - d: number, - e: number, - f: number, - ) => void; + readonly glsl_compile: (a: number, b: number, c: number, d: number, e: number, f: number) => void; readonly __wbindgen_add_to_stack_pointer: (a: number) => number; - readonly __wbindgen_malloc: (a: number) => number; - readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; - readonly __wbindgen_free: (a: number, b: number) => void; + readonly __wbindgen_malloc: (a: number, b: number) => number; + readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; } +export type SyncInitInput = BufferSource | WebAssembly.Module; /** - * If `module_or_path` is {RequestInfo} or {URL}, makes a request and - * for everything else, calls `WebAssembly.instantiate` directly. - * - * @param {InitInput | Promise} module_or_path - * - * @returns {Promise} - */ -export default function init( - module_or_path?: InitInput | Promise, -): Promise; +* Instantiates the given `module`, which can either be bytes or +* a precompiled `WebAssembly.Module`. +* +* @param {SyncInitInput} module +* +* @returns {InitOutput} +*/ +export function initSync(module: SyncInitInput): InitOutput; + +/** +* If `module_or_path` is {RequestInfo} or {URL}, makes a request and +* for everything else, calls `WebAssembly.instantiate` directly. +* +* @param {InitInput | Promise} module_or_path +* +* @returns {Promise} +*/ +export default function __wbg_init (module_or_path?: InitInput | Promise): Promise; diff --git a/rust/pkg/glsl_wgsl_compiler.js b/rust/pkg/glsl_wgsl_compiler.js index 90266d600..3ce510a72 100644 --- a/rust/pkg/glsl_wgsl_compiler.js +++ b/rust/pkg/glsl_wgsl_compiler.js @@ -1,28 +1,33 @@ let wasm; -let cachedTextDecoder = new TextDecoder('utf-8', { - ignoreBOM: true, - fatal: true, -}); +const cachedTextDecoder = + typeof TextDecoder !== 'undefined' + ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) + : { + decode: () => { + throw Error('TextDecoder not available'); + }, + }; + +if (typeof TextDecoder !== 'undefined') { + cachedTextDecoder.decode(); +} -cachedTextDecoder.decode(); +let cachedUint8Memory0 = null; -let cachegetUint8Memory0 = null; function getUint8Memory0() { - if ( - cachegetUint8Memory0 === null || - cachegetUint8Memory0.buffer !== wasm.memory.buffer - ) { - cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); } - return cachegetUint8Memory0; + return cachedUint8Memory0; } function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -const heap = new Array(32).fill(undefined); +const heap = new Array(128).fill(undefined); heap.push(undefined, null, true, false); @@ -42,7 +47,7 @@ function getObject(idx) { } function dropObject(idx) { - if (idx < 36) return; + if (idx < 132) return; heap[idx] = heap_next; heap_next = idx; } @@ -55,7 +60,14 @@ function takeObject(idx) { let WASM_VECTOR_LEN = 0; -let cachedTextEncoder = new TextEncoder('utf-8'); +const cachedTextEncoder = + typeof TextEncoder !== 'undefined' + ? new TextEncoder('utf-8') + : { + encode: () => { + throw Error('TextEncoder not available'); + }, + }; const encodeString = typeof cachedTextEncoder.encodeInto === 'function' @@ -74,7 +86,7 @@ const encodeString = function passStringToWasm0(arg, malloc, realloc) { if (realloc === undefined) { const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length); + const ptr = malloc(buf.length, 1) >>> 0; getUint8Memory0() .subarray(ptr, ptr + buf.length) .set(buf); @@ -83,7 +95,7 @@ function passStringToWasm0(arg, malloc, realloc) { } let len = arg.length; - let ptr = malloc(len); + let ptr = malloc(len, 1) >>> 0; const mem = getUint8Memory0(); @@ -99,7 +111,7 @@ function passStringToWasm0(arg, malloc, realloc) { if (offset !== 0) { arg = arg.slice(offset); } - ptr = realloc(ptr, len, (len = offset + arg.length * 3)); + ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0; const view = getUint8Memory0().subarray(ptr + offset, ptr + len); const ret = encodeString(arg, view); @@ -110,15 +122,13 @@ function passStringToWasm0(arg, malloc, realloc) { return ptr; } -let cachegetInt32Memory0 = null; +let cachedInt32Memory0 = null; + function getInt32Memory0() { - if ( - cachegetInt32Memory0 === null || - cachegetInt32Memory0.buffer !== wasm.memory.buffer - ) { - cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); } - return cachegetInt32Memory0; + return cachedInt32Memory0; } /** * @param {string} source @@ -127,31 +137,35 @@ function getInt32Memory0() { * @returns {string} */ export function glsl_compile(source, stage, validation_enabled) { + let deferred3_0; + let deferred3_1; try { const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - var ptr0 = passStringToWasm0( + const ptr0 = passStringToWasm0( source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc, ); - var len0 = WASM_VECTOR_LEN; - var ptr1 = passStringToWasm0( + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0( stage, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc, ); - var len1 = WASM_VECTOR_LEN; + const len1 = WASM_VECTOR_LEN; wasm.glsl_compile(retptr, ptr0, len0, ptr1, len1, validation_enabled); var r0 = getInt32Memory0()[retptr / 4 + 0]; var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred3_0 = r0; + deferred3_1 = r1; return getStringFromWasm0(r0, r1); } finally { wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(r0, r1); + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); } } -async function load(module, imports) { +async function __wbg_load(module, imports) { if (typeof Response === 'function' && module instanceof Response) { if (typeof WebAssembly.instantiateStreaming === 'function') { try { @@ -181,29 +195,64 @@ async function load(module, imports) { } } -async function init(input) { - if (typeof input === 'undefined') { - // input = new URL('glsl_wgsl_compiler_bg.wasm', import.meta.url); - } +function __wbg_get_imports() { const imports = {}; imports.wbg = {}; imports.wbg.__wbindgen_string_new = function (arg0, arg1) { - var ret = getStringFromWasm0(arg0, arg1); + const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); }; imports.wbg.__wbindgen_object_drop_ref = function (arg0) { takeObject(arg0); }; - imports.wbg.__wbg_log_fbd13631356d44e4 = function (arg0) { + imports.wbg.__wbg_log_1d3ae0273d8f4f8a = function (arg0) { console.log(getObject(arg0)); }; - imports.wbg.__wbg_log_00fa6d531d56c191 = function (arg0, arg1) { + imports.wbg.__wbg_log_576ca876af0d4a77 = function (arg0, arg1) { console.log(getObject(arg0), getObject(arg1)); }; imports.wbg.__wbindgen_throw = function (arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) {} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === 'undefined') { + // input = new URL('glsl_wgsl_compiler_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + if ( typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || @@ -212,12 +261,12 @@ async function init(input) { input = fetch(input); } - const { instance, module } = await load(await input, imports); + __wbg_init_memory(imports); - wasm = instance.exports; - init.__wbindgen_wasm_module = module; + const { instance, module } = await __wbg_load(await input, imports); - return wasm; + return __wbg_finalize_init(instance, module); } -export default init; +export { initSync }; +export default __wbg_init; diff --git a/rust/pkg/glsl_wgsl_compiler_bg.wasm b/rust/pkg/glsl_wgsl_compiler_bg.wasm index 8e7f6ae1b..172b691da 100644 Binary files a/rust/pkg/glsl_wgsl_compiler_bg.wasm and b/rust/pkg/glsl_wgsl_compiler_bg.wasm differ diff --git a/rust/pkg/glsl_wgsl_compiler_bg.wasm.d.ts b/rust/pkg/glsl_wgsl_compiler_bg.wasm.d.ts index 80422e735..8110124a2 100644 --- a/rust/pkg/glsl_wgsl_compiler_bg.wasm.d.ts +++ b/rust/pkg/glsl_wgsl_compiler_bg.wasm.d.ts @@ -1,15 +1,8 @@ /* tslint:disable */ /* eslint-disable */ export const memory: WebAssembly.Memory; -export function glsl_compile( - a: number, - b: number, - c: number, - d: number, - e: number, - f: number, -): void; +export function glsl_compile(a: number, b: number, c: number, d: number, e: number, f: number): void; export function __wbindgen_add_to_stack_pointer(a: number): number; -export function __wbindgen_malloc(a: number): number; -export function __wbindgen_realloc(a: number, b: number, c: number): number; -export function __wbindgen_free(a: number, b: number): void; +export function __wbindgen_malloc(a: number, b: number): number; +export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; +export function __wbindgen_free(a: number, b: number, c: number): void; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0a32cc4ab..0c31fe15a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -21,7 +21,7 @@ pub fn glsl_compile(source: &str, stage: &str, validation_enabled: bool) -> Stri } .unwrap(); - let mut parser = naga::front::glsl::Parser::default(); + let mut parser = naga::front::glsl::Frontend::default(); let module = match parser.parse( &naga::front::glsl::Options { stage: naga_stage, diff --git a/site/.dumi/global.ts b/site/.dumi/global.ts index b8363dab7..55a6126f2 100644 --- a/site/.dumi/global.ts +++ b/site/.dumi/global.ts @@ -36,6 +36,11 @@ if (window) { (window as any).gPluginBox2d = require('@antv/g-plugin-box2d'); (window as any).gPluginMatterjs = require('@antv/g-plugin-matterjs'); (window as any).gPluginYoga = require('@antv/g-plugin-yoga'); + ( + window as any + ).gPluginDeviceRenderer = require('@antv/g-plugin-device-renderer'); + (window as any).gPluginWebglDevice = require('@antv/g-plugin-webgl-device'); + (window as any).gPluginWebgpuDevice = require('@antv/g-plugin-webgpu-device'); ( window as any ).gPluginRoughCanvasRenderer = require('@antv/g-plugin-rough-canvas-renderer'); @@ -70,29 +75,29 @@ if (window) { require('../css/demo.css'); - // origin trial for WebGPU - // @see https://developer.chrome.com/origintrials/#/trials/my - // @see https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md#16-can-i-provide-tokens-by-running-script - const tokenElement1 = document.createElement('meta'); - tokenElement1.httpEquiv = 'origin-trial'; - tokenElement1.content = - // https://localhost:8000 - 'AoSdEiPBm19CVIF6skKpsmzQmAdKAC1oCV6NS2P12Xjtk1da77EZnrm8wSk1ymIsarjl7xHkMQChky3cDi7RdA0AAABJeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjkxNzExOTk5fQ=='; - document.head.appendChild(tokenElement1); + // // origin trial for WebGPU + // // @see https://developer.chrome.com/origintrials/#/trials/my + // // @see https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md#16-can-i-provide-tokens-by-running-script + // const tokenElement1 = document.createElement('meta'); + // tokenElement1.httpEquiv = 'origin-trial'; + // tokenElement1.content = + // // https://localhost:8000 + // 'AoSdEiPBm19CVIF6skKpsmzQmAdKAC1oCV6NS2P12Xjtk1da77EZnrm8wSk1ymIsarjl7xHkMQChky3cDi7RdA0AAABJeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjkxNzExOTk5fQ=='; + // document.head.appendChild(tokenElement1); - const tokenElement2 = document.createElement('meta'); - tokenElement2.httpEquiv = 'origin-trial'; - tokenElement2.content = - // https://g-next.antv.vision - 'Aiq1IshZRuOKWEmA9h5liGM4kZYradTjzi0PiIizN0S2MSVpsY3GZxIBYOM3MkbIaEbY4kZTo/2AhRMCbZKDKAwAAABleyJvcmlnaW4iOiJodHRwczovL2ctbmV4dC5hbnR2LnZpc2lvbjo0NDMiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjkxNzExOTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0='; - document.head.appendChild(tokenElement2); + // const tokenElement2 = document.createElement('meta'); + // tokenElement2.httpEquiv = 'origin-trial'; + // tokenElement2.content = + // // https://g-next.antv.vision + // 'Aiq1IshZRuOKWEmA9h5liGM4kZYradTjzi0PiIizN0S2MSVpsY3GZxIBYOM3MkbIaEbY4kZTo/2AhRMCbZKDKAwAAABleyJvcmlnaW4iOiJodHRwczovL2ctbmV4dC5hbnR2LnZpc2lvbjo0NDMiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjkxNzExOTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0='; + // document.head.appendChild(tokenElement2); - const tokenElement3 = document.createElement('meta'); - tokenElement3.httpEquiv = 'origin-trial'; - tokenElement3.content = - // https://g.antv.antgroup.com - 'Au/LZbSWTC8hhagDyjnu46PChWcCITqE8iS8sTPw9teCa7ptAToyP2KrKls8qN09QjjZSgYsGudQesarc0PxaAcAAABTeyJvcmlnaW4iOiJodHRwczovL2cuYW50di5hbnRncm91cC5jb206NDQzIiwiZmVhdHVyZSI6IldlYkdQVSIsImV4cGlyeSI6MTY5MTcxMTk5OX0='; - document.head.appendChild(tokenElement3); + // const tokenElement3 = document.createElement('meta'); + // tokenElement3.httpEquiv = 'origin-trial'; + // tokenElement3.content = + // // https://g.antv.antgroup.com + // 'Au/LZbSWTC8hhagDyjnu46PChWcCITqE8iS8sTPw9teCa7ptAToyP2KrKls8qN09QjjZSgYsGudQesarc0PxaAcAAABTeyJvcmlnaW4iOiJodHRwczovL2cuYW50di5hbnRncm91cC5jb206NDQzIiwiZmVhdHVyZSI6IldlYkdQVSIsImV4cGlyeSI6MTY5MTcxMTk5OX0='; + // document.head.appendChild(tokenElement3); } if ( diff --git a/site/css/demo.css b/site/css/demo.css index 7dc950dd6..e2634c90b 100644 --- a/site/css/demo.css +++ b/site/css/demo.css @@ -19,3 +19,25 @@ .lil-gui .c select { color: black; } + +@keyframes animated-size { + 0% { + width: 10px; + height: 500px; + } + 50% { + width: 800px; + height: 500px; + } + 100% { + width: 10px; + height: 500px; + } +} + +.animatedCanvasSize { + animation-duration: 3s; + animation-iteration-count: infinite; + animation-name: animated-size; + animation-timing-function: ease; +} diff --git a/site/docs/api/3d/geometry.en.md b/site/docs/api/3d/geometry.en.md index 0035bffd7..63bfa8545 100644 --- a/site/docs/api/3d/geometry.en.md +++ b/site/docs/api/3d/geometry.en.md @@ -172,7 +172,7 @@ const mesh = new Mesh({ bufferGeometry.setVertexBuffer({ bufferIndex: 1, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -264,7 +264,7 @@ geometry.setIndices(new Uint32Array(indices)); - bufferIndex 索引 - byteStride stride 长度(以 byte 为单位) -- frequency 支持 vertex 和 instance 两种 +- stepMode 支持 vertex 和 instance 两种 - attributes 支持 interleave,其中每个属性包括: - format 对应 Shader 中的数据类型 - bufferByteOffset 在 stride 中的偏移量 @@ -277,7 +277,7 @@ geometry.setIndices(new Uint32Array(indices)); export interface GeometryVertexBufferDescriptor { bufferIndex: number; byteStride: number; - frequency: VertexBufferFrequency; + stepMode: VertexStepMode; attributes: Array<{ format: Format, bufferByteOffset: number, @@ -301,7 +301,7 @@ layout(location = 10) attribute vec3 a_Position; geometry.setVertexBuffer({ bufferIndex: ProceduralGeometryAttributeLocation.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, // 与 vec3 对应 diff --git a/site/docs/api/3d/geometry.zh.md b/site/docs/api/3d/geometry.zh.md index 0035bffd7..63bfa8545 100644 --- a/site/docs/api/3d/geometry.zh.md +++ b/site/docs/api/3d/geometry.zh.md @@ -172,7 +172,7 @@ const mesh = new Mesh({ bufferGeometry.setVertexBuffer({ bufferIndex: 1, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -264,7 +264,7 @@ geometry.setIndices(new Uint32Array(indices)); - bufferIndex 索引 - byteStride stride 长度(以 byte 为单位) -- frequency 支持 vertex 和 instance 两种 +- stepMode 支持 vertex 和 instance 两种 - attributes 支持 interleave,其中每个属性包括: - format 对应 Shader 中的数据类型 - bufferByteOffset 在 stride 中的偏移量 @@ -277,7 +277,7 @@ geometry.setIndices(new Uint32Array(indices)); export interface GeometryVertexBufferDescriptor { bufferIndex: number; byteStride: number; - frequency: VertexBufferFrequency; + stepMode: VertexStepMode; attributes: Array<{ format: Format, bufferByteOffset: number, @@ -301,7 +301,7 @@ layout(location = 10) attribute vec3 a_Position; geometry.setVertexBuffer({ bufferIndex: ProceduralGeometryAttributeLocation.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, // 与 vec3 对应 diff --git a/site/docs/api/3d/material.en.md b/site/docs/api/3d/material.en.md index ed9e7f8b7..a983e90b9 100644 --- a/site/docs/api/3d/material.en.md +++ b/site/docs/api/3d/material.en.md @@ -105,10 +105,10 @@ export enum CullMode { ### frontFace -默认使用 `FrontFaceMode.CCW`,即逆时针方向作为正面 winding order: +默认使用 `FrontFace.CCW`,即逆时针方向作为正面 winding order: ```js -export enum FrontFaceMode { +export enum FrontFace { CCW = GL.CCW, CW = GL.CW, } diff --git a/site/docs/api/3d/material.zh.md b/site/docs/api/3d/material.zh.md index 173fb2f29..5ca00783e 100644 --- a/site/docs/api/3d/material.zh.md +++ b/site/docs/api/3d/material.zh.md @@ -105,10 +105,10 @@ export enum CullMode { ### frontFace -默认使用 `FrontFaceMode.CCW`,即逆时针方向作为正面 winding order: +默认使用 `FrontFace.CCW`,即逆时针方向作为正面 winding order: ```js -export enum FrontFaceMode { +export enum FrontFace { CCW = GL.CCW, CW = GL.CW, } diff --git a/site/examples/3d/device/demo/compute-boids.ts b/site/examples/3d/device/demo/compute-boids.ts new file mode 100644 index 000000000..c15f940a5 --- /dev/null +++ b/site/examples/3d/device/demo/compute-boids.ts @@ -0,0 +1,391 @@ +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + Buffer, + Bindings, + BufferUsage, + BufferFrequencyHint, +} from '@antv/g-plugin-device-renderer'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/computeBoids#main.ts + */ + +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render(deviceContribution: WebGPUDeviceContribution) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + const numParticles = 1500; + const initialParticleData = new Float32Array(numParticles * 4); + for (let i = 0; i < numParticles; ++i) { + initialParticleData[4 * i + 0] = 2 * (Math.random() - 0.5); + initialParticleData[4 * i + 1] = 2 * (Math.random() - 0.5); + initialParticleData[4 * i + 2] = 2 * (Math.random() - 0.5) * 0.1; + initialParticleData[4 * i + 3] = 2 * (Math.random() - 0.5) * 0.1; + } + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + wgsl: ` +struct VertexOutput { + @builtin(position) position : vec4, + @location(4) color : vec4, +} + +@vertex +fn vert_main( + @location(0) a_particlePos : vec2, + @location(1) a_particleVel : vec2, + @location(2) a_pos : vec2 +) -> VertexOutput { + let angle = -atan2(a_particleVel.x, a_particleVel.y); + let pos = vec2( + (a_pos.x * cos(angle)) - (a_pos.y * sin(angle)), + (a_pos.x * sin(angle)) + (a_pos.y * cos(angle)) + ); + + var output : VertexOutput; + output.position = vec4(pos + a_particlePos, 0.0, 1.0); + output.color = vec4( + 1.0 - sin(angle + 1.0) - a_particleVel.y, + pos.x * 100.0 - a_particleVel.y + 0.1, + a_particleVel.x + cos(angle + 0.5), + 1.0); + return output; +} +`, + }, + fragment: { + wgsl: ` +@fragment +fn frag_main(@location(4) color : vec4) -> @location(0) vec4 { + return color; +} +`, + }, + }); + + const computeProgram = device.createProgram({ + compute: { + wgsl: ` +struct Particle { + pos : vec2, + vel : vec2, +} +struct SimParams { + deltaT : f32, + rule1Distance : f32, + rule2Distance : f32, + rule3Distance : f32, + rule1Scale : f32, + rule2Scale : f32, + rule3Scale : f32, +} +struct Particles { + particles : array, +} +@binding(0) @group(0) var params : SimParams; +@binding(1) @group(0) var particlesA : Particles; +@binding(2) @group(0) var particlesB : Particles; + +// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp +@compute @workgroup_size(64) +fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) { + var index = GlobalInvocationID.x; + + var vPos = particlesA.particles[index].pos; + var vVel = particlesA.particles[index].vel; + var cMass = vec2(0.0); + var cVel = vec2(0.0); + var colVel = vec2(0.0); + var cMassCount = 0u; + var cVelCount = 0u; + var pos : vec2; + var vel : vec2; + + for (var i = 0u; i < arrayLength(&particlesA.particles); i++) { + if (i == index) { + continue; + } + + pos = particlesA.particles[i].pos.xy; + vel = particlesA.particles[i].vel.xy; + if (distance(pos, vPos) < params.rule1Distance) { + cMass += pos; + cMassCount++; + } + if (distance(pos, vPos) < params.rule2Distance) { + colVel -= pos - vPos; + } + if (distance(pos, vPos) < params.rule3Distance) { + cVel += vel; + cVelCount++; + } + } + if (cMassCount > 0) { + cMass = (cMass / vec2(f32(cMassCount))) - vPos; + } + if (cVelCount > 0) { + cVel /= f32(cVelCount); + } + vVel += (cMass * params.rule1Scale) + (colVel * params.rule2Scale) + (cVel * params.rule3Scale); + + // clamp velocity for a more pleasing simulation + vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1); + // kinematic update + vPos = vPos + (vVel * params.deltaT); + // Wrap around boundary + if (vPos.x < -1.0) { + vPos.x = 1.0; + } + if (vPos.x > 1.0) { + vPos.x = -1.0; + } + if (vPos.y < -1.0) { + vPos.y = 1.0; + } + if (vPos.y > 1.0) { + vPos.y = -1.0; + } + // Write back + particlesB.particles[index].pos = vPos; + particlesB.particles[index].vel = vVel; +} +`, + }, + }); + + const particleBuffers: Buffer[] = []; + for (let i = 0; i < 2; ++i) { + particleBuffers[i] = device.createBuffer({ + viewOrSize: initialParticleData, + usage: BufferUsage.VERTEX | BufferUsage.STORAGE, + }); + } + + const vertexBufferData = new Float32Array([ + -0.01, -0.02, 0.01, -0.02, 0.0, 0.02, + ]); + const spriteVertexBuffer = device.createBuffer({ + viewOrSize: vertexBufferData, + usage: BufferUsage.VERTEX, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: 7 * Float32Array.BYTES_PER_ELEMENT, + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const bindingLayouts = [{ numSamplers: 0, numUniformBuffers: 1 }]; + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 4, + stepMode: VertexStepMode.INSTANCE, + }, + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + // instance position + bufferIndex: 0, + location: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + { + // instance velocity + bufferIndex: 0, + location: 1, + bufferByteOffset: 4 * 2, + format: Format.F32_RG, + }, + { + // vertex positions + bufferIndex: 1, + location: 2, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const renderPipeline = device.createRenderPipeline({ + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + }); + const computePipeline = device.createComputePipeline({ + inputLayout: null, + bindingLayouts: [], + program: computeProgram, + }); + + const simParams = { + deltaT: 0.04, + rule1Distance: 0.1, + rule2Distance: 0.025, + rule3Distance: 0.025, + rule1Scale: 0.02, + rule2Scale: 0.05, + rule3Scale: 0.005, + }; + + const bindings: Bindings[] = []; + for (let i = 0; i < 2; ++i) { + bindings[i] = device.createBindings({ + pipeline: computePipeline, + bindingLayout: { + numUniformBuffers: 1, + storageEntries: [ + { + type: 'storage', + }, + { + type: 'storage', + }, + ], + }, + uniformBufferBindings: [ + { + buffer: uniformBuffer, + wordCount: 7, + }, + ], + storageBufferBindings: [ + { + buffer: particleBuffers[i], + wordCount: initialParticleData.byteLength, + }, + { + buffer: particleBuffers[(i + 1) % 2], + wordCount: initialParticleData.byteLength, + }, + ], + samplerBindings: [], + }); + } + + const renderTarget = device.createRenderTarget({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + }); + device.setResourceName(renderTarget, 'Main Render Target'); + + uniformBuffer.setSubData( + 0, + new Uint8Array( + new Float32Array([ + simParams.deltaT, + simParams.rule1Distance, + simParams.rule2Distance, + simParams.rule3Distance, + simParams.rule1Scale, + simParams.rule2Scale, + simParams.rule3Scale, + ]).buffer, + ), + ); + + let id; + let t = 0; + const frame = () => { + // compute + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const computePass = device.createComputePass(); + computePass.setPipeline(computePipeline); + computePass.setBindings(0, bindings[t % 2], [0]); + computePass.dispatchWorkgroups(Math.ceil(numParticles / 64)); + device.submitPass(computePass); + + // const renderPass = device.createRenderPass({ + // colorAttachment: [renderTarget], + // colorResolveTo: [onscreenTexture], + // colorClearColor: [TransparentWhite], + // }); + + // renderPass.setPipeline(renderPipeline); + // renderPass.setVertexInput( + // inputLayout, + // [ + // { + // buffer: particleBuffers[(t + 1) % 2], + // }, + // { + // buffer: spriteVertexBuffer, + // }, + // ], + // null, + // ); + // renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + // renderPass.draw(3, numParticles); + + // device.submitPass(renderPass); + ++t; + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + // vertexBuffer.destroy(); + inputLayout.destroy(); + renderPipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + await render(deviceContributionWebGPU); +})(); diff --git a/site/examples/3d/device/demo/cubemap.ts b/site/examples/3d/device/demo/cubemap.ts new file mode 100644 index 000000000..9be4c13fa --- /dev/null +++ b/site/examples/3d/device/demo/cubemap.ts @@ -0,0 +1,418 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + BlendMode, + BlendFactor, + TextureUsage, + CullMode, + ChannelWriteMask, + TransparentBlack, + CompareMode, + WrapMode, + TexFilterMode, + MipFilterMode, + TextureDimension, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; +import { mat4, vec3 } from 'gl-matrix'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/texturedCube + */ + +const deviceContributionWebGL1 = new WebGLDeviceContribution({ + targets: ['webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function loadImage(url: string) { + if (!!window.createImageBitmap) { + const response = await fetch(url); + const imageBitmap = await createImageBitmap(await response.blob()); + return imageBitmap; + } else { + const image = new window.Image(); + return new Promise((res) => { + image.onload = () => res(image); + image.src = url; + image.crossOrigin = 'Anonymous'; + }); + } +} + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(std140) uniform Uniforms { + mat4 u_ModelViewProjectionMatrix; +}; + +layout(location = 0) in vec4 a_Position; +layout(location = 1) in vec2 a_Uv; + +out vec2 v_Uv; + +void main() { + v_Uv = a_Uv; + gl_Position = u_ModelViewProjectionMatrix * a_Position; +} +`, + }, + fragment: { + glsl: ` +uniform samplerCube u_Texture; +in vec2 v_Uv; +out vec4 outputColor; + +void main() { + outputColor = texture(SAMPLER_Cube(u_Texture), v_Uv); +} +`, + }, + }); + + const cubeVertexSize = 4 * 10; // Byte size of one cube vertex. + const cubePositionOffset = 0; + const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute. + const cubeUVOffset = 4 * 8; + const cubeVertexCount = 36; + + const cubeVertexArray = new Float32Array([ + // float4 position, float4 color, float2 uv, + 1, -1, 1, 1, 1, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 1, 1, 1, -1, -1, -1, + 1, 0, 0, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 1, 1, 0, 1, + 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, + 1, 0, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, -1, -1, 1, 1, 0, 0, 1, 1, 0, + + -1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + 1, 0, 1, 1, 0, -1, 1, -1, 1, 0, 1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, -1, 1, 1, 1, 0, 1, 1, 0, + + -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1, 1, + 0, 1, 0, 1, 1, 0, -1, -1, -1, 1, 0, 0, 0, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, + 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, 1, + 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + + 1, -1, -1, 1, 1, 0, 0, 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 1, -1, 1, -1, + 1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, -1, -1, 1, 1, 0, 0, + 1, 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + ]); + + const vertexBuffer = device.createBuffer({ + viewOrSize: cubeVertexArray, + usage: BufferUsage.VERTEX, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: 16 * 4, // mat4 + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const imgSrcs = [ + '/images/posx.jpg', + '/images/negx.jpg', + '/images/posy.jpg', + '/images/negy.jpg', + '/images/posz.jpg', + '/images/negz.jpg', + ]; + const promises = imgSrcs.map(async (src) => { + return loadImage(src); + }); + const imageBitmaps = await Promise.all(promises); + + const texture = device.createTexture({ + pixelFormat: Format.U8_RGBA_NORM, + width: imageBitmaps[0].width, + height: imageBitmaps[0].height, + depth: 6, + dimension: TextureDimension.TEXTURE_CUBE_MAP, + usage: TextureUsage.SAMPLED, + immutable: false, + }); + texture.setImageData(imageBitmaps); + + const sampler = device.createSampler({ + wrapS: WrapMode.CLAMP, + wrapT: WrapMode.CLAMP, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.BILINEAR, + mipFilter: MipFilterMode.LINEAR, + minLOD: 0, + maxLOD: 0, + }); + + const bindingLayouts = [{ numSamplers: 1, numUniformBuffers: 1 }]; + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: cubeVertexSize, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: cubePositionOffset, + format: Format.F32_RGBA, + }, + { + location: 1, + bufferIndex: 0, + bufferByteOffset: cubeUVOffset, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + bindingLayouts, + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + depthStencilAttachmentFormat: Format.D24_S8, + megaStateDescriptor: { + attachmentsState: [ + { + channelWriteMask: ChannelWriteMask.ALL, + rgbBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.SRC_ALPHA, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + alphaBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.ONE, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + }, + ], + blendConstant: TransparentBlack, + depthWrite: true, + depthCompare: CompareMode.LESS, + // Since we are seeing from inside of the cube + // and we are using the regular cube geomtry data with outward-facing normals, + // the cullMode should be 'front' or 'none'. + cullMode: CullMode.NONE, + stencilWrite: false, + }, + }); + + const bindings = device.createBindings({ + pipeline, + bindingLayout: bindingLayouts[0], + uniformBufferBindings: [ + { + buffer: uniformBuffer, + wordCount: 16, + }, + ], + samplerBindings: [ + { + texture, + sampler, + }, + ], + }); + + const mainColorRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + const mainDepthRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.D24_S8, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + let id; + const modelMatrix = mat4.fromScaling( + mat4.create(), + vec3.fromValues(1000, 1000, 1000), + ); + const modelViewProjectionMatrix = mat4.create(); + const viewMatrix = mat4.identity(mat4.create()); + const tmpMat4 = mat4.create(); + + const frame = () => { + const aspect = $canvas.width / $canvas.height; + const projectionMatrix = mat4.perspective( + mat4.create(), + (2 * Math.PI) / 5, + aspect, + 1, + 3000, + ); + + const now = Date.now() / 800; + + mat4.rotate( + tmpMat4, + viewMatrix, + (Math.PI / 10) * Math.sin(now), + vec3.fromValues(1, 0, 0), + ); + mat4.rotate(tmpMat4, tmpMat4, now * 0.2, vec3.fromValues(0, 1, 0)); + mat4.multiply(modelViewProjectionMatrix, tmpMat4, modelMatrix); + mat4.multiply( + modelViewProjectionMatrix, + projectionMatrix, + modelViewProjectionMatrix, + ); + uniformBuffer.setSubData( + 0, + new Uint8Array((modelViewProjectionMatrix as Float32Array).buffer), + ); + // WebGL1 need this + program.setUniformsLegacy({ + u_ModelViewProjectionMatrix: modelViewProjectionMatrix, + u_Texture: texture, + }); + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [mainColorRT], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + depthStencilAttachment: mainDepthRT, + depthClearValue: 1, + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.setBindings(0, bindings, [0]); + renderPass.draw(cubeVertexCount, 1, 0, 0); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + uniformBuffer.destroy(); + inputLayout.destroy(); + bindings.destroy(); + pipeline.destroy(); + mainColorRT.destroy(); + mainDepthRT.destroy(); + texture.destroy(); + sampler.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGPU); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl1', 'webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl1') { + disposeCallback = await render(deviceContributionWebGL1); + } else if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/instanced-cubes.ts b/site/examples/3d/device/demo/instanced-cubes.ts new file mode 100644 index 000000000..940fdfc0d --- /dev/null +++ b/site/examples/3d/device/demo/instanced-cubes.ts @@ -0,0 +1,367 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + BlendMode, + BlendFactor, + TextureUsage, + CullMode, + ChannelWriteMask, + TransparentBlack, + CompareMode, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; +import { mat4, vec3 } from 'gl-matrix'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/instancedCube#main.ts + */ +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(std140) uniform Uniforms { + mat4 u_ModelViewProjectionMatrix[16]; +}; + +layout(location = 0) in vec3 a_Position; + +out vec4 v_Position; + +void main() { + v_Position = vec4(a_Position, 1.0); + gl_Position = u_ModelViewProjectionMatrix[gl_InstanceID] * vec4(a_Position, 1.0); +} +`, + }, + fragment: { + glsl: ` +in vec4 v_Position; +out vec4 outputColor; + +void main() { + outputColor = v_Position; +} +`, + }, + }); + + const cubeVertexSize = 4 * 10; // Byte size of one cube vertex. + const cubePositionOffset = 0; + const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute. + const cubeUVOffset = 4 * 8; + const cubeVertexCount = 36; + + const cubeVertexArray = new Float32Array([ + // float4 position, float4 color, float2 uv, + 1, -1, 1, 1, 1, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 1, 1, 1, -1, -1, -1, + 1, 0, 0, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 1, 1, 0, 1, + 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, + 1, 0, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, -1, -1, 1, 1, 0, 0, 1, 1, 0, + + -1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + 1, 0, 1, 1, 0, -1, 1, -1, 1, 0, 1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, -1, 1, 1, 1, 0, 1, 1, 0, + + -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1, 1, + 0, 1, 0, 1, 1, 0, -1, -1, -1, 1, 0, 0, 0, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, + 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, 1, + 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + + 1, -1, -1, 1, 1, 0, 0, 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 1, -1, 1, -1, + 1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, -1, -1, 1, 1, 0, 0, + 1, 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + ]); + + const xCount = 4; + const yCount = 4; + const numInstances = xCount * yCount; + const matrixFloatCount = 16; // 4x4 matrix + const matrixSize = 4 * matrixFloatCount; + const uniformBufferSize = numInstances * matrixSize; + + const vertexBuffer = device.createBuffer({ + viewOrSize: cubeVertexArray, + usage: BufferUsage.VERTEX, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: uniformBufferSize, // mat4 + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const bindingLayouts = [{ numSamplers: 0, numUniformBuffers: 1 }]; + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: cubeVertexSize, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RGB, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + bindingLayouts, + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + depthStencilAttachmentFormat: Format.D24_S8, + megaStateDescriptor: { + attachmentsState: [ + { + channelWriteMask: ChannelWriteMask.ALL, + rgbBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.SRC_ALPHA, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + alphaBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.ONE, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + }, + ], + blendConstant: TransparentBlack, + depthWrite: true, + depthCompare: CompareMode.LESS, + cullMode: CullMode.BACK, + stencilWrite: false, + }, + }); + + const bindings = device.createBindings({ + pipeline, + bindingLayout: bindingLayouts[0], + uniformBufferBindings: [ + { + buffer: uniformBuffer, + wordCount: matrixFloatCount * numInstances, + }, + ], + samplerBindings: [], + }); + + const mainColorRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + const mainDepthRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.D24_S8, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + const aspect = $canvas.width / $canvas.height; + const projectionMatrix = mat4.perspective( + mat4.create(), + (2 * Math.PI) / 5, + aspect, + 1, + 100, + ); + const modelMatrices = new Array(numInstances); + const mvpMatricesData = new Float32Array(matrixFloatCount * numInstances); + + const step = 4.0; + + // Initialize the matrix data for every instance. + let m = 0; + for (let x = 0; x < xCount; x++) { + for (let y = 0; y < yCount; y++) { + modelMatrices[m] = mat4.fromTranslation( + mat4.create(), + vec3.fromValues( + step * (x - xCount / 2 + 0.5), + step * (y - yCount / 2 + 0.5), + 0, + ), + ); + m++; + } + } + + const viewMatrix = mat4.fromTranslation( + mat4.create(), + vec3.fromValues(0, 0, -12), + ); + const tmpMat4 = mat4.create(); + + let id; + const frame = () => { + const now = Date.now() / 1000; + + let m = 0, + i = 0; + for (let x = 0; x < xCount; x++) { + for (let y = 0; y < yCount; y++) { + mat4.rotate( + tmpMat4, + modelMatrices[i], + 1, + vec3.fromValues( + Math.sin((x + 0.5) * now), + Math.cos((y + 0.5) * now), + 0, + ), + ); + + mat4.multiply(tmpMat4, viewMatrix, tmpMat4); + mat4.multiply(tmpMat4, projectionMatrix, tmpMat4); + + mvpMatricesData.set(tmpMat4, m); + + i++; + m += matrixFloatCount; + } + } + + uniformBuffer.setSubData(0, new Uint8Array(mvpMatricesData.buffer)); + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [mainColorRT], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + depthStencilAttachment: mainDepthRT, + depthClearValue: 1, + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.setBindings(0, bindings, [0]); + renderPass.draw(cubeVertexCount, numInstances); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + uniformBuffer.destroy(); + inputLayout.destroy(); + bindings.destroy(); + pipeline.destroy(); + mainColorRT.destroy(); + mainDepthRT.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/meta.json b/site/examples/3d/device/demo/meta.json new file mode 100644 index 000000000..dcbb55376 --- /dev/null +++ b/site/examples/3d/device/demo/meta.json @@ -0,0 +1,96 @@ +{ + "title": { + "zh": "Device API", + "en": "Device API" + }, + "demos": [ + { + "filename": "primitive-topology-points.ts", + "title": { + "zh": "绘制点 - GL.POINTS", + "en": "GL.POINTS" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*WJU8SpnjjkUAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "primitive-topology-triangles.ts", + "title": { + "zh": "绘制三角形 - GL.TRIANGLES", + "en": "GL.TRIANGLES" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*JetpQZ--TzQAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "rotating-cube.ts", + "title": { + "zh": "旋转的立方体", + "en": "Rotating cube" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ygJNSJCkAWgAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "textured-cube.ts", + "title": { + "zh": "贴图的立方体", + "en": "Textured cube" + }, + "screenshot": "https://mdn.alipayobjects.com/mdn/huamei_qa8qxu/afts/img/A*xZcvSrlYoE8AAAAAAAAAAAAADmJ7AQ" + }, + { + "filename": "instanced-cubes.ts", + "title": { + "zh": "批量绘制立方体", + "en": "Instanced cubes" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*OO5nSL6fcB0AAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "msaa.ts", + "title": { + "zh": "MSAA", + "en": "MSAA" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*JetpQZ--TzQAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "resize-canvas.ts", + "title": { + "zh": "改变 Canvas 尺寸", + "en": "Resize canvas" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*JetpQZ--TzQAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "sampler.ts", + "title": { + "zh": "Sampler", + "en": "Sampler" + }, + "screenshot": "" + }, + { + "filename": "cubemap.ts", + "title": { + "zh": "Cubemap", + "en": "Cubemap" + }, + "screenshot": "" + }, + { + "filename": "compute-boids.ts", + "title": { + "zh": "使用 Compute Shader 仿真", + "en": "Use Compute Shader" + }, + "screenshot": "" + }, + { + "filename": "wgsl.ts", + "title": { + "zh": "直接在 WebGPU 中使用 WGSL 语法", + "en": "Use WGSL" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*JetpQZ--TzQAAAAAAAAAAAAADmJ7AQ/original" + } + ] +} diff --git a/site/examples/3d/device/demo/msaa.ts b/site/examples/3d/device/demo/msaa.ts new file mode 100644 index 000000000..def76de53 --- /dev/null +++ b/site/examples/3d/device/demo/msaa.ts @@ -0,0 +1,194 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; + +/** + * Use multisampled antialiasing (MSAA). + * @see https://github.com/shrekshao/MoveWebGL1EngineToWebGL2/blob/master/Move-a-WebGL-1-Engine-To-WebGL-2-Blog-2.md#multisampled-renderbuffers + * @see https://webgpu.github.io/webgpu-samples/samples/helloTriangleMSAA#main.ts + */ + +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(location = 0) in vec2 a_Position; + +void main() { + gl_Position = vec4(a_Position, 0.0, 1.0); +} +`, + }, + fragment: { + glsl: ` +out vec4 outputColor; + +void main() { + outputColor = vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + device.setResourceName(vertexBuffer, 'a_Position'); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + sampleCount: 4, + }); + + const renderTarget = device.createRenderTarget({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + sampleCount: 4, + }); + device.setResourceName(renderTarget, 'Main Render Target'); + + let id; + const frame = () => { + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [renderTarget], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.draw(3); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + inputLayout.destroy(); + pipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/primitive-topology-points.ts b/site/examples/3d/device/demo/primitive-topology-points.ts new file mode 100644 index 000000000..c0fda99e1 --- /dev/null +++ b/site/examples/3d/device/demo/primitive-topology-points.ts @@ -0,0 +1,184 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + PrimitiveTopology, + BufferUsage, + BufferFrequencyHint, + TextureUsage, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; + +/** + * WebGPU doesn't support gl_PointSize + * @see https://github.com/gpuweb/gpuweb/issues/1190 + */ + +const deviceContributionWebGL1 = new WebGLDeviceContribution({ + targets: ['webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const onscreenTexture = swapChain.getOnscreenTexture(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(location = 0) in vec2 a_Position; + +void main() { + gl_Position = vec4(a_Position, 0.0, 1.0); + gl_PointSize = 100.0; +} +`, + }, + fragment: { + glsl: ` +out vec4 outputColor; + +void main() { + outputColor = vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + inputLayout, + program, + topology: PrimitiveTopology.POINTS, + colorAttachmentFormats: [Format.U8_RGBA_RT], + }); + + const renderTarget = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + const frame = () => { + const renderPass = device.createRenderPass({ + colorAttachment: [renderTarget], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.draw(3); + + device.submitPass(renderPass); + requestAnimationFrame(frame); + }; + + frame(); + + return () => { + program.destroy(); + vertexBuffer.destroy(); + inputLayout.destroy(); + pipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl1', 'webgl2']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl1') { + disposeCallback = await render(deviceContributionWebGL1); + } else if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/primitive-topology-triangles.ts b/site/examples/3d/device/demo/primitive-topology-triangles.ts new file mode 100644 index 000000000..075fcf651 --- /dev/null +++ b/site/examples/3d/device/demo/primitive-topology-triangles.ts @@ -0,0 +1,198 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + TextureUsage, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; + +const deviceContributionWebGL1 = new WebGLDeviceContribution({ + targets: ['webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(location = 0) in vec2 a_Position; + +void main() { + gl_Position = vec4(a_Position, 0.0, 1.0); +} +`, + }, + fragment: { + glsl: ` +out vec4 outputColor; + +void main() { + outputColor = vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + device.setResourceName(vertexBuffer, 'a_Position'); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + }); + + const renderTarget = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + device.setResourceName(renderTarget, 'Main Render Target'); + + let id; + const frame = () => { + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [renderTarget], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.draw(3); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + inputLayout.destroy(); + pipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl1', 'webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl1') { + disposeCallback = await render(deviceContributionWebGL1); + } else if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/resize-canvas.ts b/site/examples/3d/device/demo/resize-canvas.ts new file mode 100644 index 000000000..9ac18e4cf --- /dev/null +++ b/site/examples/3d/device/demo/resize-canvas.ts @@ -0,0 +1,208 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/resizeCanvas#main.ts + */ + +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.classList.add('animatedCanvasSize'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(location = 0) in vec2 a_Position; + +void main() { + gl_Position = vec4(a_Position, 0.0, 1.0); +} +`, + }, + fragment: { + glsl: ` +out vec4 outputColor; + +void main() { + outputColor = vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + device.setResourceName(vertexBuffer, 'a_Position'); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + sampleCount: 4, + }); + + let renderTarget = device.createRenderTarget({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + sampleCount: 4, + }); + + let id; + const frame = () => { + const currentWidth = $canvas.clientWidth * devicePixelRatio; + const currentHeight = $canvas.clientHeight * devicePixelRatio; + + $canvas.width = currentWidth; + $canvas.height = currentHeight; + + swapChain.configureSwapChain($canvas.width, $canvas.height); + + if (renderTarget) { + renderTarget.destroy(); + renderTarget = device.createRenderTarget({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + sampleCount: 4, + }); + } + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [renderTarget], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.draw(3); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + inputLayout.destroy(); + pipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/rotating-cube.ts b/site/examples/3d/device/demo/rotating-cube.ts new file mode 100644 index 000000000..376b9c65d --- /dev/null +++ b/site/examples/3d/device/demo/rotating-cube.ts @@ -0,0 +1,334 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + BlendMode, + BlendFactor, + TextureUsage, + CullMode, + ChannelWriteMask, + TransparentBlack, + CompareMode, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; +import { mat4, vec3 } from 'gl-matrix'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/rotatingCube + */ + +const deviceContributionWebGL1 = new WebGLDeviceContribution({ + targets: ['webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(std140) uniform Uniforms { + mat4 u_ModelViewProjectionMatrix; +}; + +layout(location = 0) in vec3 a_Position; + +out vec4 v_Position; + +void main() { + v_Position = vec4(a_Position, 1.0); + gl_Position = u_ModelViewProjectionMatrix * vec4(a_Position, 1.0); +} +`, + }, + fragment: { + glsl: ` +in vec4 v_Position; +out vec4 outputColor; + +void main() { + outputColor = v_Position; +} +`, + }, + }); + + const cubeVertexSize = 4 * 10; // Byte size of one cube vertex. + const cubePositionOffset = 0; + const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute. + const cubeUVOffset = 4 * 8; + const cubeVertexCount = 36; + + const cubeVertexArray = new Float32Array([ + // float4 position, float4 color, float2 uv, + 1, -1, 1, 1, 1, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 1, 1, 1, -1, -1, -1, + 1, 0, 0, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 1, 1, 0, 1, + 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, + 1, 0, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, -1, -1, 1, 1, 0, 0, 1, 1, 0, + + -1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + 1, 0, 1, 1, 0, -1, 1, -1, 1, 0, 1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, -1, 1, 1, 1, 0, 1, 1, 0, + + -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1, 1, + 0, 1, 0, 1, 1, 0, -1, -1, -1, 1, 0, 0, 0, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, + 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, 1, + 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + + 1, -1, -1, 1, 1, 0, 0, 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 1, -1, 1, -1, + 1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, -1, -1, 1, 1, 0, 0, + 1, 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + ]); + + const vertexBuffer = device.createBuffer({ + viewOrSize: cubeVertexArray, + usage: BufferUsage.VERTEX, + // hint: BufferFrequencyHint.DYNAMIC, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: 16 * 4, // mat4 + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const bindingLayouts = [{ numSamplers: 0, numUniformBuffers: 1 }]; + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: cubeVertexSize, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RGB, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + bindingLayouts, + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + depthStencilAttachmentFormat: Format.D24_S8, + megaStateDescriptor: { + attachmentsState: [ + { + channelWriteMask: ChannelWriteMask.ALL, + rgbBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.SRC_ALPHA, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + alphaBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.ONE, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + }, + ], + blendConstant: TransparentBlack, + depthWrite: true, + depthCompare: CompareMode.LESS, + cullMode: CullMode.BACK, + stencilWrite: false, + }, + }); + + const bindings = device.createBindings({ + pipeline, + bindingLayout: bindingLayouts[0], + uniformBufferBindings: [ + { + buffer: uniformBuffer, + wordCount: 16, + }, + ], + samplerBindings: [], + }); + + const mainColorRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + const mainDepthRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.D24_S8, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + let id; + const frame = () => { + const aspect = $canvas.width / $canvas.height; + const projectionMatrix = mat4.perspective( + mat4.create(), + (2 * Math.PI) / 5, + aspect, + 0.1, + 1000, + ); + const viewMatrix = mat4.identity(mat4.create()); + const modelViewProjectionMatrix = mat4.create(); + mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4)); + const now = Date.now() / 1000; + mat4.rotate( + viewMatrix, + viewMatrix, + 1, + vec3.fromValues(Math.sin(now), Math.cos(now), 0), + ); + mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix); + uniformBuffer.setSubData( + 0, + new Uint8Array((modelViewProjectionMatrix as Float32Array).buffer), + ); + // WebGL1 need this + program.setUniformsLegacy({ + u_ModelViewProjectionMatrix: modelViewProjectionMatrix, + }); + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [mainColorRT], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + depthStencilAttachment: mainDepthRT, + depthClearValue: 1, + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.setBindings(0, bindings, [0]); + renderPass.draw(cubeVertexCount); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + uniformBuffer.destroy(); + inputLayout.destroy(); + bindings.destroy(); + pipeline.destroy(); + mainColorRT.destroy(); + mainDepthRT.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGL2); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl1', 'webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl1') { + disposeCallback = await render(deviceContributionWebGL1); + } else if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/sampler.ts b/site/examples/3d/device/demo/sampler.ts new file mode 100644 index 000000000..e69de29bb diff --git a/site/examples/3d/device/demo/textured-cube.ts b/site/examples/3d/device/demo/textured-cube.ts new file mode 100644 index 000000000..f071d732c --- /dev/null +++ b/site/examples/3d/device/demo/textured-cube.ts @@ -0,0 +1,389 @@ +import { WebGLDeviceContribution } from '@antv/g-plugin-webgl-device'; +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + BlendMode, + BlendFactor, + TextureUsage, + CullMode, + ChannelWriteMask, + TransparentBlack, + CompareMode, + WrapMode, + TexFilterMode, + MipFilterMode, +} from '@antv/g-plugin-device-renderer'; +import * as lil from 'lil-gui'; +import { mat4, vec3 } from 'gl-matrix'; + +/** + * @see https://webgpu.github.io/webgpu-samples/samples/texturedCube + */ + +const deviceContributionWebGL1 = new WebGLDeviceContribution({ + targets: ['webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGL2 = new WebGLDeviceContribution({ + targets: ['webgl2', 'webgl1'], + onContextCreationError: () => {}, + onContextLost: () => {}, + onContextRestored(e) {}, +}); +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function loadImage(url: string) { + if (!!window.createImageBitmap) { + const response = await fetch(url); + const imageBitmap = await createImageBitmap(await response.blob()); + return imageBitmap; + } else { + const image = new window.Image(); + return new Promise((res) => { + image.onload = () => res(image); + image.src = url; + image.crossOrigin = 'Anonymous'; + }); + } +} + +async function render( + deviceContribution: WebGLDeviceContribution | WebGPUDeviceContribution, +) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + glsl: ` +layout(std140) uniform Uniforms { + mat4 u_ModelViewProjectionMatrix; +}; + +layout(location = 0) in vec4 a_Position; +layout(location = 1) in vec2 a_Uv; + +out vec2 v_Uv; + +void main() { + v_Uv = a_Uv; + gl_Position = u_ModelViewProjectionMatrix * a_Position; +} +`, + }, + fragment: { + glsl: ` +uniform sampler2D u_Texture; +in vec2 v_Uv; +out vec4 outputColor; + +void main() { + outputColor = texture(SAMPLER_2D(u_Texture), v_Uv); +} +`, + }, + }); + + const cubeVertexSize = 4 * 10; // Byte size of one cube vertex. + const cubePositionOffset = 0; + const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute. + const cubeUVOffset = 4 * 8; + const cubeVertexCount = 36; + + const cubeVertexArray = new Float32Array([ + // float4 position, float4 color, float2 uv, + 1, -1, 1, 1, 1, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 1, 1, 1, -1, -1, -1, + 1, 0, 0, 0, 1, 1, 0, 1, -1, -1, 1, 1, 0, 0, 1, 0, 0, 1, -1, 1, 1, 1, 0, 1, + 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, + 1, 0, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, -1, -1, 1, 1, 0, 0, 1, 1, 0, + + -1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + 1, 0, 1, 1, 0, -1, 1, -1, 1, 0, 1, 0, 1, 0, 0, -1, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, -1, 1, 1, 1, 0, 1, 1, 0, + + -1, -1, 1, 1, 0, 0, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, 1, -1, 1, + 0, 1, 0, 1, 1, 0, -1, -1, -1, 1, 0, 0, 0, 1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, + 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1, -1, 1, 1, + 0, 0, 1, 1, 1, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 0, 1, -1, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + + 1, -1, -1, 1, 1, 0, 0, 1, 0, 1, -1, -1, -1, 1, 0, 0, 0, 1, 1, 1, -1, 1, -1, + 1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 1, 1, 0, 1, 0, 0, 1, -1, -1, 1, 1, 0, 0, + 1, 0, 1, -1, 1, -1, 1, 0, 1, 0, 1, 1, 0, + ]); + + const vertexBuffer = device.createBuffer({ + viewOrSize: cubeVertexArray, + usage: BufferUsage.VERTEX, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: 16 * 4, // mat4 + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const imageBitmap = await loadImage( + 'https://gw.alipayobjects.com/mdn/rms_6ae20b/afts/img/A*_aqoS73Se3sAAAAAAAAAAAAAARQnAQ', + ); + const texture = device.createTexture({ + pixelFormat: Format.U8_RGBA_NORM, + width: imageBitmap.width, + height: imageBitmap.height, + usage: TextureUsage.SAMPLED, + immutable: false, + }); + texture.setImageData([imageBitmap]); + + const sampler = device.createSampler({ + wrapS: WrapMode.CLAMP, + wrapT: WrapMode.CLAMP, + minFilter: TexFilterMode.POINT, + magFilter: TexFilterMode.BILINEAR, + mipFilter: MipFilterMode.LINEAR, + minLOD: 0, + maxLOD: 0, + }); + + const bindingLayouts = [{ numSamplers: 1, numUniformBuffers: 1 }]; + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: cubeVertexSize, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: cubePositionOffset, + format: Format.F32_RGBA, + }, + { + location: 1, + bufferIndex: 0, + bufferByteOffset: cubeUVOffset, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + bindingLayouts, + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + depthStencilAttachmentFormat: Format.D24_S8, + megaStateDescriptor: { + attachmentsState: [ + { + channelWriteMask: ChannelWriteMask.ALL, + rgbBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.SRC_ALPHA, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + alphaBlendState: { + blendMode: BlendMode.ADD, + blendSrcFactor: BlendFactor.ONE, + blendDstFactor: BlendFactor.ONE_MINUS_SRC_ALPHA, + }, + }, + ], + blendConstant: TransparentBlack, + depthWrite: true, + depthCompare: CompareMode.LESS, + cullMode: CullMode.BACK, + stencilWrite: false, + }, + }); + + const bindings = device.createBindings({ + pipeline, + bindingLayout: bindingLayouts[0], + uniformBufferBindings: [ + { + buffer: uniformBuffer, + wordCount: 16, + }, + ], + samplerBindings: [ + { + texture, + sampler, + }, + ], + }); + + const mainColorRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + const mainDepthRT = device.createRenderTargetFromTexture( + device.createTexture({ + pixelFormat: Format.D24_S8, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + let id; + const frame = () => { + const aspect = $canvas.width / $canvas.height; + const projectionMatrix = mat4.perspective( + mat4.create(), + (2 * Math.PI) / 5, + aspect, + 0.1, + 1000, + ); + const viewMatrix = mat4.identity(mat4.create()); + const modelViewProjectionMatrix = mat4.create(); + mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4)); + const now = Date.now() / 1000; + mat4.rotate( + viewMatrix, + viewMatrix, + 1, + vec3.fromValues(Math.sin(now), Math.cos(now), 0), + ); + mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix); + uniformBuffer.setSubData( + 0, + new Uint8Array((modelViewProjectionMatrix as Float32Array).buffer), + ); + // WebGL1 need this + program.setUniformsLegacy({ + u_ModelViewProjectionMatrix: modelViewProjectionMatrix, + u_Texture: texture, + }); + + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [mainColorRT], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + depthStencilAttachment: mainDepthRT, + depthClearValue: 1, + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.setBindings(0, bindings, [0]); + renderPass.draw(cubeVertexCount); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + uniformBuffer.destroy(); + inputLayout.destroy(); + bindings.destroy(); + pipeline.destroy(); + mainColorRT.destroy(); + mainDepthRT.destroy(); + texture.destroy(); + sampler.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + let disposeCallback = await render(deviceContributionWebGPU); + + // GUI + const gui = new lil.GUI({ autoPlace: false }); + $container.appendChild(gui.domElement); + const rendererFolder = gui.addFolder('renderer'); + const rendererConfig = { + renderer: 'webgl2', + }; + rendererFolder + .add(rendererConfig, 'renderer', ['webgl1', 'webgl2', 'webgpu']) + .onChange(async (renderer) => { + if (disposeCallback) { + disposeCallback(); + // @ts-ignore + disposeCallback = undefined; + } + + if (renderer === 'webgl1') { + disposeCallback = await render(deviceContributionWebGL1); + } else if (renderer === 'webgl2') { + disposeCallback = await render(deviceContributionWebGL2); + } else if (renderer === 'webgpu') { + disposeCallback = await render(deviceContributionWebGPU); + } + }); + rendererFolder.open(); +})(); diff --git a/site/examples/3d/device/demo/wgsl.ts b/site/examples/3d/device/demo/wgsl.ts new file mode 100644 index 000000000..7e471642a --- /dev/null +++ b/site/examples/3d/device/demo/wgsl.ts @@ -0,0 +1,163 @@ +import { WebGPUDeviceContribution } from '@antv/g-plugin-webgpu-device'; +import { + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, +} from '@antv/g-plugin-device-renderer'; + +/** + * Draw a triangle + * @see https://webgpu.github.io/webgpu-samples/samples/helloTriangle + */ + +const deviceContributionWebGPU = new WebGPUDeviceContribution( + { + shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm', + }, + // @ts-ignore + { + globalThis: window, + }, +); + +const $container = document.getElementById('container')!; +const $canvasContainer = document.createElement('div'); +$canvasContainer.id = 'canvas'; +$container.appendChild($canvasContainer); + +async function render(deviceContribution: WebGPUDeviceContribution) { + $canvasContainer.innerHTML = ''; + const $canvas = document.createElement('canvas'); + $canvas.width = 1000; + $canvas.height = 1000; + $canvas.style.width = '500px'; + $canvas.style.height = '500px'; + $canvasContainer.appendChild($canvas); + + // create swap chain and get device + const swapChain = await deviceContribution.createSwapChain($canvas); + + // TODO: resize + swapChain.configureSwapChain($canvas.width, $canvas.height); + const device = swapChain.getDevice(); + + const program = device.createProgram({ + vertex: { + wgsl: ` +@vertex +fn main( + @builtin(vertex_index) VertexIndex : u32 +) -> @builtin(position) vec4 { + var pos = array, 3>( + vec2(0.0, 0.5), + vec2(-0.5, -0.5), + vec2(0.5, -0.5) + ); + + return vec4(pos[VertexIndex], 0.0, 1.0); +} +`, + }, + fragment: { + wgsl: ` +@fragment +fn main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + device.setResourceName(vertexBuffer, 'a_Position'); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + byteStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + }, + ], + vertexAttributeDescriptors: [ + { + location: 0, + bufferIndex: 0, + bufferByteOffset: 0, + format: Format.F32_RG, + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + inputLayout, + program, + colorAttachmentFormats: [Format.U8_RGBA_RT], + }); + + const renderTarget = device.createRenderTarget({ + pixelFormat: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + }); + device.setResourceName(renderTarget, 'Main Render Target'); + + let id; + const frame = () => { + /** + * An application should call getCurrentTexture() in the same task that renders to the canvas texture. + * Otherwise, the texture could get destroyed by these steps before the application is finished rendering to it. + */ + const onscreenTexture = swapChain.getOnscreenTexture(); + + const renderPass = device.createRenderPass({ + colorAttachment: [renderTarget], + colorResolveTo: [onscreenTexture], + colorClearColor: [TransparentWhite], + }); + + renderPass.setPipeline(pipeline); + renderPass.setVertexInput( + inputLayout, + [ + { + buffer: vertexBuffer, + }, + ], + null, + ); + renderPass.setViewport(0, 0, $canvas.width, $canvas.height); + renderPass.draw(3); + + device.submitPass(renderPass); + id = requestAnimationFrame(frame); + }; + + frame(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + program.destroy(); + vertexBuffer.destroy(); + inputLayout.destroy(); + pipeline.destroy(); + renderTarget.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +(async () => { + await render(deviceContributionWebGPU); +})(); diff --git a/site/examples/3d/device/index.en.md b/site/examples/3d/device/index.en.md new file mode 100644 index 000000000..1e94b7683 --- /dev/null +++ b/site/examples/3d/device/index.en.md @@ -0,0 +1,4 @@ +--- +title: Device API +order: 10 +--- diff --git a/site/examples/3d/device/index.zh.md b/site/examples/3d/device/index.zh.md new file mode 100644 index 000000000..1e94b7683 --- /dev/null +++ b/site/examples/3d/device/index.zh.md @@ -0,0 +1,4 @@ +--- +title: Device API +order: 10 +--- diff --git a/site/examples/3d/geometry/demo/buffer-geometry.js b/site/examples/3d/geometry/demo/buffer-geometry.js index db8310dea..cf04d50ae 100644 --- a/site/examples/3d/geometry/demo/buffer-geometry.js +++ b/site/examples/3d/geometry/demo/buffer-geometry.js @@ -4,7 +4,7 @@ import { MeshBasicMaterial, BufferGeometry, Mesh, - VertexBufferFrequency, + VertexStepMode, Format, VertexAttributeBufferIndex, VertexAttributeLocation, @@ -39,7 +39,7 @@ const canvas = new Canvas({ bufferGeometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, diff --git a/site/examples/3d/geometry/demo/cube.js b/site/examples/3d/geometry/demo/cube.js index d83a7355f..061c87476 100644 --- a/site/examples/3d/geometry/demo/cube.js +++ b/site/examples/3d/geometry/demo/cube.js @@ -46,8 +46,8 @@ const canvas = new Canvas({ // height: 1, // depth: 1, // numLevels: 1, - // dimension: TextureDimension.n2D, - // usage: TextureUsage.Sampled, + // dimension: TextureDimension.TEXTURE_2D, + // usage: TextureUsage.SAMPLED, // }); // // load image // const image = new window.Image(); diff --git a/site/examples/3d/geometry/demo/point.js b/site/examples/3d/geometry/demo/point.js index d2404c45e..f8c1e697a 100644 --- a/site/examples/3d/geometry/demo/point.js +++ b/site/examples/3d/geometry/demo/point.js @@ -4,7 +4,7 @@ import { PointMaterial, BufferGeometry, Mesh, - VertexBufferFrequency, + VertexStepMode, Format, VertexAttributeBufferIndex, VertexAttributeLocation, @@ -46,7 +46,7 @@ const canvas = new Canvas({ bufferGeometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, @@ -63,7 +63,7 @@ const canvas = new Canvas({ // draw 4 vertices bufferGeometry.vertexCount = 4; // use GL_POINT instead of GL_TRIANGLES - bufferGeometry.drawMode = PrimitiveTopology.Points; + bufferGeometry.drawMode = PrimitiveTopology.POINTS; // load texture with URL const map = plugin.loadTexture( diff --git a/site/examples/3d/material/demo/basic.js b/site/examples/3d/material/demo/basic.js index b8bbc7d25..6d5940583 100644 --- a/site/examples/3d/material/demo/basic.js +++ b/site/examples/3d/material/demo/basic.js @@ -76,8 +76,6 @@ const canvas = new Canvas({ if (stats) { stats.update(); } - torus.setOrigin(0, 0, 0); - torus.rotate(0, 0.2, 0); }); // GUI diff --git a/site/examples/3d/material/demo/shader-material.js b/site/examples/3d/material/demo/shader-material.js index bfc362466..7ee0b4837 100644 --- a/site/examples/3d/material/demo/shader-material.js +++ b/site/examples/3d/material/demo/shader-material.js @@ -4,7 +4,7 @@ import { ShaderMaterial, BufferGeometry, Mesh, - VertexBufferFrequency, + VertexStepMode, Format, VertexAttributeBufferIndex, VertexAttributeLocation, @@ -14,7 +14,9 @@ import Stats from 'stats.js'; import * as lil from 'lil-gui'; // create a renderer -const renderer = new Renderer(); +const renderer = new Renderer({ + targets: ['webgl1'], +}); renderer.registerPlugin(new Plugin3D()); // create a canvas @@ -40,7 +42,7 @@ const canvas = new Canvas({ bufferGeometry.setVertexBuffer({ bufferIndex: VertexAttributeBufferIndex.POSITION, byteStride: 4 * 3, - frequency: VertexBufferFrequency.PerVertex, + stepMode: VertexStepMode.VERTEX, attributes: [ { format: Format.F32_RGB, diff --git a/site/examples/perf/webgl/demo/circle-zoomin.js b/site/examples/perf/webgl/demo/circle-zoomin.js new file mode 100644 index 000000000..a811fb544 --- /dev/null +++ b/site/examples/perf/webgl/demo/circle-zoomin.js @@ -0,0 +1,57 @@ +import { Canvas, CanvasEvent, Circle, Text } from '@antv/g'; +import { Renderer as WebGLRenderer } from '@antv/g-webgl'; +import Stats from 'stats.js'; + +const renderer = new WebGLRenderer(); + +// create a canvas +const canvas = new Canvas({ + container: 'container', + width: 600, + height: 500, + renderer, +}); + +canvas.addEventListener(CanvasEvent.READY, () => { + for (let i = 0; i < 100; i++) { + const x = Math.random() * 600; + const y = Math.random() * 500; + canvas.appendChild( + new Circle({ + attrs: { + fill: '#C6E5FF', + stroke: 'red', + r: 20, + cx: x, + cy: y, + lineWidth: 3, + }, + }), + ); + + canvas.appendChild( + new Text({ + attrs: { + text: 'ccc', + x, + y, + fill: '#ccc', + fontSize: 12, + }, + }), + ); + } +}); + +const camera = canvas.getCamera(); +camera.setZoom(10); + +// stats +const stats = new Stats(); +stats.showPanel(0); +const $stats = stats.dom; +$stats.style.position = 'absolute'; +$stats.style.left = '0px'; +$stats.style.top = '0px'; +const $wrapper = document.getElementById('container'); +$wrapper.appendChild($stats); diff --git a/site/examples/perf/webgl/demo/meta.json b/site/examples/perf/webgl/demo/meta.json index 49bbe6eeb..826c09d47 100644 --- a/site/examples/perf/webgl/demo/meta.json +++ b/site/examples/perf/webgl/demo/meta.json @@ -1,5 +1,12 @@ { "demos": [ + { + "filename": "circle-zoomin.js", + "title": { + "zh": "缩放的 Circle", + "en": "Zoom in Circle" + } + }, { "filename": "circle-linedash.js", "title": { diff --git a/site/static/glsl_wgsl_compiler_bg.wasm b/site/static/glsl_wgsl_compiler_bg.wasm index 8e7f6ae1b..172b691da 100644 Binary files a/site/static/glsl_wgsl_compiler_bg.wasm and b/site/static/glsl_wgsl_compiler_bg.wasm differ diff --git a/site/static/images/negx.jpg b/site/static/images/negx.jpg new file mode 100644 index 000000000..992fde512 Binary files /dev/null and b/site/static/images/negx.jpg differ diff --git a/site/static/images/negy.jpg b/site/static/images/negy.jpg new file mode 100644 index 000000000..a51a38dc7 Binary files /dev/null and b/site/static/images/negy.jpg differ diff --git a/site/static/images/negz.jpg b/site/static/images/negz.jpg new file mode 100644 index 000000000..c463f0d5b Binary files /dev/null and b/site/static/images/negz.jpg differ diff --git a/site/static/images/posx.jpg b/site/static/images/posx.jpg new file mode 100644 index 000000000..106d3a68f Binary files /dev/null and b/site/static/images/posx.jpg differ diff --git a/site/static/images/posy.jpg b/site/static/images/posy.jpg new file mode 100644 index 000000000..1ea42cd20 Binary files /dev/null and b/site/static/images/posy.jpg differ diff --git a/site/static/images/posz.jpg b/site/static/images/posz.jpg new file mode 100644 index 000000000..69463d06a Binary files /dev/null and b/site/static/images/posz.jpg differ