メモリの確保はvk::DeviceのallocateMemoryメソッドで確保します。そして確保したメモリをbindImageMemoryメソッドで、前節で作成したイメージに結びつけることができます。
new式などで確保する通常のメモリと違い、これから扱うメモリはGPU上のメモリです。GPU上のメモリには種類があり、どれを使うか適切に選ぶ必要があります。どんな種類のメモリがあるかはGPU依存であり、vk::PhysicalDeviceのgetMemoryPropertiesメソッドで取得できます。
vk::PhysicalDeviceMemoryProperties memProps = physicalDevice.getMemoryProperties();
vk::PhysicalDeviceMemoryProperties構造体にはGPUが何種類のメモリを持っているかを表すmemoryTypeCountメンバ変数、各種類のメモリに関する情報を表すmemoryTypesメンバ変数(配列です)が格納されています。
また、イメージに対してどんな種類のメモリがどれだけ必要かという情報はvk::DeviceのgetImageMemoryRequirementsで取得できます。
vk::MemoryRequirements imgMemReq = device->getImageMemoryRequirements(image.get());
vk::MemoryRequirements構造体には必要なメモリサイズを表すsizeメンバ変数、そして使用可能なメモリの種類をビットマスクで表すmemoryTypeBitsメンバ変数が格納されています。
例えばmemoryTypeBitsの中身が2進数で00101011だった場合、使えるメモリの種類はメモリタイプ0番、1番、3番、5番になるのでこの中から選ぶことになります。
以上を踏まえて、メモリの確保のコードを見てみましょう。
vk::PhysicalDeviceMemoryProperties memProps = physicalDevice.getMemoryProperties(); vk::MemoryRequirements imgMemReq = device->getImageMemoryRequirements(image.get()); vk::MemoryAllocateInfo imgMemAllocInfo; imgMemAllocInfo.allocationSize = imgMemReq.size; bool suitableMemoryTypeFound = false; for (size_t i = 0; i < memProps.memoryTypeCount; i++) { if (imgMemReq.memoryTypeBits & (1 << i)) { imgMemAllocInfo.memoryTypeIndex = i; suitableMemoryTypeFound = true; break; } } if (!suitableMemoryTypeFound) { std::cerr << "使用可能なメモリタイプがありません。" << std::endl; return -1; } vk::UniqueDeviceMemory imgMem = device->allocateMemoryUnique(imgMemAllocInfo);
vk::MemoryAllocateInfoに必要なメモリのサイズと種類を指定して確保します。memoryTypeBitsを確認して、ビットが立っていればその番号のメモリタイプを使用します。今回は使えるメモリタイプの中から単純に一番最初に見つかったものを使用していますが、今後の記事で、ビットが立っているメモリタイプの中から更に選ぶ処理も実装するかもしれません。
メモリの確保ができたらあとはイメージにバインド(結びつける)するだけです。
device->bindImageMemory(image.get(), imgMem.get(), 0);
第3引数の0は何かというと、確保したメモリの先頭から何バイト目を使用するかという項目です。たとえばメモリ1000バイトを要するイメージAとメモリ1500バイトを要するイメージBの2つがあったとき、2500バイトを確保して先頭から0バイト目以降をイメージAに、1000バイト目以降をイメージBにバインドするといったことができます。ここでは普通に0を指定しています。
この節ではメモリの確保をやりました。次節ではレンダーパスを作成します。
// 環境に合わせて #define VK_USE_PLATFORM_WIN32_KHR #define VULKAN_HPP_TYPESAFE_CONVERSION #include <vulkan/vulkan.hpp> #include <iostream> #include <vector> const uint32_t screenWidth = 640; const uint32_t screenHeight = 480; int main() { vk::InstanceCreateInfo createInfo; vk::UniqueInstance instance; instance = vk::createInstanceUnique(createInfo); std::vector<vk::PhysicalDevice> physicalDevices = instance->enumeratePhysicalDevices(); vk::PhysicalDevice physicalDevice; bool existsSuitablePhysicalDevice = false; uint32_t graphicsQueueFamilyIndex; for (size_t i = 0; i < physicalDevices.size(); i++) { std::vector<vk::QueueFamilyProperties> queueProps = physicalDevices[i].getQueueFamilyProperties(); bool existsGraphicsQueue = false; for (size_t j = 0; j < queueProps.size(); i++) { if (queueProps[j].queueFlags & vk::QueueFlagBits::eGraphics) { existsGraphicsQueue = true; graphicsQueueFamilyIndex = j; break; } } if (existsGraphicsQueue) { physicalDevice = physicalDevices[i]; existsSuitablePhysicalDevice = true; break; } } if (!existsSuitablePhysicalDevice) { std::cerr << "使用可能な物理デバイスがありません。" << std::endl; return -1; } vk::DeviceCreateInfo devCreateInfo; vk::DeviceQueueCreateInfo queueCreateInfo[1]; queueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex; queueCreateInfo[0].queueCount = 1; float queuePriorities[1] = { 1.0 }; queueCreateInfo[0].pQueuePriorities = queuePriorities; devCreateInfo.pQueueCreateInfos = queueCreateInfo; devCreateInfo.queueCreateInfoCount = 1; vk::UniqueDevice device = physicalDevice.createDeviceUnique(devCreateInfo); vk::Queue graphicsQueue = device->getQueue(graphicsQueueFamilyIndex, 0); vk::CommandPoolCreateInfo cmdPoolCreateInfo; cmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex; vk::UniqueCommandPool cmdPool = device->createCommandPoolUnique(cmdPoolCreateInfo); vk::CommandBufferAllocateInfo cmdBufAllocInfo; cmdBufAllocInfo.commandPool = cmdPool.get(); cmdBufAllocInfo.commandBufferCount = 1; cmdBufAllocInfo.level = vk::CommandBufferLevel::ePrimary; std::vector<vk::UniqueCommandBuffer> cmdBufs = device->allocateCommandBuffersUnique(cmdBufAllocInfo); vk::ImageCreateInfo imgCreateInfo; imgCreateInfo.imageType = vk::ImageType::e2D; imgCreateInfo.extent = vk::Extent3D(screenWidth, screenHeight, 1); imgCreateInfo.mipLevels = 1; imgCreateInfo.arrayLayers = 1; imgCreateInfo.format = vk::Format::eR8G8B8A8Unorm; imgCreateInfo.tiling = vk::ImageTiling::eLinear; imgCreateInfo.initialLayout = vk::ImageLayout::eColorAttachmentOptimal; imgCreateInfo.usage = vk::ImageUsageFlagBits::eColorAttachment; imgCreateInfo.sharingMode = vk::SharingMode::eExclusive; imgCreateInfo.samples = vk::SampleCountFlagBits::e1; vk::UniqueImage image = device->createImageUnique(imgCreateInfo); vk::PhysicalDeviceMemoryProperties memProps = physicalDevice.getMemoryProperties(); vk::MemoryRequirements imgMemReq = device->getImageMemoryRequirements(image.get()); vk::MemoryAllocateInfo imgMemAllocInfo; imgMemAllocInfo.allocationSize = imgMemReq.size; bool suitableMemoryTypeFound = false; for (size_t i = 0; i < memProps.memoryTypeCount; i++) { if (imgMemReq.memoryTypeBits & (1 << i)) { imgMemAllocInfo.memoryTypeIndex = i; suitableMemoryTypeFound = true; break; } } if (!suitableMemoryTypeFound) { std::cerr << "使用可能なメモリタイプがありません。" << std::endl; return -1; } vk::UniqueDeviceMemory imgMem = device->allocateMemoryUnique(imgMemAllocInfo); device->bindImageMemory(image.get(), imgMem.get(), 0); vk::CommandBufferBeginInfo cmdBeginInfo; cmdBufs[0]->begin(cmdBeginInfo); // コマンドを記録 cmdBufs[0]->end(); vk::CommandBuffer submitCmdBuf[1] = { cmdBufs[0].get() }; vk::SubmitInfo submitInfo; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = submitCmdBuf; graphicsQueue.submit({ submitInfo }, nullptr); return 0; }