From 5e137c56f0856d79be2ec795eb3dc222edfb3a9c Mon Sep 17 00:00:00 2001 From: xiaoiver Date: Tue, 21 May 2024 19:18:11 +0800 Subject: [PATCH] Release (#192) * Support polygonOffset factor and units (#190) * fix: support polygonOffsetFactor and units #189 * chore: commit changeset * chore: add polygon offset example * chore(release): bump version (#191) Co-authored-by: github-actions[bot] --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 + README.md | 2 + examples/demos/index.ts | 1 + examples/demos/polygon-offset.ts | 333 +++++++++++++++++++++++++++++++ package.json | 2 +- src/api/interfaces.ts | 2 + src/api/utils/hash.ts | 4 +- src/api/utils/states.ts | 10 + src/webgl/Device.ts | 14 +- src/webgpu/utils.ts | 8 +- 10 files changed, 377 insertions(+), 5 deletions(-) create mode 100644 examples/demos/polygon-offset.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 09e211f..30d169c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-device-api +## 1.6.11 + +### Patch Changes + +- 533ee21: Support polygonOffsetFactor and units. + ## 1.6.10 ### Patch Changes diff --git a/README.md b/README.md index 039267b..488b452 100644 --- a/README.md +++ b/README.md @@ -521,6 +521,8 @@ interface MegaStateDescriptor { cullMode?: CullMode; frontFace?: FrontFace; polygonOffset?: boolean; + polygonOffsetFactor?: number; + polygonOffsetUnits?: number; } ``` diff --git a/examples/demos/index.ts b/examples/demos/index.ts index c35c6b3..2941cd0 100644 --- a/examples/demos/index.ts +++ b/examples/demos/index.ts @@ -46,3 +46,4 @@ export { render as ARThree } from './ar-three'; export { render as SetImageData } from './set-image-data'; export { render as RenderBundle } from './render-bundle'; export { render as Raymarching } from './raymarching'; +export { render as PolygonOffset } from './polygon-offset'; diff --git a/examples/demos/polygon-offset.ts b/examples/demos/polygon-offset.ts new file mode 100644 index 0000000..9e410de --- /dev/null +++ b/examples/demos/polygon-offset.ts @@ -0,0 +1,333 @@ +import { + DeviceContribution, + VertexStepMode, + Format, + TransparentWhite, + BufferUsage, + BufferFrequencyHint, + BlendMode, + BlendFactor, + TextureUsage, + CullMode, + ChannelWriteMask, + TransparentBlack, + CompareFunction, +} from '../../src'; +import { vec3, mat4 } from 'gl-matrix'; +import { + cubeVertexArray, + cubeVertexSize, + cubeVertexCount, +} from '../meshes/cube'; + +export async function render( + deviceContribution: DeviceContribution, + $canvas: HTMLCanvasElement, + useRAF = true, +) { + // 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 program2 = device.createProgram({ + vertex: { + glsl: ` +layout(location = 0) in vec2 a_Position; + +void main() { + gl_Position = vec4(a_Position, 0, 1.0); +} +`, + }, + fragment: { + glsl: ` +out vec4 outputColor; + +void main() { + outputColor = vec4(1.0, 0.0, 0.0, 1.0); +} +`, + }, + }); + + const vertexBuffer2 = device.createBuffer({ + viewOrSize: new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), + usage: BufferUsage.VERTEX, + hint: BufferFrequencyHint.DYNAMIC, + }); + device.setResourceName(vertexBuffer2, 'a_Position'); + + const inputLayout2 = device.createInputLayout({ + vertexBufferDescriptors: [ + { + arrayStride: 4 * 2, + stepMode: VertexStepMode.VERTEX, + attributes: [ + { + shaderLocation: 0, + offset: 0, + format: Format.F32_RG, + }, + ], + }, + ], + indexBufferFormat: null, + program: program2, + }); + + const pipeline2 = device.createRenderPipeline({ + inputLayout: inputLayout2, + program: program2, + 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: CompareFunction.LESS, + cullMode: CullMode.BACK, + stencilWrite: false, + }, + }); + + 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 vertexBuffer = device.createBuffer({ + viewOrSize: cubeVertexArray, + usage: BufferUsage.VERTEX, + }); + + const uniformBuffer = device.createBuffer({ + viewOrSize: 16 * 4, // mat4 + usage: BufferUsage.UNIFORM, + hint: BufferFrequencyHint.DYNAMIC, + }); + + const inputLayout = device.createInputLayout({ + vertexBufferDescriptors: [ + { + arrayStride: cubeVertexSize, + stepMode: VertexStepMode.VERTEX, + attributes: [ + { + shaderLocation: 0, + offset: 0, + format: Format.F32_RGB, + }, + ], + }, + ], + indexBufferFormat: null, + program, + }); + + const pipeline = device.createRenderPipeline({ + 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: CompareFunction.LESS, + cullMode: CullMode.BACK, + stencilWrite: false, + polygonOffset: true, + polygonOffsetFactor: 2, + polygonOffsetUnits: 3, + }, + }); + + const bindings = device.createBindings({ + pipeline, + uniformBufferBindings: [ + { + binding: 0, + buffer: uniformBuffer, + size: 16 * 4, + }, + ], + }); + + const mainColorRT = device.createRenderTargetFromTexture( + device.createTexture({ + format: Format.U8_RGBA_RT, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + const mainDepthRT = device.createRenderTargetFromTexture( + device.createTexture({ + format: Format.D24_S8, + width: $canvas.width, + height: $canvas.height, + usage: TextureUsage.RENDER_TARGET, + }), + ); + + let id: number; + 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 = useRAF ? Date.now() / 1000 : 0; + 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(); + + device.beginFrame(); + 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(bindings); + renderPass.draw(cubeVertexCount); + + renderPass.setPipeline(pipeline2); + renderPass.setVertexInput( + inputLayout2, + [ + { + buffer: vertexBuffer2, + }, + ], + null, + ); + renderPass.draw(3); + + device.submitPass(renderPass); + device.endFrame(); + if (useRAF) { + id = requestAnimationFrame(frame); + } + }; + + frame(); + + return () => { + if (useRAF && id) { + cancelAnimationFrame(id); + } + program.destroy(); + program2.destroy(); + vertexBuffer.destroy(); + vertexBuffer2.destroy(); + uniformBuffer.destroy(); + inputLayout.destroy(); + inputLayout2.destroy(); + bindings.destroy(); + pipeline.destroy(); + pipeline2.destroy(); + mainColorRT.destroy(); + mainDepthRT.destroy(); + device.destroy(); + + // For debug. + device.checkForLeaks(); + }; +} + +render.params = { + targets: ['webgl1', 'webgl2', 'webgpu'], + default: 'webgl2', +}; diff --git a/package.json b/package.json index eccbee1..770fab9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-device-api", - "version": "1.6.10", + "version": "1.6.11", "description": "A Device API references WebGPU implementations", "keywords": [ "antv", diff --git a/src/api/interfaces.ts b/src/api/interfaces.ts index 0069900..4cd8f92 100644 --- a/src/api/interfaces.ts +++ b/src/api/interfaces.ts @@ -576,6 +576,8 @@ export interface MegaStateDescriptor { cullMode?: CullMode; frontFace?: FrontFace; polygonOffset?: boolean; + polygonOffsetFactor?: number; + polygonOffsetUnits?: number; } export interface PipelineDescriptor { diff --git a/src/api/utils/hash.ts b/src/api/utils/hash.ts index 2be9f3d..4d79a90 100644 --- a/src/api/utils/hash.ts +++ b/src/api/utils/hash.ts @@ -180,7 +180,9 @@ function megaStateDescriptorEquals( a.stencilWrite === b.stencilWrite && a.cullMode === b.cullMode && a.frontFace === b.frontFace && - a.polygonOffset === b.polygonOffset + a.polygonOffset === b.polygonOffset && + a.polygonOffsetFactor === b.polygonOffsetFactor && + a.polygonOffsetUnits === b.polygonOffsetUnits ); } diff --git a/src/api/utils/states.ts b/src/api/utils/states.ts index e7fa2af..5140563 100644 --- a/src/api/utils/states.ts +++ b/src/api/utils/states.ts @@ -182,6 +182,14 @@ export function setMegaStateFlags( dst.cullMode = fallbackUndefined(src.cullMode, dst.cullMode); dst.frontFace = fallbackUndefined(src.frontFace, dst.frontFace); dst.polygonOffset = fallbackUndefined(src.polygonOffset, dst.polygonOffset); + dst.polygonOffsetFactor = fallbackUndefined( + src.polygonOffsetFactor, + dst.polygonOffsetFactor, + ); + dst.polygonOffsetUnits = fallbackUndefined( + src.polygonOffsetUnits, + dst.polygonOffsetUnits, + ); } export function copyMegaState(src: MegaStateDescriptor): MegaStateDescriptor { @@ -269,6 +277,8 @@ export const defaultMegaState: MegaStateDescriptor = { cullMode: CullMode.NONE, frontFace: FrontFace.CCW, polygonOffset: false, + polygonOffsetFactor: 0, + polygonOffsetUnits: 0, }; export function makeMegaState( diff --git a/src/webgl/Device.ts b/src/webgl/Device.ts index 5407b4c..6ecd95a 100644 --- a/src/webgl/Device.ts +++ b/src/webgl/Device.ts @@ -2145,13 +2145,25 @@ export class Device_GL implements SwapChain, Device { if (currentMegaState.polygonOffset !== newMegaState.polygonOffset) { if (newMegaState.polygonOffset) { - gl.polygonOffset(1, 1); gl.enable(gl.POLYGON_OFFSET_FILL); } else { gl.disable(gl.POLYGON_OFFSET_FILL); } currentMegaState.polygonOffset = newMegaState.polygonOffset; } + + if ( + currentMegaState.polygonOffsetFactor !== + newMegaState.polygonOffsetFactor || + currentMegaState.polygonOffsetUnits !== newMegaState.polygonOffsetUnits + ) { + gl.polygonOffset( + newMegaState.polygonOffsetFactor, + newMegaState.polygonOffsetUnits, + ); + currentMegaState.polygonOffsetFactor = newMegaState.polygonOffsetFactor; + currentMegaState.polygonOffsetUnits = newMegaState.polygonOffsetUnits; + } } private validatePipelineFormats(pipeline: RenderPipeline_GL): void { diff --git a/src/webgpu/utils.ts b/src/webgpu/utils.ts index 42e34ec..f4b5663 100644 --- a/src/webgpu/utils.ts +++ b/src/webgpu/utils.ts @@ -424,8 +424,12 @@ export function translateDepthStencilState( format: translateTextureFormat(format), depthWriteEnabled: !!megaStateDescriptor.depthWrite, depthCompare: translateCompareFunction(megaStateDescriptor.depthCompare), - depthBias: megaStateDescriptor.polygonOffset ? 1 : 0, - depthBiasSlopeScale: megaStateDescriptor.polygonOffset ? 1 : 0, + depthBias: megaStateDescriptor.polygonOffset + ? megaStateDescriptor.polygonOffsetUnits + : 0, + depthBiasSlopeScale: megaStateDescriptor.polygonOffset + ? megaStateDescriptor.polygonOffsetFactor + : 0, stencilFront: { compare: translateCompareFunction( megaStateDescriptor.stencilFront.compare,