1 module textureRectangle.entry; 2 3 import std; 4 import std.file : fremove = remove; 5 import erupted; 6 import erupted.vulkan_lib_loader; 7 import sbylib.wrapper.vulkan; 8 import sbylib.wrapper.vulkan.util : compileShader; 9 import sbylib.wrapper.glfw : GLFW, Window, WindowBuilder, ClientAPI; 10 import sbylib.wrapper.freeimage : FImage = Image; 11 12 void entryPoint() { 13 /* 14 GLFW初期化 15 */ 16 GLFW.initialize(); 17 scope (exit) 18 GLFW.terminate(); 19 20 21 /* 22 Window作成 23 */ 24 Window window; 25 with (WindowBuilder()) { 26 width = 800; 27 height = 600; 28 title = "title"; 29 clientAPI = ClientAPI.NoAPI; 30 resizable = false; 31 window = buildWindow(); 32 } 33 scope (exit) 34 window.destroy(); 35 36 37 /* 38 GLFWのVulkanサポートチェック 39 */ 40 assert(GLFW.hasVulkanSupport()); 41 42 43 /* 44 VulkanのGlobal Functionを読み込む 45 */ 46 const globalFunctionLoaded = loadGlobalLevelFunctions(); 47 assert(globalFunctionLoaded); 48 49 50 /* 51 Intanceの作成 52 53 Validation Layerを有効化する場合はここで設定する。 54 (ただし利用可能かどうかチェックする) 55 56 GLFWが要求する拡張機能も有効化する。 57 58 */ 59 Instance.CreateInfo instanceCreateInfo = { 60 applicationInfo: { 61 applicationName: "Vulkan Test", 62 applicationVersion: VK_MAKE_VERSION(1,0,0), 63 engineName: "No Engine", 64 engineVersion: VK_MAKE_VERSION(1,0,0), 65 apiVersion : VK_API_VERSION_1_0 66 }, 67 enabledLayerNames: [ 68 "VK_LAYER_LUNARG_standard_validation", 69 "VK_LAYER_KHRONOS_validation", 70 ], 71 enabledExtensionNames: GLFW.getRequiredInstanceExtensions() 72 }; 73 74 enforce(instanceCreateInfo.enabledLayerNames.all!(n => 75 LayerProperties.getAvailableInstanceLayerProperties().canFind!(l => l.layerName == n))); 76 77 auto instance = new Instance(instanceCreateInfo); 78 scope (exit) 79 instance.destroy(); 80 81 82 /* 83 Surface作成 84 85 SurfaceとはVulkanのAPIとWindow Systemをつなぐものらしい 86 */ 87 auto surface = window.createSurface(instance); 88 scope (exit) 89 surface.destroy(); 90 91 92 /* 93 GPUの選択 94 95 GPUはPhysical DeviceとVulkan内では呼ばれている。 96 利用可能なGPUの中で、Surface機能をサポートしているものを採用。 97 */ 98 auto physDevices = instance.enumeratePhysicalDevices(); 99 const gpuIndex = physDevices.countUntil!(p => p.getSurfaceSupport(surface)); 100 enforce(gpuIndex != -1, "There are no GPUs with Surface support."); 101 auto gpu = physDevices[gpuIndex]; 102 103 104 /* 105 Queue Familyの選択 106 107 Vulkanの命令の多くは 108 1. Command Bufferに命令を記録 109 2. Command BufferをQueueに追加 110 というプロセスをたどる。 111 Command Bufferに記録する命令には"Supported Queue Type"とかいう属性が1つ定められており、 112 Queueに突っ込む際はその属性をサポートしているQueueに突っ込まなければならない。 113 Queue Familyというのは要はこのQueueの種類のことであり、今回はGraphics属性をサポートしているQueueを採用する。 114 */ 115 const queueFamilyProperties = gpu.getQueueFamilyProperties(); 116 auto graphicsQueueFamilyIndex = cast(uint)queueFamilyProperties 117 .countUntil!((const QueueFamilyProperties prop) => 118 prop.supports(QueueFamilyProperties.Flags.Graphics)); 119 enforce(graphicsQueueFamilyIndex != -1, "There are no queue family with Graphics support."); 120 121 122 /* 123 Deviceの作成 124 125 選択した"物理的な"Deviceを基に"論理的な"Deviceを作成する 126 DeviceにもInstanceと同様、LayerとExtensionが付けられる。 127 今回はSwapchainのExtensionのみ有効化する。 128 ついでに先程選んだQueue FamilyでQueueを1つ作っておく。 129 */ 130 Device.DeviceCreateInfo deviceCreateInfo = { 131 queueCreateInfos: [{ 132 queuePriorities: [0.0f], 133 queueFamilyIndex: graphicsQueueFamilyIndex, 134 }], 135 enabledExtensionNames: ["VK_KHR_swapchain"] 136 }; 137 auto device = new Device(gpu, deviceCreateInfo); 138 scope (exit) 139 device.destroy(); 140 141 auto queue = device.getQueue(graphicsQueueFamilyIndex, 0); 142 143 144 /* 145 Command Poolの作成 146 147 Command PoolとはCommand Bufferを確保する元の領域らしい。 148 flagsにTransient, ResetCommandBuffer, Protectedを0個以上選んで入れられる。 149 150 Transient: 151 このCommand Poolから確保されるCommand Bufferは短命(比較的すぐResetされるか解放される)。 152 メモリ確保の戦略に使われるんじゃね?とのこと。 153 ResetCommandBuffer: 154 このCommand Poolから確保されるCommand Bufferは個別にReset可能になる。 155 ResetはvkResetCommandBufferかvkBeginCommandBufferでできる。 156 逆にこのフラグ立てずに上記命令を呼び出してはいけない(vkResetCommandPoolなる命令でまとめてやる)。 157 Protected: 158 このCommand Poolから確保されるCommand Bufferは(Memory) Protectedになる。 159 Protectedになると、よくないメモリアクセスでちゃんと怒ってくられるようになるっぽい。 160 161 */ 162 CommandPool.CreateInfo commandPoolCreateInfo = { 163 flags: CommandPool.CreateInfo.Flags.ResetCommandBuffer 164 | CommandPool.CreateInfo.Flags.Protected, 165 queueFamilyIndex: graphicsQueueFamilyIndex 166 }; 167 auto commandPool = new CommandPool(device, commandPoolCreateInfo); 168 scope (exit) 169 commandPool.destroy(); 170 171 172 /* 173 Swapchainの作成 174 175 Swapchainとはスクリーンに表示されるImageのQueueみたいなもん。 176 Swapchainを作るとき、内部に含まれるImageも併せて作成される。 177 presentModeは表示(同期)の方法。 178 179 Immediate: 180 垂直同期すらせず、来た瞬間表示 181 Mailbox: 182 垂直同期をする。 183 内部的に用意された容量1のQueueを用い、画像が来たら溜め込み、垂直同期の瞬間が来たら取り出して表示する。 184 画像が既にあるのに来ちゃったら古いほうを破棄。 185 FIFO: 186 Mailboxの容量いっぱい版。 187 FIFORelaxed: 188 FIFO + 「垂直同期のタイミングなのに次の画像がまだ来ていなかった場合、次のが来次第すぐ表示」。 189 SharedDemandRefresh: 190 表示エンジンとアプリケーションの双方からの並列なImageへのアクセスを許可する。 191 アプリケーションはアクセスしているImageの更新が必要なときは必ず更新リクエストを投げなければならない。 192 表示タイミングについてはいつかわからない。 193 SharedContinuousRefresh 194 表示エンジンとアプリケーションの双方からの並列なImageへのアクセスを許可する。 195 Imageの更新は通常のサイクルと同様に周期的に起きる。 196 アプリケーションは一番最初だけ更新リクエストを投げなければならない。 197 */ 198 const surfaceCapabilities = gpu.getSurfaceCapabilities(surface); 199 200 const surfaceFormats = gpu.getSurfaceFormats(surface); 201 const surfaceFormat = surfaceFormats.find!(f => f.format == VK_FORMAT_B8G8R8A8_UNORM).front; 202 203 Swapchain.CreateInfo swapchainCreateInfo = { 204 surface: surface, 205 minImageCount: surfaceCapabilities.minImageCount, 206 imageFormat: surfaceFormat.format, 207 imageColorSpace: surfaceFormat.colorSpace, 208 imageExtent: surfaceCapabilities.currentExtent, 209 imageArrayLayers: 1, 210 imageUsage: ImageUsage.ColorAttachment, 211 imageSharingMode: SharingMode.Exclusive, 212 compositeAlpha: CompositeAlpha.Opaque, 213 preTransform: SurfaceTransform.Identity, 214 presentMode: PresentMode.FIFO, 215 clipped: true, 216 }; 217 enforce(surfaceCapabilities.supports(swapchainCreateInfo.imageUsage)); 218 enforce(surfaceCapabilities.supports(swapchainCreateInfo.compositeAlpha)); 219 enforce(surfaceCapabilities.supports(swapchainCreateInfo.preTransform)); 220 enforce(gpu.getSurfacePresentModes(surface).canFind(swapchainCreateInfo.presentMode)); 221 222 auto swapchain = new Swapchain(device, swapchainCreateInfo); 223 scope (exit) 224 swapchain.destroy(); 225 226 auto swapchainImages = swapchain.getImages(); 227 228 229 /* 230 SwapchainからImageViewを作成 231 232 ImageViewとはその名の通り、Swapchain内のImageに対するView(Slice)である。 233 */ 234 auto swapchainImageViews = swapchainImages.map!((Image image) { 235 ImageView.CreateInfo info = { 236 image: image, 237 viewType: ImageViewType.Type2D, 238 format: surfaceFormat.format, 239 subresourceRange: { 240 aspectMask: ImageAspect.Color, 241 baseMipLevel: 0, 242 levelCount: 1, 243 baseArrayLayer: 0, 244 layerCount: 1, 245 } 246 }; 247 return new ImageView(device, info); 248 }).array; 249 scope (exit) 250 foreach (imageView; swapchainImageViews) 251 imageView.destroy(); 252 253 254 /* 255 RenderPassの作成 256 257 RenderPassはFramebufferをどうやって処理するかという設定集。 258 設定のみで、具体的な処理は書いてない。 259 260 RenderPassはAttachmentとSubpassとDependencyから成る。 261 262 Attachmentは計算中に現れる資源がどんなもんかという設定。 263 ここではAttachmentはSwapchain内に作ったImage。 264 loadOp, storeOpは計算の開始/終了時にデータをどう処理するかを書く。 265 各資源にはlayoutなる状態が定義されているらしく、開始/終了時のlayoutを決めておく。 266 資源の終了時のLayoutは必ずPRESENT_SRCにしておかないと描画(present)できないっぽい。 267 268 Subpassは計算中に資源をどういうふうに使うかの設定。 269 「Attachment0番をCOLOR_ATTACH_MENT_OPTIMALレイアウト状態でcolor attachmentとして使う」というSubpassを1つ定義している。 270 271 DependencyはSubpass間の資源の依存性に関する設定。 272 ここでは書いていない。(Subpassが1つしかないから依存もクソもない) 273 */ 274 RenderPass.CreateInfo renderPassCreateInfo = { 275 attachments: [{ 276 format: surfaceFormat.format, 277 samples: SampleCount.Count1, 278 loadOp: AttachmentLoadOp.Clear, 279 storeOp: AttachmentStoreOp.Store, 280 initialLayout: ImageLayout.Undefined, 281 finalLayout: ImageLayout.PresentSrc, 282 }], 283 subpasses: [{ 284 pipelineBindPoint: PipelineBindPoint.Graphics, 285 colorAttachments: [{ 286 attachment: 0, 287 layout: ImageLayout.ColorAttachmentOptimal 288 }] 289 }] 290 }; 291 auto renderPass = new RenderPass(device, renderPassCreateInfo); 292 scope (exit) 293 renderPass.destroy(); 294 295 296 /* 297 Framebufferの作成 298 299 いつもの。 300 */ 301 auto framebuffers = swapchainImageViews.map!((imageView) { 302 Framebuffer.CreateInfo info = { 303 renderPass: renderPass, 304 attachments: [imageView], 305 width: 800, 306 height: 600, 307 layers: 1, 308 }; 309 return new Framebuffer(device, info); 310 }).array; 311 scope (exit) 312 foreach (framebuffer; framebuffers) 313 framebuffer.destroy(); 314 315 316 /* 317 CommandBufferの確保 318 319 さっきつくったCommandPoolからCommandBufferを確保する。 320 確保とかいうからサイズとかを指定するかと思ったけど、そうでもないっぽい。 321 一気にいっぱい確保できるが、とりあえず1個確保。 322 */ 323 CommandBuffer.AllocateInfo commandbufferAllocInfo = { 324 commandPool: commandPool, 325 level: CommandBufferLevel.Primary, 326 commandBufferCount: 1, 327 }; 328 auto commandBuffer = CommandBuffer.allocate(device, commandbufferAllocInfo)[0]; 329 scope (exit) 330 commandBuffer.destroy(); 331 332 333 /* 334 頂点データの作成 335 336 好きにやればいいっぽい。 337 */ 338 struct VertexData { 339 float[2] pos; 340 float[3] color; 341 float[2] texcoord; 342 } 343 344 static VertexData[4] vertices = [ 345 VertexData([-0.75f, -0.75f], [1.0f, 0.0f, 0.0f], [0.0f, 1.0f]), 346 VertexData([-0.75f, +0.75f], [0.0f, 0.0f, 1.0f], [0.0f, 0.0f]), 347 VertexData([+0.75f, +0.75f], [0.0f, 1.0f, 0.0f], [1.0f, 0.0f]), 348 VertexData([+0.75f, -0.75f], [1.0f, 0.0f, 1.0f], [1.0f, 1.0f]), 349 ]; 350 351 auto vData = createBuffer(gpu, device, vertices, BufferUsage.VertexBuffer); 352 353 auto vertexBuffer = vData.buffer; 354 scope (exit) 355 vertexBuffer.destroy(); 356 357 auto vertexDeviceMemory = vData.deviceMemory; 358 scope (exit) 359 vertexDeviceMemory.destroy(); 360 361 362 /* 363 Uniformデータの作成 364 365 これも適当に。 366 */ 367 struct UniformData { 368 float time; 369 } 370 371 static UniformData[1] uniforms = [{ 372 time: 0 373 }]; 374 375 auto uData = createBuffer(gpu, device, uniforms, BufferUsage.UniformBuffer); 376 377 auto uniformBuffer = uData.buffer; 378 scope (exit) 379 uniformBuffer.destroy(); 380 381 auto uniformDeviceMemory = uData.deviceMemory; 382 scope (exit) 383 uniformDeviceMemory.destroy(); 384 385 386 /* 387 Textureの読み込み 388 */ 389 auto texture = FImage.load(__FILE_FULL_PATH__.dirName.buildPath("dman.png")).to32bit(); 390 auto tData = createBuffer(gpu, device, texture.dataArray, BufferUsage.TransferSrc); 391 392 auto textureBuffer = tData.buffer; 393 scope (exit) 394 textureBuffer.destroy(); 395 396 auto textureDeviceMemory = tData.deviceMemory; 397 scope (exit) 398 textureDeviceMemory.destroy(); 399 400 401 /* 402 Texture用Imageの作成 403 */ 404 Image.CreateInfo imageInfo = { 405 imageType: ImageType.Type2D, 406 extent: { 407 width: texture.width, 408 height: texture.height, 409 depth: 1 410 }, 411 mipLevels: 1, 412 arrayLayers: 1, 413 format: VK_FORMAT_R8G8B8A8_UNORM, 414 tiling: ImageTiling.Optimal, 415 initialLayout: ImageLayout.Undefined, 416 usage: ImageUsage.TransferDst | ImageUsage.Sampled, 417 sharingMode: SharingMode.Exclusive, 418 samples: SampleCount.Count1 419 }; 420 auto image = new Image(device, imageInfo); 421 scope (exit) 422 image.destroy(); 423 424 425 /* 426 Image用Device Memory確保 427 */ 428 DeviceMemory.AllocateInfo deviceMemoryAllocInfo = { 429 allocationSize: device.getImageMemoryRequirements(image).size, 430 memoryTypeIndex: cast(uint)gpu.getMemoryProperties().memoryTypes 431 .countUntil!(p => p.supports(MemoryProperties.MemoryType.Flags.DeviceLocal)) 432 }; 433 enforce(deviceMemoryAllocInfo.memoryTypeIndex != -1); 434 auto deviceMemory = new DeviceMemory(device, deviceMemoryAllocInfo); 435 scope (exit) 436 deviceMemory.destroy(); 437 438 439 /* 440 Device MemoryとImageの紐づけ 441 */ 442 deviceMemory.bindImage(image, 0); 443 444 445 /* 446 Image Viewの作成 447 */ 448 ImageView.CreateInfo imageViewInfo = { 449 image: image, 450 viewType: ImageViewType.Type2D, 451 format: VK_FORMAT_R8G8B8A8_UNORM, 452 subresourceRange: { 453 aspectMask: ImageAspect.Color, 454 baseMipLevel: 0, 455 levelCount: 1, 456 baseArrayLayer: 0, 457 layerCount: 1 458 }, 459 components: { 460 r: ComponentSwizzle.B, 461 g: ComponentSwizzle.G, 462 b: ComponentSwizzle.R, 463 a: ComponentSwizzle.A, 464 } 465 }; 466 auto imageView = new ImageView(device, imageViewInfo); 467 scope (exit) 468 imageView.destroy(); 469 470 471 /* 472 Samplerの作成 473 */ 474 Sampler.CreateInfo samplerInfo = { 475 magFilter: SamplerFilter.Linear, 476 minFilter: SamplerFilter.Linear, 477 addressModeU: SamplerAddressMode.Repeat, 478 addressModeV: SamplerAddressMode.Repeat, 479 addressModeW: SamplerAddressMode.Repeat, 480 anisotropyEnable: false, 481 maxAnisotropy: 1, 482 borderColor: BorderColor.IntOpaqueBlack, 483 unnormalizedCoordinates: false, 484 compareEnable: false, 485 compareOp: CompareOp.Always, 486 mipmapMode: SamplerMipmapMode.Linear, 487 mipLodBias: 0.0f, 488 minLod: 0.0f, 489 maxLod: 0.0f 490 }; 491 auto sampler = new Sampler(device, samplerInfo); 492 scope (exit) 493 sampler.destroy(); 494 495 496 /* 497 Descripter Set Layoutの作成 498 499 Descriptor Set LayoutはPipelineに対して色々な情報を教えるものらしい。 500 今回はUniformBufferとSamplerの情報を教えてあげる。 501 descriptorCountはそのbinding値のオブジェクトを配列として見たときの要素数。 502 */ 503 DescriptorSetLayout.CreateInfo descriptorSetLayoutInfo = { 504 bindings: [{ 505 binding: 0, 506 descriptorType: DescriptorType.UniformBuffer, 507 descriptorCount: 1, 508 stageFlags: ShaderStage.Fragment 509 },{ 510 binding: 1, 511 descriptorType: DescriptorType.CombinedImageSampler, 512 descriptorCount: 1, 513 stageFlags: ShaderStage.Fragment 514 }] 515 }; 516 auto descriptorSetLayout = new DescriptorSetLayout(device, descriptorSetLayoutInfo); 517 scope (exit) 518 descriptorSetLayout.destroy(); 519 520 521 /* 522 Descriptor Pool作成 523 */ 524 DescriptorPool.CreateInfo descriptorPoolInfo = { 525 poolSizes: [{ 526 type: DescriptorType.UniformBuffer, 527 descriptorCount: 1 528 }, { 529 type: DescriptorType.CombinedImageSampler, 530 descriptorCount: 1 531 }], 532 maxSets: 1 533 }; 534 auto descriptorPool = new DescriptorPool(device, descriptorPoolInfo); 535 scope (exit) 536 descriptorPool.destroy(); 537 538 539 /* 540 Descriptor Setの確保 541 542 Descriptor Setは色々なデータ(Image, Buffer, BufferView)に対するBindの方法を指定する物体。 543 */ 544 DescriptorSet.AllocateInfo descriptorSetAllocInfo = { 545 descriptorPool: descriptorPool, 546 setLayouts: [descriptorSetLayout] 547 }; 548 auto descriptorSet = DescriptorSet.allocate(device, descriptorSetAllocInfo)[0]; 549 550 DescriptorSet.Write[2] writes = [{ 551 dstSet: descriptorSet, 552 dstBinding: 0, 553 dstArrayElement: 0, 554 descriptorType: DescriptorType.UniformBuffer, 555 bufferInfo: [{ 556 buffer: uniformBuffer, 557 offset: 0, 558 range: UniformData.sizeof 559 }] 560 }, { 561 dstSet: descriptorSet, 562 dstBinding: 1, 563 dstArrayElement: 0, 564 descriptorType: DescriptorType.CombinedImageSampler, 565 imageInfo: [{ 566 imageLayout: ImageLayout.ShaderReadOnlyOptimal, 567 imageView: imageView, 568 sampler: sampler 569 }] 570 }]; 571 DescriptorSet.Copy[0] copies; 572 descriptorSet.update(writes, copies); 573 574 575 /* 576 Shader Module作成 577 */ 578 compileShader(__FILE_FULL_PATH__.dirName.buildPath("test.vert")); 579 scope (exit) fremove("vert.spv"); 580 581 compileShader(__FILE_FULL_PATH__.dirName.buildPath("test.frag")); 582 scope (exit) fremove("frag.spv"); 583 584 ShaderModule.CreateInfo vsShaderCreateInfo = { 585 code: cast(ubyte[])read("vert.spv") 586 }; 587 auto vsMod = new ShaderModule(device, vsShaderCreateInfo); 588 scope (exit) 589 vsMod.destroy(); 590 591 ShaderModule.CreateInfo fsShaderCreateInfo = { 592 code: cast(ubyte[])read("frag.spv") 593 }; 594 auto fsMod = new ShaderModule(device, fsShaderCreateInfo); 595 scope (exit) 596 fsMod.destroy(); 597 598 599 /* 600 PipelineLayout作成 601 602 Pipelineが使う資源のレイアウトに関する情報らしい。 603 今回はUniform Bufferに関する情報を書く。 604 */ 605 PipelineLayout.CreateInfo pipelineLayoutCreateInfo = { 606 setLayouts: [descriptorSetLayout] 607 }; 608 auto pipelineLayout = new PipelineLayout(device, pipelineLayoutCreateInfo); 609 scope (exit) 610 pipelineLayout.destroy(); 611 612 613 /* 614 Pipeline作成 615 616 Pipelineとは、その名の通り描画パイプラインそのもの。 617 設定自体は長いが、全部自明。 618 */ 619 Pipeline.GraphicsCreateInfo pipelineCreateInfo = { 620 stages: [{ 621 stage: ShaderStage.Vertex, 622 _module: vsMod, 623 pName: "main", 624 }, { 625 stage: ShaderStage.Fragment, 626 _module: fsMod, 627 pName: "main", 628 }], 629 vertexInputState: { 630 vertexBindingDescriptions: [{ 631 binding: 0, 632 stride: VertexData.sizeof, 633 inputRate: VertexInputRate.Vertex 634 }], 635 vertexAttributeDescriptions: [{ 636 location: 0, 637 binding: 0, 638 format: VK_FORMAT_R32G32_SFLOAT, 639 offset: VertexData.pos.offsetof 640 },{ 641 location: 1, 642 binding: 0, 643 format: VK_FORMAT_R32G32B32_SFLOAT, 644 offset: VertexData.color.offsetof 645 },{ 646 location: 2, 647 binding: 0, 648 format: VK_FORMAT_R32G32_SFLOAT, 649 offset: VertexData.texcoord.offsetof 650 }] 651 }, 652 inputAssemblyState: { 653 topology: PrimitiveTopology.TriangleFan, 654 }, 655 viewportState: { 656 viewports: [{ 657 x: 0.0f, 658 y: 0.0f, 659 width: window.width, 660 height: window.height, 661 minDepth: 0.0f, 662 maxDepth: 1.0f 663 }], 664 scissors: [{ 665 offset: { 666 x: 0, 667 y: 0 668 }, 669 extent: { 670 width: window.width, 671 height: window.height 672 } 673 }] 674 }, 675 rasterizationState: { 676 depthClampEnable: false, 677 rasterizerDiscardEnable: false, 678 polygonMode: PolygonMode.Fill, 679 cullMode: CullMode.None, 680 frontFace: FrontFace.CounterClockwise, 681 depthBiasEnable: false, 682 depthBiasConstantFactor: 0.0f, 683 depthBiasClamp: 0.0f, 684 depthBiasSlopeFactor: 0.0f, 685 lineWidth: 1.0f, 686 }, 687 multisampleState: { 688 rasterizationSamples: SampleCount.Count1, 689 sampleShadingEnable: false, 690 alphaToCoverageEnable: false, 691 alphaToOneEnable: false, 692 }, 693 colorBlendState: { 694 logicOpEnable: false, 695 attachments: [{ 696 blendEnable: true, 697 srcColorBlendFactor: BlendFactor.SrcAlpha, 698 dstColorBlendFactor: BlendFactor.OneMinusSrcAlpha, 699 colorBlendOp: BlendOp.Add, 700 colorWriteMask: ColorComponent.R 701 | ColorComponent.G 702 | ColorComponent.B 703 | ColorComponent.A, 704 }] 705 }, 706 layout: pipelineLayout, 707 renderPass: renderPass, 708 subpass: 0, 709 }; 710 auto pipeline = Pipeline.create(device, [pipelineCreateInfo])[0]; 711 scope (exit) 712 pipeline.destroy(); 713 714 715 /* 716 Fenceの作成 717 718 Fenceとは同期用のアイテムで、Vulkanにおける非同期命令はだいたいこのFenceと一緒に投げて、Fenceを使って待つ。 719 */ 720 Fence.CreateInfo fenceCreatInfo; 721 auto fence = new Fence(device, fenceCreatInfo); 722 scope (exit) 723 fence.destroy(); 724 725 726 /* 727 Image Indexの取得 728 729 Swapchain内のImageの何番を次叩くべきかを得る。 730 Fenceと一緒に投げる理由は、返ってくるIndexのImageが解放されているかがわからないためらしい。 731 Fenceは使い終わったら必ずResetすること。 732 */ 733 auto currentImageIndex = swapchain.acquireNextImageIndex(ulong.max, null, fence); 734 Fence.wait([fence], false, ulong.max); 735 Fence.reset([fence]); 736 737 738 /* 739 Imageのデータ転送 740 */ 741 { 742 CommandBuffer.BeginInfo beginInfo = { 743 flags: CommandBuffer.BeginInfo.Flags.OneTimeSubmit 744 }; 745 commandBuffer.begin(beginInfo); 746 747 VkImageMemoryBarrier barrier = { 748 dstAccessMask: AccessFlags.TransferWrite, 749 oldLayout: ImageLayout.Undefined, 750 newLayout: ImageLayout.TransferDstOptimal, 751 image: image.image, 752 subresourceRange: { 753 aspectMask: ImageAspect.Color, 754 baseMipLevel: 0, 755 levelCount: 1, 756 baseArrayLayer: 0, 757 layerCount: 1 758 } 759 }; 760 commandBuffer.cmdPipelineBarrier(PipelineStage.TopOfPipe, PipelineStage.Transfer, 0, null, null, [barrier]); 761 762 VkBufferImageCopy bufferImageCopy = { 763 bufferOffset: 0, 764 bufferRowLength: 0, 765 bufferImageHeight: 0, 766 imageSubresource: { 767 aspectMask: ImageAspect.Color, 768 mipLevel: 0, 769 baseArrayLayer: 0, 770 layerCount: 1, 771 }, 772 imageOffset: { 773 x: 0, 774 y: 0, 775 z: 0, 776 }, 777 imageExtent: { 778 width: texture.width, 779 height: texture.height, 780 depth: 1 781 } 782 }; 783 commandBuffer.cmdCopyBufferToImage(textureBuffer, image, ImageLayout.TransferDstOptimal, [bufferImageCopy]); 784 785 VkImageMemoryBarrier barrier2 = { 786 oldLayout: ImageLayout.TransferDstOptimal, 787 newLayout: ImageLayout.ShaderReadOnlyOptimal, 788 image: image.image, 789 subresourceRange: { 790 aspectMask: ImageAspect.Color, 791 baseMipLevel: 0, 792 levelCount: 1, 793 baseArrayLayer: 0, 794 layerCount: 1 795 } 796 }; 797 commandBuffer.cmdPipelineBarrier(PipelineStage.Transfer, PipelineStage.FragmentShader, 0, null, null, [barrier2]); 798 799 commandBuffer.end(); 800 801 Queue.SubmitInfo submitInfo = { 802 commandBuffers: [commandBuffer] 803 }; 804 queue.submit([submitInfo], fence); 805 Fence.wait([fence], true, ulong.max); 806 Fence.reset([fence]); 807 } 808 809 810 while (window.shouldClose is false) { 811 auto framebuffer = framebuffers[currentImageIndex]; 812 813 /* 814 Command Bufferへの記録 815 816 CommandBufferへCommandを記録するときは必ずbeginしてendする。 817 ここではRenderPassの実行を記録している。 818 RenderPassの実行は 819 1. PipelineのBind (OpenGLでいうusePipeline) 820 2. Vertex BufferのBind (OpenGLでいうbindBuffer) 821 3. Uniform BufferのBind (OpenGLでいうglUniform) 822 4. Draw call (OpenGLでいうdrawArrays) 823 となっている。 824 */ 825 CommandBuffer.BeginInfo beginInfo; 826 commandBuffer.begin(beginInfo); 827 828 CommandBuffer.RenderPassBeginInfo renderPassBeginInfo = { 829 renderPass: renderPass, 830 framebuffer: framebuffers[currentImageIndex], 831 renderArea: { 832 extent: VkExtent2D(window.width, window.height) 833 }, 834 clearValues: [{ 835 color: { 836 float32: [0.5f, 0.5f, 0.5f, 1.0f] 837 } 838 }] 839 }; 840 commandBuffer.cmdBeginRenderPass(renderPassBeginInfo, SubpassContents.Inline); 841 commandBuffer.cmdBindPipeline(PipelineBindPoint.Graphics, pipeline); 842 commandBuffer.cmdBindVertexBuffers(0, [vertexBuffer], [0]); 843 commandBuffer.cmdBindDescriptorSets(PipelineBindPoint.Graphics, pipelineLayout, 844 0, [descriptorSet]); 845 commandBuffer.cmdDraw(vertices.length, 1, 0, 0); 846 commandBuffer.cmdEndRenderPass(); 847 commandBuffer.end(); 848 849 850 /* 851 QueueへのCommand Bufferの投函 852 853 これも完了を待っている。 854 */ 855 Queue.SubmitInfo submitInfo = { 856 commandBuffers: [commandBuffer] 857 }; 858 queue.submit([submitInfo], fence); 859 Fence.wait([fence], true, ulong.max); 860 Fence.reset([fence]); 861 862 863 /* 864 画面への反映 865 */ 866 Queue.PresentInfo presentInfo = { 867 swapchains: [swapchain], 868 imageIndices: [currentImageIndex] 869 }; 870 queue.present(presentInfo); 871 872 873 /* 874 次の Image Indexの取得 875 */ 876 currentImageIndex = swapchain.acquireNextImageIndex(ulong.max, null, fence); 877 Fence.wait([fence], true, ulong.max); 878 Fence.reset([fence]); 879 880 /* 881 Uniformの更新 882 */ 883 auto uniformData = cast(UniformData[])uniformDeviceMemory.map(0, uniforms.sizeof, 0); 884 uniformData[0].time += 0.05f; 885 uniformDeviceMemory.unmap(); 886 887 888 /* 889 GLFWのEvent Polling 890 */ 891 GLFW.pollEvents(); 892 } 893 } 894 895 auto createBuffer(Data)(PhysicalDevice gpu, Device device, Data[] data, BufferUsage usage) { 896 /* 897 Bufferの作成 898 */ 899 Buffer.CreateInfo bufferInfo = { 900 usage: usage, 901 size: Data.sizeof * data.length, 902 sharingMode: SharingMode.Exclusive, 903 }; 904 auto buffer = new Buffer(device, bufferInfo); 905 906 907 /* 908 Device Memory確保 909 910 Buffer用のメモリ確保。 911 */ 912 const requirements = device.getBufferMemoryRequirements(buffer); 913 DeviceMemory.AllocateInfo deviceMemoryAllocInfo = { 914 allocationSize: requirements.size, 915 memoryTypeIndex: cast(uint)gpu.getMemoryProperties().memoryTypes.enumerate 916 .countUntil!(p => requirements.acceptable(p.index)) 917 }; 918 enforce(deviceMemoryAllocInfo.memoryTypeIndex != -1); 919 auto deviceMemory = new DeviceMemory(device, deviceMemoryAllocInfo); 920 921 922 /* 923 Device Memoryへのデータ転送 924 */ 925 auto memory = deviceMemory.map(0, bufferInfo.size, 0); 926 memory[] = (cast(ubyte[])data)[]; 927 deviceMemory.unmap(); 928 929 930 /* 931 Device MemoryとBufferの紐づけ 932 */ 933 deviceMemory.bindBuffer(buffer, 0); 934 935 struct Result { 936 Buffer buffer; 937 DeviceMemory deviceMemory; 938 } 939 940 return Result(buffer, deviceMemory); 941 }