#define NOMINMAX //#define VULKAN_HPP_NO_EXCEPTIONS #pragma region headers #include #include #include #include #include #include #include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE constexpr int MAX_FRAMES_IN_FLIGHT = 2; uint32_t frameIdx = 0; bool framebufferResized = false; std::vector swapChainImages; vk::CommandPool cmdPool; vk::CommandBuffer cmdBuf; vk::CommandBufferBeginInfo beginInfo; vk::Queue gfxQueue; vk::PipelineLayout pipelineLayout; std::vector swapChainImageViews; vk::Extent2D swapChainExtent; vk::Pipeline gfxPipeline; vk::PhysicalDevice physicalDevice; vk::Device device; vk::SurfaceKHR surface; vk::SwapchainKHR swapChain = nullptr; vk::SurfaceFormatKHR swapChainFormat; vk::SurfaceCapabilitiesKHR surfaceCapabilities; std::vector availFormats; std::vector availPresentModes; GLFWwindow *window; vk::DescriptorSetLayout descriptorSetLayout = nullptr; vk::DescriptorPool descriptorPool = nullptr; std::vector descriptorSets; vk::Buffer vertBuf = nullptr; vk::DeviceMemory vertBufMem = nullptr; vk::Buffer idxBuf = nullptr; vk::DeviceMemory idxBufMem = nullptr; vk::Image texImg = nullptr; vk::DeviceMemory texImgMem = nullptr; vk::ImageView texImgView = nullptr; vk::Sampler texSampler = nullptr; std::vector uniformBufs; std::vector uniformBufsMem; std::vector uniformBufsMapped; #pragma region debugCallback static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback( vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, const vk::DebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { if (severity >= vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { std::cerr << "validation layer: type " << to_string(type) << " msg: " << pCallbackData->pMessage << std::endl; } return vk::False; } #pragma endregion #pragma region findMemoryType uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags props) { vk::PhysicalDeviceMemoryProperties memProps = physicalDevice.getMemoryProperties(); for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) { if ((typeFilter & (1 << i)) && (memProps.memoryTypes[i].propertyFlags & props) == props) return i; } return 0; } #pragma endregion #pragma region transitionImgLayout void transitionImgLayout(uint32_t imgIdx, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::AccessFlags2 srcAccessMask, vk::AccessFlags2 dstAccessMask, vk::PipelineStageFlags2 srcStageMask, vk::PipelineStageFlags2 dstStageMask) { vk::ImageSubresourceRange barrierSubres = {}; barrierSubres.aspectMask = vk::ImageAspectFlagBits::eColor; barrierSubres.baseMipLevel = 0; barrierSubres.levelCount = 1; barrierSubres.baseArrayLayer = 0; barrierSubres.layerCount = 1; vk::ImageMemoryBarrier2 barrier = {}; barrier.srcStageMask = srcStageMask; barrier.dstStageMask = dstStageMask; barrier.srcAccessMask = srcAccessMask; barrier.dstAccessMask = dstAccessMask; barrier.oldLayout = oldLayout; barrier.newLayout = newLayout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = swapChainImages[imgIdx]; barrier.subresourceRange = barrierSubres; vk::DependencyInfo depInfo = {}; depInfo.dependencyFlags = {}; depInfo.imageMemoryBarrierCount = 1; depInfo.pImageMemoryBarriers = &barrier; cmdBuf.pipelineBarrier2(depInfo); } #pragma endregion #pragma region framebufferResizeCallback static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { framebufferResized = true; } #pragma endregion #pragma region swapchain void createSwapChain(vk::SwapchainKHR oldSwapChain = nullptr) { surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface); availFormats = physicalDevice.getSurfaceFormatsKHR(surface); availPresentModes = physicalDevice.getSurfacePresentModesKHR(surface); assert(!availFormats.empty()); const auto formatIt = std::ranges::find_if(availFormats, [](const auto &format) { return format.format == vk::Format::eB8G8R8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear; }); swapChainFormat = formatIt != availFormats.end() ? *formatIt : availFormats[0]; assert(std::ranges::any_of(availPresentModes, [](auto present) { return present == vk::PresentModeKHR::eFifo; })); vk::PresentModeKHR presentMode = std::ranges::any_of(availPresentModes, [](const vk::PresentModeKHR val) { return vk::PresentModeKHR::eMailbox == val; }) ? vk::PresentModeKHR::eMailbox : vk::PresentModeKHR::eFifo; if (surfaceCapabilities.currentExtent.width != std::numeric_limits::max()) { swapChainExtent = surfaceCapabilities.currentExtent; } else { int width, height; glfwGetFramebufferSize(window, &width, &height); swapChainExtent.width = std::clamp(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); swapChainExtent.height = std::clamp(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); } uint32_t minImageCount = std::max(3u, surfaceCapabilities.minImageCount); if ((0 < surfaceCapabilities.maxImageCount) && (surfaceCapabilities.maxImageCount < minImageCount)) { minImageCount = surfaceCapabilities.maxImageCount; } vk::SwapchainCreateInfoKHR swapChainCreateInfo = {}; swapChainCreateInfo.surface = surface; swapChainCreateInfo.minImageCount = minImageCount; swapChainCreateInfo.imageFormat = swapChainFormat.format; swapChainCreateInfo.imageColorSpace = swapChainFormat.colorSpace; swapChainCreateInfo.imageExtent = swapChainExtent; swapChainCreateInfo.imageArrayLayers = 1; swapChainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment; swapChainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive; swapChainCreateInfo.preTransform = surfaceCapabilities.currentTransform; swapChainCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; swapChainCreateInfo.presentMode = presentMode; swapChainCreateInfo.clipped = true; swapChainCreateInfo.oldSwapchain = oldSwapChain; swapChain = device.createSwapchainKHR(swapChainCreateInfo); swapChainImages = device.getSwapchainImagesKHR(swapChain); } vk::ImageView createImageView(const vk::Image &image, vk::Format format) { vk::ImageSubresourceRange subrange = {}; subrange.aspectMask = vk::ImageAspectFlagBits::eColor; subrange.baseMipLevel = 0; subrange.levelCount = 1; subrange.baseArrayLayer = 0; subrange.layerCount = 1; vk::ImageViewCreateInfo imgViewCreateInfo = {}; imgViewCreateInfo.image = image; imgViewCreateInfo.viewType = vk::ImageViewType::e2D; imgViewCreateInfo.format = format; imgViewCreateInfo.subresourceRange = subrange; return device.createImageView(imgViewCreateInfo); } void createImageViews() { assert(swapChainImageViews.empty()); swapChainImageViews.reserve(swapChainImages.size()); for (auto &img : swapChainImages) { swapChainImageViews.emplace_back(createImageView(img, swapChainFormat.format)); } } void createTextureImageView() { texImgView = createImageView(texImg, vk::Format::eR8G8B8A8Srgb); } void cleanupSwapChain() { for (auto &view : swapChainImageViews) device.destroyImageView(view); swapChainImageViews.clear(); if (swapChain) { device.destroySwapchainKHR(swapChain); swapChain = nullptr; } } void recreateSwapChain() { int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); } device.waitIdle(); vk::SwapchainKHR oldSwapchain = swapChain; for (auto &view : swapChainImageViews) device.destroyImageView(view); swapChainImageViews.clear(); createSwapChain(oldSwapchain); createImageViews(); if (oldSwapchain) { device.destroySwapchainKHR(oldSwapchain); } } #pragma endregion #pragma region cmdBufHelpers vk::CommandBuffer beginSingleTimeCommands() { vk::CommandBufferAllocateInfo allocInfo = {}; allocInfo.commandPool = cmdPool; allocInfo.level = vk::CommandBufferLevel::ePrimary; allocInfo.commandBufferCount = 1; vk::CommandBuffer commandBuffer = std::move(device.allocateCommandBuffers(allocInfo).front()); vk::CommandBufferBeginInfo beginInfo = {}; beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; commandBuffer.begin(beginInfo); return std::move(commandBuffer); } void endSingleTimeCommands(vk::CommandBuffer &&commandBuffer) { commandBuffer.end(); vk::SubmitInfo submitInfo = {}; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; gfxQueue.submit(submitInfo, nullptr); gfxQueue.waitIdle(); } #pragma endregion #pragma region recordCmdBuf void recordCmdBuf(uint32_t imgIdx) { cmdBuf.reset(); cmdBuf.begin(beginInfo); transitionImgLayout(imgIdx, vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal, {}, vk::AccessFlagBits2::eColorAttachmentWrite, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::PipelineStageFlagBits2::eColorAttachmentOutput); vk::ClearValue clearColor = vk::ClearColorValue(0.f, 0.f, 0.f, 1.f); vk::RenderingAttachmentInfo attachInfo = {}; attachInfo.imageView = swapChainImageViews[imgIdx]; attachInfo.imageLayout = vk::ImageLayout::eColorAttachmentOptimal; attachInfo.loadOp = vk::AttachmentLoadOp::eClear; attachInfo.storeOp = vk::AttachmentStoreOp::eStore; attachInfo.clearValue = clearColor; vk::RenderingInfo renderingInfo = {}; renderingInfo.renderArea = vk::Rect2D{ {0, 0}, swapChainExtent }; renderingInfo.layerCount = 1; renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &attachInfo; cmdBuf.beginRendering(renderingInfo); cmdBuf.bindPipeline(vk::PipelineBindPoint::eGraphics, gfxPipeline); cmdBuf.bindVertexBuffers(0, vertBuf, { 0 }); cmdBuf.bindIndexBuffer(idxBuf, 0, vk::IndexType::eUint16); cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, descriptorSets[frameIdx], nullptr); cmdBuf.setViewport(0, vk::Viewport(0.f, 0.f, static_cast(swapChainExtent.width), static_cast(swapChainExtent.height), 0.f, 1.f)); cmdBuf.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), swapChainExtent)); cmdBuf.drawIndexed(static_cast(indices.size()), 1, 0, 0, 0); cmdBuf.endRendering(); transitionImgLayout(imgIdx, vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::ePresentSrcKHR, vk::AccessFlagBits2::eColorAttachmentWrite, {}, vk::PipelineStageFlagBits2::eColorAttachmentOutput, vk::PipelineStageFlagBits2::eBottomOfPipe); cmdBuf.end(); } #pragma endregion #pragma region createBuffer std::pair createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags props) { vk::BufferCreateInfo bufInfo = {}; bufInfo.size = size; bufInfo.usage = usage; bufInfo.sharingMode = vk::SharingMode::eExclusive; vk::Buffer buf = device.createBuffer(bufInfo); vk::MemoryRequirements memReq = device.getBufferMemoryRequirements(buf); vk::MemoryAllocateInfo allocInfo = {}; allocInfo.allocationSize = memReq.size; allocInfo.memoryTypeIndex = findMemoryType(memReq.memoryTypeBits, props); vk::DeviceMemory alloc = device.allocateMemory(allocInfo); device.bindBufferMemory(buf, alloc, 0); return { std::move(buf), std::move(alloc) }; } void copyBuffer(vk::Buffer &srcBuf, vk::Buffer &dstBuf, vk::DeviceSize size) { vk::CommandBuffer cmdCopyBuf = beginSingleTimeCommands(); cmdCopyBuf.copyBuffer(srcBuf, dstBuf, vk::BufferCopy(0, 0, size)); endSingleTimeCommands(std::move(cmdCopyBuf)); } #pragma endregion #pragma region createImage std::pair createImage(uint32_t width, uint32_t height, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags props) { vk::ImageCreateInfo imgInfo = {}; imgInfo.imageType = vk::ImageType::e2D; imgInfo.format = format; imgInfo.extent = { { width, height, 1 } }; imgInfo.mipLevels = 1; imgInfo.arrayLayers = 1; imgInfo.samples = vk::SampleCountFlagBits::e1; imgInfo.tiling = tiling; imgInfo.usage = usage; imgInfo.sharingMode = vk::SharingMode::eExclusive; vk::Image img = device.createImage(imgInfo); vk::MemoryRequirements memReq = device.getImageMemoryRequirements(img); vk::MemoryAllocateInfo allocInfo = {}; allocInfo.allocationSize = memReq.size; allocInfo.memoryTypeIndex = findMemoryType(memReq.memoryTypeBits, props); vk::DeviceMemory alloc = device.allocateMemory(allocInfo); device.bindImageMemory(img, alloc, 0); return { std::move(img), std::move(alloc) }; } void copyBufferToImage(vk::CommandBuffer &commandBuffer, const vk::Buffer &buffer, vk::Image &image, uint32_t width, uint32_t height) { vk::ImageSubresourceLayers subresourceLayers = {}; subresourceLayers.aspectMask = vk::ImageAspectFlagBits::eColor; subresourceLayers.mipLevel = 0; subresourceLayers.baseArrayLayer = 0; subresourceLayers.layerCount = 1; vk::BufferImageCopy region = {}; region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource = subresourceLayers; region.imageOffset = {{ 0, 0, 0 }}; region.imageExtent = {{ width, height, 1 }}; commandBuffer.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, region); } void transitionImageLayout(vk::CommandBuffer &commandBuffer, const vk::Image &image, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { vk::ImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; subresourceRange.levelCount = 1; subresourceRange.layerCount = 1; vk::ImageMemoryBarrier barrier = {}; barrier.oldLayout = oldLayout; barrier.newLayout = newLayout; barrier.srcQueueFamilyIndex = vk::QueueFamilyIgnored; barrier.dstQueueFamilyIndex = vk::QueueFamilyIgnored; barrier.image = image; barrier.subresourceRange = subresourceRange; vk::PipelineStageFlags srcStage = {}; vk::PipelineStageFlags dstStage = {}; if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) { barrier.srcAccessMask = {}; barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite; srcStage = vk::PipelineStageFlagBits::eTopOfPipe; dstStage = vk::PipelineStageFlagBits::eTransfer; } else if (oldLayout == vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) { barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; srcStage = vk::PipelineStageFlagBits::eTransfer; dstStage = vk::PipelineStageFlagBits::eFragmentShader; } else { assert(false && "レイアウトは未対応です。"); return; } commandBuffer.pipelineBarrier(srcStage, dstStage, {}, {}, nullptr, barrier); } #pragma endregion int main() { #pragma region window int winWidth = 800; int winHeight = 600; std::string winName = reinterpret_cast(u8"Vulkanレンダー"); glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(winWidth, winHeight, winName.c_str(), nullptr, nullptr); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); #pragma endregion #pragma region instance vk::ApplicationInfo applicationInfo = {}; applicationInfo.pApplicationName = "Vulkan Render"; applicationInfo.applicationVersion = vk::makeVersion(1, 0, 0); applicationInfo.pEngineName = "Small Engine"; applicationInfo.engineVersion = vk::makeVersion(1, 0, 0); applicationInfo.apiVersion = vk::ApiVersion14; VULKAN_HPP_DEFAULT_DISPATCHER.init(); vk::InstanceCreateInfo instanceCreateInfo = {}; instanceCreateInfo.pApplicationInfo = &applicationInfo; uint32_t glfwExtCount = 0; const char **glfwExt = glfwGetRequiredInstanceExtensions(&glfwExtCount); std::vector ext(glfwExt, glfwExt + glfwExtCount); #ifndef NDEBUG ext.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); std::vector reqLayers = { "VK_LAYER_KHRONOS_validation" }; uint32_t layerCount; auto res1 = vk::enumerateInstanceLayerProperties(&layerCount, nullptr); std::vector availLayers(layerCount); auto res2 = vk::enumerateInstanceLayerProperties(&layerCount, availLayers.data()); bool layerFound = std::ranges::any_of(reqLayers, [&](const char *r) { return std::ranges::any_of(availLayers, [&](const auto &p) { return strcmp(r, p.layerName) == 0; }); }); if (!layerFound) { assert(false && "必要なレイヤーを見つけられませんでした。\n"); return -1; } instanceCreateInfo.enabledLayerCount = static_cast(reqLayers.size()); instanceCreateInfo.ppEnabledLayerNames = reqLayers.data(); #else instanceCreateInfo.enabledLayerCount = 0; #endif instanceCreateInfo.enabledExtensionCount = static_cast(ext.size()); instanceCreateInfo.ppEnabledExtensionNames = ext.data(); vk::Instance instance = vk::createInstance(instanceCreateInfo); VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); #ifndef NDEBUG vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError); vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags(vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); vk::DebugUtilsMessengerCreateInfoEXT debugUtilsInfo = {}; debugUtilsInfo.messageSeverity = severityFlags; debugUtilsInfo.messageType = messageTypeFlags; debugUtilsInfo.pfnUserCallback = &debugCallback; vk::DebugUtilsMessengerEXT debugMessenger = instance.createDebugUtilsMessengerEXT(debugUtilsInfo); #endif #pragma endregion #pragma region surface #if defined(_WIN64) vk::Win32SurfaceCreateInfoKHR surfaceCreateInfo = {}; surfaceCreateInfo.hinstance = GetModuleHandle(nullptr); surfaceCreateInfo.hwnd = glfwGetWin32Window(window); surface = instance.createWin32SurfaceKHR(surfaceCreateInfo); #elif defined(__APPLE__) vk::MetalSurfaceCreateInfoEXT surfaceCreateInfo = {}; surfaceCreateInfo.pLayer = glfwGetCocoaWindow(window); surface = instance.createMetalSurfaceEXT(surfaceCreateInfo); #else vk::XcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; surfaceCreateInfo.connection = glfwGetX11Display(); surfaceCreateInfo.window = glfwGetX11Window(window); surface = instance.createXcbSurfaceKHR(surfaceCreateInfo); #endif #pragma endregion #pragma region physdevice auto physDevs = instance.enumeratePhysicalDevices(); if (physDevs.empty()) { assert(false && "Vulkanを対応しているGPUを見つけられませんでした。"); return -1; } std::multimap physDevices; for (const auto &dev : physDevs) { auto devProp = dev.getProperties(); auto devFeat = dev.getFeatures(); uint32_t score = 0; if (devProp.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) score += 1000; score += devProp.limits.maxImageDimension2D; if (!devFeat.geometryShader) continue; if (devProp.apiVersion < vk::ApiVersion13) continue; physDevices.insert(std::make_pair(score, dev)); } if (physDevices.empty()) { assert(false && "利用可能なGPUを見つけられませんでした。"); return -1; } physicalDevice = physDevices.rbegin()->second; auto queueFamilies = physicalDevice.getQueueFamilyProperties(); bool supportsGfx = std::ranges::any_of(queueFamilies, [](auto const &q) { return !!(q.queueFlags & vk::QueueFlagBits::eGraphics); }); std::vector reqDevExt = { vk::KHRSwapchainExtensionName }; auto availDevExt = physicalDevice.enumerateDeviceExtensionProperties(); bool supportAllExt = std::ranges::all_of(reqDevExt, [&availDevExt](auto const &reqDevExt) { return std::ranges::any_of(availDevExt, [reqDevExt](auto const &e) { return strcmp(e.extensionName, reqDevExt) == 0;; }); }); auto features = physicalDevice.template getFeatures2(); bool supportReqFeature = features.template get().features.samplerAnisotropy && features.template get().dynamicRendering && features.template get().synchronization2 && features.template get().extendedDynamicState; if (!supportAllExt || !supportsGfx || !supportReqFeature) { assert(false && "利用可能なGPUを見つけられませんでした。"); return -1; } #pragma endregion #pragma region logicdevice std::vector queueFamProps = physicalDevice.getQueueFamilyProperties(); auto gfxQueueFamProp = std::ranges::find_if(queueFamProps, [](auto const &q) { return (q.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast(0); }); uint32_t queueIdx = ~0; for (uint32_t q = 0; q < queueFamProps.size(); ++q) { if ((queueFamProps[q].queueFlags & vk::QueueFlagBits::eGraphics) && physicalDevice.getSurfaceSupportKHR(q, surface)) { queueIdx = q; break; } } if (queueIdx == ~0) { assert(false && "グラフィックと表示塞翁しているキューを見つけられませんでした。"); return -1; } auto gfxIdx = static_cast(std::distance(queueFamProps.begin(), gfxQueueFamProp)); float queuePriority = .5f; vk::PhysicalDeviceFeatures2 baseFeatures{}; baseFeatures.features = {{ .samplerAnisotropy = true }}; vk::PhysicalDeviceVulkan11Features features11{}; features11.shaderDrawParameters = true; vk::PhysicalDeviceVulkan13Features features13{}; features13.dynamicRendering = true; features13.synchronization2 = true; vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT extDynState{}; extDynState.extendedDynamicState = true; vk::StructureChain featureChain = { baseFeatures, features11, features13, extDynState, }; vk::DeviceQueueCreateInfo devQueueCreateInfo = {}; devQueueCreateInfo.queueFamilyIndex = gfxIdx; devQueueCreateInfo.queueCount = 1; devQueueCreateInfo.pQueuePriorities = &queuePriority; vk::DeviceCreateInfo devCreateInfo = {}; devCreateInfo.pNext = &featureChain.get(); devCreateInfo.queueCreateInfoCount = 1; devCreateInfo.pQueueCreateInfos = &devQueueCreateInfo; devCreateInfo.enabledExtensionCount = static_cast(reqDevExt.size()); devCreateInfo.ppEnabledExtensionNames = reqDevExt.data(); device = physicalDevice.createDevice(devCreateInfo); VULKAN_HPP_DEFAULT_DISPATCHER.init(device); gfxQueue = device.getQueue(gfxIdx, 0); #pragma endregion #pragma region swapchain createSwapChain(); #pragma endregion #pragma region imageviews createImageViews(); #pragma endregion #pragma region descriptorSetLayout std::array bindings; { vk::DescriptorSetLayoutBinding uboLayoutBinding = {}; uboLayoutBinding.binding = 0; uboLayoutBinding.descriptorType = vk::DescriptorType::eUniformBuffer; uboLayoutBinding.descriptorCount = 1; uboLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eVertex; bindings[0] = uboLayoutBinding; } { vk::DescriptorSetLayoutBinding uboLayoutBinding = {}; uboLayoutBinding.binding = 1; uboLayoutBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler; uboLayoutBinding.descriptorCount = 1; uboLayoutBinding.stageFlags = vk::ShaderStageFlagBits::eFragment; bindings[1] = uboLayoutBinding; } vk::DescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.bindingCount = static_cast(bindings.size()); layoutInfo.pBindings = bindings.data(); descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo); #pragma endregion #pragma region gfxPipeline Shaders shader(&device); std::vector shaderStages = shader.GetShaderStages(); #pragma endregion #pragma region fixedFunctions std::vector dynStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, }; vk::PipelineVertexInputStateCreateInfo vertexInputInfo = shader.GetVertexInput(); vk::PipelineInputAssemblyStateCreateInfo inputAss = {}; inputAss.topology = vk::PrimitiveTopology::eTriangleList; vk::Viewport viewport = {}; viewport.x = 0.f; viewport.y = 0.f; viewport.width = static_cast(swapChainExtent.width); viewport.height = static_cast(swapChainExtent.height); viewport.minDepth = 0.f; viewport.maxDepth = 1.f; vk::Rect2D scissor = {}; scissor.offset = vk::Offset2D{ 0, 0 }; scissor.extent = swapChainExtent; vk::PipelineDynamicStateCreateInfo dynState = {}; dynState.dynamicStateCount = static_cast(dynStates.size()); dynState.pDynamicStates = dynStates.data(); vk::PipelineViewportStateCreateInfo viewportState = {}; viewportState.viewportCount = 1; viewportState.pViewports = &viewport; viewportState.scissorCount = 1; viewportState.pScissors = &scissor; vk::PipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.depthClampEnable = vk::False; rasterizer.rasterizerDiscardEnable = vk::False; rasterizer.polygonMode = vk::PolygonMode::eFill; rasterizer.cullMode = vk::CullModeFlagBits::eBack; rasterizer.frontFace = vk::FrontFace::eCounterClockwise; rasterizer.depthBiasEnable = vk::False; rasterizer.lineWidth = 1.f; vk::PipelineMultisampleStateCreateInfo multisampling = {}; multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; multisampling.sampleShadingEnable = vk::False; vk::PipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.blendEnable = vk::True; colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha; colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha; colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd; colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne; colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero; colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd; colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA; vk::PipelineColorBlendStateCreateInfo colorBlending = {}; colorBlending.logicOpEnable = vk::False; colorBlending.logicOp = vk::LogicOp::eCopy; colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; vk::PipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayout = device.createPipelineLayout(pipelineLayoutInfo); #pragma endregion #pragma region pipelineRendering vk::PipelineRenderingCreateInfo pipelineRenderingCreateInfo = {}; pipelineRenderingCreateInfo.colorAttachmentCount = 1; pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChainFormat.format; vk::GraphicsPipelineCreateInfo pipelineInfo = {}; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages.data(); pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAss; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = &dynState; pipelineInfo.layout = pipelineLayout; pipelineInfo.pNext = &pipelineRenderingCreateInfo; pipelineInfo.renderPass = nullptr; auto [result, pipeline] = device.createGraphicsPipeline(nullptr, pipelineInfo); if (result != vk::Result::eSuccess) { assert(false && "グラフィックパイプラインの設置にしっぱい。"); return -1; } gfxPipeline = pipeline; #pragma endregion #pragma region commandPool vk::CommandPoolCreateInfo poolInfo = {}; poolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; poolInfo.queueFamilyIndex = queueIdx; cmdPool = device.createCommandPool(poolInfo); #pragma endregion #pragma region textureImage { int texWidth, texHeight, texChan; stbi_uc *pixels = stbi_load("ass/texture/techsuwa.png", &texWidth, &texHeight, &texChan, STBI_rgb_alpha); vk::DeviceSize imgSize = texWidth * texHeight * 4; vk::MemoryPropertyFlags flags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; if (!pixels) { assert(false && "テキスチャーの読み込みに失敗。"); return -1; } auto [stagingBuf, stagingBufMem] = createBuffer(imgSize, vk::BufferUsageFlagBits::eTransferSrc, flags); void *dataStaging = device.mapMemory(stagingBufMem, 0, imgSize); memcpy(dataStaging, pixels, imgSize); device.unmapMemory(stagingBufMem); stbi_image_free(pixels); vk::Format fmt = vk::Format::eR8G8B8A8Srgb; vk::ImageTiling tile = vk::ImageTiling::eOptimal; vk::ImageUsageFlags usa = vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled; vk::MemoryPropertyFlags prop = vk::MemoryPropertyFlagBits::eDeviceLocal; std::tie(texImg, texImgMem) = createImage(texWidth, texHeight, fmt, tile, usa, prop); vk::CommandBuffer commandBuf = beginSingleTimeCommands(); transitionImageLayout(commandBuf, texImg, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); copyBufferToImage(commandBuf, stagingBuf, texImg, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(commandBuf, texImg, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); endSingleTimeCommands(std::move(commandBuf)); if (stagingBuf) device.destroyBuffer(stagingBuf); if (stagingBufMem) device.freeMemory(stagingBufMem); } createTextureImageView(); { vk::PhysicalDeviceProperties props = physicalDevice.getProperties(); vk::SamplerCreateInfo samplerInfo = {}; samplerInfo.magFilter = vk::Filter::eLinear; samplerInfo.minFilter = vk::Filter::eLinear; samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear; samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat; samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat; samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat; samplerInfo.anisotropyEnable = vk::True; samplerInfo.maxAnisotropy = props.limits.maxSamplerAnisotropy; samplerInfo.compareEnable = vk::False; samplerInfo.compareOp = vk::CompareOp::eAlways; samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack; samplerInfo.unnormalizedCoordinates = vk::False; samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear; samplerInfo.mipLodBias = 0.f; samplerInfo.minLod = 0.f; samplerInfo.maxLod = 0.f; texSampler = device.createSampler(samplerInfo); } #pragma endregion #pragma region vertexBuffer { vk::DeviceSize bufSize = sizeof(vertices[0]) * vertices.size(); vk::MemoryPropertyFlags flags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; auto [stagingBuf, stagingBufMem] = createBuffer(bufSize, vk::BufferUsageFlagBits::eTransferSrc, flags); void *dataStaging = device.mapMemory(stagingBufMem, 0, bufSize); memcpy(dataStaging, vertices.data(), bufSize); device.unmapMemory(stagingBufMem); std::tie(vertBuf, vertBufMem) = createBuffer(bufSize, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal); copyBuffer(stagingBuf, vertBuf, bufSize); if (stagingBuf) device.destroyBuffer(stagingBuf); if (stagingBufMem) device.freeMemory(stagingBufMem); } #pragma endregion #pragma region indexBuffer { vk::DeviceSize bufSize = sizeof(indices[0]) * indices.size(); vk::MemoryPropertyFlags flags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; auto [stagingBuf, stagingBufMem] = createBuffer(bufSize, vk::BufferUsageFlagBits::eTransferSrc, flags); void *dataStaging = device.mapMemory(stagingBufMem, 0, bufSize); memcpy(dataStaging, indices.data(), (size_t)bufSize); device.unmapMemory(stagingBufMem); std::tie(idxBuf, idxBufMem) = createBuffer(bufSize, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal); copyBuffer(stagingBuf, idxBuf, bufSize); if (stagingBuf) device.destroyBuffer(stagingBuf); if (stagingBufMem) device.freeMemory(stagingBufMem); } #pragma endregion #pragma region uniformBuffers for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vk::DeviceSize bufSize = sizeof(UniformBufferObject); vk::MemoryPropertyFlags flags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; auto [buf, bufMem] = createBuffer(bufSize, vk::BufferUsageFlagBits::eUniformBuffer, flags); uniformBufs.emplace_back(std::move(buf)); uniformBufsMem.emplace_back(std::move(bufMem)); uniformBufsMapped.emplace_back(device.mapMemory(uniformBufsMem.back(), 0, bufSize)); } #pragma endregion #pragma region descriptorPool std::array descPoolSize; { vk::DescriptorPoolSize ps = {}; ps.type = vk::DescriptorType::eUniformBuffer; ps.descriptorCount = MAX_FRAMES_IN_FLIGHT; descPoolSize[0] = ps; } { vk::DescriptorPoolSize ps = {}; ps.type = vk::DescriptorType::eCombinedImageSampler; ps.descriptorCount = MAX_FRAMES_IN_FLIGHT; descPoolSize[1] = ps; } vk::DescriptorPoolCreateInfo descPoolInfo = {}; descPoolInfo.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet; descPoolInfo.maxSets = MAX_FRAMES_IN_FLIGHT; descPoolInfo.poolSizeCount = static_cast(descPoolSize.size()); descPoolInfo.pPoolSizes = descPoolSize.data(); descriptorPool = device.createDescriptorPool(descPoolInfo); #pragma endregion #pragma region descriptorSets std::vector layouts{ MAX_FRAMES_IN_FLIGHT, descriptorSetLayout }; vk::DescriptorSetAllocateInfo descAllocInfo = {}; descAllocInfo.descriptorPool = descriptorPool; descAllocInfo.descriptorSetCount = static_cast(layouts.size()); descAllocInfo.pSetLayouts = layouts.data(); descriptorSets = device.allocateDescriptorSets(descAllocInfo); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vk::DescriptorBufferInfo descBufInfo = {}; descBufInfo.buffer = uniformBufs[i]; descBufInfo.offset = 0; descBufInfo.range = sizeof(UniformBufferObject); vk::DescriptorImageInfo imgInfo = {}; imgInfo.sampler = texSampler; imgInfo.imageView = texImgView; imgInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal; std::array descWrites; { vk::WriteDescriptorSet descWrite = {}; descWrite.dstSet = descriptorSets[i]; descWrite.dstBinding = 0; descWrite.dstArrayElement = 0; descWrite.descriptorCount = 1; descWrite.descriptorType = vk::DescriptorType::eUniformBuffer; descWrite.pBufferInfo = &descBufInfo; descWrites[0] = descWrite; } { vk::WriteDescriptorSet descWrite = {}; descWrite.dstSet = descriptorSets[i]; descWrite.dstBinding = 1; descWrite.dstArrayElement = 0; descWrite.descriptorCount = 1; descWrite.descriptorType = vk::DescriptorType::eCombinedImageSampler; descWrite.pImageInfo = &imgInfo; descWrites[1] = descWrite; } device.updateDescriptorSets(descWrites, {}); } #pragma endregion #pragma region commandbuffers vk::CommandBufferAllocateInfo allocInfo = {}; allocInfo.commandPool = cmdPool; allocInfo.level = vk::CommandBufferLevel::ePrimary; allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT; std::vector cmdBufs = device.allocateCommandBuffers(allocInfo); cmdBuf = cmdBufs.front(); beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; uint32_t imgIdx = 0; recordCmdBuf(imgIdx); #pragma endregion #pragma region syncObjects std::vector presentCompleteSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; assert(presentCompleteSemaphores.empty() && renderFinishedSemaphores.empty() && inFlightFences.empty()); for (size_t i = 0; i < swapChainImages.size(); ++i) { renderFinishedSemaphores.emplace_back(device.createSemaphore(vk::SemaphoreCreateInfo())); } for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { presentCompleteSemaphores.emplace_back(device.createSemaphore(vk::SemaphoreCreateInfo())); vk::FenceCreateInfo fenceInfo = {}; fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled; inFlightFences.emplace_back(device.createFence(fenceInfo)); } #pragma endregion while (!glfwWindowShouldClose(window)) { if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } glfwPollEvents(); #pragma region drawFrame if (framebufferResized) { recreateSwapChain(); framebufferResized = false; } auto fenceResult = device.waitForFences(inFlightFences[frameIdx], vk::True, UINT64_MAX); if (fenceResult != vk::Result::eSuccess) { assert(false && "フェンスまで待つに失敗。"); break; } device.resetFences(inFlightFences[frameIdx]); auto [acquireRes, imgIdx] = device.acquireNextImageKHR(swapChain, UINT64_MAX, presentCompleteSemaphores[frameIdx], nullptr); if (acquireRes == vk::Result::eErrorOutOfDateKHR) { recreateSwapChain(); framebufferResized = false; } else if (acquireRes != vk::Result::eSuccess && acquireRes != vk::Result::eSuboptimalKHR) { assert((acquireRes == vk::Result::eTimeout || acquireRes == vk::Result::eNotReady) && "次の画像の受け取りに失敗。"); break; } recordCmdBuf(imgIdx); vk::PipelineStageFlags waitStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; #pragma region updateUniformBuffer static auto startTime = std::chrono::high_resolution_clock::now(); auto curTime = std::chrono::high_resolution_clock::now(); float time = std::chrono::duration(curTime - startTime).count(); UniformBufferObject ubo = {}; ubo.model = rotate(glm::mat4(1.f), time * glm::radians(90.f), glm::vec3(0.f, 0.f, 1.f)); ubo.view = lookAt(glm::vec3(2.f, 2.f, 2.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 0.f, 1.f)); ubo.proj = glm::perspective(glm::radians(45.f), static_cast(swapChainExtent.width) / static_cast(swapChainExtent.height), .1f, 10.f); ubo.proj[1][1] *= -1; // -Y軸 memcpy(uniformBufsMapped[frameIdx], &ubo, sizeof(ubo)); #pragma endregion vk::SubmitInfo submitInfo = {}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIdx]; submitInfo.pWaitDstStageMask = &waitStage; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuf; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderFinishedSemaphores[frameIdx]; gfxQueue.submit(submitInfo, inFlightFences[frameIdx]); vk::PresentInfoKHR presentInfo = {}; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &renderFinishedSemaphores[frameIdx]; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &swapChain; presentInfo.pImageIndices = &imgIdx; vk::Result presentRes = gfxQueue.presentKHR(presentInfo); if (presentRes == vk::Result::eSuboptimalKHR || presentRes == vk::Result::eErrorOutOfDateKHR || framebufferResized) { recreateSwapChain(); framebufferResized = false; } else { assert(presentRes == vk::Result::eSuccess); } frameIdx = (frameIdx + 1) % MAX_FRAMES_IN_FLIGHT; #pragma endregion device.waitIdle(); } #pragma region destroy if (device) device.waitIdle(); if (cmdPool) device.destroyCommandPool(cmdPool); if (idxBuf) device.destroyBuffer(idxBuf); if (idxBufMem) device.freeMemory(idxBufMem); if (vertBuf) device.destroyBuffer(vertBuf); if (vertBufMem) device.freeMemory(vertBufMem); for (const auto &it : uniformBufs) device.destroyBuffer(it); for (const auto &it : uniformBufsMem) device.freeMemory(it); if (texSampler) device.destroySampler(texSampler); if (texImgView) device.destroyImageView(texImgView); if (texImg) device.destroyImage(texImg); if (texImgMem) device.freeMemory(texImgMem); if (gfxPipeline) device.destroyPipeline(gfxPipeline); if (pipelineLayout) device.destroyPipelineLayout(pipelineLayout); shader.Finalize(); if (descriptorSetLayout) device.destroyDescriptorSetLayout(descriptorSetLayout); if (descriptorPool) device.destroyDescriptorPool(descriptorPool); cleanupSwapChain(); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroySemaphore(presentCompleteSemaphores[i]); for (size_t i = 0; i < swapChainImages.size(); ++i) device.destroySemaphore(renderFinishedSemaphores[i]); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) device.destroyFence(inFlightFences[i]); if (device) device.destroy(); #ifndef NDEBUG if (debugMessenger) instance.destroyDebugUtilsMessengerEXT(debugMessenger); #endif if (surface) instance.destroySurfaceKHR(surface); if (instance) instance.destroy(); if (window) { glfwDestroyWindow(window); glfwTerminate(); } //Sleep(9 * 1000000); #pragma endregion return 0; }