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 }