VEX Engine
Retro looking game engine made in vulkan mostly because i wanted to understand it better.
Loading...
Searching...
No Matches
Renderer.cpp
1#include "Renderer.hpp"
4
5#include <cstdint>
6#include <memory>
7#define SDL_MAIN_HANDLED
8#include <SDL3/SDL.h>
12#include "frustum.hpp"
13#include "components/PathUtils.hpp"
14#include "limits.hpp"
15#include "components/HardwareInfo.hpp"
16#include <immintrin.h>
17
18#if defined(max)
19 #undef max
20#endif
21
22namespace vex {
23 struct FrustumSoA {
24 __m256 m_nx, m_ny, m_nz, m_dist;
25
26 void init(const vex::Frustum& f) {
27 m_nx = _mm256_setr_ps(f.planes[0].normal.x, f.planes[1].normal.x, f.planes[2].normal.x, f.planes[3].normal.x, f.planes[4].normal.x, f.planes[5].normal.x, 0.0f, 0.0f);
28 m_ny = _mm256_setr_ps(f.planes[0].normal.y, f.planes[1].normal.y, f.planes[2].normal.y, f.planes[3].normal.y, f.planes[4].normal.y, f.planes[5].normal.y, 0.0f, 0.0f);
29 m_nz = _mm256_setr_ps(f.planes[0].normal.z, f.planes[1].normal.z, f.planes[2].normal.z, f.planes[3].normal.z, f.planes[4].normal.z, f.planes[5].normal.z, 0.0f, 0.0f);
30 m_dist = _mm256_setr_ps(f.planes[0].distance, f.planes[1].distance, f.planes[2].distance, f.planes[3].distance, f.planes[4].distance, f.planes[5].distance, 0.0f, 0.0f);
31 }
32
33 __attribute__((target("avx2")))
34 bool testSphereAVX(const glm::vec3& center, float radius) const {
35 __m256 cx = _mm256_set1_ps(center.x);
36 __m256 cy = _mm256_set1_ps(center.y);
37 __m256 cz = _mm256_set1_ps(center.z);
38 __m256 r = _mm256_set1_ps(-radius);
39
40 __m256 dot = _mm256_fmadd_ps(m_nx, cx, m_dist);
41 dot = _mm256_fmadd_ps(m_ny, cy, dot);
42 dot = _mm256_fmadd_ps(m_nz, cz, dot);
43 __m256 mask = _mm256_cmp_ps(dot, r, _CMP_LT_OQ);
44
45 int res = _mm256_movemask_ps(mask);
46
47 return (res & 0x3F) == 0;
48 }
49 };
50
51 glm::vec3 extractCameraPosition(const glm::mat4& view) {
52 glm::mat4 invView = glm::inverse(view);
53 return glm::vec3(invView[3]);
54 }
55
57 std::unique_ptr<VulkanResources>& resources,
58 std::unique_ptr<VulkanPipeline>& pipeline,
59 std::unique_ptr<VulkanPipeline>& transPipeline,
60 std::unique_ptr<VulkanPipeline>& maskPipeline,
61 std::unique_ptr<VulkanPipeline>& billboardTransPipeline,
62 std::unique_ptr<VulkanPipeline>& billboardMaskedPipeline,
63 std::unique_ptr<VulkanPipeline>& particleTransPipeline,
64 std::unique_ptr<VulkanPipeline>& particleMaskedPipeline,
65 std::unique_ptr<VulkanPipeline>& uiPipeline,
66 std::unique_ptr<VulkanPipeline>& fullscreenPipeline,
67 std::unique_ptr<VulkanPipeline>& compositePipeline,
68 std::unique_ptr<VulkanSwapchainManager>& swapchainManager,
69 std::unique_ptr<MeshManager>& meshManager)
70 : m_r_context(context), m_p_resources(resources),
71 m_p_pipeline(pipeline), m_p_transPipeline(transPipeline),
72 m_p_maskPipeline(maskPipeline),
73 m_p_billboardTransPipeline(billboardTransPipeline),
74 m_p_billboardMaskedPipeline(billboardMaskedPipeline),
75 m_p_particleTransPipeline(particleTransPipeline),
76 m_p_particleMaskedPipeline(particleMaskedPipeline),
77 m_p_uiPipeline(uiPipeline),
78 m_p_fullscreenPipeline(fullscreenPipeline),
79 m_p_compositePipeline(compositePipeline),
80 m_p_swapchainManager(swapchainManager),
81 m_p_meshManager(meshManager) {
82 startTime = std::chrono::high_resolution_clock::now();
83
84 VkSamplerCreateInfo samplerInfo{};
85 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
86 samplerInfo.magFilter = VK_FILTER_NEAREST;
87 samplerInfo.minFilter = VK_FILTER_NEAREST;
88 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
89 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
90 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
91 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
92 samplerInfo.maxAnisotropy = 1.0f;
93
94 if (vkCreateSampler(m_r_context.device, &samplerInfo, nullptr, &m_screenSampler) != VK_SUCCESS) {
95 throw_error("Failed to create screen sampler");
96 }
97
98 VkSamplerCreateInfo linearSamplerInfo = samplerInfo;
99 linearSamplerInfo.magFilter = VK_FILTER_LINEAR;
100 linearSamplerInfo.minFilter = VK_FILTER_LINEAR;
101 linearSamplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
102 linearSamplerInfo.minLod = 0.0f;
103 linearSamplerInfo.maxLod = 10.0f;
104
105 if (vkCreateSampler(m_r_context.device, &linearSamplerInfo, nullptr, &m_linearSampler) != VK_SUCCESS) {
106 throw_error("Failed to create linear sampler");
107 }
108
109 VkDescriptorSetAllocateInfo allocInfo{};
110 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
111
112 VkDescriptorPoolSize poolSize = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 12 };
113 VkDescriptorPoolCreateInfo poolInfo = {};
114 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
115 poolInfo.poolSizeCount = 1;
116 poolInfo.pPoolSizes = &poolSize;
117 poolInfo.maxSets = 2;
118
119 vkCreateDescriptorPool(m_r_context.device, &poolInfo, nullptr, &m_localPool);
120
121 allocInfo.descriptorPool = m_localPool;
122 allocInfo.descriptorSetCount = 1;
123 allocInfo.pSetLayouts = &m_r_context.screenDescriptorSetLayout;
124
125 vkAllocateDescriptorSets(m_r_context.device, &allocInfo, &m_screenDescriptorSet);
126 allocInfo.pSetLayouts = &m_r_context.screenDescriptorSetLayout;
127 vkAllocateDescriptorSets(m_r_context.device, &allocInfo, &m_crtDescriptorSet);
128
129 #if DEBUG
130 m_editorCameraVulkanMesh = std::make_unique<VulkanMesh>(m_r_context);
131
132 m_debugBuffers.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
133 m_debugAllocations.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
134
135 VkBufferCreateInfo debugBufInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
136 debugBufInfo.size = 1024 * 1024;
137 debugBufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
138
139 VmaAllocationCreateInfo debugAllocInfo{};
140 debugAllocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
141
142 for(size_t i=0; i < m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
143 vmaCreateBuffer(m_r_context.allocator, &debugBufInfo, &debugAllocInfo, &m_debugBuffers[i], &m_debugAllocations[i], nullptr);
144 }
145 #endif
146 m_garbageDescriptors.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
147
148 if (m_r_context.supportsIndirectDraw) {
149 m_indirectBuffers.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
150 m_indirectAllocations.resize(m_r_context.MAX_FRAMES_IN_FLIGHT);
151
152 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
153 bufferInfo.size = 10000 * sizeof(VkDrawIndexedIndirectCommand);
154 bufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
155
156 VmaAllocationCreateInfo allocInfo = {};
157 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
158
159 for(size_t i=0; i<m_r_context.MAX_FRAMES_IN_FLIGHT; i++) {
160 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo, &m_indirectBuffers[i], &m_indirectAllocations[i], nullptr);
161 }
162 }
163
164 log("Renderer initialized successfully");
165 }
166
168 if (m_screenSampler) vkDestroySampler(m_r_context.device, m_screenSampler, nullptr);
169 if (m_linearSampler) vkDestroySampler(m_r_context.device, m_linearSampler, nullptr);
170 if (m_localPool) vkDestroyDescriptorPool(m_r_context.device, m_localPool, nullptr);
171
172 #if DEBUG
173 for(size_t i=0; i < m_debugBuffers.size(); i++) {
174 if(m_debugBuffers[i]) vmaDestroyBuffer(m_r_context.allocator, m_debugBuffers[i], m_debugAllocations[i]);
175 }
176 #endif
177
178 if (!m_indirectBuffers.empty()) {
179 for(size_t i=0; i < m_indirectBuffers.size(); i++) {
180 if(m_indirectBuffers[i] != VK_NULL_HANDLE) {
181 vmaDestroyBuffer(m_r_context.allocator, m_indirectBuffers[i], m_indirectAllocations[i]);
182 }
183 }
184 m_indirectBuffers.clear();
185 m_indirectAllocations.clear();
186 }
187
188 log("Renderer destroyed");
189 }
190
191 #if DEBUG
192 void Renderer::renderDebug(VkCommandBuffer cmd, int frameIndex, const std::vector<DebugVertex>& lines) {
193 if(lines.empty() || !m_pp_debugPipeline) return;
194
195 void* mappedData;
196 vmaMapMemory(m_r_context.allocator, m_debugAllocations[frameIndex], &mappedData);
197 size_t dataSize = lines.size() * sizeof(DebugVertex);
198 if(dataSize > 1024 * 1024) dataSize = 1024 * 1024;
199 memcpy(mappedData, lines.data(), dataSize);
200 vmaUnmapMemory(m_r_context.allocator, m_debugAllocations[frameIndex]);
201
202 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, (*m_pp_debugPipeline)->get());
203 VkViewport viewport{};
204 viewport.x = 0.0f;
205 viewport.y = 0.0f;
206 viewport.width = static_cast<float>(m_r_context.currentRenderResolution.x);
207 viewport.height = static_cast<float>(m_r_context.currentRenderResolution.y);
208 viewport.minDepth = 0.0f;
209 viewport.maxDepth = 1.0f;
210 vkCmdSetViewport(cmd, 0, 1, &viewport);
211 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
212 vkCmdSetScissor(cmd, 0, 1, &scissor);
213
214 VkDescriptorSet sceneSet = m_p_resources->getUBODescriptorSet(frameIndex);
215
216 uint32_t dynamicOffset = 0;
217 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, (*m_pp_debugPipeline)->layout(), 0, 1, &sceneSet, 1, &dynamicOffset);
218
219 VkBuffer vBuffers[] = { m_debugBuffers[frameIndex] };
220 VkDeviceSize offsets[] = { 0 };
221 vkCmdBindVertexBuffers(cmd, 0, 1, vBuffers, offsets);
222
223 vkCmdDraw(cmd, static_cast<uint32_t>(lines.size()), 1, 0, 0);
224 }
225 #endif
226
227 bool Renderer::beginFrame(glm::uvec2 renderResolution, SceneRenderData& outData) {
228 try {
229 if (renderResolution.x == 0 || renderResolution.y == 0 ||
230 renderResolution.x > 32768 || renderResolution.y > 32768) {
231 return false;
232 }
233
234 if (renderResolution != m_r_context.currentRenderResolution || m_r_context.requestSwapchainRecreation) {
235 log(LogLevel::INFO, "Recreating Swapchain", m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y, renderResolution.x, renderResolution.y);
236 m_r_context.currentRenderResolution = renderResolution;
237 m_p_pipeline->updateViewport(renderResolution);
238 m_p_swapchainManager->recreateSwapchain();
239 m_lastUsedView = VK_NULL_HANDLE;
240 if (m_cachedImGuiDescriptor != VK_NULL_HANDLE) {
241 m_garbageDescriptors[m_r_context.currentFrame].push_back(m_cachedImGuiDescriptor);
242 }
243 m_cachedImGuiDescriptor = VK_NULL_HANDLE;
244 m_lastUsedView = m_r_context.lowResColorView;
245 updateScreenDescriptor(m_r_context.lowResColorView);
246 }
247
248 if (!m_r_context.isValidFrameIndex(m_r_context.currentFrame)) {
249 log(LogLevel::ERROR,
250 "beginFrame: currentFrame %u is out of bounds",
251 m_r_context.currentFrame);
252 outData.isSwapchainValid = false;
253 return false;
254 }
255
256 if (m_r_context.inFlightFences.size() <= m_r_context.currentFrame) {
257 log(LogLevel::ERROR,
258 "beginFrame: inFlightFences not properly sized (size: %zu, currentFrame: %u)",
259 m_r_context.inFlightFences.size(), m_r_context.currentFrame);
260 outData.isSwapchainValid = false;
261 return false;
262 }
263
264 if (m_r_context.imageAvailableSemaphores.size() <= m_r_context.currentFrame) {
265 log(LogLevel::ERROR,
266 "beginFrame: imageAvailableSemaphores not properly sized (size: %zu, currentFrame: %u)",
267 m_r_context.imageAvailableSemaphores.size(), m_r_context.currentFrame);
268 outData.isSwapchainValid = false;
269 return false;
270 }
271
272 if (m_r_context.commandPools.size() <= m_r_context.currentFrame) {
273 log(LogLevel::ERROR,
274 "beginFrame: commandPools not properly sized (size: %zu, currentFrame: %u)",
275 m_r_context.commandPools.size(), m_r_context.currentFrame);
276 outData.isSwapchainValid = false;
277 return false;
278 }
279
280 if (m_r_context.commandBuffers.size() <= m_r_context.currentFrame) {
281 log(LogLevel::ERROR,
282 "beginFrame: commandBuffers not properly sized (size: %zu, currentFrame: %u)",
283 m_r_context.commandBuffers.size(), m_r_context.currentFrame);
284 outData.isSwapchainValid = false;
285 return false;
286 }
287
288 const uint64_t FENCE_TIMEOUT_NS = 1000000000;
289 VkResult fenceResult = vkWaitForFences(m_r_context.device, 1, &m_r_context.inFlightFences[m_r_context.currentFrame], VK_TRUE, FENCE_TIMEOUT_NS);
290
291 if (fenceResult == VK_TIMEOUT) {
292 log(LogLevel::ERROR, "GPU fence timeout - GPU may be hung or driver unresponsive. Requesting swapchain recreation.");
293 m_r_context.requestSwapchainRecreation = true;
294 outData.isSwapchainValid = false;
295 return false;
296 } else if (fenceResult != VK_SUCCESS) {
297 log(LogLevel::ERROR, "vkWaitForFences failed with result:", static_cast<int>(fenceResult));
298 throw_error("Failed to wait for GPU fence");
299 }
300
301 VkResult result = vkAcquireNextImageKHR(
302 m_r_context.device,
303 m_r_context.swapchain,
304 1000000000,
305 m_r_context.imageAvailableSemaphores[m_r_context.currentFrame],
306 VK_NULL_HANDLE,
307 &m_r_context.currentImageIndex
308 );
309
310 if (result == VK_TIMEOUT) {
311 m_r_context.requestSwapchainRecreation = true;
312 outData.isSwapchainValid = false;
313 return false;
314 }
315
316 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
317 m_p_swapchainManager->recreateSwapchain();
318 outData.isSwapchainValid = false;
319 log(LogLevel::WARNING, "Swapchain out of date");
320 return false;
321 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
322 throw_error("Failed to acquire swap chain image!");
323 }
324
325 vkResetFences(m_r_context.device, 1, &m_r_context.inFlightFences[m_r_context.currentFrame]);
326 vkResetCommandPool(m_r_context.device, m_r_context.commandPools[m_r_context.currentFrame], 0);
327
328 outData.commandBuffer = m_r_context.commandBuffers[m_r_context.currentFrame];
329 outData.frameIndex = m_r_context.currentFrame;
330 outData.imageIndex = m_r_context.currentImageIndex;
331 outData.isSwapchainValid = true;
332
333 VkCommandBufferBeginInfo beginInfo{};
334 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
335 vkBeginCommandBuffer(outData.commandBuffer, &beginInfo);
336
337 return true;
338 } catch (const std::exception& e) {
339 log(LogLevel::ERROR, "beginFrame failed");
341 return false;
342 }
343 }
344
345 void Renderer::renderScene(SceneRenderData& data, const vex::Entity cameraEntity, vex::Registry& registry, int frame, const std::vector<DebugVertex>* debugLines, bool isEditorMode) {
346 VkCommandBuffer cmd = data.commandBuffer;
347
348 auto now = std::chrono::high_resolution_clock::now();
349 currentTime = std::chrono::duration<float>(now - startTime).count();
350
351 if (m_r_context.supportsBindlessTextures) {
352 VkDescriptorSet globalSet = m_p_resources->getBindlessDescriptorSet();
353 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline->layout(),
354 1, 1, &globalSet, 0, nullptr);
355 }
356
357 if (m_lastUsedView != m_r_context.lowResColorView) {
358 updateScreenDescriptor(m_r_context.lowResColorView);
359 if (m_cachedImGuiDescriptor != VK_NULL_HANDLE) {
360 m_garbageDescriptors[m_r_context.currentFrame].push_back(m_cachedImGuiDescriptor);
361 }
362 m_cachedImGuiDescriptor = VK_NULL_HANDLE;
363 m_lastUsedView = m_r_context.lowResColorView;
364 }
365
366 glm::vec3 finalClearColor = m_r_context.m_environment.clearColor;
367 bool fogHandled = false;
368 vex::View<FogComponent> fogView(registry);
369
370 fogView.each([&](vex::Entity entity, FogComponent& fc) {
371 if (fogHandled) return;
372
373 m_sceneUBO.fogColor = glm::vec4(fc.color, fc.density);
374 m_sceneUBO.fogDistances = glm::vec2(fc.start, fc.end);
375
376 float skyMixFactor = glm::clamp(fc.density, 0.0f, 1.0f);
377 finalClearColor = glm::mix(finalClearColor, fc.color, skyMixFactor);
378
379 fogHandled = true;
380 });
381
382 std::vector<MeshComponent*> pendingMeshes;
384
385 preModelView.each([&](vex::Entity entity, TransformComponent& tc, MeshComponent& mesh) {
386 const std::string& path = mesh.meshData.meshPath;
387 if (!path.empty() && !m_p_meshManager->isMeshLoaded(path)) {
388 bool found = false;
389 for (auto* m : pendingMeshes) {
390 if (m->meshData.meshPath == path) {
391 found = true;
392 break;
393 }
394 }
395 if (!found) {
396 pendingMeshes.push_back(&mesh);
397 }
398 }
399 });
400
401 if (!pendingMeshes.empty()) {
402 m_p_meshManager->loadMeshesAsync(pendingMeshes);
403 }
404
406 m_r_context.lowResColorImage,
407 VK_IMAGE_LAYOUT_UNDEFINED,
408 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
409 0,
410 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
411 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
412 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
413
415 m_r_context.depthImage,
416 VK_IMAGE_LAYOUT_UNDEFINED,
417 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
418 0,
419 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
420 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
421 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
422
423 VkRenderingAttachmentInfo colorAttachment{};
424 colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
425 colorAttachment.imageView = m_r_context.lowResColorView;
426 colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
427 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
428 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
429 colorAttachment.clearValue.color = {{finalClearColor.x, finalClearColor.y, finalClearColor.z, 1.0f}};
430
431 VkRenderingAttachmentInfo depthAttachment{};
432 depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
433 depthAttachment.imageView = m_r_context.depthImageView;
434 depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
435 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
436 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
437 depthAttachment.clearValue.depthStencil = {1.0f, 0};
438
439 VkRenderingInfo renderingInfo{};
440 renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
441 renderingInfo.renderArea.offset = {0, 0};
442 renderingInfo.renderArea.extent = {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y};
443 renderingInfo.layerCount = 1;
444 renderingInfo.colorAttachmentCount = 1;
445 renderingInfo.pColorAttachments = &colorAttachment;
446 renderingInfo.pDepthAttachment = &depthAttachment;
447
448 try {
449 vkCmdBeginRendering(cmd, &renderingInfo);
450 } catch (const std::exception& e) {
452 return;
453 }
454
455 VkViewport viewport{};
456 viewport.width = (float)m_r_context.currentRenderResolution.x;
457 viewport.height = (float)m_r_context.currentRenderResolution.y;
458 viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f;
459 vkCmdSetViewport(cmd, 0, 1, &viewport);
460
461 VkRect2D scissor{};
462 scissor.extent = {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y};
463 vkCmdSetScissor(cmd, 0, 1, &scissor);
464
465 glm::mat4 view = glm::mat4(1.0f);
466 glm::mat4 proj = glm::mat4(1.0f);
467 auto& transform = registry.get<TransformComponent>(cameraEntity);
468 auto& camera = registry.get<CameraComponent>(cameraEntity);
469
470 if(!transform.isReady()){
471 transform.setRegistry(registry);
472 }
473
474 transform.recalculateMatrix();
475
476 view = glm::lookAt(transform.getWorldPosition(), transform.getWorldPosition() + transform.getForwardVector(), transform.getUpVector());
477 proj = glm::perspective(glm::radians(camera.fov), (float)m_r_context.currentRenderResolution.x / (float)m_r_context.currentRenderResolution.y, camera.nearPlane, camera.farPlane);
478 proj[1][1] *= -1;
479
480 m_sceneUBO.view = view;
481 m_sceneUBO.proj = proj;
482
483 m_sceneUBO.snapResolution = 1.f;
484 m_sceneUBO.jitterIntensity = 0.5f;
485
486 m_sceneUBO.enablePS1Effects = 0;
487
488 if(m_r_context.m_environment.vertexSnapping){
489 m_sceneUBO.enablePS1Effects |= PS1Effects::VERTEX_SNAPPING;
490 }
491
492 if(m_r_context.m_environment.passiveVertexJitter){
493 m_sceneUBO.enablePS1Effects |= PS1Effects::VERTEX_JITTER;
494 }
495
496 if(m_r_context.m_environment.affineWarping){
497 m_sceneUBO.enablePS1Effects |= PS1Effects::AFFINE_WARPING;
498 }
499
500 if(m_r_context.m_environment.screenQuantization){
501 m_sceneUBO.enablePS1Effects |= PS1Effects::SCREEN_QUANTIZATION;
502 }
503
504 if(m_r_context.m_environment.ntfsArtifacts){
505 m_sceneUBO.enablePS1Effects |= PS1Effects::NTSC_ARTIFACTS;
506 }
507
508 if(m_r_context.m_environment.gourardShading){
509 m_sceneUBO.enablePS1Effects |= PS1Effects::GOURAUD_SHADING;
510 }
511
512 if(m_r_context.m_environment.textureQuantization){
513 m_sceneUBO.enablePS1Effects |= PS1Effects::TEXTURE_QUANTIZATION;
514 }
515
516 if(m_r_context.m_environment.screenDither){
517 m_sceneUBO.enablePS1Effects |= PS1Effects::SCREEN_DITHER;
518 }
519
520 m_sceneUBO.renderResolution = m_r_context.currentRenderResolution;
521 m_sceneUBO.windowResolution = {m_r_context.swapchainExtent.width, m_r_context.swapchainExtent.height};
522 m_sceneUBO.time = currentTime;
523 m_sceneUBO.frame = frame;
524 m_sceneUBO.upscaleRatio = m_r_context.swapchainExtent.height / static_cast<float>(m_r_context.currentRenderResolution.y);
525
526 m_sceneUBO.ambientLight = glm::vec4(m_r_context.m_environment.ambientLight,1.0f);
527 m_sceneUBO.ambientLightStrength = m_r_context.m_environment.ambientLightStrength;
528 m_sceneUBO.sunLight = glm::vec4(m_r_context.m_environment.sunLight,1.0f);
529 m_sceneUBO.sunDirection = glm::vec4(m_r_context.m_environment.sunDirection,1.0f);
530
531 m_p_resources->updateSceneUBO(m_sceneUBO);
532
533 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_pipeline->get());
534
535 glm::vec3 cameraPos = extractCameraPosition(view);
536 Frustum camFrustum;
537 camFrustum.update(proj * view);
538
539 m_transparentTriangles.clear();
540 trnasMatrixes.clear();
541 uint32_t modelIndex = 0;
542
543 opaqueQueue.clear();
544 maskedQueue.clear();
545 transparentQueue.clear();
546 bMaskedQueue.clear();
547 bTransQueue.clear();
548 bEditorQueue.clear();
549 pMaskedQueue.clear();
550 pTransQueue.clear();
551
552 FrustumSoA frustumSimd;
553 static bool useAVX = HardwareInfo::HasAVX2();
554 if (useAVX) {
555 frustumSimd.init(camFrustum);
556 }
557
559 modelView.each([&](vex::Entity entity, TransformComponent& transform, MeshComponent& mesh) {
560 bool boundsNeedUpdate = transform.isDirty() || mesh.getIsFresh() || isEditorMode || mesh.worldRadius <= 0.0f;
561 glm::mat4 modelMatrix = transform.matrix();
562
563 if(!transform.isReady()){
564 transform.setRegistry(registry);
565 }
566
567 if(boundsNeedUpdate){
568 float scaleX = glm::length(glm::vec3(modelMatrix[0]));
569 float scaleY = glm::length(glm::vec3(modelMatrix[1]));
570 float scaleZ = glm::length(glm::vec3(modelMatrix[2]));
571
572 float maxScale = std::max({ scaleX, scaleY, scaleZ });
573
574 mesh.worldRadius = mesh.localRadius * maxScale;
575 mesh.worldCenter = (modelMatrix * glm::vec4(mesh.localCenter, 1.0f));
576
577 #if DEBUG
578 if(isEditorMode && frame > 0){
579 transform.convertRot();
580 }else if(isEditorMode){
581 transform.rotation = transform.getLocalRotation();
582 }
583 transform.rotation = glm::mod(transform.rotation, glm::vec3(360.0f));
584 #endif
585 }
586
587 if(!mesh.getIsFresh()) [[unlikely]]{
588 bool visible;
589 if (useAVX) [[likely]] {
590 visible = frustumSimd.testSphereAVX(mesh.worldCenter, mesh.worldRadius);
591 } else {
592 visible = camFrustum.testSphere(mesh.worldCenter, mesh.worldRadius);
593 }
594
595 if (!visible) return;
596 }
597
599 SceneLightsUBO lightUBO;
600 lightUBO.lightCount = 0;
601
602 lightView.each([&](vex::Entity lightEntity, TransformComponent& lightTransform, LightComponent& light) {
603 if (lightUBO.lightCount >= MAX_DYNAMIC_LIGHTS) {
604 return;
605 }
606
607 if(!lightTransform.isReady()){
608 lightTransform.setRegistry(registry);
609 }
610
611 float dist = glm::distance(lightTransform.getWorldPosition(), mesh.worldCenter);
612 if (dist < (light.radius + mesh.worldRadius)) {
613 auto& targetLight = lightUBO.lights[lightUBO.lightCount];
614 targetLight.position = glm::vec4(lightTransform.getWorldPosition(), light.radius);
615 targetLight.color = glm::vec4(light.color, light.intensity);
616 lightUBO.lightCount++;
617
618 if (lightUBO.lightCount == MAX_DYNAMIC_LIGHTS) {
619 log(LogLevel::WARNING, "Limit reached of per object dynamic lights, rest of the light wont affect this mesh.");
620 }
621 }
622 });
623
624 m_p_resources->updateLightUBO(m_r_context.currentFrame, modelIndex, lightUBO);
625
626 if (mesh.renderType == RenderType::OPAQUE) {
627 opaqueQueue.push_back({entity, modelIndex});
628 /* auto& vulkanMesh = m_p_meshManager->getVulkanMeshByMesh(mesh);
629 if (vulkanMesh) {
630 vulkanMesh->draw(cmd, m_p_pipeline->layout(), *m_p_resources, data.frameIndex, modelIndex, modelMatrix, mesh.color);
631 }
632 */
633 } else if (mesh.renderType == RenderType::MASKED) {
634 maskedQueue.push_back({entity, modelIndex});
635 } else if (mesh.renderType == RenderType::TRANSPARENT) {
636 transparentQueue.push_back({entity, modelIndex});
637 }
638
639 modelIndex++;
640 if(mesh.getIsFresh()) mesh.setRendered();
641 });
642
644 bView.each([&](vex::Entity entity, TransformComponent& tc, BillboardComponent& bill) {
645 uint32_t tIndex = m_p_resources->getTextureIndex(GetAssetPath(bill.texturePath));
646 if (tIndex == 0) tIndex = m_p_resources->getTextureIndex("default");
647
648 if (bill.isTransparent) bTransQueue.push_back({entity, tIndex});
649 else bMaskedQueue.push_back({entity, tIndex});
650 });
651
653 pView.each([&](vex::Entity entity, ParticleEmitterComponent& emit) {
654 if (emit.activeParticles.empty()) return;
655
656 if (emit.isTransparent) pTransQueue.push_back(entity);
657 else pMaskedQueue.push_back(entity);
658 });
659
660#if DEBUG
661if(isEditorMode){
662 vex::View<EditorBillboardComponent> bEditorView(registry);
663 bEditorView.each([&](vex::Entity entity, EditorBillboardComponent& bill) {
664 float offsetStep = 1.2f;
665 float currentOffset = -((bill.texturePaths.size() - 1) * offsetStep) / 2.0f;
666
667 for (const auto& path : bill.texturePaths) {
668 std::string correctPath = (GetExecutableDir() / path.c_str()).string();
669 uint32_t tIndex = m_p_resources->getTextureIndex(correctPath);
670 if (tIndex == 0) {
671 if (m_p_resources->loadTexture(correctPath, correctPath)) {
672 tIndex = m_p_resources->getTextureIndex(correctPath);
673 }
674 }
675 if (tIndex == 0) tIndex = m_p_resources->getTextureIndex("default");
676
677 bEditorQueue.push_back({entity, tIndex, currentOffset});
678 currentOffset += offsetStep;
679 }
680 });
681}
682#endif
683
684 for (const auto& item : opaqueQueue) {
685 auto& mesh = registry.get<MeshComponent>(item.entity);
686 auto& transform = registry.get<TransformComponent>(item.entity);
687 auto& vulkanMesh = m_p_meshManager->getVulkanMeshByMesh(mesh);
688
689 if (vulkanMesh) {
690 vulkanMesh->draw(cmd, m_p_pipeline->layout(), *m_p_resources, data.frameIndex, item.modelIndex, transform.matrix(), mesh);
691 }
692 }
693
694 #if DEBUG
695 if(isEditorMode){
697 camView.each([&](vex::Entity entity, TransformComponent& transform, CameraComponent& cam) {
698 if(cameraEntity == entity) {
699 return;
700 }
701
702 glm::vec3 worldScale = transform.getWorldScale();
703 transform.setWorldScale(glm::vec3(1.f));
704
705 if(m_editorCameraVulkanMesh->getNumOfInstances() <= 0){
706 m_editorCameraMesh.loadFromRawFile("../Assets/meshes/editorCamera.obj");
707 m_editorCameraVulkanMesh->upload(m_editorCameraMesh);
708 m_editorCameraVulkanMesh->addInstance();
709 }else{
710 auto mc = MeshComponent{};
711 mc.color = glm::vec4(0.3f, 1.0f, 0.5f, 1.0f);
712 m_editorCameraVulkanMesh->draw(cmd, m_p_pipeline->layout(), *m_p_resources, data.frameIndex, 0, transform.matrix(), mc);
713 }
714 transform.setWorldScale(worldScale);
715 });
716 }
717 #endif
718
719 if (!maskedQueue.empty()) {
720 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_maskPipeline->get());
721
722 for (const auto& item : maskedQueue) {
723 auto& mesh = registry.get<MeshComponent>(item.entity);
724 auto& transform = registry.get<TransformComponent>(item.entity);
725 auto& vulkanMesh = m_p_meshManager->getVulkanMeshByMesh(mesh);
726
727 if (vulkanMesh) {
728 vulkanMesh->draw(cmd, m_p_maskPipeline->layout(), *m_p_resources, data.frameIndex, item.modelIndex, transform.matrix(), mesh);
729 }
730 }
731 }
732
733 uint32_t currentParticleOffset = 0;
734 ParticleGPUData* particleMappedData = static_cast<ParticleGPUData*>(m_p_resources->getParticleMappedData(data.frameIndex));
735 VkDescriptorSet particleSSBODescriptorSet = m_p_resources->getParticleDescriptorSet(data.frameIndex);
736
737 if (!bMaskedQueue.empty()) {
738 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->get());
739 VkViewport viewport{0.0f, 0.0f, (float)m_r_context.currentRenderResolution.x, (float)m_r_context.currentRenderResolution.y, 0.0f, 1.0f};
740 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
741 vkCmdSetViewport(cmd, 0, 1, &viewport);
742 vkCmdSetScissor(cmd, 0, 1, &scissor);
743 VkDescriptorSet globalSet_bMaskedQueue = m_p_resources->getUBODescriptorSet(data.frameIndex);
744 uint32_t dynamicOffset_bMaskedQueue = 0;
745 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 0, 1, &globalSet_bMaskedQueue, 1, &dynamicOffset_bMaskedQueue);
746 if (m_r_context.supportsBindlessTextures) {
747 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
748 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
749 }
750 for (const auto& item : bMaskedQueue) {
751 auto& trans = registry.get<TransformComponent>(item.entity);
752 auto& bill = registry.get<BillboardComponent>(item.entity);
753
754 BillboardPushData push{};
755 push.pos = trans.getWorldPosition();
756 push.sx = bill.size.x;
757 push.sy = bill.size.y;
758 push.tID = item.texID;
759 push.unlit = bill.isUnlit ? 1 : 0;
760 push.col = glm::vec4(bill.color.r, bill.color.g, bill.color.b, bill.color.a);
761
762 vkCmdPushConstants(cmd, m_p_billboardMaskedPipeline->layout(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(BillboardPushData), &push);
763
764 VkDescriptorSet globalSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
765 uint32_t dynamicOffset = 0;
766 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 0, 1, &globalSet, 1, &dynamicOffset);
767
768 if (m_r_context.supportsBindlessTextures) {
769 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
770 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
771 } else {
772 VkDescriptorSet texSet = m_p_resources->getTextureDescriptorSet(data.frameIndex, item.texID);
773 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &texSet, 0, nullptr);
774 }
775
776 vkCmdDraw(cmd, 6, 1, 0, 0);
777 }
778 }
779
780#if DEBUG
781 if (!bEditorQueue.empty()) {
782 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->get());
783 VkViewport viewport{0.0f, 0.0f, (float)m_r_context.currentRenderResolution.x, (float)m_r_context.currentRenderResolution.y, 0.0f, 1.0f};
784 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
785 vkCmdSetViewport(cmd, 0, 1, &viewport);
786 vkCmdSetScissor(cmd, 0, 1, &scissor);
787 VkDescriptorSet globalSet_bMaskedQueue = m_p_resources->getUBODescriptorSet(data.frameIndex);
788 uint32_t dynamicOffset_bMaskedQueue = 0;
789 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 0, 1, &globalSet_bMaskedQueue, 1, &dynamicOffset_bMaskedQueue);
790 if (m_r_context.supportsBindlessTextures) {
791 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
792 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
793 }
794 for (const auto& item : bEditorQueue) {
795 auto* trans = registry.try_get<TransformComponent>(item.entity);
796
797 BillboardPushData push{};
798 glm::vec3 cameraRight = glm::vec3(view[0][0], view[1][0], view[2][0]);
799 glm::vec3 basePos = trans ? trans->getWorldPosition() : glm::vec3(0.0f);
800 push.pos = basePos + (cameraRight * item.offsetX);
801 push.sx = 1.0f;
802 push.sy = 1.0f;
803 push.tID = item.texID;
804 push.unlit = 1;
805 push.col = glm::vec4(1,1,1,1);
806
807 vkCmdPushConstants(cmd, m_p_billboardMaskedPipeline->layout(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(BillboardPushData), &push);
808
809 VkDescriptorSet globalSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
810 uint32_t dynamicOffset = 0;
811 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 0, 1, &globalSet, 1, &dynamicOffset);
812
813 if (m_r_context.supportsBindlessTextures) {
814 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
815 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
816 } else {
817 VkDescriptorSet texSet = m_p_resources->getTextureDescriptorSet(data.frameIndex, item.texID);
818 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardMaskedPipeline->layout(), 1, 1, &texSet, 0, nullptr);
819 }
820
821 vkCmdDraw(cmd, 6, 1, 0, 0);
822 }
823 }
824#endif
825
826 if (!pMaskedQueue.empty()) {
827 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleMaskedPipeline->get());
828 VkViewport viewport{0.0f, 0.0f, (float)m_r_context.currentRenderResolution.x, (float)m_r_context.currentRenderResolution.y, 0.0f, 1.0f};
829 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
830 vkCmdSetViewport(cmd, 0, 1, &viewport);
831 vkCmdSetScissor(cmd, 0, 1, &scissor);
832
833 VkDescriptorSet globalSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
834 uint32_t dynamicOffset = 0;
835 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleMaskedPipeline->layout(), 0, 1, &globalSet, 1, &dynamicOffset);
836
837 if (m_r_context.supportsBindlessTextures) {
838 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
839 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleMaskedPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
840 }
841
842 for (auto e : pMaskedQueue) {
843 auto& emit = registry.get<ParticleEmitterComponent>(e);
844 uint32_t pCount = static_cast<uint32_t>(emit.activeParticles.size());
845
846 if (currentParticleOffset + pCount > 100000) {
847 vex::log(LogLevel::WARNING, "Max particle limit reached! Skipping further particles.");
848 continue;
849 }
850
851
852
853
854 uint32_t tIndex = m_p_resources->getTextureIndex(GetAssetPath(emit.texturePath));
855 if (tIndex == 0) tIndex = m_p_resources->getTextureIndex("default");
856 for(auto& p : emit.activeParticles) p.textureID = tIndex;
857
858 memcpy(particleMappedData + currentParticleOffset, emit.activeParticles.data(), pCount * sizeof(ParticleGPUData));
859
860 if (!m_r_context.supportsBindlessTextures) {
861 VkDescriptorSet texSet = m_p_resources->getTextureDescriptorSet(data.frameIndex, tIndex);
862 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleMaskedPipeline->layout(), 1, 1, &texSet, 0, nullptr);
863 }
864
865 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleMaskedPipeline->layout(), 2, 1, &particleSSBODescriptorSet, 0, nullptr);
866
867 vkCmdDraw(cmd, 6, pCount, 0, currentParticleOffset);
868 currentParticleOffset += pCount;
869 }
870 }
871
872 vkCmdEndRendering(cmd);
873
874 transitionImageLayout(cmd, m_r_context.accumImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
875 transitionImageLayout(cmd, m_r_context.revealImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
876
877 VkRenderingAttachmentInfo transAttachments[2]{};
878
879 transAttachments[0].sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
880 transAttachments[0].imageView = m_r_context.accumView;
881 transAttachments[0].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
882 transAttachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
883 transAttachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
884 transAttachments[0].clearValue.color = {{0.0f, 0.0f, 0.0f, 0.0f}};
885
886 transAttachments[1].sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
887 transAttachments[1].imageView = m_r_context.revealView;
888 transAttachments[1].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
889 transAttachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
890 transAttachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
891 transAttachments[1].clearValue.color = {{1.0f, 0.0f, 0.0f, 0.0f}};
892
893 VkRenderingAttachmentInfo transDepthAttachment{};
894 transDepthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
895 transDepthAttachment.imageView = m_r_context.depthImageView;
896 transDepthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
897 transDepthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
898 transDepthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
899
900 VkRenderingInfo transRenderingInfo{};
901 transRenderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
902 transRenderingInfo.renderArea.offset = {0, 0};
903 transRenderingInfo.renderArea.extent = {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y};
904 transRenderingInfo.layerCount = 1;
905 transRenderingInfo.colorAttachmentCount = 2;
906 transRenderingInfo.pColorAttachments = transAttachments;
907 transRenderingInfo.pDepthAttachment = &transDepthAttachment;
908
909 vkCmdBeginRendering(cmd, &transRenderingInfo);
910
911 if (!transparentQueue.empty()) {
912 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_transPipeline->get());
913
914 for (const auto& item : transparentQueue) {
915 auto& mesh = registry.get<MeshComponent>(item.entity);
916 auto& transform = registry.get<TransformComponent>(item.entity);
917 auto& vulkanMesh = m_p_meshManager->getVulkanMeshByMesh(mesh);
918
919 if (vulkanMesh) {
920 vulkanMesh->draw(cmd, m_p_transPipeline->layout(), *m_p_resources, data.frameIndex, item.modelIndex, transform.matrix(), mesh);
921 }
922 }
923 }
924
925 if (!bTransQueue.empty()) {
926 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardTransPipeline->get());
927 VkViewport viewport{0.0f, 0.0f, (float)m_r_context.currentRenderResolution.x, (float)m_r_context.currentRenderResolution.y, 0.0f, 1.0f};
928 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
929 vkCmdSetViewport(cmd, 0, 1, &viewport);
930 vkCmdSetScissor(cmd, 0, 1, &scissor);
931 VkDescriptorSet globalSet_bTransQueue = m_p_resources->getUBODescriptorSet(data.frameIndex);
932 uint32_t dynamicOffset_bTransQueue = 0;
933 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardTransPipeline->layout(), 0, 1, &globalSet_bTransQueue, 1, &dynamicOffset_bTransQueue);
934 if (m_r_context.supportsBindlessTextures) {
935 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
936 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardTransPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
937 }
938 for (const auto& item : bTransQueue) {
939 auto& trans = registry.get<TransformComponent>(item.entity);
940 auto& bill = registry.get<BillboardComponent>(item.entity);
941
942 BillboardPushData push{};
943 push.pos = trans.getWorldPosition();
944 push.sx = bill.size.x;
945 push.sy = bill.size.y;
946 push.tID = item.texID;
947 push.unlit = bill.isUnlit ? 1 : 0;
948 push.col = glm::vec4(bill.color.r, bill.color.g, bill.color.b, bill.color.a);
949
950 vkCmdPushConstants(cmd, m_p_billboardTransPipeline->layout(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(BillboardPushData), &push);
951
952
953
954 if (!m_r_context.supportsBindlessTextures) {
955 VkDescriptorSet texSet = m_p_resources->getTextureDescriptorSet(data.frameIndex, item.texID);
956 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_billboardTransPipeline->layout(), 1, 1, &texSet, 0, nullptr);
957 }
958
959 vkCmdDraw(cmd, 6, 1, 0, 0);
960 }
961 }
962
963 if (!pTransQueue.empty()) {
964 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleTransPipeline->get());
965 VkViewport viewport{0.0f, 0.0f, (float)m_r_context.currentRenderResolution.x, (float)m_r_context.currentRenderResolution.y, 0.0f, 1.0f};
966 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
967 vkCmdSetViewport(cmd, 0, 1, &viewport);
968 vkCmdSetScissor(cmd, 0, 1, &scissor);
969
970 VkDescriptorSet globalSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
971 uint32_t dynamicOffset = 0;
972 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleTransPipeline->layout(), 0, 1, &globalSet, 1, &dynamicOffset);
973
974 if (m_r_context.supportsBindlessTextures) {
975 VkDescriptorSet bindlessSet = m_p_resources->getBindlessDescriptorSet();
976 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleTransPipeline->layout(), 1, 1, &bindlessSet, 0, nullptr);
977 }
978
979 for (auto e : pTransQueue) {
980 auto& emit = registry.get<ParticleEmitterComponent>(e);
981 uint32_t pCount = static_cast<uint32_t>(emit.activeParticles.size());
982
983 if (currentParticleOffset + pCount > 100000) {
984 vex::log(LogLevel::WARNING, "Max particle limit reached! Skipping further particles.");
985 continue;
986 }
987
988
989
990
991 uint32_t tIndex = m_p_resources->getTextureIndex(GetAssetPath(emit.texturePath));
992 if (tIndex == 0) tIndex = m_p_resources->getTextureIndex("default");
993 for(auto& p : emit.activeParticles) p.textureID = tIndex;
994
995 memcpy(particleMappedData + currentParticleOffset, emit.activeParticles.data(), pCount * sizeof(ParticleGPUData));
996
997 if (!m_r_context.supportsBindlessTextures) {
998 VkDescriptorSet texSet = m_p_resources->getTextureDescriptorSet(data.frameIndex, tIndex);
999 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleTransPipeline->layout(), 1, 1, &texSet, 0, nullptr);
1000 }
1001
1002 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_particleTransPipeline->layout(), 2, 1, &particleSSBODescriptorSet, 0, nullptr);
1003
1004 vkCmdDraw(cmd, 6, pCount, 0, currentParticleOffset);
1005 currentParticleOffset += pCount;
1006 }
1007 }
1008
1009 vkCmdEndRendering(cmd);
1010
1011 transitionImageLayout(cmd, m_r_context.accumImage, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1012 transitionImageLayout(cmd, m_r_context.revealImage, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1013
1014 transitionImageLayout(cmd, m_r_context.uiImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
1015
1016 VkRenderingAttachmentInfo uiColorAttachment = colorAttachment;
1017 uiColorAttachment.imageView = m_r_context.uiView;
1018 uiColorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1019 uiColorAttachment.clearValue.color = {{0.0f, 0.0f, 0.0f, 0.0f}};
1020
1021 VkRenderingAttachmentInfo uiDepthAttachment = depthAttachment;
1022 uiDepthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
1023 uiDepthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
1024
1025 VkRenderingInfo uiRenderingInfo = renderingInfo;
1026 uiRenderingInfo.pColorAttachments = &uiColorAttachment;
1027 uiRenderingInfo.pDepthAttachment = &uiDepthAttachment;
1028
1029 vkCmdBeginRendering(cmd, &uiRenderingInfo);
1030
1031 if (frame != 0) {
1032 m_uiObjects.clear();
1033 vex::View<UiComponent> uiView(registry);
1034 uiView.each([&](vex::Entity entity, UiComponent& uiComp) {
1035 if(uiComp.m_vexUI->isInitialized()) {
1036 m_uiObjects.emplace_back(uiComp);
1037 }
1038 });
1039 std::sort(m_uiObjects.begin(), m_uiObjects.end(), [](const UiComponent &f, const UiComponent &s) { return f.m_vexUI->getZIndex() < s.m_vexUI->getZIndex(); });
1040
1041 for(const auto& uiObject : m_uiObjects) {
1042 if(uiObject.visible){
1043 uiObject.m_vexUI->render(cmd, m_p_uiPipeline->get(), m_p_uiPipeline->layout(), data.frameIndex);
1044 }
1045 }
1046 }
1047
1048 #if DEBUG
1049 if(debugLines && !debugLines->empty()) {
1050 renderDebug(cmd, data.frameIndex, *debugLines);
1051 }
1052 #endif
1053
1054 vkCmdEndRendering(cmd);
1055
1056 transitionImageLayout(cmd, m_r_context.lowResColorImage,
1057 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1058 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1059 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
1060 VK_ACCESS_SHADER_READ_BIT,
1061 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1062 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1063
1064 transitionImageLayout(cmd, m_r_context.uiImage,
1065 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1066 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1067 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
1068 VK_ACCESS_SHADER_READ_BIT,
1069 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1070 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1071 }
1072
1074 VulkanImGUIWrapper& vkUI = static_cast<VulkanImGUIWrapper&>(ui);
1075
1076 auto& currentGarbage = m_garbageDescriptors[m_r_context.currentFrame];
1077 if (!currentGarbage.empty()) {
1078 for (VkDescriptorSet ds : currentGarbage) {
1079 if(ds != VK_NULL_HANDLE){
1080 vkUI.removeTexture(ds);
1081 }
1082 }
1083 currentGarbage.clear();
1084 }
1085
1086 if (m_cachedImGuiDescriptor == VK_NULL_HANDLE) {
1087 if (m_r_context.lowResColorView != VK_NULL_HANDLE) {
1088 m_cachedImGuiDescriptor = vkUI.addTexture(
1089 m_screenSampler,
1090 m_r_context.gameViewView,
1091 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
1092 );
1093 }
1094 }
1095 return m_cachedImGuiDescriptor;
1096 }
1097
1098 void Renderer::composeFrame(SceneRenderData& data, ImGUIWrapper& ui, bool isEditorMode) {
1099 VkCommandBuffer cmd = data.commandBuffer;
1100
1101 transitionImageLayout(cmd, m_r_context.compositeImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
1102
1103 VkRenderingAttachmentInfo preCompAtt{};
1104 preCompAtt.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
1105 preCompAtt.imageView = m_r_context.compositeView;
1106 preCompAtt.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1107 preCompAtt.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1108 preCompAtt.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1109 preCompAtt.clearValue.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
1110
1111 VkRenderingInfo preCompInfo{};
1112 preCompInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
1113 preCompInfo.renderArea.offset = {0, 0};
1114 preCompInfo.renderArea.extent = {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y};
1115 preCompInfo.layerCount = 1;
1116 preCompInfo.colorAttachmentCount = 1;
1117 preCompInfo.pColorAttachments = &preCompAtt;
1118
1119 vkCmdBeginRendering(cmd, &preCompInfo);
1120 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_compositePipeline->get());
1121 VkViewport compViewport{};
1122 compViewport.x = 0.0f;
1123 compViewport.y = 0.0f;
1124 compViewport.width = static_cast<float>(m_r_context.currentRenderResolution.x);
1125 compViewport.height = static_cast<float>(m_r_context.currentRenderResolution.y);
1126 compViewport.minDepth = 0.0f;
1127 compViewport.maxDepth = 1.0f;
1128 vkCmdSetViewport(cmd, 0, 1, &compViewport);
1129 VkRect2D compScissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
1130 vkCmdSetScissor(cmd, 0, 1, &compScissor);
1131 VkDescriptorSet compSceneSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
1132 uint32_t compDynamicOffset = 0;
1133 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_compositePipeline->layout(), 0, 1, &compSceneSet, 1, &compDynamicOffset);
1134 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_compositePipeline->layout(), 1, 1, &m_screenDescriptorSet, 0, nullptr);
1135 vkCmdDraw(cmd, 3, 1, 0, 0);
1136 vkCmdEndRendering(cmd);
1137
1138 {
1139 VkImageMemoryBarrier barrier{};
1140 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1141 barrier.image = m_r_context.compositeImage;
1142 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1143 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1144 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1145 barrier.subresourceRange.baseArrayLayer = 0;
1146 barrier.subresourceRange.layerCount = 1;
1147 barrier.subresourceRange.levelCount = 1;
1148
1149 int32_t mipWidth = m_r_context.currentRenderResolution.x;
1150 int32_t mipHeight = m_r_context.currentRenderResolution.y;
1151
1152 for (uint32_t i = 1; i < 3; i++) {
1153 barrier.subresourceRange.baseMipLevel = i - 1;
1154 barrier.oldLayout = (i == 1) ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1155 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1156 barrier.srcAccessMask = (i == 1) ? VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT : VK_ACCESS_TRANSFER_WRITE_BIT;
1157 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1158
1159 vkCmdPipelineBarrier(cmd,
1160 (i == 1) ? VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT,
1161 VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
1162 0, nullptr, 0, nullptr, 1, &barrier);
1163
1164 barrier.subresourceRange.baseMipLevel = i;
1165 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1166 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1167 barrier.srcAccessMask = 0;
1168 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1169
1170 vkCmdPipelineBarrier(cmd,
1171 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
1172 0, nullptr, 0, nullptr, 1, &barrier);
1173
1174 VkImageBlit blit{};
1175 blit.srcOffsets[0] = {0, 0, 0};
1176 blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
1177 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1178 blit.srcSubresource.mipLevel = i - 1;
1179 blit.srcSubresource.baseArrayLayer = 0;
1180 blit.srcSubresource.layerCount = 1;
1181
1182 blit.dstOffsets[0] = {0, 0, 0};
1183 blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
1184 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1185 blit.dstSubresource.mipLevel = i;
1186 blit.dstSubresource.baseArrayLayer = 0;
1187 blit.dstSubresource.layerCount = 1;
1188
1189 vkCmdBlitImage(cmd,
1190 m_r_context.compositeImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1191 m_r_context.compositeImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1192 1, &blit, VK_FILTER_LINEAR);
1193
1194 barrier.subresourceRange.baseMipLevel = i - 1;
1195 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1196 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1197 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1198 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1199
1200 vkCmdPipelineBarrier(cmd,
1201 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
1202 0, nullptr, 0, nullptr, 1, &barrier);
1203
1204 if (mipWidth > 1) mipWidth /= 2;
1205 if (mipHeight > 1) mipHeight /= 2;
1206 }
1207
1208 barrier.subresourceRange.baseMipLevel = 2;
1209 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1210 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1211 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1212 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1213
1214 vkCmdPipelineBarrier(cmd,
1215 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
1216 0, nullptr, 0, nullptr, 1, &barrier);
1217 }
1218
1219
1220 if (isEditorMode) [[unlikely]] {
1221 transitionImageLayout(cmd, m_r_context.gameViewImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
1222
1223 VkRenderingAttachmentInfo compAtt{};
1224 compAtt.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
1225 compAtt.imageView = m_r_context.gameViewView;
1226 compAtt.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1227 compAtt.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1228 compAtt.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1229 compAtt.clearValue.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
1230
1231 VkRenderingInfo compInfo{};
1232 compInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
1233 compInfo.renderArea.offset = {0, 0};
1234 compInfo.renderArea.extent = {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y};
1235 compInfo.layerCount = 1;
1236 compInfo.colorAttachmentCount = 1;
1237 compInfo.pColorAttachments = &compAtt;
1238
1239 vkCmdBeginRendering(cmd, &compInfo);
1240 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->get());
1241 VkViewport viewport{};
1242 viewport.x = 0.0f;
1243 viewport.y = 0.0f;
1244 viewport.width = static_cast<float>(m_r_context.currentRenderResolution.x);
1245 viewport.height = static_cast<float>(m_r_context.currentRenderResolution.y);
1246 viewport.minDepth = 0.0f;
1247 viewport.maxDepth = 1.0f;
1248 vkCmdSetViewport(cmd, 0, 1, &viewport);
1249 VkRect2D scissor{{0, 0}, {m_r_context.currentRenderResolution.x, m_r_context.currentRenderResolution.y}};
1250 vkCmdSetScissor(cmd, 0, 1, &scissor);
1251 VkDescriptorSet sceneSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
1252 uint32_t dynamicOffset = 0;
1253 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->layout(), 0, 1, &sceneSet, 1, &dynamicOffset);
1254 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->layout(), 1, 1, &m_crtDescriptorSet, 0, nullptr);
1255 vkCmdDraw(cmd, 3, 1, 0, 0);
1256 vkCmdEndRendering(cmd);
1257
1258 transitionImageLayout(cmd, m_r_context.gameViewImage, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
1259 }
1260
1261 transitionImageLayout(cmd, m_r_context.swapchainImages[data.imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
1262
1263 VkRenderingAttachmentInfo colorAttachment{};
1264 colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
1265 colorAttachment.imageView = m_r_context.swapchainImageViews[data.imageIndex];
1266 colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1267 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1268 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1269 colorAttachment.clearValue.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
1270
1271 VkRenderingInfo renderingInfo{};
1272 renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
1273 renderingInfo.renderArea.offset = {0, 0};
1274 renderingInfo.renderArea.extent = m_r_context.swapchainExtent;
1275 renderingInfo.layerCount = 1;
1276 renderingInfo.colorAttachmentCount = 1;
1277 renderingInfo.pColorAttachments = &colorAttachment;
1278
1279 vkCmdBeginRendering(cmd, &renderingInfo);
1280
1281 if (isEditorMode) [[unlikely]] {
1282 VulkanImGUIWrapper& vkUI = static_cast<VulkanImGUIWrapper&>(ui);
1283 if (m_cachedImGuiDescriptor == VK_NULL_HANDLE) getImGuiTextureID(ui);
1284 data.imguiTextureID = m_cachedImGuiDescriptor;
1285 vkUI.draw(cmd);
1286 } else [[likely]] {
1287 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->get());
1288
1289 VkViewport viewport{};
1290 viewport.x = 0.0f;
1291 viewport.y = 0.0f;
1292 viewport.width = static_cast<float>(m_r_context.swapchainExtent.width);
1293 viewport.height = static_cast<float>(m_r_context.swapchainExtent.height);
1294 viewport.minDepth = 0.0f;
1295 viewport.maxDepth = 1.0f;
1296 vkCmdSetViewport(cmd, 0, 1, &viewport);
1297
1298 VkRect2D scissor{};
1299 scissor.offset = {0, 0};
1300 scissor.extent = m_r_context.swapchainExtent;
1301 vkCmdSetScissor(cmd, 0, 1, &scissor);
1302
1303 VkDescriptorSet sceneSet = m_p_resources->getUBODescriptorSet(data.frameIndex);
1304 uint32_t dynamicOffset = 0;
1305 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->layout(), 0, 1, &sceneSet, 1, &dynamicOffset);
1306 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_p_fullscreenPipeline->layout(), 1, 1, &m_crtDescriptorSet, 0, nullptr);
1307 vkCmdDraw(cmd, 3, 1, 0, 0);
1308 }
1309
1310 vkCmdEndRendering(cmd);
1311 transitionImageLayout(cmd, m_r_context.swapchainImages[data.imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
1312 }
1313
1315 if (!data.isSwapchainValid) return;
1316
1317 vkEndCommandBuffer(data.commandBuffer);
1318
1319 if (!m_r_context.isValidFrameIndex(data.frameIndex)) {
1320 log(LogLevel::ERROR,
1321 "endFrame: frameIndex %u exceeds MAX_FRAMES_IN_FLIGHT (%u)",
1322 data.frameIndex, m_r_context.MAX_FRAMES_IN_FLIGHT);
1323 return;
1324 }
1325
1326 if (!m_r_context.isValidImageIndex(data.imageIndex)) {
1327 log(LogLevel::ERROR,
1328 "endFrame: imageIndex %u exceeds swapchain image count (%zu)",
1329 data.imageIndex, m_r_context.swapchainImages.size());
1330 return;
1331 }
1332
1333 if (m_r_context.imageAvailableSemaphores.size() <= data.frameIndex) {
1334 log(LogLevel::ERROR,
1335 "endFrame: imageAvailableSemaphores not properly sized (size: %zu, frameIndex: %u)",
1336 m_r_context.imageAvailableSemaphores.size(), data.frameIndex);
1337 return;
1338 }
1339
1340 if (m_r_context.renderFinishedSemaphores.size() <= data.imageIndex) {
1341 log(LogLevel::ERROR,
1342 "endFrame: renderFinishedSemaphores not properly sized (size: %zu, imageIndex: %u)",
1343 m_r_context.renderFinishedSemaphores.size(), data.imageIndex);
1344 return;
1345 }
1346
1347 if (m_r_context.inFlightFences.size() <= m_r_context.currentFrame) {
1348 log(LogLevel::ERROR,
1349 "endFrame: inFlightFences not properly sized (size: %zu, currentFrame: %u)",
1350 m_r_context.inFlightFences.size(), m_r_context.currentFrame);
1351 return;
1352 }
1353
1354 VkSubmitInfo submitInfo{};
1355 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1356
1357 VkSemaphore waitSemaphores[] = {m_r_context.imageAvailableSemaphores[data.frameIndex]};
1358 VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
1359 submitInfo.waitSemaphoreCount = 1;
1360 submitInfo.pWaitSemaphores = waitSemaphores;
1361 submitInfo.pWaitDstStageMask = waitStages;
1362 submitInfo.commandBufferCount = 1;
1363 submitInfo.pCommandBuffers = &data.commandBuffer;
1364
1365 VkSemaphore signalSemaphores[] = {m_r_context.renderFinishedSemaphores[data.imageIndex]};
1366 submitInfo.signalSemaphoreCount = 1;
1367 submitInfo.pSignalSemaphores = signalSemaphores;
1368
1369 try {
1370 if (vkQueueSubmit(m_r_context.graphicsQueue, 1, &submitInfo, m_r_context.inFlightFences[m_r_context.currentFrame]) != VK_SUCCESS) {
1371 throw_error("Failed to submit draw command buffer!");
1372 }
1373
1374 VkPresentInfoKHR presentInfo{};
1375 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1376 presentInfo.waitSemaphoreCount = 1;
1377 presentInfo.pWaitSemaphores = signalSemaphores;
1378
1379 VkSwapchainKHR swapchains[] = {m_r_context.swapchain};
1380 presentInfo.swapchainCount = 1;
1381 presentInfo.pSwapchains = swapchains;
1382 presentInfo.pImageIndices = &m_r_context.currentImageIndex;
1383
1384 VkResult result = vkQueuePresentKHR(m_r_context.presentQueue, &presentInfo);
1385
1386 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
1387 m_p_swapchainManager->recreateSwapchain();
1388 } else if (result != VK_SUCCESS) {
1389 throw_error("Failed to present swap chain image!");
1390 }
1391 } catch (const std::exception& e) {
1392 log(LogLevel::ERROR, "Queue Submit/Present failed");
1394 }
1395
1396 m_r_context.currentFrame = (m_r_context.currentFrame + 1) % m_r_context.MAX_FRAMES_IN_FLIGHT;
1397 }
1398
1399 void Renderer::updateScreenDescriptor(VkImageView view) {
1400 VkDescriptorImageInfo opaqueInfo{};
1401 opaqueInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1402 opaqueInfo.imageView = view;
1403 opaqueInfo.sampler = m_screenSampler;
1404
1405 VkDescriptorImageInfo accumInfo{};
1406 accumInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1407 accumInfo.imageView = m_r_context.accumView;
1408 accumInfo.sampler = m_screenSampler;
1409
1410 VkDescriptorImageInfo revealInfo{};
1411 revealInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1412 revealInfo.imageView = m_r_context.revealView;
1413 revealInfo.sampler = m_screenSampler;
1414
1415 VkDescriptorImageInfo uiInfo{};
1416 uiInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1417 uiInfo.imageView = m_r_context.uiView;
1418 uiInfo.sampler = m_screenSampler;
1419
1420 VkDescriptorImageInfo lutInfo{};
1421 lutInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1422 lutInfo.imageView = m_r_context.colorLutView;
1423 lutInfo.sampler = m_linearSampler;
1424
1425 std::array<VkWriteDescriptorSet, 5> writes{};
1426
1427 writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1428 writes[0].dstSet = m_screenDescriptorSet;
1429 writes[0].dstBinding = 0;
1430 writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1431 writes[0].descriptorCount = 1;
1432 writes[0].pImageInfo = &opaqueInfo;
1433
1434 writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1435 writes[1].dstSet = m_screenDescriptorSet;
1436 writes[1].dstBinding = 1;
1437 writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1438 writes[1].descriptorCount = 1;
1439 writes[1].pImageInfo = &accumInfo;
1440
1441 writes[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1442 writes[2].dstSet = m_screenDescriptorSet;
1443 writes[2].dstBinding = 2;
1444 writes[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1445 writes[2].descriptorCount = 1;
1446 writes[2].pImageInfo = &revealInfo;
1447
1448 writes[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1449 writes[3].dstSet = m_screenDescriptorSet;
1450 writes[3].dstBinding = 3;
1451 writes[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1452 writes[3].descriptorCount = 1;
1453 writes[3].pImageInfo = &uiInfo;
1454
1455 writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1456 writes[4].dstSet = m_screenDescriptorSet;
1457 writes[4].dstBinding = 4;
1458 writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1459 writes[4].descriptorCount = 1;
1460 writes[4].pImageInfo = &lutInfo;
1461
1462 vkUpdateDescriptorSets(m_r_context.device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
1463
1464 VkDescriptorImageInfo compInfo{};
1465 compInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1466 if (m_r_context.compositeView != VK_NULL_HANDLE) {
1467 compInfo.imageView = m_r_context.compositeView;
1468 } else {
1469 compInfo.imageView = view;
1470 }
1471 compInfo.sampler = m_screenSampler;
1472
1473 VkDescriptorImageInfo compLinearInfo = compInfo;
1474 compLinearInfo.sampler = m_linearSampler;
1475
1476 VkWriteDescriptorSet compWrite[4] = {};
1477 for(int i = 0; i < 4; i++) {
1478 compWrite[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1479 compWrite[i].dstSet = m_crtDescriptorSet;
1480 compWrite[i].dstBinding = i;
1481 compWrite[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1482 compWrite[i].descriptorCount = 1;
1483 compWrite[i].pImageInfo = (i == 1) ? &compLinearInfo : &compInfo;
1484 }
1485
1486 vkUpdateDescriptorSets(m_r_context.device, 4, compWrite, 0, nullptr);
1487
1488 }
1489
1490 void Renderer::transitionImageLayout(VkCommandBuffer cmd, VkImage image,
1491 VkImageLayout oldLayout, VkImageLayout newLayout,
1492 VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
1493 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage) {
1494 VkImageMemoryBarrier barrier{};
1495 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1496 barrier.oldLayout = oldLayout;
1497 barrier.newLayout = newLayout;
1498 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1499 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1500 barrier.image = image;
1501 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ||
1502 newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) {
1503 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1504 if (m_r_context.depthFormat == VK_FORMAT_D32_SFLOAT_S8_UINT ||
1505 m_r_context.depthFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
1506 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
1507 }
1508 } else {
1509 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1510 }
1511 barrier.subresourceRange.baseMipLevel = 0;
1512 barrier.subresourceRange.levelCount = 1;
1513 barrier.subresourceRange.baseArrayLayer = 0;
1514 barrier.subresourceRange.layerCount = 1;
1515 barrier.srcAccessMask = srcAccessMask;
1516 barrier.dstAccessMask = dstAccessMask;
1517
1518 vkCmdPipelineBarrier(
1519 cmd,
1520 srcStage,
1521 dstStage,
1522 0,
1523 0, nullptr,
1524 0, nullptr,
1525 1, &barrier
1526 );
1527 }
1528
1529 void Renderer::issueMultiDrawIndexed(VkCommandBuffer cmd, const std::vector<VkMultiDrawIndexedInfoEXT>& commands) {
1530 if (commands.empty()) return;
1531
1532 if (m_r_context.supportsMultiDraw && m_r_context.maxMultiDrawCount > 0) [[likely]] {
1533 const uint32_t limit = m_r_context.maxMultiDrawCount;
1534 size_t remaining = commands.size();
1535 size_t offset = 0;
1536
1537 while (remaining > 0) {
1538 uint32_t count = static_cast<uint32_t>(std::min(static_cast<size_t>(limit), remaining));
1539
1540 vkCmdDrawMultiIndexedEXT(
1541 cmd,
1542 count,
1543 commands.data() + offset,
1544 1,
1545 0,
1546 static_cast<uint32_t>(sizeof(VkMultiDrawIndexedInfoEXT)),
1547 nullptr
1548 );
1549
1550 remaining -= count;
1551 offset += count;
1552 }
1553 return;
1554 }
1555
1556 if(basicDiag) [[unlikely]] {
1557 log(LogLevel::WARNING, "MultiDraw fallback active. Count: %zu", commands.size());
1558 basicDiag = false;
1559 }
1560
1561 for (const auto& draw : commands) {
1562 vkCmdDrawIndexed(cmd, draw.indexCount, 1, draw.firstIndex, draw.vertexOffset, 0);
1563 }
1564 }
1565}
Contains basic components like transform, camera, name components..
Main header for the Entity Component System (ECS) framework.
Simple 2D quad rendered in 3d space exclusevely for editor.
This file defines VulkanPipeline Class.
This file defines Renderer Class.
static bool HasAVX2()
Checks if the CPU supports the AVX2 instruction set.
This class provides an interface template for ImGui Implementation. Every backend should implement th...
Central registry for managing entities and their components in the ECS system.
Definition Registry.hpp:29
T & get(Entity entity)
Retrieves a component from an entity.
Definition Registry.hpp:118
T * try_get(Entity entity)
Safely retrieves a component from an entity if it exists.
Definition Registry.hpp:127
std::vector< vex::Entity > pMaskedQueue
Queue of entities with masked particle emitters.
Definition Renderer.hpp:233
void issueMultiDrawIndexed(VkCommandBuffer cmd, const std::vector< VkMultiDrawIndexedInfoEXT > &commands)
Issues a multi-draw indexed command used by transparent meshes since they are drawn triangle by trian...
std::unique_ptr< VulkanPipeline > & m_p_transPipeline
Standard transparent geometry pipeline.
Definition Renderer.hpp:193
bool beginFrame(glm::uvec2 renderResolution, SceneRenderData &outData)
Prepares the frame for rendering.
Definition Renderer.cpp:227
std::unique_ptr< VulkanPipeline > & m_p_billboardTransPipeline
Transparent billboard rendering pipeline.
Definition Renderer.hpp:195
void transitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage)
Helper function for image transition.
void composeFrame(SceneRenderData &data, ImGUIWrapper &ui, bool isEditorMode)
Composes the final frame onto the Swapchain image.
void updateScreenDescriptor(VkImageView view)
Updates the screen descriptor.
std::vector< BillboardItem > bTransQueue
Queue of transparent billboards to render.
Definition Renderer.hpp:231
std::unique_ptr< VulkanPipeline > & m_p_maskPipeline
Standard masked geometry pipeline.
Definition Renderer.hpp:194
std::vector< EditorBillboardItem > bEditorQueue
Queue of editor billboards to render.
Definition Renderer.hpp:232
std::unique_ptr< VulkanPipeline > & m_p_particleMaskedPipeline
Masked particle rendering pipeline.
Definition Renderer.hpp:198
VkDescriptorSet getImGuiTextureID(ImGUIWrapper &ui)
Gets or creates a cached ImGui texture descriptor for the scene.
std::vector< RenderItem > maskedQueue
Queue of masked (alpha-cutout) 3D objects to render.
Definition Renderer.hpp:228
std::unique_ptr< VulkanPipeline > & m_p_billboardMaskedPipeline
Masked billboard rendering pipeline.
Definition Renderer.hpp:196
std::vector< vex::Entity > pTransQueue
Queue of entities with transparent particle emitters.
Definition Renderer.hpp:234
~Renderer()
Destructor for Renderer class.
Definition Renderer.cpp:167
std::vector< BillboardItem > bMaskedQueue
Queue of masked billboards to render.
Definition Renderer.hpp:230
Renderer(VulkanContext &context, std::unique_ptr< VulkanResources > &resources, std::unique_ptr< VulkanPipeline > &pipeline, std::unique_ptr< VulkanPipeline > &transPipeline, std::unique_ptr< VulkanPipeline > &maskPipeline, std::unique_ptr< VulkanPipeline > &billboardTransPipeline, std::unique_ptr< VulkanPipeline > &billboardMaskedPipeline, std::unique_ptr< VulkanPipeline > &particleTransPipeline, std::unique_ptr< VulkanPipeline > &particleMaskedPipeline, std::unique_ptr< VulkanPipeline > &uiPipeline, std::unique_ptr< VulkanPipeline > &fullscreenPipeline, std::unique_ptr< VulkanPipeline > &compositePipeline, std::unique_ptr< VulkanSwapchainManager > &swapchainManager, std::unique_ptr< MeshManager > &meshManager)
Constructor for Renderer class.
Definition Renderer.cpp:56
void endFrame(SceneRenderData &data)
Submits the command buffer and presents the image.
std::vector< RenderItem > transparentQueue
Queue of transparent 3D objects to render.
Definition Renderer.hpp:229
std::unique_ptr< VulkanPipeline > & m_p_particleTransPipeline
Transparent particle rendering pipeline.
Definition Renderer.hpp:197
void renderScene(SceneRenderData &data, const vex::Entity cameraEntity, vex::Registry &registry, int frame, const std::vector< DebugVertex > *debugLines=nullptr, bool isEditorMode=false)
Renders the 3D scene and UI to an offscreen low-res texture.
Definition Renderer.cpp:345
std::vector< RenderItem > opaqueQueue
Queue of opaque 3D objects to render.
Definition Renderer.hpp:227
Provides iteration over entities that have all specified component types.
Definition View.hpp:17
void each(Func func)
Iterates over all entities with the specified component types.
Definition View.hpp:40
Class with Imgui for vulkan backend definition, inheriting from ImGUIWrapper.
void removeTexture(VkDescriptorSet descriptorSet)
Removes a texture from the ImGUI wrapper.
VkDescriptorSet addTexture(VkSampler sampler, VkImageView imageView, VkImageLayout layout)
Adds a texture to the ImGUI wrapper.
This file defines struct holding data needed for camera frustum culling.
This files defines global variables defining vulkan limits.
uint32_t Entity
Type alias representing a unique entity identifier in the ECS system.
Definition Types.hpp:11
std::filesystem::path VEX_EXPORT GetExecutableDir()
Retrieves the directory containing the current executable.
Definition PathUtils.cpp:37
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.
void VEX_EXPORT handle_critical_exception(const std::exception &e)
Handles a critical exception, forcing termination.
std::string VEX_EXPORT GetAssetPath(const std::string &relativePath)
Resolves the absolute path of an asset based on the current build configuration.
Definition PathUtils.cpp:73
A component for rendering a 2D quad that always faces the camera.
bool isTransparent
Indicates if the billboard is rendered in the transparent pass.
vex::texture_asset_path texturePath
Path to the texture mapped onto the billboard.
Struct to hold push data for billboard rendering.
Definition Renderer.hpp:46
glm::vec3 pos
World position of the billboard.
Definition Renderer.hpp:47
Struct that contains camera properties. Used by build in CameraObject, but needed for any custom one ...
Represents a vertex used for debugging purposes.
A component for rendering a 2D quad that always faces the camera.
std::vector< vex::texture_asset_path > texturePaths
Paths to the textures mapped onto the billboard.
Struct containing fog properties.
Represents a frustum in 3D space.
Definition frustum.hpp:31
void update(const glm::mat4 &viewProj)
Extract planes from View-Projection Matrix.
Definition frustum.hpp:36
bool testSphere(const glm::vec3 &center, float radius) const
Returns true if sphere is visible (or partially visible).
Definition frustum.hpp:62
Struct containing light properties.
Struct containing raw meshData, mesh id, texture paths and material properties. It just template and ...
void setRendered()
(used internally by the engine, DO NOT CALL) sets the component as rendered.
bool getIsFresh()
(used internally by the engine, DO NOT CALL) returns true if the component is fresh.
Component that emits and manages a system of particles.
vex::texture_asset_path texturePath
Path to the texture applied to particles.
std::vector< ParticleGPUData > activeParticles
GPU data buffer built every frame for rendering.
bool isTransparent
Indicates if the particles should be rendered in the transparent pass.
Structure passed to the GPU containing per-particle rendering data.
Scene lights uniform buffer object.
Definition uniforms.hpp:47
Data structure to pass state between render stages.
Definition Renderer.hpp:57
Struct containing transform data and methods.
glm::vec3 getWorldScale()
Method to get world scale, needed when object is parented as scale parameter stores local scale.
glm::mat4 matrix(bool forceRecalculate=false)
Method used by renderer to calculate the transformation matrix.
void setWorldScale(glm::vec3 newScale)
Method to set world scale, needed when object is parented as rotation parameter stores local rotation...
glm::vec3 getWorldPosition()
Method to get world position, needed when object is parented as position parameter stores local posit...
glm::vec3 getLocalRotation() const
Get the local rotation (as Euler angles in degrees).
Struct containing VexUI class.
Struct holding all vulkan data, like device, surface, swapchain, images, views, and more.
Definition context.hpp:26
This file defines uniforms and pushconstant for rendering meshes.