void create_image_views() { swapChainImageViews.resize(swapChainImages.size(), VDeleter<VkImageView>{device, vkDestroyImageView}); for (uint32_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.format = swap_chain_image_format; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { cout<<"unable to create image views"<<endl; } } }
ImageView::ImageView(std::shared_ptr<Device> p_Device, VkImage p_Image, VkFormat p_Format) : m_Device(p_Device) , m_Handle(VK_NULL_HANDLE) { // TODO: Lots of requirements with regards to formats here etc. See spec 11.5 VkImageViewCreateInfo l_Info = {}; l_Info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; l_Info.image = p_Image; l_Info.viewType = VK_IMAGE_VIEW_TYPE_2D; l_Info.format = p_Format; // Note: Not specifying l_Info.components since COMPONENT_SWIZZLE_IDENTITY (0) is equivalent to identity mapping for each component VkImageSubresourceRange& l_SubresourceRange = l_Info.subresourceRange; if (Util::IsColorFormat(p_Format)) { l_SubresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } else if (Util::IsDepthStencilFormat(p_Format)) { l_SubresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; } else { THROW("Unsupported format for ImageView. Format=" << p_Format); } l_SubresourceRange.baseMipLevel = 0; l_SubresourceRange.levelCount = 1; l_SubresourceRange.baseArrayLayer = 0; l_SubresourceRange.layerCount = 1; VK_THROW_IF_NOT_SUCCESS(vkCreateImageView(m_Device->GetHandle(), &l_Info, nullptr, &m_Handle), "Failed to create ImageView"); LOG("ImageView created", "vulkan", Debug); }
void Window::_InitSwapchainImages() { _swapchain_images.resize( _swapchain_image_count ); _swapchain_image_views.resize( _swapchain_image_count ); ErrorCheck( vkGetSwapchainImagesKHR( _renderer->GetVulkanDevice(), _swapchain, &_swapchain_image_count, _swapchain_images.data() ) ); for( uint32_t i=0; i < _swapchain_image_count; ++i ) { VkImageViewCreateInfo image_view_create_info {}; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; image_view_create_info.image = _swapchain_images[ i ]; image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; image_view_create_info.format = _surface_format.format; image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_view_create_info.subresourceRange.baseMipLevel = 0; image_view_create_info.subresourceRange.levelCount = 1; image_view_create_info.subresourceRange.baseArrayLayer = 0; image_view_create_info.subresourceRange.layerCount = 1; ErrorCheck( vkCreateImageView( _renderer->GetVulkanDevice(), &image_view_create_info, nullptr, &_swapchain_image_views[ i ] ) ); } }
std::unique_ptr<Texture2D> Texture2D::CreateFromExistingImage(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples, VkImageViewType view_type, VkImage existing_image) { // Only need to create the image view, this is mainly for swap chains. VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, existing_image, view_type, format, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) : static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT), 0, levels, 0, layers}}; // Memory is managed by the owner of the image. VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); return nullptr; } return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type, existing_image, memory, view); }
void Image::createTexture(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkFlags requiredProps, uint32_t width, uint32_t height, uint32_t depth, VkImageLayout iLayout, uint32_t layers) { imageInfo = ImageCreateInfo(format, usage, width, height, depth, layers); imageInfo.tiling = tiling; imageInfo.initialLayout = iLayout == VK_IMAGE_LAYOUT_PREINITIALIZED ? VK_IMAGE_LAYOUT_PREINITIALIZED : VK_IMAGE_LAYOUT_UNDEFINED; OBJ_CHECK(vkCreateImage(vk, &imageInfo, NULL, &image)); VkMemoryRequirements requirements; vkGetImageMemoryRequirements(vk, image, &requirements); allocInfo = MemoryAllocateInfo(requirements, requiredProps); OBJ_CHECK(vkAllocateMemory(vk, &allocInfo, NULL, &mem)); OBJ_CHECK(vkBindImageMemory(vk, image, mem, 0)); if (iLayout != VK_IMAGE_LAYOUT_PREINITIALIZED && iLayout != VK_IMAGE_LAYOUT_UNDEFINED) { setLayout(VK_IMAGE_ASPECT_COLOR_BIT, imageInfo.initialLayout, iLayout); } if ((usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) != 0) { ImageViewCreateInfo viewInfo(image, format, VK_IMAGE_ASPECT_COLOR_BIT); if (layers == 1) { viewInfo.viewType = depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : height > 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_1D; } else { viewInfo.subresourceRange.layerCount = layers; viewInfo.viewType = height > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_1D_ARRAY; } OBJ_CHECK(vkCreateImageView(vk, &viewInfo, NULL, &view)); } }
bool VulkanCommon::CreateSwapChainImageViews() { for( size_t i = 0; i < Vulkan.SwapChain.Images.size(); ++i ) { VkImageViewCreateInfo image_view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType nullptr, // const void *pNext 0, // VkImageViewCreateFlags flags Vulkan.SwapChain.Images[i].Handle, // VkImage image VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType GetSwapChain().Format, // VkFormat format { // VkComponentMapping components VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle r VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle g VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle b VK_COMPONENT_SWIZZLE_IDENTITY // VkComponentSwizzle a }, { // VkImageSubresourceRange subresourceRange VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t baseMipLevel 1, // uint32_t levelCount 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount } }; if( vkCreateImageView( GetDevice(), &image_view_create_info, nullptr, &Vulkan.SwapChain.Images[i].ImageView ) != VK_SUCCESS ) { std::cout << "Could not create image view for framebuffer!" << std::endl; return false; } } return true; }
void VkImageTest::CreateImageView(VkImageViewCreateInfo *pCreateInfo, VkImageView *pView) { VkResult err; pCreateInfo->image = this->m_image; err = vkCreateImageView(device(), pCreateInfo, NULL, pView); ASSERT_VK_SUCCESS(err); }
void VulkanExampleBase::setupDepthStencil() { VkImageCreateInfo image = {}; image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image.pNext = NULL; image.imageType = VK_IMAGE_TYPE_2D; image.format = depthFormat; image.extent = { width, height, 1 }; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImageViewCreateInfo depthStencilView = {}; depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; depthStencilView.pNext = NULL; depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = depthFormat; depthStencilView.flags = 0; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthStencilView.subresourceRange.baseMipLevel = 0; depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; VkMemoryRequirements memReqs; VkResult err; err = vkCreateImage(device, &image, nullptr, &depthStencil.image); assert(!err); vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); mem_alloc.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); err = vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem); assert(!err); err = vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0); assert(!err); vkTools::setImageLayout( setupCmdBuffer, depthStencil.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); depthStencilView.image = depthStencil.image; err = vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view); assert(!err); }
void create_swapchain_framebuffers(const VulkanBackend& backend, PresentationInfo& presentInfo) { const size_t imgCount = presentInfo.imageCount; Assert(imgCount > 0); presentInfo.framebuffers.resize(imgCount); presentInfo.imageViews.resize(imgCount); Assert(presentInfo.renderPass != VK_NULL_HANDLE); for (size_t i = 0; i < imgCount; ++i) { VkImageViewCreateInfo image_view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType nullptr, // const void *pNext 0, // VkImageViewCreateFlags flags presentInfo.images[i], // VkImage image VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType presentInfo.surfaceFormat.format, // VkFormat format { // VkComponentMapping components VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle r VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle g VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle b VK_COMPONENT_SWIZZLE_IDENTITY // VkComponentSwizzle a }, { // VkImageSubresourceRange subresourceRange VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t baseMipLevel 1, // uint32_t levelCount 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount }}; Assert(vkCreateImageView(backend.device, &image_view_create_info, nullptr, &presentInfo.imageViews[i]) == VK_SUCCESS); VkFramebufferCreateInfo framebuffer_create_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType nullptr, // const void *pNext 0, // VkFramebufferCreateFlags flags presentInfo.renderPass, // VkRenderPass renderPass 1, // uint32_t attachmentCount &presentInfo.imageViews[i], // const VkImageView *pAttachments presentInfo.surfaceExtent.width, // uint32_t width presentInfo.surfaceExtent.height, // uint32_t height 1 // uint32_t layers }; Assert(vkCreateFramebuffer(backend.device, &framebuffer_create_info, nullptr, &presentInfo.framebuffers[i]) == VK_SUCCESS); } }
void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment *attachment) { VkImageAspectFlags aspectMask = 0; VkImageLayout imageLayout; attachment->format = format; if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) { aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } assert(aspectMask > 0); VkImageCreateInfo image = vkTools::initializers::imageCreateInfo(); image.imageType = VK_IMAGE_TYPE_2D; image.format = format; image.extent.width = offscreen.width; image.extent.height = offscreen.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; image.usage = usage | VK_IMAGE_USAGE_SAMPLED_BIT; VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &attachment->image)); vkGetImageMemoryRequirements(device, attachment->image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->mem)); VK_CHECK_RESULT(vkBindImageMemory(device, attachment->image, attachment->mem, 0)); VkImageViewCreateInfo imageView = vkTools::initializers::imageViewCreateInfo(); imageView.viewType = VK_IMAGE_VIEW_TYPE_2D; imageView.format = format; imageView.subresourceRange = {}; imageView.subresourceRange.aspectMask = aspectMask; imageView.subresourceRange.baseMipLevel = 0; imageView.subresourceRange.levelCount = 1; imageView.subresourceRange.baseArrayLayer = 0; imageView.subresourceRange.layerCount = 1; imageView.image = attachment->image; VK_CHECK_RESULT(vkCreateImageView(device, &imageView, nullptr, &attachment->view)); }
void Window::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VkImageView & imageView) { VkImageViewCreateInfo view_info {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.image = image; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = format; view_info.subresourceRange.aspectMask = aspectFlags; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; ErrorCheck(vkCreateImageView(_renderer->getDevice(), &view_info, nullptr, &imageView)); }
void Image::createDepth(uint32_t width, uint32_t height) { imageInfo = ImageCreateInfo(VK_FORMAT_D16_UNORM, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, width, height); OBJ_CHECK(vkCreateImage(vk, &imageInfo, NULL, &image)); VkMemoryRequirements requirements; vkGetImageMemoryRequirements(vk, image, &requirements); allocInfo = MemoryAllocateInfo(requirements); OBJ_CHECK(vkAllocateMemory(vk, &allocInfo, NULL, &mem)); OBJ_CHECK(vkBindImageMemory(vk, image, mem, 0)); setLayout(VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); ImageViewCreateInfo viewInfo(image, imageInfo.format, VK_IMAGE_ASPECT_DEPTH_BIT); OBJ_CHECK(vkCreateImageView(vk, &viewInfo, NULL, &view)); }
void VkApp::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, VDeleter<VkImageView>& imageView) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = format; viewInfo.subresourceRange.aspectMask = aspectFlags; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; if( vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS ){ throw std::runtime_error("failed to create texture image view!"); } }
void VulkanWindow::InitializeImageViews() { if ( VkResult result = vkGetSwapchainImagesKHR ( mVulkanRenderer.GetDevice(), mVkSwapchainKHR, &mSwapchainImageCount, nullptr ) ) { std::ostringstream stream; stream << "Get swapchain image count failed: ( " << GetVulkanResultString ( result ) << " )"; throw std::runtime_error ( stream.str().c_str() ); } mVkSwapchainImages.resize ( mSwapchainImageCount ); mVkSwapchainImageViews.resize ( mSwapchainImageCount ); if ( VkResult result = vkGetSwapchainImagesKHR ( mVulkanRenderer.GetDevice(), mVkSwapchainKHR, &mSwapchainImageCount, mVkSwapchainImages.data() ) ) { std::ostringstream stream; stream << "Get swapchain images failed: ( " << GetVulkanResultString ( result ) << " )"; throw std::runtime_error ( stream.str().c_str() ); } for ( uint32_t i = 0; i < mSwapchainImageCount; ++i ) { VkImageViewCreateInfo image_view_create_info{}; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; image_view_create_info.pNext = nullptr; image_view_create_info.flags = 0; image_view_create_info.image = mVkSwapchainImages[i]; image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; image_view_create_info.format = mVulkanRenderer.GetSurfaceFormatKHR().format; image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_view_create_info.subresourceRange.baseMipLevel = 0; image_view_create_info.subresourceRange.levelCount = 1; image_view_create_info.subresourceRange.baseArrayLayer = 0; image_view_create_info.subresourceRange.layerCount = 1; vkCreateImageView ( mVulkanRenderer.GetDevice(), &image_view_create_info, nullptr, &mVkSwapchainImageViews[i] ); } }
void VkHelper::create2DImageView(VkDevice device, VkImage img, VkFormat format, VkImageAspectFlags aspectFlags, VkImageView * imgView) { VkImageViewCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.pNext = VK_NULL_HANDLE; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.image = img; createInfo.format = format; createInfo.subresourceRange.aspectMask = aspectFlags; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; if (vkCreateImageView(device, &createInfo, nullptr, imgView)) { throw std::runtime_error("ERROR: Cannot Create Image View."); } }
void setupDepthStencil() { VkImageCreateInfo image = {}; image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image.imageType = VK_IMAGE_TYPE_2D; image.format = depthFormat; image.extent = { width, height, 1 }; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthStencil.image)); VkMemoryAllocateInfo memAlloc = {}; memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = getMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &depthStencil.mem)); VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); VkImageViewCreateInfo depthStencilView = {}; depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = depthFormat; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthStencilView.subresourceRange.baseMipLevel = 0; depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; depthStencilView.image = depthStencil.image; VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view)); }
void createImageViews() { swapChainImageViews.resize(swapChainImages.size()); for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = swapChainImages[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.format = swapChainImageFormat; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } }
// Setup the offscreen framebuffer for rendering the blurred scene // The color attachment of this framebuffer will then be used to sample frame in the fragment shader of the final pass void prepareOffscreen() { offscreenPass.width = FB_DIM; offscreenPass.height = FB_DIM; // Find a suitable depth format VkFormat fbDepthFormat; VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &fbDepthFormat); assert(validDepthFormat); // Color attachment VkImageCreateInfo image = vks::initializers::imageCreateInfo(); image.imageType = VK_IMAGE_TYPE_2D; image.format = FB_COLOR_FORMAT; image.extent.width = offscreenPass.width; image.extent.height = offscreenPass.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; // We will sample directly from the color attachment image.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.color.image)); vkGetImageMemoryRequirements(device, offscreenPass.color.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.color.mem)); VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.color.image, offscreenPass.color.mem, 0)); VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; colorImageView.format = FB_COLOR_FORMAT; colorImageView.subresourceRange = {}; colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colorImageView.subresourceRange.baseMipLevel = 0; colorImageView.subresourceRange.levelCount = 1; colorImageView.subresourceRange.baseArrayLayer = 0; colorImageView.subresourceRange.layerCount = 1; colorImageView.image = offscreenPass.color.image; VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreenPass.color.view)); // Create sampler to sample from the attachment in the fragment shader VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo(); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeV = samplerInfo.addressModeU; samplerInfo.addressModeW = samplerInfo.addressModeU; samplerInfo.mipLodBias = 0.0f; samplerInfo.maxAnisotropy = 1.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 1.0f; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &offscreenPass.sampler)); // Depth stencil attachment image.format = fbDepthFormat; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.depth.image)); vkGetImageMemoryRequirements(device, offscreenPass.depth.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.depth.mem)); VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.depth.image, offscreenPass.depth.mem, 0)); VkImageViewCreateInfo depthStencilView = vks::initializers::imageViewCreateInfo(); depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = fbDepthFormat; depthStencilView.flags = 0; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthStencilView.subresourceRange.baseMipLevel = 0; depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; depthStencilView.image = offscreenPass.depth.image; VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &offscreenPass.depth.view)); // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering std::array<VkAttachmentDescription, 2> attchmentDescriptions = {}; // Color attachment attchmentDescriptions[0].format = FB_COLOR_FORMAT; attchmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attchmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Depth attachment attchmentDescriptions[1].format = fbDepthFormat; attchmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkAttachmentReference depthReference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpassDescription = {}; subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorAttachmentCount = 1; subpassDescription.pColorAttachments = &colorReference; subpassDescription.pDepthStencilAttachment = &depthReference; // Use subpass dependencies for layout transitions std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Create the actual renderpass VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = static_cast<uint32_t>(attchmentDescriptions.size()); renderPassInfo.pAttachments = attchmentDescriptions.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpassDescription; renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size()); renderPassInfo.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreenPass.renderPass)); VkImageView attachments[2]; attachments[0] = offscreenPass.color.view; attachments[1] = offscreenPass.depth.view; VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo(); fbufCreateInfo.renderPass = offscreenPass.renderPass; fbufCreateInfo.attachmentCount = 2; fbufCreateInfo.pAttachments = attachments; fbufCreateInfo.width = offscreenPass.width; fbufCreateInfo.height = offscreenPass.height; fbufCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreenPass.frameBuffer)); // Fill a descriptor for later use in a descriptor set offscreenPass.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; offscreenPass.descriptor.imageView = offscreenPass.color.view; offscreenPass.descriptor.sampler = offscreenPass.sampler; }
bool initSwapChains() { std::cout << "initing swapchain..."; if( !getSurfaceFormats() || !getSurfacePresentModes() ) { return false; } VkResult res; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( gDevices[0], gSurface, &gSurfaceCaps ); if( res != VK_SUCCESS ) { std::cout << "error getting surface capabilities\n"; } VkExtent2D swapChainExtent = gSurfaceCaps.currentExtent; if( std::find( gPresentModes.begin(), gPresentModes.end(), VK_PRESENT_MODE_MAILBOX_KHR ) != gPresentModes.end() ) gPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; else if( std::find( gPresentModes.begin(), gPresentModes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR ) != gPresentModes.end() ) gPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; else gPresentMode = VK_PRESENT_MODE_FIFO_KHR; uint32_t desiredNumberOfSwapChainImages = gSurfaceCaps.minImageCount + 1; desiredNumberOfSwapChainImages = gSurfaceCaps.maxImageCount ? max( desiredNumberOfSwapChainImages, gSurfaceCaps.maxImageCount ) : desiredNumberOfSwapChainImages; VkSurfaceTransformFlagBitsKHR preTransform; preTransform = gSurfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : gSurfaceCaps.currentTransform; VkSwapchainCreateInfoKHR swapChain = {}; swapChain.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapChain.pNext = nullptr; swapChain.surface = gSurface; swapChain.minImageCount = desiredNumberOfSwapChainImages; swapChain.imageFormat = gFormat; swapChain.imageExtent = swapChainExtent; swapChain.preTransform = preTransform; swapChain.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapChain.imageArrayLayers = 1; swapChain.presentMode = gPresentMode; swapChain.oldSwapchain = NULL; swapChain.clipped = true; swapChain.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swapChain.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapChain.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapChain.queueFamilyIndexCount = 0; swapChain.pQueueFamilyIndices = nullptr; res = vkCreateSwapchainKHR( gDevice, &swapChain, nullptr, &gSwapchain ); if( res != VK_SUCCESS ) { std::cout << "error creating swapchain "<< res << std::endl; return false; } std::vector<VkImage> images; u32 imagesCount = 0; HR( vkGetSwapchainImagesKHR(gDevice, gSwapchain, &imagesCount, nullptr ) ); images.resize( imagesCount ); HR( vkGetSwapchainImagesKHR(gDevice, gSwapchain, &imagesCount, images.data() ) ); beginCommandBuffer( gCmd ); vkGetDeviceQueue( gDevice, gQueueFamilyIndex, 0, &gQueue ); gSwapBuffers.resize( imagesCount ); for( u32 i = 0; i < gSwapBuffers.size(); ++i ) { VkImageViewCreateInfo imageView = {}; imageView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageView.pNext = nullptr; imageView.format = gFormat; imageView.components.r = VK_COMPONENT_SWIZZLE_R; imageView.components.g = VK_COMPONENT_SWIZZLE_G; imageView.components.b = VK_COMPONENT_SWIZZLE_B; imageView.components.a = VK_COMPONENT_SWIZZLE_A; imageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageView.subresourceRange.baseMipLevel = 0; imageView.subresourceRange.levelCount = 1; imageView.subresourceRange.baseArrayLayer = 0; imageView.subresourceRange.layerCount = 1; imageView.viewType = VK_IMAGE_VIEW_TYPE_2D; imageView.flags = 0; imageView.image = images[i]; setImageLayout( gCmd, gSwapBuffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ); HR( vkCreateImageView( gDevice, &imageView, nullptr, &gSwapBuffers[i].view ) ); } endCommandBuffer( gCmd ); executeQueue( gCmd ); std::cout << "inited\n"; return true; }
int XdevLSwapChainVulkan::create(VkCommandBuffer cmdBuffer, uint32_t *width, uint32_t *height) { VkResult result; VkSwapchainKHR oldSwapchain = m_swapChain; // Get physical device surface properties and formats. VkSurfaceCapabilitiesKHR surfCaps; result = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfCaps); if(VK_SUCCESS != result) { std::cerr << "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } // ------------------------------------------------------------------------- // Get available present modes. // // Get the number of modes. uint32_t presentModeCount; result = fpGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, nullptr); if(VK_SUCCESS != result) { std::cerr << "vkGetPhysicalDeviceSurfacePresentModesKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } // Use the number of modes and create an array of modes. std::vector<VkPresentModeKHR> presentModes(presentModeCount); result = fpGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, presentModes.data()); if(VK_SUCCESS != result) { std::cerr << "vkGetPhysicalDeviceSurfacePresentModesKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } // ------------------------------------------------------------------------- VkExtent2D swapchainExtent = {}; // width and height are either both -1, or both not -1. if(surfCaps.currentExtent.width == UINT32_MAX) { // If the surface size is undefined, the size is set to // the size of the images requested. swapchainExtent.width = *width; swapchainExtent.height = *height; } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfCaps.currentExtent; *width = surfCaps.currentExtent.width; *height = surfCaps.currentExtent.height; } VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; for(size_t i = 0; i < presentModeCount; i++) { if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } // Determine the number of images uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; if((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) { desiredNumberOfSwapchainImages = surfCaps.maxImageCount; } VkSurfaceTransformFlagsKHR preTransform; if(surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { preTransform = surfCaps.currentTransform; } VkSwapchainCreateInfoKHR swapchainCI = {}; swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCI.pNext = NULL; swapchainCI.surface = m_surface; swapchainCI.minImageCount = desiredNumberOfSwapchainImages; swapchainCI.imageFormat = m_colorFormat; swapchainCI.imageColorSpace = m_colorSpace; swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; swapchainCI.imageArrayLayers = 1; swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCI.queueFamilyIndexCount = 0; swapchainCI.pQueueFamilyIndices = NULL; swapchainCI.presentMode = swapchainPresentMode; swapchainCI.oldSwapchain = oldSwapchain; swapchainCI.clipped = true; swapchainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // If we recreate a SwapChain we have to destroy the previous one. // Note: destroying the swapchain also cleans up all its associated // presentable images once the platform is done with them. if(m_oldSwapChain != VK_NULL_HANDLE) { fpDestroySwapchainKHR(m_device, m_oldSwapChain, nullptr); } result = fpCreateSwapchainKHR(m_device, &swapchainCI, nullptr, &m_swapChain); if(VK_SUCCESS != result) { std::cerr << "vkCreateSwapchainKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } result = fpGetSwapchainImagesKHR(m_device, m_swapChain, &m_imageCount, nullptr); if(VK_SUCCESS != result) { std::cerr << "vkGetSwapchainImagesKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } m_images.reserve(m_imageCount); m_images.resize(m_imageCount); result = fpGetSwapchainImagesKHR(m_device, m_swapChain, &m_imageCount, m_images.data()); if(VK_SUCCESS != result) { std::cerr << "vkGetSwapchainImagesKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } m_buffers.reserve(m_imageCount); m_buffers.resize(m_imageCount); for(uint32_t i = 0; i < m_imageCount; i++) { VkImageViewCreateInfo colorAttachmentView = {}; colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; colorAttachmentView.pNext = NULL; colorAttachmentView.format = m_colorFormat; colorAttachmentView.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colorAttachmentView.subresourceRange.baseMipLevel = 0; colorAttachmentView.subresourceRange.levelCount = 1; colorAttachmentView.subresourceRange.baseArrayLayer = 0; colorAttachmentView.subresourceRange.layerCount = 1; colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; colorAttachmentView.flags = 0; m_buffers[i].image = m_images[i]; // Transform images from initial (undefined) to present layout setImageLayout( cmdBuffer, m_buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); colorAttachmentView.image = m_buffers[i].image; result = vkCreateImageView(m_device, &colorAttachmentView, nullptr, &m_buffers[i].view); assert(!result); } return 0; }
std::unique_ptr<Texture2D> Texture2D::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples, VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage) { VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr, 0, VK_IMAGE_TYPE_2D, format, {width, height, 1}, levels, layers, samples, tiling, usage, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr, VK_IMAGE_LAYOUT_UNDEFINED}; VkImage image = VK_NULL_HANDLE; VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); return nullptr; } // Allocate memory to back this texture, we want device local memory in this case VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); VkMemoryAllocateInfo memory_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size, g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)}; VkDeviceMemory device_memory; res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); return nullptr; } res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); return nullptr; } VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, image, view_type, format, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) : static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT), 0, levels, 0, layers}}; VkImageView view = VK_NULL_HANDLE; res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); return nullptr; } return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type, image, device_memory, view); }
bool VKRenderPass::setupAttachmentImages() { VKRenderer* renderer = VKRenderer::RendererInstance; VkFormat depthFormat = renderer->GetPreferredDepthFormat(); renderer->CreateSetupCommandBuffer(); VkCommandBuffer setupCommand = renderer->GetSetupCommandBuffer(); VkResult err; //If width and height were not set, lets use the size of the screen that the renderer reports if (m_width == 0) m_width = renderer->GetWidth(); if (m_height == 0) m_height = renderer->GetHeight(); //Create an image for every output texture for (size_t i = 0; i < m_outputRenderTargets.size(); i++) { VKRenderTargetHandle vkRenderTarget = m_outputRenderTargets[i].DynamicCastHandle<VKRenderTarget>(); VkFormat colorFormat = vkRenderTarget->GetVKColorFormat(); //Attachment image that we will push back into a vector Image_vk colorImage; uint32_t width = vkRenderTarget->GetWidth(); uint32_t height = vkRenderTarget->GetHeight(); if (width == 0) width = m_width; if (height == 0) height = m_height; //Color attachment VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.pNext = nullptr; imageInfo.format = colorFormat; imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.extent = { width, height, 1 }; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageInfo.flags = 0; VkMemoryAllocateInfo memAllocInfo = {}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; VkMemoryRequirements memReqs; err = vkCreateImage(m_device, &imageInfo, nullptr, &colorImage.image); assert(!err); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error creating color image!\n"); return false; } vkGetImageMemoryRequirements(m_device, colorImage.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; renderer->MemoryTypeFromProperties(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(m_device, &memAllocInfo, nullptr, &colorImage.memory); assert(!err); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error allocating color image memory!\n"); return false; } err = vkBindImageMemory(m_device, colorImage.image, colorImage.memory, 0); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error binding color image memory!\n"); return false; } renderer->SetImageLayout(setupCommand, colorImage.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.pNext = nullptr; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = colorFormat; viewInfo.flags = 0; viewInfo.subresourceRange = {}; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; viewInfo.image = colorImage.image; err = vkCreateImageView(m_device, &viewInfo, nullptr, &colorImage.view); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error creating color image view!\n"); return false; } m_colorImages.push_back(colorImage); } //Create depth buffer VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.pNext = nullptr; imageInfo.format = depthFormat; imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.extent = { m_width, m_height, 1 }; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageInfo.flags = 0; VkMemoryAllocateInfo memAllocInfo = {}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; VkMemoryRequirements memReqs; err = vkCreateImage(m_device, &imageInfo, nullptr, &m_depthImage.image); assert(!err); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error creating color image!\n"); return false; } vkGetImageMemoryRequirements(m_device, m_depthImage.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; renderer->MemoryTypeFromProperties(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(m_device, &memAllocInfo, nullptr, &m_depthImage.memory); assert(!err); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error allocating color image memory!\n"); return false; } err = vkBindImageMemory(m_device, m_depthImage.image, m_depthImage.memory, 0); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error binding color image memory!\n"); return false; } renderer->SetImageLayout(setupCommand, m_depthImage.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.pNext = nullptr; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = depthFormat; viewInfo.flags = 0; viewInfo.subresourceRange = {}; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; viewInfo.image = m_depthImage.image; err = vkCreateImageView(m_device, &viewInfo, nullptr, &m_depthImage.view); if (err != VK_SUCCESS) { HT_DEBUG_PRINTF("VKRenderTarget::VPrepare(): Error creating color image view!\n"); return false; } renderer->FlushSetupCommandBuffer(); return true; }
void loadTextureArray(std::string filename, VkFormat format) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2DArray tex2DArray(gli::load((const char*)textureData, size)); #else gli::texture2DArray tex2DArray(gli::load(filename)); #endif assert(!tex2DArray.empty()); textureArray.width = tex2DArray.dimensions().x; textureArray.height = tex2DArray.dimensions().y; layerCount = tex2DArray.layers(); // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.extent = { textureArray.width, textureArray.height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.flags = 0; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; struct Layer { VkImage image; VkDeviceMemory memory; }; std::vector<Layer> arrayLayer; arrayLayer.resize(layerCount); // Allocate command buffer for image copies and layouts VkCommandBuffer cmdBuffer; VkCommandBufferAllocateInfo cmdBufAlllocatInfo = vkTools::initializers::commandBufferAllocateInfo( cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); VkResult err = vkAllocateCommandBuffers(device, &cmdBufAlllocatInfo, &cmdBuffer); assert(!err); VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); err = vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo); assert(!err); // Load separate cube map faces into linear tiled textures for (uint32_t i = 0; i < layerCount; ++i) { err = vkCreateImage(device, &imageCreateInfo, nullptr, &arrayLayer[i].image); assert(!err); vkGetImageMemoryRequirements(device, arrayLayer[i].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &arrayLayer[i].memory); assert(!err); err = vkBindImageMemory(device, arrayLayer[i].image, arrayLayer[i].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, arrayLayer[i].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, arrayLayer[i].memory, 0, memReqs.size, 0, &data); assert(!err); memcpy(data, tex2DArray[i].data(), tex2DArray[i].size()); vkUnmapMemory(device, arrayLayer[i].memory); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( cmdBuffer, arrayLayer[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); } // Transfer cube map faces to optimal tiling // Setup texture as blit target with optimal tiling imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.arrayLayers = layerCount; err = vkCreateImage(device, &imageCreateInfo, nullptr, &textureArray.image); assert(!err); vkGetImageMemoryRequirements(device, textureArray.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &textureArray.deviceMemory); assert(!err); err = vkBindImageMemory(device, textureArray.image, textureArray.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Set initial layout for all array layers of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = layerCount; vkTools::setImageLayout( cmdBuffer, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy cube map faces one by one for (uint32_t i = 0; i < layerCount; ++i) { // Copy region for image blit VkImageCopy copyRegion = {}; copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.srcSubresource.baseArrayLayer = 0; copyRegion.srcSubresource.mipLevel = 0; copyRegion.srcSubresource.layerCount = 1; copyRegion.srcOffset = { 0, 0, 0 }; copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.dstSubresource.baseArrayLayer = i; copyRegion.dstSubresource.mipLevel = 0; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = textureArray.width; copyRegion.extent.height = textureArray.height; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( cmdBuffer, arrayLayer[i].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, textureArray.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); } // Change texture image layout to shader read after all layers have been copied textureArray.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( cmdBuffer, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, textureArray.imageLayout, subresourceRange); err = vkEndCommandBuffer(cmdBuffer); assert(!err); VkFence nullFence = { VK_NULL_HANDLE }; // Submit command buffer to graphis queue VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; err = vkQueueSubmit(queue, 1, &submitInfo, nullFence); assert(!err); err = vkQueueWaitIdle(queue); assert(!err); // Create sampler VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 8; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &textureArray.sampler); assert(!err); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; view.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; view.subresourceRange.layerCount = layerCount; view.image = textureArray.image; err = vkCreateImageView(device, &view, nullptr, &textureArray.view); assert(!err); // Cleanup for (auto& layer : arrayLayer) { vkDestroyImage(device, layer.image, nullptr); vkFreeMemory(device, layer.memory, nullptr); } }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Input Attachment Sample"; const bool depthPresent = false; const bool vertexPresent = false; process_command_line_args(info, argc, argv); init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R8G8B8A8_UNORM, &props); if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { std::cout << "VK_FORMAT_R8G8B8A8_UNORM format unsupported for input " "attachment\n"; exit(-1); } init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info); /* VULKAN_KEY_START */ // Create a framebuffer with 2 attachments, one the color attachment // the shaders render into, and the other an input attachment which // will be cleared to yellow, and then used by the shaders to color // the drawn triangle. Final result should be a yellow triangle // Create the image that will be used as the input attachment // The image for the color attachment is the presentable image already // created in init_swapchain() VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.pNext = NULL; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = info.format; image_create_info.extent.width = info.width; image_create_info.extent.height = info.height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = NUM_SAMPLES; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImage input_image; VkDeviceMemory input_memory; res = vkCreateImage(info.device, &image_create_info, NULL, &input_image); assert(res == VK_SUCCESS); VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(info.device, input_image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); assert(pass); res = vkAllocateMemory(info.device, &mem_alloc, NULL, &input_memory); assert(res == VK_SUCCESS); res = vkBindImageMemory(info.device, input_image, input_memory, 0); assert(res == VK_SUCCESS); // Set the image layout to TRANSFER_DST_OPTIMAL to be ready for clear set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); VkImageSubresourceRange srRange = {}; srRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srRange.baseMipLevel = 0; srRange.levelCount = VK_REMAINING_MIP_LEVELS; srRange.baseArrayLayer = 0; srRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkClearColorValue clear_color; clear_color.float32[0] = 1.0f; clear_color.float32[1] = 1.0f; clear_color.float32[2] = 0.0f; clear_color.float32[3] = 0.0f; // Clear the input attachment image to yellow vkCmdClearColorImage(info.cmd, input_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &srRange); // Set the image layout to SHADER_READONLY_OPTIMAL for use by the shaders set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.image = VK_NULL_HANDLE; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = info.format; view_info.components.r = VK_COMPONENT_SWIZZLE_R; view_info.components.g = VK_COMPONENT_SWIZZLE_G; view_info.components.b = VK_COMPONENT_SWIZZLE_B; view_info.components.a = VK_COMPONENT_SWIZZLE_A; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; VkImageView input_attachment_view; view_info.image = input_image; res = vkCreateImageView(info.device, &view_info, NULL, &input_attachment_view); assert(res == VK_SUCCESS); VkDescriptorImageInfo input_image_info = {}; input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; input_image_info.imageView = input_attachment_view; input_image_info.sampler = VK_NULL_HANDLE; VkDescriptorSetLayoutBinding layout_bindings[1]; layout_bindings[0].binding = 0; layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; layout_bindings[0].descriptorCount = 1; layout_bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; layout_bindings[0].pImmutableSamplers = NULL; VkDescriptorSetLayoutCreateInfo descriptor_layout = {}; descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptor_layout.pNext = NULL; descriptor_layout.bindingCount = 1; descriptor_layout.pBindings = layout_bindings; info.desc_layout.resize(NUM_DESCRIPTOR_SETS); res = vkCreateDescriptorSetLayout(info.device, &descriptor_layout, NULL, info.desc_layout.data()); assert(res == VK_SUCCESS); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = 0; pPipelineLayoutCreateInfo.pPushConstantRanges = NULL; pPipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS; pPipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data(); res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout); assert(res == VK_SUCCESS); // First attachment is the color attachment - clear at the beginning of the // renderpass and transition layout to PRESENT_SRC_KHR at the end of // renderpass VkAttachmentDescription attachments[2]; attachments[0].format = info.format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; attachments[0].flags = 0; // Second attachment is input attachment. Once cleared it should have // width*height yellow pixels. Doing a subpassLoad in the fragment shader // should give the shader the color at the fragments x,y location // from the input attachment attachments[1].format = info.format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].flags = 0; VkAttachmentReference color_reference = {}; color_reference.attachment = 0; color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference input_reference = {}; input_reference.attachment = 1; input_reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.flags = 0; subpass.inputAttachmentCount = 1; subpass.pInputAttachments = &input_reference; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_reference; subpass.pResolveAttachments = NULL; subpass.pDepthStencilAttachment = NULL; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = NULL; VkRenderPassCreateInfo rp_info = {}; rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; rp_info.pNext = NULL; rp_info.attachmentCount = 2; rp_info.pAttachments = attachments; rp_info.subpassCount = 1; rp_info.pSubpasses = &subpass; rp_info.dependencyCount = 0; rp_info.pDependencies = NULL; res = vkCreateRenderPass(info.device, &rp_info, NULL, &info.render_pass); assert(!res); init_shaders(info, vertShaderText, fragShaderText); VkImageView fb_attachments[2]; fb_attachments[1] = input_attachment_view; VkFramebufferCreateInfo fbc_info = {}; fbc_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbc_info.pNext = NULL; fbc_info.renderPass = info.render_pass; fbc_info.attachmentCount = 2; fbc_info.pAttachments = fb_attachments; fbc_info.width = info.width; fbc_info.height = info.height; fbc_info.layers = 1; uint32_t i; info.framebuffers = (VkFramebuffer *)malloc(info.swapchainImageCount * sizeof(VkFramebuffer)); for (i = 0; i < info.swapchainImageCount; i++) { fb_attachments[0] = info.buffers[i].view; res = vkCreateFramebuffer(info.device, &fbc_info, NULL, &info.framebuffers[i]); assert(res == VK_SUCCESS); } VkDescriptorPoolSize type_count[1]; type_count[0].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; type_count[0].descriptorCount = 1; VkDescriptorPoolCreateInfo descriptor_pool = {}; descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool.pNext = NULL; descriptor_pool.maxSets = 1; descriptor_pool.poolSizeCount = 1; descriptor_pool.pPoolSizes = type_count; res = vkCreateDescriptorPool(info.device, &descriptor_pool, NULL, &info.desc_pool); assert(res == VK_SUCCESS); VkDescriptorSetAllocateInfo desc_alloc_info[1]; desc_alloc_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; desc_alloc_info[0].pNext = NULL; desc_alloc_info[0].descriptorPool = info.desc_pool; desc_alloc_info[0].descriptorSetCount = 1; desc_alloc_info[0].pSetLayouts = info.desc_layout.data(); info.desc_set.resize(1); res = vkAllocateDescriptorSets(info.device, desc_alloc_info, info.desc_set.data()); assert(res == VK_SUCCESS); VkWriteDescriptorSet writes[1]; // Write descriptor set with one write describing input attachment writes[0] = {}; writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = info.desc_set[0]; writes[0].dstBinding = 0; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; writes[0].pImageInfo = &input_image_info; writes[0].pBufferInfo = nullptr; writes[0].pTexelBufferView = nullptr; writes[0].dstArrayElement = 0; vkUpdateDescriptorSets(info.device, 1, writes, 0, NULL); init_pipeline_cache(info); init_pipeline(info, depthPresent, vertexPresent); // Color attachment clear to gray VkClearValue clear_values; clear_values.color.float32[0] = 0.2f; clear_values.color.float32[1] = 0.2f; clear_values.color.float32[2] = 0.2f; clear_values.color.float32[3] = 0.2f; VkSemaphoreCreateInfo imageAcquiredSemaphoreCreateInfo; imageAcquiredSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; imageAcquiredSemaphoreCreateInfo.pNext = NULL; imageAcquiredSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &imageAcquiredSemaphoreCreateInfo, NULL, &info.imageAcquiredSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.imageAcquiredSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); VkRenderPassBeginInfo rp_begin; rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_begin.pNext = NULL; rp_begin.renderPass = info.render_pass; rp_begin.framebuffer = info.framebuffers[info.current_buffer]; rp_begin.renderArea.offset.x = 0; rp_begin.renderArea.offset.y = 0; rp_begin.renderArea.extent.width = info.width; rp_begin.renderArea.extent.height = info.height; rp_begin.clearValueCount = 1; rp_begin.pClearValues = &clear_values; vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline); vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL); init_viewports(info); init_scissors(info); vkCmdDraw(info.cmd, 3, 1, 0, 0); vkCmdEndRenderPass(info.cmd); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); /* VULKAN_KEY_END */ const VkCommandBuffer cmd_bufs[] = {info.cmd}; VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); execute_queue_cmdbuf(info, cmd_bufs, drawFence); do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, drawFence, NULL); execute_present_image(info); wait_seconds(1); if (info.save_images) write_ppm(info, "input_attachment"); vkDestroySemaphore(info.device, info.imageAcquiredSemaphore, NULL); vkDestroyImageView(info.device, input_attachment_view, NULL); vkDestroyImage(info.device, input_image, NULL); vkFreeMemory(info.device, input_memory, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_descriptor_pool(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); destroy_descriptor_and_pipeline_layouts(info); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
void loadCubemap(std::string filename, VkFormat format, bool forceLinearTiling) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture_cube texCube(gli::load((const char*)textureData, size)); #else gli::texture_cube texCube(gli::load(filename)); #endif assert(!texCube.empty()); cubeMap.width = texCube.extent().x; cubeMap.height = texCube.extent().y; cubeMap.mipLevels = texCube.levels(); VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); bufferCreateInfo.size = texCube.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, texCube.data(), texCube.size()); vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = cubeMap.mipLevels; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { cubeMap.width, cubeMap.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // Cube faces count as array layers in Vulkan imageCreateInfo.arrayLayers = 6; // This flag is required for cube map images imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &cubeMap.image)); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeMap.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Setup buffer copy regions for each face including all of it's miplevels std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; for (uint32_t face = 0; face < 6; face++) { for (uint32_t level = 0; level < cubeMap.mipLevels; level++) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texCube[face][level].extent().x; bufferCopyRegion.imageExtent.height = texCube[face][level].extent().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += texCube[face][level].size(); } } // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = cubeMap.mipLevels; subresourceRange.layerCount = 6; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data() ); // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cubeMap.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = cubeMap.mipLevels; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sampler.maxAnisotropy = 1.0f; if (vulkanDevice->features.samplerAnisotropy) { sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = VK_TRUE; } VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &cubeMap.sampler)); // Create image view VkImageViewCreateInfo view = vks::initializers::imageViewCreateInfo(); // Cube map view type view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; // 6 array layers (faces) view.subresourceRange.layerCount = 6; // Set number of mip levels view.subresourceRange.levelCount = cubeMap.mipLevels; view.image = cubeMap.image; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &cubeMap.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); }
void VkeCubeTexture::loadCubeDDS(const char *inFile){ std::string searchPaths[] = { std::string(PROJECT_NAME), NVPWindow::sysExePath() + std::string(PROJECT_RELDIRECTORY), std::string(PROJECT_ABSDIRECTORY) }; nv_dds::CDDSImage ddsImage; for (uint32_t i = 0; i < 3; ++i){ std::string separator = ""; uint32_t strSize = searchPaths[i].size(); if(searchPaths[i].substr(strSize-1,strSize) != "/") separator = "/"; std::string filePath = searchPaths[i] + separator + std::string("images/") + std::string(inFile); ddsImage.load(filePath, true); if (ddsImage.is_valid()) break; } if (!ddsImage.is_valid()){ perror("Could not cube load texture image.\n"); exit(1); } uint32_t imgW = ddsImage.get_width(); uint32_t imgH = ddsImage.get_height(); uint32_t comCount = ddsImage.get_components(); uint32_t fmt = ddsImage.get_format(); bool isCube = ddsImage.is_cubemap(); bool isComp = ddsImage.is_compressed(); VkFormat vkFmt = VK_FORMAT_R8G8B8A8_UNORM; switch (fmt){ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: vkFmt = VK_FORMAT_BC1_RGB_SRGB_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: vkFmt = VK_FORMAT_BC2_UNORM_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: vkFmt = VK_FORMAT_BC3_UNORM_BLOCK; break; default: break; } m_width = imgW; m_height = imgH; m_format = vkFmt; VulkanDC::Device::Queue::Name queueName = "DEFAULT_GRAPHICS_QUEUE"; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID; VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VulkanDC::Device::Queue *queue = device->getQueue(queueName); VkCommandBuffer cmd = VK_NULL_HANDLE; queue->beginCommandBuffer(cmdID, &cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); imageCreateAndBind( &m_data.image, &m_data.memory, m_format, VK_IMAGE_TYPE_2D, m_width, m_height, 1, 6, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VkImageUsageFlagBits)(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), VK_IMAGE_TILING_OPTIMAL); VkBuffer cubeMapBuffer; VkDeviceMemory cubeMapMem; bufferCreate(&cubeMapBuffer, m_width*m_height * 3 * 6, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); bufferAlloc(&cubeMapBuffer, &cubeMapMem, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); if (m_memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT){ imageSetLayoutBarrier(cmdID, queueName, m_data.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL); for (uint32_t i = 0; i < 6; ++i){ void *data = NULL; VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = i; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(), cubeMapMem, layout.offset, layout.size, 0, &data), "Could not map memory for image.\n"); const nv_dds::CTexture &mipmap = ddsImage.get_cubemap_face(i); memcpy(data, (void *)mipmap, layout.size); vkUnmapMemory(getDefaultDevice(), cubeMapMem); } VkBufferImageCopy biCpyRgn[6]; for (uint32_t k = 0; k < 6; ++k){ VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = k; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); biCpyRgn[k].bufferOffset = layout.offset; biCpyRgn[k].bufferImageHeight = 0; biCpyRgn[k].bufferRowLength = 0; biCpyRgn[k].imageExtent.width = m_width; biCpyRgn[k].imageExtent.height = m_height; biCpyRgn[k].imageExtent.depth = 1; biCpyRgn[k].imageOffset.x = 0; biCpyRgn[k].imageOffset.y = 0; biCpyRgn[k].imageOffset.z = 0; biCpyRgn[k].imageSubresource.baseArrayLayer = k; biCpyRgn[k].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; biCpyRgn[k].imageSubresource.layerCount = 1; biCpyRgn[k].imageSubresource.mipLevel = 0; } VkFence copyFence; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(fenceInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(device->getVKDevice(), &fenceInfo, NULL, ©Fence); vkCmdCopyBufferToImage(cmd, cubeMapBuffer, m_data.image, m_data.imageLayout, 6, biCpyRgn); queue->flushCommandBuffer(cmdID, ©Fence); vkWaitForFences(device->getVKDevice(), 1, ©Fence, VK_TRUE, 100000000000); vkDestroyBuffer(device->getVKDevice(), cubeMapBuffer, NULL); vkFreeMemory(device->getVKDevice(), cubeMapMem, NULL); } VkSamplerCreateInfo sampler; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler.pNext = NULL; sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 1; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VkImageViewCreateInfo view; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = m_format; view.components.r = VK_COMPONENT_SWIZZLE_R; view.components.g = VK_COMPONENT_SWIZZLE_G; view.components.b = VK_COMPONENT_SWIZZLE_B; view.components.a = VK_COMPONENT_SWIZZLE_A; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.layerCount = 1; VKA_CHECK_ERROR(vkCreateSampler(getDefaultDevice(), &sampler, NULL, &m_data.sampler), "Could not create sampler for image texture.\n"); view.image = m_data.image; VKA_CHECK_ERROR(vkCreateImageView(getDefaultDevice(), &view, NULL, &m_data.view), "Could not create image view for texture.\n"); }
int main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Swapchain Initialization Sample"; /* * Set up swapchain: * - Get supported uses for all queues * - Try to find a queue that supports both graphics and present * - If no queue supports both, find a present queue and make sure we have a * graphics queue * - Get a list of supported formats and use the first one * - Get surface properties and present modes and use them to create a swap * chain * - Create swap chain buffers * - For each buffer, create a color attachment view and set its layout to * color attachment */ init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_connection(info); init_window_size(info, 50, 50); init_window(info); /* VULKAN_KEY_START */ // Construct the surface description: #ifdef _WIN32 VkWin32SurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; createInfo.pNext = NULL; createInfo.hinstance = info.connection; createInfo.hwnd = info.window; res = vkCreateWin32SurfaceKHR(info.inst, &createInfo, NULL, &info.surface); #else // _WIN32 VkXcbSurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; createInfo.pNext = NULL; createInfo.connection = info.connection; createInfo.window = info.window; res = vkCreateXcbSurfaceKHR(info.inst, &createInfo, NULL, &info.surface); #endif // _WIN32 assert(res == VK_SUCCESS); // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = (VkBool32 *)malloc(info.queue_count * sizeof(VkBool32)); for (uint32_t i = 0; i < info.queue_count; i++) { vkGetPhysicalDeviceSurfaceSupportKHR(info.gpus[0], i, info.surface, &supportsPresent[i]); } // Search for a graphics queue and a present queue in the array of queue // families, try to find one that supports both uint32_t graphicsQueueNodeIndex = UINT32_MAX; for (uint32_t i = 0; i < info.queue_count; i++) { if ((info.queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { if (supportsPresent[i] == VK_TRUE) { graphicsQueueNodeIndex = i; break; } } } free(supportsPresent); // Generate error if could not find a queue that supports both a graphics // and present if (graphicsQueueNodeIndex == UINT32_MAX) { std::cout << "Could not find a queue that supports both graphics and " "present\n"; exit(-1); } info.graphics_queue_family_index = graphicsQueueNodeIndex; init_device(info); // Get the list of VkFormats that are supported: uint32_t formatCount; res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, NULL); assert(res == VK_SUCCESS); VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); res = vkGetPhysicalDeviceSurfaceFormatsKHR(info.gpus[0], info.surface, &formatCount, surfFormats); assert(res == VK_SUCCESS); // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { info.format = VK_FORMAT_B8G8R8A8_UNORM; } else { assert(formatCount >= 1); info.format = surfFormats[0].format; } VkSurfaceCapabilitiesKHR surfCapabilities; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.gpus[0], info.surface, &surfCapabilities); assert(res == VK_SUCCESS); uint32_t presentModeCount; res = vkGetPhysicalDeviceSurfacePresentModesKHR(info.gpus[0], info.surface, &presentModeCount, NULL); assert(res == VK_SUCCESS); VkPresentModeKHR *presentModes = (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); res = vkGetPhysicalDeviceSurfacePresentModesKHR( info.gpus[0], info.surface, &presentModeCount, presentModes); assert(res == VK_SUCCESS); VkExtent2D swapChainExtent; // width and height are either both -1, or both not -1. if (surfCapabilities.currentExtent.width == (uint32_t)-1) { // If the surface size is undefined, the size is set to // the size of the images requested. swapChainExtent.width = info.width; swapChainExtent.height = info.height; } else { // If the surface size is defined, the swap chain size must match swapChainExtent = surfCapabilities.currentExtent; } // If mailbox mode is available, use it, as is the lowest-latency non- // tearing mode. If not, try IMMEDIATE which will usually be available, // and is fastest (though it tears). If not, fall back to FIFO which is // always available. VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; for (size_t i = 0; i < presentModeCount; i++) { if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } // Determine the number of VkImage's to use in the swap chain (we desire to // own only 1 image at a time, besides the images being displayed and // queued for display): uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount + 1; if ((surfCapabilities.maxImageCount > 0) && (desiredNumberOfSwapChainImages > surfCapabilities.maxImageCount)) { // Application must settle for fewer images than desired: desiredNumberOfSwapChainImages = surfCapabilities.maxImageCount; } VkSurfaceTransformFlagBitsKHR preTransform; if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } else { preTransform = surfCapabilities.currentTransform; } VkSwapchainCreateInfoKHR swap_chain = {}; swap_chain.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swap_chain.pNext = NULL; swap_chain.surface = info.surface; swap_chain.minImageCount = desiredNumberOfSwapChainImages; swap_chain.imageFormat = info.format; swap_chain.imageExtent.width = swapChainExtent.width; swap_chain.imageExtent.height = swapChainExtent.height; swap_chain.preTransform = preTransform; swap_chain.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swap_chain.imageArrayLayers = 1; swap_chain.presentMode = swapchainPresentMode; swap_chain.oldSwapchain = NULL; swap_chain.clipped = true; swap_chain.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swap_chain.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swap_chain.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swap_chain.queueFamilyIndexCount = 0; swap_chain.pQueueFamilyIndices = NULL; res = vkCreateSwapchainKHR(info.device, &swap_chain, NULL, &info.swap_chain); assert(res == VK_SUCCESS); res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, NULL); assert(res == VK_SUCCESS); VkImage *swapchainImages = (VkImage *)malloc(info.swapchainImageCount * sizeof(VkImage)); assert(swapchainImages); res = vkGetSwapchainImagesKHR(info.device, info.swap_chain, &info.swapchainImageCount, swapchainImages); assert(res == VK_SUCCESS); info.buffers.resize(info.swapchainImageCount); // Going to need a command buffer to send the memory barriers in // set_image_layout but we couldn't have created one before we knew // what our graphics_queue_family_index is, but now that we have it, // create the command buffer init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); vkGetDeviceQueue(info.device, info.graphics_queue_family_index, 0, &info.queue); for (uint32_t i = 0; i < info.swapchainImageCount; i++) { VkImageViewCreateInfo color_image_view = {}; color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; color_image_view.pNext = NULL; color_image_view.format = info.format; color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; color_image_view.subresourceRange.baseMipLevel = 0; color_image_view.subresourceRange.levelCount = 1; color_image_view.subresourceRange.baseArrayLayer = 0; color_image_view.subresourceRange.layerCount = 1; color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; color_image_view.flags = 0; info.buffers[i].image = swapchainImages[i]; set_image_layout(info, info.buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); color_image_view.image = info.buffers[i].image; res = vkCreateImageView(info.device, &color_image_view, NULL, &info.buffers[i].view); assert(res == VK_SUCCESS); } free(swapchainImages); execute_end_command_buffer(info); execute_queue_command_buffer(info); /* VULKAN_KEY_END */ /* Clean Up */ VkCommandBuffer cmd_bufs[1] = {info.cmd}; vkFreeCommandBuffers(info.device, info.cmd_pool, 1, cmd_bufs); vkDestroyCommandPool(info.device, info.cmd_pool, NULL); for (uint32_t i = 0; i < info.swapchainImageCount; i++) { vkDestroyImageView(info.device, info.buffers[i].view, NULL); } vkDestroySwapchainKHR(info.device, info.swap_chain, NULL); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
tut1_error tut7_create_images(struct tut1_physical_device *phy_dev, struct tut2_device *dev, struct tut7_image *images, uint32_t image_count) { /* * In this function, we will create a bunch of images. Images in graphics serve essentially two purposes. One * is to provide data to shaders, traditionally known as textures. Another is to render into either as the * final result or for further use, traditionally also known as textures. Vulkan calls all of these "images", * which are just glorified "buffers". We already worked with a Vulkan buffer in Tutorial 4, which was just an * array of data. Images on the other hand can have up to 3 dimensions, a format (such as BGRA), multisampling * properties, tiling properties and a layout. They are glorified buffers because all of these features can be * emulated with buffers, although besides requiring more work in the shaders, using images also allows a lot * more optimization by the device and its driver. * * That said, creating images is fairly similar to buffers. You create an image, allocate memory to it, create * an image view for access to the image, you bind it to a command buffer through a descriptor set and go on * using it in the shaders. Like buffers, you can choose to initialize the image. The data sent through * images could be anything, such as textures used to draw objects, patterns used by a shader to apply an * effect, or just general data for the shaders. The image can be written to as well. The data written to an * image could also be for anything, such as the final colors that go on to be displayed on the screen, the * depth or stencil data, the output of a filter used for further processing, the processed image to be * retrieved by the application (e.g. used by gimp), a texture that evolves over time, etc. * * Loading the image data is outside the scope of this tutorial, so we'll leave that for another time. Once * the image is created, it's device memory can be mapped, loaded and unmapped, so it is not necessary to do * that in this function either. */ uint32_t successful = 0; tut1_error retval = TUT1_ERROR_NONE; VkResult res; for (uint32_t i = 0; i < image_count; ++i) { images[i].image = NULL; images[i].image_mem = NULL; images[i].view = NULL; images[i].sampler = NULL; /* * To create an image, we need a CreateInfo struct as usual. Some parts of this struct is similar to * VkBufferCreateInfo from Tutorial 3. The ones that need explanation are explained here. The image * type specifies what are the dimensions of the image. In these tutorial series, we will use 2D * images for simplicity. Also for simplicity, let's ignore mipmapping and image layers. The image * format is one of VK_FORMAT. A normal format could be VK_FORMAT_B8G8R8A8_UNORM, but it might make * sense to use other formats, especially for images that would get their data from a texture file. * * If the image is going to be initialized, for example from a texture file, then the structure of the * image data, otherwise known as "tiling", must be set to linear. This means that the image is stored * as a normal row-major array, so that when its memory is mapped by the application, it would make * sense! If the image is not to be initialized on the other hand, it is better to keep the tiling as * optimal, which means whatever format the GPU likes best. It is necessary for the application to * copy a linear image to an optimal one for GPU usage. If the application wants to read the image * back, it must copy it from an optimal image to a linear one. This also means that the `usage` of * the linear images can contain only TRANSFER_SRC and TRANSFER_DST bits. More on image copies when we * actually start using them. * * Linear images are sampled only once. Optimal images can be multisampled. You can read about * multisampling online (from OpenGL), but in short it asks for each pixel to be sampled at multiple * locations inside the pixel, which helps with antialiasing. Here, we will simply choose a higher * number of samples as allowed by the GPU (retrieved with vkGetPhysicalDeviceImageFormatProperties * below). * * Linear images are also restricted to 2D, no mipmapping and no layers, which is fortunate because we * wanted those for simplicity anyway! There is also a restriction on the format of the image, which * cannot be depth/stencil. * * In Tutorial 3, we specified the buffer usage as storage, which was a rather generic specification. * For the image, we have more options to specify the usage. Choosing the minimum usage bits for each * image, specifying only what we actually want to do with the image allows the GPU to possibly place * the image in the most optimal memory location, or load/unload the image at necessary times. This is * left to the application to provide as it varies from case to case. The usages in short are: * * Transfer src/dst: whether the image can be used as a source/destination of an image copy. * Sampled: whether the image can be sampled by a shader. * Storage: whether the image can be read from and written to by a shader. * Color attachment: whether the image can be used as a render target (for color). * Depth/stencil attachment: whether the image can be used as a render target (for depth/stencil). * Transient attachment: whether the image is lazily allocated (ignored for now). * Input attachment: whether the image can be read (unfiltered) by a shader (ignored for now). * * If the image was to be shared between queue families, it should be declared with a special * `sharingMode` specifying that there would be concurrent access to the image, and by which queue * families. We are going to use the images and views created here in multiple pipelines, one for each * swapchain image. Since those pipelines may be created on top of different queue families, we need * to tell Vulkan that these images would be shared. At the time of this writing, on Nvidia cards * there is only one queue family and sharing is meaningless. However, it is legal for a driver to * expose multiple similar queue families instead of one queue family with multiple queues. The * application is expected to provide the queue families that would use this image. Most likely, the * result of `tut7_get_presentable_queues` is what you would want. * * Finally, an image has a layout. Each layout is limited in what operations can be done in it, but * instead is optimal for a task. The possible image layouts are: * * Undefined: no device access is allowed. This is used as an initial layout and must be transitioned * away from before use. * Preinitialized: no device access is allowed. Similar to undefined, this is only an initial layout * and must be transitioned away from before use. The only difference is that the contents of the * image are kept during the transition. * General: supports all types of device access. * Color attachment optimal: only usable with color attachment images. * Depth/stencil attachment optimal: only usable with depth/stencil attachment images. * Depth/stencil read-only optimal: only usable with depth/stencil attachment images. The difference * between this and the depth/stencil attachment optimal layout is that this image can also be used * as a read-only sampled image or input attachment for use by the shaders. * Shader read-only optimal: only usable with sampled and input attachment images. Similar to * depth/stencil read-only optimal, this layout can be used as a read-only image or input attachment * for use by the shaders. * Transfer src/dst optimal: only usable with transfer src/dst images and must only be used as the * source or destination of an image transfer. * Present src (extension): used for presenting an image to a swapchain. An image taken from the * swapchain is in this layout and must be transitioned away before use after * vkAcquireNextImageKHR. Before giving the image back with vkQueuePresentKHR, it must be * transitioned again to this layout. * * For linear images, which are going to be initialized by the application, we will use the * preinitialized layout. Otherwise, the layout must be undefined and later transitioned to the * desired layout using a pipeline barrier (more on this later). */ VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; if (images[i].will_be_initialized || images[i].host_visible) { images[i].usage &= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; layout = VK_IMAGE_LAYOUT_PREINITIALIZED; tiling = VK_IMAGE_TILING_LINEAR; } else if (images[i].multisample) { /* * To get the format properties for an image, we need to tell Vulkan how we expect to create the image, * i.e. what is its format, type, tiling, usage and flags (which we didn't use). We could check many * of the parameters given to this function with the properties returned from this function, but we'll * just take a possible sampling count out of it, and assume the parameters are fine. In a real * application, you would want to do more validity checks. */ VkImageFormatProperties format_properties; res = vkGetPhysicalDeviceImageFormatProperties(phy_dev->physical_device, images[i].format, VK_IMAGE_TYPE_2D, tiling, images[i].usage, 0, &format_properties); tut1_error_sub_set_vkresult(&retval, res); if (res == 0) { for (uint32_t s = VK_SAMPLE_COUNT_16_BIT; s != 0; s >>= 1) if ((format_properties.sampleCounts & s)) { samples = s; break; } } } /* * Create the image with the above description as usual. The CreateInfo struct takes the parameters * and memory allocation callbacks are not used. */ bool shared = images[i].sharing_queue_count > 1; struct VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = images[i].format, .extent = {images[i].extent.width, images[i].extent.height, 1}, .mipLevels = 1, .arrayLayers = 1, .samples = samples, .tiling = tiling, .usage = images[i].usage, .sharingMode = shared?VK_SHARING_MODE_CONCURRENT:VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = shared?images[i].sharing_queue_count:0, .pQueueFamilyIndices = shared?images[i].sharing_queues:NULL, .initialLayout = layout, }; res = vkCreateImage(dev->device, &image_info, NULL, &images[i].image); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; /* * In Tutorial 4, we created a buffer, allocated memory for it and bound the memory to the buffer. * Images are glorified buffers and the process is similar. The same argument regarding host-coherent * memory holds here as well. So, if the image requires device-local memory, we will look for that, * otherwise we will look for memory that is not just host-visible, but also host-coherent. */ VkMemoryRequirements mem_req = {0}; vkGetImageMemoryRequirements(dev->device, images[i].image, &mem_req); uint32_t mem_index = tut4_find_suitable_memory(phy_dev, dev, &mem_req, images[i].host_visible? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (mem_index >= phy_dev->memories.memoryTypeCount) continue; VkMemoryAllocateInfo mem_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = mem_req.size, .memoryTypeIndex = mem_index, }; res = vkAllocateMemory(dev->device, &mem_info, NULL, &images[i].image_mem); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; res = vkBindImageMemory(dev->device, images[i].image, images[i].image_mem, 0); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; if (images[i].make_view) { /* * Once we have an image, we need a view on the image to be able to use it. This is just like in * Tutorial 4 where we had a view on the buffer to work with it. In Tutorial 4, we had divided up the * buffer for concurrent processing in the shaders, and each view looked at a specific part of the * buffer. With images, this could also be useful, for example if one large image contains multiple * areas of interest (such as a texture) where different shaders need to look at. However, let's keep * things as simple as possible and create a view that is as large as the image itself. * * The image view's CreateInfo is largely similar to the one for buffer views. For image views, we * need to specify which components of the image we want to view and the range is not a simple * (offset, size) as was in the buffer view. * * For the components, we have the option to not only select which components (R, G, B and A) to view, * but also to remap them (this operation is called swizzle). For example to get the value of the red * component in place of alpha etc. The mapping for each component can be specified separately, and * mapping 0 means identity. We are not going to remap anything, so we'll leave all fields in * `components` be 0. * * The range of the image asks for which mipmap levels and image array layers we are interested in, * which are simply both 0 because we have only one of each. As part of the range of the view, we also * need to specify which aspect of the image we are looking it. This could be color, depth, stencil * etc. Here, we will decide the aspect based on the image usage; if it's used as depth/stencil, we * will set both depth and stencil aspects. Otherwise we will view the color aspect. */ VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = images[i].image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = images[i].format, .subresourceRange = { .aspectMask = (images[i].usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0? VK_IMAGE_ASPECT_COLOR_BIT: VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; res = vkCreateImageView(dev->device, &view_info, NULL, &images[i].view); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; } if ((images[i].usage & VK_IMAGE_USAGE_SAMPLED_BIT)) { /* * If the image is going to be sampled, we can create a sampler for it as well. A sampler * specifies how to sample an image. An image is just a glorified buffer, i.e., it's just an * array, as I have said before as well. However, the sampler is what makes using images so * much more powerful. When accessing a buffer, you can access each index individually. With * a sampler, you can access an image at non-integer indices. The sampler then "filters" the * image to provide some data for that index. * * The simplest example is magnification. If you sample the image at coordinates (u+0.5,v) * where u and v are integer pixel locations, then the color you get could be the average of * the colors (values) at coordinates (u,v) and (u+1,v). Vulkan uses the term `texel` to refer * to these "texture" pixels. * * The sampler parameters are explained below: * * - magFilter, minFilter: what to do if asked to sample between the texels. The options are * to take the value of the nearest texel, or interpolate between neighbors. Think about it * as what to do if you try to zoom in or out of an image. We'll go with interpolation, * since it's nicer. * - mipmapMode: similarly, if the image has multiple mipmap levels, accessing between the * levels could either interpolate between two levels or clamp to the nearest. We don't use * mipmaps here, so this doesn't matter, but let's tell it to interpolate anyway. * - addressModeU/V/W: this specifies what happens if you access outside the image. The * options are to: * * repeat the image as if it was a tiled to infinity in each direction, * * mirrored repeat the image as if a larger image containing the image and its mirror * was tiled to infinity in each direction, * * clamp to edge so that any access out of the image boundaries returns the value at the * closest point on the edge of the image, * * clamp to border so that any access out of the image boundaries returns a special * "border" value for the image (border value defined below), * * mirrored clamp to edge so that any access out of the image boundaries returns the value * at the closest point on the edge of a larger image that is made up of the image and its * mirror. * Each of these modes is useful in different situations. "Repeat" is probably the most * problematic as it introduces discontinuity around the edges. "Mirrored" solves this * problem and can add some interesting effects. "Clamp to edge" also solves this problem, * and let's just use that. "Clamp to border" would introduce other edges, and I imagine is * most useful for debugging. * - anisotropyEnable, maxAnisotropy: whether anisotropic filtering is enabled and by how * much. Anisotropic filtering is expensive but nice, so let's enable it. The maximum value * for anisotropic filtering can be retrieved from the device's limits. * - compareEnable, compareOp: this is used with depth images to result in a reading of 0 or 1 * based on the result of a compare operation. We are not interested in this for now. * - minLod, maxLod: the level-of-detail value (mip level) gets clamped to these values. We * are not using mipmapped images, so we'll just give 0 and 1 respectively. * - borderColor: if the "clamp to border" addressing mode was selected, out-of-bound accesses * to the image would return the border color, which is set here. Options are limited: * transparent, white and black. Since we're using "clamp to edge" addressing, this value is * not used. * - unnormalizedCoordinates: with Vulkan, you can either index the image using the * unnormalized coordinates, so that u and v span from 0 to size of the image, or you can * access the image using normalized coordinates, so that u and v span from 0 to 1. * Unnormalized coordinates can be useful in some circumstances, but normalized coordinates * lets you access the image without dealing with its sizes. Aside from that, with * unnormalized coordinates, you are limited in the type of images you can access; only 1D * and 2D images with a single layer and single mip level are acceptable and essentially all * other features of the sampler must be disabled too. Needless to say, we will use * normalized coordinates. */ VkSamplerCreateInfo sampler_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .anisotropyEnable = true, .maxAnisotropy = phy_dev->properties.limits.maxSamplerAnisotropy, .minLod = 0, .maxLod = 1, }; res = vkCreateSampler(dev->device, &sampler_info, NULL, &images[i].sampler); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; } ++successful; } /* * Now that you have learned all about images, we're not going to use them in this tutorial. Please don't hate * me. There is already so much here that rendering textured images can wait. It was not all in vein though * because we would need image views on the swapchain images anyway. Now at least you understand the * properties and restrictions of the swapchain images better. */ tut1_error_set_vkresult(&retval, successful == image_count?VK_SUCCESS:VK_INCOMPLETE); return retval; }
VkImageView create_image_view(const VkImageViewCreateInfo& info) { VkImageView image_view = VK_NULL_HANDLE; VkResult err = vkCreateImageView(_vulkan_device, &info, NULL, &image_view); assert(!err); return image_view; }
void VulkanTexturedQuad::CreateTexture (VkCommandBuffer uploadCommandList) { int width, height; auto image = LoadImageFromMemory (RubyTexture, sizeof (RubyTexture), 1, &width, &height); VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = nullptr; imageCreateInfo.queueFamilyIndexCount = 1; uint32_t queueFamilyIndex = static_cast<uint32_t> (queueFamilyIndex_); imageCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; imageCreateInfo.mipLevels = 1; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imageCreateInfo.arrayLayers = 1; imageCreateInfo.extent.depth = 1; imageCreateInfo.extent.height = height; imageCreateInfo.extent.width = width; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkCreateImage (device_, &imageCreateInfo, nullptr, &rubyImage_); VkMemoryRequirements requirements = {}; vkGetImageMemoryRequirements (device_, rubyImage_, &requirements); VkDeviceSize requiredSizeForImage = requirements.size; auto memoryHeaps = EnumerateHeaps (physicalDevice_); deviceImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForImage), requirements.memoryTypeBits, MT_DeviceLocal); vkBindImageMemory (device_, rubyImage_, deviceImageMemory_, 0); VkBufferCreateInfo bufferCreateInfo = {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.pNext = nullptr; bufferCreateInfo.queueFamilyIndexCount = 1; bufferCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; bufferCreateInfo.size = requiredSizeForImage; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; vkCreateBuffer (device_, &bufferCreateInfo, nullptr, &uploadImageBuffer_); vkGetBufferMemoryRequirements (device_, uploadImageBuffer_, &requirements); VkDeviceSize requiredSizeForBuffer = requirements.size; bool memoryIsHostCoherent = false; uploadImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForBuffer), requirements.memoryTypeBits, MT_HostVisible, &memoryIsHostCoherent); vkBindBufferMemory (device_, uploadImageBuffer_, uploadImageMemory_, 0); void* data = nullptr; vkMapMemory (device_, uploadImageMemory_, 0, VK_WHOLE_SIZE, 0, &data); ::memcpy (data, image.data (), image.size ()); if (!memoryIsHostCoherent) { VkMappedMemoryRange mappedMemoryRange = {}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = uploadImageMemory_; mappedMemoryRange.offset = 0; mappedMemoryRange.size = VK_WHOLE_SIZE; vkFlushMappedMemoryRanges (device_, 1, &mappedMemoryRange); } vkUnmapMemory (device_, uploadImageMemory_); VkBufferImageCopy bufferImageCopy = {}; bufferImageCopy.imageExtent.width = width; bufferImageCopy.imageExtent.height = height; bufferImageCopy.imageExtent.depth = 1; bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferImageCopy.imageSubresource.mipLevel = 0; bufferImageCopy.imageSubresource.layerCount = 1; VkImageMemoryBarrier imageBarrier = {}; imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageBarrier.pNext = nullptr; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.srcAccessMask = 0; imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.image = rubyImage_; imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBarrier.subresourceRange.layerCount = 1; imageBarrier.subresourceRange.levelCount = 1; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); vkCmdCopyBufferToImage (uploadCommandList, uploadImageBuffer_, rubyImage_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopy); imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); VkImageViewCreateInfo imageViewCreateInfo = {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.format = imageCreateInfo.format; imageViewCreateInfo.image = rubyImage_; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCreateInfo.subresourceRange.levelCount = 1; imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; vkCreateImageView (device_, &imageViewCreateInfo, nullptr, &rubyImageView_); }