#include "ge_vulkan_environment_map.hpp" #include "ge_main.hpp" #include "ge_vulkan_array_texture.hpp" #include "ge_vulkan_command_loader.hpp" #include "ge_vulkan_driver.hpp" #include "ge_vulkan_shader_manager.hpp" #include "ge_vulkan_skybox_renderer.hpp" #include #include namespace GE { // ---------------------------------------------------------------------------- GEVulkanEnvironmentMap::GEVulkanEnvironmentMap(GEVulkanSkyBoxRenderer* skybox) : m_skybox(skybox) { m_skybox->m_env_cubemap_loading.store(true); } // GEVulkanEnvironmentMap // ---------------------------------------------------------------------------- GEVulkanEnvironmentMap::~GEVulkanEnvironmentMap() { m_skybox->m_env_cubemap_loading.store(false); } // ~GEVulkanEnvironmentMap // ---------------------------------------------------------------------------- void GEVulkanEnvironmentMap::load() { struct PushConstants { int m_size; int m_sample_count; int m_level; int m_total_mipmaps; }; PushConstants pc; VkPushConstantRange push_constant = {}; push_constant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constant.offset = 0; push_constant.size = sizeof(PushConstants); VkPipelineLayoutCreateInfo pipeline_layout_info = {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = 1; pipeline_layout_info.pushConstantRangeCount = 1; pipeline_layout_info.pPushConstantRanges = &push_constant; std::array bindings = {}; bindings[0].binding = 0; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[1].binding = 1; bindings[1].descriptorCount = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; VkDescriptorSetLayoutCreateInfo setinfo = {}; setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; setinfo.pBindings = bindings.data(); setinfo.bindingCount = bindings.size(); GEVulkanDriver* vk = getVKDriver(); VkDescriptorSetLayout layout = VK_NULL_HANDLE; VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; std::vector descriptor_sets; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkPipeline diffuse_pipeline = VK_NULL_HANDLE; VkPipeline specular_pipeline = VK_NULL_HANDLE; auto levels = [](GEVulkanArrayTexture* t) { std::vector l; l.push_back(t->getSize().Width); unsigned width = l.back(); while (true) { width = width < 2 ? 1 : width >> 1; l.push_back(width); if (width == 1) break; } return l; }; GEVulkanArrayTexture* texture_cubemap = m_skybox->m_texture_cubemap; GEVulkanArrayTexture* diffuse_env_cubemap = m_skybox->m_diffuse_env_cubemap; GEVulkanArrayTexture* specular_env_cubemap = m_skybox->m_specular_env_cubemap; std::vector diffuse_levels = levels(diffuse_env_cubemap); std::vector specular_levels = levels(specular_env_cubemap); unsigned total_levels = diffuse_levels.size() + specular_levels.size(); std::array pool_sizes = {{ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, total_levels }, { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, total_levels } }}; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.maxSets = total_levels; pool_info.poolSizeCount = pool_sizes.size(); pool_info.pPoolSizes = pool_sizes.data(); std::vector data_layouts; VkDescriptorSetAllocateInfo alloc_info = {}; VkComputePipelineCreateInfo compute_info = {}; compute_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; compute_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; compute_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; compute_info.stage.pName = "main"; VkCommandBuffer cmd = VK_NULL_HANDLE; std::vector image_infos; std::vector image_views; for (unsigned i = 0; i < diffuse_levels.size(); i++) { VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.image = diffuse_env_cubemap->getImage(); view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view_info.format = diffuse_env_cubemap->getInternalFormat(); view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = i; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 6; VkImageView view = VK_NULL_HANDLE; if (vkCreateImageView(vk->getDevice(), &view_info, NULL, &view) != VK_SUCCESS) { printf("vkCreateImageView failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } image_views.push_back(view); } for (unsigned i = 0; i < specular_levels.size(); i++) { VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.image = specular_env_cubemap->getImage(); view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view_info.format = specular_env_cubemap->getInternalFormat(); view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = i; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 6; VkImageView view = VK_NULL_HANDLE; if (vkCreateImageView(vk->getDevice(), &view_info, NULL, &view) != VK_SUCCESS) { printf("vkCreateImageView failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } image_views.push_back(view); } if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo, NULL, &layout) != VK_SUCCESS) { printf("vkCreateDescriptorSetLayout failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL, &descriptor_pool) != VK_SUCCESS) { printf("createDescriptorPool for GEVulkanEnvironmentMap::load"); goto destroy; } data_layouts.resize(total_levels, layout); descriptor_sets.resize(total_levels); alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = descriptor_pool; alloc_info.descriptorSetCount = data_layouts.size(); alloc_info.pSetLayouts = data_layouts.data(); if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info, descriptor_sets.data()) != VK_SUCCESS) { printf("vkAllocateDescriptorSets failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } for (VkImageView view : image_views) { VkDescriptorImageInfo info = {}; info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; info.sampler = vk->getSampler(GVS_SKYBOX); info.imageView = texture_cubemap->getImageView(true/*srgb*/)->load(); image_infos.push_back(info); info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; info.imageView = view; image_infos.push_back(info); } for (unsigned i = 0; i < total_levels; i++) { std::array write_descriptor_set = {}; write_descriptor_set[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_descriptor_set[0].dstSet = descriptor_sets[i]; write_descriptor_set[0].dstBinding = 0; write_descriptor_set[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_descriptor_set[0].descriptorCount = 1; write_descriptor_set[0].pImageInfo = &image_infos[i * 2]; write_descriptor_set[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_descriptor_set[1].dstSet = descriptor_sets[i]; write_descriptor_set[1].dstBinding = 1; write_descriptor_set[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; write_descriptor_set[1].descriptorCount = 1; write_descriptor_set[1].pImageInfo = &image_infos[i * 2 + 1]; vkUpdateDescriptorSets(vk->getDevice(), write_descriptor_set.size(), write_descriptor_set.data(), 0, NULL); } pipeline_layout_info.pSetLayouts = &layout; if (vkCreatePipelineLayout(vk->getDevice(), &pipeline_layout_info, NULL, &pipeline_layout) != VK_SUCCESS) { printf("vkCreatePipelineLayout failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } compute_info.stage.module = GEVulkanShaderManager::getShader("diffuse_irradiance.comp"); compute_info.layout = pipeline_layout; if (vkCreateComputePipelines(vk->getDevice(), VK_NULL_HANDLE, 1, &compute_info, NULL, &diffuse_pipeline) != VK_SUCCESS) { printf("vkCreateComputePipelines failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } compute_info.stage.module = GEVulkanShaderManager::getShader("specular_prefilter.comp"); if (vkCreateComputePipelines(vk->getDevice(), VK_NULL_HANDLE, 1, &compute_info, NULL, &specular_pipeline) != VK_SUCCESS) { printf("vkCreateComputePipelines failed for " "GEVulkanEnvironmentMap::load"); goto destroy; } cmd = GEVulkanCommandLoader::beginSingleTimeCommands(); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, diffuse_pipeline); for (unsigned i = 0; i < diffuse_levels.size(); i++) { PushConstants pc; pc.m_size = diffuse_levels[i]; pc.m_sample_count = getDiffuseEnvironmentMapSampleCount(); pc.m_level = i; pc.m_total_mipmaps = diffuse_levels.size(); vkCmdPushConstants(cmd, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PushConstants), &pc); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_sets[i], 0, NULL); // Calculate dispatch size (ceil(size/16)) uint32_t dispatch_size = (pc.m_size + 15) / 16; vkCmdDispatch(cmd, dispatch_size, dispatch_size, 6); } vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, specular_pipeline); for (unsigned i = 0; i < specular_levels.size(); i++) { const unsigned offset = diffuse_levels.size(); auto next_power_of_2 = [](unsigned v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }; PushConstants pc; pc.m_size = specular_levels[i]; // Calculate sample count: start with size / 2, minimum 16 pc.m_sample_count = std::max(16u, next_power_of_2(specular_levels[i] / 2)); pc.m_level = i; pc.m_total_mipmaps = specular_levels.size(); vkCmdPushConstants(cmd, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PushConstants), &pc); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_sets[i + offset], 0, NULL); uint32_t dispatch_size = (pc.m_size + 15) / 16; vkCmdDispatch(cmd, dispatch_size, dispatch_size, 6); } destroy: if (cmd != VK_NULL_HANDLE) GEVulkanCommandLoader::endSingleTimeCommands(cmd); if (specular_pipeline != VK_NULL_HANDLE) vkDestroyPipeline(vk->getDevice(), specular_pipeline, NULL); if (diffuse_pipeline != VK_NULL_HANDLE) vkDestroyPipeline(vk->getDevice(), diffuse_pipeline, NULL); if (pipeline_layout != VK_NULL_HANDLE) vkDestroyPipelineLayout(vk->getDevice(), pipeline_layout, NULL); if (descriptor_pool != VK_NULL_HANDLE) vkDestroyDescriptorPool(vk->getDevice(), descriptor_pool, NULL); if (layout != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(vk->getDevice(), layout, NULL); for (VkImageView view : image_views) vkDestroyImageView(vk->getDevice(), view, NULL); } // load }