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 }