VkFormat VkTestFramework::GetFormat(VkInstance instance, vk_testing::Device *device) { VkFormatProperties format_props; vkGetPhysicalDeviceFormatProperties(device->phy().handle(), VK_FORMAT_B8G8R8A8_UNORM, &format_props); if (format_props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT || format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { return VK_FORMAT_B8G8R8A8_UNORM; } vkGetPhysicalDeviceFormatProperties(device->phy().handle(), VK_FORMAT_R8G8B8A8_UNORM, &format_props); if (format_props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT || format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { return VK_FORMAT_R8G8B8A8_UNORM; } printf("Error - device does not support VK_FORMAT_B8G8R8A8_UNORM nor VK_FORMAT_R8G8B8A8_UNORM - exiting\n"); exit(1); }
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat) { // Since all depth formats may be optional, we need to find a suitable depth format to use // Start with the highest precision packed format std::vector<VkFormat> depthFormats = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM }; for (auto& format : depthFormats) { VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps); // Format must support depth stencil attachment for optimal tiling if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { *depthFormat = format; return true; } } return false; }
VkBool32 Example::buildTexture() { VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice->getPhysicalDevice(), VK_FORMAT_R8G8B8A8_UNORM, &formatProperties); // Check, if storage image is possible. if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Format not supported."); return VK_FALSE; } if (!createTexture(image, deviceMemoryImage, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0)) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not create image."); return VK_FALSE; } // imageView = vkts::imageViewCreate(device->getDevice(), 0, image->getImage(), VK_IMAGE_VIEW_TYPE_2D, image->getFormat(), { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); if (!imageView.get()) { vkts::logPrint(VKTS_LOG_ERROR, __FILE__, __LINE__, "Could not create image view."); return VK_FALSE; } return VK_TRUE; }
gpu_formats_support get_optimal_tiling_supported_formats(VkPhysicalDevice physical_device) { gpu_formats_support result = {}; VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physical_device, VK_FORMAT_D24_UNORM_S8_UINT, &props); result.d24_unorm_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); vkGetPhysicalDeviceFormatProperties(physical_device, VK_FORMAT_D32_SFLOAT_S8_UINT, &props); result.d32_sfloat_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); return result; }
VkFormat VkApp::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features){ for( VkFormat format : candidates ){ VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); if( tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features ){ return format; } else if( tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features ){ return format; } } throw std::runtime_error("failed to find supported format!"); }
gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev) { gpu_formats_support result = {}; VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D24_UNORM_S8_UINT, &props); result.d24_unorm_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_D32_SFLOAT_S8_UINT, &props); result.d32_sfloat_s8 = !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) && !!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); //Hide d24_s8 if force high precision z buffer is enabled if (g_cfg.video.force_high_precision_z_buffer && result.d32_sfloat_s8) result.d24_unorm_s8 = false; return result; }
bool PhysicalDevice::getSupportBlit(VkFormat format) { // Get format properties for the swapchain color format VkFormatProperties formatProps; bool supportsBlit = true; // Check blit support for source and destination // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) vkGetPhysicalDeviceFormatProperties(handle, format, &formatProps); if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { // std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } // Check if the device supports blitting to linear images vkGetPhysicalDeviceFormatProperties(handle, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { // std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } return supportsBlit; }
VkResult VulkanTexture::Create(int w, int h, VkFormat format) { tex_width = w; tex_height = h; format_ = format; VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps); // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data. // Linear tiling is usually only supported for 2D non-array textures. // needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; // Always stage. needStaging = true; return VK_SUCCESS; }
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)); }
void loadTexture(const char* fileName, VkFormat format, bool forceLinearTiling) { VkFormatProperties formatProperties; VkResult err; AAsset* asset = AAssetManager_open(app->activity->assetManager, fileName, AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2D tex2D(gli::load((const char*)textureData, size)); assert(!tex2D.empty()); texture.width = tex2D[0].dimensions().x; texture.height = tex2D[0].dimensions().y; texture.mipLevels = tex2D.levels(); // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); // Only use linear tiling if requested (and supported by the device) // Support for linear tiling is mostly limited, so prefer to use // optimal tiling instead // On most implementations linear tiling will only support a very // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) VkBool32 useStaging = true; // Only use linear tiling if forced if (forceLinearTiling) { // Don't use linear if format is not supported for (linear) shader sampling useStaging = !(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); } VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imageCreateInfo.usage = (useStaging) ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.flags = 0; imageCreateInfo.extent = { texture.width, texture.height, 1 }; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; startSetupCommandBuffer(); if (useStaging) { // Load all available mip levels into linear textures // and copy to optimal tiling target struct MipLevel { VkImage image; VkDeviceMemory memory; }; std::vector<MipLevel> mipLevels; mipLevels.resize(texture.mipLevels); // Copy mip levels for (uint32_t level = 0; level < texture.mipLevels; ++level) { imageCreateInfo.extent.width = tex2D[level].dimensions().x; imageCreateInfo.extent.height = tex2D[level].dimensions().y; imageCreateInfo.extent.depth = 1; err = vkCreateImage(device, &imageCreateInfo, nullptr, &mipLevels[level].image); assert(!err); vkGetImageMemoryRequirements(device, mipLevels[level].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &mipLevels[level].memory); assert(!err); err = vkBindImageMemory(device, mipLevels[level].image, mipLevels[level].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, mipLevels[level].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, mipLevels[level].memory, 0, memReqs.size, 0, &data); assert(!err); size_t levelSize = tex2D[level].size(); memcpy(data, tex2D[level].data(), levelSize); vkUnmapMemory(device, mipLevels[level].memory); LOGW("setImageLayout %d", 1); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( setupCmdBuffer, mipLevels[level].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); } // Setup texture as blit target with optimal tiling imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.mipLevels = texture.mipLevels; imageCreateInfo.extent = { texture.width, texture.height, 1 }; err = vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image); assert(!err); vkGetImageMemoryRequirements(device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory); assert(!err); err = vkBindImageMemory(device, texture.image, texture.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Copy mip levels one by one for (uint32_t level = 0; level < texture.mipLevels; ++level) { // Copy region for image blit VkImageCopy copyRegion = {}; copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.srcSubresource.baseArrayLayer = 0; copyRegion.srcSubresource.mipLevel = 0; copyRegion.srcSubresource.layerCount = 1; copyRegion.srcOffset = { 0, 0, 0 }; copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.dstSubresource.baseArrayLayer = 0; // Set mip level to copy the linear image to copyRegion.dstSubresource.mipLevel = level; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = tex2D[level].dimensions().x; copyRegion.extent.height = tex2D[level].dimensions().y; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( setupCmdBuffer, mipLevels[level].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); // Change texture image layout to shader read after the copy texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.imageLayout); } // Clean up linear images // No longer required after mip levels // have been transformed over to optimal tiling for (auto& level : mipLevels) { vkDestroyImage(device, level.image, nullptr); vkFreeMemory(device, level.memory, nullptr); } } else { // Prefer using optimal tiling, as linear tiling // may support only a small set of features // depending on implementation (e.g. no mip maps, only one layer, etc.) VkImage mappableImage; VkDeviceMemory mappableMemory; // Load mip map level 0 to linear tiling image err = vkCreateImage(device, &imageCreateInfo, nullptr, &mappableImage); assert(!err); // Get memory requirements for this image // like size and alignment vkGetImageMemoryRequirements(device, mappableImage, &memReqs); // Set memory allocation size to required memory size memAllocInfo.allocationSize = memReqs.size; // Get memory type that can be mapped to host memory getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); // Allocate host memory err = vkAllocateMemory(device, &memAllocInfo, nullptr, &mappableMemory); assert(!err); // Bind allocated image for use err = vkBindImageMemory(device, mappableImage, mappableMemory, 0); assert(!err); // Get sub resource layout // Mip map count, array layer, etc. VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; // Get sub resources layout // Includes row pitch, size offsets, etc. vkGetImageSubresourceLayout(device, mappableImage, &subRes, &subResLayout); assert(!err); // Map image memory err = vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, &data); assert(!err); // Copy image data into memory memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); vkUnmapMemory(device, mappableMemory); // Linear tiled images don't need to be staged // and can be directly used as textures texture.image = mappableImage; texture.deviceMemory = mappableMemory; texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Setup image memory barrier vkTools::setImageLayout( setupCmdBuffer, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, texture.imageLayout); } flushSetupCommandBuffer(); // Create sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the // texture data // This means you could have multiple sampler objects // for the same texture with different settings // Similar to the samplers available with OpenGL 3.3 VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; // Max level-of-detail should match mip level count sampler.maxLod = (useStaging) ? (float)texture.mipLevels : 0.0f; // Enable anisotropic filtering sampler.maxAnisotropy = 8; sampler.anisotropyEnable = VK_TRUE; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &texture.sampler); assert(!err); // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = (useStaging) ? texture.mipLevels : 1; view.image = texture.image; err = vkCreateImageView(device, &view, nullptr, &texture.view); assert(!err); }
// Take a screenshot for the curretn swapchain image // This is done using a blit from the swapchain image to a linear image whose memory content is then saved as a ppm image // Getting the image date directly from a swapchain image wouldn't work as they're usually stored in an implementation dependant optimal tiling format // Note: This requires the swapchain images to be created with the VK_IMAGE_USAGE_TRANSFER_SRC_BIT flag (see VulkanSwapChain::create) void saveScreenshot(const char *filename) { screenshotSaved = false; // Get format properties for the swapchain color format VkFormatProperties formatProps; bool supportsBlit = true; // Check blit support for source and destination // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps); if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } // Check if the device supports blitting to linear images vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; } // Source for the copy is the last rendered swapchain image VkImage srcImage = swapChain.images[currentBuffer]; // Create the linear tiled destination image to copy to and to read the memory from VkImageCreateInfo imgCreateInfo(vks::initializers::imageCreateInfo()); imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imgCreateInfo.extent.width = width; imgCreateInfo.extent.height = height; imgCreateInfo.extent.depth = 1; imgCreateInfo.arrayLayers = 1; imgCreateInfo.mipLevels = 1; imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; // Create the image VkImage dstImage; VK_CHECK_RESULT(vkCreateImage(device, &imgCreateInfo, nullptr, &dstImage)); // Create memory to back up the image VkMemoryRequirements memRequirements; VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); VkDeviceMemory dstImageMemory; vkGetImageMemoryRequirements(device, dstImage, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; // Memory must be host visible to copy from memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); // Do the actual blit from the swapchain image to our host visible destination image VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier(); // Transition destination image to transfer destination layout vks::tools::insertImageMemoryBarrier( copyCmd, dstImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // Transition swapchain image from present to transfer source layout vks::tools::insertImageMemoryBarrier( copyCmd, srcImage, VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) if (supportsBlit) { // Define the region to blit (we will blit the whole swapchain image) VkOffset3D blitSize; blitSize.x = width; blitSize.y = height; blitSize.z = 1; VkImageBlit imageBlitRegion{}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.srcSubresource.layerCount = 1; imageBlitRegion.srcOffsets[1] = blitSize; imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.dstSubresource.layerCount = 1; imageBlitRegion.dstOffsets[1] = blitSize; // Issue the blit command vkCmdBlitImage( copyCmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); } else { // Otherwise use image copy (requires us to manually flip components) VkImageCopy imageCopyRegion{}; imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.srcSubresource.layerCount = 1; imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.dstSubresource.layerCount = 1; imageCopyRegion.extent.width = width; imageCopyRegion.extent.height = height; imageCopyRegion.extent.depth = 1; // Issue the copy command vkCmdCopyImage( copyCmd, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); } // Transition destination image to general layout, which is the required layout for mapping the image memory later on vks::tools::insertImageMemoryBarrier( copyCmd, dstImage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); // Transition back the swap chain image after the blit is done vks::tools::insertImageMemoryBarrier( copyCmd, srcImage, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); vulkanDevice->flushCommandBuffer(copyCmd, queue); // Get layout of the image (including row pitch) VkImageSubresource subResource{}; subResource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResourceLayout; vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); // Map image memory so we can start copying from it const char* data; vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data); data += subResourceLayout.offset; std::ofstream file(filename, std::ios::out | std::ios::binary); // ppm header file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components bool colorSwizzle = false; // Check if source is BGR // Note: Not complete, only contains most common and basic BGR surface formats for demonstation purposes if (!supportsBlit) { std::vector<VkFormat> formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end()); } // ppm binary pixel data for (uint32_t y = 0; y < height; y++) { unsigned int *row = (unsigned int*)data; for (uint32_t x = 0; x < width; x++) { if (colorSwizzle) { file.write((char*)row+2, 1); file.write((char*)row+1, 1); file.write((char*)row, 1); } else { file.write((char*)row, 3); } row++; } data += subResourceLayout.rowPitch; } file.close(); std::cout << "Screenshot saved to disk" << std::endl; // Clean up resources vkUnmapMemory(device, dstImageMemory); vkFreeMemory(device, dstImageMemory, nullptr); vkDestroyImage(device, dstImage, nullptr); screenshotSaved = true; }
VkFormatProperties Device::format_properties(VkFormat format) { VkFormatProperties data; vkGetPhysicalDeviceFormatProperties(phy().handle(), format, &data); return data; }
void createSwapChainAndImages(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { // Pick an image count and format. According to section 30.5 of VK 1.1, maxImageCount of zero // apparently means "that there is no limit on the number of images, though there may be limits // related to the total amount of memory used by presentable images." uint32_t desiredImageCount = 2; const uint32_t maxImageCount = surfaceContext.surfaceCapabilities.maxImageCount; if (desiredImageCount < surfaceContext.surfaceCapabilities.minImageCount || (maxImageCount != 0 && desiredImageCount > maxImageCount)) { utils::slog.e << "Swap chain does not support " << desiredImageCount << " images.\n"; desiredImageCount = surfaceContext.surfaceCapabilities.minImageCount; } surfaceContext.surfaceFormat = surfaceContext.surfaceFormats[0]; for (const VkSurfaceFormatKHR& format : surfaceContext.surfaceFormats) { if (format.format == VK_FORMAT_R8G8B8A8_UNORM) { surfaceContext.surfaceFormat = format; break; } } const auto compositionCaps = surfaceContext.surfaceCapabilities.supportedCompositeAlpha; const auto compositeAlpha = (compositionCaps & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // Create the low-level swap chain. const auto size = surfaceContext.surfaceCapabilities.currentExtent; VkSwapchainCreateInfoKHR createInfo { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surfaceContext.surface, .minImageCount = desiredImageCount, .imageFormat = surfaceContext.surfaceFormat.format, .imageColorSpace = surfaceContext.surfaceFormat.colorSpace, .imageExtent = size, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .compositeAlpha = compositeAlpha, .presentMode = VK_PRESENT_MODE_FIFO_KHR, .clipped = VK_TRUE }; VkSwapchainKHR swapchain; VkResult result = vkCreateSwapchainKHR(context.device, &createInfo, VKALLOC, &swapchain); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetPhysicalDeviceSurfaceFormatsKHR error."); surfaceContext.swapchain = swapchain; // Extract the VkImage handles from the swap chain. uint32_t imageCount; result = vkGetSwapchainImagesKHR(context.device, swapchain, &imageCount, nullptr); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetSwapchainImagesKHR count error."); surfaceContext.swapContexts.resize(imageCount); std::vector<VkImage> images(imageCount); result = vkGetSwapchainImagesKHR(context.device, swapchain, &imageCount, images.data()); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetSwapchainImagesKHR error."); for (size_t i = 0; i < images.size(); ++i) { surfaceContext.swapContexts[i].attachment = { .image = images[i], .format = surfaceContext.surfaceFormat.format }; } utils::slog.i << "vkCreateSwapchain" << ": " << size.width << "x" << size.height << ", " << surfaceContext.surfaceFormat.format << ", " << surfaceContext.surfaceFormat.colorSpace << ", " << imageCount << utils::io::endl; // Create image views. VkImageViewCreateInfo ivCreateInfo = {}; ivCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ivCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; ivCreateInfo.format = surfaceContext.surfaceFormat.format; ivCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivCreateInfo.subresourceRange.levelCount = 1; ivCreateInfo.subresourceRange.layerCount = 1; for (size_t i = 0; i < images.size(); ++i) { ivCreateInfo.image = images[i]; result = vkCreateImageView(context.device, &ivCreateInfo, VKALLOC, &surfaceContext.swapContexts[i].attachment.view); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateImageView error."); } createSemaphore(context.device, &surfaceContext.imageAvailable); createSemaphore(context.device, &surfaceContext.renderingFinished); surfaceContext.depth = {}; } void createDepthBuffer(VulkanContext& context, VulkanSurfaceContext& surfaceContext, VkFormat depthFormat) { assert(context.cmdbuffer); // Create an appropriately-sized device-only VkImage. const auto size = surfaceContext.surfaceCapabilities.currentExtent; VkImage depthImage; VkImageCreateInfo imageInfo { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .extent = { size.width, size.height, 1 }, .format = depthFormat, .mipLevels = 1, .arrayLayers = 1, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .samples = VK_SAMPLE_COUNT_1_BIT, }; VkResult error = vkCreateImage(context.device, &imageInfo, VKALLOC, &depthImage); ASSERT_POSTCONDITION(!error, "Unable to create depth image."); // Allocate memory for the VkImage and bind it. VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(context.device, depthImage, &memReqs); VkMemoryAllocateInfo allocInfo { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memReqs.size, .memoryTypeIndex = selectMemoryType(context, memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) }; error = vkAllocateMemory(context.device, &allocInfo, nullptr, &surfaceContext.depth.memory); ASSERT_POSTCONDITION(!error, "Unable to allocate depth memory."); error = vkBindImageMemory(context.device, depthImage, surfaceContext.depth.memory, 0); ASSERT_POSTCONDITION(!error, "Unable to bind depth memory."); // Create a VkImageView so that we can attach depth to the framebuffer. VkImageView depthView; VkImageViewCreateInfo viewInfo { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = depthImage, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = depthFormat, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .subresourceRange.levelCount = 1, .subresourceRange.layerCount = 1, }; error = vkCreateImageView(context.device, &viewInfo, VKALLOC, &depthView); ASSERT_POSTCONDITION(!error, "Unable to create depth view."); // Transition the depth image into an optimal layout. VkImageMemoryBarrier barrier { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = depthImage, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .subresourceRange.levelCount = 1, .subresourceRange.layerCount = 1, .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT }; vkCmdPipelineBarrier(context.cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); // Go ahead and set the depth attachment fields, which serves as a signal to VulkanDriver that // it is now ready. surfaceContext.depth.view = depthView; surfaceContext.depth.image = depthImage; surfaceContext.depth.format = depthFormat; } void transitionDepthBuffer(VulkanContext& context, VulkanSurfaceContext& sc, VkFormat depthFormat) { // Begin a new command buffer solely for the purpose of transitioning the image layout. SwapContext& swap = getSwapContext(context); VkResult result = vkWaitForFences(context.device, 1, &swap.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkWaitForFences error."); result = vkResetFences(context.device, 1, &swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetFences error."); VkCommandBuffer cmdbuffer = swap.cmdbuffer; result = vkResetCommandBuffer(cmdbuffer, 0); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; result = vkBeginCommandBuffer(cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkBeginCommandBuffer error."); context.cmdbuffer = cmdbuffer; // Create the depth buffer and issue a pipeline barrier command. createDepthBuffer(context, sc, depthFormat); // Flush the command buffer. result = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEndCommandBuffer error."); context.cmdbuffer = nullptr; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &swap.cmdbuffer, }; result = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkQueueSubmit error."); swap.submitted = false; } void createCommandBuffersAndFences(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { // Allocate command buffers. VkCommandBufferAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocateInfo.commandPool = context.commandPool; allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = (uint32_t) surfaceContext.swapContexts.size(); std::vector<VkCommandBuffer> cmdbufs(allocateInfo.commandBufferCount); VkResult result = vkAllocateCommandBuffers(context.device, &allocateInfo, cmdbufs.data()); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkAllocateCommandBuffers error."); for (uint32_t i = 0; i < allocateInfo.commandBufferCount; ++i) { surfaceContext.swapContexts[i].cmdbuffer = cmdbufs[i]; } // Create fences. VkFenceCreateInfo fenceCreateInfo = {}; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (uint32_t i = 0; i < allocateInfo.commandBufferCount; i++) { result = vkCreateFence(context.device, &fenceCreateInfo, VKALLOC, &surfaceContext.swapContexts[i].fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateFence error."); } } void destroySurfaceContext(VulkanContext& context, VulkanSurfaceContext& surfaceContext) { for (SwapContext& swapContext : surfaceContext.swapContexts) { vkFreeCommandBuffers(context.device, context.commandPool, 1, &swapContext.cmdbuffer); vkDestroyFence(context.device, swapContext.fence, VKALLOC); vkDestroyImageView(context.device, swapContext.attachment.view, VKALLOC); swapContext.fence = VK_NULL_HANDLE; swapContext.attachment.view = VK_NULL_HANDLE; } vkDestroySwapchainKHR(context.device, surfaceContext.swapchain, VKALLOC); vkDestroySemaphore(context.device, surfaceContext.imageAvailable, VKALLOC); vkDestroySemaphore(context.device, surfaceContext.renderingFinished, VKALLOC); vkDestroySurfaceKHR(context.instance, surfaceContext.surface, VKALLOC); vkDestroyImageView(context.device, surfaceContext.depth.view, VKALLOC); vkDestroyImage(context.device, surfaceContext.depth.image, VKALLOC); vkFreeMemory(context.device, surfaceContext.depth.memory, VKALLOC); if (context.currentSurface == &surfaceContext) { context.currentSurface = nullptr; } } uint32_t selectMemoryType(VulkanContext& context, uint32_t flags, VkFlags reqs) { for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if (flags & 1) { if ((context.memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { return i; } } flags >>= 1; } ASSERT_POSTCONDITION(false, "Unable to find a memory type that meets requirements."); return (uint32_t) ~0ul; } VkFormat getVkFormat(ElementType type, bool normalized) { using ElementType = ElementType; if (normalized) { switch (type) { // Single Component Types case ElementType::BYTE: return VK_FORMAT_R8_SNORM; case ElementType::UBYTE: return VK_FORMAT_R8_UNORM; case ElementType::SHORT: return VK_FORMAT_R16_SNORM; case ElementType::USHORT: return VK_FORMAT_R16_UNORM; // Two Component Types case ElementType::BYTE2: return VK_FORMAT_R8G8_SNORM; case ElementType::UBYTE2: return VK_FORMAT_R8G8_UNORM; case ElementType::SHORT2: return VK_FORMAT_R16G16_SNORM; case ElementType::USHORT2: return VK_FORMAT_R16G16_UNORM; // Three Component Types case ElementType::BYTE3: return VK_FORMAT_R8G8B8_SNORM; case ElementType::UBYTE3: return VK_FORMAT_R8G8B8_UNORM; case ElementType::SHORT3: return VK_FORMAT_R16G16B16_SNORM; case ElementType::USHORT3: return VK_FORMAT_R16G16B16_UNORM; // Four Component Types case ElementType::BYTE4: return VK_FORMAT_R8G8B8A8_SNORM; case ElementType::UBYTE4: return VK_FORMAT_R8G8B8A8_UNORM; case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SNORM; case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UNORM; default: ASSERT_POSTCONDITION(false, "Normalized format does not exist."); return VK_FORMAT_UNDEFINED; } } switch (type) { // Single Component Types case ElementType::BYTE: return VK_FORMAT_R8_SINT; case ElementType::UBYTE: return VK_FORMAT_R8_UINT; case ElementType::SHORT: return VK_FORMAT_R16_SINT; case ElementType::USHORT: return VK_FORMAT_R16_UINT; case ElementType::HALF: return VK_FORMAT_R16_SFLOAT; case ElementType::INT: return VK_FORMAT_R32_SINT; case ElementType::UINT: return VK_FORMAT_R32_UINT; case ElementType::FLOAT: return VK_FORMAT_R32_SFLOAT; // Two Component Types case ElementType::BYTE2: return VK_FORMAT_R8G8_SINT; case ElementType::UBYTE2: return VK_FORMAT_R8G8_UINT; case ElementType::SHORT2: return VK_FORMAT_R16G16_SINT; case ElementType::USHORT2: return VK_FORMAT_R16G16_UINT; case ElementType::HALF2: return VK_FORMAT_R16G16_SFLOAT; case ElementType::FLOAT2: return VK_FORMAT_R32G32_SFLOAT; // Three Component Types case ElementType::BYTE3: return VK_FORMAT_R8G8B8_SINT; case ElementType::UBYTE3: return VK_FORMAT_R8G8B8_UINT; case ElementType::SHORT3: return VK_FORMAT_R16G16B16_SINT; case ElementType::USHORT3: return VK_FORMAT_R16G16B16_UINT; case ElementType::HALF3: return VK_FORMAT_R16G16B16_SFLOAT; case ElementType::FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT; // Four Component Types case ElementType::BYTE4: return VK_FORMAT_R8G8B8A8_SINT; case ElementType::UBYTE4: return VK_FORMAT_R8G8B8A8_UINT; case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SINT; case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UINT; case ElementType::HALF4: return VK_FORMAT_R16G16B16A16_SFLOAT; case ElementType::FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT; } return VK_FORMAT_UNDEFINED; } VkFormat getVkFormat(TextureFormat format) { using TextureFormat = TextureFormat; switch (format) { // 8 bits per element. case TextureFormat::R8: return VK_FORMAT_R8_UNORM; case TextureFormat::R8_SNORM: return VK_FORMAT_R8_SNORM; case TextureFormat::R8UI: return VK_FORMAT_R8_UINT; case TextureFormat::R8I: return VK_FORMAT_R8_SINT; case TextureFormat::STENCIL8: return VK_FORMAT_S8_UINT; // 16 bits per element. case TextureFormat::R16F: return VK_FORMAT_R16_SFLOAT; case TextureFormat::R16UI: return VK_FORMAT_R16_UINT; case TextureFormat::R16I: return VK_FORMAT_R16_SINT; case TextureFormat::RG8: return VK_FORMAT_R8G8_UNORM; case TextureFormat::RG8_SNORM: return VK_FORMAT_R8G8_SNORM; case TextureFormat::RG8UI: return VK_FORMAT_R8G8_UINT; case TextureFormat::RG8I: return VK_FORMAT_R8G8_SINT; case TextureFormat::RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16; case TextureFormat::RGB5_A1: return VK_FORMAT_R5G5B5A1_UNORM_PACK16; case TextureFormat::RGBA4: return VK_FORMAT_R4G4B4A4_UNORM_PACK16; case TextureFormat::DEPTH16: return VK_FORMAT_D16_UNORM; // 24 bits per element. In practice, very few GPU vendors support these. For simplicity // we just assume they are not supported, not bothering to query the device capabilities. // Note that VK_FORMAT_ enums for 24-bit formats exist, but are meant for vertex attributes. case TextureFormat::RGB8: case TextureFormat::SRGB8: case TextureFormat::RGB8_SNORM: case TextureFormat::RGB8UI: case TextureFormat::RGB8I: case TextureFormat::DEPTH24: return VK_FORMAT_UNDEFINED; // 32 bits per element. case TextureFormat::R32F: return VK_FORMAT_R32_SFLOAT; case TextureFormat::R32UI: return VK_FORMAT_R32_UINT; case TextureFormat::R32I: return VK_FORMAT_R32_SINT; case TextureFormat::RG16F: return VK_FORMAT_R16G16_SFLOAT; case TextureFormat::RG16UI: return VK_FORMAT_R16G16_UINT; case TextureFormat::RG16I: return VK_FORMAT_R16G16_SINT; case TextureFormat::R11F_G11F_B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; case TextureFormat::RGB9_E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; case TextureFormat::RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; case TextureFormat::SRGB8_A8: return VK_FORMAT_R8G8B8A8_SRGB; case TextureFormat::RGBA8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; case TextureFormat::RGBM: return VK_FORMAT_R8G8B8A8_UNORM; case TextureFormat::RGB10_A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; case TextureFormat::RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; case TextureFormat::RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; case TextureFormat::DEPTH32F: return VK_FORMAT_D32_SFLOAT; case TextureFormat::DEPTH24_STENCIL8: return VK_FORMAT_D24_UNORM_S8_UINT; case TextureFormat::DEPTH32F_STENCIL8: return VK_FORMAT_D32_SFLOAT_S8_UINT; // 48 bits per element. Note that many GPU vendors do not support these. case TextureFormat::RGB16F: return VK_FORMAT_R16G16B16_SFLOAT; case TextureFormat::RGB16UI: return VK_FORMAT_R16G16B16_UINT; case TextureFormat::RGB16I: return VK_FORMAT_R16G16B16_SINT; // 64 bits per element. case TextureFormat::RG32F: return VK_FORMAT_R32G32_SFLOAT; case TextureFormat::RG32UI: return VK_FORMAT_R32G32_UINT; case TextureFormat::RG32I: return VK_FORMAT_R32G32_SINT; case TextureFormat::RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT; case TextureFormat::RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT; case TextureFormat::RGBA16I: return VK_FORMAT_R16G16B16A16_SINT; // 96-bits per element. case TextureFormat::RGB32F: return VK_FORMAT_R32G32B32_SFLOAT; case TextureFormat::RGB32UI: return VK_FORMAT_R32G32B32_UINT; case TextureFormat::RGB32I: return VK_FORMAT_R32G32B32_SINT; // 128-bits per element case TextureFormat::RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT; case TextureFormat::RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT; case TextureFormat::RGBA32I: return VK_FORMAT_R32G32B32A32_SINT; default: return VK_FORMAT_UNDEFINED; } } uint32_t getBytesPerPixel(TextureFormat format) { return details::FTexture::getFormatSize(format); } // See also FTexture::computeTextureDataSize, which takes a public-facing Texture format rather // than a driver-level Texture format, and can account for a specified byte alignment. uint32_t computeSize(TextureFormat format, uint32_t w, uint32_t h, uint32_t d) { const size_t bytesPerTexel = details::FTexture::getFormatSize(format); return bytesPerTexel * w * h * d; } SwapContext& getSwapContext(VulkanContext& context) { VulkanSurfaceContext& surface = *context.currentSurface; return surface.swapContexts[surface.currentSwapIndex]; } bool hasPendingWork(VulkanContext& context) { if (context.pendingWork.size() > 0) { return true; } if (context.currentSurface) { for (auto& swapContext : context.currentSurface->swapContexts) { if (swapContext.pendingWork.size() > 0) { return true; } } } return false; } VkCompareOp getCompareOp(SamplerCompareFunc func) { using Compare = driver::SamplerCompareFunc; switch (func) { case Compare::LE: return VK_COMPARE_OP_LESS_OR_EQUAL; case Compare::GE: return VK_COMPARE_OP_GREATER_OR_EQUAL; case Compare::L: return VK_COMPARE_OP_LESS; case Compare::G: return VK_COMPARE_OP_GREATER; case Compare::E: return VK_COMPARE_OP_EQUAL; case Compare::NE: return VK_COMPARE_OP_NOT_EQUAL; case Compare::A: return VK_COMPARE_OP_ALWAYS; case Compare::N: return VK_COMPARE_OP_NEVER; } } VkBlendFactor getBlendFactor(BlendFunction mode) { using BlendFunction = filament::driver::BlendFunction; switch (mode) { case BlendFunction::ZERO: return VK_BLEND_FACTOR_ZERO; case BlendFunction::ONE: return VK_BLEND_FACTOR_ONE; case BlendFunction::SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR; case BlendFunction::ONE_MINUS_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; case BlendFunction::DST_COLOR: return VK_BLEND_FACTOR_DST_COLOR; case BlendFunction::ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; case BlendFunction::SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA; case BlendFunction::ONE_MINUS_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; case BlendFunction::DST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA; case BlendFunction::ONE_MINUS_DST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; case BlendFunction::SRC_ALPHA_SATURATE: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; } } void waitForIdle(VulkanContext& context) { // If there's no valid GPU then we have nothing to do. if (!context.device) { return; } // If there's no surface, then there's no command buffer. if (!context.currentSurface) { return; } // First, wait for submitted command buffer(s) to finish. VkFence fences[2]; uint32_t nfences = 0; auto& surfaceContext = *context.currentSurface; for (auto& swapContext : surfaceContext.swapContexts) { assert(nfences < 2); if (swapContext.submitted && swapContext.fence) { fences[nfences++] = swapContext.fence; swapContext.submitted = false; } } if (nfences > 0) { vkWaitForFences(context.device, nfences, fences, VK_FALSE, ~0ull); } // If we don't have any pending work, we're done. if (!hasPendingWork(context)) { return; } // We cannot invoke arbitrary commands inside a render pass. assert(context.currentRenderPass.renderPass == VK_NULL_HANDLE); // Create a one-off command buffer to avoid the cost of swap chain acquisition and to avoid // the possibility of SURFACE_LOST. Note that Vulkan command buffers use the Allocate/Free // model instead of Create/Destroy and are therefore okay to create at a high frequency. VkCommandBuffer cmdbuffer; VkFence fence; VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; VkCommandBufferAllocateInfo allocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = context.commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1 }; VkFenceCreateInfo fenceCreateInfo { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, }; vkAllocateCommandBuffers(context.device, &allocateInfo, &cmdbuffer); vkCreateFence(context.device, &fenceCreateInfo, VKALLOC, &fence); // Keep performing work until there's nothing queued up. This should never iterate more than // a couple times because the only work we queue up is for resource transition / reclamation. VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &cmdbuffer, }; int cycles = 0; while (hasPendingWork(context)) { if (cycles++ > 2) { utils::slog.e << "Unexpected daisychaining of pending work." << utils::io::endl; break; } for (auto& swapContext : context.currentSurface->swapContexts) { vkBeginCommandBuffer(cmdbuffer, &beginInfo); performPendingWork(context, swapContext, cmdbuffer); vkEndCommandBuffer(cmdbuffer); vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, fence); vkWaitForFences(context.device, 1, &fence, VK_FALSE, UINT64_MAX); vkResetFences(context.device, 1, &fence); vkResetCommandBuffer(cmdbuffer, 0); } } vkFreeCommandBuffers(context.device, context.commandPool, 1, &cmdbuffer); vkDestroyFence(context.device, fence, VKALLOC); } void acquireCommandBuffer(VulkanContext& context) { // Ask Vulkan for the next image in the swap chain and update the currentSwapIndex. VulkanSurfaceContext& surface = *context.currentSurface; VkResult result = vkAcquireNextImageKHR(context.device, surface.swapchain, UINT64_MAX, surface.imageAvailable, VK_NULL_HANDLE, &surface.currentSwapIndex); ASSERT_POSTCONDITION(result != VK_ERROR_OUT_OF_DATE_KHR, "Stale / resized swap chain not yet supported."); ASSERT_POSTCONDITION(result == VK_SUBOPTIMAL_KHR || result == VK_SUCCESS, "vkAcquireNextImageKHR error."); SwapContext& swap = getSwapContext(context); // Ensure that the previous submission of this command buffer has finished. result = vkWaitForFences(context.device, 1, &swap.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkWaitForFences error."); // Restart the command buffer. result = vkResetFences(context.device, 1, &swap.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkResetFences error."); VkCommandBuffer cmdbuffer = swap.cmdbuffer; VkResult error = vkResetCommandBuffer(cmdbuffer, 0); ASSERT_POSTCONDITION(not error, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; error = vkBeginCommandBuffer(cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(not error, "vkBeginCommandBuffer error."); context.cmdbuffer = cmdbuffer; swap.submitted = false; } void releaseCommandBuffer(VulkanContext& context) { // Finalize the command buffer and set the cmdbuffer pointer to null. VkResult result = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEndCommandBuffer error."); context.cmdbuffer = nullptr; // Submit the command buffer. VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VulkanSurfaceContext& surfaceContext = *context.currentSurface; SwapContext& swapContext = getSwapContext(context); VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1u, .pWaitSemaphores = &surfaceContext.imageAvailable, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &swapContext.cmdbuffer, .signalSemaphoreCount = 1u, .pSignalSemaphores = &surfaceContext.renderingFinished, }; result = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, swapContext.fence); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkQueueSubmit error."); swapContext.submitted = true; } void performPendingWork(VulkanContext& context, SwapContext& swapContext, VkCommandBuffer cmdbuf) { // First, execute pending tasks that are specific to this swap context. Copy the tasks into a // local queue first, which allows newly added tasks to be deferred until the next frame. decltype(swapContext.pendingWork) tasks; tasks.swap(swapContext.pendingWork); for (auto& callback : tasks) { callback(cmdbuf); } // Next, execute the global pending work. Again, we copy the work queue into a local queue // to allow tasks to re-add themselves. tasks.clear(); tasks.swap(context.pendingWork); for (auto& callback : tasks) { callback(cmdbuf); } } // Flushes the command buffer and waits for it to finish executing. Useful for diagnosing // sychronization issues. void flushCommandBuffer(VulkanContext& context) { VulkanSurfaceContext& surface = *context.currentSurface; const SwapContext& sc = surface.swapContexts[surface.currentSwapIndex]; // Submit the command buffer. VkResult error = vkEndCommandBuffer(context.cmdbuffer); ASSERT_POSTCONDITION(!error, "vkEndCommandBuffer error."); VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pWaitDstStageMask = &waitDestStageMask, .commandBufferCount = 1, .pCommandBuffers = &context.cmdbuffer, }; error = vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, sc.fence); ASSERT_POSTCONDITION(!error, "vkQueueSubmit error."); // Restart the command buffer. error = vkWaitForFences(context.device, 1, &sc.fence, VK_FALSE, UINT64_MAX); ASSERT_POSTCONDITION(!error, "vkWaitForFences error."); error = vkResetFences(context.device, 1, &sc.fence); ASSERT_POSTCONDITION(!error, "vkResetFences error."); error = vkResetCommandBuffer(context.cmdbuffer, 0); ASSERT_POSTCONDITION(!error, "vkResetCommandBuffer error."); VkCommandBufferBeginInfo beginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, }; error = vkBeginCommandBuffer(context.cmdbuffer, &beginInfo); ASSERT_POSTCONDITION(!error, "vkBeginCommandBuffer error."); } VkFormat findSupportedFormat(VulkanContext& context, const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { for (VkFormat format : candidates) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(context.physicalDevice, format, &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { return format; } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { return format; } } return VK_FORMAT_UNDEFINED; } } // namespace filament } // namespace driver
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Texel Buffer Sample"; float texels[] = {1.0, 0.0, 1.0}; const bool depthPresent = false; const bool vertexPresent = false; process_command_line_args(info, argc, argv); init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); if (info.gpu_props.limits.maxTexelBufferElements < 4) { std::cout << "maxTexelBufferElements too small\n"; exit(-1); } VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R32_SFLOAT, &props); if (!(props.bufferFeatures & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)) { std::cout << "R32_SFLOAT format unsupported for texel buffer\n"; exit(-1); } init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info); VkBufferCreateInfo buf_info = {}; buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buf_info.pNext = NULL; buf_info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; buf_info.size = sizeof(texels); buf_info.queueFamilyIndexCount = 0; buf_info.pQueueFamilyIndices = NULL; buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; buf_info.flags = 0; VkBuffer texelBuf; res = vkCreateBuffer(info.device, &buf_info, NULL, &texelBuf); assert(res == VK_SUCCESS); VkMemoryRequirements mem_reqs; vkGetBufferMemoryRequirements(info.device, texelBuf, &mem_reqs); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.pNext = NULL; alloc_info.memoryTypeIndex = 0; alloc_info.allocationSize = mem_reqs.size; pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &alloc_info.memoryTypeIndex); assert(pass && "No mappable, coherent memory"); VkDeviceMemory texelMem; res = vkAllocateMemory(info.device, &alloc_info, NULL, &texelMem); assert(res == VK_SUCCESS); uint8_t *pData; res = vkMapMemory(info.device, texelMem, 0, mem_reqs.size, 0, (void **)&pData); assert(res == VK_SUCCESS); memcpy(pData, &texels, sizeof(texels)); vkUnmapMemory(info.device, texelMem); res = vkBindBufferMemory(info.device, texelBuf, texelMem, 0); assert(res == VK_SUCCESS); VkBufferView texel_view; VkBufferViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.buffer = texelBuf; view_info.format = VK_FORMAT_R32_SFLOAT; view_info.offset = 0; view_info.range = sizeof(texels); vkCreateBufferView(info.device, &view_info, NULL, &texel_view); VkDescriptorBufferInfo texel_buffer_info = {}; texel_buffer_info.buffer = texelBuf; texel_buffer_info.offset = 0; texel_buffer_info.range = sizeof(texels); // init_descriptor_and_pipeline_layouts(info, false); VkDescriptorSetLayoutBinding layout_bindings[1]; layout_bindings[0].binding = 0; layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; layout_bindings[0].descriptorCount = 1; layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; layout_bindings[0].pImmutableSamplers = NULL; /* Next take layout bindings and use them to create a descriptor set layout */ VkDescriptorSetLayoutCreateInfo descriptor_layout = {}; descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptor_layout.pNext = NULL; descriptor_layout.bindingCount = 1; descriptor_layout.pBindings = layout_bindings; info.desc_layout.resize(NUM_DESCRIPTOR_SETS); res = vkCreateDescriptorSetLayout(info.device, &descriptor_layout, NULL, info.desc_layout.data()); assert(res == VK_SUCCESS); /* Now use the descriptor layout to create a pipeline layout */ VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = 0; pPipelineLayoutCreateInfo.pPushConstantRanges = NULL; pPipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS; pPipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data(); res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout); assert(res == VK_SUCCESS); init_renderpass(info, depthPresent); init_shaders(info, vertShaderText, fragShaderText); init_framebuffers(info, depthPresent); VkDescriptorPoolSize type_count[1]; type_count[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; type_count[0].descriptorCount = 1; VkDescriptorPoolCreateInfo descriptor_pool = {}; descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool.pNext = NULL; descriptor_pool.maxSets = 1; descriptor_pool.poolSizeCount = 1; descriptor_pool.pPoolSizes = type_count; res = vkCreateDescriptorPool(info.device, &descriptor_pool, NULL, &info.desc_pool); assert(res == VK_SUCCESS); VkDescriptorSetAllocateInfo desc_alloc_info[1]; desc_alloc_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; desc_alloc_info[0].pNext = NULL; desc_alloc_info[0].descriptorPool = info.desc_pool; desc_alloc_info[0].descriptorSetCount = NUM_DESCRIPTOR_SETS; desc_alloc_info[0].pSetLayouts = info.desc_layout.data(); /* Allocate descriptor set with UNIFORM_BUFFER_DYNAMIC */ info.desc_set.resize(NUM_DESCRIPTOR_SETS); res = vkAllocateDescriptorSets(info.device, desc_alloc_info, info.desc_set.data()); assert(res == VK_SUCCESS); VkWriteDescriptorSet writes[1]; writes[0] = {}; writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = info.desc_set[0]; writes[0].dstBinding = 0; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; writes[0].pBufferInfo = &texel_buffer_info; writes[0].pTexelBufferView = &texel_view; writes[0].dstArrayElement = 0; vkUpdateDescriptorSets(info.device, 1, writes, 0, NULL); init_pipeline_cache(info); init_pipeline(info, depthPresent, vertexPresent); /* VULKAN_KEY_START */ VkClearValue clear_values[1]; clear_values[0].color.float32[0] = 0.2f; clear_values[0].color.float32[1] = 0.2f; clear_values[0].color.float32[2] = 0.2f; clear_values[0].color.float32[3] = 0.2f; VkSemaphoreCreateInfo imageAcquiredSemaphoreCreateInfo; imageAcquiredSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; imageAcquiredSemaphoreCreateInfo.pNext = NULL; imageAcquiredSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &imageAcquiredSemaphoreCreateInfo, NULL, &info.imageAcquiredSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.imageAcquiredSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); VkRenderPassBeginInfo rp_begin; rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_begin.pNext = NULL; rp_begin.renderPass = info.render_pass; rp_begin.framebuffer = info.framebuffers[info.current_buffer]; rp_begin.renderArea.offset.x = 0; rp_begin.renderArea.offset.y = 0; rp_begin.renderArea.extent.width = info.width; rp_begin.renderArea.extent.height = info.height; rp_begin.clearValueCount = 1; rp_begin.pClearValues = clear_values; vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline); vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL); init_viewports(info); init_scissors(info); vkCmdDraw(info.cmd, 3, 1, 0, 0); vkCmdEndRenderPass(info.cmd); res = vkEndCommandBuffer(info.cmd); const VkCommandBuffer cmd_bufs[] = {info.cmd}; VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); execute_queue_cmdbuf(info, cmd_bufs, drawFence); do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, drawFence, NULL); execute_present_image(info); wait_seconds(1); /* VULKAN_KEY_END */ if (info.save_images) write_ppm(info, "texel_buffer"); vkDestroySemaphore(info.device, info.imageAcquiredSemaphore, NULL); vkDestroyBufferView(info.device, texel_view, NULL); vkDestroyBuffer(info.device, texelBuf, NULL); vkFreeMemory(info.device, texelMem, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_descriptor_pool(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); destroy_descriptor_and_pipeline_layouts(info); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
//============================================================================== // Vulkan初期化 //============================================================================== bool initVulkan(HINSTANCE hinst, HWND wnd) { VkResult result; //================================================== // Vulkanのインスタンス作成 //================================================== VkApplicationInfo applicationInfo = {}; applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; applicationInfo.pApplicationName = APPLICATION_NAME; applicationInfo.pEngineName = APPLICATION_NAME; applicationInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); std::vector<LPCSTR> enabledExtensionsByInstance = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME }; VkInstanceCreateInfo instanceCreateInfo = {}; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pNext = nullptr; instanceCreateInfo.pApplicationInfo = &applicationInfo; if(enabledExtensionsByInstance.empty() == false) { instanceCreateInfo.enabledExtensionCount = enabledExtensionsByInstance.size(); instanceCreateInfo.ppEnabledExtensionNames = enabledExtensionsByInstance.data(); } result = vkCreateInstance(&instanceCreateInfo, nullptr, &g_VulkanInstance); checkVulkanError(result, TEXT("Vulkanインスタンス作成失敗")); //================================================== // 物理デバイス(GPUデバイス) //================================================== // 物理デバイス数を獲得 uint32_t gpuCount = 0; vkEnumeratePhysicalDevices(g_VulkanInstance, &gpuCount, nullptr); assert(gpuCount > 0 && TEXT("物理デバイス数の獲得失敗")); // 物理デバイス数を列挙 std::vector<VkPhysicalDevice> physicalDevices(gpuCount); result = vkEnumeratePhysicalDevices(g_VulkanInstance, &gpuCount, physicalDevices.data()); checkVulkanError(result, TEXT("物理デバイスの列挙に失敗しました")); // すべてのGPU情報を格納 g_GPUs.resize(gpuCount); for(uint32_t i = 0; i < gpuCount; ++i) { g_GPUs[i].device = physicalDevices[i]; // 物理デバイスのプロパティ獲得 vkGetPhysicalDeviceProperties(g_GPUs[i].device, &g_GPUs[i].deviceProperties); // 物理デバイスのメモリプロパティ獲得 vkGetPhysicalDeviceMemoryProperties(g_GPUs[i].device, &g_GPUs[i].deviceMemoryProperties); } // ※このサンプルでは最初に列挙されたGPUデバイスを使用する g_currentGPU = g_GPUs[0]; // グラフィックス操作をサポートするキューを検索 uint32_t queueCount; vkGetPhysicalDeviceQueueFamilyProperties(g_currentGPU.device, &queueCount, nullptr); assert(queueCount >= 1 && TEXT("物理デバイスキューの検索失敗")); std::vector<VkQueueFamilyProperties> queueProps; queueProps.resize(queueCount); vkGetPhysicalDeviceQueueFamilyProperties(g_currentGPU.device, &queueCount, queueProps.data()); uint32_t graphicsQueueIndex = 0; for(graphicsQueueIndex = 0; graphicsQueueIndex < queueCount; ++graphicsQueueIndex) { if(queueProps[graphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { break; } } assert(graphicsQueueIndex < queueCount && TEXT("グラフィックスをサポートするキューを見つけられませんでした")); //================================================== // Vulkanデバイス作成 //================================================== float queuePrioritie = 0.0f; VkDeviceQueueCreateInfo queueCreateInfo = {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = graphicsQueueIndex; queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &queuePrioritie; std::vector<LPCSTR> enabledExtensionsByDevice = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.pNext = nullptr; deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; deviceCreateInfo.pEnabledFeatures = nullptr; if(enabledExtensionsByDevice.empty() == false) { deviceCreateInfo.enabledExtensionCount = enabledExtensionsByDevice.size(); deviceCreateInfo.ppEnabledExtensionNames = enabledExtensionsByDevice.data(); } result = vkCreateDevice(g_currentGPU.device, &deviceCreateInfo, nullptr, &g_VulkanDevice); checkVulkanError(result, TEXT("Vulkanデバイス作成失敗")); // グラフィックスキュー獲得 vkGetDeviceQueue(g_VulkanDevice, graphicsQueueIndex, 0, &g_VulkanQueue); //================================================== // フェンスオブジェクト作成 //================================================== VkFenceCreateInfo fenceCreateInfo = {}; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceCreateInfo.pNext = nullptr; fenceCreateInfo.flags = 0; result = vkCreateFence(g_VulkanDevice, &fenceCreateInfo, nullptr, &g_VulkanFence); checkVulkanError(result, TEXT("フェンスオブジェクト作成失敗")); //================================================== // 同期(セマフォ)オブジェクト作成 //================================================== VkSemaphoreCreateInfo semaphoreCreateInfo = {}; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreCreateInfo.pNext = nullptr; semaphoreCreateInfo.flags = 0; // コマンドバッファ実行用セマフォ作成 result = vkCreateSemaphore(g_VulkanDevice, &semaphoreCreateInfo, nullptr, &g_VulkanSemahoreRenderComplete); checkVulkanError(result, TEXT("コマンドバッファ実行用セマフォ作成失敗")); //================================================== // コマンドプール作製 //================================================== VkCommandPoolCreateInfo cmdPoolInfo = {}; cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmdPoolInfo.queueFamilyIndex = 0; cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; result = vkCreateCommandPool(g_VulkanDevice, &cmdPoolInfo, nullptr, &g_VulkanCommandPool); checkVulkanError(result, TEXT("コマンドプール作成失敗")); //================================================== // コマンドバッファ作成 //================================================== // メモリを確保. g_commandBuffers.resize(SWAP_CHAIN_COUNT); VkCommandBufferAllocateInfo commandBufferAllocateInfo = {}; commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBufferAllocateInfo.pNext = nullptr; commandBufferAllocateInfo.commandPool = g_VulkanCommandPool; commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBufferAllocateInfo.commandBufferCount = SWAP_CHAIN_COUNT; result = vkAllocateCommandBuffers(g_VulkanDevice, &commandBufferAllocateInfo, g_commandBuffers.data()); checkVulkanError(result, TEXT("コマンドバッファ作成失敗")); //================================================== // OS(今回はWin32)用のサーフェスを作成する //================================================== VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; surfaceCreateInfo.hinstance = hinst; surfaceCreateInfo.hwnd = wnd; result = vkCreateWin32SurfaceKHR(g_VulkanInstance, &surfaceCreateInfo, nullptr, &g_VulkanSurface); checkVulkanError(result, TEXT("サーフェス作成失敗")); //================================================== // スワップチェーンを作成する //================================================== VkFormat imageFormat = VK_FORMAT_R8G8B8A8_UNORM; VkColorSpaceKHR imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; uint32_t surfaceFormatCount = 0; result = vkGetPhysicalDeviceSurfaceFormatsKHR(g_currentGPU.device, g_VulkanSurface, &surfaceFormatCount, nullptr); checkVulkanError(result, TEXT("サポートしているカラーフォーマット数の獲得失敗")); std::vector<VkSurfaceFormatKHR> surfaceFormats; surfaceFormats.resize(surfaceFormatCount); result = vkGetPhysicalDeviceSurfaceFormatsKHR(g_currentGPU.device, g_VulkanSurface, &surfaceFormatCount, surfaceFormats.data()); checkVulkanError(result, TEXT("サポートしているカラーフォーマットの獲得失敗")); // 一致するカラーフォーマットを検索する bool isFind = false; for(const auto& surfaceFormat : surfaceFormats) { if(imageFormat == surfaceFormat.format && imageColorSpace == surfaceFormat.colorSpace) { isFind = true; break; } } if(isFind == false) { imageFormat = surfaceFormats[0].format; imageColorSpace = surfaceFormats[0].colorSpace; } // サーフェスの機能を獲得する VkSurfaceCapabilitiesKHR surfaceCapabilities; VkSurfaceTransformFlagBitsKHR surfaceTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( g_currentGPU.device, g_VulkanSurface, &surfaceCapabilities); checkVulkanError(result, TEXT("サーフェスの機能の獲得失敗")); if((surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) == 0) { surfaceTransform = surfaceCapabilities.currentTransform; } // プレゼント機能を獲得する VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; uint32_t presentModeCount; result = vkGetPhysicalDeviceSurfacePresentModesKHR( g_currentGPU.device, g_VulkanSurface, &presentModeCount, nullptr); checkVulkanError(result, TEXT("プレゼント機能数の獲得失敗")); std::vector<VkPresentModeKHR> presentModes; presentModes.resize(presentModeCount); result = vkGetPhysicalDeviceSurfacePresentModesKHR( g_currentGPU.device, g_VulkanSurface, &presentModeCount, presentModes.data()); checkVulkanError(result, TEXT("プレゼント機能の獲得失敗")); for(const auto& presentModeInfo : presentModes) { if(presentModeInfo == VK_PRESENT_MODE_MAILBOX_KHR) { presentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if(presentModeInfo == VK_PRESENT_MODE_IMMEDIATE_KHR) { presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } presentModes.clear(); uint32_t desiredSwapChainImageCount = surfaceCapabilities.minImageCount + 1; if(surfaceCapabilities.maxImageCount > 0 && desiredSwapChainImageCount > surfaceCapabilities.maxImageCount) { desiredSwapChainImageCount = surfaceCapabilities.maxImageCount; } // スワップチェーン作成 VkSwapchainCreateInfoKHR swapchainCreateInfo = {}; swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.pNext = nullptr; swapchainCreateInfo.flags = 0; swapchainCreateInfo.surface = g_VulkanSurface; swapchainCreateInfo.minImageCount = desiredSwapChainImageCount; swapchainCreateInfo.imageFormat = imageFormat; swapchainCreateInfo.imageColorSpace = imageColorSpace; swapchainCreateInfo.imageExtent = { SCREEN_WIDTH, SCREEN_HEIGHT }; swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCreateInfo.queueFamilyIndexCount = 0; swapchainCreateInfo.pQueueFamilyIndices = nullptr; swapchainCreateInfo.preTransform = surfaceTransform; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchainCreateInfo.presentMode = presentMode; swapchainCreateInfo.clipped = VK_TRUE; swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; result = vkCreateSwapchainKHR(g_VulkanDevice, &swapchainCreateInfo, nullptr, &g_VulkanSwapChain); checkVulkanError(result, TEXT("サーフェス作成失敗")); //================================================== // イメージの作成 //================================================== uint32_t swapChainCount = 0; result = vkGetSwapchainImagesKHR(g_VulkanDevice, g_VulkanSwapChain, &swapChainCount, nullptr); checkVulkanError(result, TEXT("スワップチェーンイメージ数の獲得失敗")); g_backBuffersTextures.resize(swapChainCount); std::vector<VkImage> images; images.resize(swapChainCount); result = vkGetSwapchainImagesKHR(g_VulkanDevice, g_VulkanSwapChain, &swapChainCount, images.data()); checkVulkanError(result, TEXT("スワップチェーンイメージの獲得失敗")); for(uint32_t i = 0; i < swapChainCount; ++i) { g_backBuffersTextures[i].image = images[i]; } images.clear(); //================================================== // イメージビューの生成 //================================================== for(auto& backBuffer : g_backBuffersTextures) { VkImageViewCreateInfo imageViewCreateInfo = {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.pNext = nullptr; imageViewCreateInfo.flags = 0; imageViewCreateInfo.image = backBuffer.image; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCreateInfo.format = imageFormat; imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_R; imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_G; imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_B; imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_A; imageViewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; result = vkCreateImageView(g_VulkanDevice, &imageViewCreateInfo, nullptr, &backBuffer.view); checkVulkanError(result, TEXT("イメージビューの作成失敗")); setImageLayout( g_VulkanDevice, g_commandBuffers[g_currentBufferIndex], backBuffer.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } //================================================== // 深度ステンシルバッファの生成 //================================================== VkFormat depthFormat = VK_FORMAT_D24_UNORM_S8_UINT; VkImageTiling imageTiling; VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(g_currentGPU.device, depthFormat, &formatProperties); if(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { imageTiling = VK_IMAGE_TILING_LINEAR; } else if(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { imageTiling = VK_IMAGE_TILING_OPTIMAL; } else { checkVulkanError(VK_RESULT_MAX_ENUM, TEXT("サポートされていないフォーマットです")); return false; } VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = nullptr; imageCreateInfo.flags = 0; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = depthFormat; imageCreateInfo.extent.width = SCREEN_WIDTH; imageCreateInfo.extent.height = SCREEN_HEIGHT; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = imageTiling; imageCreateInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.queueFamilyIndexCount = 0; imageCreateInfo.pQueueFamilyIndices = nullptr; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; result = vkCreateImage(g_VulkanDevice, &imageCreateInfo, nullptr, &g_depthBufferTexture.image); checkVulkanError(result, TEXT("深度テクスチャ用イメージビュー作成失敗")); // メモリ要件を獲得 VkMemoryRequirements memoryRequirements; vkGetImageMemoryRequirements(g_VulkanDevice, g_depthBufferTexture.image, &memoryRequirements); VkFlags requirementsMask = 0; uint32_t typeBits = memoryRequirements.memoryTypeBits; uint32_t typeIndex = 0; for(const auto& memoryType : g_currentGPU.deviceMemoryProperties.memoryTypes) { if((typeBits & 0x1) == 1) { if((memoryType.propertyFlags & requirementsMask) == requirementsMask) { break; } } typeBits >>= 1; ++typeIndex; } // メモリ確保 VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.allocationSize = memoryRequirements.size; allocInfo.memoryTypeIndex = typeIndex; result = vkAllocateMemory(g_VulkanDevice, &allocInfo, nullptr, &g_depthBufferTexture.memory); checkVulkanError(result, TEXT("深度テクスチャ用メモリ確保失敗")); result = vkBindImageMemory(g_VulkanDevice, g_depthBufferTexture.image, g_depthBufferTexture.memory, 0); checkVulkanError(result, TEXT("深度テクスチャメモリにバインド失敗")); VkImageViewCreateInfo imageViewCreateInfo = {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.pNext = nullptr; imageViewCreateInfo.image = g_depthBufferTexture.image; imageViewCreateInfo.format = depthFormat; imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_R; imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_G; imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_B; imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_A; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; imageViewCreateInfo.subresourceRange.baseMipLevel = 0; imageViewCreateInfo.subresourceRange.levelCount = 1; imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCreateInfo.flags = 0; result = vkCreateImageView(g_VulkanDevice, &imageViewCreateInfo, nullptr, &g_depthBufferTexture.view); checkVulkanError(result, TEXT("深度テクスチャイメージビュー作成失敗")); setImageLayout( g_VulkanDevice, g_commandBuffers[g_currentBufferIndex], g_depthBufferTexture.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); //================================================== // フレームバッファの生成 //================================================== VkImageView attachments[2]; // 0=カラーバッファ、1=深度バッファ VkFramebufferCreateInfo frameBufferCreateInfo = {}; frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; frameBufferCreateInfo.pNext = nullptr; frameBufferCreateInfo.flags = 0; frameBufferCreateInfo.renderPass = VK_NULL_HANDLE; frameBufferCreateInfo.attachmentCount = 2; frameBufferCreateInfo.pAttachments = attachments; frameBufferCreateInfo.width = SCREEN_WIDTH; frameBufferCreateInfo.height = SCREEN_HEIGHT; frameBufferCreateInfo.layers = 1; g_frameBuffers.resize(SWAP_CHAIN_COUNT); for(uint32_t i = 0; i < SWAP_CHAIN_COUNT; ++i) { attachments[0] = g_backBuffersTextures[i].view; attachments[1] = g_depthBufferTexture.view; auto result = vkCreateFramebuffer(g_VulkanDevice, &frameBufferCreateInfo, nullptr, &g_frameBuffers[i]); checkVulkanError(result, TEXT("フレームバッファ作成失敗")); } return true; }
void loadTexture(const char* filename, VkFormat format, bool forceLinearTiling) { VkFormatProperties formatProperties; VkResult err; gli::textureCube texCube(gli::load(filename)); assert(!texCube.empty()); cubeMap.width = texCube[0].dimensions().x; cubeMap.height = texCube[0].dimensions().y; // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.extent = { cubeMap.width, cubeMap.height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.flags = 0; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; struct { VkImage image; VkDeviceMemory memory; } cubeFace[6]; // Allocate command buffer for image copies and layouts VkCommandBuffer cmdBuffer; VkCommandBufferAllocateInfo cmdBufAlllocatInfo = vkTools::initializers::commandBufferAllocateInfo( cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); err = vkAllocateCommandBuffers(device, &cmdBufAlllocatInfo, &cmdBuffer); assert(!err); VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); err = vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo); assert(!err); // Load separate cube map faces into linear tiled textures for (uint32_t face = 0; face < 6; ++face) { err = vkCreateImage(device, &imageCreateInfo, nullptr, &cubeFace[face].image); assert(!err); vkGetImageMemoryRequirements(device, cubeFace[face].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeFace[face].memory); assert(!err); err = vkBindImageMemory(device, cubeFace[face].image, cubeFace[face].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, cubeFace[face].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, cubeFace[face].memory, 0, memReqs.size, 0, &data); assert(!err); memcpy(data, texCube[face][subRes.mipLevel].data(), texCube[face][subRes.mipLevel].size()); vkUnmapMemory(device, cubeFace[face].memory); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( cmdBuffer, cubeFace[face].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); } // Transfer cube map faces to optimal tiling // Setup texture as blit target with optimal tiling imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; imageCreateInfo.arrayLayers = 6; err = vkCreateImage(device, &imageCreateInfo, nullptr, &cubeMap.image); assert(!err); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeMap.deviceMemory); assert(!err); err = vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy // Set initial layout for all array layers of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = 6; vkTools::setImageLayout( cmdBuffer, cubeMap.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy cube map faces one by one for (uint32_t face = 0; face < 6; ++face) { // Copy region for image blit VkImageCopy copyRegion = {}; copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.srcSubresource.baseArrayLayer = 0; copyRegion.srcSubresource.mipLevel = 0; copyRegion.srcSubresource.layerCount = 1; copyRegion.srcOffset = { 0, 0, 0 }; copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.dstSubresource.baseArrayLayer = face; copyRegion.dstSubresource.mipLevel = 0; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = cubeMap.width; copyRegion.extent.height = cubeMap.height; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( cmdBuffer, cubeFace[face].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); } // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( cmdBuffer, cubeMap.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cubeMap.imageLayout, subresourceRange); err = vkEndCommandBuffer(cmdBuffer); assert(!err); VkFence nullFence = { VK_NULL_HANDLE }; // Submit command buffer to graphis queue VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; err = vkQueueSubmit(queue, 1, &submitInfo, nullFence); assert(!err); err = vkQueueWaitIdle(queue); assert(!err); // Create sampler VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 8; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &cubeMap.sampler); assert(!err); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; view.subresourceRange.layerCount = 6; view.image = cubeMap.image; err = vkCreateImageView(device, &view, nullptr, &cubeMap.view); assert(!err); // Cleanup for (auto& face : cubeFace) { vkDestroyImage(device, face.image, nullptr); vkFreeMemory(device, face.memory, nullptr); } }
// Prepare all Vulkan resources for the 3D texture (including descriptors) // Does not fill the texture with data void prepareNoiseTexture(uint32_t width, uint32_t height, uint32_t depth) { // A 3D texture is described as width x height x depth texture.width = width; texture.height = height; texture.depth = depth; texture.mipLevels = 1; texture.format = VK_FORMAT_R8_UNORM; // Format support check // 3D texture support in Vulkan is mandatory (in contrast to OpenGL) so no need to check if it's supported VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, texture.format, &formatProperties); // Check if format supports transfer if (0 == (formatProperties.optimalTilingFeatures & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { std::cout << "Error: Device does not support flag TRANSFER_DST for selected texture format!" << std::endl; return; } // Check if GPU supports requested 3D texture dimensions uint32_t maxImageDimension3D(vulkanDevice->properties.limits.maxImageDimension3D); if (width > maxImageDimension3D || height > maxImageDimension3D || depth > maxImageDimension3D) { std::cout << "Error: Requested texture dimensions is greater than supported 3D texture dimension!" << std::endl; return; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_3D; imageCreateInfo.format = texture.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.extent.width = texture.width; imageCreateInfo.extent.height = texture.width; imageCreateInfo.extent.depth = texture.depth; // Set initial layout of the image to undefined imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image)); // Device local memory to back up image VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs = {}; 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)); // 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 = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.maxAnisotropy = 1.0; sampler.anisotropyEnable = VK_FALSE; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &texture.sampler)); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = texture.image; view.viewType = VK_IMAGE_VIEW_TYPE_3D; view.format = texture.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 = 1; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &texture.view)); // Fill image descriptor image info to be used descriptor set setup texture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; texture.descriptor.imageView = texture.view; texture.descriptor.sampler = texture.sampler; }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Copy/Blit Image"; VkImageCreateInfo image_info; VkImage bltSrcImage; VkImage bltDstImage; VkMemoryRequirements memReq; VkMemoryAllocateInfo memAllocInfo; VkDeviceMemory dmem; unsigned char *pImgMem; process_command_line_args(info, argc, argv); init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_window_size(info, 640, 640); init_connection(info); init_window(info); init_swapchain_extension(info); VkSurfaceCapabilitiesKHR surfCapabilities; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.gpus[0], info.surface, &surfCapabilities); if (!(surfCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { std::cout << "Surface cannot be destination of blit - abort \n"; exit(-1); } init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); /* VULKAN_KEY_START */ VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(info.gpus[0], info.format, &formatProps); assert( (formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source"); VkSemaphore presentCompleteSemaphore; VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; presentCompleteSemaphoreCreateInfo.pNext = NULL; presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; res = vkCreateSemaphore(info.device, &presentCompleteSemaphoreCreateInfo, NULL, &presentCompleteSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, presentCompleteSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); // Create an image, map it, and write some values to the image image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = NULL; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = info.format; image_info.extent.width = info.width; image_info.extent.height = info.height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.samples = NUM_SAMPLES; image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = NULL; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image_info.flags = 0; image_info.tiling = VK_IMAGE_TILING_LINEAR; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; res = vkCreateImage(info.device, &image_info, NULL, &bltSrcImage); memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.pNext = NULL; vkGetImageMemoryRequirements(info.device, bltSrcImage, &memReq); bool pass = memory_type_from_properties(info, memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); memAllocInfo.allocationSize = memReq.size; res = vkAllocateMemory(info.device, &memAllocInfo, NULL, &dmem); res = vkBindImageMemory(info.device, bltSrcImage, dmem, 0); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); VkFence cmdFence; init_fence(info, cmdFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submit_info = {}; submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &presentCompleteSemaphore; submit_info.pWaitDstStageMask = &pipe_stage_flags; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, cmdFence); assert(res == VK_SUCCESS); /* Make sure command buffer is finished before mapping */ do { res = vkWaitForFences(info.device, 1, &cmdFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, cmdFence, NULL); res = vkMapMemory(info.device, dmem, 0, memReq.size, 0, (void **)&pImgMem); // Checkerboard of 8x8 pixel squares for (int row = 0; row < info.height; row++) { for (int col = 0; col < info.width; col++) { unsigned char rgb = (((row & 0x8) == 0) ^ ((col & 0x8) == 0)) * 255; pImgMem[0] = rgb; pImgMem[1] = rgb; pImgMem[2] = rgb; pImgMem[3] = 255; pImgMem += 4; } } // Flush the mapped memory and then unmap it Assume it isn't coherent since // we didn't really confirm VkMappedMemoryRange memRange; memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; memRange.pNext = NULL; memRange.memory = dmem; memRange.offset = 0; memRange.size = memReq.size; res = vkFlushMappedMemoryRanges(info.device, 1, &memRange); vkUnmapMemory(info.device, dmem); vkResetCommandBuffer(info.cmd, 0); execute_begin_command_buffer(info); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); bltDstImage = info.buffers[info.current_buffer].image; // init_swap_chain will create the images as color attachment optimal // but we want transfer dst optimal set_image_layout(info, bltDstImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Do a 32x32 blit to all of the dst image - should get big squares VkImageBlit region; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; region.srcSubresource.baseArrayLayer = 0; region.srcSubresource.layerCount = 1; region.srcOffsets[0].x = 0; region.srcOffsets[0].y = 0; region.srcOffsets[0].z = 0; region.srcOffsets[1].x = 32; region.srcOffsets[1].y = 32; region.srcOffsets[1].z = 1; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.mipLevel = 0; region.dstSubresource.baseArrayLayer = 0; region.dstSubresource.layerCount = 1; region.dstOffsets[0].x = 0; region.dstOffsets[0].y = 0; region.dstOffsets[0].z = 0; region.dstOffsets[1].x = info.width; region.dstOffsets[1].y = info.height; region.dstOffsets[1].z = 1; vkCmdBlitImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR); // Do a image copy to part of the dst image - checks should stay small VkImageCopy cregion; cregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.srcSubresource.mipLevel = 0; cregion.srcSubresource.baseArrayLayer = 0; cregion.srcSubresource.layerCount = 1; cregion.srcOffset.x = 0; cregion.srcOffset.y = 0; cregion.srcOffset.z = 0; cregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.dstSubresource.mipLevel = 0; cregion.dstSubresource.baseArrayLayer = 0; cregion.dstSubresource.layerCount = 1; cregion.dstOffset.x = 256; cregion.dstOffset.y = 256; cregion.dstOffset.z = 0; cregion.extent.width = 128; cregion.extent.height = 128; cregion.extent.depth = 1; vkCmdCopyImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion); VkImageMemoryBarrier prePresentBarrier = {}; prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; prePresentBarrier.pNext = NULL; prePresentBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; prePresentBarrier.subresourceRange.baseMipLevel = 0; prePresentBarrier.subresourceRange.levelCount = 1; prePresentBarrier.subresourceRange.baseArrayLayer = 0; prePresentBarrier.subresourceRange.layerCount = 1; prePresentBarrier.image = info.buffers[info.current_buffer].image; vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &prePresentBarrier); res = vkEndCommandBuffer(info.cmd); VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = NULL; submit_info.pWaitDstStageMask = NULL; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, drawFence); assert(res == VK_SUCCESS); res = vkQueueWaitIdle(info.queue); assert(res == VK_SUCCESS); /* Now present the image in the window */ VkPresentInfoKHR present; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.pNext = NULL; present.swapchainCount = 1; present.pSwapchains = &info.swap_chain; present.pImageIndices = &info.current_buffer; present.pWaitSemaphores = NULL; present.waitSemaphoreCount = 0; present.pResults = NULL; /* Make sure command buffer is finished before presenting */ do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); res = vkQueuePresentKHR(info.queue, &present); assert(res == VK_SUCCESS); wait_seconds(1); /* VULKAN_KEY_END */ if (info.save_images) write_ppm(info, "copyblitimage"); vkDestroySemaphore(info.device, presentCompleteSemaphore, NULL); vkDestroyFence(info.device, drawFence, NULL); vkDestroyImage(info.device, bltSrcImage, NULL); vkFreeMemory(info.device, dmem, NULL); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Texture Initialization Sample"; init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_connection(info); init_window_size(info, 50, 50); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); /* VULKAN_KEY_START */ /* * Set up textures: * - Create a linear tiled image * - Map it and write the texture data into it * - If linear images cannot be used as textures, create an optimally * tiled image and blit from the linearly tiled image to the optimally * tiled image * - * - * - */ struct texture_object texObj; std::string filename = get_base_data_dir(); filename.append("lunarg.ppm"); if (!read_ppm(filename.c_str(), texObj.tex_width, texObj.tex_height, 0, NULL)) { std::cout << "Could not read texture file lunarg.ppm\n"; exit(-1); } VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R8G8B8A8_UNORM, &formatProps); /* See if we can use a linear tiled image for a texture, if not, we will * need a staging image for the texture data */ bool needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.pNext = NULL; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.extent.width = texObj.tex_width; image_create_info.extent.height = texObj.tex_height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = NUM_SAMPLES; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImage mappableImage; VkDeviceMemory mappableMemory; VkMemoryRequirements mem_reqs; /* Create a mappable image. It will be the texture if linear images are ok * to be textures or it will be the staging image if they are not. */ res = vkCreateImage(info.device, &image_create_info, NULL, &mappableImage); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(info.device, mappableImage, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; /* Find the memory type that is host mappable */ pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); assert(pass); /* allocate memory */ res = vkAllocateMemory(info.device, &mem_alloc, NULL, &(mappableMemory)); assert(res == VK_SUCCESS); /* bind memory */ res = vkBindImageMemory(info.device, mappableImage, mappableMemory, 0); assert(res == VK_SUCCESS); VkImageSubresource subres = {}; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = 0; subres.arrayLayer = 0; VkSubresourceLayout layout; void *data; /* Get the subresource layout so we know what the row pitch is */ vkGetImageSubresourceLayout(info.device, mappableImage, &subres, &layout); res = vkMapMemory(info.device, mappableMemory, 0, mem_reqs.size, 0, &data); assert(res == VK_SUCCESS); /* Read the ppm file into the mappable image's memory */ if (!read_ppm(filename.c_str(), texObj.tex_width, texObj.tex_height, layout.rowPitch, (unsigned char *)data)) { std::cout << "Could not load texture file lunarg.ppm\n"; exit(-1); } vkUnmapMemory(info.device, mappableMemory); if (!needStaging) { /* If we can use the linear tiled image as a texture, just do it */ texObj.image = mappableImage; texObj.mem = mappableMemory; texObj.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, texObj.imageLayout); } else { /* The mappable image cannot be our texture, so create an optimally * tiled image and blit to it */ image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; res = vkCreateImage(info.device, &image_create_info, NULL, &texObj.image); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(info.device, texObj.image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; /* Find memory type - don't specify any mapping requirements */ pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); assert(pass); /* allocate memory */ res = vkAllocateMemory(info.device, &mem_alloc, NULL, &texObj.mem); assert(res == VK_SUCCESS); /* bind memory */ res = vkBindImageMemory(info.device, texObj.image, texObj.mem, 0); assert(res == VK_SUCCESS); /* Since we're going to blit from the mappable image, set its layout to * SOURCE_OPTIMAL */ /* Side effect is that this will create info.cmd */ set_image_layout(info, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); /* Since we're going to blit to the texture image, set its layout to * DESTINATION_OPTIMAL */ set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageCopy copy_region; copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.srcSubresource.mipLevel = 0; copy_region.srcSubresource.baseArrayLayer = 0; copy_region.srcSubresource.layerCount = 1; copy_region.srcOffset.x = 0; copy_region.srcOffset.y = 0; copy_region.srcOffset.z = 0; copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_region.dstSubresource.mipLevel = 0; copy_region.dstSubresource.baseArrayLayer = 0; copy_region.dstSubresource.layerCount = 1; copy_region.dstOffset.x = 0; copy_region.dstOffset.y = 0; copy_region.dstOffset.z = 0; copy_region.extent.width = texObj.tex_width; copy_region.extent.height = texObj.tex_height; copy_region.extent.depth = 1; /* Put the copy command into the command buffer */ vkCmdCopyImage(info.cmd, mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texObj.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); /* Set the layout for the texture image from DESTINATION_OPTIMAL to * SHADER_READ_ONLY */ texObj.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; set_image_layout(info, texObj.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texObj.imageLayout); } execute_end_command_buffer(info); execute_queue_command_buffer(info); VkSamplerCreateInfo samplerCreateInfo = {}; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.magFilter = VK_FILTER_NEAREST; samplerCreateInfo.minFilter = VK_FILTER_NEAREST; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.mipLodBias = 0.0; samplerCreateInfo.anisotropyEnable = VK_FALSE, samplerCreateInfo.maxAnisotropy = 0; samplerCreateInfo.compareEnable = VK_FALSE; samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; samplerCreateInfo.minLod = 0.0; samplerCreateInfo.maxLod = 0.0; samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; /* create sampler */ res = vkCreateSampler(info.device, &samplerCreateInfo, NULL, &texObj.sampler); assert(res == VK_SUCCESS); VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.image = VK_NULL_HANDLE; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = VK_FORMAT_R8G8B8A8_UNORM; view_info.components.r = VK_COMPONENT_SWIZZLE_R; view_info.components.g = VK_COMPONENT_SWIZZLE_G; view_info.components.b = VK_COMPONENT_SWIZZLE_B; view_info.components.a = VK_COMPONENT_SWIZZLE_A; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; /* create image view */ view_info.image = texObj.image; res = vkCreateImageView(info.device, &view_info, NULL, &texObj.view); assert(res == VK_SUCCESS); info.textures.push_back(texObj); /* VULKAN_KEY_END */ /* Clean Up */ vkDestroySampler(info.device, texObj.sampler, NULL); vkDestroyImageView(info.device, texObj.view, NULL); vkDestroyImage(info.device, texObj.image, NULL); vkFreeMemory(info.device, texObj.mem, NULL); if (needStaging) { /* Release the resources for the staging image */ vkFreeMemory(info.device, mappableMemory, NULL); vkDestroyImage(info.device, mappableImage, NULL); } destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
void 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); }
void VkImageTest::CreateImage(uint32_t w, uint32_t h) { VkResult err; bool pass; uint32_t mipCount; VkFormat fmt; VkFormatProperties image_fmt; mipCount = 0; uint32_t _w = w; uint32_t _h = h; while( ( _w > 0 ) || ( _h > 0 ) ) { _w >>= 1; _h >>= 1; mipCount++; } fmt = VK_FORMAT_R8G8B8A8_UINT; // TODO: Pick known good format rather than just expect common format /* * XXX: What should happen if given NULL HANDLE for the pData argument? * We're not requesting VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS so there is * an expectation that pData is a valid pointer. * However, why include a returned size value? That implies that the * amount of data may vary and that doesn't work well for using a * fixed structure. */ vkGetPhysicalDeviceFormatProperties(this->objs[0], fmt, &image_fmt); // typedef struct VkImageCreateInfo_ // { // VkStructureType sType; // Must be VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO // const void* pNext; // Pointer to next structure. // VkImageType imageType; // VkFormat format; // VkExtent3D extent; // uint32_t mipLevels; // uint32_t arraySize; // VkSampleCountFlagBits samples; // VkImageTiling tiling; // VkFlags usage; // VkImageUsageFlags // VkFlags flags; // VkImageCreateFlags // } VkImageCreateInfo; VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = fmt; imageCreateInfo.arrayLayers = 1; imageCreateInfo.extent.width = w; imageCreateInfo.extent.height = h; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = mipCount; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; if ((image_fmt.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && (image_fmt.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; } else if ((image_fmt.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && (image_fmt.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; } else { ASSERT_TRUE(false) << "Cannot find supported tiling format - Exiting"; } // Image usage flags // VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000008, //typedef enum VkImageUsageFlags_ //{ // VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, // Can be used as a source of transfer operations // VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, // Can be used as a destination of transfer operations // VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, // Can be sampled from (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types) // VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, // Can be used as storage image (STORAGE_IMAGE descriptor type) // VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, // Can be used as framebuffer color attachment // VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, // Can be used as framebuffer depth/stencil attachment // VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, // Image data not needed outside of rendering // } VkImageUsageFlags; imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage( // VkDevice device, // const VkImageCreateInfo* pCreateInfo, // VkImage* pImage); err = vkCreateImage(device(), &imageCreateInfo, NULL, &m_image); ASSERT_VK_SUCCESS(err); VkMemoryRequirements mem_req; VkMemoryAllocateInfo mem_info = {}; mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_info.pNext = NULL; vkGetImageMemoryRequirements(device(), m_image, &mem_req); ASSERT_NE(0, mem_req.size) << "vkGetObjectMemoryRequirements (Image): Failed - expect images to require memory"; mem_info.allocationSize = mem_req.size; mem_info.memoryTypeIndex = 0; pass = m_device->phy().set_memory_type(mem_req.memoryTypeBits, &mem_info, 0); ASSERT_TRUE(pass); /* allocate memory */ err = vkAllocateMemory(device(), &mem_info, NULL, &m_image_mem); ASSERT_VK_SUCCESS(err); /* bind memory */ err = vkBindImageMemory(device(), m_image, m_image_mem, 0); ASSERT_VK_SUCCESS(err); }
void loadTextureArray(std::string filename, VkFormat format) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2DArray tex2DArray(gli::load((const char*)textureData, size)); #else gli::texture2DArray tex2DArray(gli::load(filename)); #endif assert(!tex2DArray.empty()); textureArray.width = tex2DArray.dimensions().x; textureArray.height = tex2DArray.dimensions().y; layerCount = tex2DArray.layers(); // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.extent = { textureArray.width, textureArray.height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.flags = 0; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; struct Layer { VkImage image; VkDeviceMemory memory; }; std::vector<Layer> arrayLayer; arrayLayer.resize(layerCount); // Allocate command buffer for image copies and layouts VkCommandBuffer cmdBuffer; VkCommandBufferAllocateInfo cmdBufAlllocatInfo = vkTools::initializers::commandBufferAllocateInfo( cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); VkResult err = vkAllocateCommandBuffers(device, &cmdBufAlllocatInfo, &cmdBuffer); assert(!err); VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); err = vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo); assert(!err); // Load separate cube map faces into linear tiled textures for (uint32_t i = 0; i < layerCount; ++i) { err = vkCreateImage(device, &imageCreateInfo, nullptr, &arrayLayer[i].image); assert(!err); vkGetImageMemoryRequirements(device, arrayLayer[i].image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &arrayLayer[i].memory); assert(!err); err = vkBindImageMemory(device, arrayLayer[i].image, arrayLayer[i].memory, 0); assert(!err); VkImageSubresource subRes = {}; subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkSubresourceLayout subResLayout; void *data; vkGetImageSubresourceLayout(device, arrayLayer[i].image, &subRes, &subResLayout); assert(!err); err = vkMapMemory(device, arrayLayer[i].memory, 0, memReqs.size, 0, &data); assert(!err); memcpy(data, tex2DArray[i].data(), tex2DArray[i].size()); vkUnmapMemory(device, arrayLayer[i].memory); // Image barrier for linear image (base) // Linear image will be used as a source for the copy vkTools::setImageLayout( cmdBuffer, arrayLayer[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); } // Transfer cube map faces to optimal tiling // Setup texture as blit target with optimal tiling imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.arrayLayers = layerCount; err = vkCreateImage(device, &imageCreateInfo, nullptr, &textureArray.image); assert(!err); vkGetImageMemoryRequirements(device, textureArray.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &textureArray.deviceMemory); assert(!err); err = vkBindImageMemory(device, textureArray.image, textureArray.deviceMemory, 0); assert(!err); // Image barrier for optimal image (target) // Set initial layout for all array layers of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = layerCount; vkTools::setImageLayout( cmdBuffer, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy cube map faces one by one for (uint32_t i = 0; i < layerCount; ++i) { // Copy region for image blit VkImageCopy copyRegion = {}; copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.srcSubresource.baseArrayLayer = 0; copyRegion.srcSubresource.mipLevel = 0; copyRegion.srcSubresource.layerCount = 1; copyRegion.srcOffset = { 0, 0, 0 }; copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyRegion.dstSubresource.baseArrayLayer = i; copyRegion.dstSubresource.mipLevel = 0; copyRegion.dstSubresource.layerCount = 1; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent.width = textureArray.width; copyRegion.extent.height = textureArray.height; copyRegion.extent.depth = 1; // Put image copy into command buffer vkCmdCopyImage( cmdBuffer, arrayLayer[i].image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, textureArray.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); } // Change texture image layout to shader read after all layers have been copied textureArray.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( cmdBuffer, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, textureArray.imageLayout, subresourceRange); err = vkEndCommandBuffer(cmdBuffer); assert(!err); VkFence nullFence = { VK_NULL_HANDLE }; // Submit command buffer to graphis queue VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; err = vkQueueSubmit(queue, 1, &submitInfo, nullFence); assert(!err); err = vkQueueWaitIdle(queue); assert(!err); // Create sampler VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 8; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &textureArray.sampler); assert(!err); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.image = VK_NULL_HANDLE; view.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; view.subresourceRange.layerCount = layerCount; view.image = textureArray.image; err = vkCreateImageView(device, &view, nullptr, &textureArray.view); assert(!err); // Cleanup for (auto& layer : arrayLayer) { vkDestroyImage(device, layer.image, nullptr); vkFreeMemory(device, layer.memory, nullptr); } }
VkBool32 Example::buildTexture(const vkts::ICommandBuffersSP& cmdBuffer, vkts::IImageSP& stageImage, vkts::IDeviceMemorySP& stageDeviceMemoryImage) { auto imageData = vkts::imageDataLoad(VKTS_TEXTURE_NAME); if (!imageData.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not load image data: '%s'", VKTS_TEXTURE_NAME); return VK_FALSE; } // VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice->getPhysicalDevice(), imageData->getFormat(), &formatProperties); VkImageTiling imageTiling = VK_IMAGE_TILING_LINEAR; VkMemoryPropertyFlagBits memoryPropertyFlagBits = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; VkImageLayout initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; VkAccessFlags accessMask = VK_ACCESS_HOST_WRITE_BIT; // Check, how to upload image data. if (!(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Format not supported."); return VK_FALSE; } imageTiling = VK_IMAGE_TILING_OPTIMAL; memoryPropertyFlagBits = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; initialLayout = VK_IMAGE_LAYOUT_GENERAL; accessMask = 0; } // if (!createTexture(image, deviceMemoryImage, imageData, imageTiling, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, initialLayout, memoryPropertyFlagBits, accessMask)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image."); return VK_FALSE; } // VkImageMemoryBarrier imageMemoryBarrier; memset(&imageMemoryBarrier, 0, sizeof(VkImageMemoryBarrier)); imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.srcAccessMask = 0; // Defined later. imageMemoryBarrier.dstAccessMask = 0; // Defined later. imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;// Defined later. imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;// Defined later. imageMemoryBarrier.srcQueueFamilyIndex = 0; imageMemoryBarrier.dstQueueFamilyIndex = 0; imageMemoryBarrier.image = VK_NULL_HANDLE; // Defined later. imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; // imageMemoryBarrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageMemoryBarrier.image = image->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // // If the image is only accessible by the device ... if (memoryPropertyFlagBits == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { // ... create texture with host visibility. This texture contains the pixel data. if (!createTexture(stageImage, stageDeviceMemoryImage, imageData, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_ACCESS_HOST_WRITE_BIT)) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image."); return VK_FALSE; } // Prepare source image layout for copy. imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; imageMemoryBarrier.image = stageImage->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // Prepare target image layout for copy. imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.image = image->getImage(); cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); // Copy image data by command. VkImageCopy imageCopy; imageCopy.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.srcOffset = { 0, 0, 0}; imageCopy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; imageCopy.dstOffset = { 0, 0, 0}; imageCopy.extent = { imageData->getWidth(), imageData->getHeight(), imageData->getDepth()}; vkCmdCopyImage(cmdBuffer->getCommandBuffer(), stageImage->getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image->getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopy); // Switch back to original layout. imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; cmdBuffer->cmdPipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); } // sampler = vkts::samplerCreate(device->getDevice(), 0, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 0.0f, VK_FALSE, 1.0f, VK_FALSE, VK_COMPARE_OP_NEVER, 0.0f, 0.0f, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, VK_FALSE); if (!sampler.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create sampler."); return VK_FALSE; } imageView = vkts::imageViewCreate(device->getDevice(), 0, image->getImage(), VK_IMAGE_VIEW_TYPE_2D, image->getFormat(), { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); if (!imageView.get()) { vkts::logPrint(VKTS_LOG_ERROR, "Example: Could not create image view."); return VK_FALSE; } return VK_TRUE; }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Input Attachment Sample"; const bool depthPresent = false; const bool vertexPresent = false; process_command_line_args(info, argc, argv); init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R8G8B8A8_UNORM, &props); if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { std::cout << "VK_FORMAT_R8G8B8A8_UNORM format unsupported for input " "attachment\n"; exit(-1); } init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info); /* VULKAN_KEY_START */ // Create a framebuffer with 2 attachments, one the color attachment // the shaders render into, and the other an input attachment which // will be cleared to yellow, and then used by the shaders to color // the drawn triangle. Final result should be a yellow triangle // Create the image that will be used as the input attachment // The image for the color attachment is the presentable image already // created in init_swapchain() VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.pNext = NULL; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = info.format; image_create_info.extent.width = info.width; image_create_info.extent.height = info.height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = NUM_SAMPLES; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImage input_image; VkDeviceMemory input_memory; res = vkCreateImage(info.device, &image_create_info, NULL, &input_image); assert(res == VK_SUCCESS); VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(info.device, input_image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); assert(pass); res = vkAllocateMemory(info.device, &mem_alloc, NULL, &input_memory); assert(res == VK_SUCCESS); res = vkBindImageMemory(info.device, input_image, input_memory, 0); assert(res == VK_SUCCESS); // Set the image layout to TRANSFER_DST_OPTIMAL to be ready for clear set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); VkImageSubresourceRange srRange = {}; srRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srRange.baseMipLevel = 0; srRange.levelCount = VK_REMAINING_MIP_LEVELS; srRange.baseArrayLayer = 0; srRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkClearColorValue clear_color; clear_color.float32[0] = 1.0f; clear_color.float32[1] = 1.0f; clear_color.float32[2] = 0.0f; clear_color.float32[3] = 0.0f; // Clear the input attachment image to yellow vkCmdClearColorImage(info.cmd, input_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &srRange); // Set the image layout to SHADER_READONLY_OPTIMAL for use by the shaders set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.image = VK_NULL_HANDLE; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = info.format; view_info.components.r = VK_COMPONENT_SWIZZLE_R; view_info.components.g = VK_COMPONENT_SWIZZLE_G; view_info.components.b = VK_COMPONENT_SWIZZLE_B; view_info.components.a = VK_COMPONENT_SWIZZLE_A; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; VkImageView input_attachment_view; view_info.image = input_image; res = vkCreateImageView(info.device, &view_info, NULL, &input_attachment_view); assert(res == VK_SUCCESS); VkDescriptorImageInfo input_image_info = {}; input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; input_image_info.imageView = input_attachment_view; input_image_info.sampler = VK_NULL_HANDLE; VkDescriptorSetLayoutBinding layout_bindings[1]; layout_bindings[0].binding = 0; layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; layout_bindings[0].descriptorCount = 1; layout_bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; layout_bindings[0].pImmutableSamplers = NULL; VkDescriptorSetLayoutCreateInfo descriptor_layout = {}; descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptor_layout.pNext = NULL; descriptor_layout.bindingCount = 1; descriptor_layout.pBindings = layout_bindings; info.desc_layout.resize(NUM_DESCRIPTOR_SETS); res = vkCreateDescriptorSetLayout(info.device, &descriptor_layout, NULL, info.desc_layout.data()); assert(res == VK_SUCCESS); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = 0; pPipelineLayoutCreateInfo.pPushConstantRanges = NULL; pPipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS; pPipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data(); res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout); assert(res == VK_SUCCESS); // First attachment is the color attachment - clear at the beginning of the // renderpass and transition layout to PRESENT_SRC_KHR at the end of // renderpass VkAttachmentDescription attachments[2]; attachments[0].format = info.format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; attachments[0].flags = 0; // Second attachment is input attachment. Once cleared it should have // width*height yellow pixels. Doing a subpassLoad in the fragment shader // should give the shader the color at the fragments x,y location // from the input attachment attachments[1].format = info.format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].flags = 0; VkAttachmentReference color_reference = {}; color_reference.attachment = 0; color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference input_reference = {}; input_reference.attachment = 1; input_reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.flags = 0; subpass.inputAttachmentCount = 1; subpass.pInputAttachments = &input_reference; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_reference; subpass.pResolveAttachments = NULL; subpass.pDepthStencilAttachment = NULL; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = NULL; VkRenderPassCreateInfo rp_info = {}; rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; rp_info.pNext = NULL; rp_info.attachmentCount = 2; rp_info.pAttachments = attachments; rp_info.subpassCount = 1; rp_info.pSubpasses = &subpass; rp_info.dependencyCount = 0; rp_info.pDependencies = NULL; res = vkCreateRenderPass(info.device, &rp_info, NULL, &info.render_pass); assert(!res); init_shaders(info, vertShaderText, fragShaderText); VkImageView fb_attachments[2]; fb_attachments[1] = input_attachment_view; VkFramebufferCreateInfo fbc_info = {}; fbc_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbc_info.pNext = NULL; fbc_info.renderPass = info.render_pass; fbc_info.attachmentCount = 2; fbc_info.pAttachments = fb_attachments; fbc_info.width = info.width; fbc_info.height = info.height; fbc_info.layers = 1; uint32_t i; info.framebuffers = (VkFramebuffer *)malloc(info.swapchainImageCount * sizeof(VkFramebuffer)); for (i = 0; i < info.swapchainImageCount; i++) { fb_attachments[0] = info.buffers[i].view; res = vkCreateFramebuffer(info.device, &fbc_info, NULL, &info.framebuffers[i]); assert(res == VK_SUCCESS); } VkDescriptorPoolSize type_count[1]; type_count[0].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; type_count[0].descriptorCount = 1; VkDescriptorPoolCreateInfo descriptor_pool = {}; descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool.pNext = NULL; descriptor_pool.maxSets = 1; descriptor_pool.poolSizeCount = 1; descriptor_pool.pPoolSizes = type_count; res = vkCreateDescriptorPool(info.device, &descriptor_pool, NULL, &info.desc_pool); assert(res == VK_SUCCESS); VkDescriptorSetAllocateInfo desc_alloc_info[1]; desc_alloc_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; desc_alloc_info[0].pNext = NULL; desc_alloc_info[0].descriptorPool = info.desc_pool; desc_alloc_info[0].descriptorSetCount = 1; desc_alloc_info[0].pSetLayouts = info.desc_layout.data(); info.desc_set.resize(1); res = vkAllocateDescriptorSets(info.device, desc_alloc_info, info.desc_set.data()); assert(res == VK_SUCCESS); VkWriteDescriptorSet writes[1]; // Write descriptor set with one write describing input attachment writes[0] = {}; writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = info.desc_set[0]; writes[0].dstBinding = 0; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; writes[0].pImageInfo = &input_image_info; writes[0].pBufferInfo = nullptr; writes[0].pTexelBufferView = nullptr; writes[0].dstArrayElement = 0; vkUpdateDescriptorSets(info.device, 1, writes, 0, NULL); init_pipeline_cache(info); init_pipeline(info, depthPresent, vertexPresent); // Color attachment clear to gray VkClearValue clear_values; clear_values.color.float32[0] = 0.2f; clear_values.color.float32[1] = 0.2f; clear_values.color.float32[2] = 0.2f; clear_values.color.float32[3] = 0.2f; VkSemaphoreCreateInfo imageAcquiredSemaphoreCreateInfo; imageAcquiredSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; imageAcquiredSemaphoreCreateInfo.pNext = NULL; imageAcquiredSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &imageAcquiredSemaphoreCreateInfo, NULL, &info.imageAcquiredSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.imageAcquiredSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); VkRenderPassBeginInfo rp_begin; rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_begin.pNext = NULL; rp_begin.renderPass = info.render_pass; rp_begin.framebuffer = info.framebuffers[info.current_buffer]; rp_begin.renderArea.offset.x = 0; rp_begin.renderArea.offset.y = 0; rp_begin.renderArea.extent.width = info.width; rp_begin.renderArea.extent.height = info.height; rp_begin.clearValueCount = 1; rp_begin.pClearValues = &clear_values; vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline); vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL); init_viewports(info); init_scissors(info); vkCmdDraw(info.cmd, 3, 1, 0, 0); vkCmdEndRenderPass(info.cmd); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); /* VULKAN_KEY_END */ const VkCommandBuffer cmd_bufs[] = {info.cmd}; VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); execute_queue_cmdbuf(info, cmd_bufs, drawFence); do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, drawFence, NULL); execute_present_image(info); wait_seconds(1); if (info.save_images) write_ppm(info, "input_attachment"); vkDestroySemaphore(info.device, info.imageAcquiredSemaphore, NULL); vkDestroyImageView(info.device, input_attachment_view, NULL); vkDestroyImage(info.device, input_image, NULL); vkFreeMemory(info.device, input_memory, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_descriptor_pool(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); destroy_descriptor_and_pipeline_layouts(info); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Depth Buffer Sample"; /* * Make a depth buffer: * - Create an Image to be the depth buffer * - Find memory requirements * - Allocate and bind memory * - Set the image layout * - Create an attachment view */ init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); /* VULKAN_KEY_START */ VkImageCreateInfo image_info = {}; const VkFormat depth_format = VK_FORMAT_D16_UNORM; VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(info.gpus[0], depth_format, &props); if (props.linearTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { image_info.tiling = VK_IMAGE_TILING_LINEAR; } else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { image_info.tiling = VK_IMAGE_TILING_OPTIMAL; } else { /* Try other depth formats? */ std::cout << "VK_FORMAT_D16_UNORM Unsupported.\n"; exit(-1); } image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = NULL; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = depth_format; image_info.extent.width = info.width; image_info.extent.height = info.height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.samples = NUM_SAMPLES; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = NULL; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.image = VK_NULL_HANDLE; view_info.format = depth_format; view_info.components.r = VK_COMPONENT_SWIZZLE_R; view_info.components.g = VK_COMPONENT_SWIZZLE_G; view_info.components.b = VK_COMPONENT_SWIZZLE_B; view_info.components.a = VK_COMPONENT_SWIZZLE_A; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.flags = 0; VkMemoryRequirements mem_reqs; info.depth.format = depth_format; /* Create image */ res = vkCreateImage(info.device, &image_info, NULL, &info.depth.image); assert(res == VK_SUCCESS); vkGetImageMemoryRequirements(info.device, info.depth.image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; /* Use the memory properties to determine the type of memory required */ pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, /* No Requirements */ &mem_alloc.memoryTypeIndex); assert(pass); /* Allocate memory */ res = vkAllocateMemory(info.device, &mem_alloc, NULL, &info.depth.mem); assert(res == VK_SUCCESS); /* Bind memory */ res = vkBindImageMemory(info.device, info.depth.image, info.depth.mem, 0); assert(res == VK_SUCCESS); /* Set the image layout to depth stencil optimal */ set_image_layout(info, info.depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); /* Create image view */ view_info.image = info.depth.image; res = vkCreateImageView(info.device, &view_info, NULL, &info.depth.view); assert(res == VK_SUCCESS); execute_end_command_buffer(info); execute_queue_command_buffer(info); /* VULKAN_KEY_END */ /* Clean Up */ vkDestroyImageView(info.device, info.depth.view, NULL); vkDestroyImage(info.device, info.depth.image, NULL); vkFreeMemory(info.device, info.depth.mem, NULL); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
void VulkanContext::ChooseDevice(int physical_device) { physical_device_ = physical_device; ILOG("Chose physical device %d: %p", physical_device, physical_devices_[physical_device]); GetDeviceLayerProperties(); if (!CheckLayers(device_layer_properties_, device_layer_names_)) { WLOG("CheckLayers for device %d failed", physical_device); } vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, nullptr); assert(queue_count >= 1); queue_props.resize(queue_count); vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queue_props.data()); assert(queue_count >= 1); // Detect preferred formats, in this order. static const VkFormat depthStencilFormats[] = { VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, }; deviceInfo_.preferredDepthStencilFormat = VK_FORMAT_UNDEFINED; for (size_t i = 0; i < ARRAY_SIZE(depthStencilFormats); i++) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physical_devices_[physical_device_], depthStencilFormats[i], &props); if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { deviceInfo_.preferredDepthStencilFormat = depthStencilFormats[i]; break; } } if (deviceInfo_.preferredDepthStencilFormat == VK_FORMAT_UNDEFINED) { // WTF? This is bad. ELOG("Could not find a usable depth stencil format."); } // This is as good a place as any to do this vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties); vkGetPhysicalDeviceProperties(physical_devices_[physical_device_], &gpu_props); // Optional features vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &featuresAvailable_); memset(&featuresEnabled_, 0, sizeof(featuresEnabled_)); // Enable a few safe ones if they are available. if (featuresAvailable_.dualSrcBlend) { featuresEnabled_.dualSrcBlend = true; } if (featuresAvailable_.largePoints) { featuresEnabled_.largePoints = true; } if (featuresAvailable_.wideLines) { featuresEnabled_.wideLines = true; } if (featuresAvailable_.geometryShader) { featuresEnabled_.geometryShader = true; } if (featuresAvailable_.logicOp) { featuresEnabled_.logicOp = true; } if (featuresAvailable_.depthClamp) { featuresEnabled_.depthClamp = true; } if (featuresAvailable_.depthBounds) { featuresEnabled_.depthBounds = true; } if (featuresAvailable_.samplerAnisotropy) { featuresEnabled_.samplerAnisotropy = true; } // For easy wireframe mode, someday. if (featuresEnabled_.fillModeNonSolid) { featuresEnabled_.fillModeNonSolid = true; } GetDeviceLayerExtensionList(nullptr, device_extension_properties_); device_extensions_enabled_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); }
// Preapre an empty texture as the blit target from // the offscreen framebuffer void prepareTextureTarget(uint32_t width, uint32_t height, VkFormat format) { createSetupCommandBuffer(); VkResult err; // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); // Check if format is supported for optimal tiling assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); // Prepare blit target texture offScreenFrameBuf.textureTarget.width = width; offScreenFrameBuf.textureTarget.height = height; VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.extent = { width, height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageCreateInfo.flags = 0; imageCreateInfo.pQueueFamilyIndices = 0; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; err = vkCreateImage(device, &imageCreateInfo, nullptr, &offScreenFrameBuf.textureTarget.image); assert(!err); vkGetImageMemoryRequirements(device, offScreenFrameBuf.textureTarget.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); err = vkAllocateMemory(device, &memAllocInfo, nullptr, &offScreenFrameBuf.textureTarget.deviceMemory); assert(!err); err = vkBindImageMemory(device, offScreenFrameBuf.textureTarget.image, offScreenFrameBuf.textureTarget.deviceMemory, 0); assert(!err); offScreenFrameBuf.textureTarget.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( setupCmdBuffer, offScreenFrameBuf.textureTarget.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, offScreenFrameBuf.textureTarget.imageLayout); // Create sampler VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = TEX_FILTER; sampler.minFilter = TEX_FILTER; 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 = 0; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; err = vkCreateSampler(device, &sampler, nullptr, &offScreenFrameBuf.textureTarget.sampler); assert(!err); // Create image view VkImageViewCreateInfo view = {}; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; 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 = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1 }; view.image = offScreenFrameBuf.textureTarget.image; err = vkCreateImageView(device, &view, nullptr, &offScreenFrameBuf.textureTarget.view); assert(!err); flushSetupCommandBuffer(); }