37 vkDeviceWaitIdle(m_r_context.device);
39 if (m_textureSampler != VK_NULL_HANDLE) {
40 vkDestroySampler(m_r_context.device, m_textureSampler,
nullptr);
41 m_textureSampler = VK_NULL_HANDLE;
44 for (
size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
45 if (m_sceneBuffers[i] != VK_NULL_HANDLE) {
46 vmaDestroyBuffer(m_r_context.allocator, m_sceneBuffers[i], m_sceneAllocs[i]);
47 m_sceneBuffers[i] = VK_NULL_HANDLE;
49 if (m_lightBuffers[i] != VK_NULL_HANDLE) {
50 vmaDestroyBuffer(m_r_context.allocator, m_lightBuffers[i], m_lightAllocs[i]);
51 m_lightBuffers[i] = VK_NULL_HANDLE;
55 for (
auto const& [name, image] : m_textureImages) {
56 if (m_textureViews.count(name) && m_textureViews[name] != VK_NULL_HANDLE) {
57 vkDestroyImageView(m_r_context.device, m_textureViews[name],
nullptr);
60 if (image != VK_NULL_HANDLE && m_textureAllocations.count(name)) {
61 vmaDestroyImage(m_r_context.allocator, image, m_textureAllocations[name]);
65 m_textureImages.clear();
66 m_textureAllocations.clear();
67 m_textureViews.clear();
70 if (m_r_context.bindlessDescriptorPool != VK_NULL_HANDLE) {
71 vkDestroyDescriptorPool(m_r_context.device, m_r_context.bindlessDescriptorPool,
nullptr);
72 m_r_context.bindlessDescriptorPool = VK_NULL_HANDLE;
75 if (m_r_context.bindlessDescriptorSetLayout != VK_NULL_HANDLE) {
76 vkDestroyDescriptorSetLayout(m_r_context.device, m_r_context.bindlessDescriptorSetLayout,
nullptr);
77 m_r_context.bindlessDescriptorSetLayout = VK_NULL_HANDLE;
80 if (m_descriptorPool != VK_NULL_HANDLE) {
81 vkDestroyDescriptorPool(m_r_context.device, m_descriptorPool,
nullptr);
82 m_descriptorPool = VK_NULL_HANDLE;
85 if (m_r_context.uboDescriptorSetLayout != VK_NULL_HANDLE) {
86 vkDestroyDescriptorSetLayout(m_r_context.device, m_r_context.uboDescriptorSetLayout,
nullptr);
87 m_r_context.uboDescriptorSetLayout = VK_NULL_HANDLE;
90 if (m_r_context.textureDescriptorSetLayout != VK_NULL_HANDLE) {
91 vkDestroyDescriptorSetLayout(m_r_context.device, m_r_context.textureDescriptorSetLayout,
nullptr);
92 m_r_context.textureDescriptorSetLayout = VK_NULL_HANDLE;
95 if (m_r_context.screenDescriptorSetLayout != VK_NULL_HANDLE) {
96 vkDestroyDescriptorSetLayout(m_r_context.device, m_r_context.screenDescriptorSetLayout,
nullptr);
97 m_r_context.screenDescriptorSetLayout = VK_NULL_HANDLE;
100 if (m_r_context.particleDescriptorSetLayout != VK_NULL_HANDLE) {
101 vkDestroyDescriptorSetLayout(m_r_context.device, m_r_context.particleDescriptorSetLayout,
nullptr);
102 m_r_context.particleDescriptorSetLayout = VK_NULL_HANDLE;
104 if (m_particleDescriptorPool != VK_NULL_HANDLE) {
105 vkDestroyDescriptorPool(m_r_context.device, m_particleDescriptorPool,
nullptr);
106 m_particleDescriptorPool = VK_NULL_HANDLE;
108 for (
size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
109 if (m_particleSSBOs[i] != VK_NULL_HANDLE) {
110 vmaUnmapMemory(m_r_context.allocator, m_particleAllocs[i]);
111 vmaDestroyBuffer(m_r_context.allocator, m_particleSSBOs[i], m_particleAllocs[i]);
112 m_particleSSBOs[i] = VK_NULL_HANDLE;
116 if (m_r_context.colorLutImage != VK_NULL_HANDLE) {
117 vkDestroyImageView(m_r_context.device, m_r_context.colorLutView,
nullptr);
118 vmaDestroyImage(m_r_context.allocator, m_r_context.colorLutImage, m_r_context.colorLutAllocation);
119 m_r_context.colorLutImage = VK_NULL_HANDLE;
120 m_r_context.colorLutView = VK_NULL_HANDLE;
121 m_r_context.colorLutAllocation = VK_NULL_HANDLE;
126 const uint32_t LUT_SIZE = 32;
127 VkDeviceSize imageSize = LUT_SIZE * LUT_SIZE * LUT_SIZE * 4;
128 std::vector<uint8_t> lutData(imageSize);
130 const float SATURATION = 1.1f;
131 const float BRIGHTNESS_BOOST = 1.4f;
132 const float textureBpc = 32.0f;
134 auto luma = [](
const glm::vec3& c) {
135 return glm::dot(c, glm::vec3(0.299f, 0.587f, 0.114f));
138 for (uint32_t z = 0; z < LUT_SIZE; z++) {
139 for (uint32_t y = 0; y < LUT_SIZE; y++) {
140 for (uint32_t x = 0; x < LUT_SIZE; x++) {
142 (
float)x / (LUT_SIZE - 1),
143 (
float)y / (LUT_SIZE - 1),
144 (
float)z / (LUT_SIZE - 1)
147 color = glm::round(color * textureBpc) / textureBpc;
149 float gray = luma(color);
150 color = glm::mix(glm::vec3(gray), color, SATURATION);
151 color *= BRIGHTNESS_BOOST;
153 color = glm::clamp(color, 0.0f, 1.0f);
155 uint32_t index = (z * LUT_SIZE * LUT_SIZE + y * LUT_SIZE + x) * 4;
156 lutData[index + 0] =
static_cast<uint8_t
>(color.r * 255.0f);
157 lutData[index + 1] =
static_cast<uint8_t
>(color.g * 255.0f);
158 lutData[index + 2] =
static_cast<uint8_t
>(color.b * 255.0f);
159 lutData[index + 3] = 255;
164 VkBuffer stagingBuffer;
165 VmaAllocation stagingAlloc;
166 VkBufferCreateInfo bufferInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
167 bufferInfo.size = imageSize;
168 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
170 VmaAllocationCreateInfo allocInfo{};
171 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
172 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAlloc,
nullptr);
175 vmaMapMemory(m_r_context.allocator, stagingAlloc, &data);
176 memcpy(data, lutData.data(),
static_cast<size_t>(imageSize));
177 vmaUnmapMemory(m_r_context.allocator, stagingAlloc);
179 VkImageCreateInfo imageInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
180 imageInfo.imageType = VK_IMAGE_TYPE_3D;
181 imageInfo.extent = { LUT_SIZE, LUT_SIZE, LUT_SIZE };
182 imageInfo.mipLevels = 1;
183 imageInfo.arrayLayers = 1;
184 imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
185 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
186 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
187 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
188 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
190 VmaAllocationCreateInfo imgAllocInfo{};
191 imgAllocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
192 vmaCreateImage(m_r_context.allocator, &imageInfo, &imgAllocInfo, &m_r_context.colorLutImage, &m_r_context.colorLutAllocation,
nullptr);
194 VkCommandPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
195 poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
196 poolInfo.queueFamilyIndex = m_r_context.graphicsQueueFamily;
197 VkCommandPool tempPool;
198 vkCreateCommandPool(m_r_context.device, &poolInfo,
nullptr, &tempPool);
200 VkCommandBufferAllocateInfo cmdAllocInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
201 cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
202 cmdAllocInfo.commandPool = tempPool;
203 cmdAllocInfo.commandBufferCount = 1;
205 vkAllocateCommandBuffers(m_r_context.device, &cmdAllocInfo, &cmd);
207 VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
208 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
209 vkBeginCommandBuffer(cmd, &beginInfo);
211 VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
212 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
213 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
214 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
215 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
216 barrier.image = m_r_context.colorLutImage;
217 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
218 barrier.subresourceRange.baseMipLevel = 0;
219 barrier.subresourceRange.levelCount = 1;
220 barrier.subresourceRange.baseArrayLayer = 0;
221 barrier.subresourceRange.layerCount = 1;
222 barrier.srcAccessMask = 0;
223 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
225 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &barrier);
227 VkBufferImageCopy region{};
228 region.bufferOffset = 0;
229 region.bufferRowLength = 0;
230 region.bufferImageHeight = 0;
231 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
232 region.imageSubresource.mipLevel = 0;
233 region.imageSubresource.baseArrayLayer = 0;
234 region.imageSubresource.layerCount = 1;
235 region.imageOffset = {0, 0, 0};
236 region.imageExtent = {LUT_SIZE, LUT_SIZE, LUT_SIZE};
238 vkCmdCopyBufferToImage(cmd, stagingBuffer, m_r_context.colorLutImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
240 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
241 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
242 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
243 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
245 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &barrier);
247 vkEndCommandBuffer(cmd);
249 VkSubmitInfo submitInfo{VK_STRUCTURE_TYPE_SUBMIT_INFO};
250 submitInfo.commandBufferCount = 1;
251 submitInfo.pCommandBuffers = &cmd;
252 vkQueueSubmit(m_r_context.graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
253 vkQueueWaitIdle(m_r_context.graphicsQueue);
255 vkDestroyCommandPool(m_r_context.device, tempPool,
nullptr);
256 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
258 VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
259 viewInfo.image = m_r_context.colorLutImage;
260 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
261 viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
262 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
263 viewInfo.subresourceRange.baseMipLevel = 0;
264 viewInfo.subresourceRange.levelCount = 1;
265 viewInfo.subresourceRange.baseArrayLayer = 0;
266 viewInfo.subresourceRange.layerCount = 1;
268 vkCreateImageView(m_r_context.device, &viewInfo,
nullptr, &m_r_context.colorLutView);
272 if (m_r_context.textureIndices.contains(name)) {
273 log(
"Texture '%s' already exists at index %u", name.c_str(), m_r_context.textureIndices[name]);
277 VkBuffer stagingBuffer;
278 VmaAllocation stagingAlloc;
280 VkBufferCreateInfo stagingBufferInfo{};
281 stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
282 stagingBufferInfo.size =
rgba.size();
283 stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
285 VmaAllocationCreateInfo stagingAllocInfo{};
286 stagingAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
288 vmaCreateBuffer(m_r_context.allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAlloc,
nullptr);
291 vmaMapMemory(m_r_context.allocator, stagingAlloc, &data);
292 memcpy(data,
rgba.data(),
rgba.size());
293 vmaUnmapMemory(m_r_context.allocator, stagingAlloc);
295 VkImage textureImage;
296 VmaAllocation textureAlloc;
298 VkImageCreateInfo imageInfo{};
299 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
300 imageInfo.imageType = VK_IMAGE_TYPE_2D;
301 imageInfo.extent.width = w;
302 imageInfo.extent.height = h;
303 imageInfo.extent.depth = 1;
304 imageInfo.mipLevels = 1;
305 imageInfo.arrayLayers = 1;
306 imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
307 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
308 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
309 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
310 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
311 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
313 VmaAllocationCreateInfo allocInfo{};
314 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
316 vmaCreateImage(m_r_context.allocator, &imageInfo, &allocInfo, &textureImage, &textureAlloc,
nullptr);
318 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
320 VkImageMemoryBarrier barrier{};
321 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
322 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
323 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
324 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
325 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
326 barrier.image = textureImage;
327 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
328 barrier.subresourceRange.baseMipLevel = 0;
329 barrier.subresourceRange.levelCount = 1;
330 barrier.subresourceRange.baseArrayLayer = 0;
331 barrier.subresourceRange.layerCount = 1;
332 barrier.srcAccessMask = 0;
333 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
335 vkCmdPipelineBarrier(
337 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
338 VK_PIPELINE_STAGE_TRANSFER_BIT,
345 VkBufferImageCopy region{};
346 region.bufferOffset = 0;
347 region.bufferRowLength = 0;
348 region.bufferImageHeight = 0;
349 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
350 region.imageSubresource.mipLevel = 0;
351 region.imageSubresource.baseArrayLayer = 0;
352 region.imageSubresource.layerCount = 1;
353 region.imageOffset = {0, 0, 0};
354 region.imageExtent = {(uint32_t)w, (uint32_t)h, 1};
356 vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
358 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
359 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
360 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
361 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
363 vkCmdPipelineBarrier(
365 VK_PIPELINE_STAGE_TRANSFER_BIT,
366 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
373 m_r_context.endSingleTimeCommands(commandBuffer);
375 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
377 VkImageViewCreateInfo viewInfo{};
378 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
379 viewInfo.image = textureImage;
380 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
381 viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
382 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
383 viewInfo.subresourceRange.levelCount = 1;
384 viewInfo.subresourceRange.layerCount = 1;
386 VkImageView textureView;
387 log(
"Creating vulkan texture view...");
388 if (vkCreateImageView(m_r_context.device, &viewInfo,
nullptr, &textureView) != VK_SUCCESS) {
389 throw_error(
"Failed to create texture image view!");
392 m_r_context.textureIndices[name] = m_r_context.nextTextureIndex++;
393 log(
"Assigned texture '%s' to index %u", name.c_str(), m_r_context.textureIndices[name]);
395 m_textures[name] = textureView;
396 m_textureImages[name] = textureImage;
397 m_textureAllocations[name] = textureAlloc;
398 m_textureViews[name] = textureView;
400 VkDescriptorImageInfo imageDescInfo{};
401 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
402 imageDescInfo.sampler = m_textureSampler;
403 imageDescInfo.imageView = textureView;
405 if (m_r_context.supportsBindlessTextures) {
406 VkWriteDescriptorSet write{};
407 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
408 write.dstSet = m_r_context.bindlessDescriptorSet;
409 write.dstBinding = 0;
410 write.dstArrayElement = m_r_context.textureIndices[name];
411 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
412 write.descriptorCount = 1;
413 write.pImageInfo = &imageDescInfo;
415 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
417 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
418 VkWriteDescriptorSet write{};
419 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
421 write.dstBinding = 0;
422 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
423 write.descriptorCount = 1;
424 write.pImageInfo = &imageDescInfo;
426 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
457 VkDescriptorSetLayoutBinding ssboBinding{};
458 ssboBinding.binding = 0;
459 ssboBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
460 ssboBinding.descriptorCount = 1;
461 ssboBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
463 VkDescriptorSetLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
464 layoutInfo.bindingCount = 1;
465 layoutInfo.pBindings = &ssboBinding;
467 if (vkCreateDescriptorSetLayout(m_r_context.device, &layoutInfo,
nullptr, &m_r_context.particleDescriptorSetLayout) != VK_SUCCESS) {
468 throw_error(
"Failed to create particle SSBO layout");
471 m_particleSSBOs.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
472 m_particleAllocs.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
473 m_particleMappedData.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
474 m_particleDescriptorSets.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
476 VkBufferCreateInfo bufferInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
478 bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
480 VmaAllocationCreateInfo allocInfo{};
481 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
482 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
484 for (
size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
485 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &m_particleSSBOs[i], &m_particleAllocs[i],
nullptr);
486 vmaMapMemory(m_r_context.allocator, m_particleAllocs[i], &m_particleMappedData[i]);
489 VkDescriptorPoolSize poolSize{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_r_context.MAX_FRAMES_IN_FLIGHT};
490 VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
491 poolInfo.poolSizeCount = 1;
492 poolInfo.pPoolSizes = &poolSize;
493 poolInfo.maxSets = m_r_context.MAX_FRAMES_IN_FLIGHT;
495 if (vkCreateDescriptorPool(m_r_context.device, &poolInfo,
nullptr, &m_particleDescriptorPool) != VK_SUCCESS) {
496 throw_error(
"Failed to create particle descriptor pool");
499 std::vector<VkDescriptorSetLayout> layouts(m_r_context.MAX_FRAMES_IN_FLIGHT, m_r_context.particleDescriptorSetLayout);
500 VkDescriptorSetAllocateInfo allocSetInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
501 allocSetInfo.descriptorPool = m_particleDescriptorPool;
502 allocSetInfo.descriptorSetCount = m_r_context.MAX_FRAMES_IN_FLIGHT;
503 allocSetInfo.pSetLayouts = layouts.data();
505 if (vkAllocateDescriptorSets(m_r_context.device, &allocSetInfo, m_particleDescriptorSets.data()) != VK_SUCCESS) {
506 throw_error(
"Failed to allocate particle descriptor sets");
509 for (
size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
510 VkDescriptorBufferInfo bInfo{};
511 bInfo.buffer = m_particleSSBOs[i];
513 bInfo.range = VK_WHOLE_SIZE;
515 VkWriteDescriptorSet writeDesc{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
516 writeDesc.dstSet = m_particleDescriptorSets[i];
517 writeDesc.dstBinding = 0;
518 writeDesc.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
519 writeDesc.descriptorCount = 1;
520 writeDesc.pBufferInfo = &bInfo;
521 vkUpdateDescriptorSets(m_r_context.device, 1, &writeDesc, 0,
nullptr);
526 log(
"Setting up VkDescriptorSetLayoutBinding...");
527 std::array<VkDescriptorSetLayoutBinding, 2> uboBindings{};
528 uboBindings[0].binding = 0;
529 uboBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
530 uboBindings[0].descriptorCount = 1;
531 uboBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
533 uboBindings[1].binding = 1;
534 uboBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
535 uboBindings[1].descriptorCount = 1;
536 uboBindings[1].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
538 VkDescriptorSetLayoutCreateInfo uboLayoutInfo{};
539 uboLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
540 uboLayoutInfo.bindingCount =
static_cast<uint32_t
>(uboBindings.size());
541 uboLayoutInfo.pBindings = uboBindings.data();
543 log(
"Creating Uniform buffer bindings...");
544 if (vkCreateDescriptorSetLayout(m_r_context.device, &uboLayoutInfo,
nullptr, &m_r_context.uboDescriptorSetLayout) != VK_SUCCESS) {
548 VkDescriptorSetLayoutBinding texBinding{};
549 texBinding.binding = 0;
550 texBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
551 texBinding.descriptorCount = 1;
552 texBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
554 VkDescriptorSetLayoutCreateInfo texLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
555 texLayoutInfo.bindingCount = 1;
556 texLayoutInfo.pBindings = &texBinding;
558 if (vkCreateDescriptorSetLayout(m_r_context.device, &texLayoutInfo,
nullptr, &m_r_context.textureDescriptorSetLayout) != VK_SUCCESS) {
559 throw_error(
"Failed to create legacy texture layout");
562 if (m_r_context.supportsBindlessTextures) {
563 log(
"Initializing Texture System: BINDLESS (Global Array)");
565 VkDescriptorBindingFlags bindlessFlags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
567 VkDescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO};
568 bindingFlagsInfo.bindingCount = 1;
569 bindingFlagsInfo.pBindingFlags = &bindlessFlags;
571 VkDescriptorSetLayoutBinding bindlessBinding{};
572 bindlessBinding.binding = 0;
573 bindlessBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
575 bindlessBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
576 bindlessBinding.pImmutableSamplers =
nullptr;
578 VkDescriptorSetLayoutCreateInfo bindlessLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
579 bindlessLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;
580 bindlessLayoutInfo.bindingCount = 1;
581 bindlessLayoutInfo.pBindings = &bindlessBinding;
582 bindlessLayoutInfo.pNext = &bindingFlagsInfo;
584 if (vkCreateDescriptorSetLayout(m_r_context.device, &bindlessLayoutInfo,
nullptr, &m_r_context.bindlessDescriptorSetLayout) != VK_SUCCESS) {
588 VkDescriptorPoolSize poolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
MAX_TEXTURES };
589 VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
590 poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;
591 poolInfo.maxSets = 1;
592 poolInfo.poolSizeCount = 1;
593 poolInfo.pPoolSizes = &poolSize;
595 vkCreateDescriptorPool(m_r_context.device, &poolInfo,
nullptr, &m_r_context.bindlessDescriptorPool);
597 VkDescriptorSetAllocateInfo allocInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
598 allocInfo.descriptorPool = m_r_context.bindlessDescriptorPool;
599 allocInfo.descriptorSetCount = 1;
600 allocInfo.pSetLayouts = &m_r_context.bindlessDescriptorSetLayout;
602 vkAllocateDescriptorSets(m_r_context.device, &allocInfo, &m_r_context.bindlessDescriptorSet);
604 log(
"Initializing Texture System: LEGACY (One Set Per Texture)");
606 VkDescriptorSetLayoutBinding texBinding{};
607 texBinding.binding = 0;
608 texBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
609 texBinding.descriptorCount = 1;
610 texBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
612 VkDescriptorSetLayoutCreateInfo texLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
613 texLayoutInfo.bindingCount = 1;
614 texLayoutInfo.pBindings = &texBinding;
616 if (vkCreateDescriptorSetLayout(m_r_context.device, &texLayoutInfo,
nullptr, &m_r_context.textureDescriptorSetLayout) != VK_SUCCESS) {
617 throw_error(
"Failed to create legacy texture layout");
621 std::array<VkDescriptorSetLayoutBinding, 5> screenBindings{};
623 for(
int i = 0; i < 5; i++) {
624 screenBindings[i].binding = i;
625 screenBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
626 screenBindings[i].descriptorCount = 1;
627 screenBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
630 VkDescriptorSetLayoutCreateInfo screenLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
631 screenLayoutInfo.bindingCount = 5;
632 screenLayoutInfo.pBindings = screenBindings.data();
633 if(vkCreateDescriptorSetLayout(m_r_context.device, &screenLayoutInfo,
nullptr, &m_r_context.screenDescriptorSetLayout) != VK_SUCCESS) {
634 throw_error(
"Failed to create screen descriptor set layout");
637 std::array<VkDescriptorPoolSize, 3> poolSizes{};
639 poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
640 poolSizes[0].descriptorCount = m_r_context.MAX_FRAMES_IN_FLIGHT;
642 poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
643 poolSizes[1].descriptorCount = m_r_context.MAX_FRAMES_IN_FLIGHT * MAX_MODELS;
645 poolSizes[2].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
646 poolSizes[2].descriptorCount = m_r_context.supportsBindlessTextures ? 1 : (m_r_context.MAX_FRAMES_IN_FLIGHT *
MAX_TEXTURES);
648 VkDescriptorPoolCreateInfo poolInfo{};
649 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
650 poolInfo.poolSizeCount =
static_cast<uint32_t
>(poolSizes.size());
651 poolInfo.pPoolSizes = poolSizes.data();
652 poolInfo.maxSets = m_r_context.MAX_FRAMES_IN_FLIGHT * (3 + (m_r_context.supportsBindlessTextures ? 0 :
MAX_TEXTURES));
654 log(
"Creating descriptor pool with %d max sets", poolInfo.maxSets);
655 if (vkCreateDescriptorPool(m_r_context.device, &poolInfo,
nullptr, &m_descriptorPool) != VK_SUCCESS) {
656 throw_error(
"Failed to create general descriptor pool");
659 std::vector<VkDescriptorSetLayout> uboLayouts(m_r_context.MAX_FRAMES_IN_FLIGHT, m_r_context.uboDescriptorSetLayout);
660 VkDescriptorSetAllocateInfo uboAllocInfo{};
661 uboAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
662 uboAllocInfo.descriptorPool = m_descriptorPool;
663 uboAllocInfo.descriptorSetCount = m_r_context.MAX_FRAMES_IN_FLIGHT;
664 uboAllocInfo.pSetLayouts = uboLayouts.data();
666 log(
"Allocating %d UBO descriptor sets", m_r_context.MAX_FRAMES_IN_FLIGHT);
667 m_descriptorSets.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
668 if (vkAllocateDescriptorSets(m_r_context.device, &uboAllocInfo, m_descriptorSets.data()) != VK_SUCCESS) {
669 throw_error(
"Failed to allocate UBO descriptor sets");
672 for (
size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
673 std::array<VkWriteDescriptorSet, 2> uboWrites{};
675 VkDescriptorBufferInfo sceneBufferInfo{};
676 sceneBufferInfo.buffer = m_sceneBuffers[i];
677 sceneBufferInfo.range =
sizeof(
SceneUBO);
679 uboWrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
680 uboWrites[0].dstSet = m_descriptorSets[i];
681 uboWrites[0].dstBinding = 0;
682 uboWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
683 uboWrites[0].descriptorCount = 1;
684 uboWrites[0].pBufferInfo = &sceneBufferInfo;
686 VkDescriptorBufferInfo lightInfo{};
687 lightInfo.buffer = m_lightBuffers[i];
690 uboWrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
691 uboWrites[1].dstSet = m_descriptorSets[i];
692 uboWrites[1].dstBinding = 1;
693 uboWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
694 uboWrites[1].descriptorCount = 1;
695 uboWrites[1].pBufferInfo = &lightInfo;
697 vkUpdateDescriptorSets(m_r_context.device, uboWrites.size(), uboWrites.data(), 0,
nullptr);
700 if (!m_r_context.supportsBindlessTextures) {
711 if (frameIndex >= m_r_context.MAX_FRAMES_IN_FLIGHT) {
713 "updateTextureDescriptor: Frame index %u exceeds MAX_FRAMES_IN_FLIGHT (%u)",
714 frameIndex, m_r_context.MAX_FRAMES_IN_FLIGHT);
720 "updateTextureDescriptor: Texture index %u exceeds MAX_TEXTURES (%u)",
725 VkDescriptorImageInfo imageInfo{};
726 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
727 imageInfo.imageView = textureView;
728 imageInfo.sampler = m_textureSampler;
730 if (m_r_context.supportsBindlessTextures) {
731 VkWriteDescriptorSet bindlessWrite{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
732 bindlessWrite.dstSet = m_r_context.bindlessDescriptorSet;
733 bindlessWrite.dstBinding = 0;
734 bindlessWrite.dstArrayElement = textureIndex;
735 bindlessWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
736 bindlessWrite.descriptorCount = 1;
737 bindlessWrite.pImageInfo = &imageInfo;
739 vkUpdateDescriptorSets(m_r_context.device, 1, &bindlessWrite, 0,
nullptr);
741 VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
743 write.dstBinding = 0;
744 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
745 write.descriptorCount = 1;
746 write.pImageInfo = &imageInfo;
748 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
803 const unsigned char pixels[] = {255, 255, 255, 255};
805 VkImageCreateInfo imageInfo{};
806 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
807 imageInfo.imageType = VK_IMAGE_TYPE_2D;
808 imageInfo.extent = {1, 1, 1};
809 imageInfo.mipLevels = 1;
810 imageInfo.arrayLayers = 1;
811 imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
812 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
813 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
814 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
815 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
817 VmaAllocationCreateInfo allocInfo{};
818 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
821 VmaAllocation allocation;
822 log(
"Creating default texture image...");
823 if (vmaCreateImage(m_r_context.allocator, &imageInfo, &allocInfo, &image, &allocation,
nullptr) != VK_SUCCESS) {
824 throw_error(
"Failed to create default texture image");
827 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
829 VkImageMemoryBarrier barrier{};
830 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
831 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
832 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
833 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
834 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
835 barrier.image = image;
836 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
837 barrier.subresourceRange.baseMipLevel = 0;
838 barrier.subresourceRange.levelCount = 1;
839 barrier.subresourceRange.baseArrayLayer = 0;
840 barrier.subresourceRange.layerCount = 1;
841 barrier.srcAccessMask = 0;
842 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
844 vkCmdPipelineBarrier(
846 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
847 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
854 m_r_context.endSingleTimeCommands(commandBuffer);
856 VkImageViewCreateInfo viewInfo{};
857 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
858 viewInfo.image = image;
859 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
860 viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
861 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
862 viewInfo.subresourceRange.levelCount = 1;
863 viewInfo.subresourceRange.layerCount = 1;
865 VkImageView imageView;
866 log(
"Creating default texture image view...");
867 if (vkCreateImageView(m_r_context.device, &viewInfo,
nullptr, &imageView) != VK_SUCCESS) {
868 vmaDestroyImage(m_r_context.allocator, image, allocation);
869 throw_error(
"Failed to create default texture image view");
872 m_textures[
"default"] = imageView;
873 m_textureImages[
"default"] = image;
874 m_textureAllocations[
"default"] = allocation;
875 m_textureViews[
"default"] = imageView;
877 m_r_context.textureIndices[
"default"] = 0;
878 m_r_context.nextTextureIndex = 1;
879 log(
"Created default texture with index 0");
883 log(
"Creating %d texture descriptor sets", m_r_context.MAX_FRAMES_IN_FLIGHT *
MAX_TEXTURES);
885 m_textureDescriptorSets.resize(m_r_context.MAX_FRAMES_IN_FLIGHT *
MAX_TEXTURES);
887 std::vector<VkDescriptorSetLayout> layouts(
889 m_r_context.textureDescriptorSetLayout
892 VkDescriptorSetAllocateInfo allocInfo{};
893 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
894 allocInfo.descriptorPool = m_descriptorPool;
895 allocInfo.descriptorSetCount =
static_cast<uint32_t
>(layouts.size());
896 allocInfo.pSetLayouts = layouts.data();
898 VkResult result = vkAllocateDescriptorSets(m_r_context.device, &allocInfo, m_textureDescriptorSets.data());
899 if (result != VK_SUCCESS) {
900 throw_error(
"Failed to allocate texture descriptor sets: " + std::to_string(result));
903 VkDescriptorImageInfo defaultImageInfo{};
904 defaultImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
905 defaultImageInfo.sampler = m_textureSampler;
908 std::vector<VkWriteDescriptorSet> writes;
909 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
910 for (uint32_t texIdx = 0; texIdx <
MAX_TEXTURES; ++texIdx) {
911 VkWriteDescriptorSet write{};
912 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
914 write.dstBinding = 0;
915 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
916 write.descriptorCount = 1;
917 write.pImageInfo = &defaultImageInfo;
918 writes.push_back(write);
921 vkUpdateDescriptorSets(m_r_context.device, writes.size(), writes.data(), 0,
nullptr);
922 log(
"Initialized %d texture descriptor sets with default texture", writes.size());
972 std::string fullPath = path;
974 if (m_r_context.textureIndices.contains(name)) {
975 log(
"Texture '%s' already exists at index %u", name.c_str(), m_r_context.textureIndices[name]);
979 if (m_r_context.textureIndices.size() >=
MAX_TEXTURES) {
980 log(LogLevel::ERROR,
"Maximum texture count (%u) reached!",
MAX_TEXTURES);
984 stbi_uc* pixels =
nullptr;
985 int texWidth, texHeight, texChannels;
988 if (!m_vfs->file_exists(fullPath)) {
989 log(LogLevel::ERROR,
"Texture file not found in VFS: %s", fullPath.c_str());
993 log(
"VFS loading image...");
995 auto fileData = m_vfs->load_file(fullPath);
997 log(LogLevel::ERROR,
"VFS failed to load texture: %s", fullPath.c_str());
1001 pixels = stbi_load_from_memory(
1002 reinterpret_cast<const stbi_uc*
>(fileData->data.data()),
1003 static_cast<int>(fileData->size),
1004 &texWidth, &texHeight, &texChannels, STBI_rgb_alpha
1008 log(LogLevel::ERROR,
"STBI failed: %s", stbi_failure_reason());
1011 }
catch (
const std::exception& e) {
1012 log(LogLevel::ERROR,
"Failed to load image data for %s", fullPath.c_str());
1013 if (pixels) stbi_image_free(pixels);
1017 log(
"Image loaded from VFS: %dx%d, %d channels", texWidth, texHeight, texChannels);
1019 uint32_t assignedIndex = 0;
1021 if (!m_r_context.recycledTextureIndices.empty()) {
1022 assignedIndex = m_r_context.recycledTextureIndices.front();
1023 m_r_context.recycledTextureIndices.pop();
1024 log(
"Recycling texture index: %u for '%s'", assignedIndex, name.c_str());
1027 log(LogLevel::ERROR,
"Maximum texture count (%u) reached!",
MAX_TEXTURES);
1028 if (pixels) stbi_image_free(pixels);
1031 assignedIndex = m_r_context.nextTextureIndex++;
1032 log(
"Assigning new texture index: %u for '%s'", assignedIndex, name.c_str());
1035 VkDeviceSize imageSize = texWidth * texHeight * 4;
1038 log(LogLevel::ERROR,
"Failed to load texture: %s", fullPath.c_str());
1042 log(
"Creating staging buffer (%zu bytes)...",
1043 static_cast<size_t>(texWidth * texHeight * 4));
1044 VkBuffer stagingBuffer;
1045 VmaAllocation stagingAlloc;
1046 VkBufferCreateInfo bufferInfo{};
1047 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1048 bufferInfo.size = imageSize;
1049 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1051 VmaAllocationCreateInfo allocInfo{};
1052 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1054 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAlloc,
nullptr);
1057 vmaMapMemory(m_r_context.allocator, stagingAlloc, &data);
1058 memcpy(data, pixels,
static_cast<size_t>(imageSize));
1059 vmaUnmapMemory(m_r_context.allocator, stagingAlloc);
1060 stbi_image_free(pixels);
1062 log(
"Creating Vulkan image...");
1063 VkImageCreateInfo imageInfo{};
1064 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1065 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1066 imageInfo.extent = {
static_cast<uint32_t
>(texWidth),
static_cast<uint32_t
>(texHeight), 1};
1067 imageInfo.mipLevels = 1;
1068 imageInfo.arrayLayers = 1;
1069 imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
1070 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1071 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1072 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
1073 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1075 VmaAllocationCreateInfo imageAllocInfo{};
1076 imageAllocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1078 VkImage textureImage;
1079 VmaAllocation textureAlloc;
1080 vmaCreateImage(m_r_context.allocator, &imageInfo, &imageAllocInfo, &textureImage, &textureAlloc,
nullptr);
1082 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
1084 VkImageMemoryBarrier barrier{};
1085 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1086 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1087 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1088 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1089 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1090 barrier.image = textureImage;
1091 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1092 barrier.subresourceRange.baseMipLevel = 0;
1093 barrier.subresourceRange.levelCount = 1;
1094 barrier.subresourceRange.baseArrayLayer = 0;
1095 barrier.subresourceRange.layerCount = 1;
1096 barrier.srcAccessMask = 0;
1097 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1100 vkCmdPipelineBarrier(commandBuffer,
1101 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1102 VK_PIPELINE_STAGE_TRANSFER_BIT,
1108 VkBufferImageCopy region{};
1109 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1110 region.imageSubresource.mipLevel = 0;
1111 region.imageSubresource.baseArrayLayer = 0;
1112 region.imageSubresource.layerCount = 1;
1113 region.imageExtent = imageInfo.extent;
1116 vkCmdCopyBufferToImage(commandBuffer,
1119 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1123 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1124 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1125 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1126 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1128 vkCmdPipelineBarrier(commandBuffer,
1129 VK_PIPELINE_STAGE_TRANSFER_BIT,
1130 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
1136 m_r_context.endSingleTimeCommands(commandBuffer);
1138 vkDeviceWaitIdle(m_r_context.device);
1140 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
1142 VkImageViewCreateInfo viewInfo{};
1143 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1144 viewInfo.image = textureImage;
1145 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1146 viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
1147 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1148 viewInfo.subresourceRange.levelCount = 1;
1149 viewInfo.subresourceRange.layerCount = 1;
1151 VkImageView textureView;
1152 log(
"Creating vulkan texture view...");
1153 if (vkCreateImageView(m_r_context.device, &viewInfo,
nullptr, &textureView) != VK_SUCCESS) {
1154 log(LogLevel::ERROR,
"Failed to create texture image view!");
1158 m_r_context.textureIndices[name] = assignedIndex;
1159 log(
"Assigned texture '%s' to index %u", name.c_str(), m_r_context.textureIndices[name]);
1161 m_textures[name] = textureView;
1162 m_textureImages[name] = textureImage;
1163 m_textureAllocations[name] = textureAlloc;
1164 m_textureViews[name] = textureView;
1166 if (m_r_context.supportsBindlessTextures) {
1167 VkDescriptorImageInfo imageInfo{};
1168 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1169 imageInfo.imageView = textureView;
1170 imageInfo.sampler = m_textureSampler;
1172 VkWriteDescriptorSet write{};
1173 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1174 write.dstSet = m_r_context.bindlessDescriptorSet;
1175 write.dstBinding = 0;
1176 write.dstArrayElement = assignedIndex;
1177 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1178 write.descriptorCount = 1;
1179 write.pImageInfo = &imageInfo;
1181 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
1183 VkDescriptorImageInfo imageDescInfo{};
1184 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1185 imageDescInfo.sampler = m_textureSampler;
1186 imageDescInfo.imageView = textureView;
1188 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
1189 VkWriteDescriptorSet write{};
1190 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1192 write.dstBinding = 0;
1193 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1194 write.descriptorCount = 1;
1195 write.pImageInfo = &imageDescInfo;
1197 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
1200 log(
"Updated texture descriptors for '%s'", name.c_str());
1205 std::vector<std::string> neededPaths;
1207 for (
const auto& path : paths) {
1208 if (!m_r_context.textureIndices.contains(path) &&
1209 std::find(m_ignoredTexturePaths.begin(), m_ignoredTexturePaths.end(), path) == m_ignoredTexturePaths.end()) {
1210 neededPaths.push_back(path);
1214 if (neededPaths.empty())
return;
1216 std::vector<std::future<BatchedTextureData>> futures;
1218 for (
const auto& path : neededPaths) {
1219 futures.push_back(GetThreadPool().enqueue([
this, path]() {
1222 auto fileData = m_vfs->load_file(path);
1224 data.m_pixels = stbi_load_from_memory(
1225 reinterpret_cast<const stbi_uc*
>(fileData->data.data()),
1226 static_cast<int>(fileData->size),
1227 &data.m_w, &data.m_h, &data.m_channels, STBI_rgb_alpha
1234 std::vector<BatchedTextureData> validData;
1235 for (
auto& f : futures) {
1236 auto data = f.get();
1237 if (data.m_pixels) {
1238 if (!m_r_context.recycledTextureIndices.empty()) {
1239 data.m_assignedIndex = m_r_context.recycledTextureIndices.front();
1240 m_r_context.recycledTextureIndices.pop();
1242 data.m_assignedIndex = m_r_context.nextTextureIndex++;
1244 validData.push_back(data);
1246 m_ignoredTexturePaths.push_back(data.m_path);
1247 log(LogLevel::WARNING,
"Batched load failed for: %s", data.m_path.c_str());
1251 if (validData.empty())
return;
1253 for (
auto& data : validData) {
1254 VkDeviceSize imageSize = data.m_w * data.m_h * 4;
1256 VkBufferCreateInfo bufferInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
1257 bufferInfo.size = imageSize;
1258 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
1259 VmaAllocationCreateInfo allocInfo{ .usage = VMA_MEMORY_USAGE_CPU_TO_GPU };
1260 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &data.m_stagingBuffer, &data.m_stagingAlloc,
nullptr);
1263 vmaMapMemory(m_r_context.allocator, data.m_stagingAlloc, &mapped);
1264 memcpy(mapped, data.m_pixels,
static_cast<size_t>(imageSize));
1265 vmaUnmapMemory(m_r_context.allocator, data.m_stagingAlloc);
1266 stbi_image_free(data.m_pixels);
1268 VkImageCreateInfo imageInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
1269 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1270 imageInfo.extent = {
static_cast<uint32_t
>(data.m_w),
static_cast<uint32_t
>(data.m_h), 1};
1271 imageInfo.mipLevels = 1;
1272 imageInfo.arrayLayers = 1;
1273 imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
1274 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1275 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1276 imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
1277 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1278 VmaAllocationCreateInfo imageAllocInfo{ .usage = VMA_MEMORY_USAGE_GPU_ONLY };
1280 vmaCreateImage(m_r_context.allocator, &imageInfo, &imageAllocInfo, &data.m_image, &data.m_imageAlloc,
nullptr);
1283 VkCommandBuffer cmd = m_r_context.beginSingleTimeCommands();
1285 for (
const auto& data : validData) {
1286 VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
1287 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1288 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1289 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1290 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1291 barrier.image = data.m_image;
1292 barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
1293 barrier.srcAccessMask = 0;
1294 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1296 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &barrier);
1298 VkBufferImageCopy region{};
1299 region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
1300 region.imageExtent = {
static_cast<uint32_t
>(data.m_w),
static_cast<uint32_t
>(data.m_h), 1};
1301 vkCmdCopyBufferToImage(cmd, data.m_stagingBuffer, data.m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
1303 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1304 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1305 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1306 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1308 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
nullptr, 0,
nullptr, 1, &barrier);
1311 m_r_context.endSingleTimeCommands(cmd);
1313 for (
const auto& data : validData) {
1314 vmaDestroyBuffer(m_r_context.allocator, data.m_stagingBuffer, data.m_stagingAlloc);
1316 VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
1317 viewInfo.image = data.m_image;
1318 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1319 viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
1320 viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
1322 VkImageView textureView;
1323 if (vkCreateImageView(m_r_context.device, &viewInfo,
nullptr, &textureView) != VK_SUCCESS)
continue;
1325 m_r_context.textureIndices[data.m_path] = data.m_assignedIndex;
1326 m_textures[data.m_path] = textureView;
1327 m_textureImages[data.m_path] = data.m_image;
1328 m_textureAllocations[data.m_path] = data.m_imageAlloc;
1329 m_textureViews[data.m_path] = textureView;
1331 VkDescriptorImageInfo imageDescInfo{};
1332 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1333 imageDescInfo.sampler = m_textureSampler;
1334 imageDescInfo.imageView = textureView;
1336 if (m_r_context.supportsBindlessTextures) {
1337 VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
1338 write.dstSet = m_r_context.bindlessDescriptorSet;
1339 write.dstBinding = 0;
1340 write.dstArrayElement = data.m_assignedIndex;
1341 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1342 write.descriptorCount = 1;
1343 write.pImageInfo = &imageDescInfo;
1344 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
1346 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
1347 VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
1349 write.dstBinding = 0;
1350 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1351 write.descriptorCount = 1;
1352 write.pImageInfo = &imageDescInfo;
1353 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
1360 if (name ==
"default")
return;
1362 auto it = m_textures.find(name);
1363 if (it == m_textures.end()){
1364 log(
"Texture %s not loaded", name.c_str());
1368 uint32_t textureIndex = m_r_context.textureIndices[name];
1370 vkDeviceWaitIdle(m_r_context.device);
1372 if (m_textureViews[name] != VK_NULL_HANDLE) {
1373 vkDestroyImageView(m_r_context.device, m_textureViews[name],
nullptr);
1374 m_textureViews[name] = VK_NULL_HANDLE;
1377 if (m_textureImages[name] != VK_NULL_HANDLE && m_textureAllocations[name] != VK_NULL_HANDLE) {
1378 vmaDestroyImage(m_r_context.allocator, m_textureImages[name], m_textureAllocations[name]);
1379 m_textureImages[name] = VK_NULL_HANDLE;
1384 VkDescriptorImageInfo imageInfo{};
1385 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1386 imageInfo.imageView = defaultView;
1387 imageInfo.sampler = m_textureSampler;
1389 if (m_r_context.supportsBindlessTextures) {
1390 VkWriteDescriptorSet bindlessWrite{};
1391 bindlessWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1392 bindlessWrite.dstSet = m_r_context.bindlessDescriptorSet;
1393 bindlessWrite.dstBinding = 0;
1394 bindlessWrite.dstArrayElement = textureIndex;
1395 bindlessWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1396 bindlessWrite.descriptorCount = 1;
1397 bindlessWrite.pImageInfo = &imageInfo;
1399 vkUpdateDescriptorSets(m_r_context.device, 1, &bindlessWrite, 0,
nullptr);
1401 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
1402 VkWriteDescriptorSet write{};
1403 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1405 write.dstBinding = 0;
1406 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1407 write.descriptorCount = 1;
1408 write.pImageInfo = &imageInfo;
1410 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0,
nullptr);
1414 m_textures.erase(name);
1415 m_textureImages.erase(name);
1416 m_textureAllocations.erase(name);
1417 m_textureViews.erase(name);
1419 m_r_context.textureIndices.erase(name);
1420 m_r_context.recycledTextureIndices.push(textureIndex);
1422 log(
"Texture %s unloaded", name.c_str());