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