VEX Engine
Retro looking game engine made in vulkan mostly because i wanted to understand it better.
Loading...
Searching...
No Matches
Resources.cpp
1#include "Resources.hpp"
2
3#include <volk.h>
4#include <vk_mem_alloc.h>
5#include <SDL3/SDL_vulkan.h>
6#include <algorithm>
8#include "components/PathUtils.hpp"
9#include "limits.hpp"
10
11#define STB_IMAGE_IMPLEMENTATION
12#include "../../../../thirdparty/stb/stb_image.h"
13
14namespace vex {
17 int m_w, m_h, m_channels;
18 stbi_uc* m_pixels = nullptr;
19 std::string m_path;
20 uint32_t m_assignedIndex = 0;
21 VkBuffer m_stagingBuffer = VK_NULL_HANDLE;
22 VmaAllocation m_stagingAlloc = VK_NULL_HANDLE;
23 VkImage m_image = VK_NULL_HANDLE;
24 VmaAllocation m_imageAlloc = VK_NULL_HANDLE;
25};
26
35
37 vkDeviceWaitIdle(m_r_context.device);
38
39 if (m_textureSampler != VK_NULL_HANDLE) {
40 vkDestroySampler(m_r_context.device, m_textureSampler, nullptr);
41 m_textureSampler = VK_NULL_HANDLE;
42 }
43
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;
48 }
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;
52 }
53 }
54
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);
58 }
59
60 if (image != VK_NULL_HANDLE && m_textureAllocations.count(name)) {
61 vmaDestroyImage(m_r_context.allocator, image, m_textureAllocations[name]);
62 }
63 }
64
65 m_textureImages.clear();
66 m_textureAllocations.clear();
67 m_textureViews.clear();
68 m_textures.clear();
69
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;
73 }
74
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;
78 }
79
80 if (m_descriptorPool != VK_NULL_HANDLE) {
81 vkDestroyDescriptorPool(m_r_context.device, m_descriptorPool, nullptr);
82 m_descriptorPool = VK_NULL_HANDLE;
83 }
84
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;
88 }
89
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;
93 }
94
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;
98 }
99
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;
103 }
104 if (m_particleDescriptorPool != VK_NULL_HANDLE) {
105 vkDestroyDescriptorPool(m_r_context.device, m_particleDescriptorPool, nullptr);
106 m_particleDescriptorPool = VK_NULL_HANDLE;
107 }
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;
113 }
114 }
115
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;
122 }
123 }
124
126 const uint32_t LUT_SIZE = 32;
127 VkDeviceSize imageSize = LUT_SIZE * LUT_SIZE * LUT_SIZE * 4;
128 std::vector<uint8_t> lutData(imageSize);
129
130 const float SATURATION = 1.1f;
131 const float BRIGHTNESS_BOOST = 1.4f;
132 const float textureBpc = 32.0f;
133
134 auto luma = [](const glm::vec3& c) {
135 return glm::dot(c, glm::vec3(0.299f, 0.587f, 0.114f));
136 };
137
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++) {
141 glm::vec3 color(
142 (float)x / (LUT_SIZE - 1),
143 (float)y / (LUT_SIZE - 1),
144 (float)z / (LUT_SIZE - 1)
145 );
146
147 color = glm::round(color * textureBpc) / textureBpc;
148
149 float gray = luma(color);
150 color = glm::mix(glm::vec3(gray), color, SATURATION);
151 color *= BRIGHTNESS_BOOST;
152
153 color = glm::clamp(color, 0.0f, 1.0f);
154
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;
160 }
161 }
162 }
163
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;
169
170 VmaAllocationCreateInfo allocInfo{};
171 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
172 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAlloc, nullptr);
173
174 void* data;
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);
178
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;
189
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);
193
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);
199
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;
204 VkCommandBuffer cmd;
205 vkAllocateCommandBuffers(m_r_context.device, &cmdAllocInfo, &cmd);
206
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);
210
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;
224
225 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
226
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};
237
238 vkCmdCopyBufferToImage(cmd, stagingBuffer, m_r_context.colorLutImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
239
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;
244
245 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
246
247 vkEndCommandBuffer(cmd);
248
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);
254
255 vkDestroyCommandPool(m_r_context.device, tempPool, nullptr);
256 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
257
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;
267
268 vkCreateImageView(m_r_context.device, &viewInfo, nullptr, &m_r_context.colorLutView);
269 }
270
271 void VulkanResources::createTextureFromRaw(const std::vector<unsigned char>& rgba, int w, int h, const std::string& name) {
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]);
274 return;
275 }
276
277 VkBuffer stagingBuffer;
278 VmaAllocation stagingAlloc;
279
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;
284
285 VmaAllocationCreateInfo stagingAllocInfo{};
286 stagingAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
287
288 vmaCreateBuffer(m_r_context.allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAlloc, nullptr);
289
290 void* data;
291 vmaMapMemory(m_r_context.allocator, stagingAlloc, &data);
292 memcpy(data, rgba.data(), rgba.size());
293 vmaUnmapMemory(m_r_context.allocator, stagingAlloc);
294
295 VkImage textureImage;
296 VmaAllocation textureAlloc;
297
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;
312
313 VmaAllocationCreateInfo allocInfo{};
314 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
315
316 vmaCreateImage(m_r_context.allocator, &imageInfo, &allocInfo, &textureImage, &textureAlloc, nullptr);
317
318 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
319
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;
334
335 vkCmdPipelineBarrier(
336 commandBuffer,
337 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
338 VK_PIPELINE_STAGE_TRANSFER_BIT,
339 0,
340 0, nullptr,
341 0, nullptr,
342 1, &barrier
343 );
344
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};
355
356 vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
357
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;
362
363 vkCmdPipelineBarrier(
364 commandBuffer,
365 VK_PIPELINE_STAGE_TRANSFER_BIT,
366 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
367 0,
368 0, nullptr,
369 0, nullptr,
370 1, &barrier
371 );
372
373 m_r_context.endSingleTimeCommands(commandBuffer);
374
375 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
376
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;
385
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!");
390 }
391
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]);
394
395 m_textures[name] = textureView;
396 m_textureImages[name] = textureImage;
397 m_textureAllocations[name] = textureAlloc;
398 m_textureViews[name] = textureView;
399
400 VkDescriptorImageInfo imageDescInfo{};
401 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
402 imageDescInfo.sampler = m_textureSampler;
403 imageDescInfo.imageView = textureView;
404
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;
414
415 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
416 } else {
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;
420 write.dstSet = getTextureDescriptorSet(frame, m_r_context.textureIndices[name]);
421 write.dstBinding = 0;
422 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
423 write.descriptorCount = 1;
424 write.pImageInfo = &imageDescInfo;
425
426 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
427 }
428 }
429 }
430
432 log("Creating uniform buffers...");
433 m_sceneBuffers.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
434 m_sceneAllocs.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
435 m_lightBuffers.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
436 m_lightAllocs.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
437
438 VkBufferCreateInfo bufferInfo{};
439 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
440 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
441
442 VmaAllocationCreateInfo allocInfo{};
443 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
444
445 for (size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
446 bufferInfo.size = sizeof(SceneUBO);
447 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo,
448 &m_sceneBuffers[i], &m_sceneAllocs[i], nullptr);
449
450 bufferInfo.size = sizeof(SceneLightsUBO) * MAX_MODELS;
451 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo,
452 &m_lightBuffers[i], &m_lightAllocs[i], nullptr);
453 }
454 }
455
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;
462
463 VkDescriptorSetLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
464 layoutInfo.bindingCount = 1;
465 layoutInfo.pBindings = &ssboBinding;
466
467 if (vkCreateDescriptorSetLayout(m_r_context.device, &layoutInfo, nullptr, &m_r_context.particleDescriptorSetLayout) != VK_SUCCESS) {
468 throw_error("Failed to create particle SSBO layout");
469 }
470
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);
475
476 VkBufferCreateInfo bufferInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
477 bufferInfo.size = sizeof(ParticleGPUData) * 100000;
478 bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
479
480 VmaAllocationCreateInfo allocInfo{};
481 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
482 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
483
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]);
487 }
488
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;
494
495 if (vkCreateDescriptorPool(m_r_context.device, &poolInfo, nullptr, &m_particleDescriptorPool) != VK_SUCCESS) {
496 throw_error("Failed to create particle descriptor pool");
497 }
498
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();
504
505 if (vkAllocateDescriptorSets(m_r_context.device, &allocSetInfo, m_particleDescriptorSets.data()) != VK_SUCCESS) {
506 throw_error("Failed to allocate particle descriptor sets");
507 }
508
509 for (size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
510 VkDescriptorBufferInfo bInfo{};
511 bInfo.buffer = m_particleSSBOs[i];
512 bInfo.offset = 0;
513 bInfo.range = VK_WHOLE_SIZE;
514
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);
522 }
523 }
524
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;
532
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;
537
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();
542
543 log("Creating Uniform buffer bindings...");
544 if (vkCreateDescriptorSetLayout(m_r_context.device, &uboLayoutInfo, nullptr, &m_r_context.uboDescriptorSetLayout) != VK_SUCCESS) {
545 throw_error("Failed to create UBO layout");
546 }
547
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;
553
554 VkDescriptorSetLayoutCreateInfo texLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
555 texLayoutInfo.bindingCount = 1;
556 texLayoutInfo.pBindings = &texBinding;
557
558 if (vkCreateDescriptorSetLayout(m_r_context.device, &texLayoutInfo, nullptr, &m_r_context.textureDescriptorSetLayout) != VK_SUCCESS) {
559 throw_error("Failed to create legacy texture layout");
560 }
561
562 if (m_r_context.supportsBindlessTextures) {
563 log("Initializing Texture System: BINDLESS (Global Array)");
564
565 VkDescriptorBindingFlags bindlessFlags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
566
567 VkDescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO};
568 bindingFlagsInfo.bindingCount = 1;
569 bindingFlagsInfo.pBindingFlags = &bindlessFlags;
570
571 VkDescriptorSetLayoutBinding bindlessBinding{};
572 bindlessBinding.binding = 0;
573 bindlessBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
574 bindlessBinding.descriptorCount = MAX_TEXTURES;
575 bindlessBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
576 bindlessBinding.pImmutableSamplers = nullptr;
577
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;
583
584 if (vkCreateDescriptorSetLayout(m_r_context.device, &bindlessLayoutInfo, nullptr, &m_r_context.bindlessDescriptorSetLayout) != VK_SUCCESS) {
585 throw_error("Failed to create bindless layout");
586 }
587
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;
594
595 vkCreateDescriptorPool(m_r_context.device, &poolInfo, nullptr, &m_r_context.bindlessDescriptorPool);
596
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;
601
602 vkAllocateDescriptorSets(m_r_context.device, &allocInfo, &m_r_context.bindlessDescriptorSet);
603 } else {
604 log("Initializing Texture System: LEGACY (One Set Per Texture)");
605
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;
611
612 VkDescriptorSetLayoutCreateInfo texLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
613 texLayoutInfo.bindingCount = 1;
614 texLayoutInfo.pBindings = &texBinding;
615
616 if (vkCreateDescriptorSetLayout(m_r_context.device, &texLayoutInfo, nullptr, &m_r_context.textureDescriptorSetLayout) != VK_SUCCESS) {
617 throw_error("Failed to create legacy texture layout");
618 }
619 }
620
621 std::array<VkDescriptorSetLayoutBinding, 5> screenBindings{};
622
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;
628 }
629
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");
635 }
636
637 std::array<VkDescriptorPoolSize, 3> poolSizes{};
638
639 poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
640 poolSizes[0].descriptorCount = m_r_context.MAX_FRAMES_IN_FLIGHT;
641
642 poolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
643 poolSizes[1].descriptorCount = m_r_context.MAX_FRAMES_IN_FLIGHT * MAX_MODELS;
644
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);
647
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));
653
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");
657 }
658
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();
665
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");
670 }
671
672 for (size_t i = 0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
673 std::array<VkWriteDescriptorSet, 2> uboWrites{};
674
675 VkDescriptorBufferInfo sceneBufferInfo{};
676 sceneBufferInfo.buffer = m_sceneBuffers[i];
677 sceneBufferInfo.range = sizeof(SceneUBO);
678
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;
685
686 VkDescriptorBufferInfo lightInfo{};
687 lightInfo.buffer = m_lightBuffers[i];
688 lightInfo.range = sizeof(SceneLightsUBO);
689
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;
696
697 vkUpdateDescriptorSets(m_r_context.device, uboWrites.size(), uboWrites.data(), 0, nullptr);
698 }
699
700 if (!m_r_context.supportsBindlessTextures) {
702 }
703 }
704
705 VkDescriptorSet VulkanResources::getUBODescriptorSet(uint32_t frameIndex) const {
706 if (frameIndex >= m_descriptorSets.size()) return VK_NULL_HANDLE;
707 return m_descriptorSets[frameIndex];
708 }
709
710 void VulkanResources::updateTextureDescriptor(uint32_t frameIndex, VkImageView textureView, uint32_t textureIndex) {
711 if (frameIndex >= m_r_context.MAX_FRAMES_IN_FLIGHT) {
712 log(LogLevel::ERROR,
713 "updateTextureDescriptor: Frame index %u exceeds MAX_FRAMES_IN_FLIGHT (%u)",
714 frameIndex, m_r_context.MAX_FRAMES_IN_FLIGHT);
715 return;
716 }
717
718 if (textureIndex >= MAX_TEXTURES) {
719 log(LogLevel::ERROR,
720 "updateTextureDescriptor: Texture index %u exceeds MAX_TEXTURES (%u)",
721 textureIndex, MAX_TEXTURES);
722 return;
723 }
724
725 VkDescriptorImageInfo imageInfo{};
726 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
727 imageInfo.imageView = textureView;
728 imageInfo.sampler = m_textureSampler;
729
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;
738
739 vkUpdateDescriptorSets(m_r_context.device, 1, &bindlessWrite, 0, nullptr);
740 } else {
741 VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
742 write.dstSet = getTextureDescriptorSet(frameIndex, textureIndex);
743 write.dstBinding = 0;
744 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
745 write.descriptorCount = 1;
746 write.pImageInfo = &imageInfo;
747
748 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
749 }
750 }
751
753 if (m_r_context.currentFrame >= m_sceneAllocs.size()) {
754 log(LogLevel::ERROR,
755 "updateSceneUBO: Current frame %u exceeds scene allocations size (%zu)",
756 m_r_context.currentFrame, m_sceneAllocs.size());
757 return;
758 }
759
760 if (m_r_context.currentFrame >= m_sceneBuffers.size()) {
761 log(LogLevel::ERROR,
762 "updateSceneUBO: Current frame %u exceeds scene buffers size (%zu)",
763 m_r_context.currentFrame, m_sceneBuffers.size());
764 return;
765 }
766
767 void* mapped;
768 vmaMapMemory(m_r_context.allocator, m_sceneAllocs[m_r_context.currentFrame], &mapped);
769 memcpy(mapped, &data, sizeof(data));
770 vmaUnmapMemory(m_r_context.allocator, m_sceneAllocs[m_r_context.currentFrame]);
771 }
772
773 void VulkanResources::updateLightUBO(uint32_t frameIndex, uint32_t modelIndex, const SceneLightsUBO& data) {
774 if (frameIndex >= m_lightAllocs.size()) {
775 log(LogLevel::ERROR,
776 "updateLightUBO: Frame index %u exceeds light allocations size (%zu)",
777 frameIndex, m_lightAllocs.size());
778 return;
779 }
780
781 if (frameIndex >= m_lightBuffers.size()) {
782 log(LogLevel::ERROR,
783 "updateLightUBO: Frame index %u exceeds light buffers size (%zu)",
784 frameIndex, m_lightBuffers.size());
785 return;
786 }
787
788 if (modelIndex >= MAX_MODELS) {
789 log(LogLevel::ERROR,
790 "updateLightUBO: Model index %u exceeds MAX_MODELS (%u)",
791 modelIndex, MAX_MODELS);
792 return;
793 }
794
795 void* mapped;
796 vmaMapMemory(m_r_context.allocator, m_lightAllocs[frameIndex], &mapped);
797 char* lightData = static_cast<char*>(mapped) + modelIndex * sizeof(SceneLightsUBO);
798 memcpy(lightData, &data, sizeof(SceneLightsUBO));
799 vmaUnmapMemory(m_r_context.allocator, m_lightAllocs[frameIndex]);
800 }
801
803 const unsigned char pixels[] = {255, 255, 255, 255};
804
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;
816
817 VmaAllocationCreateInfo allocInfo{};
818 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
819
820 VkImage image;
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");
825 }
826
827 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
828
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;
843
844 vkCmdPipelineBarrier(
845 commandBuffer,
846 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
847 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
848 0,
849 0, nullptr,
850 0, nullptr,
851 1, &barrier
852 );
853
854 m_r_context.endSingleTimeCommands(commandBuffer);
855
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;
864
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");
870 }
871
872 m_textures["default"] = imageView;
873 m_textureImages["default"] = image;
874 m_textureAllocations["default"] = allocation;
875 m_textureViews["default"] = imageView;
876
877 m_r_context.textureIndices["default"] = 0;
878 m_r_context.nextTextureIndex = 1;
879 log("Created default texture with index 0");
880 }
881
883 log("Creating %d texture descriptor sets", m_r_context.MAX_FRAMES_IN_FLIGHT * MAX_TEXTURES);
884
885 m_textureDescriptorSets.resize(m_r_context.MAX_FRAMES_IN_FLIGHT * MAX_TEXTURES);
886
887 std::vector<VkDescriptorSetLayout> layouts(
888 m_r_context.MAX_FRAMES_IN_FLIGHT * MAX_TEXTURES,
889 m_r_context.textureDescriptorSetLayout
890 );
891
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();
897
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));
901 }
902
903 VkDescriptorImageInfo defaultImageInfo{};
904 defaultImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
905 defaultImageInfo.sampler = m_textureSampler;
906 defaultImageInfo.imageView = getTextureView("default");
907
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;
913 write.dstSet = getTextureDescriptorSet(frame, texIdx);
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);
919 }
920 }
921 vkUpdateDescriptorSets(m_r_context.device, writes.size(), writes.data(), 0, nullptr);
922 log("Initialized %d texture descriptor sets with default texture", writes.size());
923 }
924
925
926 VkDescriptorSet VulkanResources::getTextureDescriptorSet(uint32_t frameIndex, uint32_t textureIndex) const {
927 const uint32_t index = frameIndex * MAX_TEXTURES + textureIndex;
928 if (index >= m_textureDescriptorSets.size()) {
929 log(LogLevel::ERROR,
930 "Texture index out of bounds (Frame: %u, TexIndex: %u, Max: %u)",
931 frameIndex, textureIndex, MAX_TEXTURES);
932 return VK_NULL_HANDLE;
933 }
934 return m_textureDescriptorSets[index];
935 }
936
938 VkSamplerCreateInfo samplerInfo{};
939 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
940 samplerInfo.magFilter = VK_FILTER_NEAREST;
941 samplerInfo.minFilter = VK_FILTER_NEAREST;
942 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
943 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
944 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
945 samplerInfo.anisotropyEnable = VK_FALSE;
946 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
947 samplerInfo.unnormalizedCoordinates = VK_FALSE;
948 samplerInfo.compareEnable = VK_FALSE;
949 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
950 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
951
952 log("Creating texture sampler...");
953 vkCreateSampler(m_r_context.device, &samplerInfo, nullptr, &m_textureSampler);
954 }
955
956
957 uint32_t VulkanResources::getTextureIndex(const std::string& name) {
958 auto it = m_r_context.textureIndices.find(name);
959 if (it != m_r_context.textureIndices.end()) return it->second;
960 if (std::find(m_ignoredTexturePaths.begin(), m_ignoredTexturePaths.end(), name) == m_ignoredTexturePaths.end()) {
961 if(loadTexture(name, name)){
962 return getTextureIndex(name);
963 }else{
964 m_ignoredTexturePaths.push_back(name);
965 log(LogLevel::WARNING, "Texture '%s' could not be loaded!", name.c_str());
966 }
967 }
968 return 0;
969 }
970
971 bool VulkanResources::loadTexture(const std::string& path, const std::string& name) {
972 std::string fullPath = path;//"Assets/" + std::string(path.c_str());
973
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]);
976 return true;
977 }
978
979 if (m_r_context.textureIndices.size() >= MAX_TEXTURES) {
980 log(LogLevel::ERROR, "Maximum texture count (%u) reached!", MAX_TEXTURES);
981 return false;
982 }
983
984 stbi_uc* pixels = nullptr;
985 int texWidth, texHeight, texChannels;
986
987 try {
988 if (!m_vfs->file_exists(fullPath)) {
989 log(LogLevel::ERROR, "Texture file not found in VFS: %s", fullPath.c_str());
990 return false;
991 }
992
993 log("VFS loading image...");
994
995 auto fileData = m_vfs->load_file(fullPath); // This returns std::unique_ptr<FileData>
996 if (!fileData) {
997 log(LogLevel::ERROR, "VFS failed to load texture: %s", fullPath.c_str());
998 return false;
999 }
1000
1001 pixels = stbi_load_from_memory(
1002 reinterpret_cast<const stbi_uc*>(fileData->data.data()), // fileData is a pointer
1003 static_cast<int>(fileData->size), // fileData->size is member
1004 &texWidth, &texHeight, &texChannels, STBI_rgb_alpha
1005 );
1006
1007 if (!pixels) {
1008 log(LogLevel::ERROR, "STBI failed: %s", stbi_failure_reason());
1009 return false;
1010 }
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);
1014 return false;
1015 }
1016
1017 log("Image loaded from VFS: %dx%d, %d channels", texWidth, texHeight, texChannels);
1018
1019 uint32_t assignedIndex = 0;
1020
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());
1025 } else {
1026 if (m_r_context.nextTextureIndex >= MAX_TEXTURES) {
1027 log(LogLevel::ERROR, "Maximum texture count (%u) reached!", MAX_TEXTURES);
1028 if (pixels) stbi_image_free(pixels);
1029 return false;
1030 }
1031 assignedIndex = m_r_context.nextTextureIndex++;
1032 log("Assigning new texture index: %u for '%s'", assignedIndex, name.c_str());
1033 }
1034
1035 VkDeviceSize imageSize = texWidth * texHeight * 4;
1036
1037 if (!pixels) {
1038 log(LogLevel::ERROR, "Failed to load texture: %s", fullPath.c_str());
1039 return false;
1040 }
1041
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;
1050
1051 VmaAllocationCreateInfo allocInfo{};
1052 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
1053
1054 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &stagingBuffer, &stagingAlloc, nullptr);
1055
1056 void* data;
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);
1061
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;
1074
1075 VmaAllocationCreateInfo imageAllocInfo{};
1076 imageAllocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1077
1078 VkImage textureImage;
1079 VmaAllocation textureAlloc;
1080 vmaCreateImage(m_r_context.allocator, &imageInfo, &imageAllocInfo, &textureImage, &textureAlloc, nullptr);
1081
1082 VkCommandBuffer commandBuffer = m_r_context.beginSingleTimeCommands();
1083
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;
1098
1099
1100 vkCmdPipelineBarrier(commandBuffer,
1101 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1102 VK_PIPELINE_STAGE_TRANSFER_BIT,
1103 0,
1104 0, nullptr,
1105 0, nullptr,
1106 1, &barrier);
1107
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;
1114
1115
1116 vkCmdCopyBufferToImage(commandBuffer,
1117 stagingBuffer,
1118 textureImage,
1119 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1120 1,
1121 &region);
1122
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;
1127
1128 vkCmdPipelineBarrier(commandBuffer,
1129 VK_PIPELINE_STAGE_TRANSFER_BIT,
1130 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
1131 0,
1132 0, nullptr,
1133 0, nullptr,
1134 1, &barrier);
1135
1136 m_r_context.endSingleTimeCommands(commandBuffer);
1137
1138 vkDeviceWaitIdle(m_r_context.device);
1139
1140 vmaDestroyBuffer(m_r_context.allocator, stagingBuffer, stagingAlloc);
1141
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;
1150
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!");
1155 return false;
1156 }
1157
1158 m_r_context.textureIndices[name] = assignedIndex;
1159 log("Assigned texture '%s' to index %u", name.c_str(), m_r_context.textureIndices[name]);
1160
1161 m_textures[name] = textureView;
1162 m_textureImages[name] = textureImage;
1163 m_textureAllocations[name] = textureAlloc;
1164 m_textureViews[name] = textureView;
1165
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;
1171
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;
1180
1181 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
1182 }else{
1183 VkDescriptorImageInfo imageDescInfo{};
1184 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1185 imageDescInfo.sampler = m_textureSampler;
1186 imageDescInfo.imageView = textureView;
1187
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;
1191 write.dstSet = getTextureDescriptorSet(frame, m_r_context.textureIndices[name]);
1192 write.dstBinding = 0;
1193 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1194 write.descriptorCount = 1;
1195 write.pImageInfo = &imageDescInfo;
1196
1197 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
1198 }
1199 }
1200 log("Updated texture descriptors for '%s'", name.c_str());
1201 return true;
1202 }
1203
1204 void VulkanResources::loadTexturesBatched(const std::vector<std::string>& paths) {
1205 std::vector<std::string> neededPaths;
1206
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);
1211 }
1212 }
1213
1214 if (neededPaths.empty()) return;
1215
1216 std::vector<std::future<BatchedTextureData>> futures;
1217
1218 for (const auto& path : neededPaths) {
1219 futures.push_back(GetThreadPool().enqueue([this, path]() {
1220 BatchedTextureData data;
1221 data.m_path = path;
1222 auto fileData = m_vfs->load_file(path);
1223 if (fileData) {
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
1228 );
1229 }
1230 return data;
1231 }));
1232 }
1233
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();
1241 } else {
1242 data.m_assignedIndex = m_r_context.nextTextureIndex++;
1243 }
1244 validData.push_back(data);
1245 } else {
1246 m_ignoredTexturePaths.push_back(data.m_path);
1247 log(LogLevel::WARNING, "Batched load failed for: %s", data.m_path.c_str());
1248 }
1249 }
1250
1251 if (validData.empty()) return;
1252
1253 for (auto& data : validData) {
1254 VkDeviceSize imageSize = data.m_w * data.m_h * 4;
1255
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);
1261
1262 void* mapped;
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);
1267
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 };
1279
1280 vmaCreateImage(m_r_context.allocator, &imageInfo, &imageAllocInfo, &data.m_image, &data.m_imageAlloc, nullptr);
1281 }
1282
1283 VkCommandBuffer cmd = m_r_context.beginSingleTimeCommands();
1284
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;
1295
1296 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
1297
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, &region);
1302
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;
1307
1308 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
1309 }
1310
1311 m_r_context.endSingleTimeCommands(cmd);
1312
1313 for (const auto& data : validData) {
1314 vmaDestroyBuffer(m_r_context.allocator, data.m_stagingBuffer, data.m_stagingAlloc);
1315
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};
1321
1322 VkImageView textureView;
1323 if (vkCreateImageView(m_r_context.device, &viewInfo, nullptr, &textureView) != VK_SUCCESS) continue;
1324
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;
1330
1331 VkDescriptorImageInfo imageDescInfo{};
1332 imageDescInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1333 imageDescInfo.sampler = m_textureSampler;
1334 imageDescInfo.imageView = textureView;
1335
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);
1345 } else {
1346 for (uint32_t frame = 0; frame < m_r_context.MAX_FRAMES_IN_FLIGHT; ++frame) {
1347 VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
1348 write.dstSet = getTextureDescriptorSet(frame, data.m_assignedIndex);
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);
1354 }
1355 }
1356 }
1357 }
1358
1359 void VulkanResources::unloadTexture(const std::string& name) {
1360 if (name == "default") return;
1361
1362 auto it = m_textures.find(name);
1363 if (it == m_textures.end()){
1364 log("Texture %s not loaded", name.c_str());
1365 return;
1366 }
1367
1368 uint32_t textureIndex = m_r_context.textureIndices[name];
1369
1370 vkDeviceWaitIdle(m_r_context.device);
1371
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;
1375 }
1376
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;
1380 }
1381
1382 VkImageView defaultView = getTextureView("default");
1383
1384 VkDescriptorImageInfo imageInfo{};
1385 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1386 imageInfo.imageView = defaultView;
1387 imageInfo.sampler = m_textureSampler;
1388
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;
1398
1399 vkUpdateDescriptorSets(m_r_context.device, 1, &bindlessWrite, 0, nullptr);
1400 } else {
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;
1404 write.dstSet = getTextureDescriptorSet(frame, textureIndex);
1405 write.dstBinding = 0;
1406 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1407 write.descriptorCount = 1;
1408 write.pImageInfo = &imageInfo;
1409
1410 vkUpdateDescriptorSets(m_r_context.device, 1, &write, 0, nullptr);
1411 }
1412 }
1413
1414 m_textures.erase(name);
1415 m_textureImages.erase(name);
1416 m_textureAllocations.erase(name);
1417 m_textureViews.erase(name);
1418
1419 m_r_context.textureIndices.erase(name);
1420 m_r_context.recycledTextureIndices.push(textureIndex);
1421
1422 log("Texture %s unloaded", name.c_str());
1423 }
1424
1425 VkImageView VulkanResources::getTextureView(const std::string& name) const {
1426 auto it = m_textures.find(name);
1427 return (it != m_textures.end()) ? it->second : m_textures.at(getDefaultTextureName());
1428 }
1429}
This file defines VulkanResources Class.
This class provides abstraction of file system needed for loading packed and unpacked assets.
VkDescriptorSet getUBODescriptorSet(uint32_t frameIndex) const
Returns the uniform buffer descriptor set for the given frame index.
void createPerMeshTextureSets()
Internal function to create per-mesh texture sets.
const std::string & getDefaultTextureName() const
Returns the default texture name.
Definition Resources.hpp:64
void updateSceneUBO(const SceneUBO &data)
Updates the scene uniform buffer.
bool loadTexture(const std::string &path, const std::string &name)
Loads a texture from a file.
void createTextureSampler()
Internal function to create texture sampler.
uint32_t getTextureIndex(const std::string &name)
Returns the index of a texture with the given name.
VulkanResources(VulkanContext &context, VirtualFileSystem *vfs)
Simple constructor.
Definition Resources.cpp:27
void updateTextureDescriptor(uint32_t frameIndex, VkImageView textureView, uint32_t textureIndex)
Updates the texture descriptor.
void unloadTexture(const std::string &name)
Unloads a texture.
~VulkanResources()
Simple destructor.
Definition Resources.cpp:36
void createParticleSSBOs()
Internal function to create particle SSBOs.
void updateLightUBO(uint32_t frameIndex, uint32_t modelIndex, const SceneLightsUBO &data)
Updates the light uniform buffer for a specific model.
VkDescriptorSet getTextureDescriptorSet(uint32_t frameIndex, uint32_t textureIndex) const
Returns a descriptor set for the given frame index and texture index.
void createDefaultTexture()
Creates a default texture.
void loadTexturesBatched(const std::vector< std::string > &paths)
Loads multiple textures in batches.
void createTextureFromRaw(const std::vector< unsigned char > &rgba, int w, int h, const std::string &name)
Creates a texture from raw data.
void createDescriptorResources()
Internal function to create descriptor resources.
void createUniformBuffers()
Creates uniform buffers.
void createColorLUT()
Generates and uploads a 32x32x32 3D Lookup Texture (LUT) to the GPU. Bakes PS1 5-bit color quantizati...
VkImageView getTextureView(const std::string &name) const
Returns the texture view for the given texture name.
This files defines global variables defining vulkan limits.
const uint32_t MAX_TEXTURES
Definition limits.hpp:13
void VEX_EXPORT throw_error(const std::string &msg)
Throws an error or terminates the application.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
Helper struct for batching texture loading.
Definition Resources.cpp:16
Structure passed to the GPU containing per-particle rendering data.
Scene lights uniform buffer object.
Definition uniforms.hpp:47
Scene uniform buffer object. Holds data that is bind once per scene.
Definition uniforms.hpp:13
Struct holding all vulkan data, like device, surface, swapchain, images, views, and more.
Definition context.hpp:26
Represents an RGBA color value. Used mainly for fancy rendering in editor.
This file defines uniforms and pushconstant for rendering meshes.