UNPKG

typegpu

Version:

A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.

197 lines (196 loc) 8.19 kB
import { $internal } from "../../shared/symbols.js"; import { isGPUBuffer } from "../../types.js"; import { DRAW_INDEXED_INDIRECT_SIZE, DRAW_INDIRECT_SIZE, resolveIndirectOffset, } from "../pipeline/pipelineUtils.js"; import { isTexture, isTextureView } from "../texture/texture.js"; import { emitRenderDraw, recordBindGroup, RenderDrawState, stampRenderPipeline, } from "../pipeline/drawState.js"; import { isQuerySet } from "../querySet/querySet.js"; import { unwrapAttachmentView, unwrapTimestampWrites, } from "./attachments.js"; // -------------- // Implementation // -------------- function attachmentAspects(view) { let format; let aspect = 'all'; if (isTexture(view)) { format = view.props.format; } else if (isTextureView(view)) { format = view[$internal].format; aspect = view[$internal].aspect ?? 'all'; } if (format === undefined) { return undefined; } return { hasDepth: format.includes('depth') && aspect !== 'stencil-only', hasStencil: format.includes('stencil') && aspect !== 'depth-only', }; } function withDepthStencilDefaults(attachment, view) { const rawAttachment = { ...attachment, view }; const aspects = attachmentAspects(attachment.view); if (aspects === undefined) { const hasExplicitOps = attachment.depthLoadOp !== undefined || attachment.depthStoreOp !== undefined || attachment.stencilLoadOp !== undefined || attachment.stencilStoreOp !== undefined; if (hasExplicitOps) { return rawAttachment; } } const { hasDepth, hasStencil } = aspects ?? { hasDepth: true, hasStencil: false }; if (hasDepth && !attachment.depthReadOnly) { rawAttachment.depthLoadOp ??= 'clear'; rawAttachment.depthStoreOp ??= 'store'; rawAttachment.depthClearValue ??= 1; } if (hasStencil && !attachment.stencilReadOnly) { rawAttachment.stencilLoadOp ??= 'clear'; rawAttachment.stencilStoreOp ??= 'store'; } return rawAttachment; } export function INTERNAL_beginRenderPass(encoder, descriptor) { const { rawEncoder, root } = encoder[$internal]; const colorAttachments = descriptor.colorAttachments === undefined ? [] : Array.isArray(descriptor.colorAttachments) ? descriptor.colorAttachments : [descriptor.colorAttachments]; const rawDescriptor = { colorAttachments: colorAttachments.map((attachment) => { if (attachment === null) { return null; } const rawAttachment = { ...attachment, loadOp: attachment.loadOp ?? 'clear', storeOp: attachment.storeOp ?? 'store', view: unwrapAttachmentView(root, attachment.view), }; if (attachment.resolveTarget !== undefined) { rawAttachment.resolveTarget = unwrapAttachmentView(root, attachment.resolveTarget); } return rawAttachment; }), }; if (descriptor.label !== undefined) { rawDescriptor.label = descriptor.label; } if (descriptor.depthStencilAttachment !== undefined) { rawDescriptor.depthStencilAttachment = withDepthStencilDefaults(descriptor.depthStencilAttachment, unwrapAttachmentView(root, descriptor.depthStencilAttachment.view)); } if (descriptor.occlusionQuerySet !== undefined) { rawDescriptor.occlusionQuerySet = isQuerySet(descriptor.occlusionQuerySet) ? root.unwrap(descriptor.occlusionQuerySet) : descriptor.occlusionQuerySet; } if (descriptor.timestampWrites !== undefined) { rawDescriptor.timestampWrites = unwrapTimestampWrites(root, descriptor.timestampWrites); } if (descriptor.maxDrawCount !== undefined) { rawDescriptor.maxDrawCount = descriptor.maxDrawCount; } return new TgpuRenderPassImpl(root, rawEncoder.beginRenderPass(rawDescriptor), encoder); } export function INTERNAL_createRenderBundleEncoder(root, descriptor) { return new TgpuRenderBundleEncoderImpl(root, root.device.createRenderBundleEncoder(descriptor), undefined); } export function INTERNAL_adoptRenderCommands(root, rawPass) { const adopted = typeof rawPass.executeBundles === 'function' ? new TgpuRenderPassImpl(root, rawPass, undefined) : new TgpuRenderCommandsImpl(root, rawPass, undefined); adopted[$internal].state.rawAccessed = true; return adopted; } class TgpuRenderCommandsImpl { [$internal]; resourceType = 'render-bundle-encoder'; #root; constructor(root, rawPass, owner) { this.#root = root; this[$internal] = { rawPass, state: new RenderDrawState(), owner, appliedVersion: undefined, }; } #emit(usesIndexBuffer, emit) { const internals = this[$internal]; const pipeline = internals.state.currentPipeline; if (!pipeline) { throw new Error('Cannot draw without a call to pass.setPipeline'); } emitRenderDraw(this.#root, internals, pipeline, usesIndexBuffer, emit); } setPipeline(pipeline) { stampRenderPipeline(this[$internal].state, pipeline); } setBindGroup(first, bindGroup) { recordBindGroup(this[$internal].state, first, bindGroup); } setVertexBuffer(vertexLayout, buffer, offset, size) { const { state } = this[$internal]; state.vertexBuffers.set(vertexLayout, { buffer, offset, size }); state.version++; } setIndexBuffer(buffer, indexFormat, offset, size) { const { state } = this[$internal]; state.indexBuffer = { buffer, indexFormat, offsetBytes: offset, sizeBytes: size }; state.version++; } draw(vertexCount, instanceCount, firstVertex, firstInstance) { this.#emit(false, (rawPass) => rawPass.draw(vertexCount, instanceCount, firstVertex, firstInstance)); } drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance) { this.#emit(true, (rawPass) => rawPass.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)); } drawIndirect(indirectBuffer, indirectOffset) { const rawBuffer = isGPUBuffer(indirectBuffer) ? indirectBuffer : indirectBuffer.buffer; const offset = resolveIndirectOffset(indirectBuffer, indirectOffset, DRAW_INDIRECT_SIZE, 'drawIndirect'); this.#emit(false, (rawPass) => rawPass.drawIndirect(rawBuffer, offset)); } drawIndexedIndirect(indirectBuffer, indirectOffset) { const rawBuffer = isGPUBuffer(indirectBuffer) ? indirectBuffer : indirectBuffer.buffer; const offset = resolveIndirectOffset(indirectBuffer, indirectOffset, DRAW_INDEXED_INDIRECT_SIZE, 'drawIndexedIndirect'); this.#emit(true, (rawPass) => rawPass.drawIndexedIndirect(rawBuffer, offset)); } } class TgpuRenderBundleEncoderImpl extends TgpuRenderCommandsImpl { resourceType = 'render-bundle-encoder'; finish(descriptor) { return this[$internal].rawPass.finish(descriptor); } } class TgpuRenderPassImpl extends TgpuRenderCommandsImpl { resourceType = 'render-pass'; setViewport(x, y, width, height, minDepth, maxDepth) { this[$internal].rawPass.setViewport(x, y, width, height, minDepth, maxDepth); } setScissorRect(x, y, width, height) { this[$internal].rawPass.setScissorRect(x, y, width, height); } setBlendConstant(color) { this[$internal].rawPass.setBlendConstant(color); } setStencilReference(reference) { const { state } = this[$internal]; state.stencilReference = reference; state.version++; } beginOcclusionQuery(queryIndex) { this[$internal].rawPass.beginOcclusionQuery(queryIndex); } endOcclusionQuery() { this[$internal].rawPass.endOcclusionQuery(); } executeBundles(bundles) { const internals = this[$internal]; internals.rawPass.executeBundles(bundles); internals.appliedVersion = undefined; } end() { this[$internal].rawPass.end(); } }