1 module texture; 2 3 import example; 4 5 import gfx.core.rc; 6 import gfx.core.typecons; 7 import gfx.graal.buffer; 8 import gfx.graal.cmd; 9 import gfx.graal.device; 10 import gfx.graal.format; 11 import gfx.graal.image; 12 import gfx.graal.memory; 13 import gfx.graal.pipeline; 14 import gfx.graal.presentation; 15 import gfx.graal.queue; 16 import gfx.graal.renderpass; 17 import gfx.graal.types; 18 19 import gl3n.linalg : mat4, mat3, vec3, vec4; 20 21 import std.exception; 22 import std.stdio; 23 import std.typecons; 24 import std.math; 25 26 class TextureExample : Example 27 { 28 Rc!RenderPass renderPass; 29 Framebuffer[] framebuffers; 30 Rc!Pipeline pipeline; 31 Rc!PipelineLayout layout; 32 PerImage[] perImages; 33 ushort[] indices; 34 Rc!Buffer vertBuf; 35 Rc!Buffer indBuf; 36 Rc!Buffer matBuf; 37 Rc!Buffer ligBuf; 38 Rc!Image texImg; 39 Rc!ImageView texView; 40 Rc!Sampler texSampler; 41 Rc!DescriptorPool descPool; 42 Rc!DescriptorSetLayout setLayout; 43 DescriptorSet set; 44 45 struct PerImage { 46 bool undefinedLayout=true; 47 } 48 49 struct Vertex { 50 float[3] position; 51 float[3] normal; 52 float[2] tex; 53 } 54 55 struct Matrices { 56 float[4][4] mvp; 57 float[4][4] normal; 58 } 59 60 enum maxLights = 5; 61 62 struct Light { 63 float[4] direction; 64 float[4] color; 65 } 66 67 struct Lights { 68 Light[maxLights] lights; 69 uint num; 70 } 71 72 this(string[] args) { 73 super("Texture", args); 74 } 75 76 override void dispose() { 77 if (device) { 78 device.waitIdle(); 79 } 80 vertBuf.unload(); 81 indBuf.unload(); 82 matBuf.unload(); 83 ligBuf.unload(); 84 texImg.unload(); 85 texView.unload(); 86 texSampler.unload(); 87 setLayout.unload(); 88 descPool.unload(); 89 layout.unload(); 90 pipeline.unload(); 91 renderPass.unload(); 92 releaseArr(framebuffers); 93 super.dispose(); 94 } 95 96 override void prepare() { 97 super.prepare(); 98 prepareBuffers(); 99 prepareTexture(); 100 prepareRenderPass(); 101 preparePipeline(); 102 prepareDescriptorSet(); 103 } 104 105 void prepareBuffers() { 106 107 import gfx.genmesh.cube : genCube; 108 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices; 109 import gfx.genmesh.poly : quad; 110 import std.algorithm : map; 111 112 auto crate = genCube() 113 .map!(f => quad( 114 Vertex( f[0].p, f[0].n, [ 0f, 0f ] ), 115 Vertex( f[1].p, f[1].n, [ 0f, 1f ] ), 116 Vertex( f[2].p, f[2].n, [ 1f, 1f ] ), 117 Vertex( f[3].p, f[3].n, [ 1f, 0f ] ), 118 )) 119 .triangulate() 120 .vertices() 121 .indexCollectMesh(); 122 123 auto normalize(in float[4] vec) { 124 return vec4(vec).normalized().vector; 125 } 126 const lights = Lights( [ 127 Light(normalize([1.0, 1.0, -1.0, 0.0]), [0.8, 0.5, 0.2, 1.0]), 128 Light(normalize([-1.0, 1.0, -1.0, 0.0]), [0.2, 0.5, 0.8, 1.0]), 129 Light.init, Light.init, Light.init 130 ], 2); 131 132 indices = crate.indices; 133 vertBuf = createStaticBuffer(crate.vertices, BufferUsage.vertex); 134 indBuf = createStaticBuffer(crate.indices, BufferUsage.index); 135 136 matBuf = createDynamicBuffer(Matrices.sizeof, BufferUsage.uniform); 137 ligBuf = createStaticBuffer(lights, BufferUsage.uniform); 138 } 139 140 void prepareTexture() { 141 import img : ImageFormat, ImgImage = Image; 142 auto img = ImgImage.loadFromView!("crate.jpg")(ImageFormat.argb); 143 texImg = createTextureImage( 144 cast(const(void)[])img.data, ImageInfo.d2(img.width, img.height).withFormat(Format.rgba8_uNorm) 145 ); 146 // argb swizzling 147 version(LittleEndian) { 148 const swizzle = Swizzle.bgra; 149 } 150 else { 151 const swizzle = Swizzle.argb; 152 } 153 texView = texImg.createView(ImageType.d2, ImageSubresourceRange(ImageAspect.color), swizzle); 154 155 import gfx.core.typecons : some; 156 157 texSampler = device.createSampler(SamplerInfo( 158 Filter.linear, Filter.linear, Filter.nearest, 159 [WrapMode.repeat, WrapMode.repeat, WrapMode.repeat], 160 some(16f), 0f, [0f, 0f] 161 )); 162 } 163 164 void prepareRenderPass() { 165 const attachments = [ 166 AttachmentDescription(swapchain.format, 1, 167 AttachmentOps(LoadOp.clear, StoreOp.store), 168 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 169 trans(ImageLayout.presentSrc, ImageLayout.presentSrc), 170 No.mayAlias 171 ) 172 ]; 173 const subpasses = [ 174 SubpassDescription( 175 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ], 176 none!AttachmentRef, [] 177 ) 178 ]; 179 180 renderPass = device.createRenderPass(attachments, subpasses, []); 181 182 framebuffers = new Framebuffer[scImages.length]; 183 foreach (i; 0 .. scImages.length) { 184 framebuffers[i] = device.createFramebuffer(renderPass, [ 185 scImages[i].createView( 186 ImageType.d2, 187 ImageSubresourceRange(ImageAspect.color), 188 Swizzle.identity 189 ) 190 ], surfaceSize[0], surfaceSize[1], 1); 191 } 192 retainArr(framebuffers); 193 } 194 195 void preparePipeline() 196 { 197 auto vtxShader = device.createShaderModule( 198 cast(immutable(uint)[])import("shader.vert.spv"), "main" 199 ).rc; 200 auto fragShader = device.createShaderModule( 201 cast(immutable(uint)[])import("shader.frag.spv"), "main" 202 ).rc; 203 204 const layoutBindings = [ 205 PipelineLayoutBinding(0, DescriptorType.uniformBuffer, 1, ShaderStage.vertex), 206 PipelineLayoutBinding(1, DescriptorType.uniformBuffer, 1, ShaderStage.fragment), 207 PipelineLayoutBinding(2, DescriptorType.combinedImageSampler, 1, ShaderStage.fragment), 208 ]; 209 210 setLayout = device.createDescriptorSetLayout(layoutBindings); 211 layout = device.createPipelineLayout([setLayout], []); 212 213 PipelineInfo info; 214 info.shaders.vertex = vtxShader; 215 info.shaders.fragment = fragShader; 216 info.inputBindings = [ 217 VertexInputBinding(0, Vertex.sizeof, No.instanced) 218 ]; 219 info.inputAttribs = [ 220 VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0), 221 VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof), 222 VertexInputAttrib(2, 0, Format.rg32_sFloat, Vertex.tex.offsetof), 223 ]; 224 info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 225 info.rasterizer = Rasterizer( 226 PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp, 227 none!DepthBias, 1f 228 ); 229 info.viewports = [ 230 ViewportConfig( 231 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]), 232 Rect(0, 0, surfaceSize[0], surfaceSize[1]) 233 ) 234 ]; 235 info.blendInfo = ColorBlendInfo( 236 none!LogicOp, [ 237 ColorBlendAttachment(No.enabled, 238 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add), 239 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add), 240 ColorMask.all 241 ) 242 ], 243 [ 0f, 0f, 0f, 0f ] 244 ); 245 info.layout = layout; 246 info.renderPass = renderPass; 247 info.subpassIndex = 0; 248 249 auto pls = device.createPipelines( [info] ); 250 pipeline = pls[0]; 251 } 252 253 void prepareDescriptorSet() { 254 const poolSizes = [ 255 DescriptorPoolSize(DescriptorType.uniformBuffer, 2), 256 DescriptorPoolSize(DescriptorType.combinedImageSampler, 1) 257 ]; 258 descPool = device.createDescriptorPool(1, poolSizes); 259 set = descPool.allocate([ setLayout ])[0]; 260 261 auto writes = [ 262 WriteDescriptorSet(set, 0, 0, new UniformBufferDescWrites([ 263 BufferRange(matBuf, 0, Matrices.sizeof) 264 ])), 265 WriteDescriptorSet(set, 1, 0, new UniformBufferDescWrites([ 266 BufferRange(ligBuf, 0, Lights.sizeof) 267 ])), 268 WriteDescriptorSet(set, 2, 0, new CombinedImageSamplerDescWrites([ 269 CombinedImageSampler(texSampler, texView, ImageLayout.shaderReadOnlyOptimal) 270 ])) 271 ]; 272 device.updateDescriptorSets(writes, []); 273 } 274 275 void updateMatrices(in Matrices mat) { 276 auto mm = matBuf.boundMemory.map(); 277 auto v = mm.view!(Matrices[])(0, 1); 278 v[0] = mat; 279 MappedMemorySet mms; 280 mm.addToSet(mms); 281 device.flushMappedMemory(mms); 282 } 283 284 override void recordCmds(size_t cmdBufInd, size_t imgInd) { 285 import gfx.core.typecons : trans; 286 287 if (!perImages.length) { 288 perImages = new PerImage[scImages.length]; 289 } 290 291 const cv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f); 292 auto subrange = ImageSubresourceRange(ImageAspect.color, 0, 1, 0, 1); 293 294 auto buf = cmdBufs[cmdBufInd]; 295 296 //buf.reset(); 297 buf.begin(No.persistent); 298 299 if (perImages[imgInd].undefinedLayout) { 300 buf.pipelineBarrier( 301 trans(PipelineStage.colorAttachmentOutput, PipelineStage.colorAttachmentOutput), [], 302 [ ImageMemoryBarrier( 303 trans(Access.none, Access.colorAttachmentWrite), 304 trans(ImageLayout.undefined, ImageLayout.presentSrc), 305 trans(graphicsQueueIndex, graphicsQueueIndex), 306 scImages[imgInd], subrange 307 ) ] 308 ); 309 perImages[imgInd].undefinedLayout = false; 310 } 311 312 buf.beginRenderPass( 313 renderPass, framebuffers[imgInd], 314 Rect(0, 0, surfaceSize[0], surfaceSize[1]), [ ClearValues(cv) ] 315 ); 316 317 buf.bindPipeline(pipeline); 318 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, 0) ]); 319 buf.bindIndexBuffer(indBuf, 0, IndexType.u16); 320 buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [set], []); 321 buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0); 322 323 buf.endRenderPass(); 324 325 buf.end(); 326 } 327 328 } 329 330 /// correction matrix for the vulkan coordinate system 331 // (gl3n is made with opengl in mind) 332 mat4 correctionMatrix() pure 333 { 334 return mat4( 335 1f, 0f, 0f, 0f, 336 0f, -1f, 0f, 0f, 337 0f, 0f, 0.5f, 0.5f, 338 0f, 0f, 0f, 1f, 339 ); 340 } 341 342 int main(string[] args) { 343 344 try { 345 auto example = new TextureExample(args); 346 example.prepare(); 347 scope(exit) example.dispose(); 348 349 example.window.onMouseOn = (uint, uint) { 350 example.window.closeFlag = true; 351 }; 352 353 import std.datetime.stopwatch : StopWatch; 354 355 ulong frameCount; 356 ulong lastUs; 357 StopWatch sw; 358 sw.start(); 359 360 enum reportFreq = 100; 361 362 // 6 RPM at 60 FPS 363 const puls = 6 * 2*PI / 3600f; 364 auto angle = 0f; 365 const view = mat4.look_at(vec3(0, -5, 3), vec3(0, 0, 0), vec3(0, 0, 1)); 366 const proj = mat4.perspective(640, 480, 45, 1, 10); 367 const viewProj = correctionMatrix() * proj*view; 368 369 while (!example.window.closeFlag) { 370 const model = mat4.rotation(angle, vec3(0, 0, 1)); 371 const mvp = viewProj*model; 372 const normals = model.inverse().transposed(); 373 angle += puls; 374 375 example.updateMatrices( TextureExample.Matrices( 376 mvp.transposed().matrix, 377 normals.transposed().matrix 378 ) ); 379 380 example.display.pollAndDispatch(); 381 example.render(); 382 ++ frameCount; 383 if ((frameCount % reportFreq) == 0) { 384 const us = sw.peek().total!"usecs"; 385 writeln("FPS: ", 1000_000.0 * reportFreq / (us - lastUs)); 386 lastUs = us; 387 } 388 } 389 390 return 0; 391 } 392 catch(Exception ex) { 393 stderr.writeln("error occured: ", ex.msg); 394 return 1; 395 } 396 }