1 module pipeline;
2 
3 import buffer;
4 
5 import gfx.core;
6 import gfx.graal;
7 import gfx.math;
8 
9 class DeferredPipelines : AtomicRefCounted
10 {
11     struct Pl {
12         DescriptorSetLayout[] descriptorLayouts;
13         PipelineLayout layout;
14         Pipeline pipeline;
15 
16         void release()
17         {
18             releaseObj(pipeline);
19             releaseObj(layout);
20             releaseArr(descriptorLayouts);
21         }
22     }
23 
24     Pl[] pls;
25 
26     this(Device device, RenderPass deferredRp, RenderPass bloomRp, RenderPass blendRp)
27     {
28         import std.algorithm : map;
29         import std.array : array;
30 
31         auto infos = [
32             geomInfo(device, deferredRp),
33             lightInfo(device, deferredRp),
34             bloomInfo(device, bloomRp),
35             blendInfo(device, blendRp),
36         ];
37 
38         pls = device.createPipelines(infos)
39             .map!(pl => Pl([], null, retainObj(pl)))
40             .array;
41 
42         foreach(i; 0 .. infos.length)
43         {
44             pls[i].layout = infos[i].layout;
45             pls[i].descriptorLayouts = infos[i].layout.descriptorLayouts;
46             releaseObj(infos[i].shaders.vertex);
47             releaseObj(infos[i].shaders.fragment);
48         }
49     }
50 
51     override void dispose()
52     {
53         foreach(ref pl; pls) {
54             pl.release();
55         }
56         pls = [];
57     }
58 
59     @property Pl geom()
60     {
61         return pls[0];
62     }
63 
64     @property Pl light()
65     {
66         return pls[1];
67     }
68 
69     @property Pl bloom()
70     {
71         return pls[2];
72     }
73 
74     @property Pl blend()
75     {
76         return pls[3];
77     }
78 
79     final PipelineInfo geomInfo(Device device, RenderPass renderPass)
80     {
81         import std.typecons : No, Yes;
82 
83         const shaderSpv = [
84             import("geom.vert.spv"), import("geom.frag.spv"),
85         ];
86 
87         const layoutBindings = [
88             PipelineLayoutBinding(
89                 0, DescriptorType.uniformBuffer, 1, ShaderStage.vertex
90             ),
91             PipelineLayoutBinding(
92                 1, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex
93             ),
94         ];
95 
96         auto descriptorLayouts = [
97             retainObj(device.createDescriptorSetLayout(layoutBindings))
98         ];
99         auto layout = retainObj(device.createPipelineLayout( descriptorLayouts, [] ));
100 
101         PipelineInfo info;
102         info.shaders.vertex = retainObj(device.createShaderModule(
103             cast(immutable(uint)[])shaderSpv[0], "main"
104         ));
105         info.shaders.fragment = retainObj(device.createShaderModule(
106             cast(immutable(uint)[])shaderSpv[1], "main"
107         ));
108         info.inputBindings = [
109             VertexInputBinding(0, P3N3Vertex.sizeof, No.instanced)
110         ];
111         info.inputAttribs = [
112             VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0),
113             VertexInputAttrib(1, 0, Format.rgb32_sFloat, P3N3Vertex.normal.offsetof),
114         ];
115         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
116         info.rasterizer = Rasterizer(
117             PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp,
118             none!DepthBias, 1f
119         );
120         info.depthInfo = DepthInfo(
121             Yes.enabled, Yes.write, CompareOp.less, No.boundsTest, 0f, 1f
122         );
123         info.blendInfo = ColorBlendInfo(
124             none!LogicOp, [
125                 ColorBlendAttachment.solid(),
126                 ColorBlendAttachment.solid(),
127                 ColorBlendAttachment.solid(),
128             ],
129         );
130         info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ];
131         info.layout = layout;
132         info.renderPass = renderPass;
133         info.subpassIndex = 0;
134 
135         return info;
136     }
137 
138     final PipelineInfo lightInfo(Device device, RenderPass renderPass)
139     {
140         import std.typecons : No, Yes;
141 
142         const shaderSpv = [
143             import("light.vert.spv"), import("light.frag.spv"),
144         ];
145 
146         const bufLayoutBindings = [
147             PipelineLayoutBinding(
148                 0, DescriptorType.uniformBuffer, 1, ShaderStage.fragment
149             ),
150             PipelineLayoutBinding(
151                 1, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex | ShaderStage.fragment
152             ),
153         ];
154         const attachLayoutBindings = [
155             PipelineLayoutBinding(
156                 0, DescriptorType.inputAttachment, 3, ShaderStage.fragment
157             ),
158         ];
159 
160         auto descriptorLayouts = [
161             retainObj(device.createDescriptorSetLayout(bufLayoutBindings)),
162             retainObj(device.createDescriptorSetLayout(attachLayoutBindings)),
163         ];
164         auto layout = retainObj(device.createPipelineLayout( descriptorLayouts, [] ));
165 
166         PipelineInfo info;
167         info.shaders.vertex = retainObj(device.createShaderModule(
168             cast(immutable(uint)[])shaderSpv[0], "main"
169         ));
170         info.shaders.fragment = retainObj(device.createShaderModule(
171             cast(immutable(uint)[])shaderSpv[1], "main"
172         ));
173         info.inputBindings = [
174             VertexInputBinding(0, FVec3.sizeof, No.instanced)
175         ];
176         info.inputAttribs = [
177             VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0),
178         ];
179         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
180         // culling so that we run the shader once per fragment
181         // front instead of back culling to also run the shader if the camera is within a sphere
182         info.rasterizer = Rasterizer(
183             PolygonMode.fill, Cull.front, FrontFace.ccw
184         );
185         info.blendInfo = ColorBlendInfo(
186             none!LogicOp, [
187                 ColorBlendAttachment.blend(
188                     BlendState(
189                         trans(BlendFactor.one, BlendFactor.one),
190                         BlendOp.add,
191                     ),
192                 ),
193                 ColorBlendAttachment.blend(
194                     BlendState(
195                         trans(BlendFactor.one, BlendFactor.one),
196                         BlendOp.add,
197                     ),
198                 ),
199             ],
200         );
201         info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ];
202         info.layout = layout;
203         info.renderPass = renderPass;
204         info.subpassIndex = 1;
205 
206         return info;
207     }
208 
209     final PipelineInfo bloomInfo(Device device, RenderPass renderPass)
210     {
211         import std.typecons : No, Yes;
212 
213         const shaderSpv = [
214             import("bloom.vert.spv"), import("bloom.frag.spv"),
215         ];
216 
217         const layoutBindings = [
218             PipelineLayoutBinding(
219                 0, DescriptorType.combinedImageSampler, 1, ShaderStage.fragment
220             ),
221         ];
222 
223         auto descriptorLayouts = [
224             retainObj(device.createDescriptorSetLayout(layoutBindings)),
225         ];
226         // pc holds horizontal-vertical boolean
227         const pushConstants = [
228             PushConstantRange(ShaderStage.fragment, 0, uint.sizeof),
229         ];
230         auto layout = retainObj(device.createPipelineLayout( descriptorLayouts, pushConstants ));
231 
232         PipelineInfo info;
233         info.shaders.vertex = retainObj(device.createShaderModule(
234             cast(immutable(uint)[])shaderSpv[0], "main"
235         ));
236         info.shaders.fragment = retainObj(device.createShaderModule(
237             cast(immutable(uint)[])shaderSpv[1], "main"
238         ));
239         info.inputBindings = [
240             VertexInputBinding(0, P2T2Vertex.sizeof, No.instanced)
241         ];
242         info.inputAttribs = [
243             VertexInputAttrib(0, 0, Format.rg32_sFloat, 0),
244             VertexInputAttrib(1, 0, Format.rg32_sFloat, P2T2Vertex.texCoord.offsetof),
245         ];
246         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
247         info.rasterizer = Rasterizer(
248             PolygonMode.fill, Cull.none, FrontFace.ccw
249         );
250         info.blendInfo = ColorBlendInfo(
251             none!LogicOp, [
252                 ColorBlendAttachment.solid(),
253             ],
254         );
255         info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ];
256         info.layout = layout;
257         info.renderPass = renderPass;
258         info.subpassIndex = 0;
259 
260         return info;
261     }
262 
263     final PipelineInfo blendInfo(Device device, RenderPass renderPass)
264     {
265         import std.typecons : No, Yes;
266 
267         const shaderSpv = [
268             import("blend.vert.spv"), import("blend.frag.spv"),
269         ];
270 
271         const layoutBindings = [
272             PipelineLayoutBinding(
273                 0, DescriptorType.inputAttachment, 2, ShaderStage.fragment
274             ),
275         ];
276 
277         auto descriptorLayouts = [
278             retainObj(device.createDescriptorSetLayout(layoutBindings)),
279         ];
280         auto layout = retainObj(device.createPipelineLayout( descriptorLayouts, [] ));
281 
282         PipelineInfo info;
283         info.shaders.vertex = retainObj(device.createShaderModule(
284             cast(immutable(uint)[])shaderSpv[0], "main"
285         ));
286         info.shaders.fragment = retainObj(device.createShaderModule(
287             cast(immutable(uint)[])shaderSpv[1], "main"
288         ));
289         info.inputBindings = [
290             VertexInputBinding(0, P2T2Vertex.sizeof, No.instanced)
291         ];
292         info.inputAttribs = [
293             VertexInputAttrib(0, 0, Format.rg32_sFloat, 0),
294         ];
295         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
296         info.rasterizer = Rasterizer(
297             PolygonMode.fill, Cull.none, FrontFace.ccw
298         );
299         info.blendInfo = ColorBlendInfo(
300             none!LogicOp, [
301                 ColorBlendAttachment.solid(),
302             ],
303         );
304         info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ];
305         info.layout = layout;
306         info.renderPass = renderPass;
307         info.subpassIndex = 0;
308 
309         return info;
310     }
311 }