1 module gfx.graal.pipeline;
2 
3 import gfx.core.rc;
4 import gfx.core.typecons;
5 import gfx.graal.buffer;
6 import gfx.graal.format;
7 import gfx.graal.image;
8 import gfx.graal.renderpass;
9 import gfx.graal.types;
10 
11 import std.typecons : Flag;
12 
13 interface ShaderModule : IAtomicRefCounted
14 {
15     import gfx.graal.device : Device;
16 
17     /// Get the parent device
18     @property Device device();
19 
20     @property string entryPoint();
21 }
22 
23 interface PipelineLayout : IAtomicRefCounted
24 {
25     import gfx.graal.device : Device;
26 
27     /// Get the parent device
28     @property Device device();
29 
30     /// Get the descriptor set layouts used to create this pipeline layout
31     @property DescriptorSetLayout[] descriptorLayouts();
32 
33     /// Get the push constant ranges in this layout
34     @property const(PushConstantRange)[] pushConstantRanges();
35 }
36 
37 interface Pipeline : IAtomicRefCounted
38 {
39     import gfx.graal.device : Device;
40 
41     /// Get the parent device
42     @property Device device();
43 }
44 
45 interface DescriptorSetLayout : IAtomicRefCounted
46 {
47     import gfx.graal.device : Device;
48 
49     /// Get the parent device
50     @property Device device();
51 }
52 
53 interface DescriptorPool : IAtomicRefCounted
54 {
55     import gfx.graal.device : Device;
56 
57     /// Get the parent device
58     @property Device device();
59 
60     /// Allocate a descriptor set per descriptor layout passed as argument
61     DescriptorSet[] allocate(DescriptorSetLayout[] layouts);
62 
63     /// Reset this pool.
64     /// All descriptors allocated with this pool are invalid after this call
65     void reset();
66 }
67 
68 interface DescriptorSet
69 {
70     @property DescriptorPool pool();
71 }
72 
73 struct PipelineInfo {
74     GraphicsShaderSet shaders;
75     VertexInputBinding[] inputBindings;
76     VertexInputAttrib[] inputAttribs;
77     InputAssembly assembly;
78     Rasterizer rasterizer;
79     /// Viewport configuration of the pipeline.
80     /// For dynamic viewport, leave this array empty
81     ViewportConfig[] viewports;
82 
83     // TODO: tesselation, multisample
84 
85     DepthInfo depthInfo;
86     StencilInfo stencilInfo;
87     ColorBlendInfo blendInfo;
88 
89     DynamicState[] dynamicStates;
90 
91     PipelineLayout layout;
92 
93     RenderPass renderPass;
94     uint subpassIndex;
95 }
96 
97 enum ShaderStage {
98     vertex                  = 0x01,
99     tessellationControl     = 0x02,
100     tessellationEvaluation  = 0x04,
101     geometry                = 0x08,
102     fragment                = 0x10,
103     compute                 = 0x20,
104 
105     allGraphics             = 0x1f,
106     all                     = allGraphics | compute,
107 }
108 
109 struct GraphicsShaderSet {
110     ShaderModule vertex;
111     ShaderModule tessControl;
112     ShaderModule tessEval;
113     ShaderModule geometry;
114     ShaderModule fragment;
115 }
116 
117 
118 /// Describes the binding of a buffer to the pipeline
119 struct VertexInputBinding {
120     uint binding;
121     size_t stride;
122     Flag!"instanced" instanced;
123 }
124 
125 /// Describes a vertex attribute
126 struct VertexInputAttrib {
127     uint location;
128     uint binding;
129     Format format;
130     size_t offset;
131 }
132 
133 
134 enum Primitive {
135     pointList, lineList, lineStrip,
136     triangleList, triangleStrip, triangleFan,
137     lineListAdjacency, lineStripAdjacency,
138     triangleListAdjacency, triangleStripAdjacency,
139     patchList,
140 }
141 
142 struct InputAssembly {
143     Primitive primitive;
144     Flag!"primitiveRestart" primitiveRestart;
145 }
146 
147 enum FrontFace {
148     ccw, cw,
149 }
150 
151 enum Cull {
152     none            = 0x00,
153     front           = 0x01,
154     back            = 0x02,
155     frontAndBack    = front | back,
156 }
157 
158 enum PolygonMode {
159     fill, line, point,
160 }
161 
162 /// Defines how a polygon can be offset (mainly to avoid shadow artifacts).
163 /// Given m as maximum depth slope of the triangle and r as implementation defined
164 /// minimal resolvable difference, the offset of the triangle is defined as follow:
165 /// o = m * slopeFactor + r * constantFactor
166 /// If clamp == 0f, o is used directly as effective offset.
167 /// If clamp > 0f, the effective offset is min(o, clamp)
168 /// If clamp < 0f, the effective offset is max(o, clamp)
169 struct DepthBias
170 {
171     /// Factor multiplied by the minimal resolvable difference of the depth buffer
172     float constantFactor;
173     /// Clamps the effective offset to a particular value
174     float clamp;
175     /// Factor multiplied by the maximum depth slope of the polygon.
176     float slopeFactor;
177 }
178 
179 struct Rasterizer {
180     PolygonMode mode;
181     Cull cull;
182     FrontFace front;
183     Flag!"depthClamp" depthClamp;
184     Option!DepthBias depthBias;
185     float lineWidth=1f;
186 }
187 
188 struct ViewportConfig {
189     Viewport viewport;
190     Rect scissors;
191 }
192 
193 enum CompareOp
194 {
195     never, less, equal, lessOrEqual, greater, notEqual, greaterOrEqual, always,
196 }
197 
198 enum StencilOp
199 {
200     keep,
201     zero,
202     replace,
203     incrementAndClamp,
204     decrementAndClamp,
205     invert,
206     incrementAndWrap,
207     decrementAndWrap,
208 }
209 
210 struct DepthInfo
211 {
212     Flag!"enabled" enabled;
213     Flag!"write" write;
214     CompareOp compareOp;
215     Flag!"boundsTest" boundsTest;
216     float minBounds;
217     float maxBounds;
218 
219     @property static DepthInfo none() {
220         return DepthInfo.init;
221     }
222 }
223 
224 struct StencilOpState
225 {
226     StencilOp failOp;
227     StencilOp passOp;
228     StencilOp depthFailOp;
229     CompareOp compareOp;
230     uint compareMask;
231     uint writeMask;
232     uint refMask;
233 }
234 
235 struct StencilInfo
236 {
237     Flag!"enabled" enabled;
238     StencilOpState front;
239     StencilOpState back;
240 
241     @property static StencilInfo none() {
242         return StencilInfo.init;
243     }
244 }
245 
246 
247 enum BlendFactor
248 {
249     zero = 0,
250     one = 1,
251     srcColor = 2,
252     oneMinusSrcColor = 3,
253     dstColor = 4,
254     oneMinusDstColor = 5,
255     srcAlpha = 6,
256     oneMinusSrcAlpha = 7,
257     dstAlpha = 8,
258     oneMinusDstAlpha = 9,
259     constantColor = 10,
260     oneMinusConstantColor = 11,
261     constantAlpha = 12,
262     oneMinusConstantAlpha = 13,
263     srcAlphaSaturate = 14,
264     src1Color = 15,
265     oneMinusSrc1Color = 16,
266     src1Alpha = 17,
267     oneMinusSrc1Alpha = 18,
268 }
269 
270 enum BlendOp {
271     add,
272     subtract,
273     reverseSubtract,
274     min,
275     max,
276 }
277 
278 struct BlendState {
279     Trans!BlendFactor factor;
280     BlendOp op;
281 }
282 
283 enum ColorMask {
284     r = 0x01,
285     g = 0x02,
286     b = 0x04,
287     a = 0x08,
288 
289     none    = 0x00,
290     all     = r | g | b | a,
291     rg      = r | g,
292     rb      = r | b,
293     ra      = r | a,
294     gb      = g | b,
295     ga      = g | a,
296     ba      = b | a,
297     rgb     = r | g | b,
298     rga     = r | g | a,
299     rba     = r | b | a,
300     gba     = g | b | a,
301     rgba    = r | g | b | a,
302 }
303 
304 struct ColorBlendAttachment {
305     Flag!"enabled" enabled;
306     BlendState colorBlend;
307     BlendState alphaBlend;
308     ColorMask colorMask;
309 
310     static ColorBlendAttachment solid(in ColorMask mask=ColorMask.all) {
311         import std.typecons : No;
312         return ColorBlendAttachment(
313             No.enabled, BlendState.init, BlendState.init, mask
314         );
315     }
316 
317     static ColorBlendAttachment blend(in BlendState blendState,
318                                       in ColorMask mask=ColorMask.all) {
319         import std.typecons : Yes;
320         return ColorBlendAttachment(
321             Yes.enabled, blendState, blendState, mask
322         );
323     }
324 
325     static ColorBlendAttachment blend(in BlendState color, in BlendState alpha,
326                                       in ColorMask mask=ColorMask.all) {
327         import std.typecons : Yes;
328         return ColorBlendAttachment(
329             Yes.enabled, color, alpha, mask
330         );
331     }
332 }
333 
334 enum LogicOp {
335     clear,
336     and,
337     andReverse,
338     copy,
339     andInverted,
340     noop,
341     xor,
342     or,
343     nor,
344     equivalent,
345     invert,
346     orReverse,
347     copyInverted,
348     orInverted,
349     nand,
350     set,
351 }
352 
353 struct ColorBlendInfo
354 {
355     Option!LogicOp logicOp;
356     ColorBlendAttachment[] attachments;
357     float[4] blendConstants = [ 0f, 0f, 0f, 0f ];
358 }
359 
360 enum DynamicState {
361     viewport,
362     scissor,
363     lineWidth,
364     depthBias,
365     blendConstants,
366     depthBounds,
367     stencilCmpMask,
368     stencilWriteMask,
369     stencilRef,
370 }
371 
372 enum DescriptorType {
373     sampler,
374     combinedImageSampler,
375     sampledImage,
376     storageImage,
377     uniformTexelBuffer,
378     storageTexelBuffer,
379     uniformBuffer,
380     storageBuffer,
381     uniformBufferDynamic,
382     storageBufferDynamic,
383     inputAttachment,
384 }
385 
386 struct PipelineLayoutBinding {
387     uint binding;
388     DescriptorType descriptorType;
389     uint descriptorCount;
390     ShaderStage stages;
391 }
392 
393 struct PushConstantRange {
394     ShaderStage stages;
395     uint offset;
396     uint size;
397 }
398 
399 struct DescriptorPoolSize {
400     DescriptorType type;
401     uint count;
402 }
403 
404 struct WriteDescriptorSet {
405     DescriptorSet dstSet;
406     uint dstBinding;
407     uint dstArrayElem;
408     DescriptorWrite write;
409 }
410 
411 struct CopyDescritporSet {
412     Trans!DescriptorSet set;
413     Trans!uint          binding;
414     Trans!uint          arrayElem;
415 }
416 
417 /// Descriptor for a buffer
418 /// to be used to update the following descriptor types:
419 ///   - DescriptorType.uniformBuffer
420 ///   - DescriptorType.uniformBufferDynamic
421 ///   - DescriptorType.storageBuffer
422 ///   - DescriptorType.storageBufferDynamic
423 struct BufferDescriptor
424 {
425     /// descriptor resource
426     Buffer buffer;
427     /// bytes offset from the beginning of the resource
428     size_t offset;
429     /// amount of bytes to use from the resource
430     size_t size;
431 }
432 
433 /// Descriptor for a texel buffer
434 /// to be used to update the following descriptor types:
435 ///   - DescriptorType.uniformTexelBuffer
436 ///   - DescriptorType.storageTexelBuffer
437 struct TexelBufferDescriptor
438 {
439     /// descriptor resource
440     TexelBufferView bufferView;
441 }
442 
443 /// Descriptor for images
444 /// to be used to update the following descriptor types:
445 ///   - DescriptorType.sampledImage
446 ///   - DescriptorType.storageImage
447 ///   - DescriptorType.inputAttachment
448 struct ImageDescriptor {
449     /// view to descriptor resource
450     ImageView view;
451     /// layout of the image resource
452     ImageLayout layout;
453 }
454 
455 /// Descriptor that combines sampler and image view
456 /// to be used to update the following descriptor types:
457 ///   - DescriptorType.combinedImageSampler
458 struct ImageSamplerDescriptor
459 {
460     /// view to image resource
461     ImageView view;
462     /// layout of the image resource
463     ImageLayout layout;
464     /// sampler resource combined with this image
465     Sampler sampler;
466 }
467 
468 /// Descriptor for a sampler
469 /// to be used to update the following descriptor types:
470 ///   - DescriptorType.sampler
471 struct SamplerDescriptor
472 {
473     /// descriptor resource
474     Sampler sampler;
475 }
476 
477 /// Gathering of descriptor or descriptors array to be written to a device
478 struct DescriptorWrite
479 {
480     private this(DescriptorType type, size_t count, Write write) {
481         _type = type;
482         _count = count;
483         _write = write;
484     }
485     private this(DescriptorType type, size_t count, Writes writes) {
486         _type = type;
487         _count = count;
488         _writes = writes;
489     }
490 
491     /// the type of the descriptor to be written
492     @property DescriptorType type() const {
493         return _type;
494     }
495 
496     /// how many descriptors are to be written
497     @property size_t count() const {
498         return _count;
499     }
500 
501     /// make a Descriptor write for a single BufferDescriptor
502     static DescriptorWrite make(in DescriptorType type, BufferDescriptor buffer)
503     in(type == DescriptorType.uniformBuffer
504         || type == DescriptorType.storageBuffer
505         || type == DescriptorType.uniformBufferDynamic
506         || type == DescriptorType.storageBufferDynamic)
507     {
508         Write write;
509         write.buffer = buffer;
510         return DescriptorWrite(type, 1, write);
511     }
512 
513     /// make a Descriptor write for a BufferDescriptor array
514     static DescriptorWrite make(in DescriptorType type, BufferDescriptor[] buffers)
515     in(type == DescriptorType.uniformBuffer
516         || type == DescriptorType.storageBuffer
517         || type == DescriptorType.uniformBufferDynamic
518         || type == DescriptorType.storageBufferDynamic)
519     in(buffers.length > 1)
520     {
521         Writes writes;
522         writes.buffers = buffers;
523         return DescriptorWrite(type, buffers.length, writes);
524     }
525 
526     /// make a Descriptor write for a single TexelBufferDescriptor
527     static DescriptorWrite make(in DescriptorType type, TexelBufferDescriptor texelBuffer)
528     in(type == DescriptorType.uniformTexelBuffer
529         || type == DescriptorType.storageTexelBuffer)
530     {
531         Write write;
532         write.texelBuffer = texelBuffer;
533         return DescriptorWrite(type, 1, write);
534     }
535 
536     /// make a Descriptor write for a TexelBufferDescriptor array
537     static DescriptorWrite make(in DescriptorType type, TexelBufferDescriptor[] texelBuffers)
538     in(type == DescriptorType.uniformTexelBuffer
539         || type == DescriptorType.storageTexelBuffer)
540     in(texelBuffers.length > 1)
541     {
542         Writes writes;
543         writes.texelBuffers = texelBuffers;
544         return DescriptorWrite(type, texelBuffers.length, writes);
545     }
546 
547     /// make a Descriptor write for a single ImageDescriptor
548     static DescriptorWrite make(in DescriptorType type, ImageDescriptor image)
549     in(type == DescriptorType.sampledImage
550         || type == DescriptorType.storageImage
551         || type == DescriptorType.inputAttachment)
552     {
553         Write write;
554         write.image = image;
555         return DescriptorWrite(type, 1, write);
556     }
557 
558     /// make a Descriptor write for a ImageDescriptor array
559     static DescriptorWrite make(in DescriptorType type, ImageDescriptor[] images)
560     in(type == DescriptorType.sampledImage
561         || type == DescriptorType.storageImage
562         || type == DescriptorType.inputAttachment)
563     in(images.length > 1)
564     {
565         Writes writes;
566         writes.images = images;
567         return DescriptorWrite(type, images.length, writes);
568     }
569 
570     /// make a Descriptor write for a single ImageSamplerDescriptor
571     static DescriptorWrite make(in DescriptorType type, ImageSamplerDescriptor imageSampler)
572     in(type == DescriptorType.combinedImageSampler)
573     {
574         Write write;
575         write.imageSampler = imageSampler;
576         return DescriptorWrite(type, 1, write);
577     }
578 
579     /// make a Descriptor write for a ImageSamplerDescriptor array
580     static DescriptorWrite make(in DescriptorType type, ImageSamplerDescriptor[] imageSamplers)
581     in(type == DescriptorType.combinedImageSampler)
582     in(imageSamplers.length > 1)
583     {
584         Writes writes;
585         writes.imageSamplers = imageSamplers;
586         return DescriptorWrite(type, imageSamplers.length, writes);
587     }
588 
589     /// make a Descriptor write for a single SamplerDescriptor
590     static DescriptorWrite make(in DescriptorType type, SamplerDescriptor sampler)
591     in(type == DescriptorType.sampler)
592     {
593         Write write;
594         write.sampler = sampler;
595         return DescriptorWrite(type, 1, write);
596     }
597 
598     /// make a Descriptor write for a SamplerDescriptor array
599     static DescriptorWrite make(in DescriptorType type, SamplerDescriptor[] samplers)
600     in(type == DescriptorType.sampler)
601     in(samplers.length > 1)
602     {
603         Writes writes;
604         writes.samplers = samplers;
605         return DescriptorWrite(type, samplers.length, writes);
606     }
607 
608     /// access BufferDescriptor array
609     @property BufferDescriptor[] buffers() return
610     in(type == DescriptorType.uniformBuffer
611         || type == DescriptorType.storageBuffer
612         || type == DescriptorType.uniformBufferDynamic
613         || type == DescriptorType.storageBufferDynamic)
614     {
615         if (_count == 1) {
616             return (&_write.buffer)[0 .. 1];
617         }
618         else {
619             return _writes.buffers;
620         }
621     }
622 
623     /// access TexelBufferDescriptor array
624     @property TexelBufferDescriptor[] texelBuffers() return
625     in(type == DescriptorType.uniformTexelBuffer
626         || type == DescriptorType.storageTexelBuffer)
627     {
628         if (_count == 1) {
629             return (&_write.texelBuffer)[0 .. 1];
630         }
631         else {
632             return _writes.texelBuffers;
633         }
634     }
635 
636     /// access ImageDescriptor array
637     @property ImageDescriptor[] images() return
638     in(type == DescriptorType.sampledImage
639         || type == DescriptorType.storageImage
640         || type == DescriptorType.inputAttachment)
641     {
642         if (_count == 1) {
643             return (&_write.image)[0 .. 1];
644         }
645         else {
646             return _writes.images;
647         }
648     }
649 
650     /// access ImageSamplerDescriptor array
651     @property ImageSamplerDescriptor[] imageSamplers() return
652     in(type == DescriptorType.combinedImageSampler)
653     {
654         if (_count == 1) {
655             return (&_write.imageSampler)[0 .. 1];
656         }
657         else {
658             return _writes.imageSamplers;
659         }
660     }
661 
662     /// access SamplerDescriptor array
663     @property SamplerDescriptor[] samplers() return
664     in(type == DescriptorType.sampler)
665     {
666         if (_count == 1) {
667             return (&_write.sampler)[0 .. 1];
668         }
669         else {
670             return _writes.samplers;
671         }
672     }
673 
674     // union used if count > 1 (heap allocation)
675     private union Writes
676     {
677         BufferDescriptor[] buffers;
678         TexelBufferDescriptor[] texelBuffers;
679         ImageDescriptor[] images;
680         ImageSamplerDescriptor[] imageSamplers;
681         SamplerDescriptor[] samplers;
682     }
683 
684     // avoid heap allocation when a single descriptor is specified
685     // (probably over 90% of use cases)
686     private union Write
687     {
688         BufferDescriptor buffer;
689         TexelBufferDescriptor texelBuffer;
690         ImageDescriptor image;
691         ImageSamplerDescriptor imageSampler;
692         SamplerDescriptor sampler;
693     }
694 
695     private DescriptorType _type;
696     private size_t _count;
697     private Writes _writes;
698     private Write _write;
699 }