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 }