12bool VulkanRenderer::CreateIBLResources() {
13 if (m_iblResourcesCreated)
return true;
16 auto createStubImage = [&](uint32_t layers, VkFormat fmt,
17 VkImage& img, VkDeviceMemory& mem, VkImageView& view) ->
bool {
18 VkImageCreateInfo info{};
19 info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
20 info.imageType = VK_IMAGE_TYPE_2D;
21 info.extent = {1, 1, 1};
23 info.arrayLayers = layers;
25 info.tiling = VK_IMAGE_TILING_OPTIMAL;
26 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
27 info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
28 | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
29 info.samples = VK_SAMPLE_COUNT_1_BIT;
30 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
31 if (layers == 6) info.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
33 if (vkCreateImage(device, &info,
nullptr, &img) != VK_SUCCESS)
return false;
35 VkMemoryRequirements memReqs;
36 vkGetImageMemoryRequirements(device, img, &memReqs);
37 VkMemoryAllocateInfo memInfo{};
38 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
39 memInfo.allocationSize = memReqs.size;
40 memInfo.memoryTypeIndex = FindMemoryType(memReqs.memoryTypeBits,
41 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
42 if (vkAllocateMemory(device, &memInfo,
nullptr, &mem) != VK_SUCCESS)
return false;
43 vkBindImageMemory(device, img, mem, 0);
45 VkImageViewCreateInfo viewInfo{};
46 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
48 viewInfo.viewType = (layers == 6) ? VK_IMAGE_VIEW_TYPE_CUBE
49 : VK_IMAGE_VIEW_TYPE_2D;
50 viewInfo.format = fmt;
51 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
52 viewInfo.subresourceRange.baseMipLevel = 0;
53 viewInfo.subresourceRange.levelCount = 1;
54 viewInfo.subresourceRange.baseArrayLayer = 0;
55 viewInfo.subresourceRange.layerCount = layers;
56 return vkCreateImageView(device, &viewInfo,
nullptr, &view) == VK_SUCCESS;
59 auto createSampler = [&](
bool mips, VkSampler& samp) ->
bool {
60 VkSamplerCreateInfo si{};
61 si.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
62 si.magFilter = VK_FILTER_LINEAR;
63 si.minFilter = VK_FILTER_LINEAR;
64 si.mipmapMode = mips ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
65 si.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
66 si.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
67 si.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
69 si.maxLod = mips ?
static_cast<float>(IBL_PREFILTER_MIP_LEVELS) : 1.0f;
70 return vkCreateSampler(device, &si,
nullptr, &samp) == VK_SUCCESS;
73 constexpr VkFormat hdrFmt = VK_FORMAT_R16G16B16A16_SFLOAT;
74 constexpr VkFormat lutFmt = VK_FORMAT_R16G16_SFLOAT;
76 if (!createStubImage(6, hdrFmt, m_iblIrradianceImage, m_iblIrradianceMemory, m_iblIrradianceView)) {
77 SLEAK_ERROR(
"IBL: Failed to create irradiance image!");
return false;
79 if (!createSampler(
false, m_iblIrradianceSampler)) {
80 SLEAK_ERROR(
"IBL: Failed to create irradiance sampler!");
return false;
83 if (!createStubImage(6, hdrFmt, m_iblPrefilterImage, m_iblPrefilterMemory, m_iblPrefilterView)) {
84 SLEAK_ERROR(
"IBL: Failed to create prefilter image!");
return false;
86 if (!createSampler(
true, m_iblPrefilterSampler)) {
87 SLEAK_ERROR(
"IBL: Failed to create prefilter sampler!");
return false;
90 if (!createStubImage(1, lutFmt, m_iblBrdfLutImage, m_iblBrdfLutMemory, m_iblBrdfLutView)) {
91 SLEAK_ERROR(
"IBL: Failed to create BRDF LUT image!");
return false;
93 if (!createSampler(
false, m_iblBrdfLutSampler)) {
94 SLEAK_ERROR(
"IBL: Failed to create BRDF LUT sampler!");
return false;
99 VkCommandBufferAllocateInfo cmdAlloc{};
100 cmdAlloc.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
101 cmdAlloc.commandPool = commands;
102 cmdAlloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
103 cmdAlloc.commandBufferCount = 1;
105 vkAllocateCommandBuffers(device, &cmdAlloc, &cmd);
107 VkCommandBufferBeginInfo begin{};
108 begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
109 begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
110 vkBeginCommandBuffer(cmd, &begin);
112 auto transitionImage = [&](VkImage img, uint32_t layers) {
113 VkImageMemoryBarrier bar{};
114 bar.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
115 bar.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
116 bar.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
117 bar.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
118 bar.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
120 bar.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers};
121 bar.srcAccessMask = 0;
122 bar.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
123 vkCmdPipelineBarrier(cmd,
124 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
125 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
126 0, 0,
nullptr, 0,
nullptr, 1, &bar);
128 transitionImage(m_iblIrradianceImage, 6);
129 transitionImage(m_iblPrefilterImage, 6);
130 transitionImage(m_iblBrdfLutImage, 1);
132 vkEndCommandBuffer(cmd);
134 VkSubmitInfo submit{};
135 submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
136 submit.commandBufferCount = 1;
137 submit.pCommandBuffers = &cmd;
139 VkFenceCreateInfo fenceInfo{};
140 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
142 vkCreateFence(device, &fenceInfo,
nullptr, &fence);
143 vkQueueSubmit(graphicsQueue, 1, &submit, fence);
144 vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
145 vkDestroyFence(device, fence,
nullptr);
146 vkFreeCommandBuffers(device, commands, 1, &cmd);
150 std::array<VkDescriptorSetLayoutBinding, 4> iblBindings{};
152 iblBindings[0] = {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr};
154 iblBindings[1] = {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr};
156 iblBindings[2] = {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr};
158 iblBindings[3] = {3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr};
160 VkDescriptorSetLayoutCreateInfo iblDSLInfo{};
161 iblDSLInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
162 iblDSLInfo.bindingCount =
static_cast<uint32_t
>(iblBindings.size());
163 iblDSLInfo.pBindings = iblBindings.data();
164 if (vkCreateDescriptorSetLayout(device, &iblDSLInfo,
nullptr, &m_iblDSL) != VK_SUCCESS) {
165 SLEAK_ERROR(
"IBL: Failed to create IBL DSL!");
return false;
169 std::array<VkDescriptorPoolSize, 2> iblPoolSizes{};
170 iblPoolSizes[0] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3 * MAX_FRAMES_IN_FLIGHT};
171 iblPoolSizes[1] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 * MAX_FRAMES_IN_FLIGHT};
172 VkDescriptorPoolCreateInfo iblPoolInfo{};
173 iblPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
174 iblPoolInfo.poolSizeCount =
static_cast<uint32_t
>(iblPoolSizes.size());
175 iblPoolInfo.pPoolSizes = iblPoolSizes.data();
176 iblPoolInfo.maxSets = MAX_FRAMES_IN_FLIGHT;
177 if (vkCreateDescriptorPool(device, &iblPoolInfo,
nullptr, &m_iblPool) != VK_SUCCESS) {
178 SLEAK_ERROR(
"IBL: Failed to create IBL pool!");
return false;
182 std::array<VkDescriptorSetLayout, MAX_FRAMES_IN_FLIGHT> iblLayouts;
183 iblLayouts.fill(m_iblDSL);
184 VkDescriptorSetAllocateInfo iblAlloc{};
185 iblAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
186 iblAlloc.descriptorPool = m_iblPool;
187 iblAlloc.descriptorSetCount = MAX_FRAMES_IN_FLIGHT;
188 iblAlloc.pSetLayouts = iblLayouts.data();
189 if (vkAllocateDescriptorSets(device, &iblAlloc, m_iblSets.data()) != VK_SUCCESS) {
190 SLEAK_ERROR(
"IBL: Failed to allocate IBL descriptor sets!");
return false;
194 constexpr VkDeviceSize settingsSize =
sizeof(IBLSettingsGPUData);
195 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
196 VkBufferCreateInfo bufInfo{};
197 bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
198 bufInfo.size = settingsSize;
199 bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
200 bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
201 if (vkCreateBuffer(device, &bufInfo,
nullptr, &m_iblSettingsBuffers[i]) != VK_SUCCESS) {
202 SLEAK_ERROR(
"IBL: Failed to create settings buffer {}!", i);
return false;
204 VkMemoryRequirements memReqs;
205 vkGetBufferMemoryRequirements(device, m_iblSettingsBuffers[i], &memReqs);
206 VkMemoryAllocateInfo memInfo{};
207 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
208 memInfo.allocationSize = memReqs.size;
209 memInfo.memoryTypeIndex = FindMemoryType(memReqs.memoryTypeBits,
210 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
211 if (vkAllocateMemory(device, &memInfo,
nullptr, &m_iblSettingsMemory[i]) != VK_SUCCESS) {
212 SLEAK_ERROR(
"IBL: Failed to allocate settings memory {}!", i);
return false;
214 vkBindBufferMemory(device, m_iblSettingsBuffers[i], m_iblSettingsMemory[i], 0);
215 vkMapMemory(device, m_iblSettingsMemory[i], 0, settingsSize, 0, &m_iblSettingsMapped[i]);
218 IBLSettingsGPUData settings{};
219 settings.IBLEnabled = 0;
220 settings.IBLIntensity = 1.0f;
221 settings.MaxReflectionLOD =
static_cast<float>(IBL_PREFILTER_MIP_LEVELS - 1);
222 memcpy(m_iblSettingsMapped[i], &settings,
sizeof(settings));
226 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
227 VkDescriptorImageInfo irradianceInfo{};
228 irradianceInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
229 irradianceInfo.imageView = m_iblIrradianceView;
230 irradianceInfo.sampler = m_iblIrradianceSampler;
232 VkDescriptorImageInfo prefilterInfo{};
233 prefilterInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
234 prefilterInfo.imageView = m_iblPrefilterView;
235 prefilterInfo.sampler = m_iblPrefilterSampler;
237 VkDescriptorImageInfo lutInfo{};
238 lutInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
239 lutInfo.imageView = m_iblBrdfLutView;
240 lutInfo.sampler = m_iblBrdfLutSampler;
242 VkDescriptorBufferInfo settingsBufInfo{};
243 settingsBufInfo.buffer = m_iblSettingsBuffers[i];
244 settingsBufInfo.offset = 0;
245 settingsBufInfo.range =
sizeof(IBLSettingsGPUData);
247 std::array<VkWriteDescriptorSet, 4> writes{};
248 writes[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
249 m_iblSets[i], 0, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
250 &irradianceInfo,
nullptr,
nullptr};
251 writes[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
252 m_iblSets[i], 1, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
253 &prefilterInfo,
nullptr,
nullptr};
254 writes[2] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
255 m_iblSets[i], 2, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
256 &lutInfo,
nullptr,
nullptr};
257 writes[3] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
258 m_iblSets[i], 3, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
259 nullptr, &settingsBufInfo,
nullptr};
260 vkUpdateDescriptorSets(device,
static_cast<uint32_t
>(writes.size()),
261 writes.data(), 0,
nullptr);
264 m_iblResourcesCreated =
true;
266 SLEAK_INFO(
"VulkanRenderer: IBL resources created (IBL disabled until skybox precompute)");
271void VulkanRenderer::CleanupIBLResources() {
272 if (!m_iblResourcesCreated)
return;
274 for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
275 if (m_iblSettingsMapped[i]) {
276 vkUnmapMemory(device, m_iblSettingsMemory[i]);
277 m_iblSettingsMapped[i] =
nullptr;
279 if (m_iblSettingsBuffers[i]) {
280 vkDestroyBuffer(device, m_iblSettingsBuffers[i],
nullptr);
281 m_iblSettingsBuffers[i] = VK_NULL_HANDLE;
283 if (m_iblSettingsMemory[i]) {
284 vkFreeMemory(device, m_iblSettingsMemory[i],
nullptr);
285 m_iblSettingsMemory[i] = VK_NULL_HANDLE;
289 if (m_iblPool) { vkDestroyDescriptorPool(device, m_iblPool,
nullptr); m_iblPool = VK_NULL_HANDLE; }
290 if (m_iblDSL) { vkDestroyDescriptorSetLayout(device, m_iblDSL,
nullptr); m_iblDSL = VK_NULL_HANDLE; }
292 if (m_iblBrdfLutSampler) { vkDestroySampler(device, m_iblBrdfLutSampler,
nullptr); m_iblBrdfLutSampler = VK_NULL_HANDLE; }
293 if (m_iblBrdfLutView) { vkDestroyImageView(device, m_iblBrdfLutView,
nullptr); m_iblBrdfLutView = VK_NULL_HANDLE; }
294 if (m_iblBrdfLutImage) { vkDestroyImage(device, m_iblBrdfLutImage,
nullptr); m_iblBrdfLutImage = VK_NULL_HANDLE; }
295 if (m_iblBrdfLutMemory) { vkFreeMemory(device, m_iblBrdfLutMemory,
nullptr); m_iblBrdfLutMemory = VK_NULL_HANDLE; }
297 if (m_iblPrefilterSampler) { vkDestroySampler(device, m_iblPrefilterSampler,
nullptr); m_iblPrefilterSampler = VK_NULL_HANDLE; }
298 if (m_iblPrefilterView) { vkDestroyImageView(device, m_iblPrefilterView,
nullptr); m_iblPrefilterView = VK_NULL_HANDLE; }
299 if (m_iblPrefilterImage) { vkDestroyImage(device, m_iblPrefilterImage,
nullptr); m_iblPrefilterImage = VK_NULL_HANDLE; }
300 if (m_iblPrefilterMemory) { vkFreeMemory(device, m_iblPrefilterMemory,
nullptr); m_iblPrefilterMemory = VK_NULL_HANDLE; }
302 if (m_iblIrradianceSampler) { vkDestroySampler(device, m_iblIrradianceSampler,
nullptr); m_iblIrradianceSampler = VK_NULL_HANDLE; }
303 if (m_iblIrradianceView) { vkDestroyImageView(device, m_iblIrradianceView,
nullptr); m_iblIrradianceView = VK_NULL_HANDLE; }
304 if (m_iblIrradianceImage) { vkDestroyImage(device, m_iblIrradianceImage,
nullptr); m_iblIrradianceImage = VK_NULL_HANDLE; }
305 if (m_iblIrradianceMemory) { vkFreeMemory(device, m_iblIrradianceMemory,
nullptr); m_iblIrradianceMemory = VK_NULL_HANDLE; }
307 m_iblResourcesCreated =
false;
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.