void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { u32 block_width = Util::GetBlockWidth(m_format); u32 block_x = (x + block_width - 1) / block_width; u32 block_y = (y + block_width - 1) / block_width; u32 block_h = std::max(1u, (height + block_width - 1) / block_width); // Flush CPU and GPU caches if not coherent mapping. VkDeviceSize buffer_flush_offset = block_y * m_row_stride; VkDeviceSize buffer_flush_size = block_h * m_row_stride; FlushCPUCache(buffer_flush_offset, buffer_flush_size); InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, buffer_flush_offset, buffer_flush_size); // Issue the buffer->image copy VkBufferImageCopy image_copy = { block_y * m_row_stride + block_x * m_texel_size,// VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset {width, height, 1} // VkExtent3D imageExtent }; vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); }
void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { // If we're still mapped, flush the mapped range 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 writes are visible to GPU. VkDeviceSize copy_size = m_row_stride * height; Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); // Issue the buffer->image copy VkBufferImageCopy image_copy = { 0, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset {width, height, 1} // VkExtent3D imageExtent }; vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); }
void CopyContext::updateTextureSubresource(const Texture* pTexture, uint32_t subresourceIndex, const void* pData) { mCommandsPending = true; VkBufferImageCopy vkCopy; Buffer::SharedPtr pStaging; size_t dataSize; initTexAccessParams(pTexture, subresourceIndex, vkCopy, pStaging, pData, dataSize); // Execute the copy resourceBarrier(pTexture, Resource::State::CopyDest); resourceBarrier(pStaging.get(), Resource::State::CopySource); vkCmdCopyBufferToImage(mpLowLevelData->getCommandList(), pStaging->getApiHandle(), pTexture->getApiHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vkCopy); }
void WImage::UnmapPixels() { VkDevice device = m_app->GetVulkanDevice(); vkUnmapMemory(device, m_stagingMemory); if (!m_readOnlyMap) { VkResult err = m_app->BeginCommandBuffer(); if (err) return; VkBufferImageCopy bufferCopyRegion = {}; // Setup buffer copy regions for each mip level bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = m_width; bufferCopyRegion.imageExtent.height = m_height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = 0; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy vkTools::setImageLayout( m_app->GetCommandBuffer(), m_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( m_app->GetCommandBuffer(), m_stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion ); // Change texture image layout to shader read after all mip levels have been copied vkTools::setImageLayout( m_app->GetCommandBuffer(), m_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); err = m_app->EndCommandBuffer(); } }
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { VkBufferImageCopy copy_region = {}; copy_region.bufferOffset = offset; copy_region.bufferRowLength = (uint32_t)rowLength; copy_region.bufferImageHeight = 0; // 2D copy_region.imageExtent.width = mipWidth; copy_region.imageExtent.height = mipHeight; copy_region.imageExtent.depth = 1; copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.imageSubresource.mipLevel = mip; copy_region.imageSubresource.baseArrayLayer = 0; copy_region.imageSubresource.layerCount = 1; VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); }
void VkeCubeTexture::loadCubeDDS(const char *inFile){ std::string searchPaths[] = { std::string(PROJECT_NAME), NVPWindow::sysExePath() + std::string(PROJECT_RELDIRECTORY), std::string(PROJECT_ABSDIRECTORY) }; nv_dds::CDDSImage ddsImage; for (uint32_t i = 0; i < 3; ++i){ std::string separator = ""; uint32_t strSize = searchPaths[i].size(); if(searchPaths[i].substr(strSize-1,strSize) != "/") separator = "/"; std::string filePath = searchPaths[i] + separator + std::string("images/") + std::string(inFile); ddsImage.load(filePath, true); if (ddsImage.is_valid()) break; } if (!ddsImage.is_valid()){ perror("Could not cube load texture image.\n"); exit(1); } uint32_t imgW = ddsImage.get_width(); uint32_t imgH = ddsImage.get_height(); uint32_t comCount = ddsImage.get_components(); uint32_t fmt = ddsImage.get_format(); bool isCube = ddsImage.is_cubemap(); bool isComp = ddsImage.is_compressed(); VkFormat vkFmt = VK_FORMAT_R8G8B8A8_UNORM; switch (fmt){ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: vkFmt = VK_FORMAT_BC1_RGB_SRGB_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: vkFmt = VK_FORMAT_BC2_UNORM_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: vkFmt = VK_FORMAT_BC3_UNORM_BLOCK; break; default: break; } m_width = imgW; m_height = imgH; m_format = vkFmt; VulkanDC::Device::Queue::Name queueName = "DEFAULT_GRAPHICS_QUEUE"; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID; VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VulkanDC::Device::Queue *queue = device->getQueue(queueName); VkCommandBuffer cmd = VK_NULL_HANDLE; queue->beginCommandBuffer(cmdID, &cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); imageCreateAndBind( &m_data.image, &m_data.memory, m_format, VK_IMAGE_TYPE_2D, m_width, m_height, 1, 6, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VkImageUsageFlagBits)(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), VK_IMAGE_TILING_OPTIMAL); VkBuffer cubeMapBuffer; VkDeviceMemory cubeMapMem; bufferCreate(&cubeMapBuffer, m_width*m_height * 3 * 6, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); bufferAlloc(&cubeMapBuffer, &cubeMapMem, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); if (m_memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT){ imageSetLayoutBarrier(cmdID, queueName, m_data.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL); for (uint32_t i = 0; i < 6; ++i){ void *data = NULL; VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = i; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(), cubeMapMem, layout.offset, layout.size, 0, &data), "Could not map memory for image.\n"); const nv_dds::CTexture &mipmap = ddsImage.get_cubemap_face(i); memcpy(data, (void *)mipmap, layout.size); vkUnmapMemory(getDefaultDevice(), cubeMapMem); } VkBufferImageCopy biCpyRgn[6]; for (uint32_t k = 0; k < 6; ++k){ VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = k; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); biCpyRgn[k].bufferOffset = layout.offset; biCpyRgn[k].bufferImageHeight = 0; biCpyRgn[k].bufferRowLength = 0; biCpyRgn[k].imageExtent.width = m_width; biCpyRgn[k].imageExtent.height = m_height; biCpyRgn[k].imageExtent.depth = 1; biCpyRgn[k].imageOffset.x = 0; biCpyRgn[k].imageOffset.y = 0; biCpyRgn[k].imageOffset.z = 0; biCpyRgn[k].imageSubresource.baseArrayLayer = k; biCpyRgn[k].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; biCpyRgn[k].imageSubresource.layerCount = 1; biCpyRgn[k].imageSubresource.mipLevel = 0; } VkFence copyFence; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(fenceInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(device->getVKDevice(), &fenceInfo, NULL, ©Fence); vkCmdCopyBufferToImage(cmd, cubeMapBuffer, m_data.image, m_data.imageLayout, 6, biCpyRgn); queue->flushCommandBuffer(cmdID, ©Fence); vkWaitForFences(device->getVKDevice(), 1, ©Fence, VK_TRUE, 100000000000); vkDestroyBuffer(device->getVKDevice(), cubeMapBuffer, NULL); vkFreeMemory(device->getVKDevice(), cubeMapMem, NULL); } VkSamplerCreateInfo sampler; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler.pNext = NULL; sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 1; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VkImageViewCreateInfo view; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = m_format; view.components.r = VK_COMPONENT_SWIZZLE_R; view.components.g = VK_COMPONENT_SWIZZLE_G; view.components.b = VK_COMPONENT_SWIZZLE_B; view.components.a = VK_COMPONENT_SWIZZLE_A; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.layerCount = 1; VKA_CHECK_ERROR(vkCreateSampler(getDefaultDevice(), &sampler, NULL, &m_data.sampler), "Could not create sampler for image texture.\n"); view.image = m_data.image; VKA_CHECK_ERROR(vkCreateImageView(getDefaultDevice(), &view, NULL, &m_data.view), "Could not create image view for texture.\n"); }
void VkeCubeTexture::loadTextureFiles(const char **inPath){ bool imagesOK = true; VKA_INFO_MSG("Loading Cube Texture.\n"); for (uint32_t i = 0; i < 6; ++i){ if (!loadTexture(inPath[i], NULL, NULL, &m_width, &m_height)){ VKA_ERROR_MSG("Error loading texture image.\n"); printf("Texture : %d not available (%s).\n", i, inPath[i]); return; } } VulkanDC::Device::Queue::Name queueName = "DEFAULT_GRAPHICS_QUEUE"; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID; VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VulkanDC::Device::Queue *queue = device->getQueue(queueName); VkCommandBuffer cmd = VK_NULL_HANDLE; queue->beginCommandBuffer(cmdID, &cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); imageCreateAndBind( &m_data.image, &m_data.memory, m_format, VK_IMAGE_TYPE_2D, m_width, m_height, 1, 6, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VkImageUsageFlagBits)( VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ), VK_IMAGE_TILING_OPTIMAL); VkBuffer cubeMapBuffer; VkDeviceMemory cubeMapMem; bufferCreate(&cubeMapBuffer, m_width*m_height * 4 * 6, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); bufferAlloc(&cubeMapBuffer, &cubeMapMem, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VkDeviceSize dSize = m_width * m_height * 4; uint32_t rowPitch = m_width * 4; if (m_memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT){ imageSetLayoutBarrier(cmdID, queueName, m_data.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL); for (uint32_t i = 0; i < 6; ++i){ void *data = NULL; VkDeviceSize ofst = dSize*i; VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(),cubeMapMem, ofst, dSize, 0, &data), "Could not map memory for image.\n"); if (!loadTexture(inPath[i], (uint8_t**)&data, rowPitch, &m_width, &m_height)){ VKA_ERROR_MSG("Could not load final image.\n"); } vkUnmapMemory(getDefaultDevice(), cubeMapMem); } VkBufferImageCopy biCpyRgn[6]; for (uint32_t k = 0; k < 6; ++k){ VkDeviceSize ofst = dSize*k; biCpyRgn[k].bufferOffset = ofst; biCpyRgn[k].bufferImageHeight = 0; biCpyRgn[k].bufferRowLength = 0; biCpyRgn[k].imageExtent.width = m_width; biCpyRgn[k].imageExtent.height = m_height; biCpyRgn[k].imageExtent.depth = 1; biCpyRgn[k].imageOffset.x = 0; biCpyRgn[k].imageOffset.y = 0; biCpyRgn[k].imageOffset.z = 0; biCpyRgn[k].imageSubresource.baseArrayLayer = k; biCpyRgn[k].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; biCpyRgn[k].imageSubresource.layerCount = 1; biCpyRgn[k].imageSubresource.mipLevel = 0; } VkFence copyFence; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(fenceInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(device->getVKDevice(), &fenceInfo,NULL , ©Fence); vkCmdCopyBufferToImage(cmd, cubeMapBuffer, m_data.image, m_data.imageLayout, 6, biCpyRgn); queue->flushCommandBuffer(cmdID , ©Fence); vkWaitForFences(device->getVKDevice(), 1, ©Fence, VK_TRUE, 100000000000); vkDestroyBuffer(device->getVKDevice(), cubeMapBuffer, NULL); vkFreeMemory(device->getVKDevice(), cubeMapMem, NULL); } VkSamplerCreateInfo sampler; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler.pNext = NULL; sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 1; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VkImageViewCreateInfo view; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = m_format; view.components = { 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, 0 }; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.layerCount = 1; VKA_CHECK_ERROR(vkCreateSampler(getDefaultDevice(), &sampler,NULL, &m_data.sampler), "Could not create sampler for image texture.\n"); view.image = m_data.image; VKA_CHECK_ERROR(vkCreateImageView(getDefaultDevice(), &view,NULL, &m_data.view), "Could not create image view for texture.\n"); VKA_INFO_MSG("Created CUBE Image Texture.\n"); }
WError WImage::CreateFromPixelsArray( void* pixels, unsigned int width, unsigned int height, bool bDynamic, unsigned int num_components, VkFormat fmt, size_t comp_size) { VkDevice device = m_app->GetVulkanDevice(); VkResult err; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; VkBufferImageCopy bufferCopyRegion = {}; VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(); uint8_t *data; VkFormat format = fmt; if (fmt == VK_FORMAT_UNDEFINED) { switch (num_components) { case 1: format = VK_FORMAT_R32_SFLOAT; break; case 2: format = VK_FORMAT_R32G32_SFLOAT; break; case 3: format = VK_FORMAT_R32G32B32_SFLOAT; break; case 4: format = VK_FORMAT_R32G32B32A32_SFLOAT; break; default: return WError(W_INVALIDPARAM); } comp_size = 4; } _DestroyResources(); // Create a host-visible staging buffer that contains the raw image data bufferCreateInfo.size = width * height * num_components * comp_size; // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(device, &bufferCreateInfo, nullptr, &m_stagingBuffer); if (err) { _DestroyResources(); return WError(W_OUTOFMEMORY); } // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, m_stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer m_app->GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &m_stagingMemory); if (err) { vkDestroyBuffer(device, m_stagingBuffer, nullptr); _DestroyResources(); return WError(W_OUTOFMEMORY); } err = vkBindBufferMemory(device, m_stagingBuffer, m_stagingMemory, 0); if (err) goto free_buffers; // Copy texture data into staging buffer if (pixels) { err = vkMapMemory(device, m_stagingMemory, 0, memReqs.size, 0, (void **)&data); if (err) goto free_buffers; memcpy(data, pixels, bufferCreateInfo.size); vkUnmapMemory(device, m_stagingMemory); } // Create optimal tiled target image imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; // TODO: USE m_app->engineParams["numGeneratedMips"] imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.extent = { width, height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage(device, &imageCreateInfo, nullptr, &m_image); if (err) goto free_buffers; vkGetImageMemoryRequirements(device, m_image, &memReqs); memAllocInfo.allocationSize = memReqs.size; m_app->GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &m_deviceMemory); if (err) goto free_buffers; err = vkBindImageMemory(device, m_image, m_deviceMemory, 0); if (err) goto free_buffers; // Setup buffer copy regions for each mip level bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = width; bufferCopyRegion.imageExtent.height = height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = 0; err = m_app->BeginCommandBuffer(); if (err) goto free_buffers; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy vkTools::setImageLayout( m_app->GetCommandBuffer(), m_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( m_app->GetCommandBuffer(), m_stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion ); // Change texture image layout to shader read after all mip levels have been copied vkTools::setImageLayout( m_app->GetCommandBuffer(), m_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); err = m_app->EndCommandBuffer(); if (err) goto free_buffers; free_buffers: // Clean up staging resources if (err || !bDynamic) { vkFreeMemory(device, m_stagingMemory, nullptr); vkDestroyBuffer(device, m_stagingBuffer, nullptr); m_stagingMemory = VK_NULL_HANDLE; m_stagingBuffer = VK_NULL_HANDLE; } if (err) { _DestroyResources(); return WError(W_OUTOFMEMORY); } // 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 = 1; // mips view.image = m_image; err = vkCreateImageView(device, &view, nullptr, &m_view); if (err) { _DestroyResources(); return WError(W_UNABLETOCREATEIMAGE); } m_width = width; m_height = height; m_numComponents = num_components; m_componentSize = comp_size; m_mapSize = bufferCreateInfo.size; m_format = format; return WError(W_SUCCEEDED); }
void loadTexture(std::string fileName, VkFormat format, bool forceLinearTiling) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, fileName.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2d tex2D(gli::load((const char*)textureData, size)); #else gli::texture2d tex2D(gli::load(fileName)); #endif assert(!tex2D.empty()); VkFormatProperties formatProperties; texture.width = static_cast<uint32_t>(tex2D[0].extent().x); texture.height = static_cast<uint32_t>(tex2D[0].extent().y); // calculate num of mip maps // numLevels = 1 + floor(log2(max(w, h, d))) // Calculated as log2(max(width, height, depth))c + 1 (see specs) texture.mipLevels = floor(log2(std::max(texture.width, texture.height))) + 1; // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); // Mip-chain generation requires support for blit source and destination assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT); VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(); bufferCreateInfo.size = tex2D.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, tex2D.data(), tex2D.size()); vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = texture.mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { texture.width, texture.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image)); vkGetImageMemoryRequirements(device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, texture.image, texture.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.levelCount = 1; subresourceRange.layerCount = 1; // Optimal image will be used as destination for the copy, so we must transfer from our initial undefined image layout to the transfer destination layout vkTools::setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the first mip of the chain, remaining mips will be generated VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texture.width; bufferCopyRegion.imageExtent.height = texture.height; bufferCopyRegion.imageExtent.depth = 1; vkCmdCopyBufferToImage(copyCmd, stagingBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); // Transition first mip level to transfer source for read during blit texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); // Generate the mip chain // --------------------------------------------------------------- // We copy down the whole mip chain doing a blit from mip-1 to mip // An alternative way would be to always blit from the first mip level and sample that one down VkCommandBuffer blitCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Copy down mips from n-1 to n for (int32_t i = 1; i < texture.mipLevels; i++) { VkImageBlit imageBlit{}; // Source imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlit.srcSubresource.layerCount = 1; imageBlit.srcSubresource.mipLevel = i-1; imageBlit.srcOffsets[1].x = int32_t(texture.width >> (i - 1)); imageBlit.srcOffsets[1].y = int32_t(texture.height >> (i - 1)); imageBlit.srcOffsets[1].z = 1; // Destination imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlit.dstSubresource.layerCount = 1; imageBlit.dstSubresource.mipLevel = i; imageBlit.dstOffsets[1].x = int32_t(texture.width >> i); imageBlit.dstOffsets[1].y = int32_t(texture.height >> i); imageBlit.dstOffsets[1].z = 1; VkImageSubresourceRange mipSubRange = {}; mipSubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; mipSubRange.baseMipLevel = i; mipSubRange.levelCount = 1; mipSubRange.layerCount = 1; // Transiton current mip level to transfer dest vkTools::setImageLayout( blitCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipSubRange, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT); // Blit from previous level vkCmdBlitImage( blitCmd, texture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlit, VK_FILTER_LINEAR); // Transiton current mip level to transfer source for read in next iteration vkTools::setImageLayout( blitCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mipSubRange, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); } // After the loop, all mip layers are in TRANSFER_SRC layout, so transition all to SHADER_READ subresourceRange.levelCount = texture.mipLevels; vkTools::setImageLayout( blitCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(blitCmd, queue, true); // --------------------------------------------------------------- // Create samplers samplers.resize(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_MIRRORED_REPEAT; sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sampler.maxAnisotropy = 1.0; sampler.anisotropyEnable = VK_FALSE; // Without mip mapping VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &samplers[0])); // With mip mapping sampler.maxLod = (float)texture.mipLevels; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &samplers[1])); // With mip mapping and anisotropic filtering if (vulkanDevice->features.samplerAnisotropy) { sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = VK_TRUE; } VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &samplers[2])); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = texture.image; 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; view.subresourceRange.levelCount = texture.mipLevels; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &texture.view)); }
bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); size_t upload_size = width*height*4*sizeof(char); VkResult err; // Create the Image: { VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.extent.width = width; info.extent.height = height; info.extent.depth = 1; info.mipLevels = 1; info.arrayLayers = 1; info.samples = VK_SAMPLE_COUNT_1_BIT; info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; err = vkCreateImage(g_Device, &info, g_Allocator, &g_FontImage); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetImageMemoryRequirements(g_Device, g_FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_FontMemory); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindImageMemory(g_Device, g_FontImage, g_FontMemory, 0); ImGui_ImplGlfwVulkan_VkResult(err); } // Create the Image View: { VkResult err; VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = g_FontImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; err = vkCreateImageView(g_Device, &info, g_Allocator, &g_FontView); ImGui_ImplGlfwVulkan_VkResult(err); } // Update the Descriptor Set: { VkDescriptorImageInfo desc_image[1] = {}; desc_image[0].sampler = g_FontSampler; desc_image[0].imageView = g_FontView; desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet write_desc[1] = {}; write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_desc[0].dstSet = g_DescriptorSet; write_desc[0].descriptorCount = 1; write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_desc[0].pImageInfo = desc_image; vkUpdateDescriptorSets(g_Device, 1, write_desc, 0, NULL); } // Create the Upload Buffer: { VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = upload_size; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_UploadBuffer); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_UploadBuffer, &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_UploadBufferMemory); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_UploadBuffer, g_UploadBufferMemory, 0); ImGui_ImplGlfwVulkan_VkResult(err); } // Upload to Buffer: { char* map = NULL; err = vkMapMemory(g_Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); ImGui_ImplGlfwVulkan_VkResult(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = g_UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(g_Device, 1, range); ImGui_ImplGlfwVulkan_VkResult(err); vkUnmapMemory(g_Device, g_UploadBufferMemory); } // Copy to Image: { VkImageMemoryBarrier copy_barrier[1] = {}; copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].image = g_FontImage; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; region.imageExtent.width = width; region.imageExtent.height = height; vkCmdCopyBufferToImage(command_buffer, g_UploadBuffer, g_FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].image = g_FontImage; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier); } // Store our identifier io.Fonts->TexID = (void *)(intptr_t)g_FontImage; return true; }
/** * Prepare all vulkan resources required to render the font * The text overlay uses separate resources for descriptors (pool, sets, layouts), pipelines and command buffers */ void prepareResources() { static unsigned char font24pixels[STB_FONT_HEIGHT][STB_FONT_WIDTH]; STB_FONT_NAME(stbFontData, font24pixels, STB_FONT_HEIGHT); // Command buffer // Pool VkCommandPoolCreateInfo cmdPoolInfo = {}; cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VK_CHECK_RESULT(vkCreateCommandPool(vulkanDevice->logicalDevice, &cmdPoolInfo, nullptr, &commandPool)); VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo( commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, (uint32_t)cmdBuffers.size()); VK_CHECK_RESULT(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data())); // Vertex buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &vertexBuffer, MAX_CHAR_COUNT * sizeof(glm::vec4))); // Map persistent vertexBuffer.map(); // Font texture VkImageCreateInfo imageInfo = vks::initializers::imageCreateInfo(); imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.format = VK_FORMAT_R8_UNORM; imageInfo.extent.width = STB_FONT_WIDTH; imageInfo.extent.height = STB_FONT_HEIGHT; imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageInfo, nullptr, &image)); VkMemoryRequirements memReqs; VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo(); vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, image, &memReqs); allocInfo.allocationSize = memReqs.size; allocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &allocInfo, nullptr, &imageMemory)); VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, image, imageMemory, 0)); // Staging vks::Buffer stagingBuffer; VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer, allocInfo.allocationSize)); stagingBuffer.map(); memcpy(stagingBuffer.mapped, &font24pixels[0][0], STB_FONT_WIDTH * STB_FONT_HEIGHT); // Only one channel, so data size = W * H (*R8) stagingBuffer.unmap(); // Copy to image VkCommandBuffer copyCmd; cmdBufAllocateInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, ©Cmd)); VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo)); // Prepare for transfer vks::tools::setImageLayout( copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = STB_FONT_WIDTH; bufferCopyRegion.imageExtent.height = STB_FONT_HEIGHT; bufferCopyRegion.imageExtent.depth = 1; vkCmdCopyBufferToImage( copyCmd, stagingBuffer.buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion ); // Prepare for shader read vks::tools::setImageLayout( copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd)); VkSubmitInfo submitInfo = vks::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = ©Cmd; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueWaitIdle(queue)); stagingBuffer.destroy(); vkFreeCommandBuffers(vulkanDevice->logicalDevice, commandPool, 1, ©Cmd); VkImageViewCreateInfo imageViewInfo = vks::initializers::imageViewCreateInfo(); imageViewInfo.image = image; imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewInfo.format = imageInfo.format; imageViewInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; imageViewInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &imageViewInfo, nullptr, &view)); // Sampler VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo(); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipLodBias = 0.0f; samplerInfo.compareOp = VK_COMPARE_OP_NEVER; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 1.0f; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; samplerInfo.maxAnisotropy = 1.0f; VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &samplerInfo, nullptr, &sampler)); // Descriptor // Font uses a separate descriptor pool std::array<VkDescriptorPoolSize, 1> poolSizes; poolSizes[0] = vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1); VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo( static_cast<uint32_t>(poolSizes.size()), poolSizes.data(), 1); VK_CHECK_RESULT(vkCreateDescriptorPool(vulkanDevice->logicalDevice, &descriptorPoolInfo, nullptr, &descriptorPool)); // Descriptor set layout std::array<VkDescriptorSetLayoutBinding, 1> setLayoutBindings; setLayoutBindings[0] = vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0); VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo( setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size())); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(vulkanDevice->logicalDevice, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); // Pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = vks::initializers::pipelineLayoutCreateInfo( &descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(vulkanDevice->logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout)); // Descriptor set VkDescriptorSetAllocateInfo descriptorSetAllocInfo = vks::initializers::descriptorSetAllocateInfo( descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &descriptorSetAllocInfo, &descriptorSet)); VkDescriptorImageInfo texDescriptor = vks::initializers::descriptorImageInfo( sampler, view, VK_IMAGE_LAYOUT_GENERAL); std::array<VkWriteDescriptorSet, 1> writeDescriptorSets; writeDescriptorSets[0] = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texDescriptor); vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); // Pipeline cache VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; VK_CHECK_RESULT(vkCreatePipelineCache(vulkanDevice->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); // Command buffer execution fence VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo(); VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &fence)); }
void vkImageBase::updateMipVkImage(uint64_t texSize, std::vector<void *> &pixels, std::vector<ImageInfo> &bitmapInfos, std::vector<VkBufferImageCopy> &bufferCopyRegions, VkImageViewType target, VkFormat internalFormat, int mipLevels, VkImageCreateFlags flags) { VkResult err; bool pass; VulkanRenderer *vk_renderer = static_cast<VulkanRenderer *>(Renderer::getInstance()); VkDevice device = vk_renderer->getDevice(); VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(vk_renderer->getPhysicalDevice(), internalFormat, &formatProperties); VkBuffer texBuffer; VkDeviceMemory texMemory; VkMemoryAllocateInfo memoryAllocateInfo = {}; memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memoryAllocateInfo.pNext = NULL; memoryAllocateInfo.allocationSize = 0; memoryAllocateInfo.memoryTypeIndex = 0; err = vkCreateBuffer(device, gvr::BufferCreateInfo(texSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT), nullptr, &texBuffer); GVR_VK_CHECK(!err); // Obtain the requirements on memory for this buffer VkMemoryRequirements mem_reqs; vkGetBufferMemoryRequirements(device, texBuffer, &mem_reqs); assert(!err); memoryAllocateInfo.allocationSize = mem_reqs.size; pass = vk_renderer->GetMemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memoryAllocateInfo.memoryTypeIndex); assert(pass); size = mem_reqs.size; err = vkAllocateMemory(device, gvr::MemoryAllocateInfo(mem_reqs.size, memoryAllocateInfo.memoryTypeIndex), NULL, &texMemory); unsigned char *texData; err = vkMapMemory(device, texMemory, 0, memoryAllocateInfo.allocationSize, 0, (void **) &texData); assert(!err); int i = 0; for (auto &buffer_copy_region: bufferCopyRegions) { memcpy(texData + buffer_copy_region.bufferOffset, pixels[i], bitmapInfos[i].size); i++; } vkUnmapMemory(device, texMemory); // Bind our buffer to the memory err = vkBindBufferMemory(device, texBuffer, texMemory, 0); assert(!err); err = vkCreateImage(device, gvr::ImageCreateInfo(VK_IMAGE_TYPE_2D, internalFormat, bitmapInfos[0].width, bitmapInfos[0].height, 1, mipLevels, pixels.size(), VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, flags, getVKSampleBit(mSampleCount), VK_IMAGE_LAYOUT_UNDEFINED), NULL, &imageHandle); assert(!err); vkGetImageMemoryRequirements(device, imageHandle, &mem_reqs); memoryAllocateInfo.allocationSize = mem_reqs.size; pass = vk_renderer->GetMemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryAllocateInfo.memoryTypeIndex); assert(pass); /* allocate memory */ err = vkAllocateMemory(device, &memoryAllocateInfo, NULL, &device_memory); assert(!err); /* bind memory */ err = vkBindImageMemory(device, imageHandle, device_memory, 0); assert(!err); // Reset the setup command buffer VkCommandBuffer textureCmdBuffer; vk_renderer->initCmdBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, textureCmdBuffer); vkResetCommandBuffer(textureCmdBuffer, 0); VkCommandBufferInheritanceInfo commandBufferInheritanceInfo = {}; commandBufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; commandBufferInheritanceInfo.pNext = NULL; commandBufferInheritanceInfo.renderPass = VK_NULL_HANDLE; commandBufferInheritanceInfo.subpass = 0; commandBufferInheritanceInfo.framebuffer = VK_NULL_HANDLE; commandBufferInheritanceInfo.occlusionQueryEnable = VK_FALSE; commandBufferInheritanceInfo.queryFlags = 0; commandBufferInheritanceInfo.pipelineStatistics = 0; VkCommandBufferBeginInfo setupCmdsBeginInfo; setupCmdsBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; setupCmdsBeginInfo.pNext = NULL; setupCmdsBeginInfo.flags = 0; setupCmdsBeginInfo.pInheritanceInfo = &commandBufferInheritanceInfo; // Begin recording to the command buffer. vkBeginCommandBuffer(textureCmdBuffer, &setupCmdsBeginInfo); VkImageMemoryBarrier imageMemoryBarrier = {}; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.pNext = NULL; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.layerCount = pixels.size(); imageMemoryBarrier.srcAccessMask = 0; imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; // Optimal image will be used as destination for the copy, so we must transfer from our initial undefined image layout to the transfer destination layout setImageLayout(imageMemoryBarrier, textureCmdBuffer, imageHandle, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageMemoryBarrier.subresourceRange); vkCmdCopyBufferToImage( textureCmdBuffer, texBuffer, imageHandle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data()); imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; setImageLayout(imageMemoryBarrier, textureCmdBuffer, imageHandle, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageMemoryBarrier.subresourceRange); // We are finished recording operations. vkEndCommandBuffer(textureCmdBuffer); VkCommandBuffer buffers[1]; buffers[0] = textureCmdBuffer; VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = NULL; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = NULL; submit_info.pWaitDstStageMask = NULL; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffers[0]; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; VkQueue queue = vk_renderer->getQueue(); // Submit to our shared graphics queue. err = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); assert(!err); // Wait for the queue to become idle. err = vkQueueWaitIdle(queue); assert(!err); vkFreeMemory(device, texMemory, nullptr); vkDestroyBuffer(device, texBuffer, nullptr); if(mipLevels > 1) createMipLevels(formatProperties, vk_renderer, setupCmdsBeginInfo, bufferCopyRegions, mipLevels, bitmapInfos, imageMemoryBarrier, submit_info, buffers, queue); err = vkCreateImageView(device, gvr::ImageViewCreateInfo(imageHandle, target, internalFormat, mipLevels,0, pixels.size(), VK_IMAGE_ASPECT_COLOR_BIT), NULL, &imageView); assert(!err); }
// Generate randomized noise and upload it to the 3D texture using staging void updateNoiseTexture() { const uint32_t texMemSize = texture.width * texture.height * texture.depth; uint8_t *data = new uint8_t[texMemSize]; memset(data, 0, texMemSize); // Generate perlin based noise std::cout << "Generating " << texture.width << " x " << texture.height << " x " << texture.depth << " noise texture..." << std::endl; auto tStart = std::chrono::high_resolution_clock::now(); PerlinNoise<float> perlinNoise; FractalNoise<float> fractalNoise(perlinNoise); std::default_random_engine rndEngine(std::random_device{}()); const int32_t noiseType = rand() % 2; const float noiseScale = static_cast<float>(rand() % 10) + 4.0f; #pragma omp parallel for for (int32_t z = 0; z < (int32_t)texture.depth; z++) { for (int32_t y = 0; y < (int32_t)texture.height; y++) { for (int32_t x = 0; x < (int32_t)texture.width; x++) { float nx = (float)x / (float)texture.width; float ny = (float)y / (float)texture.height; float nz = (float)z / (float)texture.depth; #define FRACTAL #ifdef FRACTAL float n = fractalNoise.noise(nx * noiseScale, ny * noiseScale, nz * noiseScale); #else float n = 20.0 * perlinNoise.noise(nx, ny, nz); #endif n = n - floor(n); data[x + y * texture.width + z * texture.width * texture.height] = static_cast<uint8_t>(floor(n * 255)); } } } auto tEnd = std::chrono::high_resolution_clock::now(); auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count(); std::cout << "Done in " << tDiff << "ms" << std::endl; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; // Buffer object VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(); bufferCreateInfo.size = texMemSize; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Allocate host visible memory for data upload VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *mapped; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&mapped)); memcpy(mapped, data, texMemSize); vkUnmapMemory(device, stagingMemory); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Image barrier for optimal image // The sub resource range describes the regions of the image we will be transition VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = 1; // Optimal image will be used as destination for the copy, so we must transfer from our // initial undefined image layout to the transfer destination layout vkTools::setImageLayout( copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy 3D noise data to texture // Setup buffer copy regions VkBufferImageCopy bufferCopyRegion{}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texture.width; bufferCopyRegion.imageExtent.height = texture.height; bufferCopyRegion.imageExtent.depth = texture.depth; vkCmdCopyBufferToImage( copyCmd, stagingBuffer, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Clean up staging resources delete[] data; vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); regenerateNoise = false; }
void BaseImage::UploadContent() { VkBufferCreateInfo srcBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; srcBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; srcBufCreateInfo.size = 4 * m_CreateInfo.extent.width * m_CreateInfo.extent.height; VmaAllocationCreateInfo srcBufAllocCreateInfo = {}; srcBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; srcBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; VkBuffer srcBuf = nullptr; VmaAllocation srcBufAlloc = nullptr; VmaAllocationInfo srcAllocInfo = {}; TEST( vmaCreateBuffer(g_hAllocator, &srcBufCreateInfo, &srcBufAllocCreateInfo, &srcBuf, &srcBufAlloc, &srcAllocInfo) == VK_SUCCESS ); // Fill texels with: r = x % 255, g = u % 255, b = 13, a = 25 uint32_t* srcBufPtr = (uint32_t*)srcAllocInfo.pMappedData; for(uint32_t y = 0, sizeY = m_CreateInfo.extent.height; y < sizeY; ++y) { for(uint32_t x = 0, sizeX = m_CreateInfo.extent.width; x < sizeX; ++x, ++srcBufPtr) { const uint8_t r = (uint8_t)x; const uint8_t g = (uint8_t)y; const uint8_t b = 13; const uint8_t a = 25; *srcBufPtr = (uint32_t)r << 24 | (uint32_t)g << 16 | (uint32_t)b << 8 | (uint32_t)a; } } BeginSingleTimeCommands(); // Barrier undefined to transfer dst. { VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = m_Image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.levelCount = 1; vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask 0, // dependencyFlags 0, nullptr, // memoryBarriers 0, nullptr, // bufferMemoryBarriers 1, &barrier); // imageMemoryBarriers } // CopyBufferToImage { VkBufferImageCopy region = {}; region.bufferOffset = 0; region.bufferRowLength = 0; // Zeros mean tightly packed. region.bufferImageHeight = 0; // Zeros mean tightly packed. region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = { 0, 0, 0 }; region.imageExtent = m_CreateInfo.extent; vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, srcBuf, m_Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); } // Barrier transfer dst to fragment shader read only. { VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = m_Image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.levelCount = 1; vkCmdPipelineBarrier(g_hTemporaryCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask 0, // dependencyFlags 0, nullptr, // memoryBarriers 0, nullptr, // bufferMemoryBarriers 1, &barrier); // imageMemoryBarriers } EndSingleTimeCommands(); vmaDestroyBuffer(g_hAllocator, srcBuf, srcBufAlloc); }
bool Cubemap::Init(VulkanDevice * device, VulkanCommandBuffer * cmdBuffer, std::string cubemapDir) { VkResult result; void * pData; mipMapLevels = -1; std::vector<MipMap> mipMapsRight; std::vector<MipMap> mipMapsLeft; std::vector<MipMap> mipMapsTop; std::vector<MipMap> mipMapsBottom; std::vector<MipMap> mipMapsBack; std::vector<MipMap> mipMapsFront; // Read each cube face if (!ReadCubeFace(cubemapDir + "/right.rct", mipMapsRight)) return false; if (!ReadCubeFace(cubemapDir + "/left.rct", mipMapsLeft)) return false; if (!ReadCubeFace(cubemapDir + "/up.rct", mipMapsTop)) return false; if (!ReadCubeFace(cubemapDir + "/down.rct", mipMapsBottom)) return false; if (!ReadCubeFace(cubemapDir + "/back.rct", mipMapsBack)) return false; if (!ReadCubeFace(cubemapDir + "/front.rct", mipMapsFront)) return false; unsigned int totalTextureSize = 0; for (unsigned int i = 0; i < mipMapsRight.size(); i++) totalTextureSize += mipMapsRight[i].size; for (unsigned int i = 0; i < mipMapsLeft.size(); i++) totalTextureSize += mipMapsLeft[i].size; for (unsigned int i = 0; i < mipMapsTop.size(); i++) totalTextureSize += mipMapsTop[i].size; for (unsigned int i = 0; i < mipMapsBottom.size(); i++) totalTextureSize += mipMapsBottom[i].size; for (unsigned int i = 0; i < mipMapsBack.size(); i++) totalTextureSize += mipMapsBack[i].size; for (unsigned int i = 0; i < mipMapsFront.size(); i++) totalTextureSize += mipMapsFront[i].size; // Create an array of bits which stores all of the texture data std::vector<unsigned char> textureData; for (unsigned int i = 0; i < mipMapsRight.size(); i++) for (unsigned int j = 0; j < mipMapsRight[i].size; j++) textureData.push_back(mipMapsRight[i].data[j]); for (unsigned int i = 0; i < mipMapsLeft.size(); i++) for (unsigned int j = 0; j < mipMapsLeft[i].size; j++) textureData.push_back(mipMapsLeft[i].data[j]); for (unsigned int i = 0; i < mipMapsTop.size(); i++) for (unsigned int j = 0; j < mipMapsTop[i].size; j++) textureData.push_back(mipMapsTop[i].data[j]); for (unsigned int i = 0; i < mipMapsBottom.size(); i++) for (unsigned int j = 0; j < mipMapsBottom[i].size; j++) textureData.push_back(mipMapsBottom[i].data[j]); for (unsigned int i = 0; i < mipMapsBack.size(); i++) for (unsigned int j = 0; j < mipMapsBack[i].size; j++) textureData.push_back(mipMapsBack[i].data[j]); for (unsigned int i = 0; i < mipMapsFront.size(); i++) for (unsigned int j = 0; j < mipMapsFront[i].size; j++) textureData.push_back(mipMapsFront[i].data[j]); for (int i = 0; i < mipMapsRight.size(); i++) delete[] mipMapsRight[i].data; for (int i = 0; i < mipMapsLeft.size(); i++) delete[] mipMapsLeft[i].data; for (int i = 0; i < mipMapsTop.size(); i++) delete[] mipMapsTop[i].data; for (int i = 0; i < mipMapsBottom.size(); i++) delete[] mipMapsBottom[i].data; for (int i = 0; i < mipMapsBack.size(); i++) delete[] mipMapsBack[i].data; for (int i = 0; i < mipMapsFront.size(); i++) delete[] mipMapsFront[i].data; VkMemoryRequirements memReq{}; VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCI{}; bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCI.size = totalTextureSize; bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; result = vkCreateBuffer(device->GetDevice(), &bufferCI, VK_NULL_HANDLE, &stagingBuffer); if (result != VK_SUCCESS) return false; vkGetBufferMemoryRequirements(device->GetDevice(), stagingBuffer, &memReq); allocInfo.allocationSize = memReq.size; if (!device->MemoryTypeFromProperties(memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &allocInfo.memoryTypeIndex)) return false; result = vkAllocateMemory(device->GetDevice(), &allocInfo, VK_NULL_HANDLE, &stagingMemory); if (result != VK_SUCCESS) return false; result = vkBindBufferMemory(device->GetDevice(), stagingBuffer, stagingMemory, 0); if (result != VK_SUCCESS) return false; result = vkMapMemory(device->GetDevice(), stagingMemory, 0, memReq.size, 0, &pData); if (result != VK_SUCCESS) return false; memcpy(pData, textureData.data(), textureData.size()); vkUnmapMemory(device->GetDevice(), stagingMemory); std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; for (int face = 0; face < 6; face++) { for (unsigned int level = 0; level < mipMapLevels; level++) { VkBufferImageCopy bufferCopyRegion{}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; // Every face has the same width, height and mipmap bufferCopyRegion.imageExtent.width = mipMapsRight[level].width; bufferCopyRegion.imageExtent.height = mipMapsRight[level].height; offset += (uint32_t)mipMapsRight[level].size; bufferCopyRegions.push_back(bufferCopyRegion); } } VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = VK_FORMAT_R8G8B8A8_UNORM; imageCI.mipLevels = mipMapLevels; imageCI.arrayLayers = 6; imageCI.samples = VK_SAMPLE_COUNT_1_BIT; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCI.extent.width = mipMapsRight[0].width; imageCI.extent.height = mipMapsRight[0].height; imageCI.extent.depth = 1; imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; result = vkCreateImage(device->GetDevice(), &imageCI, VK_NULL_HANDLE, &textureImage); if (result != VK_SUCCESS) return false; vkGetImageMemoryRequirements(device->GetDevice(), textureImage, &memReq); VkMemoryAllocateInfo memAlloc{}; memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAlloc.allocationSize = memReq.size; if (!device->MemoryTypeFromProperties(memReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAlloc.memoryTypeIndex)) return false; result = vkAllocateMemory(device->GetDevice(), &memAlloc, VK_NULL_HANDLE, &textureMemory); if (result != VK_SUCCESS) return false; result = vkBindImageMemory(device->GetDevice(), textureImage, textureMemory, 0); if (result != VK_SUCCESS) return false; VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; range.baseMipLevel = 0; range.levelCount = mipMapLevels; range.layerCount = 6; cmdBuffer->BeginRecording(); VulkanTools::SetImageLayout(textureImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &range, cmdBuffer, device, false); vkCmdCopyBufferToImage(cmdBuffer->GetCommandBuffer(), stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)bufferCopyRegions.size(), bufferCopyRegions.data()); VulkanTools::SetImageLayout(textureImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, &range, cmdBuffer, device, false); cmdBuffer->EndRecording(); cmdBuffer->Execute(device, NULL, NULL, NULL, true); vkFreeMemory(device->GetDevice(), stagingMemory, VK_NULL_HANDLE); vkDestroyBuffer(device->GetDevice(), stagingBuffer, VK_NULL_HANDLE); VkImageViewCreateInfo viewCI{}; viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewCI.image = textureImage; viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE; viewCI.format = VK_FORMAT_R8G8B8A8_UNORM; viewCI.components.r = VK_COMPONENT_SWIZZLE_R; viewCI.components.g = VK_COMPONENT_SWIZZLE_G; viewCI.components.b = VK_COMPONENT_SWIZZLE_B; viewCI.components.a = VK_COMPONENT_SWIZZLE_A; viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewCI.subresourceRange.baseMipLevel = 0; viewCI.subresourceRange.baseArrayLayer = 0; viewCI.subresourceRange.layerCount = 6; viewCI.subresourceRange.levelCount = mipMapLevels; result = vkCreateImageView(device->GetDevice(), &viewCI, VK_NULL_HANDLE, &textureImageView); if (result != VK_SUCCESS) return false; return true; }
void VulkanTexturedQuad::CreateTexture (VkCommandBuffer uploadCommandList) { int width, height; auto image = LoadImageFromMemory (RubyTexture, sizeof (RubyTexture), 1, &width, &height); VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = nullptr; imageCreateInfo.queueFamilyIndexCount = 1; uint32_t queueFamilyIndex = static_cast<uint32_t> (queueFamilyIndex_); imageCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; imageCreateInfo.mipLevels = 1; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imageCreateInfo.arrayLayers = 1; imageCreateInfo.extent.depth = 1; imageCreateInfo.extent.height = height; imageCreateInfo.extent.width = width; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkCreateImage (device_, &imageCreateInfo, nullptr, &rubyImage_); VkMemoryRequirements requirements = {}; vkGetImageMemoryRequirements (device_, rubyImage_, &requirements); VkDeviceSize requiredSizeForImage = requirements.size; auto memoryHeaps = EnumerateHeaps (physicalDevice_); deviceImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForImage), requirements.memoryTypeBits, MT_DeviceLocal); vkBindImageMemory (device_, rubyImage_, deviceImageMemory_, 0); VkBufferCreateInfo bufferCreateInfo = {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.pNext = nullptr; bufferCreateInfo.queueFamilyIndexCount = 1; bufferCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; bufferCreateInfo.size = requiredSizeForImage; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; vkCreateBuffer (device_, &bufferCreateInfo, nullptr, &uploadImageBuffer_); vkGetBufferMemoryRequirements (device_, uploadImageBuffer_, &requirements); VkDeviceSize requiredSizeForBuffer = requirements.size; bool memoryIsHostCoherent = false; uploadImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForBuffer), requirements.memoryTypeBits, MT_HostVisible, &memoryIsHostCoherent); vkBindBufferMemory (device_, uploadImageBuffer_, uploadImageMemory_, 0); void* data = nullptr; vkMapMemory (device_, uploadImageMemory_, 0, VK_WHOLE_SIZE, 0, &data); ::memcpy (data, image.data (), image.size ()); if (!memoryIsHostCoherent) { VkMappedMemoryRange mappedMemoryRange = {}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = uploadImageMemory_; mappedMemoryRange.offset = 0; mappedMemoryRange.size = VK_WHOLE_SIZE; vkFlushMappedMemoryRanges (device_, 1, &mappedMemoryRange); } vkUnmapMemory (device_, uploadImageMemory_); VkBufferImageCopy bufferImageCopy = {}; bufferImageCopy.imageExtent.width = width; bufferImageCopy.imageExtent.height = height; bufferImageCopy.imageExtent.depth = 1; bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferImageCopy.imageSubresource.mipLevel = 0; bufferImageCopy.imageSubresource.layerCount = 1; VkImageMemoryBarrier imageBarrier = {}; imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageBarrier.pNext = nullptr; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.srcAccessMask = 0; imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.image = rubyImage_; imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBarrier.subresourceRange.layerCount = 1; imageBarrier.subresourceRange.levelCount = 1; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); vkCmdCopyBufferToImage (uploadCommandList, uploadImageBuffer_, rubyImage_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopy); imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); VkImageViewCreateInfo imageViewCreateInfo = {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.format = imageCreateInfo.format; imageViewCreateInfo.image = rubyImage_; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCreateInfo.subresourceRange.levelCount = 1; imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; vkCreateImageView (device_, &imageViewCreateInfo, nullptr, &rubyImageView_); }
bool Texture::Init(VulkanDevice * device, VulkanCommandBuffer * cmdBuffer, std::string filename) { struct MipMap { unsigned char * data; int width; int height; unsigned int size; }; VkResult result; void * pData; std::vector<MipMap> mipMaps; FILE * file = fopen(filename.c_str(), "rb"); if (file == NULL) { gLogManager->AddMessage("ERROR: Texture file not found! (" + filename + ")"); return false; } // Read original image (as mipmap level 0) MipMap originalImage; fread(&originalImage.width, sizeof(unsigned int), 1, file); fread(&originalImage.height, sizeof(unsigned int), 1, file); fread(&originalImage.size, sizeof(unsigned int), 1, file); originalImage.data = new unsigned char[originalImage.size]; fread(originalImage.data, sizeof(unsigned char), originalImage.size, file); mipMaps.push_back(originalImage); // Read mipmaps fread(&mipMapsCount, sizeof(int), 1, file); for (int i = 0; i < mipMapsCount; i++) { MipMap mipMap; fread(&mipMap.width, sizeof(int), 1, file); fread(&mipMap.height, sizeof(int), 1, file); fread(&mipMap.size, sizeof(unsigned int), 1, file); mipMap.data = new unsigned char[mipMap.size]; fread(mipMap.data, sizeof(unsigned char), mipMap.size, file); mipMaps.push_back(mipMap); } fclose(file); unsigned int totalTextureSize = 0; for (unsigned int i = 0; i < mipMaps.size(); i++) totalTextureSize += mipMaps[i].size; // Create an array of bits which stores all of the texture data std::vector<unsigned char> textureData; for (unsigned int i = 0; i < mipMaps.size(); i++) for (unsigned int j = 0; j < mipMaps[i].size; j++) textureData.push_back(mipMaps[i].data[j]); for (int i = 0; i < mipMaps.size(); i++) delete[] mipMaps[i].data; VkMemoryRequirements memReq{}; VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCI{}; bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCI.size = totalTextureSize; bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; result = vkCreateBuffer(device->GetDevice(), &bufferCI, VK_NULL_HANDLE, &stagingBuffer); if (result != VK_SUCCESS) return false; vkGetBufferMemoryRequirements(device->GetDevice(), stagingBuffer, &memReq); allocInfo.allocationSize = memReq.size; if (!device->MemoryTypeFromProperties(memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &allocInfo.memoryTypeIndex)) return false; result = vkAllocateMemory(device->GetDevice(), &allocInfo, VK_NULL_HANDLE, &stagingMemory); if (result != VK_SUCCESS) return false; result = vkBindBufferMemory(device->GetDevice(), stagingBuffer, stagingMemory, 0); if (result != VK_SUCCESS) return false; result = vkMapMemory(device->GetDevice(), stagingMemory, 0, memReq.size, 0, &pData); if (result != VK_SUCCESS) return false; memcpy(pData, textureData.data(), textureData.size()); vkUnmapMemory(device->GetDevice(), stagingMemory); std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; for (unsigned int level = 0; level < mipMaps.size(); level++) { VkBufferImageCopy bufferCopyRegion{}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegion.imageExtent.width = mipMaps[level].width; bufferCopyRegion.imageExtent.height = mipMaps[level].height; offset += (uint32_t)mipMaps[level].size; bufferCopyRegions.push_back(bufferCopyRegion); } VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = VK_FORMAT_R8G8B8A8_UNORM; imageCI.mipLevels = (uint32_t)mipMaps.size(); imageCI.arrayLayers = 1; imageCI.samples = VK_SAMPLE_COUNT_1_BIT; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCI.extent.width = mipMaps[0].width; imageCI.extent.height = mipMaps[0].height; imageCI.extent.depth = 1; result = vkCreateImage(device->GetDevice(), &imageCI, VK_NULL_HANDLE, &textureImage); if (result != VK_SUCCESS) return false; vkGetImageMemoryRequirements(device->GetDevice(), textureImage, &memReq); VkMemoryAllocateInfo memAlloc{}; memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAlloc.allocationSize = memReq.size; if (!device->MemoryTypeFromProperties(memReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAlloc.memoryTypeIndex)) return false; result = vkAllocateMemory(device->GetDevice(), &memAlloc, VK_NULL_HANDLE, &textureMemory); if (result != VK_SUCCESS) return false; result = vkBindImageMemory(device->GetDevice(), textureImage, textureMemory, 0); if (result != VK_SUCCESS) return false; VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; range.baseMipLevel = 0; range.levelCount = (uint32_t)mipMaps.size(); range.layerCount = 1; cmdBuffer->BeginRecording(); VulkanTools::SetImageLayout(textureImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &range, cmdBuffer, device, false); vkCmdCopyBufferToImage(cmdBuffer->GetCommandBuffer(), stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)bufferCopyRegions.size(), bufferCopyRegions.data()); VulkanTools::SetImageLayout(textureImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, &range, cmdBuffer, device, false); cmdBuffer->EndRecording(); cmdBuffer->Execute(device, NULL, NULL, NULL, true); vkFreeMemory(device->GetDevice(), stagingMemory, VK_NULL_HANDLE); vkDestroyBuffer(device->GetDevice(), stagingBuffer, VK_NULL_HANDLE); VkImageViewCreateInfo viewCI{}; viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewCI.image = textureImage; viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; viewCI.format = VK_FORMAT_R8G8B8A8_UNORM; viewCI.components.r = VK_COMPONENT_SWIZZLE_R; viewCI.components.g = VK_COMPONENT_SWIZZLE_G; viewCI.components.b = VK_COMPONENT_SWIZZLE_B; viewCI.components.a = VK_COMPONENT_SWIZZLE_A; viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewCI.subresourceRange.baseMipLevel = 0; viewCI.subresourceRange.baseArrayLayer = 0; viewCI.subresourceRange.layerCount = 1; viewCI.subresourceRange.levelCount = (uint32_t)mipMaps.size(); result = vkCreateImageView(device->GetDevice(), &viewCI, VK_NULL_HANDLE, &textureImageView); if (result != VK_SUCCESS) return false; return true; }
void loadTextureArray(std::string filename, VkFormat format) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2DArray tex2DArray(gli::load((const char*)textureData, size)); #else gli::texture2DArray tex2DArray(gli::load(filename)); #endif assert(!tex2DArray.empty()); textureArray.width = tex2DArray.dimensions().x; textureArray.height = tex2DArray.dimensions().y; layerCount = tex2DArray.layers(); VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(); bufferCreateInfo.size = tex2DArray.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkTools::checkResult(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); vkTools::checkResult(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); vkTools::checkResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; vkTools::checkResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, tex2DArray.data(), tex2DArray.size()); vkUnmapMemory(device, stagingMemory); // Setup buffer copy regions for array layers std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; // Check if all array layers have the same dimesions bool sameDims = true; for (uint32_t layer = 0; layer < layerCount; layer++) { if (tex2DArray[layer].dimensions().x != textureArray.width || tex2DArray[layer].dimensions().y != textureArray.height) { sameDims = false; break; } } // If all layers of the texture array have the same dimensions, we only need to do one copy if (sameDims) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = layerCount; bufferCopyRegion.imageExtent.width = tex2DArray[0].dimensions().x; bufferCopyRegion.imageExtent.height = tex2DArray[0].dimensions().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); } else { // If dimensions differ, copy layer by layer and pass offsets for (uint32_t layer = 0; layer < layerCount; layer++) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = layer; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2DArray[layer].dimensions().x; bufferCopyRegion.imageExtent.height = tex2DArray[layer].dimensions().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); offset += tex2DArray[layer].size(); } } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.extent = { textureArray.width, textureArray.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.arrayLayers = layerCount; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &textureArray.image)); vkGetImageMemoryRequirements(device, textureArray.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &textureArray.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, textureArray.image, textureArray.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = layerCount; vkTools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, textureArray.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, bufferCopyRegions.size(), bufferCopyRegions.data() ); // Change texture image layout to shader read after all faces have been copied textureArray.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, textureArray.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // 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; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &textureArray.sampler)); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); 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; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &textureArray.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); }
void loadCubemap(std::string filename, VkFormat format, bool forceLinearTiling) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture_cube texCube(gli::load((const char*)textureData, size)); #else gli::texture_cube texCube(gli::load(filename)); #endif assert(!texCube.empty()); cubeMap.width = texCube.extent().x; cubeMap.height = texCube.extent().y; cubeMap.mipLevels = texCube.levels(); VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); bufferCreateInfo.size = texCube.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, texCube.data(), texCube.size()); vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = cubeMap.mipLevels; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { cubeMap.width, cubeMap.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // Cube faces count as array layers in Vulkan imageCreateInfo.arrayLayers = 6; // This flag is required for cube map images imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &cubeMap.image)); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeMap.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Setup buffer copy regions for each face including all of it's miplevels std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; for (uint32_t face = 0; face < 6; face++) { for (uint32_t level = 0; level < cubeMap.mipLevels; level++) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texCube[face][level].extent().x; bufferCopyRegion.imageExtent.height = texCube[face][level].extent().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += texCube[face][level].size(); } } // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = cubeMap.mipLevels; subresourceRange.layerCount = 6; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data() ); // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cubeMap.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = cubeMap.mipLevels; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sampler.maxAnisotropy = 1.0f; if (vulkanDevice->features.samplerAnisotropy) { sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = VK_TRUE; } VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &cubeMap.sampler)); // Create image view VkImageViewCreateInfo view = vks::initializers::imageViewCreateInfo(); // Cube map view type view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; // 6 array layers (faces) view.subresourceRange.layerCount = 6; // Set number of mip levels view.subresourceRange.levelCount = cubeMap.mipLevels; view.image = cubeMap.image; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &cubeMap.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); }
static gboolean _build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx, GstBuffer * buffer, struct cmd_data *cmd_data, GError ** error) { GstVulkanBufferMemory *buf_mem; GstVulkanImageMemory *swap_mem; VkCommandBuffer cmd; VkResult err; g_return_val_if_fail (swap_idx < swapper->n_swap_chain_images, FALSE); swap_mem = swapper->swap_chain_images[swap_idx]; cmd_data->notify = NULL; if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error)) return FALSE; buf_mem = (GstVulkanBufferMemory *) gst_buffer_peek_memory (buffer, 0); { VkCommandBufferInheritanceInfo buf_inh = { 0, }; VkCommandBufferBeginInfo cmd_buf_info = { 0, }; buf_inh.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; buf_inh.pNext = NULL; buf_inh.renderPass = VK_NULL_HANDLE; buf_inh.subpass = 0; buf_inh.framebuffer = VK_NULL_HANDLE; buf_inh.occlusionQueryEnable = FALSE; buf_inh.queryFlags = 0; buf_inh.pipelineStatistics = 0; cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmd_buf_info.pNext = NULL; cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cmd_buf_info.pInheritanceInfo = &buf_inh; err = vkBeginCommandBuffer (cmd, &cmd_buf_info); if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0) return FALSE; } if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, error)) { return FALSE; } { VkBufferImageCopy region = { 0, }; guint32 dst_width = gst_vulkan_image_memory_get_width (swap_mem); guint32 dst_height = gst_vulkan_image_memory_get_height (swap_mem); guint src_width = GST_VIDEO_INFO_WIDTH (&swapper->v_info); guint src_height = GST_VIDEO_INFO_HEIGHT (&swapper->v_info); guint x, y; if (src_width != dst_width || src_height != dst_height) { /* FIXME: broken with LunarG's driver x = (src_width - dst_width) / 2; y = (src_height - dst_height) / 2;*/ x = y = 0; } else { x = y = 0; } /* FIXME: scale rect */ GST_VK_BUFFER_IMAGE_COPY (region, 0, src_width, src_height, GST_VK_IMAGE_SUBRESOURCE_LAYERS_INIT (VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1), GST_VK_OFFSET3D_INIT (x, y, 0), GST_VK_EXTENT3D_INIT (src_width, src_height, 1)); vkCmdCopyBufferToImage (cmd, buf_mem->buffer, swap_mem->image, swap_mem->image_layout, 1, ®ion); } if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, error)) { return FALSE; } err = vkEndCommandBuffer (cmd); if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0) return FALSE; cmd_data->cmd = cmd; cmd_data->notify = NULL; if (!_new_fence (swapper->device, &cmd_data->fence, error)) { return FALSE; } return TRUE; }