11import type { TgpuBuffer , Vertex } from '../../core/buffer/buffer' ;
22import type { Disarray } from '../../data/dataTypes' ;
33import type { AnyWgslData , WgslArray } from '../../data/wgslTypes' ;
4- import { MissingBindGroupError } from '../../errors' ;
4+ import {
5+ MissingBindGroupsError ,
6+ MissingVertexBuffersError ,
7+ } from '../../errors' ;
58import type { TgpuNamable } from '../../namable' ;
69import { resolve } from '../../resolutionCtx' ;
710import type { AnyVertexAttribs } from '../../shared/vertexFormat' ;
@@ -48,6 +51,10 @@ export interface TgpuRenderPipeline<Output extends IOLayout = IOLayout>
4851 attachment : FragmentOutToColorAttachment < Output > ,
4952 ) : TgpuRenderPipeline < IOLayout > ;
5053
54+ withDepthStencilAttachment (
55+ attachment : DepthStencilAttachment ,
56+ ) : TgpuRenderPipeline < IOLayout > ;
57+
5158 draw (
5259 vertexCount : number ,
5360 instanceCount ?: number ,
@@ -111,6 +118,60 @@ export interface ColorAttachment {
111118 storeOp : GPUStoreOp ;
112119}
113120
121+ export interface DepthStencilAttachment {
122+ /**
123+ * A {@link GPUTextureView} | ({@link TgpuTexture} & {@link Render}) describing the texture subresource that will be output to
124+ * and read from for this depth/stencil attachment.
125+ */
126+ view : ( TgpuTexture & Render ) | GPUTextureView ;
127+ /**
128+ * Indicates the value to clear {@link GPURenderPassDepthStencilAttachment#view}'s depth component
129+ * to prior to executing the render pass. Ignored if {@link GPURenderPassDepthStencilAttachment#depthLoadOp}
130+ * is not {@link GPULoadOp#"clear"}. Must be between 0.0 and 1.0, inclusive (unless unrestricted depth is enabled).
131+ */
132+ depthClearValue ?: number ;
133+ /**
134+ * Indicates the load operation to perform on {@link GPURenderPassDepthStencilAttachment#view}'s
135+ * depth component prior to executing the render pass.
136+ * Note: It is recommended to prefer clearing; see {@link GPULoadOp#"clear"} for details.
137+ */
138+ depthLoadOp ?: GPULoadOp ;
139+ /**
140+ * The store operation to perform on {@link GPURenderPassDepthStencilAttachment#view}'s
141+ * depth component after executing the render pass.
142+ */
143+ depthStoreOp ?: GPUStoreOp ;
144+ /**
145+ * Indicates that the depth component of {@link GPURenderPassDepthStencilAttachment#view}
146+ * is read only.
147+ */
148+ depthReadOnly ?: boolean ;
149+ /**
150+ * Indicates the value to clear {@link GPURenderPassDepthStencilAttachment#view}'s stencil component
151+ * to prior to executing the render pass. Ignored if {@link GPURenderPassDepthStencilAttachment#stencilLoadOp}
152+ * is not {@link GPULoadOp#"clear"}.
153+ * The value will be converted to the type of the stencil aspect of `view` by taking the same
154+ * number of LSBs as the number of bits in the stencil aspect of one texel block|texel of `view`.
155+ */
156+ stencilClearValue ?: GPUStencilValue ;
157+ /**
158+ * Indicates the load operation to perform on {@link GPURenderPassDepthStencilAttachment#view}'s
159+ * stencil component prior to executing the render pass.
160+ * Note: It is recommended to prefer clearing; see {@link GPULoadOp#"clear"} for details.
161+ */
162+ stencilLoadOp ?: GPULoadOp ;
163+ /**
164+ * The store operation to perform on {@link GPURenderPassDepthStencilAttachment#view}'s
165+ * stencil component after executing the render pass.
166+ */
167+ stencilStoreOp ?: GPUStoreOp ;
168+ /**
169+ * Indicates that the stencil component of {@link GPURenderPassDepthStencilAttachment#view}
170+ * is read only.
171+ */
172+ stencilReadOnly ?: boolean ;
173+ }
174+
114175export type AnyFragmentColorAttachment =
115176 | ColorAttachment
116177 | Record < string , ColorAttachment > ;
@@ -122,6 +183,7 @@ export type RenderPipelineCoreOptions = {
122183 vertexFn : TgpuVertexFn ;
123184 fragmentFn : TgpuFragmentFn ;
124185 primitiveState : GPUPrimitiveState | undefined ;
186+ depthStencilState : GPUDepthStencilState | undefined ;
125187 targets : AnyFragmentTargets ;
126188} ;
127189
@@ -143,6 +205,7 @@ type TgpuRenderPipelinePriors = {
143205 | Map < TgpuBindGroupLayout , TgpuBindGroup >
144206 | undefined ;
145207 readonly colorAttachment ?: AnyFragmentColorAttachment | undefined ;
208+ readonly depthStencilAttachment ?: DepthStencilAttachment | undefined ;
146209} ;
147210
148211type Memo = {
@@ -212,6 +275,15 @@ class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
212275 } ) ;
213276 }
214277
278+ withDepthStencilAttachment (
279+ attachment : DepthStencilAttachment ,
280+ ) : TgpuRenderPipeline {
281+ return new TgpuRenderPipelineImpl ( this . _core , {
282+ ...this . _priors ,
283+ depthStencilAttachment : attachment ,
284+ } ) ;
285+ }
286+
215287 draw (
216288 vertexCount : number ,
217289 instanceCount ?: number ,
@@ -235,37 +307,68 @@ class TgpuRenderPipelineImpl implements TgpuRenderPipeline {
235307 return attachment ;
236308 } ) as GPURenderPassColorAttachment [ ] ;
237309
238- const pass = branch . commandEncoder . beginRenderPass ( {
239- label : this . _core . label ?? '<unnamed>' ,
310+ const renderPassDescriptor : GPURenderPassDescriptor = {
240311 colorAttachments,
241- } ) ;
312+ } ;
313+
314+ if ( this . _core . label !== undefined ) {
315+ renderPassDescriptor . label = this . _core . label ;
316+ }
317+
318+ if ( this . _priors . depthStencilAttachment !== undefined ) {
319+ const attachment = this . _priors . depthStencilAttachment ;
320+ if ( isTexture ( attachment . view ) ) {
321+ renderPassDescriptor . depthStencilAttachment = {
322+ ...attachment ,
323+ view : branch . unwrap ( attachment . view ) . createView ( ) ,
324+ } ;
325+ } else {
326+ renderPassDescriptor . depthStencilAttachment =
327+ attachment as GPURenderPassDepthStencilAttachment ;
328+ }
329+ }
330+
331+ const pass = branch . commandEncoder . beginRenderPass ( renderPassDescriptor ) ;
242332
243333 pass . setPipeline ( memo . pipeline ) ;
244334
335+ const missingBindGroups = new Set ( memo . bindGroupLayouts ) ;
336+
245337 memo . bindGroupLayouts . forEach ( ( layout , idx ) => {
246338 if ( memo . catchall && idx === memo . catchall [ 0 ] ) {
247339 // Catch-all
248340 pass . setBindGroup ( idx , branch . unwrap ( memo . catchall [ 1 ] ) ) ;
341+ missingBindGroups . delete ( layout ) ;
249342 } else {
250343 const bindGroup = this . _priors . bindGroupLayoutMap ?. get ( layout ) ;
251- if ( bindGroup === undefined ) {
252- throw new MissingBindGroupError ( layout . label ) ;
344+ if ( bindGroup !== undefined ) {
345+ missingBindGroups . delete ( layout ) ;
346+ pass . setBindGroup ( idx , branch . unwrap ( bindGroup ) ) ;
253347 }
254- pass . setBindGroup ( idx , branch . unwrap ( bindGroup ) ) ;
255348 }
256349 } ) ;
257350
258- this . _core . usedVertexLayouts . forEach ( ( vertexLayout , idx ) => {
351+ const missingVertexLayouts = new Set ( this . _core . usedVertexLayouts ) ;
352+
353+ const usedVertexLayouts = this . _core . usedVertexLayouts ;
354+ usedVertexLayouts . forEach ( ( vertexLayout , idx ) => {
259355 const buffer = this . _priors . vertexLayoutMap ?. get ( vertexLayout ) ;
260- if ( ! buffer ) {
261- throw new Error (
262- `Missing vertex buffer for layout '${ vertexLayout . label ?? '<unnamed>' } '. Please provide it using pipeline.with(layout, buffer).(...)` ,
263- ) ;
356+ if ( buffer ) {
357+ missingVertexLayouts . delete ( vertexLayout ) ;
358+ pass . setVertexBuffer ( idx , branch . unwrap ( buffer ) ) ;
264359 }
265- pass . setVertexBuffer ( idx , branch . unwrap ( buffer ) ) ;
266360 } ) ;
267361
362+ if ( missingBindGroups . size > 0 ) {
363+ throw new MissingBindGroupsError ( missingBindGroups ) ;
364+ }
365+
366+ if ( missingVertexLayouts . size > 0 ) {
367+ throw new MissingVertexBuffersError ( missingVertexLayouts ) ;
368+ }
369+
268370 pass . draw ( vertexCount , instanceCount , firstVertex , firstInstance ) ;
371+
269372 pass . end ( ) ;
270373 }
271374}
@@ -295,8 +398,14 @@ class RenderPipelineCore {
295398
296399 public unwrap ( ) : Memo {
297400 if ( this . _memo === undefined ) {
298- const { branch, vertexFn, fragmentFn, slotBindings, primitiveState } =
299- this . options ;
401+ const {
402+ branch,
403+ vertexFn,
404+ fragmentFn,
405+ slotBindings,
406+ primitiveState,
407+ depthStencilState,
408+ } = this . options ;
300409
301410 // Resolving code
302411 const { code, bindGroupLayouts, catchall } = resolve (
@@ -353,6 +462,10 @@ class RenderPipelineCore {
353462 descriptor . primitive = primitiveState ;
354463 }
355464
465+ if ( depthStencilState ) {
466+ descriptor . depthStencil = depthStencilState ;
467+ }
468+
356469 this . _memo = {
357470 pipeline : device . createRenderPipeline ( descriptor ) ,
358471 bindGroupLayouts,
0 commit comments