1 module sbylib.graphics.material.standard.material;
2 
3 public import std;
4 public import sbylib.math;
5 public import sbylib.wrapper.vulkan;
6 public import sbylib.graphics.wrapper.texture;
7 
8 import sbylib.wrapper.glfw;
9 import erupted;
10 import sbylib.graphics.core.descriptor;
11 import sbylib.graphics.core.shader;
12 import sbylib.graphics.util.own;
13 
14 mixin template UseMaterial(MaterialType) {
15     import sbylib.event;
16 
17     alias DataType = MaterialType.DataSet;
18     DataType _data;
19     alias _data this;
20     private void delegate() delegate() _reregister;
21     private void delegate() _unregister;
22 
23     this(Geometry)(Window window, Geometry geom) {
24         constructor(window, geom);
25     }
26 
27     void constructor(Geometry)(Window window, Geometry geom) {
28         _data = new MaterialType.DataSet(geom);
29         auto register = (CommandBuffer commandBuffer) {
30             _data.record(geom, MaterialType.getInstance(window, geom.primitive), commandBuffer);
31         };
32         this._reregister = () => MaterialType.RenderPassType(window).register(register);
33         when(Frame).once({
34             this._unregister = _reregister();
35             _data.pushReleaseCallback(_unregister);
36         });
37     }
38 
39     ~this() {
40         this._data.destroy();
41     }
42 
43 
44     void unregister() {
45         _unregister();
46     }
47 
48     void reregister() {
49         _unregister = _reregister();
50     }
51 }
52 
53 class Material {
54 
55     mixin Descriptor;
56 
57     protected @own {
58         PipelineLayout pipelineLayout;
59         Pipeline pipeline;
60     }
61 
62     mixin ImplReleaseOwn;
63 
64     struct CreateInfo {
65         Pipeline.RasterizationStateCreateInfo rasterization;
66         Pipeline.TessellationStateCreateInfo tessellation;
67         Pipeline.MultisampleStateCreateInfo multisample;
68         Pipeline.DepthStencilStateCreateInfo depthStencil;
69         Pipeline.ColorBlendStateCreateInfo colorBlend;
70     }
71 
72     mixin template Info(CreateInfo info_) {
73         private CreateInfo info () {
74             return info_;
75         }
76     }
77 
78     mixin template RenderPass(RenderPassType_) {
79         alias RenderPassType = RenderPassType_;
80     }
81 
82     mixin template Instance() {
83         import sbylib.graphics.core;
84         import sbylib.graphics.util;
85         import sbylib.graphics.wrapper;
86         import sbylib.wrapper.glfw : Window;
87         alias This = typeof(this);
88 
89         mixin ImplDescriptor;
90 
91         private static typeof(this)[Tuple!(Window,PrimitiveTopology)] instance;
92 
93         static typeof(this) getInstance(Window window, PrimitiveTopology prim) {
94             auto key = tuple(window,prim);
95             if (auto r = key in instance) {
96                 return *r;
97             }
98             assert(window, "window is not registered.");
99             auto r = instance[key] = new typeof(this)(window, prim);
100             VDevice().pushReleaseCallback({
101                 r.destroy();
102                 instance.remove(key);
103             });
104             return r;
105         }
106 
107         this(Window window, PrimitiveTopology topology) {
108             initializeDescriptor();
109 
110             PipelineLayout.CreateInfo pipelineLayoutCreateInfo = {
111                 setLayouts: [descriptorSetLayout]
112             };
113             this.pipelineLayout = new PipelineLayout(VDevice(), pipelineLayoutCreateInfo);
114 
115             auto i = info();
116             Pipeline.GraphicsCreateInfo pipelineCreateInfo = {
117                 vertexInputState: vertexInputState!(This),
118                 inputAssemblyState: {
119                     topology: topology
120                 },
121                 viewportState: {
122                     viewports: [{
123                         x: 0.0f,
124                         y: 0.0f,
125                         width: window.width,
126                         height: window.height,
127                         minDepth: 0.0f,
128                         maxDepth: 1.0f
129                     }],
130                     scissors: [{
131                         offset: {
132                             x: 0,
133                             y: 0
134                         },
135                         extent: {
136                             width: window.width,
137                             height: window.height
138                         }
139                     }]
140                 },
141                 rasterizationState: i.rasterization,
142                 tessellationState: i.tessellation,
143                 multisampleState: i.multisample,
144                 depthStencilState: i.depthStencil,
145                 colorBlendState: i.colorBlend,
146                 layout: pipelineLayout,
147                 renderPass: RenderPassType(window),
148                 subpass: 0,
149             };
150 
151             static foreach (f; getSymbolsByUDA!(typeof(this), stages)) {
152                 pipelineCreateInfo.stages ~= f();
153             }
154             assert(pipelineCreateInfo.stages.length > 0);
155             this.pipeline = Pipeline.create(VDevice(), [pipelineCreateInfo])[0];
156         }
157 
158         static class DataSet {
159             mixin ImplResourceStack;
160             mixin ImplReleaseOwn;
161             mixin DefineInstanceMembers!(This);
162 
163             ~this() {
164                 destroyStack();
165             }
166 
167             alias Vertex = getSymbolsByUDA!(This, vertex)[0];
168             alias Index = uint;
169 
170             public @own {
171                 VBuffer!Vertex vertexBuffer;
172                 VBuffer!Index indexBuffer;
173                 DescriptorSet descriptorSet;
174             }
175 
176             this(Geometry)(Geometry geom) {
177                 this.vertexBuffer = new VBuffer!Vertex(geom.vertexList.map!((vIn) {
178                     Vertex v;
179                     static foreach (mem; __traits(allMembers, Vertex)) {
180                         static if (!isCallable!(__traits(getMember, Vertex, mem))) {
181                             __traits(getMember, v, mem) = __traits(getMember, vIn, mem);
182                         }
183                     }
184                     return v;
185                 }).array, BufferUsage.VertexBuffer);
186 
187                 static if (Geometry.hasIndex) {
188                     this.indexBuffer = new VBuffer!Index(geom.indexList, BufferUsage.IndexBuffer);
189                 }
190                 initializeDefinedBuffers();
191             }
192 
193             void initializeDescriptorSet(DescriptorPool descriptorPool, DescriptorSetLayout descriptorSetLayout) {
194                 this.descriptorSet = createDescriptorSet(descriptorPool, descriptorSetLayout);
195             }
196 
197             void record(Geometry)(Geometry geom, Material inst, CommandBuffer commandBuffer) {
198                 if (!descriptorSet) initializeDescriptorSet(inst.descriptorPool, inst.descriptorSetLayout);
199                 commandBuffer.cmdBindPipeline(PipelineBindPoint.Graphics, inst.pipeline);
200                 commandBuffer.cmdBindDescriptorSets(PipelineBindPoint.Graphics, inst.pipelineLayout, 0, [descriptorSet]);
201                 commandBuffer.cmdBindVertexBuffers(0, [vertexBuffer.buffer], [0]);
202 
203                 bool indexBound = false;
204                 static if (Geometry.hasIndex) {
205                     assert(this.indexBuffer);
206                     commandBuffer.cmdBindIndexBuffer(indexBuffer.buffer, 0, indexType);
207                     commandBuffer.cmdDrawIndexed(cast(uint)geom.indexList.length, 1, 0, 0, 0);
208                     indexBound = true;
209                 } else {
210                     commandBuffer.cmdDraw(cast(uint)geom.vertexList.length, 1, 0, 0);
211                 }
212             }
213 
214             static if (is(Index == ubyte)) {
215                 enum indexType = IndexType.Uint8;
216             } else static if (is(Index == ushort)) {
217                 enum indexType = IndexType.Uint16;
218             } else static if (is(Index == uint)) {
219                 enum indexType = IndexType.Uint32;
220             } else {
221                 static assert(false, "Invalid index type: " ~ Index.stringof);
222             }
223         }
224     }
225 
226     protected VkVertexInputAttributeDescription[] createVertexAttributeDescriptions(Vertex)(uint binding) {
227         VkVertexInputAttributeDescription[] result;
228 
229         uint location = 0;
230         static foreach (name; __traits(allMembers, Vertex)) {
231             {
232                 static if (!isCallable!(__traits(getMember, Vertex, name))) {
233                     VkVertexInputAttributeDescription description = {
234                         binding: binding,
235                         location: location++,
236                         format: getFormat!(typeof(__traits(getMember, Vertex, name))),
237                         offset: __traits(getMember, Vertex, name).offsetof
238                     };
239                     result ~= description;
240                 }
241             }
242         }
243 
244         return result;
245     }
246 
247     protected template getFormat(Type) {
248         static if (is(Type == float)) {
249             enum getFormat = VK_FORMAT_R32_SFLOAT;
250         } else static if (is(Type == vec2)) {
251             enum getFormat = VK_FORMAT_R32G32_SFLOAT;
252         } else static if (is(Type == vec3)) {
253             enum getFormat = VK_FORMAT_R32G32B32_SFLOAT;
254         } else static if (is(Type == vec4)) {
255             enum getFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
256         } else static if (is(Type == int)) {
257             enum getFormat = VK_FORMAT_R32_SINT;
258         } else static if (is(Type == ivec2)) {
259             enum getFormat = VK_FORMAT_R32G32_SINT;
260         } else static if (is(Type == ivec3)) {
261             enum getFormat = VK_FORMAT_R32G32B32_SINT;
262         } else static if (is(Type == ivec4)) {
263             enum getFormat = VK_FORMAT_R32G32B32A32_SINT;
264         } else {
265             static assert(false, "Unsupported type: " ~ Type.stringof);
266         }
267     }
268 
269     protected Pipeline.VertexInputStateCreateInfo vertexInputState(This)() {
270         alias Vertex = getSymbolsByUDA!(This, vertex)[0];
271         Pipeline.VertexInputStateCreateInfo result = {
272             vertexBindingDescriptions: [{
273                 stride: Vertex.sizeof,
274                 inputRate: VertexInputRate.Vertex
275             }],
276             vertexAttributeDescriptions: createVertexAttributeDescriptions!(Vertex)(0)
277         };
278         return result;
279     }
280 }