void loadAssets() { // Models models.skybox.loadFromFile(vulkanDevice, getAssetPath() + "models/cube.obj", vertexLayout, 0.05f, queue); std::vector<std::string> filenames = {"sphere.obj", "teapot.dae", "torusknot.obj"}; for (auto file : filenames) { vks::Model model; model.loadFromFile(vulkanDevice, getAssetPath() + "models/" + file, vertexLayout, 0.05f, queue); models.objects.push_back(model); } // Load HDR texture (equirectangular projected) // VK_FORMAT_BC6H_UFLOAT_BLOCK is a compressed 16 bit unsigned floating point format for storing HDR content textures.envmap.loadFromFile(getAssetPath() + "textures/hdr_uffizi_bc6uf.DDS", VK_FORMAT_BC6H_UFLOAT_BLOCK, vulkanDevice, queue); // Custom sampler with clamping adress mode vkDestroySampler(device, textures.envmap.sampler, nullptr); VkSamplerCreateInfo sampler{}; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; 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.maxLod = (float)textures.envmap.mipLevels; sampler.maxLod = 1.0f; sampler.anisotropyEnable = VK_TRUE; sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &sampler, nullptr, &textures.envmap.sampler)); textures.envmap.descriptor.sampler = textures.envmap.sampler; }
void op3d::Engine::createTextureSampler() { VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.anisotropyEnable = VK_TRUE; samplerInfo.maxAnisotropy = 1; samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; samplerInfo.unnormalizedCoordinates = VK_FALSE; samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 0.0f; if(vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } }
Error SamplerImpl::init(const SamplerInitInfo& ii) { // Fill the create cio VkSamplerCreateInfo ci; memset(&ci, 0, sizeof(ci)); ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; if(ii.m_minMagFilter == SamplingFilter::NEAREST) { ci.magFilter = ci.minFilter = VK_FILTER_NEAREST; } else { ANKI_ASSERT(ii.m_minMagFilter == SamplingFilter::LINEAR); ci.magFilter = ci.minFilter = VK_FILTER_LINEAR; } if(ii.m_mipmapFilter == SamplingFilter::BASE || ii.m_mipmapFilter == SamplingFilter::NEAREST) { ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; } else { ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; } if(ii.m_repeat) { ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; } else { ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; } ci.mipLodBias = 0.0; if(ii.m_anisotropyLevel > 0) { ci.anisotropyEnable = VK_TRUE; ci.maxAnisotropy = ii.m_anisotropyLevel; } ci.compareOp = convertCompareOp(ii.m_compareOperation); if(ci.compareOp != VK_COMPARE_OP_ALWAYS) { ci.compareEnable = VK_TRUE; } ci.minLod = ii.m_minLod; ci.maxLod = ii.m_maxLod; ci.unnormalizedCoordinates = VK_FALSE; // Create ANKI_VK_CHECK(vkCreateSampler(getDevice(), &ci, nullptr, &m_handle)); return ErrorCode::NONE; }
VkSampler null_sampler() { if (g_null_sampler) return g_null_sampler; VkSamplerCreateInfo sampler_info = {}; sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler_info.anisotropyEnable = VK_FALSE; sampler_info.compareEnable = VK_FALSE; sampler_info.unnormalizedCoordinates = VK_FALSE; sampler_info.mipLodBias = 0; sampler_info.maxAnisotropy = 0; sampler_info.magFilter = VK_FILTER_NEAREST; sampler_info.minFilter = VK_FILTER_NEAREST; sampler_info.compareOp = VK_COMPARE_OP_NEVER; sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; vkCreateSampler(*g_current_renderer, &sampler_info, nullptr, &g_null_sampler); return g_null_sampler; }
VkSampler create_sampler(VkSamplerCreateInfo& info) { VkSampler sampler; VkResult err; err = vkCreateSampler(_vulkan_device, &info, NULL, &sampler); assert(!err); return sampler; }
void VulkanTexturedQuad::CreateSampler () { VkSamplerCreateInfo samplerCreateInfo = {}; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.magFilter = VK_FILTER_LINEAR; samplerCreateInfo.minFilter = VK_FILTER_LINEAR; vkCreateSampler (device_, &samplerCreateInfo, nullptr, &sampler_); }
void NvModelExtVK::PrepareForRendering(NvVkContext& vk, NvModelExt* pModel) { VkResult result; VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samplerCreateInfo.magFilter = VK_FILTER_LINEAR; samplerCreateInfo.minFilter = VK_FILTER_LINEAR; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerCreateInfo.mipLodBias = 0.0; samplerCreateInfo.maxAnisotropy = 1; samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; samplerCreateInfo.minLod = 0.0; samplerCreateInfo.maxLod = 16.0; samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; result = vkCreateSampler(vk.device(), &samplerCreateInfo, 0, &m_linearSampler); // Get GL usable versions of all the textures used by the model uint32_t textureCount = m_pSourceModel->GetTextureCount(); m_textures.resize(textureCount); for (uint32_t textureIndex = 0; textureIndex < textureCount; ++textureIndex) { NvVkTexture* t = new NvVkTexture; if (vk.uploadTextureFromFile(m_pSourceModel->GetTextureName(textureIndex).c_str(), *t)) { m_textures[textureIndex] = t; } } // Get VK usable versions of all the materials in the model uint32_t materialCount = pModel->GetMaterialCount(); m_materials.resize(materialCount); if (materialCount > 0) { for (uint32_t materialIndex = 0; materialIndex < materialCount; ++materialIndex) { m_materials[materialIndex].InitFromMaterial(m_pSourceModel, materialIndex); } } // Get VK renderable versions of all meshes in the model uint32_t meshCount = pModel->GetMeshCount(); m_meshes.resize(meshCount); if (meshCount > 0) { for (uint32_t meshIndex = 0; meshIndex < meshCount; ++meshIndex) { m_meshes[meshIndex] = new NvMeshExtVK; m_meshes[meshIndex]->InitFromSubmesh(vk, pModel, meshIndex); } } InitVertexState(); }
bool ObjectCache::CreateStaticSamplers() { VkSamplerCreateInfo create_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext 0, // VkSamplerCreateFlags flags VK_FILTER_NEAREST, // VkFilter magFilter VK_FILTER_NEAREST, // VkFilter minFilter VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, // VkSamplerAddressMode addressModeU VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, // VkSamplerAddressMode addressModeV VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW 0.0f, // float mipLodBias VK_FALSE, // VkBool32 anisotropyEnable 1.0f, // float maxAnisotropy VK_FALSE, // VkBool32 compareEnable VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp std::numeric_limits<float>::min(), // float minLod std::numeric_limits<float>::max(), // float maxLod VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor VK_FALSE // VkBool32 unnormalizedCoordinates }; VkResult res = vkCreateSampler(g_vulkan_context->GetDevice(), &create_info, nullptr, &m_point_sampler); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateSampler failed: "); return false; } // Most fields are shared across point<->linear samplers, so only change those necessary. create_info.minFilter = VK_FILTER_LINEAR; create_info.magFilter = VK_FILTER_LINEAR; create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; res = vkCreateSampler(g_vulkan_context->GetDevice(), &create_info, nullptr, &m_linear_sampler); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateSampler failed: "); return false; } return true; }
VkSampler ObjectCache::GetSampler(const SamplerState& info) { auto iter = m_sampler_cache.find(info); if (iter != m_sampler_cache.end()) return iter->second; static constexpr std::array<VkFilter, 4> filters = {{VK_FILTER_NEAREST, VK_FILTER_LINEAR}}; static constexpr std::array<VkSamplerMipmapMode, 2> mipmap_modes = { {VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_MIPMAP_MODE_LINEAR}}; static constexpr std::array<VkSamplerAddressMode, 4> address_modes = { {VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT}}; VkSamplerCreateInfo create_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext 0, // VkSamplerCreateFlags flags filters[static_cast<u32>(info.mag_filter.Value())], // VkFilter magFilter filters[static_cast<u32>(info.min_filter.Value())], // VkFilter minFilter mipmap_modes[static_cast<u32>(info.mipmap_filter.Value())], // VkSamplerMipmapMode mipmapMode address_modes[static_cast<u32>(info.wrap_u.Value())], // VkSamplerAddressMode addressModeU address_modes[static_cast<u32>(info.wrap_v.Value())], // VkSamplerAddressMode addressModeV VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW info.lod_bias / 256.0f, // float mipLodBias VK_FALSE, // VkBool32 anisotropyEnable 0.0f, // float maxAnisotropy VK_FALSE, // VkBool32 compareEnable VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp info.min_lod / 16.0f, // float minLod info.max_lod / 16.0f, // float maxLod VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor VK_FALSE // VkBool32 unnormalizedCoordinates }; // Can we use anisotropic filtering with this sampler? if (info.anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) { // Cap anisotropy to device limits. create_info.anisotropyEnable = VK_TRUE; create_info.maxAnisotropy = std::min(static_cast<float>(1 << g_ActiveConfig.iMaxAnisotropy), g_vulkan_context->GetMaxSamplerAnisotropy()); } VkSampler sampler = VK_NULL_HANDLE; VkResult res = vkCreateSampler(g_vulkan_context->GetDevice(), &create_info, nullptr, &sampler); if (res != VK_SUCCESS) LOG_VULKAN_ERROR(res, "vkCreateSampler failed: "); // Store it even if it failed m_sampler_cache.emplace(info, sampler); return sampler; }
void Copy_To_Swapchain::create() { set_layout = Descriptor_Set_Layout() .sampler (0, VK_SHADER_STAGE_COMPUTE_BIT) .sampled_image (1, VK_SHADER_STAGE_COMPUTE_BIT) .storage_image (2, VK_SHADER_STAGE_COMPUTE_BIT) .create ("copy_to_swapchain_set_layout"); // pipeline layout { VkPushConstantRange range; range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; range.offset = 0; range.size = 8; // uint32 width + uint32 height VkPipelineLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; create_info.setLayoutCount = 1; create_info.pSetLayouts = &set_layout; create_info.pushConstantRangeCount = 1; create_info.pPushConstantRanges = ⦥ VK_CHECK(vkCreatePipelineLayout(vk.device, &create_info, nullptr, &pipeline_layout)); } // pipeline { VkShaderModule copy_shader = vk_load_spirv("spirv/copy_to_swapchain.comp.spv"); VkPipelineShaderStageCreateInfo compute_stage { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; compute_stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; compute_stage.module = copy_shader; compute_stage.pName = "main"; VkComputePipelineCreateInfo create_info{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; create_info.stage = compute_stage; create_info.layout = pipeline_layout; VK_CHECK(vkCreateComputePipelines(vk.device, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline)); vkDestroyShaderModule(vk.device, copy_shader, nullptr); } // point sampler { VkSamplerCreateInfo create_info { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; VK_CHECK(vkCreateSampler(vk.device, &create_info, nullptr, &point_sampler)); vk_set_debug_name(point_sampler, "point_sampler"); } }
bool game_buffer_editor_create(game_buffer_editor_create_info *game_buffer_editor_create_info, game_buffer_editor *game_buffer_editor) { ImGuiIO *imgui_io = &ImGui::GetIO(); vulkan_image_2d_bgra_srgb imgui_white_vulkan_image = {}; vulkan_image_2d_bgra_srgb imgui_font_atlas_vulkan_image = {}; vulkan_buffer imgui_vertex_index_vulkan_buffer = {}; VkResult vk_result = {}; VkPipeline imgui_vulkan_pipeline = {}; VkDescriptorSetLayout imgui_vulkan_pipeline_descriptor_set_layout = {}; VkPipelineLayout imgui_vulkan_pipeline_layout; VkDescriptorPool imgui_vulkan_descriptor_pool = {}; VkDescriptorSet imgui_vulkan_descriptor_sets[2] = {}; { { game_buffer_editor_create_info->set_imgui_keymap(imgui_io); imgui_io->DisplaySize.x = (float)game_buffer_editor_create_info->game_buffer->vulkan_framebuffer_image_width; imgui_io->DisplaySize.y = (float)game_buffer_editor_create_info->game_buffer->vulkan_framebuffer_image_height; imgui_io->IniFilename = game_buffer_editor_create_info->imgui_init_file; imgui_io->MousePos = {-1, -1}; { uint8 white_image_data[4 * 4 * 4]; memset(white_image_data, 0xFF, sizeof(white_image_data)); if (!vulkan_image_2d_bgra_srgb_create(game_buffer_editor_create_info->vulkan->device, &game_buffer_editor_create_info->vulkan->physical_device_memory_properties, white_image_data, 4, 4, VK_IMAGE_USAGE_SAMPLED_BIT, &imgui_white_vulkan_image)) { return false; } if (!imgui_io->Fonts->AddFontFromFileTTF(game_buffer_editor_create_info->imgui_font_file, (float)game_buffer_editor_create_info->imgui_font_size)) { return false; } unsigned char* font_atlas; int font_atlas_width; int font_atlas_height; imgui_io->Fonts->GetTexDataAsRGBA32(&font_atlas, &font_atlas_width, &font_atlas_height); if (!vulkan_image_2d_bgra_srgb_create(game_buffer_editor_create_info->vulkan->device, &game_buffer_editor_create_info->vulkan->physical_device_memory_properties, font_atlas, font_atlas_width, font_atlas_height, VK_IMAGE_USAGE_SAMPLED_BIT, &imgui_font_atlas_vulkan_image)) { return false; } imgui_io->Fonts->TexID = (void *)imgui_font_atlas_vulkan_image.image; } if (!vulkan_buffer_create(game_buffer_editor_create_info->vulkan->device, &game_buffer_editor_create_info->vulkan->physical_device_memory_properties, m_megabytes(16), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &imgui_vertex_index_vulkan_buffer)) { return false; } } { VkPipelineShaderStageCreateInfo shader_stage_create_infos[2] = { { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }, { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO } }; shader_stage_create_infos[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shader_stage_create_infos[0].module = game_buffer_editor_create_info->imgui_vulkan_shaders[0]; shader_stage_create_infos[0].pName = "main"; shader_stage_create_infos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shader_stage_create_infos[1].module = game_buffer_editor_create_info->imgui_vulkan_shaders[1]; shader_stage_create_infos[1].pName = "main"; VkVertexInputBindingDescription vertex_input_binding_description = { 0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX }; VkVertexInputAttributeDescription vertex_input_attribute_descriptions[3] = { { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32_SFLOAT, 8 }, { 2, 0, VK_FORMAT_R8G8B8A8_UNORM, 16 } }; VkPipelineVertexInputStateCreateInfo vertex_input_state_info = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; vertex_input_state_info.vertexBindingDescriptionCount = 1; vertex_input_state_info.pVertexBindingDescriptions = &vertex_input_binding_description; vertex_input_state_info.vertexAttributeDescriptionCount = m_countof(vertex_input_attribute_descriptions); vertex_input_state_info.pVertexAttributeDescriptions = vertex_input_attribute_descriptions; VkPipelineInputAssemblyStateCreateInfo input_assembly_state_info = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; input_assembly_state_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineViewportStateCreateInfo viewport_state_info = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; viewport_state_info.viewportCount = 1; viewport_state_info.scissorCount = 1; VkViewport viewport = {}; VkRect2D scissor = {}; viewport_state_info.pViewports = &viewport; viewport_state_info.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterization_state_info = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rasterization_state_info.polygonMode = VK_POLYGON_MODE_FILL; rasterization_state_info.cullMode = VK_CULL_MODE_BACK_BIT; rasterization_state_info.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterization_state_info.lineWidth = 1; VkPipelineMultisampleStateCreateInfo multisample_state_info = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; multisample_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineDepthStencilStateCreateInfo depth_stencil_state_info = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; VkPipelineColorBlendStateCreateInfo color_blend_state_info = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; color_blend_state_info.attachmentCount = 1; VkPipelineColorBlendAttachmentState color_blend_attachment_state = {}; color_blend_attachment_state.blendEnable = VK_TRUE; color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; color_blend_state_info.pAttachments = &color_blend_attachment_state; VkPipelineDynamicStateCreateInfo dynamic_state_info = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; dynamic_state_info.dynamicStateCount = 2; VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; dynamic_state_info.pDynamicStates = dynamic_states; VkDescriptorSetLayoutBinding descriptor_set_layout_binding = { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; descriptor_set_layout_create_info.bindingCount = 1; descriptor_set_layout_create_info.pBindings = &descriptor_set_layout_binding; if ((vk_result = vkCreateDescriptorSetLayout(game_buffer_editor_create_info->vulkan->device, &descriptor_set_layout_create_info, nullptr, &imgui_vulkan_pipeline_descriptor_set_layout)) != VK_SUCCESS) { return false; } VkPipelineLayoutCreateInfo pipeline_layout_create_info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipeline_layout_create_info.setLayoutCount = 1; pipeline_layout_create_info.pSetLayouts = &imgui_vulkan_pipeline_descriptor_set_layout; VkPushConstantRange push_constant_range = { VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(mat4) }; pipeline_layout_create_info.pushConstantRangeCount = 1; pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; if((vk_result = vkCreatePipelineLayout(game_buffer_editor_create_info->vulkan->device, &pipeline_layout_create_info, nullptr, &imgui_vulkan_pipeline_layout)) != VK_SUCCESS) { return false; } VkGraphicsPipelineCreateInfo pipeline_info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipeline_info.stageCount = m_countof(shader_stage_create_infos); pipeline_info.pStages = shader_stage_create_infos; pipeline_info.pVertexInputState = &vertex_input_state_info; pipeline_info.pInputAssemblyState = &input_assembly_state_info; pipeline_info.pViewportState = &viewport_state_info; pipeline_info.pRasterizationState = &rasterization_state_info; pipeline_info.pMultisampleState = &multisample_state_info; pipeline_info.pDepthStencilState = &depth_stencil_state_info; pipeline_info.pColorBlendState = &color_blend_state_info; pipeline_info.pDynamicState = &dynamic_state_info; pipeline_info.layout = imgui_vulkan_pipeline_layout; pipeline_info.renderPass = game_buffer_editor_create_info->game_buffer->vulkan_render_pass; pipeline_info.basePipelineIndex = -1; if((vk_result = vkCreateGraphicsPipelines(game_buffer_editor_create_info->vulkan->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &imgui_vulkan_pipeline)) != VK_SUCCESS) { return false; } } { VkDescriptorPoolCreateInfo descriptor_pool_create_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; descriptor_pool_create_info.maxSets = 2; descriptor_pool_create_info.poolSizeCount = 2; VkDescriptorPoolSize descriptor_pool_sizes[2] = { { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } }; descriptor_pool_create_info.pPoolSizes = descriptor_pool_sizes; if ((vk_result = vkCreateDescriptorPool(game_buffer_editor_create_info->vulkan->device, &descriptor_pool_create_info, nullptr, &imgui_vulkan_descriptor_pool)) != VK_SUCCESS) { return false; } VkDescriptorSetAllocateInfo descriptor_set_allocate_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; descriptor_set_allocate_info.descriptorPool = imgui_vulkan_descriptor_pool; descriptor_set_allocate_info.descriptorSetCount = 2; VkDescriptorSetLayout descriptor_set_layouts[2] = { imgui_vulkan_pipeline_descriptor_set_layout, imgui_vulkan_pipeline_descriptor_set_layout }; descriptor_set_allocate_info.pSetLayouts = descriptor_set_layouts; if ((vk_result = vkAllocateDescriptorSets(game_buffer_editor_create_info->vulkan->device, &descriptor_set_allocate_info, imgui_vulkan_descriptor_sets)) != VK_SUCCESS) { return false; } VkSamplerCreateInfo sampler_create_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; sampler_create_info.magFilter = VK_FILTER_LINEAR; sampler_create_info.minFilter = VK_FILTER_LINEAR; sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; VkSampler sampler = {}; if ((vk_result = vkCreateSampler(game_buffer_editor_create_info->vulkan->device, &sampler_create_info, nullptr, &sampler)) != VK_SUCCESS) { return false; } VkWriteDescriptorSet write_descriptor_sets[2] = { { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET } }; write_descriptor_sets[0].dstSet = imgui_vulkan_descriptor_sets[0]; write_descriptor_sets[0].descriptorCount = 1; write_descriptor_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorImageInfo descriptor_image_info_0 = { sampler, imgui_font_atlas_vulkan_image.view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; write_descriptor_sets[0].pImageInfo = &descriptor_image_info_0; write_descriptor_sets[1].dstSet = imgui_vulkan_descriptor_sets[1]; write_descriptor_sets[1].descriptorCount = 1; write_descriptor_sets[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorImageInfo descriptor_image_info_1 = { sampler, imgui_white_vulkan_image.view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; write_descriptor_sets[1].pImageInfo = &descriptor_image_info_1; vkUpdateDescriptorSets(game_buffer_editor_create_info->vulkan->device, 2, write_descriptor_sets, 0, nullptr); } } string_ring_buffer message_string_ring_buffer = {}; memory_pool job_memory_pool = {}; memory_pool job_message_memory_pool = {}; { string_ring_buffer_create(&game_buffer_editor_create_info->game_buffer->memory_arena, 256, 2048, &message_string_ring_buffer); memory_pool_create<game_buffer_editor_job>(&game_buffer_editor_create_info->game_buffer->memory_arena, 32, &job_memory_pool); memory_pool_create<game_buffer_editor_job_message>(&game_buffer_editor_create_info->game_buffer->memory_arena, 32 * 32, &job_message_memory_pool); } { game_buffer_editor->imgui_io = imgui_io; game_buffer_editor->imgui_white_vulkan_image = imgui_white_vulkan_image; game_buffer_editor->imgui_font_atlas_vulkan_image = imgui_font_atlas_vulkan_image; game_buffer_editor->imgui_vertex_index_vulkan_buffer = imgui_vertex_index_vulkan_buffer; game_buffer_editor->imgui_vulkan_pipeline = imgui_vulkan_pipeline; game_buffer_editor->imgui_vulkan_pipeline_layout = imgui_vulkan_pipeline_layout; game_buffer_editor->imgui_vulkan_descriptor_pool = imgui_vulkan_descriptor_pool; m_array_assign(game_buffer_editor->imgui_vulkan_descriptor_sets, imgui_vulkan_descriptor_sets); game_buffer_editor->message_string_ring_buffer = message_string_ring_buffer; game_buffer_editor->job_memory_pool = job_memory_pool; game_buffer_editor->job_message_memory_pool = job_message_memory_pool; } return true; }
bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() { VkResult err; VkShaderModule vert_module; VkShaderModule frag_module; // Create The Shader Modules: { VkShaderModuleCreateInfo vert_info = {}; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_info.codeSize = __glsl_shader_vert_spv_len; vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; err = vkCreateShaderModule(g_Device, &vert_info, g_Allocator, &vert_module); ImGui_ImplGlfwVulkan_VkResult(err); VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.codeSize = __glsl_shader_frag_spv_len; frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; err = vkCreateShaderModule(g_Device, &frag_info, g_Allocator, &frag_module); ImGui_ImplGlfwVulkan_VkResult(err); } if (!g_FontSampler) { VkSamplerCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; info.magFilter = VK_FILTER_LINEAR; info.minFilter = VK_FILTER_LINEAR; info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; info.minLod = -1000; info.maxLod = 1000; err = vkCreateSampler(g_Device, &info, g_Allocator, &g_FontSampler); ImGui_ImplGlfwVulkan_VkResult(err); } if (!g_DescriptorSetLayout) { VkSampler sampler[1] = {g_FontSampler}; VkDescriptorSetLayoutBinding binding[1] = {}; binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding[0].descriptorCount = 1; binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; binding[0].pImmutableSamplers = sampler; VkDescriptorSetLayoutCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; info.bindingCount = 1; info.pBindings = binding; err = vkCreateDescriptorSetLayout(g_Device, &info, g_Allocator, &g_DescriptorSetLayout); ImGui_ImplGlfwVulkan_VkResult(err); } // Create Descriptor Set: { VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = g_DescriptorPool; alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &g_DescriptorSetLayout; err = vkAllocateDescriptorSets(g_Device, &alloc_info, &g_DescriptorSet); ImGui_ImplGlfwVulkan_VkResult(err); } if (!g_PipelineLayout) { VkPushConstantRange push_constants[1] = {}; push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; VkDescriptorSetLayout set_layout[1] = {g_DescriptorSetLayout}; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.setLayoutCount = 1; layout_info.pSetLayouts = set_layout; layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; err = vkCreatePipelineLayout(g_Device, &layout_info, g_Allocator, &g_PipelineLayout); ImGui_ImplGlfwVulkan_VkResult(err); } VkPipelineShaderStageCreateInfo stage[2] = {}; stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stage[0].module = vert_module; stage[0].pName = "main"; stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stage[1].module = frag_module; stage[1].pName = "main"; VkVertexInputBindingDescription binding_desc[1] = {}; binding_desc[0].stride = sizeof(ImDrawVert); binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkVertexInputAttributeDescription attribute_desc[3] = {}; attribute_desc[0].location = 0; attribute_desc[0].binding = binding_desc[0].binding; attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; attribute_desc[0].offset = (size_t)(&((ImDrawVert*)0)->pos); attribute_desc[1].location = 1; attribute_desc[1].binding = binding_desc[0].binding; attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; attribute_desc[1].offset = (size_t)(&((ImDrawVert*)0)->uv); attribute_desc[2].location = 2; attribute_desc[2].binding = binding_desc[0].binding; attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; attribute_desc[2].offset = (size_t)(&((ImDrawVert*)0)->col); VkPipelineVertexInputStateCreateInfo vertex_info = {}; vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_info.vertexBindingDescriptionCount = 1; vertex_info.pVertexBindingDescriptions = binding_desc; vertex_info.vertexAttributeDescriptionCount = 3; vertex_info.pVertexAttributeDescriptions = attribute_desc; VkPipelineInputAssemblyStateCreateInfo ia_info = {}; ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineViewportStateCreateInfo viewport_info = {}; viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_info.viewportCount = 1; viewport_info.scissorCount = 1; VkPipelineRasterizationStateCreateInfo raster_info = {}; raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; raster_info.polygonMode = VK_POLYGON_MODE_FILL; raster_info.cullMode = VK_CULL_MODE_NONE; raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; raster_info.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo ms_info = {}; ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState color_attachment[1] = {}; color_attachment[0].blendEnable = VK_TRUE; color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineDepthStencilStateCreateInfo depth_info = {}; depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; VkPipelineColorBlendStateCreateInfo blend_info = {}; blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; blend_info.attachmentCount = 1; blend_info.pAttachments = color_attachment; VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state.dynamicStateCount = 2; dynamic_state.pDynamicStates = dynamic_states; VkGraphicsPipelineCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.flags = g_PipelineCreateFlags; info.stageCount = 2; info.pStages = stage; info.pVertexInputState = &vertex_info; info.pInputAssemblyState = &ia_info; info.pViewportState = &viewport_info; info.pRasterizationState = &raster_info; info.pMultisampleState = &ms_info; info.pDepthStencilState = &depth_info; info.pColorBlendState = &blend_info; info.pDynamicState = &dynamic_state; info.layout = g_PipelineLayout; info.renderPass = g_RenderPass; err = vkCreateGraphicsPipelines(g_Device, g_PipelineCache, 1, &info, g_Allocator, &g_Pipeline); ImGui_ImplGlfwVulkan_VkResult(err); vkDestroyShaderModule(g_Device, vert_module, g_Allocator); vkDestroyShaderModule(g_Device, frag_module, g_Allocator); return true; }
tut1_error tut7_create_images(struct tut1_physical_device *phy_dev, struct tut2_device *dev, struct tut7_image *images, uint32_t image_count) { /* * In this function, we will create a bunch of images. Images in graphics serve essentially two purposes. One * is to provide data to shaders, traditionally known as textures. Another is to render into either as the * final result or for further use, traditionally also known as textures. Vulkan calls all of these "images", * which are just glorified "buffers". We already worked with a Vulkan buffer in Tutorial 4, which was just an * array of data. Images on the other hand can have up to 3 dimensions, a format (such as BGRA), multisampling * properties, tiling properties and a layout. They are glorified buffers because all of these features can be * emulated with buffers, although besides requiring more work in the shaders, using images also allows a lot * more optimization by the device and its driver. * * That said, creating images is fairly similar to buffers. You create an image, allocate memory to it, create * an image view for access to the image, you bind it to a command buffer through a descriptor set and go on * using it in the shaders. Like buffers, you can choose to initialize the image. The data sent through * images could be anything, such as textures used to draw objects, patterns used by a shader to apply an * effect, or just general data for the shaders. The image can be written to as well. The data written to an * image could also be for anything, such as the final colors that go on to be displayed on the screen, the * depth or stencil data, the output of a filter used for further processing, the processed image to be * retrieved by the application (e.g. used by gimp), a texture that evolves over time, etc. * * Loading the image data is outside the scope of this tutorial, so we'll leave that for another time. Once * the image is created, it's device memory can be mapped, loaded and unmapped, so it is not necessary to do * that in this function either. */ uint32_t successful = 0; tut1_error retval = TUT1_ERROR_NONE; VkResult res; for (uint32_t i = 0; i < image_count; ++i) { images[i].image = NULL; images[i].image_mem = NULL; images[i].view = NULL; images[i].sampler = NULL; /* * To create an image, we need a CreateInfo struct as usual. Some parts of this struct is similar to * VkBufferCreateInfo from Tutorial 3. The ones that need explanation are explained here. The image * type specifies what are the dimensions of the image. In these tutorial series, we will use 2D * images for simplicity. Also for simplicity, let's ignore mipmapping and image layers. The image * format is one of VK_FORMAT. A normal format could be VK_FORMAT_B8G8R8A8_UNORM, but it might make * sense to use other formats, especially for images that would get their data from a texture file. * * If the image is going to be initialized, for example from a texture file, then the structure of the * image data, otherwise known as "tiling", must be set to linear. This means that the image is stored * as a normal row-major array, so that when its memory is mapped by the application, it would make * sense! If the image is not to be initialized on the other hand, it is better to keep the tiling as * optimal, which means whatever format the GPU likes best. It is necessary for the application to * copy a linear image to an optimal one for GPU usage. If the application wants to read the image * back, it must copy it from an optimal image to a linear one. This also means that the `usage` of * the linear images can contain only TRANSFER_SRC and TRANSFER_DST bits. More on image copies when we * actually start using them. * * Linear images are sampled only once. Optimal images can be multisampled. You can read about * multisampling online (from OpenGL), but in short it asks for each pixel to be sampled at multiple * locations inside the pixel, which helps with antialiasing. Here, we will simply choose a higher * number of samples as allowed by the GPU (retrieved with vkGetPhysicalDeviceImageFormatProperties * below). * * Linear images are also restricted to 2D, no mipmapping and no layers, which is fortunate because we * wanted those for simplicity anyway! There is also a restriction on the format of the image, which * cannot be depth/stencil. * * In Tutorial 3, we specified the buffer usage as storage, which was a rather generic specification. * For the image, we have more options to specify the usage. Choosing the minimum usage bits for each * image, specifying only what we actually want to do with the image allows the GPU to possibly place * the image in the most optimal memory location, or load/unload the image at necessary times. This is * left to the application to provide as it varies from case to case. The usages in short are: * * Transfer src/dst: whether the image can be used as a source/destination of an image copy. * Sampled: whether the image can be sampled by a shader. * Storage: whether the image can be read from and written to by a shader. * Color attachment: whether the image can be used as a render target (for color). * Depth/stencil attachment: whether the image can be used as a render target (for depth/stencil). * Transient attachment: whether the image is lazily allocated (ignored for now). * Input attachment: whether the image can be read (unfiltered) by a shader (ignored for now). * * If the image was to be shared between queue families, it should be declared with a special * `sharingMode` specifying that there would be concurrent access to the image, and by which queue * families. We are going to use the images and views created here in multiple pipelines, one for each * swapchain image. Since those pipelines may be created on top of different queue families, we need * to tell Vulkan that these images would be shared. At the time of this writing, on Nvidia cards * there is only one queue family and sharing is meaningless. However, it is legal for a driver to * expose multiple similar queue families instead of one queue family with multiple queues. The * application is expected to provide the queue families that would use this image. Most likely, the * result of `tut7_get_presentable_queues` is what you would want. * * Finally, an image has a layout. Each layout is limited in what operations can be done in it, but * instead is optimal for a task. The possible image layouts are: * * Undefined: no device access is allowed. This is used as an initial layout and must be transitioned * away from before use. * Preinitialized: no device access is allowed. Similar to undefined, this is only an initial layout * and must be transitioned away from before use. The only difference is that the contents of the * image are kept during the transition. * General: supports all types of device access. * Color attachment optimal: only usable with color attachment images. * Depth/stencil attachment optimal: only usable with depth/stencil attachment images. * Depth/stencil read-only optimal: only usable with depth/stencil attachment images. The difference * between this and the depth/stencil attachment optimal layout is that this image can also be used * as a read-only sampled image or input attachment for use by the shaders. * Shader read-only optimal: only usable with sampled and input attachment images. Similar to * depth/stencil read-only optimal, this layout can be used as a read-only image or input attachment * for use by the shaders. * Transfer src/dst optimal: only usable with transfer src/dst images and must only be used as the * source or destination of an image transfer. * Present src (extension): used for presenting an image to a swapchain. An image taken from the * swapchain is in this layout and must be transitioned away before use after * vkAcquireNextImageKHR. Before giving the image back with vkQueuePresentKHR, it must be * transitioned again to this layout. * * For linear images, which are going to be initialized by the application, we will use the * preinitialized layout. Otherwise, the layout must be undefined and later transitioned to the * desired layout using a pipeline barrier (more on this later). */ VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; if (images[i].will_be_initialized || images[i].host_visible) { images[i].usage &= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; layout = VK_IMAGE_LAYOUT_PREINITIALIZED; tiling = VK_IMAGE_TILING_LINEAR; } else if (images[i].multisample) { /* * To get the format properties for an image, we need to tell Vulkan how we expect to create the image, * i.e. what is its format, type, tiling, usage and flags (which we didn't use). We could check many * of the parameters given to this function with the properties returned from this function, but we'll * just take a possible sampling count out of it, and assume the parameters are fine. In a real * application, you would want to do more validity checks. */ VkImageFormatProperties format_properties; res = vkGetPhysicalDeviceImageFormatProperties(phy_dev->physical_device, images[i].format, VK_IMAGE_TYPE_2D, tiling, images[i].usage, 0, &format_properties); tut1_error_sub_set_vkresult(&retval, res); if (res == 0) { for (uint32_t s = VK_SAMPLE_COUNT_16_BIT; s != 0; s >>= 1) if ((format_properties.sampleCounts & s)) { samples = s; break; } } } /* * Create the image with the above description as usual. The CreateInfo struct takes the parameters * and memory allocation callbacks are not used. */ bool shared = images[i].sharing_queue_count > 1; struct VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = images[i].format, .extent = {images[i].extent.width, images[i].extent.height, 1}, .mipLevels = 1, .arrayLayers = 1, .samples = samples, .tiling = tiling, .usage = images[i].usage, .sharingMode = shared?VK_SHARING_MODE_CONCURRENT:VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = shared?images[i].sharing_queue_count:0, .pQueueFamilyIndices = shared?images[i].sharing_queues:NULL, .initialLayout = layout, }; res = vkCreateImage(dev->device, &image_info, NULL, &images[i].image); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; /* * In Tutorial 4, we created a buffer, allocated memory for it and bound the memory to the buffer. * Images are glorified buffers and the process is similar. The same argument regarding host-coherent * memory holds here as well. So, if the image requires device-local memory, we will look for that, * otherwise we will look for memory that is not just host-visible, but also host-coherent. */ VkMemoryRequirements mem_req = {0}; vkGetImageMemoryRequirements(dev->device, images[i].image, &mem_req); uint32_t mem_index = tut4_find_suitable_memory(phy_dev, dev, &mem_req, images[i].host_visible? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (mem_index >= phy_dev->memories.memoryTypeCount) continue; VkMemoryAllocateInfo mem_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = mem_req.size, .memoryTypeIndex = mem_index, }; res = vkAllocateMemory(dev->device, &mem_info, NULL, &images[i].image_mem); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; res = vkBindImageMemory(dev->device, images[i].image, images[i].image_mem, 0); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; if (images[i].make_view) { /* * Once we have an image, we need a view on the image to be able to use it. This is just like in * Tutorial 4 where we had a view on the buffer to work with it. In Tutorial 4, we had divided up the * buffer for concurrent processing in the shaders, and each view looked at a specific part of the * buffer. With images, this could also be useful, for example if one large image contains multiple * areas of interest (such as a texture) where different shaders need to look at. However, let's keep * things as simple as possible and create a view that is as large as the image itself. * * The image view's CreateInfo is largely similar to the one for buffer views. For image views, we * need to specify which components of the image we want to view and the range is not a simple * (offset, size) as was in the buffer view. * * For the components, we have the option to not only select which components (R, G, B and A) to view, * but also to remap them (this operation is called swizzle). For example to get the value of the red * component in place of alpha etc. The mapping for each component can be specified separately, and * mapping 0 means identity. We are not going to remap anything, so we'll leave all fields in * `components` be 0. * * The range of the image asks for which mipmap levels and image array layers we are interested in, * which are simply both 0 because we have only one of each. As part of the range of the view, we also * need to specify which aspect of the image we are looking it. This could be color, depth, stencil * etc. Here, we will decide the aspect based on the image usage; if it's used as depth/stencil, we * will set both depth and stencil aspects. Otherwise we will view the color aspect. */ VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = images[i].image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = images[i].format, .subresourceRange = { .aspectMask = (images[i].usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0? VK_IMAGE_ASPECT_COLOR_BIT: VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; res = vkCreateImageView(dev->device, &view_info, NULL, &images[i].view); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; } if ((images[i].usage & VK_IMAGE_USAGE_SAMPLED_BIT)) { /* * If the image is going to be sampled, we can create a sampler for it as well. A sampler * specifies how to sample an image. An image is just a glorified buffer, i.e., it's just an * array, as I have said before as well. However, the sampler is what makes using images so * much more powerful. When accessing a buffer, you can access each index individually. With * a sampler, you can access an image at non-integer indices. The sampler then "filters" the * image to provide some data for that index. * * The simplest example is magnification. If you sample the image at coordinates (u+0.5,v) * where u and v are integer pixel locations, then the color you get could be the average of * the colors (values) at coordinates (u,v) and (u+1,v). Vulkan uses the term `texel` to refer * to these "texture" pixels. * * The sampler parameters are explained below: * * - magFilter, minFilter: what to do if asked to sample between the texels. The options are * to take the value of the nearest texel, or interpolate between neighbors. Think about it * as what to do if you try to zoom in or out of an image. We'll go with interpolation, * since it's nicer. * - mipmapMode: similarly, if the image has multiple mipmap levels, accessing between the * levels could either interpolate between two levels or clamp to the nearest. We don't use * mipmaps here, so this doesn't matter, but let's tell it to interpolate anyway. * - addressModeU/V/W: this specifies what happens if you access outside the image. The * options are to: * * repeat the image as if it was a tiled to infinity in each direction, * * mirrored repeat the image as if a larger image containing the image and its mirror * was tiled to infinity in each direction, * * clamp to edge so that any access out of the image boundaries returns the value at the * closest point on the edge of the image, * * clamp to border so that any access out of the image boundaries returns a special * "border" value for the image (border value defined below), * * mirrored clamp to edge so that any access out of the image boundaries returns the value * at the closest point on the edge of a larger image that is made up of the image and its * mirror. * Each of these modes is useful in different situations. "Repeat" is probably the most * problematic as it introduces discontinuity around the edges. "Mirrored" solves this * problem and can add some interesting effects. "Clamp to edge" also solves this problem, * and let's just use that. "Clamp to border" would introduce other edges, and I imagine is * most useful for debugging. * - anisotropyEnable, maxAnisotropy: whether anisotropic filtering is enabled and by how * much. Anisotropic filtering is expensive but nice, so let's enable it. The maximum value * for anisotropic filtering can be retrieved from the device's limits. * - compareEnable, compareOp: this is used with depth images to result in a reading of 0 or 1 * based on the result of a compare operation. We are not interested in this for now. * - minLod, maxLod: the level-of-detail value (mip level) gets clamped to these values. We * are not using mipmapped images, so we'll just give 0 and 1 respectively. * - borderColor: if the "clamp to border" addressing mode was selected, out-of-bound accesses * to the image would return the border color, which is set here. Options are limited: * transparent, white and black. Since we're using "clamp to edge" addressing, this value is * not used. * - unnormalizedCoordinates: with Vulkan, you can either index the image using the * unnormalized coordinates, so that u and v span from 0 to size of the image, or you can * access the image using normalized coordinates, so that u and v span from 0 to 1. * Unnormalized coordinates can be useful in some circumstances, but normalized coordinates * lets you access the image without dealing with its sizes. Aside from that, with * unnormalized coordinates, you are limited in the type of images you can access; only 1D * and 2D images with a single layer and single mip level are acceptable and essentially all * other features of the sampler must be disabled too. Needless to say, we will use * normalized coordinates. */ VkSamplerCreateInfo sampler_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .anisotropyEnable = true, .maxAnisotropy = phy_dev->properties.limits.maxSamplerAnisotropy, .minLod = 0, .maxLod = 1, }; res = vkCreateSampler(dev->device, &sampler_info, NULL, &images[i].sampler); tut1_error_sub_set_vkresult(&retval, res); if (res) continue; } ++successful; } /* * Now that you have learned all about images, we're not going to use them in this tutorial. Please don't hate * me. There is already so much here that rendering textured images can wait. It was not all in vein though * because we would need image views on the swapchain images anyway. Now at least you understand the * properties and restrictions of the swapchain images better. */ tut1_error_set_vkresult(&retval, successful == image_count?VK_SUCCESS:VK_INCOMPLETE); return retval; }
// Prepare a new framebuffer and attachments for offscreen rendering (G-Buffer) void prepareoffscreenfer() { { offscreen.width = width; offscreen.height = height; // Color attachments // Two floating point color buffers createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &offscreen.color[0]); createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &offscreen.color[1]); // Depth attachment createAttachment(depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, &offscreen.depth); // Set up separate renderpass with references to the colorand depth attachments std::array<VkAttachmentDescription, 3> attachmentDescs = {}; // Init attachment properties for (uint32_t i = 0; i < 3; ++i) { attachmentDescs[i].samples = VK_SAMPLE_COUNT_1_BIT; attachmentDescs[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachmentDescs[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachmentDescs[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachmentDescs[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; if (i == 2) { attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } else { attachmentDescs[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachmentDescs[i].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } } // Formats attachmentDescs[0].format = offscreen.color[0].format; attachmentDescs[1].format = offscreen.color[1].format; attachmentDescs[2].format = offscreen.depth.format; std::vector<VkAttachmentReference> colorReferences; colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); colorReferences.push_back({ 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); VkAttachmentReference depthReference = {}; depthReference.attachment = 2; depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.pColorAttachments = colorReferences.data(); subpass.colorAttachmentCount = 2; subpass.pDepthStencilAttachment = &depthReference; // Use subpass dependencies for attachment layput transitions std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.pAttachments = attachmentDescs.data(); renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size()); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 2; renderPassInfo.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreen.renderPass)); std::array<VkImageView, 3> attachments; attachments[0] = offscreen.color[0].view; attachments[1] = offscreen.color[1].view; attachments[2] = offscreen.depth.view; VkFramebufferCreateInfo fbufCreateInfo = {}; fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbufCreateInfo.pNext = NULL; fbufCreateInfo.renderPass = offscreen.renderPass; fbufCreateInfo.pAttachments = attachments.data(); fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size()); fbufCreateInfo.width = offscreen.width; fbufCreateInfo.height = offscreen.height; fbufCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreen.frameBuffer)); // Create sampler to sample from the color attachments VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; 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 = 1.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &offscreen.sampler)); } // Bloom separable filter pass { filterPass.width = width; filterPass.height = height; // Color attachments // Two floating point color buffers createAttachment(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &filterPass.color[0]); // Set up separate renderpass with references to the colorand depth attachments std::array<VkAttachmentDescription, 1> attachmentDescs = {}; // Init attachment properties attachmentDescs[0].samples = VK_SAMPLE_COUNT_1_BIT; attachmentDescs[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachmentDescs[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachmentDescs[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachmentDescs[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachmentDescs[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachmentDescs[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachmentDescs[0].format = filterPass.color[0].format; std::vector<VkAttachmentReference> colorReferences; colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.pColorAttachments = colorReferences.data(); subpass.colorAttachmentCount = 1; // Use subpass dependencies for attachment layput transitions std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.pAttachments = attachmentDescs.data(); renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescs.size()); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 2; renderPassInfo.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &filterPass.renderPass)); std::array<VkImageView, 1> attachments; attachments[0] = filterPass.color[0].view; VkFramebufferCreateInfo fbufCreateInfo = {}; fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbufCreateInfo.pNext = NULL; fbufCreateInfo.renderPass = filterPass.renderPass; fbufCreateInfo.pAttachments = attachments.data(); fbufCreateInfo.attachmentCount = static_cast<uint32_t>(attachments.size()); fbufCreateInfo.width = filterPass.width; fbufCreateInfo.height = filterPass.height; fbufCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &filterPass.frameBuffer)); // Create sampler to sample from the color attachments VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; 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 = 1.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &filterPass.sampler)); } }
void VkeCubeTexture::loadTextureFiles(const char **inPath){ bool imagesOK = true; VKA_INFO_MSG("Loading Cube Texture.\n"); for (uint32_t i = 0; i < 6; ++i){ if (!loadTexture(inPath[i], NULL, NULL, &m_width, &m_height)){ VKA_ERROR_MSG("Error loading texture image.\n"); printf("Texture : %d not available (%s).\n", i, inPath[i]); return; } } VulkanDC::Device::Queue::Name queueName = "DEFAULT_GRAPHICS_QUEUE"; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID; VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VulkanDC::Device::Queue *queue = device->getQueue(queueName); VkCommandBuffer cmd = VK_NULL_HANDLE; queue->beginCommandBuffer(cmdID, &cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); imageCreateAndBind( &m_data.image, &m_data.memory, m_format, VK_IMAGE_TYPE_2D, m_width, m_height, 1, 6, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VkImageUsageFlagBits)( VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ), VK_IMAGE_TILING_OPTIMAL); VkBuffer cubeMapBuffer; VkDeviceMemory cubeMapMem; bufferCreate(&cubeMapBuffer, m_width*m_height * 4 * 6, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); bufferAlloc(&cubeMapBuffer, &cubeMapMem, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VkDeviceSize dSize = m_width * m_height * 4; uint32_t rowPitch = m_width * 4; if (m_memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT){ imageSetLayoutBarrier(cmdID, queueName, m_data.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL); for (uint32_t i = 0; i < 6; ++i){ void *data = NULL; VkDeviceSize ofst = dSize*i; VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(),cubeMapMem, ofst, dSize, 0, &data), "Could not map memory for image.\n"); if (!loadTexture(inPath[i], (uint8_t**)&data, rowPitch, &m_width, &m_height)){ VKA_ERROR_MSG("Could not load final image.\n"); } vkUnmapMemory(getDefaultDevice(), cubeMapMem); } VkBufferImageCopy biCpyRgn[6]; for (uint32_t k = 0; k < 6; ++k){ VkDeviceSize ofst = dSize*k; biCpyRgn[k].bufferOffset = ofst; biCpyRgn[k].bufferImageHeight = 0; biCpyRgn[k].bufferRowLength = 0; biCpyRgn[k].imageExtent.width = m_width; biCpyRgn[k].imageExtent.height = m_height; biCpyRgn[k].imageExtent.depth = 1; biCpyRgn[k].imageOffset.x = 0; biCpyRgn[k].imageOffset.y = 0; biCpyRgn[k].imageOffset.z = 0; biCpyRgn[k].imageSubresource.baseArrayLayer = k; biCpyRgn[k].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; biCpyRgn[k].imageSubresource.layerCount = 1; biCpyRgn[k].imageSubresource.mipLevel = 0; } VkFence copyFence; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(fenceInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(device->getVKDevice(), &fenceInfo,NULL , ©Fence); vkCmdCopyBufferToImage(cmd, cubeMapBuffer, m_data.image, m_data.imageLayout, 6, biCpyRgn); queue->flushCommandBuffer(cmdID , ©Fence); vkWaitForFences(device->getVKDevice(), 1, ©Fence, VK_TRUE, 100000000000); vkDestroyBuffer(device->getVKDevice(), cubeMapBuffer, NULL); vkFreeMemory(device->getVKDevice(), cubeMapMem, NULL); } VkSamplerCreateInfo sampler; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler.pNext = NULL; sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 1; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VkImageViewCreateInfo view; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = m_format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 0 }; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.layerCount = 1; VKA_CHECK_ERROR(vkCreateSampler(getDefaultDevice(), &sampler,NULL, &m_data.sampler), "Could not create sampler for image texture.\n"); view.image = m_data.image; VKA_CHECK_ERROR(vkCreateImageView(getDefaultDevice(), &view,NULL, &m_data.view), "Could not create image view for texture.\n"); VKA_INFO_MSG("Created CUBE Image Texture.\n"); }
void VkeCubeTexture::loadCubeDDS(const char *inFile){ std::string searchPaths[] = { std::string(PROJECT_NAME), NVPWindow::sysExePath() + std::string(PROJECT_RELDIRECTORY), std::string(PROJECT_ABSDIRECTORY) }; nv_dds::CDDSImage ddsImage; for (uint32_t i = 0; i < 3; ++i){ std::string separator = ""; uint32_t strSize = searchPaths[i].size(); if(searchPaths[i].substr(strSize-1,strSize) != "/") separator = "/"; std::string filePath = searchPaths[i] + separator + std::string("images/") + std::string(inFile); ddsImage.load(filePath, true); if (ddsImage.is_valid()) break; } if (!ddsImage.is_valid()){ perror("Could not cube load texture image.\n"); exit(1); } uint32_t imgW = ddsImage.get_width(); uint32_t imgH = ddsImage.get_height(); uint32_t comCount = ddsImage.get_components(); uint32_t fmt = ddsImage.get_format(); bool isCube = ddsImage.is_cubemap(); bool isComp = ddsImage.is_compressed(); VkFormat vkFmt = VK_FORMAT_R8G8B8A8_UNORM; switch (fmt){ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: vkFmt = VK_FORMAT_BC1_RGB_SRGB_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: vkFmt = VK_FORMAT_BC2_UNORM_BLOCK; break; case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: vkFmt = VK_FORMAT_BC3_UNORM_BLOCK; break; default: break; } m_width = imgW; m_height = imgH; m_format = vkFmt; VulkanDC::Device::Queue::Name queueName = "DEFAULT_GRAPHICS_QUEUE"; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID; VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VulkanDC::Device::Queue *queue = device->getQueue(queueName); VkCommandBuffer cmd = VK_NULL_HANDLE; queue->beginCommandBuffer(cmdID, &cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); imageCreateAndBind( &m_data.image, &m_data.memory, m_format, VK_IMAGE_TYPE_2D, m_width, m_height, 1, 6, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VkImageUsageFlagBits)(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), VK_IMAGE_TILING_OPTIMAL); VkBuffer cubeMapBuffer; VkDeviceMemory cubeMapMem; bufferCreate(&cubeMapBuffer, m_width*m_height * 3 * 6, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); bufferAlloc(&cubeMapBuffer, &cubeMapMem, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); if (m_memory_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT){ imageSetLayoutBarrier(cmdID, queueName, m_data.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL); for (uint32_t i = 0; i < 6; ++i){ void *data = NULL; VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = i; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(), cubeMapMem, layout.offset, layout.size, 0, &data), "Could not map memory for image.\n"); const nv_dds::CTexture &mipmap = ddsImage.get_cubemap_face(i); memcpy(data, (void *)mipmap, layout.size); vkUnmapMemory(getDefaultDevice(), cubeMapMem); } VkBufferImageCopy biCpyRgn[6]; for (uint32_t k = 0; k < 6; ++k){ VkSubresourceLayout layout; VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = m_mip_level; subres.arrayLayer = k; vkGetImageSubresourceLayout(getDefaultDevice(), m_data.image, &subres, &layout); biCpyRgn[k].bufferOffset = layout.offset; biCpyRgn[k].bufferImageHeight = 0; biCpyRgn[k].bufferRowLength = 0; biCpyRgn[k].imageExtent.width = m_width; biCpyRgn[k].imageExtent.height = m_height; biCpyRgn[k].imageExtent.depth = 1; biCpyRgn[k].imageOffset.x = 0; biCpyRgn[k].imageOffset.y = 0; biCpyRgn[k].imageOffset.z = 0; biCpyRgn[k].imageSubresource.baseArrayLayer = k; biCpyRgn[k].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; biCpyRgn[k].imageSubresource.layerCount = 1; biCpyRgn[k].imageSubresource.mipLevel = 0; } VkFence copyFence; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(fenceInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(device->getVKDevice(), &fenceInfo, NULL, ©Fence); vkCmdCopyBufferToImage(cmd, cubeMapBuffer, m_data.image, m_data.imageLayout, 6, biCpyRgn); queue->flushCommandBuffer(cmdID, ©Fence); vkWaitForFences(device->getVKDevice(), 1, ©Fence, VK_TRUE, 100000000000); vkDestroyBuffer(device->getVKDevice(), cubeMapBuffer, NULL); vkFreeMemory(device->getVKDevice(), cubeMapMem, NULL); } VkSamplerCreateInfo sampler; sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler.pNext = NULL; sampler.magFilter = VK_FILTER_NEAREST; sampler.minFilter = VK_FILTER_NEAREST; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 1; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VkImageViewCreateInfo view; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = m_format; view.components.r = VK_COMPONENT_SWIZZLE_R; view.components.g = VK_COMPONENT_SWIZZLE_G; view.components.b = VK_COMPONENT_SWIZZLE_B; view.components.a = VK_COMPONENT_SWIZZLE_A; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.levelCount = 1; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view.subresourceRange.layerCount = 1; VKA_CHECK_ERROR(vkCreateSampler(getDefaultDevice(), &sampler, NULL, &m_data.sampler), "Could not create sampler for image texture.\n"); view.image = m_data.image; VKA_CHECK_ERROR(vkCreateImageView(getDefaultDevice(), &view, NULL, &m_data.view), "Could not create image view for texture.\n"); }
void BaseImage::ValidateContent(RandomNumberGenerator& rand) { /* dstBuf has following layout: For each of texels to be sampled, [0..valueCount): struct { in uint32_t pixelX; in uint32_t pixelY; out uint32_t pixelColor; } */ const uint32_t valueCount = 128; VkBufferCreateInfo dstBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; dstBufCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; dstBufCreateInfo.size = valueCount * sizeof(uint32_t) * 3; VmaAllocationCreateInfo dstBufAllocCreateInfo = {}; dstBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; dstBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; VkBuffer dstBuf = nullptr; VmaAllocation dstBufAlloc = nullptr; VmaAllocationInfo dstBufAllocInfo = {}; TEST( vmaCreateBuffer(g_hAllocator, &dstBufCreateInfo, &dstBufAllocCreateInfo, &dstBuf, &dstBufAlloc, &dstBufAllocInfo) == VK_SUCCESS ); // Fill dstBuf input data. { uint32_t* dstBufContent = (uint32_t*)dstBufAllocInfo.pMappedData; for(uint32_t i = 0; i < valueCount; ++i) { const uint32_t x = rand.Generate() % m_CreateInfo.extent.width; const uint32_t y = rand.Generate() % m_CreateInfo.extent.height; dstBufContent[i * 3 ] = x; dstBufContent[i * 3 + 1] = y; dstBufContent[i * 3 + 2] = 0; } } VkSamplerCreateInfo samplerCreateInfo = { 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.unnormalizedCoordinates = VK_TRUE; VkSampler sampler = nullptr; TEST( vkCreateSampler( g_hDevice, &samplerCreateInfo, nullptr, &sampler) == VK_SUCCESS ); VkDescriptorSetLayoutBinding bindings[2] = {}; bindings[0].binding = 0; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].descriptorCount = 1; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[0].pImmutableSamplers = &sampler; bindings[1].binding = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[1].descriptorCount = 1; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; descSetLayoutCreateInfo.bindingCount = 2; descSetLayoutCreateInfo.pBindings = bindings; VkDescriptorSetLayout descSetLayout = nullptr; TEST( vkCreateDescriptorSetLayout(g_hDevice, &descSetLayoutCreateInfo, nullptr, &descSetLayout) == VK_SUCCESS ); VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipelineLayoutCreateInfo.setLayoutCount = 1; pipelineLayoutCreateInfo.pSetLayouts = &descSetLayout; VkPipelineLayout pipelineLayout = nullptr; TEST( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) == VK_SUCCESS ); std::vector<char> shaderCode; LoadShader(shaderCode, "SparseBindingTest.comp.spv"); VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; shaderModuleCreateInfo.codeSize = shaderCode.size(); shaderModuleCreateInfo.pCode = (const uint32_t*)shaderCode.data(); VkShaderModule shaderModule = nullptr; TEST( vkCreateShaderModule(g_hDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) == VK_SUCCESS ); VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; pipelineCreateInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipelineCreateInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; pipelineCreateInfo.stage.module = shaderModule; pipelineCreateInfo.stage.pName = "main"; pipelineCreateInfo.layout = pipelineLayout; VkPipeline pipeline = nullptr; TEST( vkCreateComputePipelines(g_hDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline) == VK_SUCCESS ); VkDescriptorPoolSize poolSizes[2] = {}; poolSizes[0].type = bindings[0].descriptorType; poolSizes[0].descriptorCount = bindings[0].descriptorCount; poolSizes[1].type = bindings[1].descriptorType; poolSizes[1].descriptorCount = bindings[1].descriptorCount; VkDescriptorPoolCreateInfo descPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; descPoolCreateInfo.maxSets = 1; descPoolCreateInfo.poolSizeCount = 2; descPoolCreateInfo.pPoolSizes = poolSizes; VkDescriptorPool descPool = nullptr; TEST( vkCreateDescriptorPool(g_hDevice, &descPoolCreateInfo, nullptr, &descPool) == VK_SUCCESS ); VkDescriptorSetAllocateInfo descSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; descSetAllocInfo.descriptorPool = descPool; descSetAllocInfo.descriptorSetCount = 1; descSetAllocInfo.pSetLayouts = &descSetLayout; VkDescriptorSet descSet = nullptr; TEST( vkAllocateDescriptorSets(g_hDevice, &descSetAllocInfo, &descSet) == VK_SUCCESS ); VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; imageViewCreateInfo.image = m_Image; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCreateInfo.format = m_CreateInfo.format; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.subresourceRange.levelCount = 1; VkImageView imageView = nullptr; TEST( vkCreateImageView(g_hDevice, &imageViewCreateInfo, nullptr, &imageView) == VK_SUCCESS ); VkDescriptorImageInfo descImageInfo = {}; descImageInfo.imageView = imageView; descImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkDescriptorBufferInfo descBufferInfo = {}; descBufferInfo.buffer = dstBuf; descBufferInfo.offset = 0; descBufferInfo.range = VK_WHOLE_SIZE; VkWriteDescriptorSet descWrites[2] = {}; descWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descWrites[0].dstSet = descSet; descWrites[0].dstBinding = bindings[0].binding; descWrites[0].dstArrayElement = 0; descWrites[0].descriptorCount = 1; descWrites[0].descriptorType = bindings[0].descriptorType; descWrites[0].pImageInfo = &descImageInfo; descWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descWrites[1].dstSet = descSet; descWrites[1].dstBinding = bindings[1].binding; descWrites[1].dstArrayElement = 0; descWrites[1].descriptorCount = 1; descWrites[1].descriptorType = bindings[1].descriptorType; descWrites[1].pBufferInfo = &descBufferInfo; vkUpdateDescriptorSets(g_hDevice, 2, descWrites, 0, nullptr); BeginSingleTimeCommands(); vkCmdBindPipeline(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr); vkCmdDispatch(g_hTemporaryCommandBuffer, valueCount, 1, 1); EndSingleTimeCommands(); // Validate dstBuf output data. { const uint32_t* dstBufContent = (const uint32_t*)dstBufAllocInfo.pMappedData; for(uint32_t i = 0; i < valueCount; ++i) { const uint32_t x = dstBufContent[i * 3 ]; const uint32_t y = dstBufContent[i * 3 + 1]; const uint32_t color = dstBufContent[i * 3 + 2]; const uint8_t a = (uint8_t)(color >> 24); const uint8_t b = (uint8_t)(color >> 16); const uint8_t g = (uint8_t)(color >> 8); const uint8_t r = (uint8_t)color; TEST(r == (uint8_t)x && g == (uint8_t)y && b == 13 && a == 25); } } vkDestroyImageView(g_hDevice, imageView, nullptr); vkDestroyDescriptorPool(g_hDevice, descPool, nullptr); vmaDestroyBuffer(g_hAllocator, dstBuf, dstBufAlloc); vkDestroyPipeline(g_hDevice, pipeline, nullptr); vkDestroyShaderModule(g_hDevice, shaderModule, nullptr); vkDestroyPipelineLayout(g_hDevice, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(g_hDevice, descSetLayout, nullptr); vkDestroySampler(g_hDevice, sampler, nullptr); }
void loadAssets() { models.skysphere.loadFromFile(getAssetPath() + "models/geosphere.obj", vertexLayout, 1.0f, vulkanDevice, queue); // Textures std::string texFormatSuffix; VkFormat texFormat; // Get supported compressed texture format if (vulkanDevice->features.textureCompressionBC) { texFormatSuffix = "_bc3_unorm"; texFormat = VK_FORMAT_BC3_UNORM_BLOCK; } else if (vulkanDevice->features.textureCompressionASTC_LDR) { texFormatSuffix = "_astc_8x8_unorm"; texFormat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK; } else if (vulkanDevice->features.textureCompressionETC2) { texFormatSuffix = "_etc2_unorm"; texFormat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; } else { vks::tools::exitFatal("Device does not support any compressed texture format!", VK_ERROR_FEATURE_NOT_PRESENT); } textures.skySphere.loadFromFile(getAssetPath() + "textures/skysphere" + texFormatSuffix + ".ktx", texFormat, vulkanDevice, queue); // Terrain textures are stored in a texture array with layers corresponding to terrain height textures.terrainArray.loadFromFile(getAssetPath() + "textures/terrain_texturearray" + texFormatSuffix + ".ktx", texFormat, vulkanDevice, queue); // Height data is stored in a one-channel texture textures.heightMap.loadFromFile(getAssetPath() + "textures/terrain_heightmap_r16.ktx", VK_FORMAT_R16_UNORM, vulkanDevice, queue); VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo(); // Setup a mirroring sampler for the height map vkDestroySampler(device, textures.heightMap.sampler, nullptr); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; samplerInfo.addressModeV = samplerInfo.addressModeU; samplerInfo.addressModeW = samplerInfo.addressModeU; samplerInfo.compareOp = VK_COMPARE_OP_NEVER; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = (float)textures.heightMap.mipLevels; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &textures.heightMap.sampler)); textures.heightMap.descriptor.sampler = textures.heightMap.sampler; // Setup a repeating sampler for the terrain texture layers vkDestroySampler(device, textures.terrainArray.sampler, nullptr); samplerInfo = vks::initializers::samplerCreateInfo(); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = samplerInfo.addressModeU; samplerInfo.addressModeW = samplerInfo.addressModeU; samplerInfo.compareOp = VK_COMPARE_OP_NEVER; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = (float)textures.terrainArray.mipLevels; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; if (deviceFeatures.samplerAnisotropy) { samplerInfo.maxAnisotropy = 4.0f; samplerInfo.anisotropyEnable = VK_TRUE; } VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &textures.terrainArray.sampler)); textures.terrainArray.descriptor.sampler = textures.terrainArray.sampler; }
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); } }
void loadCubemap(std::string filename, VkFormat format, bool forceLinearTiling) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture_cube texCube(gli::load((const char*)textureData, size)); #else gli::texture_cube texCube(gli::load(filename)); #endif assert(!texCube.empty()); cubeMap.width = texCube.extent().x; cubeMap.height = texCube.extent().y; cubeMap.mipLevels = texCube.levels(); VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); bufferCreateInfo.size = texCube.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, texCube.data(), texCube.size()); vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = cubeMap.mipLevels; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { cubeMap.width, cubeMap.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // Cube faces count as array layers in Vulkan imageCreateInfo.arrayLayers = 6; // This flag is required for cube map images imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &cubeMap.image)); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubeMap.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Setup buffer copy regions for each face including all of it's miplevels std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; for (uint32_t face = 0; face < 6; face++) { for (uint32_t level = 0; level < cubeMap.mipLevels; level++) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texCube[face][level].extent().x; bufferCopyRegion.imageExtent.height = texCube[face][level].extent().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += texCube[face][level].size(); } } // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = cubeMap.mipLevels; subresourceRange.layerCount = 6; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data() ); // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vks::tools::setImageLayout( copyCmd, cubeMap.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cubeMap.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = cubeMap.mipLevels; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sampler.maxAnisotropy = 1.0f; if (vulkanDevice->features.samplerAnisotropy) { sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = VK_TRUE; } VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &cubeMap.sampler)); // Create image view VkImageViewCreateInfo view = vks::initializers::imageViewCreateInfo(); // Cube map view type view.viewType = VK_IMAGE_VIEW_TYPE_CUBE; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; // 6 array layers (faces) view.subresourceRange.layerCount = 6; // Set number of mip levels view.subresourceRange.levelCount = cubeMap.mipLevels; view.image = cubeMap.image; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &cubeMap.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); }
void 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); } }
void loadTextureArray(std::string filename, VkFormat format) { #if defined(__ANDROID__) // Textures are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *textureData = malloc(size); AAsset_read(asset, textureData, size); AAsset_close(asset); gli::texture2DArray tex2DArray(gli::load((const char*)textureData, size)); #else gli::texture2DArray tex2DArray(gli::load(filename)); #endif assert(!tex2DArray.empty()); textureArray.width = tex2DArray.dimensions().x; textureArray.height = tex2DArray.dimensions().y; layerCount = tex2DArray.layers(); VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(); bufferCreateInfo.size = tex2DArray.size(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkTools::checkResult(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); vkTools::checkResult(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); vkTools::checkResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer uint8_t *data; vkTools::checkResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); memcpy(data, tex2DArray.data(), tex2DArray.size()); vkUnmapMemory(device, stagingMemory); // Setup buffer copy regions for array layers std::vector<VkBufferImageCopy> bufferCopyRegions; uint32_t offset = 0; // Check if all array layers have the same dimesions bool sameDims = true; for (uint32_t layer = 0; layer < layerCount; layer++) { if (tex2DArray[layer].dimensions().x != textureArray.width || tex2DArray[layer].dimensions().y != textureArray.height) { sameDims = false; break; } } // If all layers of the texture array have the same dimensions, we only need to do one copy if (sameDims) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = layerCount; bufferCopyRegion.imageExtent.width = tex2DArray[0].dimensions().x; bufferCopyRegion.imageExtent.height = tex2DArray[0].dimensions().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); } else { // If dimensions differ, copy layer by layer and pass offsets for (uint32_t layer = 0; layer < layerCount; layer++) { VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = layer; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2DArray[layer].dimensions().x; bufferCopyRegion.imageExtent.height = tex2DArray[layer].dimensions().y; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); offset += tex2DArray[layer].size(); } } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; imageCreateInfo.extent = { textureArray.width, textureArray.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.arrayLayers = layerCount; VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &textureArray.image)); vkGetImageMemoryRequirements(device, textureArray.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &textureArray.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, textureArray.image, textureArray.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = layerCount; vkTools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, textureArray.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, bufferCopyRegions.size(), bufferCopyRegions.data() ); // Change texture image layout to shader read after all faces have been copied textureArray.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkTools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, textureArray.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 8; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &textureArray.sampler)); // Create image view VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); view.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; view.subresourceRange.layerCount = layerCount; view.image = textureArray.image; VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &textureArray.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr); }
/** * Prepare all vulkan resources required to render the font * The text overlay uses separate resources for descriptors (pool, sets, layouts), pipelines and command buffers */ void prepareResources() { static unsigned char font24pixels[STB_FONT_HEIGHT][STB_FONT_WIDTH]; STB_FONT_NAME(stbFontData, font24pixels, STB_FONT_HEIGHT); // Command buffer // Pool VkCommandPoolCreateInfo cmdPoolInfo = {}; cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VK_CHECK_RESULT(vkCreateCommandPool(vulkanDevice->logicalDevice, &cmdPoolInfo, nullptr, &commandPool)); VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo( commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, (uint32_t)cmdBuffers.size()); VK_CHECK_RESULT(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data())); // Vertex buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &vertexBuffer, MAX_CHAR_COUNT * sizeof(glm::vec4))); // Map persistent vertexBuffer.map(); // Font texture VkImageCreateInfo imageInfo = vks::initializers::imageCreateInfo(); imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.format = VK_FORMAT_R8_UNORM; imageInfo.extent.width = STB_FONT_WIDTH; imageInfo.extent.height = STB_FONT_HEIGHT; imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageInfo, nullptr, &image)); VkMemoryRequirements memReqs; VkMemoryAllocateInfo allocInfo = vks::initializers::memoryAllocateInfo(); vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, image, &memReqs); allocInfo.allocationSize = memReqs.size; allocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &allocInfo, nullptr, &imageMemory)); VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, image, imageMemory, 0)); // Staging vks::Buffer stagingBuffer; VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer, allocInfo.allocationSize)); stagingBuffer.map(); memcpy(stagingBuffer.mapped, &font24pixels[0][0], STB_FONT_WIDTH * STB_FONT_HEIGHT); // Only one channel, so data size = W * H (*R8) stagingBuffer.unmap(); // Copy to image VkCommandBuffer copyCmd; cmdBufAllocateInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufAllocateInfo, ©Cmd)); VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo)); // Prepare for transfer vks::tools::setImageLayout( copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkBufferImageCopy bufferCopyRegion = {}; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = STB_FONT_WIDTH; bufferCopyRegion.imageExtent.height = STB_FONT_HEIGHT; bufferCopyRegion.imageExtent.depth = 1; vkCmdCopyBufferToImage( copyCmd, stagingBuffer.buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion ); // Prepare for shader read vks::tools::setImageLayout( copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd)); VkSubmitInfo submitInfo = vks::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = ©Cmd; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueWaitIdle(queue)); stagingBuffer.destroy(); vkFreeCommandBuffers(vulkanDevice->logicalDevice, commandPool, 1, ©Cmd); VkImageViewCreateInfo imageViewInfo = vks::initializers::imageViewCreateInfo(); imageViewInfo.image = image; imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewInfo.format = imageInfo.format; imageViewInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; imageViewInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &imageViewInfo, nullptr, &view)); // Sampler VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo(); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipLodBias = 0.0f; samplerInfo.compareOp = VK_COMPARE_OP_NEVER; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 1.0f; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; samplerInfo.maxAnisotropy = 1.0f; VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &samplerInfo, nullptr, &sampler)); // Descriptor // Font uses a separate descriptor pool std::array<VkDescriptorPoolSize, 1> poolSizes; poolSizes[0] = vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1); VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo( static_cast<uint32_t>(poolSizes.size()), poolSizes.data(), 1); VK_CHECK_RESULT(vkCreateDescriptorPool(vulkanDevice->logicalDevice, &descriptorPoolInfo, nullptr, &descriptorPool)); // Descriptor set layout std::array<VkDescriptorSetLayoutBinding, 1> setLayoutBindings; setLayoutBindings[0] = vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0); VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo( setLayoutBindings.data(), static_cast<uint32_t>(setLayoutBindings.size())); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(vulkanDevice->logicalDevice, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); // Pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = vks::initializers::pipelineLayoutCreateInfo( &descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(vulkanDevice->logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout)); // Descriptor set VkDescriptorSetAllocateInfo descriptorSetAllocInfo = vks::initializers::descriptorSetAllocateInfo( descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(vulkanDevice->logicalDevice, &descriptorSetAllocInfo, &descriptorSet)); VkDescriptorImageInfo texDescriptor = vks::initializers::descriptorImageInfo( sampler, view, VK_IMAGE_LAYOUT_GENERAL); std::array<VkWriteDescriptorSet, 1> writeDescriptorSets; writeDescriptorSets[0] = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texDescriptor); vkUpdateDescriptorSets(vulkanDevice->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); // Pipeline cache VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; VK_CHECK_RESULT(vkCreatePipelineCache(vulkanDevice->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); // Command buffer execution fence VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo(); VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &fence)); }
// Setup the offscreen framebuffer for rendering the blurred scene // The color attachment of this framebuffer will then be used to sample frame in the fragment shader of the final pass void prepareOffscreen() { offscreenPass.width = FB_DIM; offscreenPass.height = FB_DIM; // Find a suitable depth format VkFormat fbDepthFormat; VkBool32 validDepthFormat = vks::tools::getSupportedDepthFormat(physicalDevice, &fbDepthFormat); assert(validDepthFormat); // Color attachment VkImageCreateInfo image = vks::initializers::imageCreateInfo(); image.imageType = VK_IMAGE_TYPE_2D; image.format = FB_COLOR_FORMAT; image.extent.width = offscreenPass.width; image.extent.height = offscreenPass.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; // We will sample directly from the color attachment image.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.color.image)); vkGetImageMemoryRequirements(device, offscreenPass.color.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.color.mem)); VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.color.image, offscreenPass.color.mem, 0)); VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; colorImageView.format = FB_COLOR_FORMAT; colorImageView.subresourceRange = {}; colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colorImageView.subresourceRange.baseMipLevel = 0; colorImageView.subresourceRange.levelCount = 1; colorImageView.subresourceRange.baseArrayLayer = 0; colorImageView.subresourceRange.layerCount = 1; colorImageView.image = offscreenPass.color.image; VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreenPass.color.view)); // Create sampler to sample from the attachment in the fragment shader VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo(); samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo.addressModeV = samplerInfo.addressModeU; samplerInfo.addressModeW = samplerInfo.addressModeU; samplerInfo.mipLodBias = 0.0f; samplerInfo.maxAnisotropy = 1.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 1.0f; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &offscreenPass.sampler)); // Depth stencil attachment image.format = fbDepthFormat; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.depth.image)); vkGetImageMemoryRequirements(device, offscreenPass.depth.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.depth.mem)); VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.depth.image, offscreenPass.depth.mem, 0)); VkImageViewCreateInfo depthStencilView = vks::initializers::imageViewCreateInfo(); depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = fbDepthFormat; depthStencilView.flags = 0; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthStencilView.subresourceRange.baseMipLevel = 0; depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; depthStencilView.image = offscreenPass.depth.image; VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &offscreenPass.depth.view)); // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering std::array<VkAttachmentDescription, 2> attchmentDescriptions = {}; // Color attachment attchmentDescriptions[0].format = FB_COLOR_FORMAT; attchmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attchmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Depth attachment attchmentDescriptions[1].format = fbDepthFormat; attchmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkAttachmentReference depthReference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpassDescription = {}; subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorAttachmentCount = 1; subpassDescription.pColorAttachments = &colorReference; subpassDescription.pDepthStencilAttachment = &depthReference; // Use subpass dependencies for layout transitions std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Create the actual renderpass VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = static_cast<uint32_t>(attchmentDescriptions.size()); renderPassInfo.pAttachments = attchmentDescriptions.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpassDescription; renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size()); renderPassInfo.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreenPass.renderPass)); VkImageView attachments[2]; attachments[0] = offscreenPass.color.view; attachments[1] = offscreenPass.depth.view; VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo(); fbufCreateInfo.renderPass = offscreenPass.renderPass; fbufCreateInfo.attachmentCount = 2; fbufCreateInfo.pAttachments = attachments; fbufCreateInfo.width = offscreenPass.width; fbufCreateInfo.height = offscreenPass.height; fbufCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreenPass.frameBuffer)); // Fill a descriptor for later use in a descriptor set offscreenPass.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; offscreenPass.descriptor.imageView = offscreenPass.color.view; offscreenPass.descriptor.sampler = offscreenPass.sampler; }
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); }
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; }
// 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; }
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)); }
// 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(); }