void VkHelper::copyImage(VkDevice device, VkCommandPool cmdPool, VkQueue queue, VkImage srcImageId, VkImage dstImageId, uint32_t width, uint32_t height) { VkCommandBuffer cmdBuffer = beginSingleTimeCommandBufffer(device, cmdPool); VkImageSubresourceLayers subResource = {}; subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subResource.baseArrayLayer = 0; subResource.layerCount = 1; subResource.mipLevel = 0; VkImageCopy region{}; region.srcOffset = { 0, 0, 0 }; region.srcSubresource = subResource; region.dstOffset = { 0, 0, 0 }; region.dstSubresource = subResource; region.extent.width = width; region.extent.height = height; region.extent.depth = 1; vkCmdCopyImage( cmdBuffer, srcImageId, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImageId, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); endSingleTimeCommandBuffer(device, cmdPool, queue, cmdBuffer); }
void Renderer::copyImage(VkCommandPool pool, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { VkCommandBuffer command_buffer = _BeginSingleTimeCommands(pool); VkImageSubresourceLayers subresource {}; subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresource.baseArrayLayer = 0; subresource.mipLevel = 0; subresource.layerCount = 1; VkImageCopy region {}; region.srcSubresource = subresource; region.dstSubresource = subresource; region.srcOffset = { 0, 0, 0 }; region.dstOffset = { 0, 0, 0 }; region.extent.width = width; region.extent.height = height; region.extent.depth = 1; vkCmdCopyImage( command_buffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion ); _EndSingleTimeCommands(pool, command_buffer); }
void op3d::Engine::copyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { VkCommandBuffer commandBuffer = commandBufferManager.beginSingleTimeCommands(); VkImageSubresourceLayers subResource = {}; subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subResource.baseArrayLayer = 0; subResource.mipLevel = 0; subResource.layerCount = 1; VkImageCopy region = {}; region.srcSubresource = subResource; region.dstSubresource = subResource; region.srcOffset = {0, 0, 0}; region.dstOffset = {0, 0, 0}; region.extent.width = width; region.extent.height = height; region.extent.depth = 1; vkCmdCopyImage( commandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion ); commandBufferManager.endSingleTimeCommands(commandBuffer, graphicsQueue); }
void StagingTexture2DLinear::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { // Prepare the buffer for copying. // We don't care about the existing contents, so set to UNDEFINED. VkImageMemoryBarrier before_transfer_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext 0, // VkAccessFlags srcAccessMask VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex m_image, // VkImage image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange }; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &before_transfer_barrier); // Issue the image copy, gpu -> host. VkImageCopy copy_region = { {src_aspect, level, layer, 1}, // VkImageSubresourceLayers srcSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D srcOffset {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers dstSubresource {0, 0, 0}, // VkOffset3D dstOffset {width, height, 1} // VkExtent3D extent }; vkCmdCopyImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); // Ensure writes are visible to the host. VkImageMemoryBarrier visible_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex m_image, // VkImage image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange }; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, 1, &visible_barrier); m_layout = VK_IMAGE_LAYOUT_GENERAL; // Invalidate memory range if currently mapped. if (m_map_pointer && !m_coherent) { VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_map_offset, m_map_size}; vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); } }
void CopyContext::copySubresource(const Texture* pDst, uint32_t dstSubresourceIdx, const Texture* pSrc, uint32_t srcSubresourceIdx) { resourceBarrier(pDst, Resource::State::CopyDest); resourceBarrier(pSrc, Resource::State::CopySource); VkImageCopy region = {}; initRegionSubresourceData(pSrc, srcSubresourceIdx, region.srcSubresource); initRegionSubresourceData(pDst, dstSubresourceIdx, region.dstSubresource); uint32_t mipLevel = region.dstSubresource.mipLevel; region.extent.width = pDst->getWidth(mipLevel); region.extent.height = pDst->getHeight(mipLevel); region.extent.depth = pDst->getDepth(mipLevel); vkCmdCopyImage(mpLowLevelData->getCommandList(), pSrc->getApiHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, pDst->getApiHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); mCommandsPending = true; }
void CopyContext::copyResource(const Resource* pDst, const Resource* pSrc) { const Buffer* pDstBuffer = dynamic_cast<const Buffer*>(pDst); if (pDstBuffer) { const Buffer* pSrcBuffer = dynamic_cast<const Buffer*>(pSrc); assert(pSrcBuffer && (pSrcBuffer->getSize() == pDstBuffer->getSize())); copyBufferRegion(pDstBuffer, 0, pSrcBuffer, 0, pSrcBuffer->getSize()); } else { const Texture* pSrcTex = dynamic_cast<const Texture*>(pSrc); const Texture* pDstTex = dynamic_cast<const Texture*>(pDst); assert(pSrcTex && pDstTex); assert((pSrcTex->getArraySize() == pDstTex->getArraySize()) && (pSrcTex->getMipCount() == pDstTex->getMipCount())); uint32_t mipCount = pSrcTex->getMipCount(); std::vector<VkImageCopy> regions(mipCount); VkImageAspectFlags srcAspect = getAspectFlagsFromFormat(pSrcTex->getFormat()); VkImageAspectFlags dstAspect = getAspectFlagsFromFormat(pDstTex->getFormat()); uint32_t arraySize = pSrcTex->getArraySize(); for (uint32_t i = 0; i < mipCount; i++) { regions[i] = {}; regions[i].srcSubresource.aspectMask = srcAspect; regions[i].srcSubresource.baseArrayLayer = 0; regions[i].srcSubresource.layerCount = arraySize; regions[i].srcSubresource.mipLevel = i; regions[i].dstSubresource = regions[i].srcSubresource; regions[i].dstSubresource.aspectMask = dstAspect; regions[i].extent.width = pSrcTex->getWidth(i); regions[i].extent.height = pSrcTex->getHeight(i); regions[i].extent.depth = pSrcTex->getDepth(i); } resourceBarrier(pDst, Resource::State::CopyDest); resourceBarrier(pSrc, Resource::State::CopySource); vkCmdCopyImage(mpLowLevelData->getCommandList(), pSrc->getApiHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, pDst->getApiHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipCount, regions.data()); } mCommandsPending = true; }
void StagingTexture2DLinear::CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { // Flush memory range if currently mapped. if (m_map_pointer && !m_coherent) { VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_map_offset, m_map_size}; vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); } // Ensure any writes to the image are visible to the GPU. VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask m_layout, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex m_image, // VkImage image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange }; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; // Issue the image copy, host -> gpu. VkImageCopy copy_region = { {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers srcSubresource {0, 0, 0}, // VkOffset3D srcOffset {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers dstSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D dstOffset {width, height, 1} // VkExtent3D extent }; vkCmdCopyImage(command_buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); }
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, u32 dst_level) { Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier(); _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() && static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(), "Source rect is too large for CopyRectangleFromTexture"); _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= m_config.width && static_cast<u32>(dst_rect.GetHeight()) <= m_config.height, "Dest rect is too large for CopyRectangleFromTexture"); VkImageCopy image_copy = { {VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()}, {src_rect.left, src_rect.top, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers}, {dst_rect.left, dst_rect.top, 0}, {static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1}}; // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }
void Image::copyImage(const VkCommandBuffer cmdBuffer, IImageSP& targetImage, const VkImageCopy& imageCopy) { if (!targetImage.get()) { return; } VkImageLayout sourceImageLayout = getImageLayout(imageCopy.srcSubresource.mipLevel, imageCopy.srcSubresource.baseArrayLayer); VkAccessFlags sourceAccessMask = getAccessMask(imageCopy.srcSubresource.mipLevel, imageCopy.srcSubresource.baseArrayLayer); VkImageLayout targetImageLayout = targetImage->getImageLayout(imageCopy.dstSubresource.mipLevel, imageCopy.dstSubresource.baseArrayLayer); VkAccessFlags targetAccessMask = targetImage->getAccessMask(imageCopy.dstSubresource.mipLevel, imageCopy.dstSubresource.baseArrayLayer); // Prepare source image for copy. VkImageSubresourceRange srcImageSubresourceRange = {imageCopy.srcSubresource.aspectMask, imageCopy.srcSubresource.mipLevel, 1, imageCopy.srcSubresource.baseArrayLayer, imageCopy.srcSubresource.layerCount}; cmdPipelineBarrier(cmdBuffer, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcImageSubresourceRange); // Prepare target image for copy. VkImageSubresourceRange dstImageSubresourceRange = {imageCopy.dstSubresource.aspectMask, imageCopy.dstSubresource.mipLevel, 1, imageCopy.dstSubresource.baseArrayLayer, imageCopy.dstSubresource.layerCount}; targetImage->cmdPipelineBarrier(cmdBuffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstImageSubresourceRange); // Copy image by command. vkCmdCopyImage(cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, targetImage->getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopy); // Revert back. targetImage->cmdPipelineBarrier(cmdBuffer, targetAccessMask, targetImageLayout, dstImageSubresourceRange); // Revert back. cmdPipelineBarrier(cmdBuffer, sourceAccessMask, sourceImageLayout, srcImageSubresourceRange); }
void TextureCache::CopyTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect) { _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() && static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(), "Source rect is too large for CopyRectangleFromTexture"); _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= dst_texture->config.width && static_cast<u32>(dst_rect.GetHeight()) <= dst_texture->config.height, "Dest rect is too large for CopyRectangleFromTexture"); VkImageCopy image_copy = { { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, src_texture->GetLayers() }, // VkImageSubresourceLayers srcSubresource { src_rect.left, src_rect.top, 0 }, // VkOffset3D srcOffset { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource dst_texture->config.layers }, { dst_rect.left, dst_rect.top, 0 }, // VkOffset3D dstOffset { static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1 } // VkExtent3D extent }; // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_texture->GetTexture()->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); }
// Take a screenshot for the curretn swapchain image // This is done using a blit from the swapchain image to a linear image whose memory content is then saved as a ppm image // Getting the image date directly from a swapchain image wouldn't work as they're usually stored in an implementation dependant optimal tiling format // Note: This requires the swapchain images to be created with the VK_IMAGE_USAGE_TRANSFER_SRC_BIT flag (see VulkanSwapChain::create) void saveScreenshot(const char *filename) { screenshotSaved = false; // Get format properties for the swapchain color format VkFormatProperties formatProps; bool supportsBlit = true; // Check blit support for source and destination // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps); if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } // Check if the device supports blitting to linear images vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } // Source for the copy is the last rendered swapchain image VkImage srcImage = swapChain.images[currentBuffer]; // Create the linear tiled destination image to copy to and to read the memory from VkImageCreateInfo imgCreateInfo(vks::initializers::imageCreateInfo()); imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imgCreateInfo.extent.width = width; imgCreateInfo.extent.height = height; imgCreateInfo.extent.depth = 1; imgCreateInfo.arrayLayers = 1; imgCreateInfo.mipLevels = 1; imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; // Create the image VkImage dstImage; VK_CHECK_RESULT(vkCreateImage(device, &imgCreateInfo, nullptr, &dstImage)); // Create memory to back up the image VkMemoryRequirements memRequirements; VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); VkDeviceMemory dstImageMemory; vkGetImageMemoryRequirements(device, dstImage, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; // Memory must be host visible to copy from memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); // Do the actual blit from the swapchain image to our host visible destination image VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier(); // Transition destination image to transfer destination layout vks::tools::insertImageMemoryBarrier( copyCmd, dstImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // Transition swapchain image from present to transfer source layout vks::tools::insertImageMemoryBarrier( copyCmd, srcImage, VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) if (supportsBlit) { // Define the region to blit (we will blit the whole swapchain image) VkOffset3D blitSize; blitSize.x = width; blitSize.y = height; blitSize.z = 1; VkImageBlit imageBlitRegion{}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.srcSubresource.layerCount = 1; imageBlitRegion.srcOffsets[1] = blitSize; imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.dstSubresource.layerCount = 1; imageBlitRegion.dstOffsets[1] = blitSize; // Issue the blit command vkCmdBlitImage( copyCmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); } else { // Otherwise use image copy (requires us to manually flip components) VkImageCopy imageCopyRegion{}; imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.srcSubresource.layerCount = 1; imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.dstSubresource.layerCount = 1; imageCopyRegion.extent.width = width; imageCopyRegion.extent.height = height; imageCopyRegion.extent.depth = 1; // Issue the copy command vkCmdCopyImage( copyCmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); } // Transition destination image to general layout, which is the required layout for mapping the image memory later on vks::tools::insertImageMemoryBarrier( copyCmd, dstImage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // Transition back the swap chain image after the blit is done vks::tools::insertImageMemoryBarrier( copyCmd, srcImage, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); vulkanDevice->flushCommandBuffer(copyCmd, queue); // Get layout of the image (including row pitch) VkImageSubresource subResource{}; subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResourceLayout; vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); // Map image memory so we can start copying from it const char* data; vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data); data += subResourceLayout.offset; std::ofstream file(filename, std::ios::out | std::ios::binary); // ppm header file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components bool colorSwizzle = false; // Check if source is BGR // Note: Not complete, only contains most common and basic BGR surface formats for demonstation purposes if (!supportsBlit) { std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end()); } // ppm binary pixel data for (uint32_t y = 0; y < height; y++) { unsigned int *row = (unsigned int*)data; for (uint32_t x = 0; x < width; x++) { if (colorSwizzle) { file.write((char*)row+2, 1); file.write((char*)row+1, 1); file.write((char*)row, 1); } else { file.write((char*)row, 3); } row++; } data += subResourceLayout.rowPitch; } file.close(); std::cout << "Screenshot saved to disk" << std::endl; // Clean up resources vkUnmapMemory(device, dstImageMemory); vkFreeMemory(device, dstImageMemory, nullptr); vkDestroyImage(device, dstImage, nullptr); screenshotSaved = true; }
void VulkanTexture::copyImpl(const SPtr<Texture>& target, const TEXTURE_COPY_DESC& desc, const SPtr<CommandBuffer>& commandBuffer) { VulkanTexture* other = static_cast<VulkanTexture*>(target.get()); const TextureProperties& srcProps = mProperties; const TextureProperties& dstProps = other->getProperties(); bool srcHasMultisample = srcProps.getNumSamples() > 1; bool destHasMultisample = dstProps.getNumSamples() > 1; if ((srcProps.getUsage() & TU_DEPTHSTENCIL) != 0 || (dstProps.getUsage() & TU_DEPTHSTENCIL) != 0) { LOGERR("Texture copy/resolve isn't supported for depth-stencil textures."); return; } bool needsResolve = srcHasMultisample && !destHasMultisample; bool isMSCopy = srcHasMultisample || destHasMultisample; if (!needsResolve && isMSCopy) { if (srcProps.getNumSamples() != dstProps.getNumSamples()) { LOGERR("When copying textures their multisample counts must match. Ignoring copy."); return; } } VkImageLayout transferSrcLayout = mDirectlyMappable ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; VkImageLayout transferDstLayout = other->mDirectlyMappable ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; UINT32 mipWidth, mipHeight, mipDepth; bool copyEntireSurface = desc.srcVolume.getWidth() == 0 || desc.srcVolume.getHeight() == 0 || desc.srcVolume.getDepth() == 0; if(copyEntireSurface) { PixelUtil::getSizeForMipLevel( srcProps.getWidth(), srcProps.getHeight(), srcProps.getDepth(), desc.srcMip, mipWidth, mipHeight, mipDepth); } else { mipWidth = desc.srcVolume.getWidth(); mipHeight = desc.srcVolume.getHeight(); mipDepth = desc.srcVolume.getDepth(); } VkImageResolve resolveRegion; resolveRegion.srcOffset = { (INT32)desc.srcVolume.left, (INT32)desc.srcVolume.top, (INT32)desc.srcVolume.front }; resolveRegion.dstOffset = { desc.dstPosition.x, desc.dstPosition.y, desc.dstPosition.z }; resolveRegion.extent = { mipWidth, mipHeight, mipDepth }; resolveRegion.srcSubresource.baseArrayLayer = desc.srcFace; resolveRegion.srcSubresource.layerCount = 1; resolveRegion.srcSubresource.mipLevel = desc.srcMip; resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; resolveRegion.dstSubresource.baseArrayLayer = desc.dstFace; resolveRegion.dstSubresource.layerCount = 1; resolveRegion.dstSubresource.mipLevel = desc.dstMip; resolveRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkImageCopy imageRegion; imageRegion.srcOffset = { (INT32)desc.srcVolume.left, (INT32)desc.srcVolume.top, (INT32)desc.srcVolume.front }; imageRegion.dstOffset = { desc.dstPosition.x, desc.dstPosition.y, desc.dstPosition.z }; imageRegion.extent = { mipWidth, mipHeight, mipDepth }; imageRegion.srcSubresource.baseArrayLayer = desc.srcFace; imageRegion.srcSubresource.layerCount = 1; imageRegion.srcSubresource.mipLevel = desc.srcMip; imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageRegion.dstSubresource.baseArrayLayer = desc.dstFace; imageRegion.dstSubresource.layerCount = 1; imageRegion.dstSubresource.mipLevel = desc.dstMip; imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkImageSubresourceRange srcRange; srcRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srcRange.baseArrayLayer = desc.srcFace; srcRange.layerCount = 1; srcRange.baseMipLevel = desc.srcMip; srcRange.levelCount = 1; VkImageSubresourceRange dstRange; dstRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; dstRange.baseArrayLayer = desc.dstFace; dstRange.layerCount = 1; dstRange.baseMipLevel = desc.dstMip; dstRange.levelCount = 1; VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance()); VulkanCmdBuffer* vkCB; if (commandBuffer != nullptr) vkCB = static_cast<VulkanCommandBuffer*>(commandBuffer.get())->getInternal(); else vkCB = rapi._getMainCommandBuffer()->getInternal(); UINT32 deviceIdx = vkCB->getDeviceIdx(); VulkanImage* srcImage = mImages[deviceIdx]; VulkanImage* dstImage = other->getResource(deviceIdx); if (srcImage == nullptr || dstImage == nullptr) return; VkImageLayout srcLayout = vkCB->getCurrentLayout(srcImage, srcRange, false); VkImageLayout dstLayout = vkCB->getCurrentLayout(dstImage, dstRange, false); VkCommandBuffer vkCmdBuf = vkCB->getHandle(); VkAccessFlags srcAccessMask = srcImage->getAccessFlags(srcLayout); VkAccessFlags dstAccessMask = dstImage->getAccessFlags(dstLayout); if (vkCB->isInRenderPass()) vkCB->endRenderPass(); // Transfer textures to a valid layout vkCB->setLayout(srcImage->getHandle(), srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcLayout, transferSrcLayout, srcRange); vkCB->setLayout(dstImage->getHandle(), dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstLayout, transferDstLayout, dstRange); if (srcHasMultisample && !destHasMultisample) // Resolving from MS to non-MS texture { vkCmdResolveImage(vkCmdBuf, srcImage->getHandle(), transferSrcLayout, dstImage->getHandle(), transferDstLayout, 1, &resolveRegion); } else // Just a normal copy { vkCmdCopyImage(vkCmdBuf, srcImage->getHandle(), transferSrcLayout, dstImage->getHandle(), transferDstLayout, 1, &imageRegion); } // Transfer back to optimal layouts srcLayout = srcImage->getOptimalLayout(); // Notify the command buffer that these resources are being used on it vkCB->registerResource(srcImage, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VulkanUseFlag::Read, ResourceUsage::Transfer); vkCB->registerResource(dstImage, dstRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VulkanUseFlag::Write, ResourceUsage::Transfer); }
void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer, TextureCache::TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) { VKTexture* destination_texture = static_cast<VKTexture*>(entry->texture.get()); auto key = std::make_pair(format, palette_format); auto iter = m_decoding_pipelines.find(key); if (iter == m_decoding_pipelines.end()) return; struct PushConstants { u32 dst_size[2]; u32 src_size[2]; u32 src_offset; u32 src_row_stride; u32 palette_offset; }; // Copy to GPU-visible buffer, aligned to the data type auto info = iter->second; u32 bytes_per_buffer_elem = TextureConversionShader::GetBytesPerBufferElement(info.base_info->buffer_format); // Calculate total data size, including palette. // Only copy palette if it is required. u32 total_upload_size = static_cast<u32>(data_size); u32 palette_size = iter->second.base_info->palette_size; u32 palette_offset = total_upload_size; bool has_palette = palette_size > 0; if (has_palette) { // Align to u16. if ((total_upload_size % sizeof(u16)) != 0) { total_upload_size++; palette_offset++; } total_upload_size += palette_size; } // Allocate space for upload, if it fails, execute the buffer. if (!m_texel_buffer->ReserveMemory(total_upload_size, bytes_per_buffer_elem)) { Util::ExecuteCurrentCommandsAndRestoreState(true, false); if (!m_texel_buffer->ReserveMemory(total_upload_size, bytes_per_buffer_elem)) PanicAlert("Failed to reserve memory for encoded texture upload"); } // Copy/commit upload buffer. u32 texel_buffer_offset = static_cast<u32>(m_texel_buffer->GetCurrentOffset()); std::memcpy(m_texel_buffer->GetCurrentHostPointer(), data, data_size); if (has_palette) std::memcpy(m_texel_buffer->GetCurrentHostPointer() + palette_offset, palette, palette_size); m_texel_buffer->CommitMemory(total_upload_size); // Determine uniforms. PushConstants constants = { {width, height}, {aligned_width, aligned_height}, texel_buffer_offset / bytes_per_buffer_elem, row_stride / bytes_per_buffer_elem, static_cast<u32>((texel_buffer_offset + palette_offset) / sizeof(u16))}; // Determine view to use for texel buffers. VkBufferView data_view = VK_NULL_HANDLE; switch (iter->second.base_info->buffer_format) { case TextureConversionShader::BUFFER_FORMAT_R8_UINT: data_view = m_texel_buffer_view_r8_uint; break; case TextureConversionShader::BUFFER_FORMAT_R16_UINT: data_view = m_texel_buffer_view_r16_uint; break; case TextureConversionShader::BUFFER_FORMAT_R32G32_UINT: data_view = m_texel_buffer_view_r32g32_uint; break; default: break; } // Dispatch compute to temporary texture. ComputeShaderDispatcher dispatcher(command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_COMPUTE), iter->second.compute_shader); m_decoding_texture->TransitionToLayout(command_buffer, Texture2D::ComputeImageLayout::WriteOnly); dispatcher.SetPushConstants(&constants, sizeof(constants)); dispatcher.SetStorageImage(m_decoding_texture->GetView(), m_decoding_texture->GetLayout()); dispatcher.SetTexelBuffer(0, data_view); if (has_palette) dispatcher.SetTexelBuffer(1, m_texel_buffer_view_r16_uint); auto groups = TextureConversionShader::GetDispatchCount(iter->second.base_info, aligned_width, aligned_height); dispatcher.Dispatch(groups.first, groups.second, 1); // Copy from temporary texture to final destination. Texture2D* vulkan_tex_identifier = destination_texture->GetRawTexIdentifier(); m_decoding_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); vulkan_tex_identifier->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy image_copy = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, 0, 1}, {0, 0, 0}, {width, height, 1}}; vkCmdCopyImage(command_buffer, m_decoding_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vulkan_tex_identifier->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); }
void VulkanTexture::Unlock() { vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); // if we already have an image, queue it for destruction and forget it. Wipe(); if (!needStaging) { // If we can use the linear tiled image as a texture, just do it image = mappableImage; mem = mappableMemory; TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Make sure we don't accidentally delete the main image. mappableImage = VK_NULL_HANDLE; mappableMemory = VK_NULL_HANDLE; } else { VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = format_; image_create_info.extent.width = tex_width; image_create_info.extent.height = tex_height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; // The mappable image cannot be our texture, so create an optimally tiled image and blit to it image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; mem_alloc.memoryTypeIndex = 0; mem_alloc.allocationSize = mem_reqs.size; // Find memory type - don't specify any mapping requirements bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex); assert(pass); res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); assert(res == VK_SUCCESS); res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0); assert(res == VK_SUCCESS); // Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL TransitionImageLayout(cmd, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy copy_region; copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.srcSubresource.mipLevel = 0; copy_region.srcSubresource.baseArrayLayer = 0; copy_region.srcSubresource.layerCount = 1; copy_region.srcOffset.x = 0; copy_region.srcOffset.y = 0; copy_region.srcOffset.z = 0; copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.dstSubresource.mipLevel = 0; copy_region.dstSubresource.baseArrayLayer = 0; copy_region.dstSubresource.layerCount = 1; copy_region.dstOffset.x = 0; copy_region.dstOffset.y = 0; copy_region.dstOffset.z = 0; copy_region.extent.width = tex_width; copy_region.extent.height = tex_height; copy_region.extent.depth = 1; // Put the copy command into the command buffer vkCmdCopyImage(cmd, mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); assert(res == VK_SUCCESS); // Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Then drop the temporary mappable image - although should not be necessary... vulkan_->Delete().QueueDeleteImage(mappableImage); vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory); mappableImage = VK_NULL_HANDLE; mappableMemory = VK_NULL_HANDLE; } VkImageViewCreateInfo view_info = { 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.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; VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view); assert(res == VK_SUCCESS); }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Copy/Blit Image"; VkImageCreateInfo image_info; VkImage bltSrcImage; VkImage bltDstImage; VkMemoryRequirements memReq; VkMemoryAllocateInfo memAllocInfo; VkDeviceMemory dmem; unsigned char *pImgMem; 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); init_window_size(info, 640, 640); init_connection(info); init_window(info); init_swapchain_extension(info); VkSurfaceCapabilitiesKHR surfCapabilities; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.gpus[0], info.surface, &surfCapabilities); if (!(surfCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { std::cout << "Surface cannot be destination of blit - abort \n"; exit(-1); } init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); /* VULKAN_KEY_START */ VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(info.gpus[0], info.format, &formatProps); assert( (formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source"); VkSemaphore presentCompleteSemaphore; VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; presentCompleteSemaphoreCreateInfo.pNext = NULL; presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; res = vkCreateSemaphore(info.device, &presentCompleteSemaphoreCreateInfo, NULL, &presentCompleteSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, presentCompleteSemaphore, 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); // Create an image, map it, and write some values to the image image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = NULL; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = info.format; image_info.extent.width = info.width; image_info.extent.height = info.height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.samples = NUM_SAMPLES; image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = NULL; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image_info.flags = 0; image_info.tiling = VK_IMAGE_TILING_LINEAR; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; res = vkCreateImage(info.device, &image_info, NULL, &bltSrcImage); memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.pNext = NULL; vkGetImageMemoryRequirements(info.device, bltSrcImage, &memReq); bool pass = memory_type_from_properties(info, memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); memAllocInfo.allocationSize = memReq.size; res = vkAllocateMemory(info.device, &memAllocInfo, NULL, &dmem); res = vkBindImageMemory(info.device, bltSrcImage, dmem, 0); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); VkFence cmdFence; init_fence(info, cmdFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submit_info = {}; submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &presentCompleteSemaphore; submit_info.pWaitDstStageMask = &pipe_stage_flags; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, cmdFence); assert(res == VK_SUCCESS); /* Make sure command buffer is finished before mapping */ do { res = vkWaitForFences(info.device, 1, &cmdFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, cmdFence, NULL); res = vkMapMemory(info.device, dmem, 0, memReq.size, 0, (void **)&pImgMem); // Checkerboard of 8x8 pixel squares for (int row = 0; row < info.height; row++) { for (int col = 0; col < info.width; col++) { unsigned char rgb = (((row & 0x8) == 0) ^ ((col & 0x8) == 0)) * 255; pImgMem[0] = rgb; pImgMem[1] = rgb; pImgMem[2] = rgb; pImgMem[3] = 255; pImgMem += 4; } } // Flush the mapped memory and then unmap it Assume it isn't coherent since // we didn't really confirm VkMappedMemoryRange memRange; memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; memRange.pNext = NULL; memRange.memory = dmem; memRange.offset = 0; memRange.size = memReq.size; res = vkFlushMappedMemoryRanges(info.device, 1, &memRange); vkUnmapMemory(info.device, dmem); vkResetCommandBuffer(info.cmd, 0); execute_begin_command_buffer(info); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); bltDstImage = info.buffers[info.current_buffer].image; // init_swap_chain will create the images as color attachment optimal // but we want transfer dst optimal set_image_layout(info, bltDstImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Do a 32x32 blit to all of the dst image - should get big squares VkImageBlit region; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; region.srcSubresource.baseArrayLayer = 0; region.srcSubresource.layerCount = 1; region.srcOffsets[0].x = 0; region.srcOffsets[0].y = 0; region.srcOffsets[0].z = 0; region.srcOffsets[1].x = 32; region.srcOffsets[1].y = 32; region.srcOffsets[1].z = 1; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.mipLevel = 0; region.dstSubresource.baseArrayLayer = 0; region.dstSubresource.layerCount = 1; region.dstOffsets[0].x = 0; region.dstOffsets[0].y = 0; region.dstOffsets[0].z = 0; region.dstOffsets[1].x = info.width; region.dstOffsets[1].y = info.height; region.dstOffsets[1].z = 1; vkCmdBlitImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR); // Do a image copy to part of the dst image - checks should stay small VkImageCopy cregion; cregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.srcSubresource.mipLevel = 0; cregion.srcSubresource.baseArrayLayer = 0; cregion.srcSubresource.layerCount = 1; cregion.srcOffset.x = 0; cregion.srcOffset.y = 0; cregion.srcOffset.z = 0; cregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.dstSubresource.mipLevel = 0; cregion.dstSubresource.baseArrayLayer = 0; cregion.dstSubresource.layerCount = 1; cregion.dstOffset.x = 256; cregion.dstOffset.y = 256; cregion.dstOffset.z = 0; cregion.extent.width = 128; cregion.extent.height = 128; cregion.extent.depth = 1; vkCmdCopyImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion); VkImageMemoryBarrier prePresentBarrier = {}; prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; prePresentBarrier.pNext = NULL; prePresentBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; prePresentBarrier.subresourceRange.baseMipLevel = 0; prePresentBarrier.subresourceRange.levelCount = 1; prePresentBarrier.subresourceRange.baseArrayLayer = 0; prePresentBarrier.subresourceRange.layerCount = 1; prePresentBarrier.image = info.buffers[info.current_buffer].image; vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &prePresentBarrier); res = vkEndCommandBuffer(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); submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = NULL; submit_info.pWaitDstStageMask = NULL; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, drawFence); assert(res == VK_SUCCESS); res = vkQueueWaitIdle(info.queue); assert(res == VK_SUCCESS); /* Now present the image in the window */ VkPresentInfoKHR present; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.pNext = NULL; present.swapchainCount = 1; present.pSwapchains = &info.swap_chain; present.pImageIndices = &info.current_buffer; present.pWaitSemaphores = NULL; present.waitSemaphoreCount = 0; present.pResults = NULL; /* Make sure command buffer is finished before presenting */ do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); res = vkQueuePresentKHR(info.queue, &present); assert(res == VK_SUCCESS); wait_seconds(1); /* VULKAN_KEY_END */ if (info.save_images) write_ppm(info, "copyblitimage"); vkDestroySemaphore(info.device, presentCompleteSemaphore, NULL); vkDestroyFence(info.device, drawFence, NULL); vkDestroyImage(info.device, bltSrcImage, NULL); vkFreeMemory(info.device, dmem, NULL); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
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[] = "Texture Initialization Sample"; 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); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); /* VULKAN_KEY_START */ /* * Set up textures: * - Create a linear tiled image * - Map it and write the texture data into it * - If linear images cannot be used as textures, create an optimally * tiled image and blit from the linearly tiled image to the optimally * tiled image * - * - * - */ struct texture_object texObj; std::string filename = get_base_data_dir(); filename.append("lunarg.ppm"); if (!read_ppm(filename.c_str(), texObj.tex_width, texObj.tex_height, 0, NULL)) { std::cout << "Could not read texture file lunarg.ppm\n"; exit(-1); } VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R8G8B8A8_UNORM, &formatProps); /* See if we can use a linear tiled image for a texture, if not, we will * need a staging image for the texture data */ bool needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; 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 = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.extent.width = texObj.tex_width; image_create_info.extent.height = texObj.tex_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_LINEAR; image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_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 mappableImage; VkDeviceMemory mappableMemory; VkMemoryRequirements mem_reqs; /* Create a mappable image. It will be the texture if linear images are ok * to be textures or it will be the staging image if they are not. */ res = vkCreateImage(info.device, &image_create_info, NULL, &mappableImage); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(info.device, mappableImage, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; /* Find the memory type that is host mappable */ pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); assert(pass); /* allocate memory */ res = vkAllocateMemory(info.device, &mem_alloc, NULL, &(mappableMemory)); assert(res == VK_SUCCESS); /* bind memory */ res = vkBindImageMemory(info.device, mappableImage, mappableMemory, 0); assert(res == VK_SUCCESS); VkImageSubresource subres = {}; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = 0; subres.arrayLayer = 0; VkSubresourceLayout layout; void *data; /* Get the subresource layout so we know what the row pitch is */ vkGetImageSubresourceLayout(info.device, mappableImage, &subres, &layout); res = vkMapMemory(info.device, mappableMemory, 0, mem_reqs.size, 0, &data); assert(res == VK_SUCCESS); /* Read the ppm file into the mappable image's memory */ if (!read_ppm(filename.c_str(), texObj.tex_width, texObj.tex_height, layout.rowPitch, (unsigned char *)data)) { std::cout << "Could not load texture file lunarg.ppm\n"; exit(-1); } vkUnmapMemory(info.device, mappableMemory); if (!needStaging) { /* If we can use the linear tiled image as a texture, just do it */ texObj.image = mappableImage; texObj.mem = mappableMemory; texObj.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, texObj.imageLayout); } else { /* The mappable image cannot be our texture, so create an optimally * tiled image and blit to it */ image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; res = vkCreateImage(info.device, &image_create_info, NULL, &texObj.image); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(info.device, texObj.image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; /* Find memory type - don't specify any mapping requirements */ pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); assert(pass); /* allocate memory */ res = vkAllocateMemory(info.device, &mem_alloc, NULL, &texObj.mem); assert(res == VK_SUCCESS); /* bind memory */ res = vkBindImageMemory(info.device, texObj.image, texObj.mem, 0); assert(res == VK_SUCCESS); /* Since we're going to blit from the mappable image, set its layout to * SOURCE_OPTIMAL */ /* Side effect is that this will create info.cmd */ set_image_layout(info, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); /* Since we're going to blit to the texture image, set its layout to * DESTINATION_OPTIMAL */ set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy copy_region; copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.srcSubresource.mipLevel = 0; copy_region.srcSubresource.baseArrayLayer = 0; copy_region.srcSubresource.layerCount = 1; copy_region.srcOffset.x = 0; copy_region.srcOffset.y = 0; copy_region.srcOffset.z = 0; copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.dstSubresource.mipLevel = 0; copy_region.dstSubresource.baseArrayLayer = 0; copy_region.dstSubresource.layerCount = 1; copy_region.dstOffset.x = 0; copy_region.dstOffset.y = 0; copy_region.dstOffset.z = 0; copy_region.extent.width = texObj.tex_width; copy_region.extent.height = texObj.tex_height; copy_region.extent.depth = 1; /* Put the copy command into the command buffer */ vkCmdCopyImage(info.cmd, mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texObj.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); /* Set the layout for the texture image from DESTINATION_OPTIMAL to * SHADER_READ_ONLY */ texObj.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texObj.imageLayout); } execute_end_command_buffer(info); execute_queue_command_buffer(info); VkSamplerCreateInfo samplerCreateInfo = {}; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.magFilter = VK_FILTER_NEAREST; samplerCreateInfo.minFilter = VK_FILTER_NEAREST; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.mipLodBias = 0.0; samplerCreateInfo.anisotropyEnable = VK_FALSE, samplerCreateInfo.maxAnisotropy = 0; samplerCreateInfo.compareEnable = VK_FALSE; samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; samplerCreateInfo.minLod = 0.0; samplerCreateInfo.maxLod = 0.0; samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; /* create sampler */ res = vkCreateSampler(info.device, &samplerCreateInfo, NULL, &texObj.sampler); assert(res == VK_SUCCESS); 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 = VK_FORMAT_R8G8B8A8_UNORM; 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; /* create image view */ view_info.image = texObj.image; res = vkCreateImageView(info.device, &view_info, NULL, &texObj.view); assert(res == VK_SUCCESS); info.textures.push_back(texObj); /* VULKAN_KEY_END */ /* Clean Up */ vkDestroySampler(info.device, texObj.sampler, NULL); vkDestroyImageView(info.device, texObj.view, NULL); vkDestroyImage(info.device, texObj.image, NULL); vkFreeMemory(info.device, texObj.mem, NULL); if (needStaging) { /* Release the resources for the staging image */ vkFreeMemory(info.device, mappableMemory, NULL); vkDestroyImage(info.device, mappableImage, NULL); } destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
void VulkanTexture::copyImage(VulkanTransferBuffer* cb, VulkanImage* srcImage, VulkanImage* dstImage, VkImageLayout srcFinalLayout, VkImageLayout dstFinalLayout) { UINT32 numFaces = mProperties.getNumFaces(); UINT32 numMipmaps = mProperties.getNumMipmaps() + 1; UINT32 mipWidth = mProperties.getWidth(); UINT32 mipHeight = mProperties.getHeight(); UINT32 mipDepth = mProperties.getDepth(); VkImageCopy* imageRegions = bs_stack_alloc<VkImageCopy>(numMipmaps); for(UINT32 i = 0; i < numMipmaps; i++) { VkImageCopy& imageRegion = imageRegions[i]; imageRegion.srcOffset = { 0, 0, 0 }; imageRegion.dstOffset = { 0, 0, 0 }; imageRegion.extent = { mipWidth, mipHeight, mipDepth }; imageRegion.srcSubresource.baseArrayLayer = 0; imageRegion.srcSubresource.layerCount = numFaces; imageRegion.srcSubresource.mipLevel = i; imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageRegion.dstSubresource.baseArrayLayer = 0; imageRegion.dstSubresource.layerCount = numFaces; imageRegion.dstSubresource.mipLevel = i; imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; if (mipWidth != 1) mipWidth /= 2; if (mipHeight != 1) mipHeight /= 2; if (mipDepth != 1) mipDepth /= 2; } VkImageSubresourceRange range; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; range.baseArrayLayer = 0; range.layerCount = numFaces; range.baseMipLevel = 0; range.levelCount = numMipmaps; VkImageLayout transferSrcLayout, transferDstLayout; if (mDirectlyMappable) { transferSrcLayout = VK_IMAGE_LAYOUT_GENERAL; transferDstLayout = VK_IMAGE_LAYOUT_GENERAL; } else { transferSrcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; transferDstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; } // Transfer textures to a valid layout cb->setLayout(srcImage, range, VK_ACCESS_TRANSFER_READ_BIT, transferSrcLayout); cb->setLayout(dstImage, range, VK_ACCESS_TRANSFER_WRITE_BIT, transferDstLayout); vkCmdCopyImage(cb->getCB()->getHandle(), srcImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, numMipmaps, imageRegions); // Transfer back to final layouts VkAccessFlags srcAccessMask = srcImage->getAccessFlags(srcFinalLayout); cb->setLayout(srcImage->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, srcAccessMask, transferSrcLayout, srcFinalLayout, range); VkAccessFlags dstAccessMask = dstImage->getAccessFlags(dstFinalLayout); cb->setLayout(dstImage->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, transferDstLayout, dstFinalLayout, range); cb->getCB()->registerResource(srcImage, range, VulkanUseFlag::Read, ResourceUsage::Transfer); cb->getCB()->registerResource(dstImage, range, VulkanUseFlag::Write, ResourceUsage::Transfer); bs_stack_free(imageRegions); }
void Image::loadTexture(const char *path) { PixelBuffer<uint8_t> pb; if(!pb.load(path)) throw "Failed to load texture"; VkFormatProperties &props = formatProperties[VK_FORMAT_R8G8B8A8_UNORM]; bool direct = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0; VkImageSubresource subres = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; VkSubresourceLayout sublayout; createTexture(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, direct ? VK_IMAGE_USAGE_SAMPLED_BIT : VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, pb.getWidth(), pb.getHeight()); vkGetImageSubresourceLayout(vk, image, &subres, &sublayout); uint8_t *data = NULL; OBJ_CHECK(vkMapMemory(vk, mem, 0, allocInfo.allocationSize, 0, (void **)&data)); uint32_t x = 0, y = 0; uint8_t temp[4] = {0, 0, 0, 255}; for (y = 0; y < pb.getHeight(); y++) { uint32_t *dest = (uint32_t *)data; uint8_t *src = pb(0, y); switch (pb.getChannels()) { case 4: memcpy(dest, src, pb.getWidth() * 4); break; case 3: for (x = 0; x < pb.getWidth(); x++) { temp[0] = *src++; // R temp[1] = *src++; // G temp[2] = *src++; // B *dest++ = *(uint32_t *)temp; } break; case 2: for (x = 0; x < pb.getWidth(); x++) { temp[0] = *src++; // R temp[1] = *src++; // G *dest++ = *(uint32_t *)temp; } break; case 1: for (x = 0; x < pb.getWidth(); x++) { temp[0] = *src++; // R *dest++ = *(uint32_t *)temp; } break; } data += sublayout.rowPitch; } vkUnmapMemory(vk, mem); if (direct) { setLayout(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } else { // Create a staging image visible to the host to load the texture into with linear tiling Image staging; Math::Swap(staging.image, image); Math::Swap(mem, staging.mem); Math::Swap(layout, staging.layout); Math::Swap(imageInfo, staging.imageInfo); Math::Swap(allocInfo, staging.allocInfo); staging.setLayout(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // Now create the actual device-local image and copy into it from the staging image createTexture(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, pb.getWidth(), pb.getHeight()); setLayout(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); ImageCopy copy_region(pb.getWidth(), pb.getHeight()); vkCmdCopyImage(vk, staging.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); setLayout(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); vk.flush(); // Wait for the copy command to complete before the staging texture goes out of scope! } ImageViewCreateInfo viewInfo(image, imageInfo.format, VK_IMAGE_ASPECT_COLOR_BIT); OBJ_CHECK(vkCreateImageView(vk, &viewInfo, NULL, &view)); }
void loadTexture(const char* filename, VkFormat format, bool forceLinearTiling) { VkFormatProperties formatProperties; VkResult err; gli::textureCube texCube(gli::load(filename)); assert(!texCube.empty()); cubeMap.width = texCube[0].dimensions().x; cubeMap.height = texCube[0].dimensions().y; // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.extent = { cubeMap.width, cubeMap.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 { VkImage image; VkDeviceMemory memory; } cubeFace[6]; // Allocate command buffer for image copies and layouts VkCommandBuffer cmdBuffer; VkCommandBufferAllocateInfo cmdBufAlllocatInfo = vkTools::initializers::commandBufferAllocateInfo( cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); 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 face = 0; face < 6; ++face) { err = vkCreateImage(device, &imageCreateInfo, nullptr, &cubeFace[face].image); assert(!err); vkGetImageMemoryRequirements(device, cubeFace[face].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeFace[face].memory); assert(!err); err = vkBindImageMemory(device, cubeFace[face].image, cubeFace[face].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, cubeFace[face].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, cubeFace[face].memory, 0, memReqs.size, 0, &data); assert(!err); memcpy(data, texCube[face][subRes.mipLevel].data(), texCube[face][subRes.mipLevel].size()); vkUnmapMemory(device, cubeFace[face].memory); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( cmdBuffer, cubeFace[face].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.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; imageCreateInfo.arrayLayers = 6; err = vkCreateImage(device, &imageCreateInfo, nullptr, &cubeMap.image); assert(!err); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeMap.deviceMemory); assert(!err); err = vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy // 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 = 6; vkTools::setImageLayout( cmdBuffer, cubeMap.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 face = 0; face < 6; ++face) { // 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 = face; copyRegion.dstSubresource.mipLevel = 0; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = cubeMap.width; copyRegion.extent.height = cubeMap.height; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( cmdBuffer, cubeFace[face].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); } // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( cmdBuffer, cubeMap.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cubeMap.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, &cubeMap.sampler); assert(!err); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; 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 }; view.subresourceRange.layerCount = 6; view.image = cubeMap.image; err = vkCreateImageView(device, &view, nullptr, &cubeMap.view); assert(!err); // Cleanup for (auto& face : cubeFace) { vkDestroyImage(device, face.image, nullptr); vkFreeMemory(device, face.memory, nullptr); } }
VkBool32 Example::buildTexture(const vkts::ICommandBuffersSP& cmdBuffer, vkts::IImageSP& stageImage, vkts::IDeviceMemorySP& stageDeviceMemoryImage) { auto imageData = vkts::imageDataLoad(VKTS_TEXTURE_NAME); if (!imageData.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not load image data: '%s'", VKTS_TEXTURE_NAME); return VK_FALSE; } // VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice->getPhysicalDevice(), imageData->getFormat(), &formatProperties); VkImageTiling imageTiling = VK_IMAGE_TILING_LINEAR; VkMemoryPropertyFlagBits memoryPropertyFlagBits = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; VkImageLayout initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; VkAccessFlags accessMask = VK_ACCESS_HOST_WRITE_BIT; // Check, how to upload image data. if (!(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Format not supported."); return VK_FALSE; } imageTiling = VK_IMAGE_TILING_OPTIMAL; memoryPropertyFlagBits = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; initialLayout = VK_IMAGE_LAYOUT_GENERAL; accessMask = 0; } // if (!createTexture(image, deviceMemoryImage, imageData, imageTiling, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, initialLayout, memoryPropertyFlagBits, accessMask)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image."); return VK_FALSE; } // VkImageMemoryBarrier imageMemoryBarrier; memset(&imageMemoryBarrier, 0, sizeof(VkImageMemoryBarrier)); imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.srcAccessMask = 0; // Defined later. imageMemoryBarrier.dstAccessMask = 0; // Defined later. imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;// Defined later. imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;// Defined later. imageMemoryBarrier.srcQueueFamilyIndex = 0; imageMemoryBarrier.dstQueueFamilyIndex = 0; imageMemoryBarrier.image = VK_NULL_HANDLE; // Defined later. imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; // imageMemoryBarrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageMemoryBarrier.image = image->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // // If the image is only accessible by the device ... if (memoryPropertyFlagBits == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { // ... create texture with host visibility. This texture contains the pixel data. if (!createTexture(stageImage, stageDeviceMemoryImage, imageData, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_ACCESS_HOST_WRITE_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image."); return VK_FALSE; } // Prepare source image layout for copy. imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; imageMemoryBarrier.image = stageImage->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // Prepare target image layout for copy. imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.image = image->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // Copy image data by command. VkImageCopy imageCopy; imageCopy.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.srcOffset = { 0, 0, 0}; imageCopy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.dstOffset = { 0, 0, 0}; imageCopy.extent = { imageData->getWidth(), imageData->getHeight(), imageData->getDepth()}; vkCmdCopyImage(cmdBuffer->getCommandBuffer(), stageImage->getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image->getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopy); // Switch back to original layout. imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); } // sampler = vkts::samplerCreate(device->getDevice(), 0, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 0.0f, VK_FALSE, 1.0f, VK_FALSE, VK_COMPARE_OP_NEVER, 0.0f, 0.0f, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, VK_FALSE); if (!sampler.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create sampler."); return VK_FALSE; } imageView = vkts::imageViewCreate(device->getDevice(), 0, image->getImage(), VK_IMAGE_VIEW_TYPE_2D, image->getFormat(), { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); if (!imageView.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image view."); return VK_FALSE; } return VK_TRUE; }
void loadTexture(const char* fileName, VkFormat format, bool forceLinearTiling) { VkFormatProperties formatProperties; VkResult err; AAsset* asset = AAssetManager_open(app->activity->assetManager, fileName, 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::texture2D tex2D(gli::load((const char*)textureData, size)); assert(!tex2D.empty()); texture.width = tex2D[0].dimensions().x; texture.height = tex2D[0].dimensions().y; texture.mipLevels = tex2D.levels(); // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); // Only use linear tiling if requested (and supported by the device) // Support for linear tiling is mostly limited, so prefer to use // optimal tiling instead // On most implementations linear tiling will only support a very // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) VkBool32 useStaging = true; // Only use linear tiling if forced if (forceLinearTiling) { // Don't use linear if format is not supported for (linear) shader sampling useStaging = !(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); } VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imageCreateInfo.usage = (useStaging) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.flags = 0; imageCreateInfo.extent = { texture.width, texture.height, 1 }; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; startSetupCommandBuffer(); if (useStaging) { // Load all available mip levels into linear textures // and copy to optimal tiling target struct MipLevel { VkImage image; VkDeviceMemory memory; }; std::vector<MipLevel> mipLevels; mipLevels.resize(texture.mipLevels); // Copy mip levels for (uint32_t level = 0; level < texture.mipLevels; ++level) { imageCreateInfo.extent.width = tex2D[level].dimensions().x; imageCreateInfo.extent.height = tex2D[level].dimensions().y; imageCreateInfo.extent.depth = 1; err = vkCreateImage(device, &imageCreateInfo, nullptr, &mipLevels[level].image); assert(!err); vkGetImageMemoryRequirements(device, mipLevels[level].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &mipLevels[level].memory); assert(!err); err = vkBindImageMemory(device, mipLevels[level].image, mipLevels[level].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, mipLevels[level].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, mipLevels[level].memory, 0, memReqs.size, 0, &data); assert(!err); size_t levelSize = tex2D[level].size(); memcpy(data, tex2D[level].data(), levelSize); vkUnmapMemory(device, mipLevels[level].memory); LOGW("setImageLayout %d", 1); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( setupCmdBuffer, mipLevels[level].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); } // 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.mipLevels = texture.mipLevels; imageCreateInfo.extent = { texture.width, texture.height, 1 }; err = vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image); assert(!err); vkGetImageMemoryRequirements(device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory); assert(!err); err = vkBindImageMemory(device, texture.image, texture.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Copy mip levels one by one for (uint32_t level = 0; level < texture.mipLevels; ++level) { // 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 = 0; // Set mip level to copy the linear image to copyRegion.dstSubresource.mipLevel = level; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = tex2D[level].dimensions().x; copyRegion.extent.height = tex2D[level].dimensions().y; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( setupCmdBuffer, mipLevels[level].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); // Change texture image layout to shader read after the copy texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.imageLayout); } // Clean up linear images // No longer required after mip levels // have been transformed over to optimal tiling for (auto& level : mipLevels) { vkDestroyImage(device, level.image, nullptr); vkFreeMemory(device, level.memory, nullptr); } } else { // Prefer using optimal tiling, as linear tiling // may support only a small set of features // depending on implementation (e.g. no mip maps, only one layer, etc.) VkImage mappableImage; VkDeviceMemory mappableMemory; // Load mip map level 0 to linear tiling image err = vkCreateImage(device, &imageCreateInfo, nullptr, &mappableImage); assert(!err); // Get memory requirements for this image // like size and alignment vkGetImageMemoryRequirements(device, mappableImage, &memReqs); // Set memory allocation size to required memory size memAllocInfo.allocationSize = memReqs.size; // Get memory type that can be mapped to host memory getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); // Allocate host memory err = vkAllocateMemory(device, &memAllocInfo, nullptr, &mappableMemory); assert(!err); // Bind allocated image for use err = vkBindImageMemory(device, mappableImage, mappableMemory, 0); assert(!err); // Get sub resource layout // Mip map count, array layer, etc. VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; // Get sub resources layout // Includes row pitch, size offsets, etc. vkGetImageSubresourceLayout(device, mappableImage, &subRes, &subResLayout); assert(!err); // Map image memory err = vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, &data); assert(!err); // Copy image data into memory memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); vkUnmapMemory(device, mappableMemory); // Linear tiled images don't need to be staged // and can be directly used as textures texture.image = mappableImage; texture.deviceMemory = mappableMemory; texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Setup image memory barrier vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, texture.imageLayout); } flushSetupCommandBuffer(); // Create sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the // texture data // This means you could have multiple sampler objects // for the same texture with different settings // Similar to the samplers available with OpenGL 3.3 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.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; // Max level-of-detail should match mip level count sampler.maxLod = (useStaging) ? (float)texture.mipLevels : 0.0f; // Enable anisotropic filtering sampler.maxAnisotropy = 8; sampler.anisotropyEnable = VK_TRUE; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &texture.sampler); assert(!err); // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = (useStaging) ? texture.mipLevels : 1; view.image = texture.image; err = vkCreateImageView(device, &view, nullptr, &texture.view); assert(!err); }
vkUnmapMemory(device, uniformDataOffscreenVS.memory); } // Copy offscreen depth frame buffer contents to the depth texture void updateTexture() { // Make sure color writes to the framebuffer are finished before using it as transfer source vkTools::setImageLayout( offScreenCmdBuffer, offScreenFrameBuf.depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // Transform texture target to transfer source vkTools::setImageLayout( offScreenCmdBuffer, offScreenFrameBuf.textureTarget.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy imgCopy = {}; imgCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; imgCopy.srcSubresource.mipLevel = 0; imgCopy.srcSubresource.baseArrayLayer = 0; imgCopy.srcSubresource.layerCount = 1; imgCopy.srcOffset = { 0, 0, 0 }; imgCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; imgCopy.dstSubresource.mipLevel = 0; imgCopy.dstSubresource.baseArrayLayer = 0; imgCopy.dstSubresource.layerCount = 1; imgCopy.dstOffset = { 0, 0, 0 }; imgCopy.extent.width = TEX_DIM; imgCopy.extent.height = TEX_DIM; imgCopy.extent.depth = 1; vkCmdCopyImage( offScreenCmdBuffer, offScreenFrameBuf.depth.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, offScreenFrameBuf.textureTarget.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imgCopy); // Transform framebuffer color attachment back vkTools::setImageLayout( offScreenCmdBuffer, offScreenFrameBuf.depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); // Transform texture target back to shader read // Makes sure that writes to the textuer are finished before // it's accessed in the shader vkTools::setImageLayout( offScreenCmdBuffer, offScreenFrameBuf.textureTarget.image,