From 34c5b3b7e5b5ab8c422a525774ff58fbafe43a81 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 5 Dec 2025 12:30:06 +0530 Subject: [PATCH 001/107] More vulkan documentation and minor KarmaGui class description --- Karma/src/Karma/KarmaGui/KarmaGui.h | 13 ++ .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 125 +++++++++++++++--- 3 files changed, 124 insertions(+), 16 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGui.h b/Karma/src/Karma/KarmaGui/KarmaGui.h index eba8279c..a36c8a2d 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGui.h +++ b/Karma/src/Karma/KarmaGui/KarmaGui.h @@ -174,6 +174,19 @@ struct KGVec4 namespace Karma { + /** + * @brief The chief class for our UI needs. The responsibility includes + * - Context creation and access + * - Main functions to start and end frames, render, and access draw data + * - Demo, Debug, Information windows + * - Styles + * - Windows and Child Windows management + * - Window Utilities and Manipulation + * - Content region access + * - Windows Scrolling + * + * @since Karma 1.0.0 + */ class KARMA_API KarmaGui { public: diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 8b9ead70..0dc694ce 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -253,6 +253,7 @@ namespace Karma windowRenderBuffers->Count = vulkanAPI->GetMaxFramesInFlight(); // Caution: Need to think about the object instantiation and resource management + // Cowboy's Note: delete is done in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_ShivaWindowRenderBuffers windowRenderBuffers->FrameRenderBuffers = new KarmaGui_ImplVulkanH_ImageFrameRenderBuffers[windowRenderBuffers->Count]; } @@ -1454,7 +1455,6 @@ namespace Karma info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; //info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - info.samples = VK_SAMPLE_COUNT_1_BIT; result = vkCreateImage(vulkanInfo->Device, &info, vulkanInfo->Allocator, &textureData->DepthImage); KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't create a image"); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 8de9a9bd..9341872a 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -97,11 +97,16 @@ namespace Karma VkDescriptorPool DescriptorPool; /** - * @brief A handle to render pass object which represents a collection of attachments (depth attachment or colo(u)r attachment ), subpasses, and dependencies between the subpasses, and describes how the attachments are used over the course of the subpasses. Taken from the one created in VulkanContext::CreateRenderPass(). + * @brief A handle to render pass object which represents a collection of attachments (depth attachment or colo(u)r attachment ), subpasses, and dependencies between the subpasses, and describes how the attachments are used over the course of the subpasses. * + * Taken from the one created in VulkanContext::CreateRenderPass(). + * + * @note Used to set KarmaGui_ImplVulkan_Data::RenderPass (backend data) in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_Init() + * + * @see KarmaGuiRenderer::SetupKarmaGuiRenderer * @since Karma 1.0.0 */ - VkRenderPass RenderPass; // Very experimental here + VkRenderPass RenderPass; // begin Optional /** @@ -112,7 +117,7 @@ namespace Karma VkPipelineCache PipelineCache; /** - * @brief Not sure what the sue of this Subpass is. Seems vestigial? + * @brief Not sure what the use of this Subpass is. Seems vestigial? * * @since Karma 1.0.0 */ @@ -138,10 +143,24 @@ namespace Karma /** * @brief Is a VkSampleCountFlagBits value specifying the number of samples used in rasterization. This value is ignored for the purposes of setting the number of samples used in rasterization if the pipeline is created with the VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT dynamic state set, but if VK_DYNAMIC_STATE_SAMPLE_MASK_EXT dynamic state is not set, it is still used to define the size of the pSampleMask array. * - * @note >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT) + * This is using in the rasterization stage of the graphics pipeline to determine how many samples per pixel will be used when rendering. + * + * @note >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT). This setting disables multisampling, equivalent to no anti-aliasing, + * where each pixel has a single sample for color and depth processing during rasterization. Images created + * with this sample count cannot be used for multisample resolve operations or multisampled samplers, as they + * are treated as non-multisampled. Higher values like VK_SAMPLE_COUNT_2_BIT or VK_SAMPLE_COUNT_4_BIT enable + * multiple samples per pixel for improved anti-aliasing quality + * + * @note In rasterization stage the primitives are converted to fragments. Each fragment corresponds to a pixel in the framebuffer. When multisampling is enabled, multiple samples are taken within each pixel to improve image quality and reduce aliasing effects. + * @remark While using higher sample counts (for instance VK_SAMPLE_COUNT_4_BIT), I couldn't produce coherent 2D + * image here, despit of setting VK_SAMPLE_COUNT_4_BIT in KarmaGuiHandler::CreateOffScreenRenderResources() and + * appropriately setting image, depthimage, and pipeline to use that sample count. Perplexity isn't particularly + * able to help. So, I am leaving VK_SAMPLE_COUNT_1_BIT (no multisampling). + * + * @see KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline * @since Karma 1.0.0 */ - VkSampleCountFlagBits MSAASamples; + VkSampleCountFlagBits MSAASamples; // Multi sample anti aliasing samples /** * @brief A pointer to a valid VkAllocationCallbacks structure. Structure containing callback function pointers for memory allocation @@ -336,6 +355,13 @@ namespace Karma * * * @note Taken from VulkanContext::CreateRenderPass() set in KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow() + * + * @note There are multiple RenderPass references for instance in KarmaGui_ImplVulkan_InitInfo and created in VulkanContext::CreateRenderPass(). Here, this + * RenderPass is again usually set to the one created in VulkanContext::CreateRenderPass(), meant for KarmaGui's primitive + * rendering. + * However the RenderPass in KarmaGui_OffScreenRenderResources (created in KarmaGuiVulkanHandler::CreateOffScreenRenderResources) + * is different, and meant for offscreen rendering purpose. + * * @since Karma 1.0.0 */ VkRenderPass RenderPass; @@ -388,7 +414,7 @@ namespace Karma * * @since Karma 1.0.0 */ - uint32_t MAX_FRAMES_IN_FLIGHT; + uint32_t MAX_FRAMES_IN_FLIGHT; /** * @brief Just a container for buffers (framebuffers, images, and imageviews etc) and all those sizes depending on VulkanHolder::GetVulkanContext()->GetSwapChainImages().size(); @@ -430,9 +456,13 @@ namespace Karma /** * @brief Reusable buffers used for rendering 1 current in-flight ImageFrame, for KarmaGui_ImplVulkan_RenderDrawData(). - * Seems like data structure with single instantiation for each of the FrameIndex. + * Seems like data structure with single instantiation for each of the FrameIndex (not ImageFrameIndex which is + * returned by vkGetSwapchainImagesKHR). * * @note Please zero-clear before use. + * + * @see KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_RenderDrawData + * @since Karma 1.0.0 */ struct KarmaGui_ImplVulkanH_ImageFrameRenderBuffers { @@ -566,7 +596,6 @@ namespace Karma * @since Karma 1.0.0 */ KarmaGui_ImplVulkanH_WindowRenderBuffers RenderBuffers; - //ImGui_ImplVulkanH_Window* ImGUILayerWindowData; /** * @brief A constructor for allocation and 0 initialization @@ -588,7 +617,7 @@ namespace Karma }; /** - * @brief Data structure containing the Mesa image texture relevant data. For instance, file and folder icons of Content Browser. + * @brief Data structure containing the Mesa image texture relevant data. For instance, file, and folder icons of Content Browser. * * Similar to MesaDecalData in KarmaGuiOpenGLHandler. * @@ -698,34 +727,100 @@ namespace Karma } }; + /** + * @brief Data structure for 3D scene to 2D texture rendering element(s) in KarmaGui. + * + * @see KarmaGuiRenderer::FrameRender + * @since Karma 1.0.0 + */ struct KarmaGui_3DScene_To_2DTexture_Data { - std::shared_ptr Scene3D; + /** + * @brief 3D scene to be rendered offscreen to 2D texture for display in KarmaGui element. + */ + std::shared_ptr Scene3D; + /** + * @brief Size of the 2D texture to be rendered offscreen from 3D scene. + */ KGVec2 Size; + + /** + * @brief Texture ID to be used in KarmaGui::Image() and related functions, for display of 2D rendered rendertarget + * in some KarmaGui window. + */ KGTextureID KarmaGui_Texture; + /** + * @brief Vulkan image for offscreen rendered 2D texture. + */ VkImage Image; + + /** + * @brief Device memory for the offscreen rendered 2D texture image. + */ VkDeviceMemory DeviceMemory; + + /** + * @brief Image view for the offscreen rendered 2D texture image. + */ VkImageView Image_View; - //VkSampler Sampler; - // Depth resources + /** + * @brief Depth image for offscreen rendered 2D texture. + */ VkImage DepthImage; + + /** + * @brief Device memory for the offscreen rendered 2D texture depth image. + */ VkDeviceMemory DepthDeviceMemory; + /** + * @brief Image view for the offscreen rendered 2D texture depth image. + */ VkImageView DepthImage_View; - //VkRenderPass RenderPass; + /** + * @brief Framebuffer for offscreen rendered 2D texture. + */ VkFramebuffer FrameBuffer; }; + /** + * @brief Resources common to screen rendering of 3D scene to 2D texture elements. + * + * @see KarmaGuiRenderer::FrameRender + * @since Karma 1.0.0 + */ struct KarmaGui_OffScreen_Render_Resources { + /** + * @brief Sampler for offscreen rendered 2D texture(s). + * + * A Vulkan sampler is an object that defines how to sample textures when they are accessed in shaders. + * It encapsulates various sampling parameters such as filtering modes, addressing modes, and mipmapping + * settings. + * + * @see KarmaGuiRenderer::Add3DSceneFor2DRendering + */ VkSampler Sampler; - VkRenderPass RenderPass; - // Since the resources in here require allocation only once + /** + * @brief Render pass for offscreen rendering of 3D scene to 2D texture(s). + * + * A render pass in Vulkan defines a sequence of rendering operations, including the attachments (color, depth, stencil) used during rendering, + * how they are used, and the order of subpasses. It encapsulates the rendering workflow and is essential for efficient rendering. + * + * @see KarmaGuiRenderer::Add3DSceneFor2DRendering + */ + VkRenderPass RenderPass; + + /** + * @brief Flag indicating whether the allocation of offscreen rendering resources has been done at least once. + * + * @see KarmaGuiRenderer::Add3DSceneFor2DRendering + */ bool bAllocationDoneOnce; KarmaGui_OffScreen_Render_Resources() : bAllocationDoneOnce(false) From a293a358e85f43211b64fdda3a2feb0b9fa45114 Mon Sep 17 00:00:00 2001 From: pcboy Date: Sun, 7 Dec 2025 07:07:21 +0530 Subject: [PATCH 002/107] XCode relevant comment in top level cmakelists --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2601f88..39569602 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,10 +34,16 @@ set(MAC_PACKAGE FALSE) # This is so that .app can be installed in KarmaBin if (APPLE) if(MAC_PACKAGE) + # We will be needing the install scheme so generate all the schemes set(CMAKE_XCODE_GENERATE_SCHEME ON CACHE BOOL "") set(CMAKE_INSTALL_PREFIX "${REPOSITORYROOT}/KarmaBin" CACHE PATH "Custom install location for .app bundle" FORCE) else() + # No need for generating all the schemes. Selected schemes can be + # be generated for appropriate targets. See Pranjal + # set_target_properties(Pranjal PROPERTIES + # XCODE_GENERATE_SCHEME TRUE + # XCODE_SCHEME_WORKING_DIRECTORY "${REPOSITORYROOT}/KarmaBin") set(CMAKE_XCODE_GENERATE_SCHEME OFF CACHE BOOL "") endif() endif() From b3cb4f1d996c1e54e2975c602a4aea5cf407da5e Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 7 Dec 2025 13:50:26 +0530 Subject: [PATCH 003/107] Removing krpch from header files --- Karma/src/Karma/Application.h | 2 -- Karma/src/Karma/Core/Field.h | 1 - Karma/src/Karma/Core/GFrameworkMacros.h | 2 -- Karma/src/Karma/Core/Package.h | 1 - Karma/src/Karma/Core/SubClassOf.h | 2 -- Karma/src/Karma/Core/TrueCore/GenericPlatformMemory.h | 2 -- Karma/src/Karma/Core/TrueCore/KarmaMemory.h | 2 -- Karma/src/Karma/Core/TrueCore/KarmaSmriti.h | 2 -- Karma/src/Karma/Core/UObjectGlobals.h | 2 -- Karma/src/Karma/Engine/Engine.h | 2 -- Karma/src/Karma/Engine/GameInstance.h | 2 -- Karma/src/Karma/Engine/GameViewportClient.h | 2 -- Karma/src/Karma/Events/ApplicationEvent.h | 2 -- Karma/src/Karma/Events/ControllerDeviceEvent.h | 2 -- Karma/src/Karma/Events/Event.h | 2 -- Karma/src/Karma/Events/MouseEvent.h | 1 - Karma/src/Karma/GameFramework/Actor.h | 5 +++-- Karma/src/Karma/GameFramework/ActorComponent.h | 2 -- Karma/src/Karma/GameFramework/ActorIterator.h | 2 -- Karma/src/Karma/GameFramework/ChildActorComponent.h | 2 -- Karma/src/Karma/GameFramework/Level.h | 2 -- Karma/src/Karma/GameFramework/PrimitiveComponent.h | 2 -- Karma/src/Karma/GameFramework/SceneComponent.h | 2 -- Karma/src/Karma/GameFramework/World.h | 2 -- Karma/src/Karma/GameFramework/WorldSettings.h | 2 -- Karma/src/Karma/Ganit/KarmaMath.h | 2 -- Karma/src/Karma/Ganit/Transform.h | 8 +++----- Karma/src/Karma/Input.h | 2 -- Karma/src/Karma/KarmaGui/KarmaGui.h | 3 --- Karma/src/Karma/KarmaGui/KarmaGuiInternal.h | 1 - Karma/src/Karma/KarmaGui/KarmaGuiLayer.h | 2 -- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h | 3 +-- Karma/src/Karma/KarmaUtilities.h | 1 - Karma/src/Karma/Layer.h | 2 -- Karma/src/Karma/LayerStack.h | 2 -- Karma/src/Karma/Renderer/Buffer.h | 2 -- Karma/src/Karma/Renderer/Camera/Camera.h | 2 -- Karma/src/Karma/Renderer/Material.h | 4 +--- Karma/src/Karma/Renderer/Mesh.h | 2 -- Karma/src/Karma/Renderer/RenderCommand.h | 2 -- Karma/src/Karma/Renderer/Renderer.h | 2 -- Karma/src/Karma/Renderer/RendererAPI.h | 2 -- Karma/src/Karma/Renderer/Scene.h | 2 -- Karma/src/Karma/Renderer/Shader.h | 2 -- Karma/src/Karma/Renderer/SkeletalMesh.h | 2 -- Karma/src/Karma/Renderer/Texture.h | 2 -- Karma/src/Karma/Renderer/VertexArray.h | 2 -- Karma/src/Karma/Window.h | 2 -- Karma/src/Platform/Linux/Core/LinuxPlatformMemory.h | 2 -- Karma/src/Platform/Mac/Core/MacPlatformMemory.h | 2 -- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h | 2 -- Karma/src/Platform/Vulkan/VulkanContext.h | 1 - Karma/src/Platform/Vulkan/VulkanHolder.h | 4 +--- Karma/src/Platform/Vulkan/VulkanRendererAPI.h | 2 -- Karma/src/Platform/Vulkan/VulkanShader.h | 2 -- Karma/src/Platform/Windows/Core/WindowsPlatformMemory.h | 4 +--- 56 files changed, 10 insertions(+), 113 deletions(-) diff --git a/Karma/src/Karma/Application.h b/Karma/src/Karma/Application.h index 30c84e7a..1c7c2005 100644 --- a/Karma/src/Karma/Application.h +++ b/Karma/src/Karma/Application.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Window.h" #include "Karma/Events/ApplicationEvent.h" #include "Karma/Events/ControllerDeviceEvent.h" diff --git a/Karma/src/Karma/Core/Field.h b/Karma/src/Karma/Core/Field.h index cd2031ab..4b430be5 100644 --- a/Karma/src/Karma/Core/Field.h +++ b/Karma/src/Karma/Core/Field.h @@ -10,7 +10,6 @@ #pragma once -#include "krpch.h" #include "UObjectGlobals.h" namespace Karma diff --git a/Karma/src/Karma/Core/GFrameworkMacros.h b/Karma/src/Karma/Core/GFrameworkMacros.h index a65ce7b7..5d972ce8 100644 --- a/Karma/src/Karma/Core/GFrameworkMacros.h +++ b/Karma/src/Karma/Core/GFrameworkMacros.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - enum EInternal {EC_InternalUseOnlyConstructor}; typedef void (*ClassConstructorType) (const Karma::FObjectInitializer&); diff --git a/Karma/src/Karma/Core/Package.h b/Karma/src/Karma/Core/Package.h index b938921a..5a143514 100644 --- a/Karma/src/Karma/Core/Package.h +++ b/Karma/src/Karma/Core/Package.h @@ -10,7 +10,6 @@ #pragma once -#include "krpch.h" #include "Object.h" namespace Karma diff --git a/Karma/src/Karma/Core/SubClassOf.h b/Karma/src/Karma/Core/SubClassOf.h index a602dbbe..9356a3e4 100644 --- a/Karma/src/Karma/Core/SubClassOf.h +++ b/Karma/src/Karma/Core/SubClassOf.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Class.h" #include "Field.h" diff --git a/Karma/src/Karma/Core/TrueCore/GenericPlatformMemory.h b/Karma/src/Karma/Core/TrueCore/GenericPlatformMemory.h index 337b90a2..52671ec6 100644 --- a/Karma/src/Karma/Core/TrueCore/GenericPlatformMemory.h +++ b/Karma/src/Karma/Core/TrueCore/GenericPlatformMemory.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - namespace Karma { typedef size_t SIZE_T; diff --git a/Karma/src/Karma/Core/TrueCore/KarmaMemory.h b/Karma/src/Karma/Core/TrueCore/KarmaMemory.h index c9e9f8b6..44c84e7b 100644 --- a/Karma/src/Karma/Core/TrueCore/KarmaMemory.h +++ b/Karma/src/Karma/Core/TrueCore/KarmaMemory.h @@ -12,8 +12,6 @@ #pragma once -#include "krpch.h" - #ifdef KR_WINDOWS_PLATFORM #include "Platform/Windows/Core/WindowsPlatformMemory.h" #elif KR_MAC_PLATFORM diff --git a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.h b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.h index 9e971c92..fc9de626 100644 --- a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.h +++ b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Core/UObjectAllocator.h" namespace Karma diff --git a/Karma/src/Karma/Core/UObjectGlobals.h b/Karma/src/Karma/Core/UObjectGlobals.h index c7aa3668..90747122 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.h +++ b/Karma/src/Karma/Core/UObjectGlobals.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - namespace Karma { class UObject; diff --git a/Karma/src/Karma/Engine/Engine.h b/Karma/src/Karma/Engine/Engine.h index 346ac91d..5d462b49 100644 --- a/Karma/src/Karma/Engine/Engine.h +++ b/Karma/src/Karma/Engine/Engine.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Object.h" #include "Core/KarmaTypes.h" diff --git a/Karma/src/Karma/Engine/GameInstance.h b/Karma/src/Karma/Engine/GameInstance.h index adf280c2..0f5f9ba6 100644 --- a/Karma/src/Karma/Engine/GameInstance.h +++ b/Karma/src/Karma/Engine/GameInstance.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Object.h" namespace Karma diff --git a/Karma/src/Karma/Engine/GameViewportClient.h b/Karma/src/Karma/Engine/GameViewportClient.h index 5348d93c..d2c63997 100644 --- a/Karma/src/Karma/Engine/GameViewportClient.h +++ b/Karma/src/Karma/Engine/GameViewportClient.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Object.h" namespace Karma diff --git a/Karma/src/Karma/Events/ApplicationEvent.h b/Karma/src/Karma/Events/ApplicationEvent.h index 21c8b8cf..e3c8d84f 100644 --- a/Karma/src/Karma/Events/ApplicationEvent.h +++ b/Karma/src/Karma/Events/ApplicationEvent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Event.h" namespace Karma diff --git a/Karma/src/Karma/Events/ControllerDeviceEvent.h b/Karma/src/Karma/Events/ControllerDeviceEvent.h index 518051f9..1b759f03 100644 --- a/Karma/src/Karma/Events/ControllerDeviceEvent.h +++ b/Karma/src/Karma/Events/ControllerDeviceEvent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Event.h" namespace Karma diff --git a/Karma/src/Karma/Events/Event.h b/Karma/src/Karma/Events/Event.h index 99dc47e2..fe6b379e 100644 --- a/Karma/src/Karma/Events/Event.h +++ b/Karma/src/Karma/Events/Event.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - namespace Karma { /** diff --git a/Karma/src/Karma/Events/MouseEvent.h b/Karma/src/Karma/Events/MouseEvent.h index a0429e3a..b3114af8 100644 --- a/Karma/src/Karma/Events/MouseEvent.h +++ b/Karma/src/Karma/Events/MouseEvent.h @@ -9,7 +9,6 @@ */ #pragma once -#include "krpch.h" #include "Event.h" namespace Karma diff --git a/Karma/src/Karma/GameFramework/Actor.h b/Karma/src/Karma/GameFramework/Actor.h index 68d76b5d..12a78cfc 100644 --- a/Karma/src/Karma/GameFramework/Actor.h +++ b/Karma/src/Karma/GameFramework/Actor.h @@ -10,9 +10,10 @@ #pragma once -#include "krpch.h" +#include "Karma/Core.h" +#include "Karma/Log.h" +#include "Karma/Core/KarmaTypes.h" #include "Object.h" - #include "GameFramework/SceneComponent.h" namespace Karma diff --git a/Karma/src/Karma/GameFramework/ActorComponent.h b/Karma/src/Karma/GameFramework/ActorComponent.h index b195b897..e60de3d3 100644 --- a/Karma/src/Karma/GameFramework/ActorComponent.h +++ b/Karma/src/Karma/GameFramework/ActorComponent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Core/Object.h" class AActor; diff --git a/Karma/src/Karma/GameFramework/ActorIterator.h b/Karma/src/Karma/GameFramework/ActorIterator.h index ea12bbc6..c3fbcf44 100644 --- a/Karma/src/Karma/GameFramework/ActorIterator.h +++ b/Karma/src/Karma/GameFramework/ActorIterator.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "KarmaUtilities.h" namespace Karma diff --git a/Karma/src/Karma/GameFramework/ChildActorComponent.h b/Karma/src/Karma/GameFramework/ChildActorComponent.h index 6c6c7691..ac84785b 100644 --- a/Karma/src/Karma/GameFramework/ChildActorComponent.h +++ b/Karma/src/Karma/GameFramework/ChildActorComponent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "SceneComponent.h" #include "SubClassOf.h" diff --git a/Karma/src/Karma/GameFramework/Level.h b/Karma/src/Karma/GameFramework/Level.h index 07d95a28..4f0201fd 100644 --- a/Karma/src/Karma/GameFramework/Level.h +++ b/Karma/src/Karma/GameFramework/Level.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Core/Object.h" namespace Karma diff --git a/Karma/src/Karma/GameFramework/PrimitiveComponent.h b/Karma/src/Karma/GameFramework/PrimitiveComponent.h index ed94eaa5..5bfd5980 100644 --- a/Karma/src/Karma/GameFramework/PrimitiveComponent.h +++ b/Karma/src/Karma/GameFramework/PrimitiveComponent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "SceneComponent.h" namespace Karma diff --git a/Karma/src/Karma/GameFramework/SceneComponent.h b/Karma/src/Karma/GameFramework/SceneComponent.h index df623ceb..8cc1ed39 100644 --- a/Karma/src/Karma/GameFramework/SceneComponent.h +++ b/Karma/src/Karma/GameFramework/SceneComponent.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "ActorComponent.h" #include "glm/glm.hpp" diff --git a/Karma/src/Karma/GameFramework/World.h b/Karma/src/Karma/GameFramework/World.h index 5752373d..edb2eb03 100644 --- a/Karma/src/Karma/GameFramework/World.h +++ b/Karma/src/Karma/GameFramework/World.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Object.h" #include "SubClassOf.h" diff --git a/Karma/src/Karma/GameFramework/WorldSettings.h b/Karma/src/Karma/GameFramework/WorldSettings.h index b042e22d..0832c13d 100644 --- a/Karma/src/Karma/GameFramework/WorldSettings.h +++ b/Karma/src/Karma/GameFramework/WorldSettings.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Actor.h" namespace Karma diff --git a/Karma/src/Karma/Ganit/KarmaMath.h b/Karma/src/Karma/Ganit/KarmaMath.h index f05c38bb..6172efb0 100644 --- a/Karma/src/Karma/Ganit/KarmaMath.h +++ b/Karma/src/Karma/Ganit/KarmaMath.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "glm/common.hpp" namespace Karma diff --git a/Karma/src/Karma/Ganit/Transform.h b/Karma/src/Karma/Ganit/Transform.h index 360ef7fb..ba08e587 100644 --- a/Karma/src/Karma/Ganit/Transform.h +++ b/Karma/src/Karma/Ganit/Transform.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "glm/glm.hpp" #include #define GLM_ENABLE_EXPERIMENTAL @@ -21,7 +19,7 @@ namespace Karma #define KR_SMALL_NUMBER (1.e-8f) /** - * Floating point quaternion that can represent a rotation about an axis in 3-D space. + * @brief Floating point quaternion that can represent a rotation about an axis in 3-D space. * The X, Y, Z, W components also double as the Axis/Angle format. * * Order matters when composing quaternions: C = A * B will yield a quaternion C that logically @@ -48,7 +46,7 @@ namespace Karma }; /** - * Implements a container for rotation information. + * @brief Implements a container for rotation information. * * All rotation values are stored in degrees. * @@ -98,7 +96,7 @@ namespace Karma }; /** - * Transform composed of Scale, Rotation (as a quaternion), and Translation. + * @brief Transform composed of Scale, Rotation (as a quaternion), and Translation. * * Transforms can be used to convert from one space to another, for example by transforming * positions and directions from local space to world space. diff --git a/Karma/src/Karma/Input.h b/Karma/src/Karma/Input.h index 99a57741..21ec2489 100644 --- a/Karma/src/Karma/Input.h +++ b/Karma/src/Karma/Input.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Log.h" #include "Karma/Events/Event.h" #include "Karma/Window.h" diff --git a/Karma/src/Karma/KarmaGui/KarmaGui.h b/Karma/src/Karma/KarmaGui/KarmaGui.h index a36c8a2d..87111380 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGui.h +++ b/Karma/src/Karma/KarmaGui/KarmaGui.h @@ -25,9 +25,6 @@ #define KG_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 -// Includes -#include "krpch.h" - //----------------------------------------------------------------------------- // [SECTION] Forward declarations and basic types //----------------------------------------------------------------------------- diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h b/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h index dec72dff..8d891d1e 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h @@ -43,7 +43,6 @@ Index of this file: #pragma once -#include "krpch.h" #include "KarmaGui.h" // Enable stb_truetype by default unless FreeType is enabled. diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiLayer.h b/Karma/src/Karma/KarmaGui/KarmaGuiLayer.h index 5edc26b0..a2734fbb 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiLayer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiLayer.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Layer.h" #include "Events/KeyEvent.h" #include "Events/MouseEvent.h" diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h index 7be5624e..17a31f05 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h @@ -10,9 +10,8 @@ #pragma once -#include "krpch.h" - #include "imgui_impl_glfw.h" + #include "Platform/Vulkan/KarmaGuiVulkanHandler.h" #include "Platform/OpenGL/KarmaGuiOpenGLHandler.h" diff --git a/Karma/src/Karma/KarmaUtilities.h b/Karma/src/Karma/KarmaUtilities.h index 111dda26..6b05e6ba 100644 --- a/Karma/src/Karma/KarmaUtilities.h +++ b/Karma/src/Karma/KarmaUtilities.h @@ -9,7 +9,6 @@ */ #pragma once -#include "krpch.h" #include "SubClassOf.h" #include "Actor.h" #include "stb_image.h" diff --git a/Karma/src/Karma/Layer.h b/Karma/src/Karma/Layer.h index 0eea1319..867e0510 100644 --- a/Karma/src/Karma/Layer.h +++ b/Karma/src/Karma/Layer.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Events/Event.h" #include "Renderer/Scene.h" diff --git a/Karma/src/Karma/LayerStack.h b/Karma/src/Karma/LayerStack.h index 8cfb96fa..cfd12bd2 100644 --- a/Karma/src/Karma/LayerStack.h +++ b/Karma/src/Karma/LayerStack.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Layer.h" namespace Karma diff --git a/Karma/src/Karma/Renderer/Buffer.h b/Karma/src/Karma/Renderer/Buffer.h index 536881f3..c12d9903 100644 --- a/Karma/src/Karma/Renderer/Buffer.h +++ b/Karma/src/Karma/Renderer/Buffer.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "glm/gtc/type_ptr.hpp" #include "stb_image.h" diff --git a/Karma/src/Karma/Renderer/Camera/Camera.h b/Karma/src/Karma/Renderer/Camera/Camera.h index 56330553..37a32d15 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.h +++ b/Karma/src/Karma/Renderer/Camera/Camera.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "glm/glm.hpp" namespace Karma diff --git a/Karma/src/Karma/Renderer/Material.h b/Karma/src/Karma/Renderer/Material.h index 91aa8fe0..1b8ab2e7 100644 --- a/Karma/src/Karma/Renderer/Material.h +++ b/Karma/src/Karma/Renderer/Material.h @@ -9,12 +9,10 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Renderer/Shader.h" - #include "Texture.h" #include "Camera/Camera.h" +#include namespace Karma { diff --git a/Karma/src/Karma/Renderer/Mesh.h b/Karma/src/Karma/Renderer/Mesh.h index 1fc8a6ed..aaef3608 100644 --- a/Karma/src/Karma/Renderer/Mesh.h +++ b/Karma/src/Karma/Renderer/Mesh.h @@ -10,8 +10,6 @@ #pragma once -#include "krpch.h" - #include "Buffer.h" #include #include diff --git a/Karma/src/Karma/Renderer/RenderCommand.h b/Karma/src/Karma/Renderer/RenderCommand.h index 21ea42fb..5268bfd4 100644 --- a/Karma/src/Karma/Renderer/RenderCommand.h +++ b/Karma/src/Karma/Renderer/RenderCommand.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "RendererAPI.h" namespace Karma diff --git a/Karma/src/Karma/Renderer/Renderer.h b/Karma/src/Karma/Renderer/Renderer.h index c40148c6..19d854b4 100644 --- a/Karma/src/Karma/Renderer/Renderer.h +++ b/Karma/src/Karma/Renderer/Renderer.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "RenderCommand.h" #include "Karma/Renderer/Camera/Camera.h" #include "glm/glm.hpp" diff --git a/Karma/src/Karma/Renderer/RendererAPI.h b/Karma/src/Karma/Renderer/RendererAPI.h index 591c4a67..10327b69 100644 --- a/Karma/src/Karma/Renderer/RendererAPI.h +++ b/Karma/src/Karma/Renderer/RendererAPI.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "glm/glm.hpp" #include "VertexArray.h" diff --git a/Karma/src/Karma/Renderer/Scene.h b/Karma/src/Karma/Renderer/Scene.h index cfcd8000..400631d6 100644 --- a/Karma/src/Karma/Renderer/Scene.h +++ b/Karma/src/Karma/Renderer/Scene.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Camera.h" #include "VertexArray.h" diff --git a/Karma/src/Karma/Renderer/Shader.h b/Karma/src/Karma/Renderer/Shader.h index 558e67f4..f143129f 100644 --- a/Karma/src/Karma/Renderer/Shader.h +++ b/Karma/src/Karma/Renderer/Shader.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Renderer/Buffer.h" #include "glm/glm.hpp" diff --git a/Karma/src/Karma/Renderer/SkeletalMesh.h b/Karma/src/Karma/Renderer/SkeletalMesh.h index 50cdba54..a5a8f4fd 100644 --- a/Karma/src/Karma/Renderer/SkeletalMesh.h +++ b/Karma/src/Karma/Renderer/SkeletalMesh.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Mesh.h" namespace Karma diff --git a/Karma/src/Karma/Renderer/Texture.h b/Karma/src/Karma/Renderer/Texture.h index acc37bad..6942eede 100644 --- a/Karma/src/Karma/Renderer/Texture.h +++ b/Karma/src/Karma/Renderer/Texture.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - namespace Karma { /** diff --git a/Karma/src/Karma/Renderer/VertexArray.h b/Karma/src/Karma/Renderer/VertexArray.h index 2bcb8c1e..7263a690 100644 --- a/Karma/src/Karma/Renderer/VertexArray.h +++ b/Karma/src/Karma/Renderer/VertexArray.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Renderer/Buffer.h" #include "Karma/Renderer/Shader.h" #include "Karma/Renderer/Mesh.h" diff --git a/Karma/src/Karma/Window.h b/Karma/src/Karma/Window.h index f844934b..6c15de45 100644 --- a/Karma/src/Karma/Window.h +++ b/Karma/src/Karma/Window.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Core.h" #include "Karma/Events/Event.h" diff --git a/Karma/src/Platform/Linux/Core/LinuxPlatformMemory.h b/Karma/src/Platform/Linux/Core/LinuxPlatformMemory.h index b595cac2..fa785977 100644 --- a/Karma/src/Platform/Linux/Core/LinuxPlatformMemory.h +++ b/Karma/src/Platform/Linux/Core/LinuxPlatformMemory.h @@ -8,8 +8,6 @@ */ #pragma once -#include "krpch.h" - #include "Core/TrueCore/GenericPlatformMemory.h" namespace Karma diff --git a/Karma/src/Platform/Mac/Core/MacPlatformMemory.h b/Karma/src/Platform/Mac/Core/MacPlatformMemory.h index 49a9430a..066b42a9 100644 --- a/Karma/src/Platform/Mac/Core/MacPlatformMemory.h +++ b/Karma/src/Platform/Mac/Core/MacPlatformMemory.h @@ -9,8 +9,6 @@ #pragma once -#include "krpch.h" - #include "Core/TrueCore/GenericPlatformMemory.h" namespace Karma diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 9341872a..94357c21 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -17,8 +17,6 @@ #pragma once -#include "krpch.h" - #include "KarmaGui.h" #include "KarmaGuiInternal.h" #include "Karma/Renderer/Scene.h" diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index efb830dd..b9641ad8 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -10,7 +10,6 @@ #pragma once #define GLFW_INCLUDE_VULKAN -#include "krpch.h" #include "Karma/Renderer/GraphicsContext.h" #include "GLFW/glfw3.h" diff --git a/Karma/src/Platform/Vulkan/VulkanHolder.h b/Karma/src/Platform/Vulkan/VulkanHolder.h index 32dd0a46..4e332c3c 100644 --- a/Karma/src/Platform/Vulkan/VulkanHolder.h +++ b/Karma/src/Platform/Vulkan/VulkanHolder.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Platform/Vulkan/VulkanContext.h" namespace Karma @@ -54,4 +52,4 @@ namespace Karma private: static VulkanContext* m_VulkanContext; }; -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRendererAPI.h b/Karma/src/Platform/Vulkan/VulkanRendererAPI.h index 50a7347a..42852282 100644 --- a/Karma/src/Platform/Vulkan/VulkanRendererAPI.h +++ b/Karma/src/Platform/Vulkan/VulkanRendererAPI.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Renderer/RendererAPI.h" #include "vulkan/vulkan.h" diff --git a/Karma/src/Platform/Vulkan/VulkanShader.h b/Karma/src/Platform/Vulkan/VulkanShader.h index 8e9bde53..127fb48f 100644 --- a/Karma/src/Platform/Vulkan/VulkanShader.h +++ b/Karma/src/Platform/Vulkan/VulkanShader.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Karma/Renderer/Shader.h" #include "glslang/Public/ShaderLang.h" #include "Karma/KarmaUtilities.h" diff --git a/Karma/src/Platform/Windows/Core/WindowsPlatformMemory.h b/Karma/src/Platform/Windows/Core/WindowsPlatformMemory.h index ed999651..32a73819 100644 --- a/Karma/src/Platform/Windows/Core/WindowsPlatformMemory.h +++ b/Karma/src/Platform/Windows/Core/WindowsPlatformMemory.h @@ -9,8 +9,6 @@ */ #pragma once -#include "krpch.h" - #include "Core/TrueCore/GenericPlatformMemory.h" namespace Karma @@ -37,4 +35,4 @@ namespace Karma }; typedef FWindowsPlatformMemory FPlatformMemory; -} \ No newline at end of file +} From e09d983ef27c060a1dac3e420d92d1c38152f8c8 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 7 Dec 2025 14:01:49 +0530 Subject: [PATCH 004/107] Removing redundant includes --- Karma/src/Karma/Animation/Animation.h | 4 +--- Karma/src/Karma/Application.cpp | 1 - Karma/src/Karma/Renderer/Buffer.cpp | 1 - Karma/src/Karma/Renderer/Shader.cpp | 1 - Karma/src/Karma/Renderer/VertexArray.cpp | 3 +-- Karma/src/Platform/Linux/LinuxWindow.cpp | 1 - Karma/src/Platform/Mac/MacWindow.cpp | 1 - Karma/src/Platform/OpenGL/OpenGLContext.cpp | 2 +- Karma/src/Platform/Windows/WindowsWindow.cpp | 3 +-- 9 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Karma/src/Karma/Animation/Animation.h b/Karma/src/Karma/Animation/Animation.h index 8d64c552..f6c3fbad 100644 --- a/Karma/src/Karma/Animation/Animation.h +++ b/Karma/src/Karma/Animation/Animation.h @@ -1,7 +1,5 @@ #pragma once -#include "krpch.h" - namespace Karma { class KARMA_API Animation @@ -12,4 +10,4 @@ namespace Karma }; -} \ No newline at end of file +} diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index 923fe423..79ff3a90 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -1,5 +1,4 @@ #include "Application.h" -#include "Karma/Log.h" #include "Karma/Input.h" #include "Karma/Renderer/Renderer.h" #include "chrono" diff --git a/Karma/src/Karma/Renderer/Buffer.cpp b/Karma/src/Karma/Renderer/Buffer.cpp index d449fde6..ff722974 100644 --- a/Karma/src/Karma/Renderer/Buffer.cpp +++ b/Karma/src/Karma/Renderer/Buffer.cpp @@ -1,5 +1,4 @@ #include "Buffer.h" -#include "Karma/Core.h" #include "Renderer.h" #include "Platform/OpenGL/OpenGLBuffer.h" #include "Platform/Vulkan/VulkanBuffer.h" diff --git a/Karma/src/Karma/Renderer/Shader.cpp b/Karma/src/Karma/Renderer/Shader.cpp index 7976a8b1..e557c8a4 100644 --- a/Karma/src/Karma/Renderer/Shader.cpp +++ b/Karma/src/Karma/Renderer/Shader.cpp @@ -1,6 +1,5 @@ #include "Shader.h" #include "Renderer.h" -#include "Karma/Core.h" #include "Platform/OpenGL/OpenGLShader.h" #include "Platform/Vulkan/VulkanShader.h" diff --git a/Karma/src/Karma/Renderer/VertexArray.cpp b/Karma/src/Karma/Renderer/VertexArray.cpp index 9c81a96d..0d4ac753 100644 --- a/Karma/src/Karma/Renderer/VertexArray.cpp +++ b/Karma/src/Karma/Renderer/VertexArray.cpp @@ -1,6 +1,5 @@ #include "VertexArray.h" #include "Renderer.h" -#include "Karma/Core.h" #include "Platform/OpenGL/OpenGLVertexArray.h" #include "Platform/Vulkan/VulkanVertexArray.h" @@ -22,4 +21,4 @@ namespace Karma KR_CORE_ASSERT(false, "Unknown RendererAPI"); return nullptr; } -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Linux/LinuxWindow.cpp b/Karma/src/Platform/Linux/LinuxWindow.cpp index d88a8d46..51d794e6 100644 --- a/Karma/src/Platform/Linux/LinuxWindow.cpp +++ b/Karma/src/Platform/Linux/LinuxWindow.cpp @@ -1,5 +1,4 @@ #include "LinuxWindow.h" -#include "Karma/Log.h" #include "Karma/Events/ApplicationEvent.h" #include "Karma/Events/KeyEvent.h" #include "Karma/Events/MouseEvent.h" diff --git a/Karma/src/Platform/Mac/MacWindow.cpp b/Karma/src/Platform/Mac/MacWindow.cpp index f7f34539..e15e91ca 100644 --- a/Karma/src/Platform/Mac/MacWindow.cpp +++ b/Karma/src/Platform/Mac/MacWindow.cpp @@ -1,5 +1,4 @@ #include "MacWindow.h" -#include "Karma/Log.h" #include "Karma/Events/ApplicationEvent.h" #include "Karma/Events/KeyEvent.h" #include "Karma/Events/MouseEvent.h" diff --git a/Karma/src/Platform/OpenGL/OpenGLContext.cpp b/Karma/src/Platform/OpenGL/OpenGLContext.cpp index efa5adde..8ac847aa 100644 --- a/Karma/src/Platform/OpenGL/OpenGLContext.cpp +++ b/Karma/src/Platform/OpenGL/OpenGLContext.cpp @@ -1,7 +1,7 @@ #include "OpenGLContext.h" + #include "glad/glad.h" #include "GLFW/glfw3.h" -#include "Karma/Core.h" namespace Karma { diff --git a/Karma/src/Platform/Windows/WindowsWindow.cpp b/Karma/src/Platform/Windows/WindowsWindow.cpp index 75d7b977..82057cdd 100644 --- a/Karma/src/Platform/Windows/WindowsWindow.cpp +++ b/Karma/src/Platform/Windows/WindowsWindow.cpp @@ -1,5 +1,4 @@ #include "WindowsWindow.h" -#include "Karma/Log.h" #include "Karma/Events/ApplicationEvent.h" #include "Karma/Events/KeyEvent.h" #include "Karma/Events/MouseEvent.h" @@ -251,4 +250,4 @@ namespace Karma { return m_Data.VSync; } -} \ No newline at end of file +} From eff93128dceaf8b97f1d79f8a4e749021731c4d7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 7 Dec 2025 15:28:21 +0530 Subject: [PATCH 005/107] ImGuizmo incarnation in KarmaGuizmo --- Karma/src/Karma/KarmaGui/KarmaGuizmo.cpp | 3141 ++++++++++++++++++++++ Karma/src/Karma/KarmaGui/KarmaGuizmo.h | 305 +++ 2 files changed, 3446 insertions(+) create mode 100644 Karma/src/Karma/KarmaGui/KarmaGuizmo.cpp create mode 100644 Karma/src/Karma/KarmaGui/KarmaGuizmo.h diff --git a/Karma/src/Karma/KarmaGui/KarmaGuizmo.cpp b/Karma/src/Karma/KarmaGui/KarmaGuizmo.cpp new file mode 100644 index 00000000..44c29efa --- /dev/null +++ b/Karma/src/Karma/KarmaGui/KarmaGuizmo.cpp @@ -0,0 +1,3141 @@ +// https://github.com/CedricGuillemet/KarmaGuizmo +// v1.92.5 WIP +// +// The MIT License(MIT) +// +// Copyright(c) 2016-2021 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include "KarmaGui.h" +#include "KarmaGuiInternal.h" + +#include "KarmaGuizmo.h" + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#endif +#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR) +#define _malloca(x) alloca(x) +#define _freea(x) +#endif + +// includes patches for multiview from +// https://github.com/CedricGuillemet/KarmaGuizmo/issues/15 + +namespace KARMAGUIZMO_NAMESPACE +{ + static const float ZPI = 3.14159265358979323846f; + static const float RAD2DEG = (180.f / ZPI); + static const float DEG2RAD = (ZPI / 180.f); + const float screenRotateSize = 0.06f; + // scale a bit so translate axis do not touch when in universal + const float rotationDisplayFactor = 1.2f; + + static OPERATION operator&(OPERATION lhs, OPERATION rhs) + { + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + + static bool operator!=(OPERATION lhs, int rhs) + { + return static_cast(lhs) != rhs; + } + + static bool Intersects(OPERATION lhs, OPERATION rhs) + { + return (lhs & rhs) != 0; + } + + // True if lhs contains rhs + static bool Contains(OPERATION lhs, OPERATION rhs) + { + return (lhs & rhs) == rhs; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // utility and math + + void FPU_MatrixF_x_MatrixF(const float* a, const float* b, float* r) + { + r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; + r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; + r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; + r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; + + r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; + r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; + r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; + r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; + + r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; + r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; + r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; + r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; + + r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; + r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; + r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; + r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; + } + + void Frustum(float left, float right, float bottom, float top, float znear, float zfar, float* m16) + { + float temp, temp2, temp3, temp4; + temp = 2.0f * znear; + temp2 = right - left; + temp3 = top - bottom; + temp4 = zfar - znear; + m16[0] = temp / temp2; + m16[1] = 0.0; + m16[2] = 0.0; + m16[3] = 0.0; + m16[4] = 0.0; + m16[5] = temp / temp3; + m16[6] = 0.0; + m16[7] = 0.0; + m16[8] = (right + left) / temp2; + m16[9] = (top + bottom) / temp3; + m16[10] = (-zfar - znear) / temp4; + m16[11] = -1.0f; + m16[12] = 0.0; + m16[13] = 0.0; + m16[14] = (-temp * zfar) / temp4; + m16[15] = 0.0; + } + + void Perspective(float fovyInDegrees, float aspectRatio, float znear, float zfar, float* m16) + { + float ymax, xmax; + ymax = znear * tanf(fovyInDegrees * DEG2RAD); + xmax = ymax * aspectRatio; + Frustum(-xmax, xmax, -ymax, ymax, znear, zfar, m16); + } + + void Cross(const float* a, const float* b, float* r) + { + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; + } + + float Dot(const float* a, const float* b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + void Normalize(const float* a, float* r) + { + float il = 1.f / (sqrtf(Dot(a, a)) + FLT_EPSILON); + r[0] = a[0] * il; + r[1] = a[1] * il; + r[2] = a[2] * il; + } + + void LookAt(const float* eye, const float* at, const float* up, float* m16) + { + float X[3], Y[3], Z[3], tmp[3]; + + tmp[0] = eye[0] - at[0]; + tmp[1] = eye[1] - at[1]; + tmp[2] = eye[2] - at[2]; + Normalize(tmp, Z); + Normalize(up, Y); + Cross(Y, Z, tmp); + Normalize(tmp, X); + Cross(Z, X, tmp); + Normalize(tmp, Y); + + m16[0] = X[0]; + m16[1] = Y[0]; + m16[2] = Z[0]; + m16[3] = 0.0f; + m16[4] = X[1]; + m16[5] = Y[1]; + m16[6] = Z[1]; + m16[7] = 0.0f; + m16[8] = X[2]; + m16[9] = Y[2]; + m16[10] = Z[2]; + m16[11] = 0.0f; + m16[12] = -Dot(X, eye); + m16[13] = -Dot(Y, eye); + m16[14] = -Dot(Z, eye); + m16[15] = 1.0f; + } + + template T Clamp(T x, T y, T z) { return ((x < y) ? y : ((x > z) ? z : x)); } + template T max(T x, T y) { return (x > y) ? x : y; } + template T min(T x, T y) { return (x < y) ? x : y; } + template bool IsWithin(T x, T y, T z) { return (x >= y) && (x <= z); } + + struct matrix_t; + struct vec_t + { + public: + float x, y, z, w; + + void Lerp(const vec_t& v, float t) + { + x += (v.x - x) * t; + y += (v.y - y) * t; + z += (v.z - z) * t; + w += (v.w - w) * t; + } + + void Set(float v) { x = y = z = w = v; } + void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; } + + vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } + vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } + vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; } + vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; } + + vec_t operator * (float f) const; + vec_t operator - () const; + vec_t operator - (const vec_t& v) const; + vec_t operator + (const vec_t& v) const; + vec_t operator * (const vec_t& v) const; + + const vec_t& operator + () const { return (*this); } + float Length() const { return sqrtf(x * x + y * y + z * z); }; + float LengthSq() const { return (x * x + y * y + z * z); }; + vec_t Normalize() { (*this) *= (1.f / ( Length() > FLT_EPSILON ? Length() : FLT_EPSILON ) ); return (*this); } + vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } + vec_t Abs() const; + + void Cross(const vec_t& v) + { + vec_t res; + res.x = y * v.z - z * v.y; + res.y = z * v.x - x * v.z; + res.z = x * v.y - y * v.x; + + x = res.x; + y = res.y; + z = res.z; + w = 0.f; + } + + void Cross(const vec_t& v1, const vec_t& v2) + { + x = v1.y * v2.z - v1.z * v2.y; + y = v1.z * v2.x - v1.x * v2.z; + z = v1.x * v2.y - v1.y * v2.x; + w = 0.f; + } + + float Dot(const vec_t& v) const + { + return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w); + } + + float Dot3(const vec_t& v) const + { + return (x * v.x) + (y * v.y) + (z * v.z); + } + + void Transform(const matrix_t& matrix); + void Transform(const vec_t& s, const matrix_t& matrix); + + void TransformVector(const matrix_t& matrix); + void TransformPoint(const matrix_t& matrix); + void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); } + void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); } + + float& operator [] (size_t index) { return ((float*)&x)[index]; } + const float& operator [] (size_t index) const { return ((float*)&x)[index]; } + bool operator!=(const vec_t& other) const { return memcmp(this, &other, sizeof(vec_t)) != 0; } + }; + + vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) { vec_t res; res.x = _x; res.y = _y; res.z = _z; res.w = _w; return res; } + vec_t makeVect(KGVec2 v) { vec_t res; res.x = v.x; res.y = v.y; res.z = 0.f; res.w = 0.f; return res; } + vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w * f); } + vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); } + vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); } + vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } + vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } + vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } + + vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } + vec_t Cross(const vec_t& v1, const vec_t& v2) + { + vec_t res; + res.x = v1.y * v2.z - v1.z * v2.y; + res.y = v1.z * v2.x - v1.x * v2.z; + res.z = v1.x * v2.y - v1.y * v2.x; + res.w = 0.f; + return res; + } + + float Dot(const vec_t& v1, const vec_t& v2) + { + return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); + } + + vec_t BuildPlan(const vec_t& p_point1, const vec_t& p_normal) + { + vec_t normal, res; + normal.Normalize(p_normal); + res.w = normal.Dot(p_point1); + res.x = normal.x; + res.y = normal.y; + res.z = normal.z; + return res; + } + + struct matrix_t + { + public: + + union + { + float m[4][4]; + float m16[16]; + struct + { + vec_t right, up, dir, position; + } v; + vec_t component[4]; + }; + + operator float* () { return m16; } + operator const float* () const { return m16; } + void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); } + + void Translation(const vec_t& vt) + { + v.right.Set(1.f, 0.f, 0.f, 0.f); + v.up.Set(0.f, 1.f, 0.f, 0.f); + v.dir.Set(0.f, 0.f, 1.f, 0.f); + v.position.Set(vt.x, vt.y, vt.z, 1.f); + } + + void Scale(float _x, float _y, float _z) + { + v.right.Set(_x, 0.f, 0.f, 0.f); + v.up.Set(0.f, _y, 0.f, 0.f); + v.dir.Set(0.f, 0.f, _z, 0.f); + v.position.Set(0.f, 0.f, 0.f, 1.f); + } + void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); } + + matrix_t& operator *= (const matrix_t& mat) + { + matrix_t tmpMat; + tmpMat = *this; + tmpMat.Multiply(mat); + *this = tmpMat; + return *this; + } + matrix_t operator * (const matrix_t& mat) const + { + matrix_t matT; + matT.Multiply(*this, mat); + return matT; + } + + void Multiply(const matrix_t& matrix) + { + matrix_t tmp; + tmp = *this; + + FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this); + } + + void Multiply(const matrix_t& m1, const matrix_t& m2) + { + FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this); + } + + float GetDeterminant() const + { + return m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - + m[0][2] * m[1][1] * m[2][0] - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1]; + } + + float Inverse(const matrix_t& srcMatrix, bool affine = false); + void SetToIdentity() + { + v.right.Set(1.f, 0.f, 0.f, 0.f); + v.up.Set(0.f, 1.f, 0.f, 0.f); + v.dir.Set(0.f, 0.f, 1.f, 0.f); + v.position.Set(0.f, 0.f, 0.f, 1.f); + } + void Transpose() + { + matrix_t tmpm; + for (int l = 0; l < 4; l++) + { + for (int c = 0; c < 4; c++) + { + tmpm.m[l][c] = m[c][l]; + } + } + (*this) = tmpm; + } + + void RotationAxis(const vec_t& axis, float angle); + + void OrthoNormalize() + { + v.right.Normalize(); + v.up.Normalize(); + v.dir.Normalize(); + } + }; + + void vec_t::Transform(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + void vec_t::Transform(const vec_t& s, const matrix_t& matrix) + { + *this = s; + Transform(matrix); + } + + void vec_t::TransformPoint(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + void vec_t::TransformVector(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + float matrix_t::Inverse(const matrix_t& srcMatrix, bool affine) + { + float det = 0; + + if (affine) + { + det = GetDeterminant(); + float s = 1 / det; + m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s; + m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s; + m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s; + m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s; + m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s; + m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s; + m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s; + m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s; + m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s; + m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]); + m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]); + m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]); + } + else + { + // transpose matrix + float src[16]; + for (int i = 0; i < 4; ++i) + { + src[i] = srcMatrix.m16[i * 4]; + src[i + 4] = srcMatrix.m16[i * 4 + 1]; + src[i + 8] = srcMatrix.m16[i * 4 + 2]; + src[i + 12] = srcMatrix.m16[i * 4 + 3]; + } + + // calculate pairs for first 8 elements (cofactors) + float tmp[12]; // temp array for pairs + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + + // calculate first 8 elements (cofactors) + m16[0] = (tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]) - (tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]); + m16[1] = (tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]) - (tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]); + m16[2] = (tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]) - (tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]); + m16[3] = (tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]) - (tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]); + m16[4] = (tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]) - (tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]); + m16[5] = (tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]) - (tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]); + m16[6] = (tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]) - (tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]); + m16[7] = (tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]) - (tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]); + + // calculate pairs for second 8 elements (cofactors) + tmp[0] = src[2] * src[7]; + tmp[1] = src[3] * src[6]; + tmp[2] = src[1] * src[7]; + tmp[3] = src[3] * src[5]; + tmp[4] = src[1] * src[6]; + tmp[5] = src[2] * src[5]; + tmp[6] = src[0] * src[7]; + tmp[7] = src[3] * src[4]; + tmp[8] = src[0] * src[6]; + tmp[9] = src[2] * src[4]; + tmp[10] = src[0] * src[5]; + tmp[11] = src[1] * src[4]; + + // calculate second 8 elements (cofactors) + m16[8] = (tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]) - (tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]); + m16[9] = (tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]) - (tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]); + m16[10] = (tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]) - (tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]); + m16[11] = (tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]) - (tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]); + m16[12] = (tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]) - (tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]); + m16[13] = (tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]) - (tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]); + m16[14] = (tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]) - (tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]); + m16[15] = (tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]) - (tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]); + + // calculate determinant + det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3]; + + // calculate matrix inverse + float invdet = 1 / det; + for (int j = 0; j < 16; ++j) + { + m16[j] *= invdet; + } + } + + return det; + } + + void matrix_t::RotationAxis(const vec_t& axis, float angle) + { + float length2 = axis.LengthSq(); + if (length2 < FLT_EPSILON) + { + SetToIdentity(); + return; + } + + vec_t n = axis * (1.f / sqrtf(length2)); + float s = sinf(angle); + float c = cosf(angle); + float k = 1.f - c; + + float xx = n.x * n.x * k + c; + float yy = n.y * n.y * k + c; + float zz = n.z * n.z * k + c; + float xy = n.x * n.y * k; + float yz = n.y * n.z * k; + float zx = n.z * n.x * k; + float xs = n.x * s; + float ys = n.y * s; + float zs = n.z * s; + + m[0][0] = xx; + m[0][1] = xy + zs; + m[0][2] = zx - ys; + m[0][3] = 0.f; + m[1][0] = xy - zs; + m[1][1] = yy; + m[1][2] = yz + xs; + m[1][3] = 0.f; + m[2][0] = zx + ys; + m[2][1] = yz - xs; + m[2][2] = zz; + m[2][3] = 0.f; + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = 0.f; + m[3][3] = 1.f; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + + enum MOVETYPE + { + MT_NONE, + MT_MOVE_X, + MT_MOVE_Y, + MT_MOVE_Z, + MT_MOVE_YZ, + MT_MOVE_ZX, + MT_MOVE_XY, + MT_MOVE_SCREEN, + MT_ROTATE_X, + MT_ROTATE_Y, + MT_ROTATE_Z, + MT_ROTATE_SCREEN, + MT_SCALE_X, + MT_SCALE_Y, + MT_SCALE_Z, + MT_SCALE_XYZ + }; + + static bool IsTranslateType(int type) + { + return type >= MT_MOVE_X && type <= MT_MOVE_SCREEN; + } + + static bool IsRotateType(int type) + { + return type >= MT_ROTATE_X && type <= MT_ROTATE_SCREEN; + } + + static bool IsScaleType(int type) + { + return type >= MT_SCALE_X && type <= MT_SCALE_XYZ; + } + + // Matches MT_MOVE_AB order + static const OPERATION TRANSLATE_PLANS[3] = { TRANSLATE_Y | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Y }; + + Style::Style() + { + // default values + TranslationLineThickness = 3.0f; + TranslationLineArrowSize = 6.0f; + RotationLineThickness = 2.0f; + RotationOuterLineThickness = 3.0f; + ScaleLineThickness = 3.0f; + ScaleLineCircleSize = 6.0f; + HatchedAxisLineThickness = 6.0f; + CenterCircleSize = 6.0f; + + // initialize default colors + Colors[DIRECTION_X] = KGVec4(0.666f, 0.000f, 0.000f, 1.000f); + Colors[DIRECTION_Y] = KGVec4(0.000f, 0.666f, 0.000f, 1.000f); + Colors[DIRECTION_Z] = KGVec4(0.000f, 0.000f, 0.666f, 1.000f); + Colors[PLANE_X] = KGVec4(0.666f, 0.000f, 0.000f, 0.380f); + Colors[PLANE_Y] = KGVec4(0.000f, 0.666f, 0.000f, 0.380f); + Colors[PLANE_Z] = KGVec4(0.000f, 0.000f, 0.666f, 0.380f); + Colors[SELECTION] = KGVec4(1.000f, 0.500f, 0.062f, 0.541f); + Colors[INACTIVE] = KGVec4(0.600f, 0.600f, 0.600f, 0.600f); + Colors[TRANSLATION_LINE] = KGVec4(0.666f, 0.666f, 0.666f, 0.666f); + Colors[SCALE_LINE] = KGVec4(0.250f, 0.250f, 0.250f, 1.000f); + Colors[ROTATION_USING_BORDER] = KGVec4(1.000f, 0.500f, 0.062f, 1.000f); + Colors[ROTATION_USING_FILL] = KGVec4(1.000f, 0.500f, 0.062f, 0.500f); + Colors[HATCHED_AXIS_LINES] = KGVec4(0.000f, 0.000f, 0.000f, 0.500f); + Colors[TEXT] = KGVec4(1.000f, 1.000f, 1.000f, 1.000f); + Colors[TEXT_SHADOW] = KGVec4(0.000f, 0.000f, 0.000f, 1.000f); + } + + struct Context + { + Context() : mbUsing(false), mbUsingViewManipulate(false), mbEnable(true), mIsViewManipulatorHovered(false), mbUsingBounds(false) + { + } + + KGDrawList* mDrawList; + Style mStyle; + + MODE mMode; + matrix_t mViewMat; + matrix_t mProjectionMat; + matrix_t mModel; + matrix_t mModelLocal; // orthonormalized model + matrix_t mModelInverse; + matrix_t mModelSource; + matrix_t mModelSourceInverse; + matrix_t mMVP; + matrix_t mMVPLocal; // MVP with full model matrix whereas mMVP's model matrix might only be translation in case of World space edition + matrix_t mViewProjection; + + vec_t mModelScaleOrigin; + vec_t mCameraEye; + vec_t mCameraRight; + vec_t mCameraDir; + vec_t mCameraUp; + vec_t mRayOrigin; + vec_t mRayVector; + + float mRadiusSquareCenter; + KGVec2 mScreenSquareCenter; + KGVec2 mScreenSquareMin; + KGVec2 mScreenSquareMax; + + float mScreenFactor; + vec_t mRelativeOrigin; + + bool mbUsing; + bool mbUsingViewManipulate; + bool mbEnable; + bool mbMouseOver; + bool mReversed; // reversed projection matrix + bool mIsViewManipulatorHovered; + + // translation + vec_t mTranslationPlan; + vec_t mTranslationPlanOrigin; + vec_t mMatrixOrigin; + vec_t mTranslationLastDelta; + + // rotation + vec_t mRotationVectorSource; + float mRotationAngle; + float mRotationAngleOrigin; + //vec_t mWorldToLocalAxis; + + // scale + vec_t mScale; + vec_t mScaleValueOrigin; + vec_t mScaleLast; + float mSaveMousePosx; + + // save axis factor when using gizmo + bool mBelowAxisLimit[3]; + int mAxisMask = 0; + bool mBelowPlaneLimit[3]; + float mAxisFactor[3]; + + float mAxisLimit=0.0025f; + float mPlaneLimit=0.02f; + + // bounds stretching + vec_t mBoundsPivot; + vec_t mBoundsAnchor; + vec_t mBoundsPlan; + vec_t mBoundsLocalPivot; + int mBoundsBestAxis; + int mBoundsAxis[2]; + bool mbUsingBounds; + matrix_t mBoundsMatrix; + + // + int mCurrentOperation; + + float mX = 0.f; + float mY = 0.f; + float mWidth = 0.f; + float mHeight = 0.f; + float mXMax = 0.f; + float mYMax = 0.f; + float mDisplayRatio = 1.f; + + bool mIsOrthographic = false; + // check to not have multiple gizmo highlighted at the same time + bool mbOverGizmoHotspot = false; + + KGGuiWindow* mAlternativeWindow = nullptr; + KGVector mIDStack; + KGGuiID mEditingID = -1; + OPERATION mOperation = OPERATION(-1); + + bool mAllowAxisFlip = true; + float mGizmoSizeClipSpace = 0.1f; + + inline KGGuiID GetCurrentID() + { + if (mIDStack.empty()) + { + mIDStack.push_back(-1); + } + return mIDStack.back(); + } + }; + + static Context gContext; + + static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; + static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", + "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f", + "X : %5.3f Y : %5.3f Z : %5.3f" }; + static const char* scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" }; + static const char* rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" }; + static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 1,2,0, 0,2,0, 0,1,0, 0,1,2 }; + static const float quadMin = 0.5f; + static const float quadMax = 0.8f; + static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin }; + static const int halfCircleSegmentCount = 64; + static const float snapTension = 0.5f; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion); + static int GetRotateType(OPERATION op); + static int GetScaleType(OPERATION op); + + Style& GetStyle() + { + return gContext.mStyle; + } + + static KGU32 GetColorU32(int idx) + { + KR_CORE_ASSERT(idx < COLOR::COUNT, "Non color index maybe"); + return Karma::KarmaGui::ColorConvertFloat4ToU32(gContext.mStyle.Colors[idx]); + } + + static KGVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, KGVec2 position = KGVec2(gContext.mX, gContext.mY), KGVec2 size = KGVec2(gContext.mWidth, gContext.mHeight)) + { + vec_t trans; + trans.TransformPoint(worldPos, mat); + trans *= 0.5f / trans.w; + trans += makeVect(0.5f, 0.5f); + trans.y = 1.f - trans.y; + trans.x *= size.x; + trans.y *= size.y; + trans.x += position.x; + trans.y += position.y; + return KGVec2(trans.x, trans.y); + } + + static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, KGVec2 position = KGVec2(gContext.mX, gContext.mY), KGVec2 size = KGVec2(gContext.mWidth, gContext.mHeight)) + { + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + + matrix_t mViewProjInverse; + mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat); + + const float mox = ((io.MousePos.x - position.x) / size.x) * 2.f - 1.f; + const float moy = (1.f - ((io.MousePos.y - position.y) / size.y)) * 2.f - 1.f; + + const float zNear = gContext.mReversed ? (1.f - FLT_EPSILON) : 0.f; + const float zFar = gContext.mReversed ? 0.f : (1.f - FLT_EPSILON); + + rayOrigin.Transform(makeVect(mox, moy, zNear, 1.f), mViewProjInverse); + rayOrigin *= 1.f / rayOrigin.w; + vec_t rayEnd; + rayEnd.Transform(makeVect(mox, moy, zFar, 1.f), mViewProjInverse); + rayEnd *= 1.f / rayEnd.w; + rayDir = Normalized(rayEnd - rayOrigin); + } + + static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false) + { + vec_t startOfSegment = start; + const matrix_t& mvp = localCoordinates ? gContext.mMVPLocal : gContext.mMVP; + startOfSegment.TransformPoint(mvp); + if (fabsf(startOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction + { + startOfSegment *= 1.f / startOfSegment.w; + } + + vec_t endOfSegment = end; + endOfSegment.TransformPoint(mvp); + if (fabsf(endOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction + { + endOfSegment *= 1.f / endOfSegment.w; + } + + vec_t clipSpaceAxis = endOfSegment - startOfSegment; + if (gContext.mDisplayRatio < 1.0) + clipSpaceAxis.x *= gContext.mDisplayRatio; + else + clipSpaceAxis.y /= gContext.mDisplayRatio; + float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x * clipSpaceAxis.x + clipSpaceAxis.y * clipSpaceAxis.y); + return segmentLengthInClipSpace; + } + + static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB) + { + vec_t pts[] = { ptO, ptA, ptB }; + for (unsigned int i = 0; i < 3; i++) + { + pts[i].TransformPoint(gContext.mMVP); + if (fabsf(pts[i].w) > FLT_EPSILON) // check for axis aligned with camera direction + { + pts[i] *= 1.f / pts[i].w; + } + } + vec_t segA = pts[1] - pts[0]; + vec_t segB = pts[2] - pts[0]; + segA.y /= gContext.mDisplayRatio; + segB.y /= gContext.mDisplayRatio; + vec_t segAOrtho = makeVect(-segA.y, segA.x); + segAOrtho.Normalize(); + float dt = segAOrtho.Dot3(segB); + float surface = sqrtf(segA.x * segA.x + segA.y * segA.y) * fabsf(dt); + return surface; + } + + inline vec_t PointOnSegment(const vec_t& point, const vec_t& vertPos1, const vec_t& vertPos2) + { + vec_t c = point - vertPos1; + vec_t V; + + V.Normalize(vertPos2 - vertPos1); + float d = (vertPos2 - vertPos1).Length(); + float t = V.Dot3(c); + + if (t < 0.f) + { + return vertPos1; + } + + if (t > d) + { + return vertPos2; + } + + return vertPos1 + V * t; + } + + static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan) + { + const float numer = plan.Dot3(rOrigin) - plan.w; + const float denom = plan.Dot3(rVector); + + if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect + { + return -1.0f; + } + + return -(numer / denom); + } + + static float DistanceToPlane(const vec_t& point, const vec_t& plan) + { + return plan.Dot3(point) + plan.w; + } + + static bool IsInContextRect(KGVec2 p) + { + return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax); + } + + static bool IsHoveringWindow() + { + KarmaGuiContext& g = *Karma::KarmaGui::GetCurrentContext(); + KGGuiWindow* window = Karma::KarmaGuiInternal::FindWindowByName(gContext.mDrawList->_OwnerName); + if (g.HoveredWindow == window) // Mouse hovering drawlist window + return true; + if (gContext.mAlternativeWindow != nullptr && g.HoveredWindow == gContext.mAlternativeWindow) + return true; + if (g.HoveredWindow != NULL) // Any other window is hovered + return false; + if (Karma::KarmaGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max, false)) // Hovering drawlist window rect, while no other window is hovered (for _NoInputs windows) + return true; + return false; + } + + void SetRect(float x, float y, float width, float height) + { + gContext.mX = x; + gContext.mY = y; + gContext.mWidth = width; + gContext.mHeight = height; + gContext.mXMax = gContext.mX + gContext.mWidth; + gContext.mYMax = gContext.mY + gContext.mXMax; + gContext.mDisplayRatio = width / height; + } + + void SetOrthographic(bool isOrthographic) + { + gContext.mIsOrthographic = isOrthographic; + } + + void SetDrawlist(KGDrawList* drawlist) + { + gContext.mDrawList = drawlist ? drawlist : Karma::KarmaGui::GetWindowDrawList(); + } + + void SetKarmaGuiContext(KarmaGuiContext* ctx) + { + Karma::KarmaGui::SetCurrentContext(ctx); + } + + void BeginFrame() + { + const KGU32 flags = KGGuiWindowFlags_NoTitleBar | KGGuiWindowFlags_NoResize | KGGuiWindowFlags_NoScrollbar | KGGuiWindowFlags_NoInputs | KGGuiWindowFlags_NoSavedSettings | KGGuiWindowFlags_NoFocusOnAppearing | KGGuiWindowFlags_NoBringToFrontOnFocus; + +#ifdef KGGUI_HAS_VIEWPORT + KarmaGui::SetNextWindowSize(KarmaGui::GetMainViewport()->Size); + KarmaGui::SetNextWindowPos(KarmaGui::GetMainViewport()->Pos); +#else + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + Karma::KarmaGui::SetNextWindowSize(io.DisplaySize); + Karma::KarmaGui::SetNextWindowPos(KGVec2(0, 0)); +#endif + + Karma::KarmaGui::PushStyleColor(KGGuiCol_WindowBg, 0); + Karma::KarmaGui::PushStyleColor(KGGuiCol_Border, 0); + Karma::KarmaGui::PushStyleVar(KGGuiStyleVar_WindowRounding, 0.0f); + + Karma::KarmaGui::Begin("gizmo", NULL, flags); + gContext.mDrawList = Karma::KarmaGui::GetWindowDrawList(); + gContext.mbOverGizmoHotspot = false; + Karma::KarmaGui::End(); + Karma::KarmaGui::PopStyleVar(); + Karma::KarmaGui::PopStyleColor(2); + } + + bool IsUsing() + { + return (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) || gContext.mbUsingBounds; + } + + bool IsUsingViewManipulate() + { + return gContext.mbUsingViewManipulate; + } + + bool IsViewManipulateHovered() + { + return gContext.mIsViewManipulatorHovered; + } + + bool IsUsingAny() + { + return gContext.mbUsing || gContext.mbUsingBounds; + } + + bool IsOver() + { + return (Intersects(gContext.mOperation, TRANSLATE) && GetMoveType(gContext.mOperation, NULL) != MT_NONE) || + (Intersects(gContext.mOperation, ROTATE) && GetRotateType(gContext.mOperation) != MT_NONE) || + (Intersects(gContext.mOperation, SCALE) && GetScaleType(gContext.mOperation) != MT_NONE) || IsUsing(); + } + + bool IsOver(OPERATION op) + { + if(IsUsing()) + { + return true; + } + if(Intersects(op, SCALE) && GetScaleType(op) != MT_NONE) + { + return true; + } + if(Intersects(op, ROTATE) && GetRotateType(op) != MT_NONE) + { + return true; + } + if(Intersects(op, TRANSLATE) && GetMoveType(op, NULL) != MT_NONE) + { + return true; + } + return false; + } + + void Enable(bool enable) + { + gContext.mbEnable = enable; + if (!enable) + { + gContext.mbUsing = false; + gContext.mbUsingBounds = false; + } + } + + static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode) + { + gContext.mMode = mode; + gContext.mViewMat = *(matrix_t*)view; + gContext.mProjectionMat = *(matrix_t*)projection; + gContext.mbMouseOver = IsHoveringWindow(); + + gContext.mModelLocal = *(matrix_t*)matrix; + gContext.mModelLocal.OrthoNormalize(); + + if (mode == LOCAL) + { + gContext.mModel = gContext.mModelLocal; + } + else + { + gContext.mModel.Translation(((matrix_t*)matrix)->v.position); + } + gContext.mModelSource = *(matrix_t*)matrix; + gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); + + gContext.mModelInverse.Inverse(gContext.mModel); + gContext.mModelSourceInverse.Inverse(gContext.mModelSource); + gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat; + gContext.mMVP = gContext.mModel * gContext.mViewProjection; + gContext.mMVPLocal = gContext.mModelLocal * gContext.mViewProjection; + + matrix_t viewInverse; + viewInverse.Inverse(gContext.mViewMat); + gContext.mCameraDir = viewInverse.v.dir; + gContext.mCameraEye = viewInverse.v.position; + gContext.mCameraRight = viewInverse.v.right; + gContext.mCameraUp = viewInverse.v.up; + + // projection reverse + vec_t nearPos, farPos; + nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat); + farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat); + + gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w); + + // compute scale from the size of camera right vector projected on screen at the matrix position + vec_t pointRight = viewInverse.v.right; + pointRight.TransformPoint(gContext.mViewProjection); + + vec_t rightViewInverse = viewInverse.v.right; + rightViewInverse.TransformVector(gContext.mModelInverse); + float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse); + gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength; + + KGVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP); + gContext.mScreenSquareCenter = centerSSpace; + gContext.mScreenSquareMin = KGVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f); + gContext.mScreenSquareMax = KGVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f); + + ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector); + } + + static void ComputeColors(KGU32* colors, int type, OPERATION operation) + { + if (gContext.mbEnable) + { + KGU32 selectionColor = GetColorU32(SELECTION); + + switch (operation) + { + case TRANSLATE: + colors[0] = (type == MT_MOVE_SCREEN) ? selectionColor : KG_COL32_WHITE; + for (int i = 0; i < 3; i++) + { + colors[i + 1] = (type == (int)(MT_MOVE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); + colors[i + 4] = (type == (int)(MT_MOVE_YZ + i)) ? selectionColor : GetColorU32(PLANE_X + i); + colors[i + 4] = (type == MT_MOVE_SCREEN) ? selectionColor : colors[i + 4]; + } + break; + case ROTATE: + colors[0] = (type == MT_ROTATE_SCREEN) ? selectionColor : KG_COL32_WHITE; + for (int i = 0; i < 3; i++) + { + colors[i + 1] = (type == (int)(MT_ROTATE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); + } + break; + case SCALEU: + case SCALE: + colors[0] = (type == MT_SCALE_XYZ) ? selectionColor : KG_COL32_WHITE; + for (int i = 0; i < 3; i++) + { + colors[i + 1] = (type == (int)(MT_SCALE_X + i)) ? selectionColor : GetColorU32(DIRECTION_X + i); + } + break; + // note: this internal function is only called with three possible values for operation + default: + break; + } + } + else + { + KGU32 inactiveColor = GetColorU32(INACTIVE); + for (int i = 0; i < 7; i++) + { + colors[i] = inactiveColor; + } + } + } + + static void ComputeTripodAxisAndVisibility(const int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit, const bool localCoordinates = false) + { + dirAxis = directionUnary[axisIndex]; + dirPlaneX = directionUnary[(axisIndex + 1) % 3]; + dirPlaneY = directionUnary[(axisIndex + 2) % 3]; + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) + { + // when using, use stored factors so the gizmo doesn't flip when we translate + + // Apply axis mask to axes and planes + belowAxisLimit = gContext.mBelowAxisLimit[axisIndex] && ((1< FLT_EPSILON) ? -1.f : 1.f; + float mulAxisX = (allowFlip && lenDirPlaneX < lenDirMinusPlaneX&& fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f; + float mulAxisY = (allowFlip && lenDirPlaneY < lenDirMinusPlaneY&& fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f; + dirAxis *= mulAxis; + dirPlaneX *= mulAxisX; + dirPlaneY *= mulAxisY; + + // for axis + float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor, localCoordinates); + + float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor); + // Apply axis mask to axes and planes + belowPlaneLimit = (paraSurf > gContext.mAxisLimit) && (((1< gContext.mPlaneLimit) && !((1< (1.f - snapTension)) + { + *value = *value - modulo + snap * ((*value < 0.f) ? -1.f : 1.f); + } + } + static void ComputeSnap(vec_t& value, const float* snap) + { + for (int i = 0; i < 3; i++) + { + ComputeSnap(&value[i], snap[i]); + } + } + + static float ComputeAngleOnPlan() + { + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position); + + vec_t perpendicularVector; + perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan); + perpendicularVector.Normalize(); + float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -1.f, 1.f); + float angle = acosf(acosAngle); + angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f; + return angle; + } + + static void DrawRotationGizmo(OPERATION op, int type) + { + if(!Intersects(op, ROTATE)) + { + return; + } + KGDrawList* drawList = gContext.mDrawList; + + bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0; + bool isNoAxesMasked = !gContext.mAxisMask; + + // colors + KGU32 colors[7]; + ComputeColors(colors, type, ROTATE); + + vec_t viewDirNormalized; + if (gContext.mIsOrthographic) + { + matrix_t viewInverse; + viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat); + viewDirNormalized = -viewInverse.v.dir; + } + else + { + viewDirNormalized = Normalized(gContext.mCameraDir); + } + + viewDirNormalized.TransformVector(gContext.mModelInverse); + + gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight; + + bool hasRSC = Intersects(op, ROTATE_SCREEN); + for (int axis = 0; axis < 3; axis++) + { + if(!Intersects(op, static_cast(ROTATE_Z >> axis))) + { + continue; + } + + bool isAxisMasked = ((1 << (2 - axis)) & gContext.mAxisMask) != 0; + + if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked) + { + continue; + } + const bool usingAxis = (gContext.mbUsing && type == MT_ROTATE_Z - axis); + const int circleMul = (hasRSC && !usingAxis) ? 1 : 2; + + KGVec2* circlePos = (KGVec2*)alloca(sizeof(KGVec2) * (circleMul * halfCircleSegmentCount + 1)); + + float angleStart = atan2f(viewDirNormalized[(4 - axis) % 3], viewDirNormalized[(3 - axis) % 3]) + (gContext.mIsOrthographic ? ZPI : -ZPI) * 0.5f; + + for (int i = 0; i < circleMul * halfCircleSegmentCount + 1; i++) + { + float ng = angleStart + (float)circleMul * ZPI * ((float)i / (float)(circleMul * halfCircleSegmentCount)); + vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f); + vec_t pos = makeVect(axisPos[axis], axisPos[(axis + 1) % 3], axisPos[(axis + 2) % 3]) * gContext.mScreenFactor * rotationDisplayFactor; + circlePos[i] = worldToPos(pos, gContext.mMVP); + } + if (!gContext.mbUsing || usingAxis) + { + drawList->AddPolyline(circlePos, circleMul* halfCircleSegmentCount + 1, colors[3 - axis], false, gContext.mStyle.RotationLineThickness); + } + + float radiusAxis = sqrtf((KGLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]))); + if (radiusAxis > gContext.mRadiusSquareCenter) + { + gContext.mRadiusSquareCenter = radiusAxis; + } + } + if(hasRSC && (!gContext.mbUsing || type == MT_ROTATE_SCREEN) && (!isMultipleAxesMasked && isNoAxesMasked)) + { + drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, gContext.mStyle.RotationOuterLineThickness); + } + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsRotateType(type)) + { + KGVec2 circlePos[halfCircleSegmentCount + 1]; + + circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + for (unsigned int i = 1; i < halfCircleSegmentCount + 1; i++) + { + float ng = gContext.mRotationAngle * ((float)(i - 1) / (float)(halfCircleSegmentCount - 1)); + matrix_t rotateVectorMatrix; + rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng); + vec_t pos; + pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix); + pos *= gContext.mScreenFactor * rotationDisplayFactor; + circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection); + } + drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount + 1, GetColorU32(ROTATION_USING_FILL)); + drawList->AddPolyline(circlePos, halfCircleSegmentCount + 1, GetColorU32(ROTATION_USING_BORDER), true, gContext.mStyle.RotationLineThickness); + + KGVec2 destinationPosOnScreen = circlePos[1]; + char tmps[512]; + KGFormatString(tmps, sizeof(tmps), rotationInfoMask[type - MT_ROTATE_X], (gContext.mRotationAngle / ZPI) * 180.f, gContext.mRotationAngle); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); + } + } + + static void DrawHatchedAxis(const vec_t& axis) + { + if (gContext.mStyle.HatchedAxisLineThickness <= 0.0f) + { + return; + } + + for (int j = 1; j < 10; j++) + { + KGVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP); + gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, GetColorU32(HATCHED_AXIS_LINES), gContext.mStyle.HatchedAxisLineThickness); + } + } + + static void DrawScaleGizmo(OPERATION op, int type) + { + KGDrawList* drawList = gContext.mDrawList; + + if(!Intersects(op, SCALE)) + { + return; + } + + // colors + KGU32 colors[7]; + ComputeColors(colors, type, SCALE); + + // draw + vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) + { + scaleDisplay = gContext.mScale; + } + + for (int i = 0; i < 3; i++) + { + if(!Intersects(op, static_cast(SCALE_X << i))) + { + continue; + } + const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i); + if (!gContext.mbUsing || usingAxis) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); + + // draw axis + if (belowAxisLimit) + { + bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); + float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; + KGVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP); + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) + { + KGU32 scaleLineColor = GetColorU32(SCALE_LINE); + drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, scaleLineColor, gContext.mStyle.ScaleLineThickness); + drawList->AddCircleFilled(worldDirSSpaceNoScale, gContext.mStyle.ScaleLineCircleSize, scaleLineColor); + } + + if (!hasTranslateOnAxis || gContext.mbUsing) + { + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], gContext.mStyle.ScaleLineThickness); + } + drawList->AddCircleFilled(worldDirSSpace, gContext.mStyle.ScaleLineCircleSize, colors[i + 1]); + + if (gContext.mAxisFactor[i] < 0.f) + { + DrawHatchedAxis(dirAxis * scaleDisplay[i]); + } + } + } + } + + // draw screen cirle + drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(type)) + { + //KGVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); + KGVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); + dif.Normalize(); + dif *= 5.f; + drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); + drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); + drawList->AddLine(KGVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), KGVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); + */ + char tmps[512]; + //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; + int componentInfoIndex = (type - MT_SCALE_X) * 3; + KGFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); + } + } + + + static void DrawScaleUniveralGizmo(OPERATION op, int type) + { + KGDrawList* drawList = gContext.mDrawList; + + if (!Intersects(op, SCALEU)) + { + return; + } + + // colors + KGU32 colors[7]; + ComputeColors(colors, type, SCALEU); + + // draw + vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) + { + scaleDisplay = gContext.mScale; + } + + for (int i = 0; i < 3; i++) + { + if (!Intersects(op, static_cast(SCALE_XU << i))) + { + continue; + } + const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i); + if (!gContext.mbUsing || usingAxis) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); + + // draw axis + if (belowAxisLimit) + { + bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); + float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; + //KGVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal); + //KGVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal); + +#if 0 + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) + { + drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, KG_COL32(0x40, 0x40, 0x40, 0xFF), 3.f); + drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, KG_COL32(0x40, 0x40, 0x40, 0xFF)); + } + /* + if (!hasTranslateOnAxis || gContext.mbUsing) + { + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); + } + */ +#endif + drawList->AddCircleFilled(worldDirSSpace, 12.f, colors[i + 1]); + } + } + } + + // draw screen cirle + drawList->AddCircle(gContext.mScreenSquareCenter, 20.f, colors[0], 32, gContext.mStyle.CenterCircleSize); + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(type)) + { + //KGVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); + KGVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); + dif.Normalize(); + dif *= 5.f; + drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); + drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); + drawList->AddLine(KGVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), KGVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); + */ + char tmps[512]; + //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; + int componentInfoIndex = (type - MT_SCALE_X) * 3; + KGFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); + } + } + + static void DrawTranslationGizmo(OPERATION op, int type) + { + KGDrawList* drawList = gContext.mDrawList; + if (!drawList) + { + return; + } + + if(!Intersects(op, TRANSLATE)) + { + return; + } + + // colors + KGU32 colors[7]; + ComputeColors(colors, type, TRANSLATE); + + const KGVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + + // draw + bool belowAxisLimit = false; + bool belowPlaneLimit = false; + for (int i = 0; i < 3; ++i) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + + if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_X + i)) + { + // draw axis + if (belowAxisLimit && Intersects(op, static_cast(TRANSLATE_X << i))) + { + KGVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP); + + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], gContext.mStyle.TranslationLineThickness); + + // Arrow head begin + KGVec2 dir(origin - worldDirSSpace); + + float d = sqrtf(KGLengthSqr(dir)); + dir /= d; // Normalize + dir *= gContext.mStyle.TranslationLineArrowSize; + + KGVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector + KGVec2 a(worldDirSSpace + dir); + drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]); + // Arrow head end + + if (gContext.mAxisFactor[i] < 0.f) + { + DrawHatchedAxis(dirAxis); + } + } + } + // draw plane + if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_YZ + i)) + { + if (belowPlaneLimit && Contains(op, TRANSLATE_PLANS[i])) + { + KGVec2 screenQuadPts[4]; + for (int j = 0; j < 4; ++j) + { + vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor; + screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP); + } + drawList->AddPolyline(screenQuadPts, 4, GetColorU32(DIRECTION_X + i), true, 1.0f); + drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]); + } + } + } + + drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); + + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsTranslateType(type)) + { + KGU32 translationLineColor = GetColorU32(TRANSLATION_LINE); + + KGVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); + KGVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f }; + dif.Normalize(); + dif *= 5.f; + drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); + drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); + drawList->AddLine(KGVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), KGVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); + + char tmps[512]; + vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; + int componentInfoIndex = (type - MT_MOVE_X) * 3; + KGFormatString(tmps, sizeof(tmps), translationInfoMask[type - MT_MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); + } + } + + static bool CanActivate() + { + if (Karma::KarmaGui::IsMouseClicked(0) && !Karma::KarmaGui::IsAnyItemHovered() && !Karma::KarmaGui::IsAnyItemActive()) + { + return true; + } + return false; + } + + static bool HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation) + { + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + KGDrawList* drawList = gContext.mDrawList; + + bool manipulated = false; + + // compute best projection axis + vec_t axesWorldDirections[3]; + vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f }; + int axes[3]; + unsigned int numAxes = 1; + axes[0] = gContext.mBoundsBestAxis; + int bestAxis = axes[0]; + if (!gContext.mbUsingBounds) + { + numAxes = 0; + float bestDot = 0.f; + for (int i = 0; i < 3; i++) + { + vec_t dirPlaneNormalWorld; + dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); + dirPlaneNormalWorld.Normalize(); + + float dt = fabsf(Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld)); + if (dt >= bestDot) + { + bestDot = dt; + bestAxis = i; + bestAxisWorldDirection = dirPlaneNormalWorld; + } + + if (dt >= 0.1f) + { + axes[numAxes] = i; + axesWorldDirections[numAxes] = dirPlaneNormalWorld; + ++numAxes; + } + } + } + + if (numAxes == 0) + { + axes[0] = bestAxis; + axesWorldDirections[0] = bestAxisWorldDirection; + numAxes = 1; + } + + else if (bestAxis != axes[0]) + { + unsigned int bestIndex = 0; + for (unsigned int i = 0; i < numAxes; i++) + { + if (axes[i] == bestAxis) + { + bestIndex = i; + break; + } + } + int tempAxis = axes[0]; + axes[0] = axes[bestIndex]; + axes[bestIndex] = tempAxis; + vec_t tempDirection = axesWorldDirections[0]; + axesWorldDirections[0] = axesWorldDirections[bestIndex]; + axesWorldDirections[bestIndex] = tempDirection; + } + + for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex) + { + bestAxis = axes[axisIndex]; + bestAxisWorldDirection = axesWorldDirections[axisIndex]; + + // corners + vec_t aabb[4]; + + int secondAxis = (bestAxis + 1) % 3; + int thirdAxis = (bestAxis + 2) % 3; + + for (int i = 0; i < 4; i++) + { + aabb[i][3] = aabb[i][bestAxis] = 0.f; + aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; + aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; + } + + // draw bounds + unsigned int anchorAlpha = gContext.mbEnable ? KG_COL32_BLACK : KG_COL32(0, 0, 0, 0x80); + + matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; + for (int i = 0; i < 4; i++) + { + KGVec2 worldBound1 = worldToPos(aabb[i], boundsMVP); + KGVec2 worldBound2 = worldToPos(aabb[(i + 1) % 4], boundsMVP); + if (!IsInContextRect(worldBound1) || !IsInContextRect(worldBound2)) + { + continue; + } + float boundDistance = sqrtf(KGLengthSqr(worldBound1 - worldBound2)); + int stepCount = (int)(boundDistance / 10.f); + stepCount = min(stepCount, 1000); + for (int j = 0; j < stepCount; j++) + { + float stepLength = 1.f / (float)stepCount; + float t1 = (float)j * stepLength; + float t2 = (float)j * stepLength + stepLength * 0.5f; + KGVec2 worldBoundSS1 = KGLerp(worldBound1, worldBound2, KGVec2(t1, t1)); + KGVec2 worldBoundSS2 = KGLerp(worldBound1, worldBound2, KGVec2(t2, t2)); + //drawList->AddLine(worldBoundSS1, worldBoundSS2, KG_COL32(0, 0, 0, 0) + anchorAlpha, 3.f); + drawList->AddLine(worldBoundSS1, worldBoundSS2, KG_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha, 2.f); + } + vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4]) * 0.5f; + KGVec2 midBound = worldToPos(midPoint, boundsMVP); + static const float AnchorBigRadius = 8.f; + static const float AnchorSmallRadius = 6.f; + bool overBigAnchor = KGLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius); + bool overSmallAnchor = KGLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius); + + int type = MT_NONE; + vec_t gizmoHitProportion; + + if(Intersects(operation, TRANSLATE)) + { + type = GetMoveType(operation, &gizmoHitProportion); + } + if(Intersects(operation, ROTATE) && type == MT_NONE) + { + type = GetRotateType(operation); + } + if(Intersects(operation, SCALE) && type == MT_NONE) + { + type = GetScaleType(operation); + } + + if (type != MT_NONE) + { + overBigAnchor = false; + overSmallAnchor = false; + } + + KGU32 selectionColor = GetColorU32(SELECTION); + + unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (KG_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha); + unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (KG_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha); + + drawList->AddCircleFilled(worldBound1, AnchorBigRadius, KG_COL32_BLACK); + drawList->AddCircleFilled(worldBound1, AnchorBigRadius - 1.2f, bigAnchorColor); + + drawList->AddCircleFilled(midBound, AnchorSmallRadius, KG_COL32_BLACK); + drawList->AddCircleFilled(midBound, AnchorSmallRadius - 1.2f, smallAnchorColor); + int oppositeIndex = (i + 2) % 4; + // big anchor on corners + if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate()) + { + gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + gContext.mBoundsAxis[0] = secondAxis; + gContext.mBoundsAxis[1] = thirdAxis; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; + gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; + + gContext.mbUsingBounds = true; + gContext.mEditingID = gContext.GetCurrentID(); + gContext.mBoundsMatrix = gContext.mModelSource; + } + // small anchor on middle of segment + if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate()) + { + vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; + gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + int indices[] = { secondAxis , thirdAxis }; + gContext.mBoundsAxis[0] = indices[i % 2]; + gContext.mBoundsAxis[1] = -1; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); + + gContext.mbUsingBounds = true; + gContext.mEditingID = gContext.GetCurrentID(); + gContext.mBoundsMatrix = gContext.mModelSource; + } + } + + if (gContext.mbUsingBounds && (gContext.GetCurrentID() == gContext.mEditingID)) + { + matrix_t scale; + scale.SetToIdentity(); + + // compute projected mouse position on plan + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + + // compute a reference and delta vectors base on mouse move + vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); + vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); + + // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length + for (int i = 0; i < 2; i++) + { + int axisIndex1 = gContext.mBoundsAxis[i]; + if (axisIndex1 == -1) + { + continue; + } + + float ratioAxis = 1.f; + vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs(); + + float dtAxis = axisDir.Dot(referenceVector); + float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1]; + if (dtAxis > FLT_EPSILON) + { + ratioAxis = axisDir.Dot(deltaVector) / dtAxis; + } + + if (snapValues) + { + float length = boundSize * ratioAxis; + ComputeSnap(&length, snapValues[axisIndex1]); + if (boundSize > FLT_EPSILON) + { + ratioAxis = length / boundSize; + } + } + scale.component[axisIndex1] *= ratioAxis; + + if (fabsf(ratioAxis - 1.0f) > FLT_EPSILON) { + manipulated = true; + } + } + + // transform matrix + matrix_t preScale, postScale; + preScale.Translation(-gContext.mBoundsLocalPivot); + postScale.Translation(gContext.mBoundsLocalPivot); + matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; + *matrix = res; + + // info text + char tmps[512]; + KGVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + KGFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z: %.2f" + , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length() + , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length() + , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length() + ); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), GetColorU32(TEXT_SHADOW), tmps); + drawList->AddText(KGVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), GetColorU32(TEXT), tmps); + } + + if (!io.MouseDown[0]) { + gContext.mbUsingBounds = false; + gContext.mEditingID = -1; + } + if (gContext.mbUsingBounds) + { + break; + } + } + + return manipulated; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + + static int GetScaleType(OPERATION op) + { + if (gContext.mbUsing) + { + return MT_NONE; + } + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + int type = MT_NONE; + + // screen + if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && + io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y && + Contains(op, SCALE)) + { + type = MT_SCALE_XYZ; + } + + // compute + for (int i = 0; i < 3 && type == MT_NONE; i++) + { + if(!Intersects(op, static_cast(SCALE_X << i))) + { + continue; + } + bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0; + + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); + dirAxis.TransformVector(gContext.mModelLocal); + dirPlaneX.TransformVector(gContext.mModelLocal); + dirPlaneY.TransformVector(gContext.mModelLocal); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModelLocal.v.position, dirAxis)); + vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; + + const float startOffset = Contains(op, static_cast(TRANSLATE_X << i)) ? 1.0f : 0.1f; + const float endOffset = Contains(op, static_cast(TRANSLATE_X << i)) ? 1.4f : 1.0f; + const KGVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection); + const KGVec2 axisStartOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * startOffset, gContext.mViewProjection); + const KGVec2 axisEndOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * endOffset, gContext.mViewProjection); + + vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); + + if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size + { + if (!isAxisMasked) + type = MT_SCALE_X + i; + } + } + + // universal + + vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; + float dist = deltaScreen.Length(); + if (Contains(op, SCALEU) && dist >= 17.0f && dist < 23.0f) + { + type = MT_SCALE_XYZ; + } + + for (int i = 0; i < 3 && type == MT_NONE; i++) + { + if (!Intersects(op, static_cast(SCALE_XU << i))) + { + continue; + } + + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); + + // draw axis + if (belowAxisLimit) + { + bool hasTranslateOnAxis = Contains(op, static_cast(TRANSLATE_X << i)); + float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f; + //KGVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal); + //KGVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); + KGVec2 worldDirSSpace = worldToPos((dirAxis * markerScale) * gContext.mScreenFactor, gContext.mMVPLocal); + + float distance = sqrtf(KGLengthSqr(worldDirSSpace - io.MousePos)); + if (distance < 12.f) + { + type = MT_SCALE_X + i; + } + } + } + return type; + } + + static int GetRotateType(OPERATION op) + { + if (gContext.mbUsing) + { + return MT_NONE; + } + + bool isNoAxesMasked = !gContext.mAxisMask; + bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0; + + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + int type = MT_NONE; + + vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; + float dist = deltaScreen.Length(); + if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f)) + { + if (!isNoAxesMasked) + return MT_NONE; + type = MT_ROTATE_SCREEN; + } + + const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir }; + + vec_t modelViewPos; + modelViewPos.TransformPoint(gContext.mModel.v.position, gContext.mViewMat); + + for (int i = 0; i < 3 && type == MT_NONE; i++) + { + if(!Intersects(op, static_cast(ROTATE_X << i))) + { + continue; + } + bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0; + // pickup plan + vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan); + const vec_t intersectWorldPos = gContext.mRayOrigin + gContext.mRayVector * len; + vec_t intersectViewPos; + intersectViewPos.TransformPoint(intersectWorldPos, gContext.mViewMat); + + if (KGAbs(modelViewPos.z) - KGAbs(intersectViewPos.z) < -FLT_EPSILON) + { + continue; + } + + const vec_t localPos = intersectWorldPos - gContext.mModel.v.position; + vec_t idealPosOnCircle = Normalized(localPos); + idealPosOnCircle.TransformVector(gContext.mModelInverse); + const KGVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * rotationDisplayFactor * gContext.mScreenFactor, gContext.mMVP); + + //gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, KG_COL32_WHITE); + const KGVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos; + + const float distance = makeVect(distanceOnScreen).Length(); + if (distance < 8.f) // pixel size + { + if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked) + break; + type = MT_ROTATE_X + i; + } + } + + return type; + } + + static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion) + { + if(!Intersects(op, TRANSLATE) || gContext.mbUsing || !gContext.mbMouseOver) + { + return MT_NONE; + } + + bool isNoAxesMasked = !gContext.mAxisMask; + bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0; + + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + int type = MT_NONE; + + // screen + if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && + io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y && + Contains(op, TRANSLATE)) + { + type = MT_MOVE_SCREEN; + } + + const vec_t screenCoord = makeVect(io.MousePos - KGVec2(gContext.mX, gContext.mY)); + + // compute + for (int i = 0; i < 3 && type == MT_NONE; i++) + { + bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0; + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + dirAxis.TransformVector(gContext.mModel); + dirPlaneX.TransformVector(gContext.mModel); + dirPlaneY.TransformVector(gContext.mModel); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis)); + vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; + + const KGVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection) - KGVec2(gContext.mX, gContext.mY); + const KGVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection) - KGVec2(gContext.mX, gContext.mY); + + vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); + if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast(TRANSLATE_X << i))) // pixel size + { + if (isAxisMasked) + break; + type = MT_MOVE_X + i; + } + + const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); + const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); + if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i])) + { + if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked) + break; + type = MT_MOVE_YZ + i; + } + + if (gizmoHitProportion) + { + *gizmoHitProportion = makeVect(dx, dy, 0.f); + } + } + return type; + } + + static bool HandleTranslation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) + { + if(!Intersects(op, TRANSLATE) || type != MT_NONE) + { + return false; + } + const KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + const bool applyRotationLocaly = gContext.mMode == LOCAL || type == MT_MOVE_SCREEN; + bool modified = false; + + // move + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation)) + { + + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + + const float signedLength = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + const float len = fabsf(signedLength); // near plan + const vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + + // compute delta + const vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; + vec_t delta = newOrigin - gContext.mModel.v.position; + + // 1 axis constraint + if (gContext.mCurrentOperation >= MT_MOVE_X && gContext.mCurrentOperation <= MT_MOVE_Z) + { + const int axisIndex = gContext.mCurrentOperation - MT_MOVE_X; + const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; + const float lengthOnAxis = Dot(axisValue, delta); + delta = axisValue * lengthOnAxis; + } + + // snap + if (snap) + { + vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin; + if (applyRotationLocaly) + { + matrix_t modelSourceNormalized = gContext.mModelSource; + modelSourceNormalized.OrthoNormalize(); + matrix_t modelSourceNormalizedInverse; + modelSourceNormalizedInverse.Inverse(modelSourceNormalized); + cumulativeDelta.TransformVector(modelSourceNormalizedInverse); + ComputeSnap(cumulativeDelta, snap); + cumulativeDelta.TransformVector(modelSourceNormalized); + } + else + { + ComputeSnap(cumulativeDelta, snap); + } + delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position; + + } + + if (delta != gContext.mTranslationLastDelta) + { + modified = true; + } + gContext.mTranslationLastDelta = delta; + + // compute matrix & delta + matrix_t deltaMatrixTranslation; + deltaMatrixTranslation.Translation(delta); + if (deltaMatrix) + { + memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16); + } + + const matrix_t res = gContext.mModelSource * deltaMatrixTranslation; + *(matrix_t*)matrix = res; + + if (!io.MouseDown[0]) + { + gContext.mbUsing = false; + } + + type = gContext.mCurrentOperation; + } + else + { + // find new possible way to move + vec_t gizmoHitProportion; + type = gContext.mbOverGizmoHotspot ? MT_NONE : GetMoveType(op, &gizmoHitProportion); + gContext.mbOverGizmoHotspot |= type != MT_NONE; + if (type != MT_NONE) + { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + } + if (CanActivate() && type != MT_NONE) + { + gContext.mbUsing = true; + gContext.mEditingID = gContext.GetCurrentID(); + gContext.mCurrentOperation = type; + vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, + gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, + -gContext.mCameraDir }; + + vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); + for (unsigned int i = 0; i < 3; i++) + { + vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized); + movePlanNormal[i].Cross(orthoVector); + movePlanNormal[i].Normalize(); + } + // pickup plan + gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_MOVE_X]); + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; + gContext.mMatrixOrigin = gContext.mModel.v.position; + + gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); + } + } + return modified; + } + + static bool HandleScale(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) + { + if((!Intersects(op, SCALE) && !Intersects(op, SCALEU)) || type != MT_NONE || !gContext.mbMouseOver) + { + return false; + } + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + bool modified = false; + + if (!gContext.mbUsing) + { + // find new possible way to scale + type = gContext.mbOverGizmoHotspot ? MT_NONE : GetScaleType(op); + gContext.mbOverGizmoHotspot |= type != MT_NONE; + + if (type != MT_NONE) + { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + } + if (CanActivate() && type != MT_NONE) + { + gContext.mbUsing = true; + gContext.mEditingID = gContext.GetCurrentID(); + gContext.mCurrentOperation = type; + const vec_t movePlanNormal[] = { gContext.mModelLocal.v.up, gContext.mModelLocal.v.dir, gContext.mModelLocal.v.right, gContext.mModelLocal.v.dir, gContext.mModelLocal.v.up, gContext.mModelLocal.v.right, -gContext.mCameraDir }; + // pickup plan + + gContext.mTranslationPlan = BuildPlan(gContext.mModelLocal.v.position, movePlanNormal[type - MT_SCALE_X]); + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; + gContext.mMatrixOrigin = gContext.mModelLocal.v.position; + gContext.mScale.Set(1.f, 1.f, 1.f); + gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModelLocal.v.position) * (1.f / gContext.mScreenFactor); + gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); + gContext.mSaveMousePosx = io.MousePos.x; + } + } + // scale + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation)) + { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; + vec_t delta = newOrigin - gContext.mModelLocal.v.position; + + // 1 axis constraint + if (gContext.mCurrentOperation >= MT_SCALE_X && gContext.mCurrentOperation <= MT_SCALE_Z) + { + int axisIndex = gContext.mCurrentOperation - MT_SCALE_X; + const vec_t& axisValue = *(vec_t*)&gContext.mModelLocal.m[axisIndex]; + float lengthOnAxis = Dot(axisValue, delta); + delta = axisValue * lengthOnAxis; + + vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModelLocal.v.position; + float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector); + + gContext.mScale[axisIndex] = max(ratio, 0.001f); + } + else + { + float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f; + gContext.mScale.Set(max(1.f + scaleDelta, 0.001f)); + } + + // snap + if (snap) + { + float scaleSnap[] = { snap[0], snap[0], snap[0] }; + ComputeSnap(gContext.mScale, scaleSnap); + } + + // no 0 allowed + for (int i = 0; i < 3; i++) + gContext.mScale[i] = max(gContext.mScale[i], 0.001f); + + if (gContext.mScaleLast != gContext.mScale) + { + modified = true; + } + gContext.mScaleLast = gContext.mScale; + + // compute matrix & delta + matrix_t deltaMatrixScale; + deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin); + + matrix_t res = deltaMatrixScale * gContext.mModelLocal; + *(matrix_t*)matrix = res; + + if (deltaMatrix) + { + vec_t deltaScale = gContext.mScale * gContext.mScaleValueOrigin; + + vec_t originalScaleDivider; + originalScaleDivider.x = 1 / gContext.mModelScaleOrigin.x; + originalScaleDivider.y = 1 / gContext.mModelScaleOrigin.y; + originalScaleDivider.z = 1 / gContext.mModelScaleOrigin.z; + + deltaScale = deltaScale * originalScaleDivider; + + deltaMatrixScale.Scale(deltaScale); + memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16); + } + + if (!io.MouseDown[0]) + { + gContext.mbUsing = false; + gContext.mScale.Set(1.f, 1.f, 1.f); + } + + type = gContext.mCurrentOperation; + } + return modified; + } + + static bool HandleRotation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) + { + if(!Intersects(op, ROTATE) || type != MT_NONE || !gContext.mbMouseOver) + { + return false; + } + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + bool applyRotationLocaly = gContext.mMode == LOCAL; + bool modified = false; + + if (!gContext.mbUsing) + { + type = gContext.mbOverGizmoHotspot ? MT_NONE : GetRotateType(op); + gContext.mbOverGizmoHotspot |= type != MT_NONE; + + if (type != MT_NONE) + { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + } + + if (type == MT_ROTATE_SCREEN) + { + applyRotationLocaly = true; + } + + if (CanActivate() && type != MT_NONE) + { + gContext.mbUsing = true; + gContext.mEditingID = gContext.GetCurrentID(); + gContext.mCurrentOperation = type; + const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; + // pickup plan + if (applyRotationLocaly) + { + gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - MT_ROTATE_X]); + } + else + { + gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - MT_ROTATE_X]); + } + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; + gContext.mRotationVectorSource = Normalized(localPos); + gContext.mRotationAngleOrigin = ComputeAngleOnPlan(); + } + } + + // rotation + if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation)) + { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + + gContext.mRotationAngle = ComputeAngleOnPlan(); + if (snap) + { + float snapInRadian = snap[0] * DEG2RAD; + ComputeSnap(&gContext.mRotationAngle, snapInRadian); + } + vec_t rotationAxisLocalSpace; + + rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse); + rotationAxisLocalSpace.Normalize(); + + matrix_t deltaRotation; + deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin); + if (gContext.mRotationAngle != gContext.mRotationAngleOrigin) + { + modified = true; + } + gContext.mRotationAngleOrigin = gContext.mRotationAngle; + + matrix_t scaleOrigin; + scaleOrigin.Scale(gContext.mModelScaleOrigin); + + if (applyRotationLocaly) + { + *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModelLocal; + } + else + { + matrix_t res = gContext.mModelSource; + res.v.position.Set(0.f); + + *(matrix_t*)matrix = res * deltaRotation; + ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position; + } + + if (deltaMatrix) + { + *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel; + } + + if (!io.MouseDown[0]) + { + gContext.mbUsing = false; + gContext.mEditingID = -1; + } + type = gContext.mCurrentOperation; + } + return modified; + } + + void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale) + { + matrix_t mat = *(matrix_t*)matrix; + + scale[0] = mat.v.right.Length(); + scale[1] = mat.v.up.Length(); + scale[2] = mat.v.dir.Length(); + + mat.OrthoNormalize(); + + rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]); + rotation[1] = RAD2DEG * atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2] * mat.m[2][2])); + rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]); + + translation[0] = mat.v.position.x; + translation[1] = mat.v.position.y; + translation[2] = mat.v.position.z; + } + + void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix) + { + matrix_t& mat = *(matrix_t*)matrix; + + matrix_t rot[3]; + for (int i = 0; i < 3; i++) + { + rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD); + } + + mat = rot[0] * rot[1] * rot[2]; + + float validScale[3]; + for (int i = 0; i < 3; i++) + { + if (fabsf(scale[i]) < FLT_EPSILON) + { + validScale[i] = 0.001f; + } + else + { + validScale[i] = scale[i]; + } + } + mat.v.right *= validScale[0]; + mat.v.up *= validScale[1]; + mat.v.dir *= validScale[2]; + mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); + } + + void SetAlternativeWindow(KGGuiWindow* window) + { + gContext.mAlternativeWindow = window; + } + + void SetID(int id) + { + if (gContext.mIDStack.empty()) + { + gContext.mIDStack.push_back(-1); + } + gContext.mIDStack.back() = id; + } + + KGGuiID GetID(const char* str, const char* str_end) + { + KGGuiID seed = gContext.GetCurrentID(); + KGGuiID id = KGHashStr(str, str_end ? (str_end - str) : 0, seed); + return id; + } + + KGGuiID GetID(const char* str) + { + return GetID(str, nullptr); + } + + KGGuiID GetID(const void* ptr) + { + KGGuiID seed = gContext.GetCurrentID(); + KGGuiID id = KGHashData(&ptr, sizeof(void*), seed); + return id; + } + + KGGuiID GetID(int n) + { + KGGuiID seed = gContext.GetCurrentID(); + KGGuiID id = KGHashData(&n, sizeof(n), seed); + return id; + } + + void PushID(const char* str_id) + { + KGGuiID id = GetID(str_id); + gContext.mIDStack.push_back(id); + } + + void PushID(const char* str_id_begin, const char* str_id_end) + { + KGGuiID id = GetID(str_id_begin, str_id_end); + gContext.mIDStack.push_back(id); + } + + void PushID(const void* ptr_id) + { + KGGuiID id = GetID(ptr_id); + gContext.mIDStack.push_back(id); + } + + void PushID(int int_id) + { + KGGuiID id = GetID(int_id); + gContext.mIDStack.push_back(id); + } + + void PopID() + { + KR_CORE_ASSERT(gContext.mIDStack.Size > 1, "Too many PopID(), or could be popping in a wrong/different window?"); // Too many PopID(), or could be popping in a wrong/different window? + gContext.mIDStack.pop_back(); + if (gContext.mIDStack.empty()) + { + gContext.mIDStack.clear(); + } + } + + void AllowAxisFlip(bool value) + { + gContext.mAllowAxisFlip = value; + } + + void SetAxisLimit(float value) + { + gContext.mAxisLimit=value; + } + + void SetAxisMask(bool x, bool y, bool z) + { + gContext.mAxisMask = (x ? 1 : 0) + (y ? 2 : 0) + (z ? 4 : 0); + } + + void SetPlaneLimit(float value) + { + gContext.mPlaneLimit = value; + } + + bool IsOver(float* position, float pixelRadius) + { + const KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + + float radius = sqrtf((KGLengthSqr(worldToPos({ position[0], position[1], position[2], 0.0f }, gContext.mViewProjection) - io.MousePos))); + return radius < pixelRadius; + } + + bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap) + { + gContext.mDrawList->PushClipRect (KGVec2 (gContext.mX, gContext.mY), KGVec2 (gContext.mX + gContext.mWidth, gContext.mY + gContext.mHeight), false); + + // Scale is always local or matrix will be skewed when applying world scale or oriented matrix + ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); + + // set delta to identity + if (deltaMatrix) + { + ((matrix_t*)deltaMatrix)->SetToIdentity(); + } + + // behind camera + vec_t camSpacePosition; + camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP); + if (!gContext.mIsOrthographic && camSpacePosition.z < 0.001f && !gContext.mbUsing) + { + return false; + } + + // -- + int type = MT_NONE; + bool manipulated = false; + if (gContext.mbEnable) + { + if (!gContext.mbUsingBounds) + { + manipulated = HandleTranslation(matrix, deltaMatrix, operation, type, snap) || + HandleScale(matrix, deltaMatrix, operation, type, snap) || + HandleRotation(matrix, deltaMatrix, operation, type, snap); + } + } + + if (localBounds && !gContext.mbUsing) + { + manipulated |= HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation); + } + + gContext.mOperation = operation; + if (!gContext.mbUsingBounds) + { + DrawRotationGizmo(operation, type); + DrawTranslationGizmo(operation, type); + DrawScaleGizmo(operation, type); + DrawScaleUniveralGizmo(operation, type); + } + + gContext.mDrawList->PopClipRect (); + return manipulated; + } + + void SetGizmoSizeClipSpace(float value) + { + gContext.mGizmoSizeClipSpace = value; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeFrustumPlanes(vec_t* frustum, const float* clip) + { + frustum[0].x = clip[3] - clip[0]; + frustum[0].y = clip[7] - clip[4]; + frustum[0].z = clip[11] - clip[8]; + frustum[0].w = clip[15] - clip[12]; + + frustum[1].x = clip[3] + clip[0]; + frustum[1].y = clip[7] + clip[4]; + frustum[1].z = clip[11] + clip[8]; + frustum[1].w = clip[15] + clip[12]; + + frustum[2].x = clip[3] + clip[1]; + frustum[2].y = clip[7] + clip[5]; + frustum[2].z = clip[11] + clip[9]; + frustum[2].w = clip[15] + clip[13]; + + frustum[3].x = clip[3] - clip[1]; + frustum[3].y = clip[7] - clip[5]; + frustum[3].z = clip[11] - clip[9]; + frustum[3].w = clip[15] - clip[13]; + + frustum[4].x = clip[3] - clip[2]; + frustum[4].y = clip[7] - clip[6]; + frustum[4].z = clip[11] - clip[10]; + frustum[4].w = clip[15] - clip[14]; + + frustum[5].x = clip[3] + clip[2]; + frustum[5].y = clip[7] + clip[6]; + frustum[5].z = clip[11] + clip[10]; + frustum[5].w = clip[15] + clip[14]; + + for (int i = 0; i < 6; i++) + { + frustum[i].Normalize(); + } + } + + void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount) + { + matrix_t viewInverse; + viewInverse.Inverse(*(matrix_t*)view); + + struct CubeFace + { + float z; + KGVec2 faceCoordsScreen[4]; + KGU32 color; + }; + CubeFace* faces = (CubeFace*)_malloca(sizeof(CubeFace) * matrixCount * 6); + + if (!faces) + { + return; + } + + vec_t frustum[6]; + matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection; + ComputeFrustumPlanes(frustum, viewProjection.m16); + + int cubeFaceCount = 0; + for (int cube = 0; cube < matrixCount; cube++) + { + const float* matrix = &matrices[cube * 16]; + + matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection; + + for (int iFace = 0; iFace < 6; iFace++) + { + const int normalIndex = (iFace % 3); + const int perpXIndex = (normalIndex + 1) % 3; + const int perpYIndex = (normalIndex + 2) % 3; + const float invert = (iFace > 2) ? -1.f : 1.f; + + const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex], + directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex], + directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex], + directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex], + }; + + // clipping + /* + bool skipFace = false; + for (unsigned int iCoord = 0; iCoord < 4; iCoord++) + { + vec_t camSpacePosition; + camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, res); + if (camSpacePosition.z < 0.001f) + { + skipFace = true; + break; + } + } + if (skipFace) + { + continue; + } + */ + vec_t centerPosition, centerPositionVP; + centerPosition.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, *(matrix_t*)matrix); + centerPositionVP.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, res); + + bool inFrustum = true; + for (int iFrustum = 0; iFrustum < 6; iFrustum++) + { + float dist = DistanceToPlane(centerPosition, frustum[iFrustum]); + if (dist < 0.f) + { + inFrustum = false; + break; + } + } + + if (!inFrustum) + { + continue; + } + CubeFace& cubeFace = faces[cubeFaceCount]; + + // 3D->2D + //KGVec2 faceCoordsScreen[4]; + for (unsigned int iCoord = 0; iCoord < 4; iCoord++) + { + cubeFace.faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res); + } + + KGU32 directionColor = GetColorU32(DIRECTION_X + normalIndex); + cubeFace.color = directionColor | KG_COL32(0x80, 0x80, 0x80, 0); + + cubeFace.z = centerPositionVP.z / centerPositionVP.w; + cubeFaceCount++; + } + } + qsort(faces, cubeFaceCount, sizeof(CubeFace), [](void const* _a, void const* _b) { + CubeFace* a = (CubeFace*)_a; + CubeFace* b = (CubeFace*)_b; + if (a->z < b->z) + { + return 1; + } + return -1; + }); + // draw face with lighter color + for (int iFace = 0; iFace < cubeFaceCount; iFace++) + { + const CubeFace& cubeFace = faces[iFace]; + gContext.mDrawList->AddConvexPolyFilled(cubeFace.faceCoordsScreen, 4, cubeFace.color); + } + + _freea(faces); + } + + void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize) + { + matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection; + vec_t frustum[6]; + ComputeFrustumPlanes(frustum, viewProjection.m16); + matrix_t res = *(matrix_t*)matrix * viewProjection; + + for (float f = -gridSize; f <= gridSize; f += 1.f) + { + for (int dir = 0; dir < 2; dir++) + { + vec_t ptA = makeVect(dir ? -gridSize : f, 0.f, dir ? f : -gridSize); + vec_t ptB = makeVect(dir ? gridSize : f, 0.f, dir ? f : gridSize); + bool visible = true; + for (int i = 0; i < 6; i++) + { + float dA = DistanceToPlane(ptA, frustum[i]); + float dB = DistanceToPlane(ptB, frustum[i]); + if (dA < 0.f && dB < 0.f) + { + visible = false; + break; + } + if (dA > 0.f && dB > 0.f) + { + continue; + } + if (dA < 0.f) + { + float len = fabsf(dA - dB); + float t = fabsf(dA) / len; + ptA.Lerp(ptB, t); + } + if (dB < 0.f) + { + float len = fabsf(dB - dA); + float t = fabsf(dB) / len; + ptB.Lerp(ptA, t); + } + } + if (visible) + { + KGU32 col = KG_COL32(0x80, 0x80, 0x80, 0xFF); + col = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? KG_COL32(0x90, 0x90, 0x90, 0xFF) : col; + col = (fabsf(f) < FLT_EPSILON) ? KG_COL32(0x40, 0x40, 0x40, 0xFF): col; + + float thickness = 1.f; + thickness = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? 1.5f : thickness; + thickness = (fabsf(f) < FLT_EPSILON) ? 2.3f : thickness; + + gContext.mDrawList->AddLine(worldToPos(ptA, res), worldToPos(ptB, res), col, thickness); + } + } + } + } + + void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor) + { + // Scale is always local or matrix will be skewed when applying world scale or oriented matrix + ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); + ViewManipulate(view, length, position, size, backgroundColor); + } + + void ViewManipulate(float* view, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor) + { + static bool isDraging = false; + static bool isClicking = false; + static vec_t interpolationUp; + static vec_t interpolationDir; + static int interpolationFrames = 0; + const vec_t referenceUp = makeVect(0.f, 1.f, 0.f); + + matrix_t svgView, svgProjection; + svgView = gContext.mViewMat; + svgProjection = gContext.mProjectionMat; + + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + gContext.mDrawList->AddRectFilled(position, position + size, backgroundColor); + matrix_t viewInverse; + viewInverse.Inverse(*(matrix_t*)view); + + const vec_t camTarget = viewInverse.v.position - viewInverse.v.dir * length; + + // view/projection matrices + const float distance = 3.f; + matrix_t cubeProjection, cubeView; + float fov = acosf(distance / (sqrtf(distance * distance + 3.f))) * RAD2DEG; + Perspective(fov / sqrtf(2.f), size.x / size.y, 0.01f, 1000.f, cubeProjection.m16); + + vec_t dir = makeVect(viewInverse.m[2][0], viewInverse.m[2][1], viewInverse.m[2][2]); + vec_t up = makeVect(viewInverse.m[1][0], viewInverse.m[1][1], viewInverse.m[1][2]); + vec_t eye = dir * distance; + vec_t zero = makeVect(0.f, 0.f); + LookAt(&eye.x, &zero.x, &up.x, cubeView.m16); + + // set context + gContext.mViewMat = cubeView; + gContext.mProjectionMat = cubeProjection; + ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector, position, size); + + const matrix_t res = cubeView * cubeProjection; + + // panels + static const KGVec2 panelPosition[9] = { KGVec2(0.75f,0.75f), KGVec2(0.25f, 0.75f), KGVec2(0.f, 0.75f), + KGVec2(0.75f, 0.25f), KGVec2(0.25f, 0.25f), KGVec2(0.f, 0.25f), + KGVec2(0.75f, 0.f), KGVec2(0.25f, 0.f), KGVec2(0.f, 0.f) }; + + static const KGVec2 panelSize[9] = { KGVec2(0.25f,0.25f), KGVec2(0.5f, 0.25f), KGVec2(0.25f, 0.25f), + KGVec2(0.25f, 0.5f), KGVec2(0.5f, 0.5f), KGVec2(0.25f, 0.5f), + KGVec2(0.25f, 0.25f), KGVec2(0.5f, 0.25f), KGVec2(0.25f, 0.25f) }; + + // tag faces + bool boxes[27]{}; + static int overBox = -1; + for (int iPass = 0; iPass < 2; iPass++) + { + for (int iFace = 0; iFace < 6; iFace++) + { + const int normalIndex = (iFace % 3); + const int perpXIndex = (normalIndex + 1) % 3; + const int perpYIndex = (normalIndex + 2) % 3; + const float invert = (iFace > 2) ? -1.f : 1.f; + const vec_t indexVectorX = directionUnary[perpXIndex] * invert; + const vec_t indexVectorY = directionUnary[perpYIndex] * invert; + const vec_t boxOrigin = directionUnary[normalIndex] * -invert - indexVectorX - indexVectorY; + + // plan local space + const vec_t n = directionUnary[normalIndex] * invert; + vec_t viewSpaceNormal = n; + vec_t viewSpacePoint = n * 0.5f; + viewSpaceNormal.TransformVector(cubeView); + viewSpaceNormal.Normalize(); + viewSpacePoint.TransformPoint(cubeView); + const vec_t viewSpaceFacePlan = BuildPlan(viewSpacePoint, viewSpaceNormal); + + // back face culling + if (viewSpaceFacePlan.w > 0.f) + { + continue; + } + + const vec_t facePlan = BuildPlan(n * 0.5f, n); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, facePlan); + vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len - (n * 0.5f); + + float localx = Dot(directionUnary[perpXIndex], posOnPlan) * invert + 0.5f; + float localy = Dot(directionUnary[perpYIndex], posOnPlan) * invert + 0.5f; + + // panels + const vec_t dx = directionUnary[perpXIndex]; + const vec_t dy = directionUnary[perpYIndex]; + const vec_t origin = directionUnary[normalIndex] - dx - dy; + for (int iPanel = 0; iPanel < 9; iPanel++) + { + vec_t boxCoord = boxOrigin + indexVectorX * float(iPanel % 3) + indexVectorY * float(iPanel / 3) + makeVect(1.f, 1.f, 1.f); + const KGVec2 p = panelPosition[iPanel] * 2.f; + const KGVec2 s = panelSize[iPanel] * 2.f; + KGVec2 faceCoordsScreen[4]; + vec_t panelPos[4] = { dx * p.x + dy * p.y, + dx * p.x + dy * (p.y + s.y), + dx * (p.x + s.x) + dy * (p.y + s.y), + dx * (p.x + s.x) + dy * p.y }; + + for (unsigned int iCoord = 0; iCoord < 4; iCoord++) + { + faceCoordsScreen[iCoord] = worldToPos((panelPos[iCoord] + origin) * 0.5f * invert, res, position, size); + } + + const KGVec2 panelCorners[2] = { panelPosition[iPanel], panelPosition[iPanel] + panelSize[iPanel] }; + bool insidePanel = localx > panelCorners[0].x && localx < panelCorners[1].x && localy > panelCorners[0].y && localy < panelCorners[1].y; + int boxCoordInt = int(boxCoord.x * 9.f + boxCoord.y * 3.f + boxCoord.z); + KR_CORE_ASSERT(boxCoordInt < 27, "Too much boxcoordint?"); + boxes[boxCoordInt] |= insidePanel && (!isDraging) && gContext.mbMouseOver; + + // draw face with lighter color + if (iPass) + { + KGU32 directionColor = GetColorU32(DIRECTION_X + normalIndex); + gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor | KG_COL32(0x80, 0x80, 0x80, 0x80)) | (gContext.mIsViewManipulatorHovered ? KG_COL32(0x08, 0x08, 0x08, 0) : 0)); + if (boxes[boxCoordInt]) + { + KGU32 selectionColor = GetColorU32(SELECTION); + gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, selectionColor); + + if (io.MouseDown[0] && !isClicking && !isDraging && GKarmaGui->ActiveId == 0) { + overBox = boxCoordInt; + isClicking = true; + isDraging = true; + } + } + } + } + } + } + if (interpolationFrames) + { + interpolationFrames--; + vec_t newDir = viewInverse.v.dir; + newDir.Lerp(interpolationDir, 0.2f); + newDir.Normalize(); + + vec_t newUp = viewInverse.v.up; + newUp.Lerp(interpolationUp, 0.3f); + newUp.Normalize(); + newUp = interpolationUp; + vec_t newEye = camTarget + newDir * length; + LookAt(&newEye.x, &camTarget.x, &newUp.x, view); + } + gContext.mIsViewManipulatorHovered = gContext.mbMouseOver && KGRect(position, position + size).Contains(io.MousePos); + + if (io.MouseDown[0] && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])) && isClicking) + { + isClicking = false; + } + + if (!io.MouseDown[0]) + { + if (isClicking) + { + // apply new view direction + int cx = overBox / 9; + int cy = (overBox - cx * 9) / 3; + int cz = overBox % 3; + interpolationDir = makeVect(1.f - (float)cx, 1.f - (float)cy, 1.f - (float)cz); + interpolationDir.Normalize(); + + if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f) + { + vec_t right = viewInverse.v.right; + if (fabsf(right.x) > fabsf(right.z)) + { + right.z = 0.f; + } + else + { + right.x = 0.f; + } + right.Normalize(); + interpolationUp = Cross(interpolationDir, right); + interpolationUp.Normalize(); + } + else + { + interpolationUp = referenceUp; + } + interpolationFrames = 40; + + } + isClicking = false; + isDraging = false; + } + + + if (isDraging) + { + matrix_t rx, ry, roll; + + rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f); + ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f); + + roll = rx * ry; + + vec_t newDir = viewInverse.v.dir; + newDir.TransformVector(roll); + newDir.Normalize(); + + // clamp + vec_t planDir = Cross(viewInverse.v.right, referenceUp); + planDir.y = 0.f; + planDir.Normalize(); + float dt = Dot(planDir, newDir); + if (dt < 0.0f) + { + newDir += planDir * dt; + newDir.Normalize(); + } + + vec_t newEye = camTarget + newDir * length; + LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view); + } + + gContext.mbUsingViewManipulate = (interpolationFrames != 0) || isDraging; + if (isClicking || gContext.mbUsingViewManipulate || gContext.mIsViewManipulatorHovered) { + Karma::KarmaGui::SetNextFrameWantCaptureMouse(true); + } + + // restore view/projection because it was used to compute ray + ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode); + } +}; diff --git a/Karma/src/Karma/KarmaGui/KarmaGuizmo.h b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h new file mode 100644 index 00000000..4f9d60f1 --- /dev/null +++ b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h @@ -0,0 +1,305 @@ +// https://github.com/CedricGuillemet/KarmaGuizmo +// v1.92.5 WIP +// +// The MIT License(MIT) +// +// Copyright(c) 2016-2021 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// ------------------------------------------------------------------------------------------- +// History : +// 2019/11/03 View gizmo +// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode. +// 2016/09/09 Hatched negative axis. Snapping. Documentation update. +// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved +// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results. +// 2016/08/31 First version +// +// ------------------------------------------------------------------------------------------- +// Future (no order): +// +// - Multi view +// - display rotation/translation/scale infos in local/world space and not only local +// - finish local/world matrix application +// - OPERATION as bitmask +// +// ------------------------------------------------------------------------------------------- +// Example +#if 0 +void EditTransform(const Camera& camera, matrix_t& matrix) +{ + static KarmaGuizmo::OPERATION mCurrentGizmoOperation(KarmaGuizmo::ROTATE); + static KarmaGuizmo::MODE mCurrentGizmoMode(KarmaGuizmo::WORLD); + if (KarmaGui::IsKeyPressed(90)) + mCurrentGizmoOperation = KarmaGuizmo::TRANSLATE; + if (KarmaGui::IsKeyPressed(69)) + mCurrentGizmoOperation = KarmaGuizmo::ROTATE; + if (KarmaGui::IsKeyPressed(82)) // r Key + mCurrentGizmoOperation = KarmaGuizmo::SCALE; + if (KarmaGui::RadioButton("Translate", mCurrentGizmoOperation == KarmaGuizmo::TRANSLATE)) + mCurrentGizmoOperation = KarmaGuizmo::TRANSLATE; + KarmaGui::SameLine(); + if (KarmaGui::RadioButton("Rotate", mCurrentGizmoOperation == KarmaGuizmo::ROTATE)) + mCurrentGizmoOperation = KarmaGuizmo::ROTATE; + KarmaGui::SameLine(); + if (KarmaGui::RadioButton("Scale", mCurrentGizmoOperation == KarmaGuizmo::SCALE)) + mCurrentGizmoOperation = KarmaGuizmo::SCALE; + float matrixTranslation[3], matrixRotation[3], matrixScale[3]; + KarmaGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale); + KarmaGui::InputFloat3("Tr", matrixTranslation, 3); + KarmaGui::InputFloat3("Rt", matrixRotation, 3); + KarmaGui::InputFloat3("Sc", matrixScale, 3); + KarmaGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16); + + if (mCurrentGizmoOperation != KarmaGuizmo::SCALE) + { + if (KarmaGui::RadioButton("Local", mCurrentGizmoMode == KarmaGuizmo::LOCAL)) + mCurrentGizmoMode = KarmaGuizmo::LOCAL; + KarmaGui::SameLine(); + if (KarmaGui::RadioButton("World", mCurrentGizmoMode == KarmaGuizmo::WORLD)) + mCurrentGizmoMode = KarmaGuizmo::WORLD; + } + static bool useSnap(false); + if (KarmaGui::IsKeyPressed(83)) + useSnap = !useSnap; + KarmaGui::Checkbox("", &useSnap); + KarmaGui::SameLine(); + vec_t snap; + switch (mCurrentGizmoOperation) + { + case KarmaGuizmo::TRANSLATE: + snap = config.mSnapTranslation; + KarmaGui::InputFloat3("Snap", &snap.x); + break; + case KarmaGuizmo::ROTATE: + snap = config.mSnapRotation; + KarmaGui::InputFloat("Angle Snap", &snap.x); + break; + case KarmaGuizmo::SCALE: + snap = config.mSnapScale; + KarmaGui::InputFloat("Scale Snap", &snap.x); + break; + } + KarmaGuiIO& io = Karma::KarmaGui::GetIO(); + KarmaGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); + KarmaGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL); +} +#endif +#pragma once + +#include "KarmaGui.h" + +#ifndef IMGUI_API +#define IMGUI_API +#endif + +#ifndef KARMAGUIZMO_NAMESPACE +#define KARMAGUIZMO_NAMESPACE KarmaGuizmo +#endif + +struct KGGuiWindow; + +namespace KARMAGUIZMO_NAMESPACE +{ + // call inside your own window and before Manipulate() in order to draw gizmo to that window. + // Or pass a specific KGDrawList to draw to (e.g. KarmaGui::GetForegroundDrawList()). + IMGUI_API void SetDrawlist(KGDrawList* drawlist = nullptr); + + // call BeginFrame right after KarmaGui_XXXX_NewFrame(); + IMGUI_API void BeginFrame(); + + // this is necessary because when imguizmo is compiled into a dll, and imgui into another + // globals are not shared between them. + // More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam + // expose method to set imgui context + IMGUI_API void SetKarmaGuiContext(KarmaGuiContext* ctx); + + // return true if mouse cursor is over any gizmo control (axis, plan or screen component) + IMGUI_API bool IsOver(); + + // return true if mouse IsOver or if the gizmo is in moving state + IMGUI_API bool IsUsing(); + + // return true if the view gizmo is in moving state + IMGUI_API bool IsUsingViewManipulate(); + // only check if your mouse is over the view manipulator - no matter whether it's active or not + IMGUI_API bool IsViewManipulateHovered(); + + // return true if any gizmo is in moving state + IMGUI_API bool IsUsingAny(); + + // enable/disable the gizmo. Stay in the state until next call to Enable. + // gizmo is rendered with gray half transparent color when disabled + IMGUI_API void Enable(bool enable); + + // helper functions for manualy editing translation/rotation/scale with an input float + // translation, rotation and scale float points to 3 floats each + // Angles are in degrees (more suitable for human editing) + // example: + // float matrixTranslation[3], matrixRotation[3], matrixScale[3]; + // KarmaGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale); + // KarmaGui::InputFloat3("Tr", matrixTranslation, 3); + // KarmaGui::InputFloat3("Rt", matrixRotation, 3); + // KarmaGui::InputFloat3("Sc", matrixScale, 3); + // KarmaGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16); + // + // These functions have some numerical stability issues for now. Use with caution. + IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale); + IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix); + + IMGUI_API void SetRect(float x, float y, float width, float height); + // default is false + IMGUI_API void SetOrthographic(bool isOrthographic); + + // Render a cube with face color corresponding to face normal. Usefull for debug/tests + IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount); + IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize); + + // call it when you want a gizmo + // Needs view and projection matrices. + // matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional + // translation is applied in world space + enum OPERATION + { + TRANSLATE_X = (1u << 0), + TRANSLATE_Y = (1u << 1), + TRANSLATE_Z = (1u << 2), + ROTATE_X = (1u << 3), + ROTATE_Y = (1u << 4), + ROTATE_Z = (1u << 5), + ROTATE_SCREEN = (1u << 6), + SCALE_X = (1u << 7), + SCALE_Y = (1u << 8), + SCALE_Z = (1u << 9), + BOUNDS = (1u << 10), + SCALE_XU = (1u << 11), + SCALE_YU = (1u << 12), + SCALE_ZU = (1u << 13), + + TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z, + ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN, + SCALE = SCALE_X | SCALE_Y | SCALE_Z, + SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal + UNIVERSAL = TRANSLATE | ROTATE | SCALEU + }; + + inline OPERATION operator|(OPERATION lhs, OPERATION rhs) + { + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + + enum MODE + { + LOCAL, + WORLD + }; + + IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); + // + // Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en + // It seems to be a defensive patent in the US. I don't think it will bring troubles using it as + // other software are using the same mechanics. But just in case, you are now warned! + // + IMGUI_API void ViewManipulate(float* view, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); + + // use this version if you did not call Manipulate before and you are just using ViewManipulate + IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); + + IMGUI_API void SetAlternativeWindow(KGGuiWindow* window); + + [[deprecated("Use PushID/PopID instead.")]] + IMGUI_API void SetID(int id); + + // ID stack/scopes + // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // - Those questions are answered and impacted by understanding of the ID stack system: + // - "Q: Why is my widget not reacting when I click on it?" + // - "Q: How can I have widgets with an empty label?" + // - "Q: How can I have multiple widgets with the same label?" + // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely + // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. + // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, + // whereas "str_id" denote a string that is only used as an ID and not normally displayed. + IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). + IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). + IMGUI_API void PopID(); // pop from the ID stack. + IMGUI_API KGGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself + IMGUI_API KGGuiID GetID(const char* str_id_begin, const char* str_id_end); + IMGUI_API KGGuiID GetID(const void* ptr_id); + + // return true if the cursor is over the operation's gizmo + IMGUI_API bool IsOver(OPERATION op); + IMGUI_API void SetGizmoSizeClipSpace(float value); + + // Allow axis to flip + // When true (default), the guizmo axis flip for better visibility + // When false, they always stay along the positive world/local axis + IMGUI_API void AllowAxisFlip(bool value); + + // Configure the limit where axis are hidden + IMGUI_API void SetAxisLimit(float value); + // Set an axis mask to permanently hide a given axis (true -> hidden, false -> shown) + IMGUI_API void SetAxisMask(bool x, bool y, bool z); + // Configure the limit where planes are hiden + IMGUI_API void SetPlaneLimit(float value); + // from a x,y,z point in space and using Manipulation view/projection matrix, check if mouse is in pixel radius distance of that projected point + IMGUI_API bool IsOver(float* position, float pixelRadius); + + enum COLOR + { + DIRECTION_X, // directionColor[0] + DIRECTION_Y, // directionColor[1] + DIRECTION_Z, // directionColor[2] + PLANE_X, // planeColor[0] + PLANE_Y, // planeColor[1] + PLANE_Z, // planeColor[2] + SELECTION, // selectionColor + INACTIVE, // inactiveColor + TRANSLATION_LINE, // translationLineColor + SCALE_LINE, + ROTATION_USING_BORDER, + ROTATION_USING_FILL, + HATCHED_AXIS_LINES, + TEXT, + TEXT_SHADOW, + COUNT + }; + + struct Style + { + IMGUI_API Style(); + + float TranslationLineThickness; // Thickness of lines for translation gizmo + float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo + float RotationLineThickness; // Thickness of lines for rotation gizmo + float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo + float ScaleLineThickness; // Thickness of lines for scale gizmo + float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo + float HatchedAxisLineThickness; // Thickness of hatched axis lines + float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo + + KGVec4 Colors[COLOR::COUNT]; + }; + + IMGUI_API Style& GetStyle(); +} From 6305e1838aa6bbdc32fb523d535dd76d401704ca Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 7 Dec 2025 16:14:15 +0530 Subject: [PATCH 006/107] Some minor changes and documentation --- Karma/src/Karma.h | 1 + Karma/src/Karma/Ganit/Transform.h | 17 ++++- Karma/src/Karma/KarmaGui/KarmaGuiInternal.h | 2 +- Karma/src/Karma/KarmaGui/KarmaGuizmo.h | 78 ++++++++++----------- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/Karma/src/Karma.h b/Karma/src/Karma.h index b94f41f8..c8b293fd 100644 --- a/Karma/src/Karma.h +++ b/Karma/src/Karma.h @@ -14,6 +14,7 @@ #include "Karma/KarmaGui/KarmaGuiLayer.h" #include "Karma/KarmaGui/KarmaGui.h" #include "Karma/KarmaGui/KarmaGuiInternal.h" +#include "Karma/KarmaGui/KarmaGuizmo.h" #include "Karma/KarmaGui/KarmaGuiRenderer.h" #include "Karma/Renderer/Shader.h" diff --git a/Karma/src/Karma/Ganit/Transform.h b/Karma/src/Karma/Ganit/Transform.h index ba08e587..de251183 100644 --- a/Karma/src/Karma/Ganit/Transform.h +++ b/Karma/src/Karma/Ganit/Transform.h @@ -28,6 +28,8 @@ namespace Karma * * Example: LocalToWorld = (LocalToWorld * DeltaRotation) will change rotation in local space by DeltaRotation. * Example: LocalToWorld = (DeltaRotation * LocalToWorld) will change rotation in world space by DeltaRotation. + * + * @since Karma 1.0.0 */ struct KARMA_API TQuaternion { @@ -58,6 +60,7 @@ namespace Karma * Note that these conventions differ from quaternion axis/angle. UE Quat always considers a positive angle to be a left-handed rotation, * whereas Rotator treats yaw as left-handed but pitch and roll as right-handed. * + * @since Karma 1.0.0 */ struct KARMA_API TRotator { @@ -74,7 +77,7 @@ namespace Karma float m_Roll; /** - * Returns the counter of this rotation governed by Yaw, Pitch, and Roll in the + * @brief Returns the counter of this rotation governed by Yaw, Pitch, and Roll in the * fashion above */ inline TRotator Inverse() const; @@ -109,6 +112,10 @@ namespace Karma * * Example: LocalToWorld = (DeltaRotation * LocalToWorld) will change rotation in local space by DeltaRotation. * Example: LocalToWorld = (LocalToWorld * DeltaRotation) will change rotation in world space by DeltaRotation. + * + * @note We will be following Unreal Engine's left handed convention, meaning +X is forward, +Y is right, +Z is up. + * + * @since Karma 1.0.0 */ class KARMA_API FTransform { @@ -141,22 +148,26 @@ namespace Karma FTransform GetRelativeTransform(const FTransform& RelativeToWhat) const; /** - * Return a transform that is the result of this multiplied by another transform. + * @brief Return a transform that is the result of this multiplied by another transform. * Order matters when composing transforms : C = A * B will yield a transform C that logically first applies A then B to any subsequent transformation. * * @param Other other transform by which to multiply. * @return new transform: this * Other + * + * @since Karma 1.0.0 */ FTransform operator*(const FTransform& Other) const; /** - * Create a new transform: OutTransform = A * B. + * @brief Create a new transform: OutTransform = A * B. * * Order matters when composing transforms : A * B will yield a transform that logically first applies A then B to any subsequent transformation. * * @param OutTransform pointer to transform that will store the result of A * B. * @param A Transform A. * @param B Transform B. + * + * @since Karma 1.0.0 */ inline static void Multiply(FTransform* OutTransform, const FTransform* A, const FTransform* B); diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h b/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h index 8d891d1e..f4ba02bb 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiInternal.h @@ -1728,7 +1728,7 @@ struct KGGuiContextHook }; //----------------------------------------------------------------------------- -// [SECTION] KarmaGuiContext (main Dear ImGui context) +// [SECTION] KarmaGuiContext (main KarmaGui context) //----------------------------------------------------------------------------- struct KarmaGuiContext { diff --git a/Karma/src/Karma/KarmaGui/KarmaGuizmo.h b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h index 4f9d60f1..75e9d57c 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuizmo.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h @@ -106,8 +106,8 @@ void EditTransform(const Camera& camera, matrix_t& matrix) #include "KarmaGui.h" -#ifndef IMGUI_API -#define IMGUI_API +#ifndef KARMAGUI_API +#define KARMAGUI_API #endif #ifndef KARMAGUIZMO_NAMESPACE @@ -120,34 +120,34 @@ namespace KARMAGUIZMO_NAMESPACE { // call inside your own window and before Manipulate() in order to draw gizmo to that window. // Or pass a specific KGDrawList to draw to (e.g. KarmaGui::GetForegroundDrawList()). - IMGUI_API void SetDrawlist(KGDrawList* drawlist = nullptr); + KARMAGUI_API void SetDrawlist(KGDrawList* drawlist = nullptr); // call BeginFrame right after KarmaGui_XXXX_NewFrame(); - IMGUI_API void BeginFrame(); + KARMAGUI_API void BeginFrame(); // this is necessary because when imguizmo is compiled into a dll, and imgui into another // globals are not shared between them. // More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam // expose method to set imgui context - IMGUI_API void SetKarmaGuiContext(KarmaGuiContext* ctx); + KARMAGUI_API void SetKarmaGuiContext(KarmaGuiContext* ctx); // return true if mouse cursor is over any gizmo control (axis, plan or screen component) - IMGUI_API bool IsOver(); + KARMAGUI_API bool IsOver(); // return true if mouse IsOver or if the gizmo is in moving state - IMGUI_API bool IsUsing(); + KARMAGUI_API bool IsUsing(); // return true if the view gizmo is in moving state - IMGUI_API bool IsUsingViewManipulate(); + KARMAGUI_API bool IsUsingViewManipulate(); // only check if your mouse is over the view manipulator - no matter whether it's active or not - IMGUI_API bool IsViewManipulateHovered(); + KARMAGUI_API bool IsViewManipulateHovered(); // return true if any gizmo is in moving state - IMGUI_API bool IsUsingAny(); + KARMAGUI_API bool IsUsingAny(); // enable/disable the gizmo. Stay in the state until next call to Enable. // gizmo is rendered with gray half transparent color when disabled - IMGUI_API void Enable(bool enable); + KARMAGUI_API void Enable(bool enable); // helper functions for manualy editing translation/rotation/scale with an input float // translation, rotation and scale float points to 3 floats each @@ -161,16 +161,16 @@ namespace KARMAGUIZMO_NAMESPACE // KarmaGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16); // // These functions have some numerical stability issues for now. Use with caution. - IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale); - IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix); + KARMAGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale); + KARMAGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix); - IMGUI_API void SetRect(float x, float y, float width, float height); + KARMAGUI_API void SetRect(float x, float y, float width, float height); // default is false - IMGUI_API void SetOrthographic(bool isOrthographic); + KARMAGUI_API void SetOrthographic(bool isOrthographic); // Render a cube with face color corresponding to face normal. Usefull for debug/tests - IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount); - IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize); + KARMAGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount); + KARMAGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize); // call it when you want a gizmo // Needs view and projection matrices. @@ -211,21 +211,21 @@ namespace KARMAGUIZMO_NAMESPACE WORLD }; - IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); + KARMAGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); // // Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en // It seems to be a defensive patent in the US. I don't think it will bring troubles using it as // other software are using the same mechanics. But just in case, you are now warned! // - IMGUI_API void ViewManipulate(float* view, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); + KARMAGUI_API void ViewManipulate(float* view, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); // use this version if you did not call Manipulate before and you are just using ViewManipulate - IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); + KARMAGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, KGVec2 position, KGVec2 size, KGU32 backgroundColor); - IMGUI_API void SetAlternativeWindow(KGGuiWindow* window); + KARMAGUI_API void SetAlternativeWindow(KGGuiWindow* window); [[deprecated("Use PushID/PopID instead.")]] - IMGUI_API void SetID(int id); + KARMAGUI_API void SetID(int id); // ID stack/scopes // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. @@ -238,32 +238,32 @@ namespace KARMAGUIZMO_NAMESPACE // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, // whereas "str_id" denote a string that is only used as an ID and not normally displayed. - IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). - IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). - IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). - IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). - IMGUI_API void PopID(); // pop from the ID stack. - IMGUI_API KGGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself - IMGUI_API KGGuiID GetID(const char* str_id_begin, const char* str_id_end); - IMGUI_API KGGuiID GetID(const void* ptr_id); + KARMAGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). + KARMAGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). + KARMAGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). + KARMAGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). + KARMAGUI_API void PopID(); // pop from the ID stack. + KARMAGUI_API KGGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself + KARMAGUI_API KGGuiID GetID(const char* str_id_begin, const char* str_id_end); + KARMAGUI_API KGGuiID GetID(const void* ptr_id); // return true if the cursor is over the operation's gizmo - IMGUI_API bool IsOver(OPERATION op); - IMGUI_API void SetGizmoSizeClipSpace(float value); + KARMAGUI_API bool IsOver(OPERATION op); + KARMAGUI_API void SetGizmoSizeClipSpace(float value); // Allow axis to flip // When true (default), the guizmo axis flip for better visibility // When false, they always stay along the positive world/local axis - IMGUI_API void AllowAxisFlip(bool value); + KARMAGUI_API void AllowAxisFlip(bool value); // Configure the limit where axis are hidden - IMGUI_API void SetAxisLimit(float value); + KARMAGUI_API void SetAxisLimit(float value); // Set an axis mask to permanently hide a given axis (true -> hidden, false -> shown) - IMGUI_API void SetAxisMask(bool x, bool y, bool z); + KARMAGUI_API void SetAxisMask(bool x, bool y, bool z); // Configure the limit where planes are hiden - IMGUI_API void SetPlaneLimit(float value); + KARMAGUI_API void SetPlaneLimit(float value); // from a x,y,z point in space and using Manipulation view/projection matrix, check if mouse is in pixel radius distance of that projected point - IMGUI_API bool IsOver(float* position, float pixelRadius); + KARMAGUI_API bool IsOver(float* position, float pixelRadius); enum COLOR { @@ -287,7 +287,7 @@ namespace KARMAGUIZMO_NAMESPACE struct Style { - IMGUI_API Style(); + KARMAGUI_API Style(); float TranslationLineThickness; // Thickness of lines for translation gizmo float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo @@ -301,5 +301,5 @@ namespace KARMAGUIZMO_NAMESPACE KGVec4 Colors[COLOR::COUNT]; }; - IMGUI_API Style& GetStyle(); + KARMAGUI_API Style& GetStyle(); } From ee3152241fb906d6a639847662683de2fb173012 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 8 Dec 2025 00:36:13 +0530 Subject: [PATCH 007/107] Conforming attempt to that of Unreal Engine --- Karma/src/Karma/{GameFramework => Engine/Classes}/Level.cpp | 0 Karma/src/Karma/{GameFramework => Engine/Classes}/Level.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Karma/src/Karma/{GameFramework => Engine/Classes}/Level.cpp (100%) rename Karma/src/Karma/{GameFramework => Engine/Classes}/Level.h (100%) diff --git a/Karma/src/Karma/GameFramework/Level.cpp b/Karma/src/Karma/Engine/Classes/Level.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/Level.cpp rename to Karma/src/Karma/Engine/Classes/Level.cpp diff --git a/Karma/src/Karma/GameFramework/Level.h b/Karma/src/Karma/Engine/Classes/Level.h similarity index 100% rename from Karma/src/Karma/GameFramework/Level.h rename to Karma/src/Karma/Engine/Classes/Level.h From 48bd74bda3bfe08da40d3af048cf757c8375618d Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 8 Dec 2025 00:50:10 +0530 Subject: [PATCH 008/107] Moved to further subdirectory --- Karma/src/Karma.h | 2 +- Karma/src/Karma/Engine/Classes/{ => Engine}/Level.cpp | 0 Karma/src/Karma/Engine/Classes/{ => Engine}/Level.h | 0 Karma/src/Karma/GameFramework/Actor.cpp | 2 +- Karma/src/Karma/GameFramework/World.cpp | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename Karma/src/Karma/Engine/Classes/{ => Engine}/Level.cpp (100%) rename Karma/src/Karma/Engine/Classes/{ => Engine}/Level.h (100%) diff --git a/Karma/src/Karma.h b/Karma/src/Karma.h index c8b293fd..123944d0 100644 --- a/Karma/src/Karma.h +++ b/Karma/src/Karma.h @@ -6,7 +6,7 @@ #include "Karma/GameFramework/Actor.h" #include "Karma/GameFramework/World.h" -#include "Karma/GameFramework/Level.h" +#include "Engine/Level.h" #include "Karma/Application.h" #include "Karma/Layer.h" diff --git a/Karma/src/Karma/Engine/Classes/Level.cpp b/Karma/src/Karma/Engine/Classes/Engine/Level.cpp similarity index 100% rename from Karma/src/Karma/Engine/Classes/Level.cpp rename to Karma/src/Karma/Engine/Classes/Engine/Level.cpp diff --git a/Karma/src/Karma/Engine/Classes/Level.h b/Karma/src/Karma/Engine/Classes/Engine/Level.h similarity index 100% rename from Karma/src/Karma/Engine/Classes/Level.h rename to Karma/src/Karma/Engine/Classes/Engine/Level.h diff --git a/Karma/src/Karma/GameFramework/Actor.cpp b/Karma/src/Karma/GameFramework/Actor.cpp index 2284daca..f8379ec0 100644 --- a/Karma/src/Karma/GameFramework/Actor.cpp +++ b/Karma/src/Karma/GameFramework/Actor.cpp @@ -1,5 +1,5 @@ #include "Actor.h" -#include "GameFramework/Level.h" +#include "Engine/Level.h" #include "World.h" #include "GameFramework/Pawn.h" #include "Ganit/Transform.h" diff --git a/Karma/src/Karma/GameFramework/World.cpp b/Karma/src/Karma/GameFramework/World.cpp index 376d3bd4..deef7d91 100644 --- a/Karma/src/Karma/GameFramework/World.cpp +++ b/Karma/src/Karma/GameFramework/World.cpp @@ -2,7 +2,7 @@ #include "Core/UObjectGlobals.h" #include "GameFramework/Actor.h" #include "Core/Class.h" -#include "GameFramework/Level.h" +#include "Engine/Level.h" #include "Ganit/Transform.h" #include "Level.h" #include "Karma/Core/Package.h" From c13ce7d1c8e14334c6c4665dc09d1c4ddaa46fa7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 8 Dec 2025 00:57:09 +0530 Subject: [PATCH 009/107] Moved GameFramework folder to Engine/Classes --- Karma/src/Karma.h | 4 ++-- Karma/src/Karma/{ => Engine/Classes}/GameFramework/Actor.cpp | 0 Karma/src/Karma/{ => Engine/Classes}/GameFramework/Actor.h | 0 .../{ => Engine/Classes}/GameFramework/ActorComponent.cpp | 0 .../Karma/{ => Engine/Classes}/GameFramework/ActorComponent.h | 0 .../{ => Engine/Classes}/GameFramework/ActorIterator.cpp | 0 .../Karma/{ => Engine/Classes}/GameFramework/ActorIterator.h | 0 .../Classes}/GameFramework/ChildActorComponent.cpp | 0 .../{ => Engine/Classes}/GameFramework/ChildActorComponent.h | 0 Karma/src/Karma/{ => Engine/Classes}/GameFramework/Pawn.cpp | 0 Karma/src/Karma/{ => Engine/Classes}/GameFramework/Pawn.h | 0 .../{ => Engine/Classes}/GameFramework/PrimitiveComponent.cpp | 0 .../{ => Engine/Classes}/GameFramework/PrimitiveComponent.h | 0 .../{ => Engine/Classes}/GameFramework/SceneComponent.cpp | 0 .../Karma/{ => Engine/Classes}/GameFramework/SceneComponent.h | 0 Karma/src/Karma/{ => Engine/Classes}/GameFramework/World.cpp | 0 Karma/src/Karma/{ => Engine/Classes}/GameFramework/World.h | 0 .../{ => Engine/Classes}/GameFramework/WorldSettings.cpp | 0 .../Karma/{ => Engine/Classes}/GameFramework/WorldSettings.h | 0 19 files changed, 2 insertions(+), 2 deletions(-) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/Actor.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/Actor.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ActorComponent.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ActorComponent.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ActorIterator.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ActorIterator.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ChildActorComponent.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/ChildActorComponent.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/Pawn.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/Pawn.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/PrimitiveComponent.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/PrimitiveComponent.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/SceneComponent.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/SceneComponent.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/World.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/World.h (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/WorldSettings.cpp (100%) rename Karma/src/Karma/{ => Engine/Classes}/GameFramework/WorldSettings.h (100%) diff --git a/Karma/src/Karma.h b/Karma/src/Karma.h index 123944d0..8c204cae 100644 --- a/Karma/src/Karma.h +++ b/Karma/src/Karma.h @@ -3,8 +3,8 @@ // To be included in clients // Game code specific includes -#include "Karma/GameFramework/Actor.h" -#include "Karma/GameFramework/World.h" +#include "GameFramework/Actor.h" +#include "GameFramework/World.h" #include "Engine/Level.h" diff --git a/Karma/src/Karma/GameFramework/Actor.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/Actor.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp diff --git a/Karma/src/Karma/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h similarity index 100% rename from Karma/src/Karma/GameFramework/Actor.h rename to Karma/src/Karma/Engine/Classes/GameFramework/Actor.h diff --git a/Karma/src/Karma/GameFramework/ActorComponent.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/ActorComponent.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.cpp diff --git a/Karma/src/Karma/GameFramework/ActorComponent.h b/Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.h similarity index 100% rename from Karma/src/Karma/GameFramework/ActorComponent.h rename to Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.h diff --git a/Karma/src/Karma/GameFramework/ActorIterator.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/ActorIterator.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/ActorIterator.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/ActorIterator.cpp diff --git a/Karma/src/Karma/GameFramework/ActorIterator.h b/Karma/src/Karma/Engine/Classes/GameFramework/ActorIterator.h similarity index 100% rename from Karma/src/Karma/GameFramework/ActorIterator.h rename to Karma/src/Karma/Engine/Classes/GameFramework/ActorIterator.h diff --git a/Karma/src/Karma/GameFramework/ChildActorComponent.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/ChildActorComponent.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.cpp diff --git a/Karma/src/Karma/GameFramework/ChildActorComponent.h b/Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.h similarity index 100% rename from Karma/src/Karma/GameFramework/ChildActorComponent.h rename to Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.h diff --git a/Karma/src/Karma/GameFramework/Pawn.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/Pawn.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/Pawn.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/Pawn.cpp diff --git a/Karma/src/Karma/GameFramework/Pawn.h b/Karma/src/Karma/Engine/Classes/GameFramework/Pawn.h similarity index 100% rename from Karma/src/Karma/GameFramework/Pawn.h rename to Karma/src/Karma/Engine/Classes/GameFramework/Pawn.h diff --git a/Karma/src/Karma/GameFramework/PrimitiveComponent.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/PrimitiveComponent.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.cpp diff --git a/Karma/src/Karma/GameFramework/PrimitiveComponent.h b/Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.h similarity index 100% rename from Karma/src/Karma/GameFramework/PrimitiveComponent.h rename to Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.h diff --git a/Karma/src/Karma/GameFramework/SceneComponent.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/SceneComponent.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.cpp diff --git a/Karma/src/Karma/GameFramework/SceneComponent.h b/Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.h similarity index 100% rename from Karma/src/Karma/GameFramework/SceneComponent.h rename to Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.h diff --git a/Karma/src/Karma/GameFramework/World.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/World.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/World.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/World.cpp diff --git a/Karma/src/Karma/GameFramework/World.h b/Karma/src/Karma/Engine/Classes/GameFramework/World.h similarity index 100% rename from Karma/src/Karma/GameFramework/World.h rename to Karma/src/Karma/Engine/Classes/GameFramework/World.h diff --git a/Karma/src/Karma/GameFramework/WorldSettings.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/WorldSettings.cpp similarity index 100% rename from Karma/src/Karma/GameFramework/WorldSettings.cpp rename to Karma/src/Karma/Engine/Classes/GameFramework/WorldSettings.cpp diff --git a/Karma/src/Karma/GameFramework/WorldSettings.h b/Karma/src/Karma/Engine/Classes/GameFramework/WorldSettings.h similarity index 100% rename from Karma/src/Karma/GameFramework/WorldSettings.h rename to Karma/src/Karma/Engine/Classes/GameFramework/WorldSettings.h From fe759a942f60bce0501d5f8f47e04f6a68a9e5e0 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 8 Dec 2025 09:03:14 +0530 Subject: [PATCH 010/107] Added staticmeshactor --- .../Engine/Classes/Engine/StaticMeshActor.cpp | 14 +++++++ .../Engine/Classes/Engine/StaticMeshActor.h | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp create mode 100644 Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp new file mode 100644 index 00000000..6fc977d3 --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -0,0 +1,14 @@ +#include "StaticMeshActor.h" + +namespace Karma +{ + AStaticMeshActor::AStaticMeshActor(const std::string& name) + : AActor() + { + } + + AStaticMeshActor::~AStaticMeshActor() + { + // Cleanup code if necessary + } +} \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h new file mode 100644 index 00000000..fcaaab05 --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -0,0 +1,37 @@ +/** + * @file StaticMeshActor.h + * @brief Declaration of the StaticMeshActor class, representing an actor with a static mesh component in the game world. + * @author Ravi Mohan (the_cowboy) + * @date December 12, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "GameFramework/Actor.h" + +namespace Karma +{ + /** + * @brief An actor that contains a static mesh component, allowing it to be rendered in the game world. + * + * @since Karma 1.0.0 + */ + class KARMA_API AStaticMeshActor : public AActor + { + public: + /** + * @brief Constructor for StaticMeshActor + * + * @param name The name of the static mesh actor. + */ + AStaticMeshActor(const std::string& name = "StaticMeshActor"); + + /** + * @brief Destructor for StaticMeshActor + */ + virtual ~AStaticMeshActor(); + // Additional methods and members specific to StaticMeshActor can be added here. + }; +} \ No newline at end of file From f2995a0fb748692a03757698bcd28183d4ead353 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 8 Dec 2025 21:44:34 +0530 Subject: [PATCH 011/107] Getting rid of Vulkan image transition validation error --- Karma/src/Karma/Application.cpp | 5 ++++- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index 79ff3a90..d2e8d7aa 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -101,7 +101,10 @@ namespace Karma deltaTime /= 1000000.0f; // Tick KEngine - GEngine->Tick(deltaTime, false); + if(GEngine) + { + GEngine->Tick(deltaTime, false); + } for (auto layer : *m_LayerStack) { diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 057efc7f..0f577749 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -540,6 +540,24 @@ namespace Karma } vkCmdEndRenderPass(frameOnFlightData->CommandBuffer); + + // Transition the swapchain image from VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + // else there'd be validation error + // NOTE: We are using VulkanHolder::GetVulkanContext()->GetSwapChainImages() in case we decide to create + // different swapchain images for KarmaGui + VkImageMemoryBarrier presentBarrier = {}; + presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + presentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + presentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + presentBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + presentBarrier.image = VulkanHolder::GetVulkanContext()->GetSwapChainImages()[windowData->ImageFrameIndex]; // Swapchain only! + presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + presentBarrier.subresourceRange.levelCount = 1; + presentBarrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(frameOnFlightData->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &presentBarrier); result = vkEndCommandBuffer(frameOnFlightData->CommandBuffer); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to end command buffer"); From a528645faaeb1d72280ec03939688829fc8d473f Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 8 Dec 2025 22:50:53 +0530 Subject: [PATCH 012/107] Removing unnecessary \n from KarmaGui's KR_CORE_INFO --- Karma/src/Karma/KarmaGui/KarmaGui.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGui.cpp b/Karma/src/Karma/KarmaGui/KarmaGui.cpp index 7adf8634..737efbf9 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGui.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGui.cpp @@ -7009,7 +7009,7 @@ void Karma::KarmaGui::SetKeyboardFocusHere(int offset) KarmaGuiContext& g = *GKarmaGui; KGGuiWindow* window = g.CurrentWindow; KR_CORE_ASSERT(offset >= -1, ""); // -1 is allowed but not below - KR_CORE_INFO("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); + KR_CORE_INFO("SetKeyboardFocusHere(%d) in window \"%s\"", offset, window->Name); // It makes sense in the vast majority of cases to never interrupt a drag and drop. // When we refactor this function into ActivateItem() we may want to make this an option. @@ -7017,7 +7017,7 @@ void Karma::KarmaGui::SetKeyboardFocusHere(int offset) // is also automatically dropped in the event g.ActiveId is stolen. if (g.DragDropActive || g.MovingWindow != NULL) { - KR_CORE_INFO("SetKeyboardFocusHere() ignored while DragDropActive!\n"); + KR_CORE_INFO("SetKeyboardFocusHere() ignored while DragDropActive!"); return; } @@ -10132,7 +10132,7 @@ void Karma::KarmaGuiInternal::SetNavWindow(KGGuiWindow* window) KarmaGuiContext& g = *GKarmaGui; if (g.NavWindow != window) { - KR_CORE_INFO("[focus] SetNavWindow(\"{0}\")\n", window ? window->Name : ""); + KR_CORE_INFO("[focus] SetNavWindow(\"{0}\")", window ? window->Name : ""); g.NavWindow = window; } g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; @@ -10614,7 +10614,7 @@ void Karma::KarmaGuiInternal::NavInitWindow(KGGuiWindow* window, bool force_rein bool init_for_nav = false; if (window == window->RootWindow || (window->Flags & KGGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) init_for_nav = true; - KR_CORE_INFO("[nav] NavInitRequest: from NavInitWindow(), init_for_nav={0}, window=\"{1}\"", init_for_nav, window->Name); + KR_CORE_INFO("[nav] NavInitRequest: from NavInitWindow(), init_for_nav={0}, window=\"{1}\"", init_for_nav, window->Name); if (init_for_nav) { SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, KGRect()); @@ -10858,7 +10858,7 @@ void Karma::KarmaGuiInternal::NavInitRequestApplyResult() // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. - KR_CORE_INFO("[nav] NavInitRequest: ApplyResult: NavID {0} Window \"{1}\"", g.NavInitResultId, g.NavWindow->Name); + KR_CORE_INFO("[nav] NavInitRequest: ApplyResult: NavID {0} Window \"{1}\"", g.NavInitResultId, g.NavWindow->Name); SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (g.NavInitRequestFromMove) @@ -10929,7 +10929,7 @@ void Karma::KarmaGuiInternal::NavUpdateCreateMoveRequest() // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) { - KR_CORE_INFO("[nav] NavInitRequest: from move, window \"{0}\"", window ? window->Name : ""); + KR_CORE_INFO("[nav] NavInitRequest: from move, window \"{0}\"", window ? window->Name : ""); g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResultId = 0; g.NavDisableHighlight = false; @@ -14403,7 +14403,7 @@ static void Karma::DockNodeAddWindow(KGGuiDockNode* node, KGGuiWindow* window, b Karma::DockNodeRemoveWindow(window->DockNode, window, 0); } KR_CORE_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL, ""); - KR_CORE_INFO("[docking] DockNodeAddWindow node 0x%08X window '{0}'\n", node->ID, window->Name); + KR_CORE_INFO("[docking] DockNodeAddWindow node 0x%08X window '{0}'", node->ID, window->Name); // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window, // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame). @@ -14459,7 +14459,7 @@ void Karma::DockNodeRemoveWindow(KGGuiDockNode* node, KGGuiWindow* window, KGGui //KR_CORE_ASSERT(window->RootWindowDockTree == node->HostWindow); //KR_CORE_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() KR_CORE_ASSERT(save_dock_id == 0 || save_dock_id == node->ID, ""); - KR_CORE_INFO("[docking] DockNodeRemoveWindow node 0x%08X window '{0}'\n", node->ID, window->Name); + KR_CORE_INFO("[docking] DockNodeRemoveWindow node 0x%08X window '{0}'", node->ID, window->Name); window->DockNode = NULL; window->DockIsActive = window->DockTabWantClose = false; @@ -16704,7 +16704,7 @@ static KGGuiDockNode* DockBuilderCopyNodeRec(KGGuiDockNode* src_node, KGGuiID ds dst_node->ChildNodes[child_n]->ParentNode = dst_node; } - KR_CORE_INFO("[docking] Fork node {0} -> {1} ({2} childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); + KR_CORE_INFO("[docking] Fork node {0} -> {1} ({2} childs)", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); return dst_node; } From babd36bdf23ba2de3ff18581cdad660b4b712df7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 9 Dec 2025 23:45:44 +0530 Subject: [PATCH 013/107] Some modifications and additions 1. Placement of component files like PrimitiveComponent etc to Engine/Classes/Components to conform to UE's directory structure 2. Addition of MeshComponent and StaticMeshComponent classes --- .../ActorComponent.cpp | 0 .../ActorComponent.h | 0 .../ChildActorComponent.cpp | 0 .../ChildActorComponent.h | 0 .../Classes/Components/MeshComponent.cpp | 1 + .../Engine/Classes/Components/MeshComponent.h | 27 ++++++++++ .../PrimitiveComponent.cpp | 0 .../PrimitiveComponent.h | 4 ++ .../SceneComponent.cpp | 0 .../SceneComponent.h | 0 .../Components/StaticMeshComponent.cpp | 13 +++++ .../Classes/Components/StaticMeshComponent.h | 50 +++++++++++++++++++ .../Engine/Classes/Engine/StaticMeshActor.h | 7 ++- .../Engine/Classes/GameFramework/Actor.h | 2 +- 14 files changed, 102 insertions(+), 2 deletions(-) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/ActorComponent.cpp (100%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/ActorComponent.h (100%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/ChildActorComponent.cpp (100%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/ChildActorComponent.h (100%) create mode 100644 Karma/src/Karma/Engine/Classes/Components/MeshComponent.cpp create mode 100644 Karma/src/Karma/Engine/Classes/Components/MeshComponent.h rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/PrimitiveComponent.cpp (100%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/PrimitiveComponent.h (97%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/SceneComponent.cpp (100%) rename Karma/src/Karma/Engine/Classes/{GameFramework => Components}/SceneComponent.h (100%) create mode 100644 Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp create mode 100644 Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.cpp rename to Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.h b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/ActorComponent.h rename to Karma/src/Karma/Engine/Classes/Components/ActorComponent.h diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/ChildActorComponent.cpp similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.cpp rename to Karma/src/Karma/Engine/Classes/Components/ChildActorComponent.cpp diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.h b/Karma/src/Karma/Engine/Classes/Components/ChildActorComponent.h similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/ChildActorComponent.h rename to Karma/src/Karma/Engine/Classes/Components/ChildActorComponent.h diff --git a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.cpp new file mode 100644 index 00000000..085ca58b --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.cpp @@ -0,0 +1 @@ +#include "MeshComponent.h" \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h new file mode 100644 index 00000000..a4eec443 --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h @@ -0,0 +1,27 @@ +/** + * @file MeshComponent.h + * @brief Declaration of the MeshComponent class, representing a PrimitiveComponent that handles mesh rendering in the game engine. + * @author Ravi Mohan (the_cowboy) + * @date December 9, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once +#include "Components/PrimitiveComponent.h" + +namespace Karma +{ + /** + * @brief A component that represents a mesh in the game engine, responsible for rendering and managing mesh data. + * + * MeshComponent is an abstract base for any component that is an instance of a renderable collection of triangles. + * + * @since Karma 1.0.0 + */ + class KARMA_API UMeshComponent : public UPrimitiveComponent + { + DECLARE_KARMA_CLASS(UPrimitiveComponent, UMeshComponent) + + }; +} \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.cpp rename to Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.h b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h similarity index 97% rename from Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.h rename to Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h index 5bfd5980..29e0b018 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/PrimitiveComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h @@ -15,8 +15,12 @@ namespace Karma { /** * @brief PrimitiveComponents are SceneComponents that contain or generate some sort of geometry, generally to be rendered or used as collision data. + * * There are several subclasses for the various types of geometry, but the most common by far are the ShapeComponents (Capsule, Sphere, Box), StaticMeshComponent, and SkeletalMeshComponent. * ShapeComponents generate geometry that is used for collision detection but are not rendered, while StaticMeshComponents and SkeletalMeshComponents contain pre-built geometry that is rendered, but can also be used for collision detection. + * + * @see UStaticMeshComponent + * @since Karma 1.0.0 */ class KARMA_API UPrimitiveComponent : public USceneComponent { diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.cpp rename to Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.h b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h similarity index 100% rename from Karma/src/Karma/Engine/Classes/GameFramework/SceneComponent.h rename to Karma/src/Karma/Engine/Classes/Components/SceneComponent.h diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp new file mode 100644 index 00000000..c0de21f5 --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp @@ -0,0 +1,13 @@ +#include "StaticMeshComponent.h" + +namespace Karma +{ + UStaticMeshComponent::UStaticMeshComponent(const std::string& name) + { + // Constructor implementation (if any) + } + UStaticMeshComponent::~UStaticMeshComponent() + { + // Destructor implementation (if any) + } +} // namespace Karma \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h new file mode 100644 index 00000000..4ba17979 --- /dev/null +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -0,0 +1,50 @@ +/** + * @file StaticMeshComponent.h + * @brief Declaration of the StaticMeshComponent class, representing a component that handles static mesh rendering in the game engine. + * @author Ravi Mohan (the_cowboy) + * @date December 9, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "Components/MeshComponent.h" + +namespace Karma +{ + /** + * @brief A component that represents a static mesh in the game engine, responsible for rendering and managing static mesh data. + * + * StaticMeshComponent is used to render a static mesh asset in the world. It is a subclass of MeshComponent and provides functionality specific to static meshes. + * + * @since Karma 1.0.0 + */ + class KARMA_API UStaticMeshComponent : public UMeshComponent + { + DECLARE_KARMA_CLASS(UMeshComponent, UStaticMeshComponent) + + private: + + /** + * @brief The static mesh asset associated with this component for rendering + * + * @note This is analogous to Unreal Engine's UStaticMeshComponent::StaticMesh + */ + class Mesh* m_StaticMesh; + + public: + /** + * @brief Constructor for StaticMeshComponent + * + * @param name The name of the static mesh component. + */ + UStaticMeshComponent(const std::string& name = "StaticMeshComponent"); + + /** + * @brief Destructor for StaticMeshComponent + */ + virtual ~UStaticMeshComponent(); + // Additional methods and members specific to StaticMeshComponent can be added here. + }; +} \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index fcaaab05..03765fb6 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -2,7 +2,7 @@ * @file StaticMeshActor.h * @brief Declaration of the StaticMeshActor class, representing an actor with a static mesh component in the game world. * @author Ravi Mohan (the_cowboy) - * @date December 12, 2025 + * @date December 7, 2025 * * @copyright Karma Engine copyright(c) People of India */ @@ -20,6 +20,11 @@ namespace Karma */ class KARMA_API AStaticMeshActor : public AActor { + DECLARE_KARMA_CLASS(AActor, AStaticMeshActor) + + private: + + public: /** * @brief Constructor for StaticMeshActor diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h index 12a78cfc..c7d30f40 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h @@ -14,7 +14,7 @@ #include "Karma/Log.h" #include "Karma/Core/KarmaTypes.h" #include "Object.h" -#include "GameFramework/SceneComponent.h" +#include "Components/SceneComponent.h" namespace Karma { From b6f5416132d0b34b49f094488170a087fe257e36 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 10 Dec 2025 09:47:07 +0530 Subject: [PATCH 014/107] Added test hook in EditorLayer to spawn staticmesh actor and to do adjustments in vulkan code --- .../Classes/Components/StaticMeshComponent.h | 2 +- .../Engine/Classes/Engine/StaticMeshActor.h | 9 ++++-- Pranjal/Source/Private/EditorLayer.cpp | 32 +++++++++++++++++++ Pranjal/Source/Public/EditorLayer.h | 4 +++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 4ba17979..85737b63 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -31,7 +31,7 @@ namespace Karma * * @note This is analogous to Unreal Engine's UStaticMeshComponent::StaticMesh */ - class Mesh* m_StaticMesh; + std::shared_ptr m_StaticMesh; public: /** diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 03765fb6..33bdf70c 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -14,7 +14,7 @@ namespace Karma { /** - * @brief An actor that contains a static mesh component, allowing it to be rendered in the game world. + * @brief An actor that contains a static mesh component, allowing the mesh to be rendered in the game world. * * @since Karma 1.0.0 */ @@ -23,7 +23,12 @@ namespace Karma DECLARE_KARMA_CLASS(AActor, AStaticMeshActor) private: - + /** + * @brief The static mesh component associated with this actor for rendering + * + * @note This is analogous to Unreal Engine's AStaticMeshActor::StaticMeshComponent + */ + std::shared_ptr m_StaticMeshComponent; public: /** diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index f05b0967..8e740087 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -10,6 +10,7 @@ #include "UObjectIterator.h" #include "Engine.h" #include "GameInstance.h" +#include "Engine/StaticMeshActor.h" namespace Karma { @@ -200,6 +201,10 @@ namespace Karma { IterateActors(); } + else if (e.GetKeyCode() == GLFW_KEY_V) + { + SpawnStaticMeshActor(); + } return false; } @@ -340,6 +345,33 @@ namespace Karma // delete testWorld; } + void EditorLayer::SpawnStaticMeshActor() + { + // We are attempting to spawn StaticMeshActor here which will hold the model + + UWorld* currentWorld = GEngine->GetCurrentGameInstance()->GetWorldContext()->World(); + + if (currentWorld) + { + KR_INFO("Current World is : {0}", currentWorld->GetName()); + + FTransform smActorTransform = FTransform::m_Identity; + + FActorSpawnParameters smActorParams; + smActorParams.m_Owner = nullptr; + smActorParams.m_Name = "StaticMeshActor"; + smActorParams.m_OverrideLevel = currentWorld->GetCurrentLevel(); + + + AActor* staticMeshActor = currentWorld->SpawnActor(AStaticMeshActor::StaticClass(), &smActorTransform, smActorParams); + + if (staticMeshActor) + { + KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); + } + } + } + void EditorLayer::IterateActors() { // Iterate over all actors, can also supply a different derived class diff --git a/Pranjal/Source/Public/EditorLayer.h b/Pranjal/Source/Public/EditorLayer.h index 988364f5..80cbd228 100644 --- a/Pranjal/Source/Public/EditorLayer.h +++ b/Pranjal/Source/Public/EditorLayer.h @@ -26,6 +26,10 @@ namespace Karma void TentativeTrigger(); void IterateActors(); + // Tentative routine for incorporating spawning of full fledged StaticMeshActor (SM actor) + // with model + void SpawnStaticMeshActor(); + private: std::shared_ptr m_ModelShader; std::shared_ptr m_ModelVertexArray; From 772a31062d7f924788ff0b8ce1fb3df074751f3b Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 10 Dec 2025 20:41:54 +0530 Subject: [PATCH 015/107] Attempting to utilize UE's way of making a scene --- .../Classes/Components/StaticMeshComponent.h | 8 ++++++++ .../Engine/Classes/Engine/StaticMeshActor.cpp | 17 +++++++++++++++++ .../Engine/Classes/Engine/StaticMeshActor.h | 12 +++++++++++- .../Engine/Classes/GameFramework/World.h | 6 ++++++ Karma/src/Karma/Renderer/Mesh.cpp | 19 +++++++++++++++++++ Karma/src/Karma/Renderer/Mesh.h | 2 ++ Pranjal/Source/Private/EditorLayer.cpp | 14 ++++++++------ 7 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 85737b63..54b8fc37 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -46,5 +46,13 @@ namespace Karma */ virtual ~UStaticMeshComponent(); // Additional methods and members specific to StaticMeshComponent can be added here. + + /** + * @brief Sets the static mesh associated with this component. + * + * @param staticMesh The static mesh to associate with this component. + * @since Karma 1.0.0 + */ + void SetStaticMesh(std::shared_ptr staticMesh) { m_StaticMesh = staticMesh; } }; } \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 6fc977d3..1f665b45 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -1,4 +1,7 @@ #include "StaticMeshActor.h" +#include "Mesh.h" + +#include "StaticMeshComponent.h" namespace Karma { @@ -11,4 +14,18 @@ namespace Karma { // Cleanup code if necessary } + + void AStaticMeshActor::LoadMeshFromFile(const std::string& filePath) + { + std::shared_ptr loadedMesh; + loadedMesh.reset(new Mesh(filePath)); + + if (!m_StaticMeshComponent) + { + // need to write code to destroy UObjects later + m_StaticMeshComponent = NewObject(nullptr, UStaticMeshComponent::StaticClass(), "StaticMeshComponent"); + } + + m_StaticMeshComponent->SetStaticMesh(loadedMesh); + } } \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 33bdf70c..20446892 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -28,7 +28,8 @@ namespace Karma * * @note This is analogous to Unreal Engine's AStaticMeshActor::StaticMeshComponent */ - std::shared_ptr m_StaticMeshComponent; + //std::shared_ptr m_StaticMeshComponent; + class UStaticMeshComponent* m_StaticMeshComponent; public: /** @@ -37,6 +38,15 @@ namespace Karma * @param name The name of the static mesh actor. */ AStaticMeshActor(const std::string& name = "StaticMeshActor"); + + /** + * @brief Loads a static mesh from a file and assigns it to the static mesh component. + * + * @param filePath The file path of the static mesh to load. + * + * @since Karma 1.0.0 + */ + void LoadMeshFromFile(const std::string& filePath); /** * @brief Destructor for StaticMeshActor diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/World.h b/Karma/src/Karma/Engine/Classes/GameFramework/World.h index edb2eb03..84907b7e 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/World.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/World.h @@ -184,6 +184,12 @@ namespace Karma return CastChecked(SpawnActor(Class, nullptr, SpawnParameters), ECastCheckedType::NullAllowed); } + template< class T > + T* SpawnActor(UClass* Class, FTransform const* Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()) + { + return CastChecked(SpawnActor(Class, Transform, SpawnParameters), ECastCheckedType::NullAllowed); + } + /** * Getter for m_PersistentLevel * diff --git a/Karma/src/Karma/Renderer/Mesh.cpp b/Karma/src/Karma/Renderer/Mesh.cpp index 20f4f61d..4201ff65 100644 --- a/Karma/src/Karma/Renderer/Mesh.cpp +++ b/Karma/src/Karma/Renderer/Mesh.cpp @@ -1,5 +1,6 @@ #include "Mesh.h" #include "RenderCommand.h" +#include "Renderer/Material.h" namespace Karma { @@ -57,6 +58,24 @@ namespace Karma } ProcessNode(scene->mRootNode, scene); + + // Set default static material + m_StaticMaterial.reset(new Material()); + + std::shared_ptr shaderUniform; + shaderUniform.reset(UniformBufferObject::Create({ Karma::ShaderDataType::Mat4, Karma::ShaderDataType::Mat4 }, 0)); + + // We are creating shader here for the static material, but need to find a way to share shaders amongst multiple materials + std::shared_ptr m_ModelShader; + m_ModelShader.reset(Karma::Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", shaderUniform, "CylinderShader")); + + m_StaticMaterial->AddShader(m_ModelShader); + + // We add default texture to the material + std::shared_ptr defaultTexture; + defaultTexture.reset(new Texture(TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); + + m_StaticMaterial->AddTexture(defaultTexture); } void Mesh::ProcessNode(aiNode* nodeToProcess, const aiScene* theScene) diff --git a/Karma/src/Karma/Renderer/Mesh.h b/Karma/src/Karma/Renderer/Mesh.h index aaef3608..666df38b 100644 --- a/Karma/src/Karma/Renderer/Mesh.h +++ b/Karma/src/Karma/Renderer/Mesh.h @@ -121,5 +121,7 @@ namespace Karma MeshType m_MeshType; static std::shared_ptr> m_NameToAttributeDictionary; + + std::shared_ptr m_StaticMaterial; }; } diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 8e740087..c7f1bcd1 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -30,28 +30,28 @@ namespace Karma // First intantiate VertexArray m_ModelVertexArray.reset(Karma::VertexArray::Create()); - { + /* { // Get hold of model std::shared_ptr modelMesh; modelMesh.reset(new Karma::Mesh("../Resources/Models/BonedCylinder.obj")); // Set the mesh in vertex array m_ModelVertexArray->SetMesh(modelMesh); - } + }*/ // Next, instantiate material - m_ModelMaterial.reset(new Karma::Material()); + // m_ModelMaterial.reset(new Karma::Material()); { // Setting shader // Uniforms for regular transform uploads - std::shared_ptr shaderUniform; + /*std::shared_ptr shaderUniform; shaderUniform.reset(Karma::UniformBufferObject::Create({ Karma::ShaderDataType::Mat4, Karma::ShaderDataType::Mat4 }, 0)); m_ModelShader.reset(Karma::Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", shaderUniform, "CylinderShader")); - m_ModelMaterial->AddShader(m_ModelShader); + m_ModelMaterial->AddShader(m_ModelShader);*/ } // Then we set texture @@ -363,11 +363,13 @@ namespace Karma smActorParams.m_OverrideLevel = currentWorld->GetCurrentLevel(); - AActor* staticMeshActor = currentWorld->SpawnActor(AStaticMeshActor::StaticClass(), &smActorTransform, smActorParams); + AStaticMeshActor* staticMeshActor = currentWorld->SpawnActor(AStaticMeshActor::StaticClass(), &smActorTransform, smActorParams); if (staticMeshActor) { KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); + + staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); } } } From 8f3e12744c2214bda94dfc1c63dd43808f4242bc Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 11 Dec 2025 16:07:00 +0530 Subject: [PATCH 016/107] Vulkan Unreal - Phase I --- Application/src/KarmaApp.cpp | 2 +- .../Engine/Classes/Engine/StaticMeshActor.cpp | 7 +++++ Karma/src/Karma/Renderer/Camera/Camera.cpp | 15 +++++++++-- Karma/src/Karma/Renderer/Camera/Camera.h | 10 +++++++ Karma/src/Karma/Renderer/Material.cpp | 4 +-- Karma/src/Karma/Renderer/Mesh.cpp | 5 +--- Karma/src/Karma/Renderer/Shader.cpp | 7 +++-- Karma/src/Karma/Renderer/Shader.h | 17 ++---------- Karma/src/Platform/OpenGL/OpenGLShader.cpp | 11 ++------ Karma/src/Platform/OpenGL/OpenGLShader.h | 4 +-- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 3 +++ Karma/src/Platform/Vulkan/VulkanBuffer.h | 2 ++ Karma/src/Platform/Vulkan/VulkanContext.h | 26 ++++++++++++------- Karma/src/Platform/Vulkan/VulkanShader.cpp | 4 +-- Karma/src/Platform/Vulkan/VulkanShader.h | 4 +-- .../src/Platform/Vulkan/VulkanVertexArray.cpp | 12 ++++----- Pranjal/Source/Private/EditorLayer.cpp | 3 +++ 17 files changed, 75 insertions(+), 61 deletions(-) diff --git a/Application/src/KarmaApp.cpp b/Application/src/KarmaApp.cpp index 4cdaf301..738a5357 100644 --- a/Application/src/KarmaApp.cpp +++ b/Application/src/KarmaApp.cpp @@ -17,7 +17,7 @@ class ExampleLayer : public Karma::Layer std::shared_ptr shaderUniform; shaderUniform.reset(Karma::UniformBufferObject::Create({ Karma::ShaderDataType::Mat4, Karma::ShaderDataType::Mat4 }, 0)); - m_BlueSQShader.reset(Karma::Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", shaderUniform, "CylinderShader")); + m_BlueSQShader.reset(Karma::Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", "CylinderShader")); m_SquareMat.reset(new Karma::Material()); m_SquareMat->AddShader(m_BlueSQShader); diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 1f665b45..4e6d7348 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -20,6 +20,13 @@ namespace Karma std::shared_ptr loadedMesh; loadedMesh.reset(new Mesh(filePath)); + // We create uniform buffer object for model's transformation matrix + std::shared_ptr shaderUniform; + shaderUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 0));// Binding point 0 and gets registered in VulkanContext during its initialization + + UBODataPointer uModelMatrix(&GetTransform()); + shaderUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform + if (!m_StaticMeshComponent) { // need to write code to destroy UObjects later diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index 438c8a9d..37624099 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -1,12 +1,14 @@ - #include "Camera.h" #include "glm/gtc/matrix_transform.hpp" #include "Karma/Input.h" +#include "Buffer.h" +#include "Platform/Vulkan/VulkanHolder.h" namespace Karma { Camera::Camera(const glm::vec3& initialCameraPosition) : m_Position(initialCameraPosition), m_LastMouseX(0.0f), m_LastMouseY(0.0f) - { + { + m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); } Camera::~Camera() @@ -24,6 +26,15 @@ namespace Karma m_ViewMatrix = glm::lookAt(m_Position, m_Position + m_CameraFront, m_CameraUp); } + void Camera::OnUpdate(float deltaTime) + { + UBODataPointer uProjection(&m_ProjectionMatrix); + UBODataPointer uView(&m_ViewMatrix); + + // This should work with just updating once in the constructor, may see later + m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); + } + void Camera::MoveForward(float amount) { m_Position += amount * m_CameraFront; diff --git a/Karma/src/Karma/Renderer/Camera/Camera.h b/Karma/src/Karma/Renderer/Camera/Camera.h index 37a32d15..b8221144 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.h +++ b/Karma/src/Karma/Renderer/Camera/Camera.h @@ -36,6 +36,13 @@ namespace Karma */ virtual ~Camera(); + /** + * @brief A hook into the game loop for updating camera position and orientation in the level perhaps + * + * @since Karma 1.0.0 + */ + virtual void OnUpdate(float deltaTime); + /** * @brief Getter for current position of the Camers * @@ -190,5 +197,8 @@ namespace Karma const float m_Sensitivity = 0.1f; float m_Roll = 0.0f; float m_Pitch = 0.0f; + + private: + std::shared_ptr m_ViewProjectionUBO; }; } diff --git a/Karma/src/Karma/Renderer/Material.cpp b/Karma/src/Karma/Renderer/Material.cpp index c2ad6335..32059d82 100644 --- a/Karma/src/Karma/Renderer/Material.cpp +++ b/Karma/src/Karma/Renderer/Material.cpp @@ -8,7 +8,7 @@ namespace Karma void Material::OnUpdate() { - if (m_MainCamera != nullptr) + /*if (m_MainCamera != nullptr) { UBODataPointer uProjection(&m_MainCamera->GetProjectionMatrix()); UBODataPointer uView(&m_MainCamera->GetViewMatirx()); @@ -22,7 +22,7 @@ namespace Karma else { KR_CORE_WARN("No main camera specified for Material."); - } + }*/ } void Material::AttatchMainCamera(std::shared_ptr mCamera) diff --git a/Karma/src/Karma/Renderer/Mesh.cpp b/Karma/src/Karma/Renderer/Mesh.cpp index 4201ff65..03382f2c 100644 --- a/Karma/src/Karma/Renderer/Mesh.cpp +++ b/Karma/src/Karma/Renderer/Mesh.cpp @@ -62,12 +62,9 @@ namespace Karma // Set default static material m_StaticMaterial.reset(new Material()); - std::shared_ptr shaderUniform; - shaderUniform.reset(UniformBufferObject::Create({ Karma::ShaderDataType::Mat4, Karma::ShaderDataType::Mat4 }, 0)); - // We are creating shader here for the static material, but need to find a way to share shaders amongst multiple materials std::shared_ptr m_ModelShader; - m_ModelShader.reset(Karma::Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", shaderUniform, "CylinderShader")); + m_ModelShader.reset(Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", "CylinderShader")); m_StaticMaterial->AddShader(m_ModelShader); diff --git a/Karma/src/Karma/Renderer/Shader.cpp b/Karma/src/Karma/Renderer/Shader.cpp index e557c8a4..a507f171 100644 --- a/Karma/src/Karma/Renderer/Shader.cpp +++ b/Karma/src/Karma/Renderer/Shader.cpp @@ -26,8 +26,7 @@ namespace Karma return nullptr; } - Shader* Shader::Create(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, std::shared_ptr ubo, - const std::string& shaderName) + Shader* Shader::Create(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, const std::string& shaderName) { switch (Renderer::GetAPI()) { @@ -35,9 +34,9 @@ namespace Karma KR_CORE_ASSERT(false, "RendererAPI::None is currently not supported"); return nullptr; case RendererAPI::API::OpenGL: - return new OpenGLShader(vertexSrcFile, fragmentSrcFile, ubo, shaderName); + return new OpenGLShader(vertexSrcFile, fragmentSrcFile, shaderName); case RendererAPI::API::Vulkan: - return new VulkanShader(vertexSrcFile, fragmentSrcFile, ubo); + return new VulkanShader(vertexSrcFile, fragmentSrcFile); } KR_CORE_ASSERT(false, "Unknown RendererAPI"); diff --git a/Karma/src/Karma/Renderer/Shader.h b/Karma/src/Karma/Renderer/Shader.h index f143129f..44d6f74b 100644 --- a/Karma/src/Karma/Renderer/Shader.h +++ b/Karma/src/Karma/Renderer/Shader.h @@ -26,7 +26,7 @@ namespace Karma * @param ubo Uniform buffer object to a assigned * @since Karma 1.0.0 */ - Shader(std::shared_ptr ubo) : m_UniformBufferObject(ubo) + Shader() {} /** @@ -60,14 +60,6 @@ namespace Karma */ virtual void UnBind() const {} - /** - * @brief Getter for uniform buffer object - * - * @return pointer to UniformBufferObject present in the shader - * @since Karma 1.0.0 - */ - std::shared_ptr GetUniformBufferObject() const { return m_UniformBufferObject; } - /** * @brief Instantiating shader object according to the programmer chosen API * @@ -82,12 +74,10 @@ namespace Karma * @param vertexSrcFile Path to vertex shader (filename included). For instance "../Resources/Shaders/shader.vert", relative to * Engine's running directory * @param fragmentSrcFile Path to fragment shader (filename included). - * @param ubo UniformBufferObject to be used * @param shaderName Name of the shader to be supplied * @since Karma 1.0.0 */ - static Shader* Create(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, std::shared_ptr ubo, - const std::string& shaderName = "NoNamedShader"); + static Shader* Create(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, const std::string& shaderName = "NoNamedShader"); // Getters /** @@ -97,9 +87,6 @@ namespace Karma * @since Karma 1.0.0 */ const std::string& GetShaderName() const { return m_ShaderName; } - - private: - std::shared_ptr m_UniformBufferObject; protected: std::string m_ShaderName; diff --git a/Karma/src/Platform/OpenGL/OpenGLShader.cpp b/Karma/src/Platform/OpenGL/OpenGLShader.cpp index 8a9b261c..94712bd3 100644 --- a/Karma/src/Platform/OpenGL/OpenGLShader.cpp +++ b/Karma/src/Platform/OpenGL/OpenGLShader.cpp @@ -5,7 +5,7 @@ namespace Karma { - OpenGLShader::OpenGLShader(const std::string& vertexSrc, const std::string& fragmentSrc) : Shader(nullptr) + OpenGLShader::OpenGLShader(const std::string& vertexSrc, const std::string& fragmentSrc) : Shader() { // Create an empty vertex shader handle GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); @@ -109,8 +109,7 @@ namespace Karma glDetachShader(program, fragmentShader); } - OpenGLShader::OpenGLShader(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, std::shared_ptr ubo, - const std::string& shaderName) : Shader(ubo) + OpenGLShader::OpenGLShader(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, const std::string& shaderName) : Shader() { m_ShaderName = shaderName; @@ -120,12 +119,6 @@ namespace Karma shaderSources[GL_FRAGMENT_SHADER] = KarmaUtilities::ReadFileToSpitString(fragmentSrcFile); Compile(shaderSources); - - m_UniformBufferObject = std::static_pointer_cast(ubo); - if (m_UniformBufferObject == nullptr) - { - KR_CORE_WARN("Cast to OpenGLUniformBuffer failed."); - } } void OpenGLShader::Compile(const std::unordered_map& shaderSources) diff --git a/Karma/src/Platform/OpenGL/OpenGLShader.h b/Karma/src/Platform/OpenGL/OpenGLShader.h index 722cd83e..b9c44628 100644 --- a/Karma/src/Platform/OpenGL/OpenGLShader.h +++ b/Karma/src/Platform/OpenGL/OpenGLShader.h @@ -52,8 +52,7 @@ namespace Karma * * @since Karma 1.0.0 */ - OpenGLShader(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, std::shared_ptr ubo, - const std::string& shaderName); + OpenGLShader(const std::string& vertexSrcFile, const std::string& fragmentSrcFile, const std::string& shaderName); /** * @brief Deletes the appropriate resources, no longer in use, and clean up @@ -111,6 +110,5 @@ namespace Karma private: // OpenGL's identification scheme uint32_t m_RendererID; - std::shared_ptr m_UniformBufferObject; }; } diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 50544bd7..8f7cc250 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -253,6 +253,9 @@ namespace Karma { m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); BufferCreation(); + + std::shared_ptr uboPtr(this); + VulkanHolder::GetVulkanContext()->RegisterUBO(uboPtr); } VulkanUniformBuffer::~VulkanUniformBuffer() diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index f10a3725..3b73e418 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -293,6 +293,8 @@ namespace Karma public: /** * @brief Constructor for Vulkan buffer. Calls VulkanUniformBuffer::BufferCreation(). + * + * Also registers the UBO with VulkanContext so that it can be updated appropriately during rendering. * * @param dataTypes List of data types for uniforms to be uploaded to GPU (like used in shaders), * for instance https://github.com/ravimohan1991/KarmaEngine/blob/138c172ccedf31acfab982af51ae130f9a37d3bb/Application/src/KarmaApp.cpp#L39 where Mat4 are for https://github.com/ravimohan1991/KarmaEngine/blob/138c172ccedf31acfab982af51ae130f9a37d3bb/Resources/Shaders/shader.vert#L9-L13 diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index b9641ad8..2c1e9789 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -24,11 +24,6 @@ namespace Karma */ class RendererAPI; - /** - * @brief Forward declaration - */ - class VulkanVertexArray; - /** * @brief Forward declaration */ @@ -117,11 +112,16 @@ namespace Karma }; /** - * @brief Vulkan API has the following concepts - * 1. Physical Device (https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families): The software counterpart (VkPhysicalDevice) of a graphics card (GPU). Logical device is created from physical device. - * 2. Device (https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues): The so called logical device for interfacing with the physical device. All the machinery (swapchain, graphicspipeline, and all that) are created from logical device. + * @brief A class for Vulkan specific graphics context. This class also contains all the common Vulkan resources shared by various + * elements that are rendered, for instance UniformBufferObjects, graphicspipeline etc. + * + * ATM this class also contains resources like swapchain, commandpool etc which are used by KarmaGui. * - * Host : is CPU the host? + * @note Vulkan API has the following concepts + * 1. Physical Device (https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families): The software counterpart (VkPhysicalDevice) of a graphics card (GPU). Logical device is created from physical device. + * 2. Device (https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues): The so called logical device for interfacing with the physical device. All the machinery (swapchain, graphicspipeline, and all that) are created from logical device. + * @note CPU is always the host in Vulkan and GPU is the device. + * @since Karma 1.0.0 */ class KARMA_API VulkanContext : public GraphicsContext { @@ -545,6 +545,13 @@ namespace Karma * * @param frameIndex The index of the frame for which to upload UBO data * + * @note The frameIndex is used to determine which uniform buffer to update, as multiple buffers + * may be used for double or triple buffering to avoid synchronization issues between CPU and GPU. + * + * @note The uniform buffers are resized automatically based on MAX_FRAMES_IN_FLIGHT (number of images (to work upon (CPU side) + * whilst an image is being rendered (GPU side processing)) + 1) + * @see VulkanUniformBuffer::BufferCreation() + * * @see KarmaGuiRenderer::FrameRender, VulkanBuffer::UploadUniformBuffer * @since Karma 1.0.0 */ @@ -608,6 +615,7 @@ namespace Karma std::set> m_VulkanUBO; + bool bVSync = false; VkImage m_DepthImage; diff --git a/Karma/src/Platform/Vulkan/VulkanShader.cpp b/Karma/src/Platform/Vulkan/VulkanShader.cpp index 170de2a4..e62e93b9 100644 --- a/Karma/src/Platform/Vulkan/VulkanShader.cpp +++ b/Karma/src/Platform/Vulkan/VulkanShader.cpp @@ -5,15 +5,13 @@ namespace Karma { - VulkanShader::VulkanShader(const std::string& vertexSrc, const std::string& fragmentSrc, std::shared_ptr ubo) : Shader(ubo) + VulkanShader::VulkanShader(const std::string& vertexSrc, const std::string& fragmentSrc) { std::string vString = KarmaUtilities::ReadFileToSpitString(vertexSrc); vertSpirV = Compile(vertexSrc, vString, EShLangVertex);// vertex shader vString = KarmaUtilities::ReadFileToSpitString(fragmentSrc); fragSpirV = Compile(fragmentSrc, vString, EShLangFragment);// fragment shader - - m_UniformBufferObject = std::static_pointer_cast(ubo); } VulkanShader::~VulkanShader() diff --git a/Karma/src/Platform/Vulkan/VulkanShader.h b/Karma/src/Platform/Vulkan/VulkanShader.h index 127fb48f..e6c6115b 100644 --- a/Karma/src/Platform/Vulkan/VulkanShader.h +++ b/Karma/src/Platform/Vulkan/VulkanShader.h @@ -56,7 +56,7 @@ namespace Karma * @see Shader::Create() * @since Karma 1.0.0 */ - VulkanShader(const std::string& vertexSrc, const std::string& fragmentSrc, std::shared_ptr ubo); + VulkanShader(const std::string& vertexSrc, const std::string& fragmentSrc); /** * @brief Destructor @@ -94,12 +94,10 @@ namespace Karma //Getters const std::vector& GetVertSpirV() const { return vertSpirV; } const std::vector& GetFragSpirV() const { return fragSpirV; } - std::shared_ptr GetUniformBufferObject() const { return m_UniformBufferObject; } private: std::vector vertSpirV; std::vector fragSpirV; - std::shared_ptr m_UniformBufferObject; }; } diff --git a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp index eb797342..c49fa777 100644 --- a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp +++ b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp @@ -45,7 +45,7 @@ namespace Karma void VulkanVertexArray::SetShader(std::shared_ptr shader) { m_Shader = std::static_pointer_cast(shader); - VulkanHolder::GetVulkanContext()->RegisterUBO(m_Shader->GetUniformBufferObject()); + //VulkanHolder::GetVulkanContext()->RegisterUBO(m_Shader->GetUniformBufferObject()); GenerateVulkanVA(); } @@ -365,7 +365,7 @@ namespace Karma m_Materials.push_back(material); m_Shader = std::static_pointer_cast(material->GetShader(0)); - VulkanHolder::GetVulkanContext()->RegisterUBO(m_Shader->GetUniformBufferObject()); + //VulkanHolder::GetVulkanContext()->RegisterUBO(m_Shader->GetUniformBufferObject()); GenerateVulkanVA(); } @@ -499,7 +499,7 @@ namespace Karma void VulkanVertexArray::CreateDescriptorSetLayout() { VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = m_Shader->GetUniformBufferObject()->GetBindingPointIndex(); + uboLayoutBinding.binding = 0;//m_Shader->GetUniformBufferObject()->GetBindingPointIndex(); uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uboLayoutBinding.descriptorCount = 1; uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; @@ -553,9 +553,9 @@ namespace Karma for (size_t i = 0; i < maxFramesInFlight; i++) { VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = m_Shader->GetUniformBufferObject()->GetUniformBuffers()[i]; + //bufferInfo.buffer = m_Shader->GetUniformBufferObject()->GetUniformBuffers()[i]; bufferInfo.offset = 0; - bufferInfo.range = m_Shader->GetUniformBufferObject()->GetBufferSize(); + //bufferInfo.range = m_Shader->GetUniformBufferObject()->GetBufferSize(); // Fetch right texture pointer first whose image is to be considered. // Caution: GetTexture index is with temporary assumption that needs addressing. @@ -570,7 +570,7 @@ namespace Karma descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[0].dstSet = m_descriptorSets[i]; - descriptorWrites[0].dstBinding = m_Shader->GetUniformBufferObject()->GetBindingPointIndex(); + descriptorWrites[0].dstBinding = 0;//m_Shader->GetUniformBufferObject()->GetBindingPointIndex(); descriptorWrites[0].dstArrayElement = 0; descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrites[0].descriptorCount = 1; diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index c7f1bcd1..7e3b122d 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -108,6 +108,9 @@ namespace Karma void EditorLayer::OnUpdate(float deltaTime) { + // Hacky, need to see if ubos are updated in correct manner (wrt to input etc) + m_EditorCamera->OnUpdate(deltaTime); + InputPolling(deltaTime); } From 45800e8bcb0946f99a184f094e77fc4c5f9cb210 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 12 Dec 2025 18:08:14 +0530 Subject: [PATCH 017/107] Vulkan Unreal - Phase II --- .../Classes/Components/StaticMeshComponent.h | 13 +- .../Engine/Classes/Engine/StaticMeshActor.cpp | 14 +- .../Engine/Classes/Engine/StaticMeshActor.h | 13 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 62 ++- Karma/src/Karma/Renderer/Camera/Camera.h | 7 + Karma/src/Karma/Renderer/Mesh.cpp | 12 +- Karma/src/Karma/Renderer/Scene.cpp | 6 + Karma/src/Karma/Renderer/Scene.h | 23 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 13 +- Karma/src/Platform/Vulkan/VulkanContext.cpp | 469 ++++++++++++++++++ Karma/src/Platform/Vulkan/VulkanContext.h | 109 ++++ Pranjal/Source/Private/EditorLayer.cpp | 9 +- Resources/Shaders/shader.frag | 3 +- Resources/Shaders/shader.vert | 11 +- 14 files changed, 717 insertions(+), 47 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 54b8fc37..4c985ced 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -10,6 +10,7 @@ #pragma once #include "Components/MeshComponent.h" +#include "Renderer/Mesh.h" namespace Karma { @@ -31,7 +32,7 @@ namespace Karma * * @note This is analogous to Unreal Engine's UStaticMeshComponent::StaticMesh */ - std::shared_ptr m_StaticMesh; + std::shared_ptr m_StaticMesh; public: /** @@ -53,6 +54,14 @@ namespace Karma * @param staticMesh The static mesh to associate with this component. * @since Karma 1.0.0 */ - void SetStaticMesh(std::shared_ptr staticMesh) { m_StaticMesh = staticMesh; } + void SetStaticMesh(std::shared_ptr staticMesh) { m_StaticMesh = staticMesh; } + + /** + * @brief Gets the static mesh associated with this component. + * + * @return std::shared_ptr The static mesh associated with this component. + * @since Karma 1.0.0 + */ + std::shared_ptr GetStaticMesh() const { return m_StaticMesh; } }; } \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 4e6d7348..b5dc4924 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -1,8 +1,6 @@ #include "StaticMeshActor.h" #include "Mesh.h" -#include "StaticMeshComponent.h" - namespace Karma { AStaticMeshActor::AStaticMeshActor(const std::string& name) @@ -21,17 +19,17 @@ namespace Karma loadedMesh.reset(new Mesh(filePath)); // We create uniform buffer object for model's transformation matrix - std::shared_ptr shaderUniform; - shaderUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 0));// Binding point 0 and gets registered in VulkanContext during its initialization + std::shared_ptr meshTransformUniform; + meshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization UBODataPointer uModelMatrix(&GetTransform()); - shaderUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform + meshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform - if (!m_StaticMeshComponent) - { + //if (!m_StaticMeshComponent) + //{ // need to write code to destroy UObjects later m_StaticMeshComponent = NewObject(nullptr, UStaticMeshComponent::StaticClass(), "StaticMeshComponent"); - } + //} m_StaticMeshComponent->SetStaticMesh(loadedMesh); } diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 20446892..2c86659e 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -10,9 +10,11 @@ #pragma once #include "GameFramework/Actor.h" +#include "Components/StaticMeshComponent.h" namespace Karma { + /** * @brief An actor that contains a static mesh component, allowing the mesh to be rendered in the game world. * @@ -29,7 +31,7 @@ namespace Karma * @note This is analogous to Unreal Engine's AStaticMeshActor::StaticMeshComponent */ //std::shared_ptr m_StaticMeshComponent; - class UStaticMeshComponent* m_StaticMeshComponent; + UStaticMeshComponent* m_StaticMeshComponent; public: /** @@ -47,6 +49,15 @@ namespace Karma * @since Karma 1.0.0 */ void LoadMeshFromFile(const std::string& filePath); + + /** + * @brief Getter for the static mesh component + * + * @return UStaticMeshComponent* The static mesh component of this actor. + * + * @since Karma 1.0.0 + */ + UStaticMeshComponent* GetStaticMeshComponent() const { return m_StaticMeshComponent; } /** * @brief Destructor for StaticMeshActor diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 0f577749..f5de307a 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -2,7 +2,7 @@ #include "Renderer/RendererAPI.h" #include "Vulkan/VulkanHolder.h" #include "Renderer/RenderCommand.h" - +#include "StaticMeshActor.h" #include "Platform/Vulkan/VulkanVertexArray.h" // Emedded font @@ -469,6 +469,21 @@ namespace Karma for(auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { + std::shared_ptr scene3D = it->Scene3D; + + //scene3D->GetSceneCamera()->GetViewProjectionUBO()->UploadUniformBuffer(windowData->SemaphoreIndex); + VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); + + FrameDescriptorSets dSets; + if (VulkanHolder::GetVulkanContext()->GetGeneralDescriptorSets().size() > 0) + { + dSets = VulkanHolder::GetVulkanContext()->GetGeneralDescriptorSets()[windowData->SemaphoreIndex]; + } + else + { + continue; + } + { VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; @@ -488,30 +503,39 @@ namespace Karma // The pass starts here and all commands until vkCmdEndRenderPass are recorded into it vkCmdBeginRenderPass(frameOnFlightData->CommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); - vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkanVA->GetKarmaGuiGraphicsPipeline()); - + + // ---- Bind Graphics Pipeline ---- + vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralGraphicsPipeline()); + + // === BIND GLOBAL DESCRIPTOR SETS (once per frame) === + // Set 0: Camera UBO + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 0, 1, &dSets.viewSet, 0, nullptr); + + // Set 1: Texture (shared by all meshes) + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 1, 1, &dSets.textureSet, 0, nullptr); + + uint32_t objectIndex = 0; + // ---- Bind 3D Vertex And Index Buffers ---- + for(const auto& smActor : scene3D->GetSMActors()) { - VkBuffer vertexBuffers[1] = { vulkanVA->GetVertexBuffer()->GetVertexBuffer() }; + std::shared_ptr mesh = smActor->GetStaticMeshComponent()->GetStaticMesh(); + + VkBuffer vertexBuffers[1] = { std::static_pointer_cast(mesh->GetVertexBuffer())->GetVertexBuffer() }; VkDeviceSize vertexOffset[1] = { 0 }; vkCmdBindVertexBuffers(frameOnFlightData->CommandBuffer, 0, 1, vertexBuffers, vertexOffset); - vkCmdBindIndexBuffer(frameOnFlightData->CommandBuffer, vulkanVA->GetIndexBuffer()->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); + vkCmdBindIndexBuffer(frameOnFlightData->CommandBuffer, std::static_pointer_cast(mesh->GetIndexBuffer())->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); + + uint32_t dynamicOffsets[] = { objectIndex * 256 }; + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectSet, 1, dynamicOffsets); + + // ----Issue Draw Commands---- + // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) + vkCmdDrawIndexed(frameOnFlightData->CommandBuffer, objectIndex, 1, 0, 0, 0); + + objectIndex++; } - vulkanVA->UpdateProcessAndSetReadyForSubmission(); - vulkanVA->Bind(); - - VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); - - // ---- Bind descriptor sets (e.g., uniforms for MVP matrices, lighting) ---- - // Assumes layout compatibility between pipeline and descriptor set - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkanVA->GetGraphicsPipelineLayout(), 0, 1, &vulkanVA->GetDescriptorSets()[windowData->SemaphoreIndex], 0, nullptr); - - // ---- Issue Draw Commands ---- - // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) - vkCmdDrawIndexed(frameOnFlightData->CommandBuffer, vulkanVA->GetIndexBuffer()->GetCount(), 1, 0, 0, 0); - // ---- End the Offscreen Render Pass ---- vkCmdEndRenderPass(frameOnFlightData->CommandBuffer); } diff --git a/Karma/src/Karma/Renderer/Camera/Camera.h b/Karma/src/Karma/Renderer/Camera/Camera.h index b8221144..5b407b17 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.h +++ b/Karma/src/Karma/Renderer/Camera/Camera.h @@ -176,6 +176,13 @@ namespace Karma */ const glm::mat4& GetViewMatirx() const { return m_ViewMatrix; } + /** + * @brief Getter for ViewProjection UBO + * + * @since Karma 1.0.0 + */ + const std::shared_ptr& GetViewProjectionUBO() const { return m_ViewProjectionUBO; } + private: void RecalculateViewMatrix(); diff --git a/Karma/src/Karma/Renderer/Mesh.cpp b/Karma/src/Karma/Renderer/Mesh.cpp index 03382f2c..80959c20 100644 --- a/Karma/src/Karma/Renderer/Mesh.cpp +++ b/Karma/src/Karma/Renderer/Mesh.cpp @@ -2,6 +2,9 @@ #include "RenderCommand.h" #include "Renderer/Material.h" +// Experimental +#include "Platform/Vulkan/VulkanHolder.h" + namespace Karma { // We initialize the dictionary @@ -62,11 +65,8 @@ namespace Karma // Set default static material m_StaticMaterial.reset(new Material()); - // We are creating shader here for the static material, but need to find a way to share shaders amongst multiple materials - std::shared_ptr m_ModelShader; - m_ModelShader.reset(Shader::Create("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag", "CylinderShader")); - - m_StaticMaterial->AddShader(m_ModelShader); + // TODO: Need to abastract this shader addition process. ATM tied to vulkan only + // m_StaticMaterial->AddShader(std::static_pointer_cast(VulkanHolder::GetVulkanContext()->GetGeneralShader())); // We add default texture to the material std::shared_ptr defaultTexture; @@ -195,10 +195,12 @@ namespace Karma } } + // Checkout VulkanContext::CreateKarmaGuiGeneralGraphicsPipeline if you modify this function void Mesh::GaugeVertexDataLayout(aiMesh* meshToProcess, BufferLayout& buffLayout) { buffLayout.PushElement({ ShaderDataType::Float3, "v_Position" }); + // We check for optional attributes and add them to the layout if (meshToProcess->mTextureCoords[0] != nullptr) { buffLayout.PushElement({ ShaderDataType::Float2, "v_UV" }); diff --git a/Karma/src/Karma/Renderer/Scene.cpp b/Karma/src/Karma/Renderer/Scene.cpp index e80e5c29..3bdd40d2 100644 --- a/Karma/src/Karma/Renderer/Scene.cpp +++ b/Karma/src/Karma/Renderer/Scene.cpp @@ -1,4 +1,5 @@ #include "Scene.h" +#include "StaticMeshActor.h" namespace Karma { @@ -28,6 +29,11 @@ namespace Karma m_VertexArrays.push_back(vertexArray); } + void Scene::AddStaticMeshActor(std::shared_ptr smActor) + { + m_SMActors.push_back(smActor); + } + void Scene::AddCamera(std::shared_ptr camera) { m_Cameras.push_back(camera); diff --git a/Karma/src/Karma/Renderer/Scene.h b/Karma/src/Karma/Renderer/Scene.h index 400631d6..6c28e0b0 100644 --- a/Karma/src/Karma/Renderer/Scene.h +++ b/Karma/src/Karma/Renderer/Scene.h @@ -14,6 +14,9 @@ namespace Karma { + + class AStaticMeshActor; + /** * @brief Class for organizing and containing Scene relevant data */ @@ -42,6 +45,14 @@ namespace Karma */ void AddVertexArray(std::shared_ptr vertexArray); + /** + * @brief Add StaticMeshActor to the scene + * + * @param smActor The StaticMeshActor to be added + * @since Karma 1.0.0 + */ + void AddStaticMeshActor(std::shared_ptr smActor); + /** * @brief Add Camera * @@ -83,11 +94,19 @@ namespace Karma // Getters /** * @brief Getter for the VertexArray - * + * + * @note to be deprecated * @since Karma 1.0.0 */ std::shared_ptr GetRenderableVertexArray() const; + /** + * @brief Get the list of StaticMeshActors in the scene + * + * @since Karma 1.0.0 + */ + const std::vector>& GetSMActors() const { return m_SMActors; } + /** * @brief Getter for the Camera being used for the scene * @@ -132,6 +151,8 @@ namespace Karma private: std::vector> m_VertexArrays; + + std::vector> m_SMActors; std::vector> m_Cameras; glm::vec4 m_ClearColor; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 0dc694ce..109c0450 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1316,8 +1316,11 @@ namespace Karma CreateOffScreenTextureFrameBufferResource(&(*it)); - std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); - vulkanVA->CreateKarmaGuiGraphicsPipeline(backendData->OffScreenRR.RenderPass, it->Size.x, it->Size.y); + // std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); + // vulkanVA->CreateKarmaGuiGraphicsPipeline(backendData->OffScreenRR.RenderPass, it->Size.x, it->Size.y); + + // assuming that all 3D scenes use same pipeline layout. This may change later + VulkanHolder::GetVulkanContext()->CreateKarmaGuiGeneralGraphicsPipeline(backendData->OffScreenRR.RenderPass, it->Size.x, it->Size.y); } } @@ -1712,11 +1715,13 @@ namespace Karma vkFreeMemory(vulkanInfo->Device, it->DeviceMemory, nullptr); vkDestroyFramebuffer(vulkanInfo->Device, it->FrameBuffer, nullptr); - std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); - vulkanVA->CleanupKarmaGuiGraphicsPipeline(); + //std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); + //vulkanVA->CleanupKarmaGuiGraphicsPipeline(); //vkDestroyRenderPass(vulkanInfo->Device, it->RenderPass, nullptr); } + // Assuming only on graphicspipeline + VulkanHolder::GetVulkanContext()->CleanUpKarmaGuiGeneralGraphicsPipeline(); ClearVulkanWindowData(windowData, true); } diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index a7aed6c4..66da6253 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -6,6 +6,7 @@ #include "Karma/Renderer/RenderCommand.h" #include "Platform/Vulkan/VulkanVertexArray.h" #include "Platform/Vulkan/VulkanBuffer.h" +#include "Platform/Vulkan/VulkanTexture.h" namespace Karma { @@ -19,6 +20,37 @@ namespace Karma bool VulkanContext::bEnableValidationLayers = false; #endif + static VkFormat ShaderDataTypeToVulkanType(ShaderDataType type) + { + switch (type) + { + case ShaderDataType::Float: + return VK_FORMAT_R32_SFLOAT; + case ShaderDataType::Float2: + return VK_FORMAT_R32G32_SFLOAT; + case ShaderDataType::Float3: + return VK_FORMAT_R32G32B32_SFLOAT; + case ShaderDataType::Float4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case ShaderDataType::None: + case ShaderDataType::Mat3: + case ShaderDataType::Mat4: + case ShaderDataType::Int: + case ShaderDataType::Int2: + case ShaderDataType::Int3: + case ShaderDataType::Int4: + case ShaderDataType::Bool: + // Refer Mesh::GaugeVertexDataLayout for usual datatype + // to be used in the context of vertex buffer + KR_CORE_ASSERT(false, "Weird ShaderDataType is being used") + return VK_FORMAT_UNDEFINED; + break; + } + + KR_CORE_ASSERT(false, "Vulkan doesn't support this ShaderDatatype"); + return VK_FORMAT_UNDEFINED; + } + VulkanContext::VulkanContext(GLFWwindow* windowHandle) : m_windowHandle(windowHandle) { @@ -28,6 +60,14 @@ namespace Karma VulkanContext::~VulkanContext() { + vkDestroyDescriptorSetLayout(m_device, m_ViewLayout, nullptr); + vkDestroyDescriptorSetLayout(m_device, m_TextureLayout, nullptr); + + vkDestroyDescriptorSetLayout(m_device, m_ObjectLayout, nullptr); + vkDestroyPipelineLayout(m_device, m_KarmaGuiGeneralPipelineLayout, nullptr); + + vkDestroyDescriptorPool(m_device, m_GeneralDescriptorPool, nullptr); + m_vulkanRendererAPI->ClearVulkanRendererAPI(); ClearUBO(); @@ -60,6 +100,11 @@ namespace Karma glslang::FinalizeProcess(); } + void VulkanContext::CleanUpKarmaGuiGeneralGraphicsPipeline() + { + vkDestroyPipeline(m_device, m_KarmaGuiGeneralGraphicsPipeline, nullptr); + } + void VulkanContext::RegisterUBO(const std::shared_ptr& ubo) { m_VulkanUBO.insert(ubo); @@ -109,6 +154,430 @@ namespace Karma // For glslang Initializeglslang(); + + CreateGeneralShader(); + CreateGeneralDescriptorSetLayouts(); + CreateGeneralDescriptorPool(); + + } + + void VulkanContext::CreateGeneralShader() + { + // We are creating general shader here for the static material (material used as default for meshes) + // Ponder how this would look like in OpenGL + m_GeneralShader.reset(new VulkanShader("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag")); + } + + VkShaderModule VulkanContext::CreateShaderModule(const std::vector& code) + { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size() * sizeof(uint32_t); + createInfo.pCode = code.data(); + + VkShaderModule shaderModule; + VkResult result = vkCreateShaderModule(m_device, &createInfo, nullptr, &shaderModule); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create shader module!"); + + return shaderModule; + } + + void VulkanContext::CreateKarmaGuiGeneralGraphicsPipeline(VkRenderPass renderPassKG, float windowKGWidth, float windowKGHeight) + { + std::array setLayouts = { + m_ViewLayout, // set = 0 + m_TextureLayout, // set = 1 + m_ObjectLayout // set = 2 + }; + + VkPipelineLayoutCreateInfo plInfo{}; + plInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + plInfo.setLayoutCount = static_cast(setLayouts.size()); + plInfo.pSetLayouts = setLayouts.data(); + plInfo.pushConstantRangeCount = 0; + plInfo.pPushConstantRanges = nullptr; + + VkResult result = vkCreatePipelineLayout(m_device, &plInfo, nullptr, &m_KarmaGuiGeneralPipelineLayout); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create pipeline layout!"); + + VkShaderModule vertShaderModule = CreateShaderModule(m_GeneralShader->GetVertSpirV()); + VkShaderModule fragShaderModule = CreateShaderModule(m_GeneralShader->GetFragSpirV()); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + // Telling vulkan what to expect from vertex data in terms of attributes and their rate of loading + uint32_t index = 0; + + // See Mesh::GaugeVertexDataLayout to understand the layout of vertex data we are using + BufferLayout layout; + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Position")); + layout.PushElement(BufferElement(ShaderDataType::Float2, "v_UV")); + + VkVertexInputBindingDescription bindingDescription = {}; + std::vector attributeDescriptions; + + bindingDescription.binding = 0; + bindingDescription.stride = layout.GetStride(); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + for (const auto& element : layout) + { + VkVertexInputAttributeDescription elementAttributeDescription{}; + elementAttributeDescription.binding = 0; + elementAttributeDescription.location = index; + elementAttributeDescription.format = ShaderDataTypeToVulkanType(element.Type); + elementAttributeDescription.offset = static_cast(element.Offset); + + attributeDescriptions.push_back(elementAttributeDescription); + index++; + } + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = windowKGWidth; + viewport.height = windowKGHeight; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent.width = windowKGWidth; + scissor.extent.height = windowKGHeight; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + // Antialiasing + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineDepthStencilStateCreateInfo depthStencil{}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + + VkBool32 bLogicalOperationsAllowed = m_SupportedDeviceFeatures.logicOp; + + // Mix the old and new value to produce a final color + // finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor; + // finalColor.a = newAlpha.a; + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + if (!bLogicalOperationsAllowed) + { + colorBlendAttachment.blendEnable = VK_TRUE; + } + else + { + colorBlendAttachment.blendEnable = VK_FALSE; + } + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // Combine the old and new value using a bitwise operation + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + if (bLogicalOperationsAllowed) + { + colorBlending.logicOpEnable = VK_TRUE; + } + else + { + colorBlending.logicOpEnable = VK_FALSE; + } + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = m_KarmaGuiGeneralPipelineLayout; + pipelineInfo.renderPass = renderPassKG; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.pDepthStencilState = &depthStencil; + + VkResult resultGP = vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, + 1, &pipelineInfo, nullptr, &m_KarmaGuiGeneralGraphicsPipeline); + + KR_CORE_ASSERT(resultGP == VK_SUCCESS, "Failed to create graphics pipeline!"); + + + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + } + + void VulkanContext::CreateGeneralDescriptorSetLayouts() + { + // ===== Set 0: Camera UBO ===== + VkDescriptorSetLayoutBinding viewBinding{}; + viewBinding.binding = 0; + viewBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + viewBinding.descriptorCount = 1; + viewBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + viewBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo viewLayoutInfo{}; + viewLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + viewLayoutInfo.bindingCount = 1; + viewLayoutInfo.pBindings = &viewBinding; + VkResult result = vkCreateDescriptorSetLayout(m_device, &viewLayoutInfo, nullptr, &m_ViewLayout); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create view descriptor set layout!"); + + // === SET 1: MATERIAL UBO + 1 TEXTURE === + /*std::array materialBindings{}; + + // Binding 0: Material UBO + materialBindings[0].binding = 0; + materialBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + materialBindings[0].descriptorCount = 1; + materialBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT; + + // Binding 1: SINGLE Texture + materialBindings[1].binding = 1; + materialBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + materialBindings[1].descriptorCount = 1; + materialBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo materialLayoutInfo{}; + materialLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + materialLayoutInfo.bindingCount = 2; // Only 2 bindings now + materialLayoutInfo.pBindings = materialBindings.data(); + vkCreateDescriptorSetLayout(device, &materialLayoutInfo, nullptr, &materialLayout); + + GLSLANG USAGE EXAMPLE: + // Set 1: Material UBO + 1 Texture + layout(set = 1, binding = 0) uniform MaterialUBO { + vec4 baseColor; + float metallic; + float roughness; + } material; + + layout(set = 1, binding = 1) uniform sampler2D albedoTexture; // Single texture! + */ + + // ===== Set 1: Single texture (combined image + sampler) ===== + VkDescriptorSetLayoutBinding texBinding{}; + texBinding.binding = 0; + texBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texBinding.descriptorCount = 1; + texBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + texBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo texInfo{}; + texInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + texInfo.bindingCount = 1; + texInfo.pBindings = &texBinding; + + result = vkCreateDescriptorSetLayout(m_device, &texInfo, nullptr, &m_TextureLayout); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create texture descriptor set layout!"); + + // ===== Set 2: Per-mesh transform UBO (dynamic so you can pack many) ===== + VkDescriptorSetLayoutBinding objectBinding{}; + objectBinding.binding = 0; + objectBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + objectBinding.descriptorCount = 1; + objectBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + objectBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo objInfo{}; + objInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + objInfo.bindingCount = 1; + objInfo.pBindings = &objectBinding; + + result = vkCreateDescriptorSetLayout(m_device, &objInfo, nullptr, &m_ObjectLayout); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create object descriptor set layout!"); + } + + void VulkanContext::CreateGeneralDescriptorPool() + { + RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); + VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); + + if (!vulkanRendererAPI) + { + KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); + return; + } + + uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); + + std::array poolSizes{}; + + // Uniform Buffers : Camera UBO + Object UBO + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = 2 * maxFramesInFlight; // 1 for camera UBO + 1 for object UBO + + // Combined Image Samplers : Texture + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = 1 * maxFramesInFlight; // 1 for texture + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = static_cast(poolSizes.size()); + poolInfo.pPoolSizes = poolSizes.data(); + poolInfo.maxSets = 3 * maxFramesInFlight; // 3 sets per frame + + VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_GeneralDescriptorPool); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create general descriptor pool!"); + } + + void VulkanContext::CreateGeneralDescriptorSets() + { + RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); + VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); + + if (!vulkanRendererAPI) + { + KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); + return; + } + + uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); + + std::vector> m_GeneralDescriptorSets(maxFramesInFlight); + for (size_t i = 0; i < maxFramesInFlight; i++) + { + std::array setLayouts = { + m_ViewLayout, // set = 0 + m_TextureLayout, // set = 1 + m_ObjectLayout // set = 2 + }; + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_GeneralDescriptorPool; + allocInfo.descriptorSetCount = static_cast(setLayouts.size()); + allocInfo.pSetLayouts = setLayouts.data(); + + VkResult result = vkAllocateDescriptorSets(m_device, &allocInfo, m_GeneralDescriptorSets[i].data()); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to allocate general descriptor sets!"); + } + } + + void VulkanContext::UpdateGeneralDescriptorSets(uint32_t frameIndex, VkBuffer viewBuffer, uint32_t viewBufferSize, std::shared_ptr texture, VkBuffer objectBuffer) + { + FrameDescriptorSets& frameDescriptorSets = m_GeneralDescriptorSets[frameIndex]; + + // Update Set 0: Camera UBO + { + VkDescriptorBufferInfo viewInfo{}; + viewInfo.buffer = viewBuffer; + viewInfo.offset = frameIndex * viewBufferSize;// Try 2 * sizeof(glm::mat4) also + viewInfo.range = viewBufferSize; + + VkWriteDescriptorSet viewWrite{}; + viewWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + viewWrite.dstSet = frameDescriptorSets.viewSet; + viewWrite.dstBinding = 0; + viewWrite.dstArrayElement = 0; + viewWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + viewWrite.pBufferInfo = &viewInfo; + + vkUpdateDescriptorSets(m_device, 1, &viewWrite, 0, nullptr); + } + + // Update set 1: Texture + { + VkDescriptorImageInfo texInfo{}; + texInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + texInfo.sampler = texture->GetImageSampler(); + texInfo.imageView = texture->GetImageView(); + + VkWriteDescriptorSet texWrite{}; + texWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texWrite.dstSet = frameDescriptorSets.textureSet; + texWrite.dstBinding = 0; + texWrite.dstArrayElement = 0; + texWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texWrite.pImageInfo = &texInfo; + texWrite.descriptorCount = 1; + + vkUpdateDescriptorSets(m_device, 1, &texWrite, 0, nullptr); + } + + // Update set 2: Per-mesh UBO will be updated during mesh rendering + { + uint32_t maxObjects = 100; + + VkDescriptorBufferInfo objectInfo{}; + objectInfo.buffer = objectBuffer; + objectInfo.offset = frameIndex * maxObjects * sizeof(glm::mat4); // Assuming each object's transform is a mat4 + objectInfo.range = maxObjects * sizeof(glm::mat4); + + VkWriteDescriptorSet objectWrite{}; + objectWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + objectWrite.dstSet = frameDescriptorSets.objectSet; + objectWrite.dstBinding = 0; + objectWrite.dstArrayElement = 0; + objectWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + objectWrite.pBufferInfo = &objectInfo; + objectWrite.descriptorCount = 1; + + vkUpdateDescriptorSets(m_device, 1, &objectWrite, 0, nullptr); + } } void VulkanContext::Initializeglslang() diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index 2c1e9789..b353be92 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -19,6 +19,8 @@ namespace Karma { + class VulkanShader; + /** * @brief Forward declaration */ @@ -29,6 +31,13 @@ namespace Karma */ struct VulkanUniformBuffer; + struct FrameDescriptorSets + { + VkDescriptorSet viewSet; // Set 0: Camera UBO + VkDescriptorSet textureSet; // Set 1: Texture + VkDescriptorSet objectSet; // Set 2: Per-mesh UBO (dynamic) + }; + /** * @brief A structure for graphics and present queuefamilies * @@ -527,6 +536,92 @@ namespace Karma */ void RecreateUBO(); + /** + * @brief Creates a Vulkan shader module from SPIR-V bytecode. + * + * This is specifically used when creating the graphics pipeline to load vertex and fragment shaders. + * + * For instance while creating vertex shader stage info (pipeline creation): + * @code{.cpp} + * VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + * vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + * vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + * vertShaderStageInfo.module = vertShaderModule; + * vertShaderStageInfo.pName = "main"; + * @endcode + * + * @param code The SPIR-V bytecode as a vector of uint32_t + * + * @return The created VkShaderModule + * @since Karma 1.0.0 + */ + VkShaderModule CreateShaderModule(const std::vector& code); + + void CreateGeneralShader(); + + /** + * @brief Creates general descriptor set layouts for common resources like camera UBO, texture sampler, and object UBO. + * + * These descriptor set layouts define how shader resources are organized and accessed in the rendering pipeline. + * + * @since Karma 1.0.0 + */ + void CreateGeneralDescriptorSetLayouts(); + + /** + * @brief Creates the graphics pipeline for rendering 3D objects in the KarmaGui's exhibitor window. + * + * @param renderPassKG The render pass to be used with the Karma GUI graphics pipeline + * @param windowKGWidth The width of the window for viewport and scissor setup + * @param windowKGHeight The height of the window for viewport and scissor setup + * + * @since Karma 1.0.0 + */ + void CreateKarmaGuiGeneralGraphicsPipeline(VkRenderPass renderPassKG, float windowKGWidth, float windowKGHeight); + + /** + * @brief Cleans up the resources associated with the KarmaGui general graphics pipeline. + * + * This includes destroying the pipeline and pipeline layout is destroyed in destructor. + * + * @since Karma 1.0.0 + */ + void CleanUpKarmaGuiGeneralGraphicsPipeline(); + + /** + * @brief Creates a general descriptor pool for allocating descriptor sets for common resources. + * + * The descriptor sets are like so + * 1. Camera UBO descriptor set + * 2. Texture sampler descriptor set + * 3. Object UBO descriptor set + * 4. Maybe add more later + * + * @since Karma 1.0.0 + */ + void CreateGeneralDescriptorPool(); + + /** + * @brief Allocates and writes to general descriptor sets for common resources like camera UBO, texture sampler, and object UBO. + * + * These descriptor sets are used in the graphics pipeline to bind the appropriate resources for rendering. + * + * @since Karma 1.0.0 + */ + void CreateGeneralDescriptorSets(); + + /** + * @brief Updates the general descriptor sets with the provided view buffer for the specified frame index. + * + * @note viewbuffer is the vkbuffer of the camera in scene + * + * @param frameIndex The index of the frame for which to update the descriptor sets + * @param viewBuffer The buffer containing view (camera) data + * + * @since Karma 1.0.0 + */ + void UpdateGeneralDescriptorSets(uint32_t frameIndex, VkBuffer viewBuffer, uint32_t viewBufferSize, std::shared_ptr texture, VkBuffer objectBuffer); + /** * @brief Uploads data to the registered VulkanUniformBuffers for the specified frame index. * @@ -578,6 +673,10 @@ namespace Karma uint32_t GetMinImageCount() const { return m_MinImageCount; } VkSurfaceKHR GetSurface() const { return m_surface; } VkPresentModeKHR GetPresentMode() const { return m_presentMode; } + std::shared_ptr GetGeneralShader() const { return m_GeneralShader; } + VkPipeline GetKarmaGuiGeneralGraphicsPipeline() const { return m_KarmaGuiGeneralGraphicsPipeline; } + VkPipelineLayout GetKarmaGuiGeneralPipelineLayout() const { return m_KarmaGuiGeneralPipelineLayout; } + const std::vector& GetGeneralDescriptorSets() const { return m_GeneralDescriptorSets; } private: // Apologies for little out-of-sync naming convention, was dealing with flood of lines when @@ -615,6 +714,16 @@ namespace Karma std::set> m_VulkanUBO; + // General vulkan resources + std::shared_ptr m_GeneralShader; + + VkPipelineLayout m_KarmaGuiGeneralPipelineLayout; + VkPipeline m_KarmaGuiGeneralGraphicsPipeline; + VkDescriptorSetLayout m_ViewLayout; // Camera UBO + VkDescriptorSetLayout m_TextureLayout; // Material UBO + texture sampler + VkDescriptorSetLayout m_ObjectLayout; // Object UBO + VkDescriptorPool m_GeneralDescriptorPool; + std::vector m_GeneralDescriptorSets; bool bVSync = false; diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 7e3b122d..b719bfac 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -55,16 +55,16 @@ namespace Karma } // Then we set texture - m_ModelTexture.reset(new Karma::Texture(Karma::TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); + /*m_ModelTexture.reset(new Karma::Texture(Karma::TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); m_ModelMaterial->AddTexture(m_ModelTexture); m_ModelMaterial->AttatchMainCamera(m_EditorCamera); //Is this needed? - m_ModelVertexArray->SetMaterial(m_ModelMaterial); + m_ModelVertexArray->SetMaterial(m_ModelMaterial);*/ m_EditorScene.reset(new Karma::Scene()); - m_EditorScene->AddCamera(m_EditorCamera); - m_EditorScene->AddVertexArray(m_ModelVertexArray); + //m_EditorScene->AddCamera(m_EditorCamera); + //m_EditorScene->AddVertexArray(m_ModelVertexArray); m_EditorScene->SetClearColor({ 0.0f, 0.0f, 0.0f, 1 }); } @@ -373,6 +373,7 @@ namespace Karma KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); + m_EditorScene->AddStaticMeshActor(std::shared_ptr(staticMeshActor)); } } } diff --git a/Resources/Shaders/shader.frag b/Resources/Shaders/shader.frag index a6d8701f..22641847 100644 --- a/Resources/Shaders/shader.frag +++ b/Resources/Shaders/shader.frag @@ -6,7 +6,8 @@ layout(location = 1) in vec2 fragUVs; layout(location = 0) out vec4 outColor; -layout(binding = 1) uniform sampler2D texSampler; +// set 1: texture +layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { diff --git a/Resources/Shaders/shader.vert b/Resources/Shaders/shader.vert index ab96f219..eaf63270 100644 --- a/Resources/Shaders/shader.vert +++ b/Resources/Shaders/shader.vert @@ -11,16 +11,23 @@ layout(location = 2) in vec4 inColor; layout(location = 0) out vec4 fragColor; layout(location = 1) out vec2 fragUVs; -layout(std140, binding = 0) uniform MVPUniformBufferObject +// set 0: camera +layout(set = 0, binding = 0) uniform VPUniformBufferObject { mat4 u_Projection; mat4 u_View; }; +// set 2: per-mesh transform +layout(set = 2, binding = 0) uniform ModelUniformBufferObject +{ + mat4 u_Model; +}; + void main() { - gl_Position = u_Projection * u_View * vec4(inPosition, 1.0); + gl_Position = u_Projection * u_View * u_Model * vec4(inPosition, 1.0); fragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); fragUVs = inUV; } From ee6d7dcf643549637023d55a48f77812c42c3539 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 13 Dec 2025 06:52:52 +0530 Subject: [PATCH 018/107] Code no-compile --- .../Classes/Components/StaticMeshComponent.h | 4 ++-- .../Engine/Classes/Engine/StaticMeshActor.cpp | 18 ++++++------------ .../Engine/Classes/Engine/StaticMeshActor.h | 12 ++++-------- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 2 ++ Karma/src/Karma/Renderer/Camera/Camera.cpp | 10 +++++----- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 2 +- Karma/src/Platform/Vulkan/VulkanContext.cpp | 11 +++++++---- Karma/src/Platform/Vulkan/VulkanContext.h | 2 +- Resources/Shaders/shader.vert | 2 -- 9 files changed, 28 insertions(+), 35 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 4c985ced..31de591c 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -23,7 +23,7 @@ namespace Karma */ class KARMA_API UStaticMeshComponent : public UMeshComponent { - DECLARE_KARMA_CLASS(UMeshComponent, UStaticMeshComponent) + DECLARE_KARMA_CLASS(UStaticMeshComponent, UMeshComponent) private: @@ -64,4 +64,4 @@ namespace Karma */ std::shared_ptr GetStaticMesh() const { return m_StaticMesh; } }; -} \ No newline at end of file +} diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index b5dc4924..71189a31 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -8,29 +8,23 @@ namespace Karma { } - AStaticMeshActor::~AStaticMeshActor() - { - // Cleanup code if necessary - } - void AStaticMeshActor::LoadMeshFromFile(const std::string& filePath) { std::shared_ptr loadedMesh; loadedMesh.reset(new Mesh(filePath)); // We create uniform buffer object for model's transformation matrix - std::shared_ptr meshTransformUniform; - meshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization + m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization UBODataPointer uModelMatrix(&GetTransform()); - meshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform + m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform - //if (!m_StaticMeshComponent) - //{ + if (!m_StaticMeshComponent) + { // need to write code to destroy UObjects later m_StaticMeshComponent = NewObject(nullptr, UStaticMeshComponent::StaticClass(), "StaticMeshComponent"); - //} + } m_StaticMeshComponent->SetStaticMesh(loadedMesh); } -} \ No newline at end of file +} diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 2c86659e..b372d636 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -22,7 +22,7 @@ namespace Karma */ class KARMA_API AStaticMeshActor : public AActor { - DECLARE_KARMA_CLASS(AActor, AStaticMeshActor) + DECLARE_KARMA_CLASS(AStaticMeshActor, AActor) private: /** @@ -32,6 +32,8 @@ namespace Karma */ //std::shared_ptr m_StaticMeshComponent; UStaticMeshComponent* m_StaticMeshComponent; + + std::shared_ptr m_MeshTransformUniform; public: /** @@ -58,11 +60,5 @@ namespace Karma * @since Karma 1.0.0 */ UStaticMeshComponent* GetStaticMeshComponent() const { return m_StaticMeshComponent; } - - /** - * @brief Destructor for StaticMeshActor - */ - virtual ~AStaticMeshActor(); - // Additional methods and members specific to StaticMeshActor can be added here. }; -} \ No newline at end of file +} diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index f5de307a..91920f29 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -503,6 +503,8 @@ namespace Karma // The pass starts here and all commands until vkCmdEndRenderPass are recorded into it vkCmdBeginRenderPass(frameOnFlightData->CommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + // UpdateGeneralDescriptorSets // ---- Bind Graphics Pipeline ---- vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralGraphicsPipeline()); diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index 37624099..d670e6e9 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -9,6 +9,11 @@ namespace Karma Camera::Camera(const glm::vec3& initialCameraPosition) : m_Position(initialCameraPosition), m_LastMouseX(0.0f), m_LastMouseY(0.0f) { m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); + + UBODataPointer uProjection(&m_ProjectionMatrix); + UBODataPointer uView(&m_ViewMatrix); + + m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); } Camera::~Camera() @@ -28,11 +33,6 @@ namespace Karma void Camera::OnUpdate(float deltaTime) { - UBODataPointer uProjection(&m_ProjectionMatrix); - UBODataPointer uView(&m_ViewMatrix); - - // This should work with just updating once in the constructor, may see later - m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); } void Camera::MoveForward(float amount) diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 8f7cc250..e341489c 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -260,7 +260,7 @@ namespace Karma VulkanUniformBuffer::~VulkanUniformBuffer() { - //ClearBuffer(); + ClearBuffer(); } void VulkanUniformBuffer::BufferCreation() diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 66da6253..359233f3 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -69,7 +69,7 @@ namespace Karma vkDestroyDescriptorPool(m_device, m_GeneralDescriptorPool, nullptr); m_vulkanRendererAPI->ClearVulkanRendererAPI(); - ClearUBO(); + //ClearUBO(); for (auto framebuffer : m_swapChainFrameBuffers) { @@ -105,7 +105,7 @@ namespace Karma vkDestroyPipeline(m_device, m_KarmaGuiGeneralGraphicsPipeline, nullptr); } - void VulkanContext::RegisterUBO(const std::shared_ptr& ubo) + void VulkanContext::RegisterUBO(std::shared_ptr ubo) { m_VulkanUBO.insert(ubo); } @@ -158,7 +158,7 @@ namespace Karma CreateGeneralShader(); CreateGeneralDescriptorSetLayouts(); CreateGeneralDescriptorPool(); - + CreateGeneralDescriptorSets(); } void VulkanContext::CreateGeneralShader() @@ -498,7 +498,10 @@ namespace Karma uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); - std::vector> m_GeneralDescriptorSets(maxFramesInFlight); + //std::vector> m_GeneralDescriptorSets(maxFramesInFlight); + + m_GeneralDescriptorSets.resize(maxFramesInFlight); + for (size_t i = 0; i < maxFramesInFlight; i++) { std::array setLayouts = { diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index b353be92..83cc1c49 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -518,7 +518,7 @@ namespace Karma * @see VulkanContext::m_VulkanUBO * @since Karma 1.0.0 */ - void RegisterUBO(const std::shared_ptr& ubo); + void RegisterUBO(std::shared_ptr ubo); /** * @brief Clears all registered VulkanUniformBuffers, freeing their resources. diff --git a/Resources/Shaders/shader.vert b/Resources/Shaders/shader.vert index eaf63270..13cee965 100644 --- a/Resources/Shaders/shader.vert +++ b/Resources/Shaders/shader.vert @@ -5,8 +5,6 @@ layout(location = 0) in vec3 inPosition; layout(location = 1) in vec2 inUV; -layout(location = 2) in vec4 inColor; - layout(location = 0) out vec4 fragColor; layout(location = 1) out vec2 fragUVs; From b820015705e5ca5283f7f5f28e4ca531fbc70f97 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 13 Dec 2025 08:42:27 +0530 Subject: [PATCH 019/107] Code compile --- Karma/src/Platform/Vulkan/VulkanContext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 359233f3..38561906 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -498,9 +498,9 @@ namespace Karma uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); - //std::vector> m_GeneralDescriptorSets(maxFramesInFlight); + std::vector> m_GeneralDescriptorSets(maxFramesInFlight); - m_GeneralDescriptorSets.resize(maxFramesInFlight); + //m_GeneralDescriptorSets.resize(maxFramesInFlight); for (size_t i = 0; i < maxFramesInFlight; i++) { From 3927c514db299cc503dc49840faecd382513526f Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 13 Dec 2025 12:16:00 +0530 Subject: [PATCH 020/107] Vulkan Unreal - Phase III --- Karma/src/Karma/Core/Object.h | 2 ++ Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp | 16 +++++++++++++++- Karma/src/Karma/Core/UObjectAllocator.cpp | 5 +++++ Karma/src/Karma/Core/UObjectAllocator.h | 2 ++ Karma/src/Karma/Core/UObjectBase.h | 4 ++++ Karma/src/Karma/Core/UObjectGlobals.cpp | 4 ++++ Karma/src/Karma/Core/UObjectGlobals.h | 2 ++ .../Engine/Classes/Components/ActorComponent.h | 2 ++ .../Engine/Classes/Components/MeshComponent.h | 4 +++- .../Classes/Components/PrimitiveComponent.h | 2 ++ .../Engine/Classes/Components/SceneComponent.h | 2 ++ .../Classes/Components/StaticMeshComponent.cpp | 2 +- .../Classes/Components/StaticMeshComponent.h | 3 +++ .../Engine/Classes/Engine/StaticMeshActor.cpp | 6 +++--- .../Karma/Engine/Classes/GameFramework/Actor.h | 3 +++ Karma/src/Karma/Renderer/Camera/Camera.cpp | 8 ++++---- Karma/src/Karma/Renderer/Mesh.cpp | 8 ++++---- Karma/src/Karma/Renderer/Scene.cpp | 2 +- Karma/src/Karma/Renderer/Scene.h | 6 +++--- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 4 ++-- Karma/src/Platform/Vulkan/VulkanContext.cpp | 6 ++++-- Karma/src/Platform/Vulkan/VulkanContext.h | 4 ++-- Karma/src/Platform/Vulkan/VulkanVertexArray.cpp | 2 +- Pranjal/Source/Private/EditorLayer.cpp | 2 +- 24 files changed, 75 insertions(+), 26 deletions(-) diff --git a/Karma/src/Karma/Core/Object.h b/Karma/src/Karma/Core/Object.h index cd0b84c7..74121c32 100644 --- a/Karma/src/Karma/Core/Object.h +++ b/Karma/src/Karma/Core/Object.h @@ -129,6 +129,8 @@ namespace Karma */ UObject(); + virtual void ShivaUObject() { this->~UObject(); } + /** * A legacy function from UE */ diff --git a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp index 7647ccc5..3525dd71 100644 --- a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp +++ b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp @@ -1,7 +1,8 @@ #include "KarmaSmriti.h" #include "KarmaMemory.h" - +#include "UObjectBase.h" #include "Core/UObjectAllocator.h" +#include namespace Karma { @@ -30,6 +31,19 @@ namespace Karma void KarmaSmriti::ShutDown() { + for (FRawObjectIterator ObjectItr; ObjectItr; ++ObjectItr) + { + if ((*ObjectItr)->m_Object) + { + KR_INFO("Attempting to delete UObject: {0} ", (*ObjectItr)->m_Object->GetName()); + (*ObjectItr)->m_Object->ShivaUObject(); + } + else + { + KR_INFO("Can't delete {0}", (*ObjectItr)->m_Object->GetName()); + } + } + FMemory::SystemFree(m_pMemBlock); KR_CORE_INFO("Freed Karma's memory softbed"); } diff --git a/Karma/src/Karma/Core/UObjectAllocator.cpp b/Karma/src/Karma/Core/UObjectAllocator.cpp index fa8ddf79..1523a530 100644 --- a/Karma/src/Karma/Core/UObjectAllocator.cpp +++ b/Karma/src/Karma/Core/UObjectAllocator.cpp @@ -2,12 +2,17 @@ #include "Karma/Core/TrueCore/KarmaMemory.h" #include "Karma/Ganit/KarmaMath.h" #include "Core/UObjectBase.h" +#include namespace Karma { /** Global UObjectBase allocator */ FUObjectAllocator GUObjectAllocator; + FUObjectAllocator::~FUObjectAllocator() + { + } + void FUObjectAllocator::AllocatePermanentObjectPool(int32_t InPermanentObjectPoolSize) { m_PermanentObjectPoolSize = InPermanentObjectPoolSize; diff --git a/Karma/src/Karma/Core/UObjectAllocator.h b/Karma/src/Karma/Core/UObjectAllocator.h index e9ef318b..ce5d9c02 100644 --- a/Karma/src/Karma/Core/UObjectAllocator.h +++ b/Karma/src/Karma/Core/UObjectAllocator.h @@ -123,6 +123,8 @@ namespace Karma { } + ~FUObjectAllocator(); + /** * Allocates and initializes the permanent object pool. * diff --git a/Karma/src/Karma/Core/UObjectBase.h b/Karma/src/Karma/Core/UObjectBase.h index 12a6cb94..5ddba2d2 100644 --- a/Karma/src/Karma/Core/UObjectBase.h +++ b/Karma/src/Karma/Core/UObjectBase.h @@ -73,6 +73,10 @@ namespace Karma */ UObjectBase(UClass* inClass, EObjectFlags inFlags, EInternalObjectFlags inInternalFlags, UObject* inOuter, const std::string& inName); + ~UObjectBase() {}; + + virtual void ShivaUObject() { this->~UObjectBase(); } + /** * Walks up the list of outers until it finds a package directly associated with the object. * diff --git a/Karma/src/Karma/Core/UObjectGlobals.cpp b/Karma/src/Karma/Core/UObjectGlobals.cpp index 0d7d33bd..b9c47274 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.cpp +++ b/Karma/src/Karma/Core/UObjectGlobals.cpp @@ -596,6 +596,10 @@ namespace Karma return true; } + FUObjectArray::~FUObjectArray() + { + } + void GetObjectsOfClass(const UClass* ClassToLookFor, KarmaVector& Results, bool bIncludeDerivedClasses, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { //SCOPE_CYCLE_COUNTER(STAT_Hash_GetObjectsOfClass); diff --git a/Karma/src/Karma/Core/UObjectGlobals.h b/Karma/src/Karma/Core/UObjectGlobals.h index 90747122..2215009e 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.h +++ b/Karma/src/Karma/Core/UObjectGlobals.h @@ -538,6 +538,8 @@ namespace Karma * @since Karma 1.0.0 */ bool IsValid(const UObjectBase* Object) const; + + ~FUObjectArray(); }; /** diff --git a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h index e60de3d3..53731d52 100644 --- a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h @@ -75,6 +75,8 @@ namespace Karma */ UActorComponent(); + virtual void ShivaUObject() { this->~UActorComponent(); } + /** * @brief Function called every frame on this ActorComponent. Override this function to implement custom logic to be executed every frame. * Only executes if the component is registered, and also PrimaryComponentTick.bCanEverTick must be set to true. diff --git a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h index a4eec443..46e5fea2 100644 --- a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h @@ -22,6 +22,8 @@ namespace Karma class KARMA_API UMeshComponent : public UPrimitiveComponent { DECLARE_KARMA_CLASS(UPrimitiveComponent, UMeshComponent) - + + public: + virtual void ShivaUObject() { this->~UMeshComponent(); } }; } \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h index 29e0b018..1c5f0bd0 100644 --- a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h @@ -51,6 +51,8 @@ namespace Karma //friend class FPrimitiveSceneInfo; public: + virtual void ShivaUObject() { this->~UPrimitiveComponent(); } + /** * @brief Utility to cache the max draw distance based on cull distance volumes or the desired max draw distance * diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h index 8cc1ed39..54b62bbe 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h @@ -50,6 +50,8 @@ namespace Karma */ USceneComponent(); + virtual void ShivaUObject() { this->~USceneComponent(); } + /** Returns the transform of the component relative to its parent */ //FTransform GetRelativeTransform() const; diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp index c0de21f5..749e392a 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp @@ -8,6 +8,6 @@ namespace Karma } UStaticMeshComponent::~UStaticMeshComponent() { - // Destructor implementation (if any) + KR_CORE_INFO("Destroy called on UStaticMeshComponent"); } } // namespace Karma \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 31de591c..698bd8c1 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -46,6 +46,9 @@ namespace Karma * @brief Destructor for StaticMeshComponent */ virtual ~UStaticMeshComponent(); + + virtual void ShivaUObject() { this->~UStaticMeshComponent(); } + // Additional methods and members specific to StaticMeshComponent can be added here. /** diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 71189a31..5641ef0d 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -14,10 +14,10 @@ namespace Karma loadedMesh.reset(new Mesh(filePath)); // We create uniform buffer object for model's transformation matrix - m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization + //m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization - UBODataPointer uModelMatrix(&GetTransform()); - m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform + //UBODataPointer uModelMatrix(&GetTransform()); + //m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform if (!m_StaticMeshComponent) { diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h index c7d30f40..286facff 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h @@ -144,6 +144,9 @@ namespace Karma uint8_t m_bAutoDestroyWhenFinished : 1; public: + + virtual void ShivaUObject() { this->~AActor(); } + /** * Return the ULevel that this Actor is part of. * diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index d670e6e9..e6e3262d 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -8,12 +8,12 @@ namespace Karma { Camera::Camera(const glm::vec3& initialCameraPosition) : m_Position(initialCameraPosition), m_LastMouseX(0.0f), m_LastMouseY(0.0f) { - m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); + //m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); - UBODataPointer uProjection(&m_ProjectionMatrix); - UBODataPointer uView(&m_ViewMatrix); + //UBODataPointer uProjection(&m_ProjectionMatrix); + //UBODataPointer uView(&m_ViewMatrix); - m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); + //m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); } Camera::~Camera() diff --git a/Karma/src/Karma/Renderer/Mesh.cpp b/Karma/src/Karma/Renderer/Mesh.cpp index 80959c20..9edebddb 100644 --- a/Karma/src/Karma/Renderer/Mesh.cpp +++ b/Karma/src/Karma/Renderer/Mesh.cpp @@ -63,16 +63,16 @@ namespace Karma ProcessNode(scene->mRootNode, scene); // Set default static material - m_StaticMaterial.reset(new Material()); + //m_StaticMaterial.reset(new Material()); // TODO: Need to abastract this shader addition process. ATM tied to vulkan only // m_StaticMaterial->AddShader(std::static_pointer_cast(VulkanHolder::GetVulkanContext()->GetGeneralShader())); // We add default texture to the material - std::shared_ptr defaultTexture; - defaultTexture.reset(new Texture(TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); + //std::shared_ptr defaultTexture; + //defaultTexture.reset(new Texture(TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); - m_StaticMaterial->AddTexture(defaultTexture); + //m_StaticMaterial->AddTexture(defaultTexture); } void Mesh::ProcessNode(aiNode* nodeToProcess, const aiScene* theScene) diff --git a/Karma/src/Karma/Renderer/Scene.cpp b/Karma/src/Karma/Renderer/Scene.cpp index 3bdd40d2..d4ac075e 100644 --- a/Karma/src/Karma/Renderer/Scene.cpp +++ b/Karma/src/Karma/Renderer/Scene.cpp @@ -29,7 +29,7 @@ namespace Karma m_VertexArrays.push_back(vertexArray); } - void Scene::AddStaticMeshActor(std::shared_ptr smActor) + void Scene::AddStaticMeshActor(AStaticMeshActor* smActor) { m_SMActors.push_back(smActor); } diff --git a/Karma/src/Karma/Renderer/Scene.h b/Karma/src/Karma/Renderer/Scene.h index 6c28e0b0..ded0828a 100644 --- a/Karma/src/Karma/Renderer/Scene.h +++ b/Karma/src/Karma/Renderer/Scene.h @@ -51,7 +51,7 @@ namespace Karma * @param smActor The StaticMeshActor to be added * @since Karma 1.0.0 */ - void AddStaticMeshActor(std::shared_ptr smActor); + void AddStaticMeshActor(AStaticMeshActor* smActor); /** * @brief Add Camera @@ -105,7 +105,7 @@ namespace Karma * * @since Karma 1.0.0 */ - const std::vector>& GetSMActors() const { return m_SMActors; } + const std::vector& GetSMActors() const { return m_SMActors; } /** * @brief Getter for the Camera being used for the scene @@ -152,7 +152,7 @@ namespace Karma private: std::vector> m_VertexArrays; - std::vector> m_SMActors; + std::vector m_SMActors; std::vector> m_Cameras; glm::vec4 m_ClearColor; diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index e341489c..ee5fb36a 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -254,8 +254,8 @@ namespace Karma m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); BufferCreation(); - std::shared_ptr uboPtr(this); - VulkanHolder::GetVulkanContext()->RegisterUBO(uboPtr); + //std::shared_ptr uboPtr(this); + VulkanHolder::GetVulkanContext()->RegisterUBO(this); } VulkanUniformBuffer::~VulkanUniformBuffer() diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 38561906..f9e98b26 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -69,7 +69,9 @@ namespace Karma vkDestroyDescriptorPool(m_device, m_GeneralDescriptorPool, nullptr); m_vulkanRendererAPI->ClearVulkanRendererAPI(); - //ClearUBO(); + + // Tentative for the moment + // ClearUBO(); for (auto framebuffer : m_swapChainFrameBuffers) { @@ -105,7 +107,7 @@ namespace Karma vkDestroyPipeline(m_device, m_KarmaGuiGeneralGraphicsPipeline, nullptr); } - void VulkanContext::RegisterUBO(std::shared_ptr ubo) + void VulkanContext::RegisterUBO(VulkanUniformBuffer* ubo) { m_VulkanUBO.insert(ubo); } diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index 83cc1c49..f90d6147 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -518,7 +518,7 @@ namespace Karma * @see VulkanContext::m_VulkanUBO * @since Karma 1.0.0 */ - void RegisterUBO(std::shared_ptr ubo); + void RegisterUBO(VulkanUniformBuffer* ubo); /** * @brief Clears all registered VulkanUniformBuffers, freeing their resources. @@ -712,7 +712,7 @@ namespace Karma std::vector m_swapChainFrameBuffers; VkCommandPool m_commandPool; - std::set> m_VulkanUBO; + std::set m_VulkanUBO; // General vulkan resources std::shared_ptr m_GeneralShader; diff --git a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp index c49fa777..24bc8c80 100644 --- a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp +++ b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp @@ -13,7 +13,7 @@ namespace Karma VulkanVertexArray::~VulkanVertexArray() { vkDeviceWaitIdle(m_device); - CleanupPipeline(); + //CleanupPipeline(); } void VulkanVertexArray::Bind() const diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index b719bfac..787287e5 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -373,7 +373,7 @@ namespace Karma KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); - m_EditorScene->AddStaticMeshActor(std::shared_ptr(staticMeshActor)); + //m_EditorScene->AddStaticMeshActor(staticMeshActor); } } } From da793b3550d11a840f0976378dd1e9691dc892aa Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 13 Dec 2025 12:48:39 +0530 Subject: [PATCH 021/107] Wrote ShivaUObject routine for UObject desctruction and according changes --- Karma/src/Karma/Core/GFrameworkMacros.h | 7 +++++++ Karma/src/Karma/Core/Object.h | 2 -- Karma/src/Karma/Engine/Classes/Components/ActorComponent.h | 2 -- Karma/src/Karma/Engine/Classes/Components/MeshComponent.h | 5 +---- .../Karma/Engine/Classes/Components/PrimitiveComponent.h | 2 -- Karma/src/Karma/Engine/Classes/Components/SceneComponent.h | 2 -- .../Karma/Engine/Classes/Components/StaticMeshComponent.h | 2 -- Karma/src/Karma/Engine/Classes/GameFramework/Actor.h | 2 -- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/Karma/src/Karma/Core/GFrameworkMacros.h b/Karma/src/Karma/Core/GFrameworkMacros.h index 5d972ce8..4f73b6d3 100644 --- a/Karma/src/Karma/Core/GFrameworkMacros.h +++ b/Karma/src/Karma/Core/GFrameworkMacros.h @@ -35,12 +35,19 @@ typedef void (*ClassConstructorType) (const Karma::FObjectInitializer&); * calls default constructor with placement new during UObjectAllocation * (*InClass->m_ClassConstructor)(FObjectInitializer) * + * Also defines the routine to destroy (call class destructor) accessible from + * UObjectBase. See KarmaSmriti::ShutDown() for application. + * * @see StaticConstructObject_Internal() in UObjectGlobals.cpp * @remark In UE, this is done in ObjectMacros.h, #define DECLARE_CLASS * */ #define DECLARE_KARMA_CLASS(TClass, TSuperClass) \ public: \ + inline virtual void ShivaUObject()\ + {\ + this->~##TClass();\ + } \ DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \ /** Typedef for the base class ({{ typedef-type }}) */ \ typedef TSuperClass Super;\ diff --git a/Karma/src/Karma/Core/Object.h b/Karma/src/Karma/Core/Object.h index 74121c32..cd0b84c7 100644 --- a/Karma/src/Karma/Core/Object.h +++ b/Karma/src/Karma/Core/Object.h @@ -129,8 +129,6 @@ namespace Karma */ UObject(); - virtual void ShivaUObject() { this->~UObject(); } - /** * A legacy function from UE */ diff --git a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h index 53731d52..e60de3d3 100644 --- a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h @@ -75,8 +75,6 @@ namespace Karma */ UActorComponent(); - virtual void ShivaUObject() { this->~UActorComponent(); } - /** * @brief Function called every frame on this ActorComponent. Override this function to implement custom logic to be executed every frame. * Only executes if the component is registered, and also PrimaryComponentTick.bCanEverTick must be set to true. diff --git a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h index 46e5fea2..04f457e1 100644 --- a/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/MeshComponent.h @@ -21,9 +21,6 @@ namespace Karma */ class KARMA_API UMeshComponent : public UPrimitiveComponent { - DECLARE_KARMA_CLASS(UPrimitiveComponent, UMeshComponent) - - public: - virtual void ShivaUObject() { this->~UMeshComponent(); } + DECLARE_KARMA_CLASS(UMeshComponent, UPrimitiveComponent) }; } \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h index 1c5f0bd0..29e0b018 100644 --- a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h @@ -51,8 +51,6 @@ namespace Karma //friend class FPrimitiveSceneInfo; public: - virtual void ShivaUObject() { this->~UPrimitiveComponent(); } - /** * @brief Utility to cache the max draw distance based on cull distance volumes or the desired max draw distance * diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h index 54b62bbe..8cc1ed39 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h @@ -50,8 +50,6 @@ namespace Karma */ USceneComponent(); - virtual void ShivaUObject() { this->~USceneComponent(); } - /** Returns the transform of the component relative to its parent */ //FTransform GetRelativeTransform() const; diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h index 698bd8c1..4b92abc4 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.h @@ -47,8 +47,6 @@ namespace Karma */ virtual ~UStaticMeshComponent(); - virtual void ShivaUObject() { this->~UStaticMeshComponent(); } - // Additional methods and members specific to StaticMeshComponent can be added here. /** diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h index 286facff..8c8563c8 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h @@ -145,8 +145,6 @@ namespace Karma public: - virtual void ShivaUObject() { this->~AActor(); } - /** * Return the ULevel that this Actor is part of. * From 1c908af4d86c0af6aa36aae267bc6fb32ffcd486 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 13 Dec 2025 13:45:49 +0530 Subject: [PATCH 022/107] Vulkan Unreal - Phase IV --- Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp | 1 - Karma/src/Karma/Core/UObjectBase.h | 10 ++++++++-- .../Engine/Classes/Components/StaticMeshComponent.cpp | 1 - .../Karma/Engine/Classes/Engine/StaticMeshActor.cpp | 6 +++--- Karma/src/Karma/Renderer/Camera/Camera.cpp | 8 ++++---- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp index 3525dd71..4f5e7fef 100644 --- a/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp +++ b/Karma/src/Karma/Core/TrueCore/KarmaSmriti.cpp @@ -35,7 +35,6 @@ namespace Karma { if ((*ObjectItr)->m_Object) { - KR_INFO("Attempting to delete UObject: {0} ", (*ObjectItr)->m_Object->GetName()); (*ObjectItr)->m_Object->ShivaUObject(); } else diff --git a/Karma/src/Karma/Core/UObjectBase.h b/Karma/src/Karma/Core/UObjectBase.h index 5ddba2d2..d2b2227f 100644 --- a/Karma/src/Karma/Core/UObjectBase.h +++ b/Karma/src/Karma/Core/UObjectBase.h @@ -62,7 +62,7 @@ namespace Karma public: /** - * Constructor used by StaticAllocateObject + * @brief Constructor used by StaticAllocateObject * @param InClass non NULL, this gives the class of the new object, if known at this time * @param InFlags RF_Flags to assign * @param InOuter outer for this object (UObject this object resides in, for instance LevelToSpawnIn is outer for AActor) @@ -75,10 +75,16 @@ namespace Karma ~UObjectBase() {}; + /** + * @brief One function to destroy them + * + * @see KarmaSmriti::ShutDown() + * @since Karma 1.0.0 + */ virtual void ShivaUObject() { this->~UObjectBase(); } /** - * Walks up the list of outers until it finds a package directly associated with the object. + * @brief Walks up the list of outers until it finds a package directly associated with the object. * * @return the package the object is in * @since Karma 1.0.0 diff --git a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp index 749e392a..625e2d94 100644 --- a/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/StaticMeshComponent.cpp @@ -8,6 +8,5 @@ namespace Karma } UStaticMeshComponent::~UStaticMeshComponent() { - KR_CORE_INFO("Destroy called on UStaticMeshComponent"); } } // namespace Karma \ No newline at end of file diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 5641ef0d..71189a31 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -14,10 +14,10 @@ namespace Karma loadedMesh.reset(new Mesh(filePath)); // We create uniform buffer object for model's transformation matrix - //m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization + m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization - //UBODataPointer uModelMatrix(&GetTransform()); - //m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform + UBODataPointer uModelMatrix(&GetTransform()); + m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform if (!m_StaticMeshComponent) { diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index e6e3262d..d670e6e9 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -8,12 +8,12 @@ namespace Karma { Camera::Camera(const glm::vec3& initialCameraPosition) : m_Position(initialCameraPosition), m_LastMouseX(0.0f), m_LastMouseY(0.0f) { - //m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); + m_ViewProjectionUBO.reset(UniformBufferObject::Create({ ShaderDataType::Mat4, ShaderDataType::Mat4 }, 0)); - //UBODataPointer uProjection(&m_ProjectionMatrix); - //UBODataPointer uView(&m_ViewMatrix); + UBODataPointer uProjection(&m_ProjectionMatrix); + UBODataPointer uView(&m_ViewMatrix); - //m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); + m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); } Camera::~Camera() From 50791cb682fc8de72be8bd43f40b8c213017c701 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 14 Dec 2025 08:37:48 +0530 Subject: [PATCH 023/107] Vulkan Unreal - Phase V --- Application/src/KarmaApp.cpp | 2 +- Karma/src/Karma/Core/GFrameworkMacros.h | 6 +- .../Engine/Classes/Engine/StaticMeshActor.h | 17 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 12 +- Karma/src/Karma/Renderer/Camera/Camera.h | 2 +- Karma/src/Karma/Renderer/Renderer.cpp | 2 +- Karma/src/Karma/Renderer/Scene.cpp | 11 - Karma/src/Karma/Renderer/Scene.h | 17 -- .../Platform/OpenGL/KarmaGuiOpenGLHandler.cpp | 4 +- Karma/src/Platform/Vulkan/VulkanContext.cpp | 253 +++++++++++------- Karma/src/Platform/Vulkan/VulkanContext.h | 30 ++- Pranjal/Source/Private/EditorLayer.cpp | 7 +- 12 files changed, 210 insertions(+), 153 deletions(-) diff --git a/Application/src/KarmaApp.cpp b/Application/src/KarmaApp.cpp index 738a5357..76bec48b 100644 --- a/Application/src/KarmaApp.cpp +++ b/Application/src/KarmaApp.cpp @@ -31,7 +31,7 @@ class ExampleLayer : public Karma::Layer m_Scene.reset(new Karma::Scene()); m_Scene->AddCamera(m_Camera); - m_Scene->AddVertexArray(m_SquareVA); + //m_Scene->AddVertexArray(m_SquareVA); m_Scene->SetClearColor({ 0.0f, 0.0f, 0.0f, 1 }); diff --git a/Karma/src/Karma/Core/GFrameworkMacros.h b/Karma/src/Karma/Core/GFrameworkMacros.h index 4f73b6d3..d5020b9e 100644 --- a/Karma/src/Karma/Core/GFrameworkMacros.h +++ b/Karma/src/Karma/Core/GFrameworkMacros.h @@ -36,7 +36,7 @@ typedef void (*ClassConstructorType) (const Karma::FObjectInitializer&); * (*InClass->m_ClassConstructor)(FObjectInitializer) * * Also defines the routine to destroy (call class destructor) accessible from - * UObjectBase. See KarmaSmriti::ShutDown() for application. + * UObjectBase. See KarmaSmriti::ShutDown() for the application. * * @see StaticConstructObject_Internal() in UObjectGlobals.cpp * @remark In UE, this is done in ObjectMacros.h, #define DECLARE_CLASS @@ -44,9 +44,9 @@ typedef void (*ClassConstructorType) (const Karma::FObjectInitializer&); */ #define DECLARE_KARMA_CLASS(TClass, TSuperClass) \ public: \ - inline virtual void ShivaUObject()\ + inline virtual void ShivaUObject() override\ {\ - this->~##TClass();\ + this->~TClass();\ } \ DEFINE_DEFAULT_CONSTRUCTOR_CALL(TClass) \ /** Typedef for the base class ({{ typedef-type }}) */ \ diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index b372d636..bb22c542 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -14,6 +14,7 @@ namespace Karma { + class UniformBufferObject; /** * @brief An actor that contains a static mesh component, allowing the mesh to be rendered in the game world. @@ -33,7 +34,12 @@ namespace Karma //std::shared_ptr m_StaticMeshComponent; UStaticMeshComponent* m_StaticMeshComponent; - std::shared_ptr m_MeshTransformUniform; + /** + * @brief Uniform buffer object for the mesh's transformation matrix + * + * Used to upload the actor's transform to the GPU for rendering + */ + std::shared_ptr m_MeshTransformUniform; public: /** @@ -60,5 +66,14 @@ namespace Karma * @since Karma 1.0.0 */ UStaticMeshComponent* GetStaticMeshComponent() const { return m_StaticMeshComponent; } + + /** + * @brief Getter for the mesh transform uniform buffer object + * + * @return std::shared_ptr The uniform buffer object for the mesh's transformation matrix. + * + * @since Karma 1.0.0 + */ + std::shared_ptr GetMeshTransformUniform() const { return m_MeshTransformUniform; } }; } diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 91920f29..2558b6f0 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -503,8 +503,6 @@ namespace Karma // The pass starts here and all commands until vkCmdEndRenderPass are recorded into it vkCmdBeginRenderPass(frameOnFlightData->CommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - - // UpdateGeneralDescriptorSets // ---- Bind Graphics Pipeline ---- vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralGraphicsPipeline()); @@ -513,9 +511,6 @@ namespace Karma // Set 0: Camera UBO vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 0, 1, &dSets.viewSet, 0, nullptr); - // Set 1: Texture (shared by all meshes) - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 1, 1, &dSets.textureSet, 0, nullptr); - uint32_t objectIndex = 0; // ---- Bind 3D Vertex And Index Buffers ---- @@ -528,8 +523,11 @@ namespace Karma vkCmdBindVertexBuffers(frameOnFlightData->CommandBuffer, 0, 1, vertexBuffers, vertexOffset); vkCmdBindIndexBuffer(frameOnFlightData->CommandBuffer, std::static_pointer_cast(mesh->GetIndexBuffer())->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); - uint32_t dynamicOffsets[] = { objectIndex * 256 }; - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectSet, 1, dynamicOffsets); + // Set 1: Texture + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 1, 1, &dSets.textureSet[objectIndex], 0, nullptr); + + // Set 2: Object UBO + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectsSet[objectIndex], 1, nullptr); // ----Issue Draw Commands---- // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) diff --git a/Karma/src/Karma/Renderer/Camera/Camera.h b/Karma/src/Karma/Renderer/Camera/Camera.h index 5b407b17..1af38d37 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.h +++ b/Karma/src/Karma/Renderer/Camera/Camera.h @@ -181,7 +181,7 @@ namespace Karma * * @since Karma 1.0.0 */ - const std::shared_ptr& GetViewProjectionUBO() const { return m_ViewProjectionUBO; } + std::shared_ptr& GetViewProjectionUBO() { return m_ViewProjectionUBO; } private: void RecalculateViewMatrix(); diff --git a/Karma/src/Karma/Renderer/Renderer.cpp b/Karma/src/Karma/Renderer/Renderer.cpp index f4265004..11310b52 100644 --- a/Karma/src/Karma/Renderer/Renderer.cpp +++ b/Karma/src/Karma/Renderer/Renderer.cpp @@ -16,7 +16,7 @@ namespace Karma void Renderer::Submit(std::shared_ptr scene) { - RenderCommand::DrawIndexed(scene->GetRenderableVertexArray()); + //RenderCommand::DrawIndexed(scene->GetRenderableVertexArray()); } void Renderer::DeleteData() diff --git a/Karma/src/Karma/Renderer/Scene.cpp b/Karma/src/Karma/Renderer/Scene.cpp index d4ac075e..4ebded8c 100644 --- a/Karma/src/Karma/Renderer/Scene.cpp +++ b/Karma/src/Karma/Renderer/Scene.cpp @@ -12,23 +12,12 @@ namespace Karma { } - std::shared_ptr Scene::GetRenderableVertexArray() const - { - // Get the first for now - return m_VertexArrays.at(0); - } - std::shared_ptr Scene::GetSceneCamera() const { // Get the first for now return m_Cameras.at(0); } - void Scene::AddVertexArray(std::shared_ptr vertexArray) - { - m_VertexArrays.push_back(vertexArray); - } - void Scene::AddStaticMeshActor(AStaticMeshActor* smActor) { m_SMActors.push_back(smActor); diff --git a/Karma/src/Karma/Renderer/Scene.h b/Karma/src/Karma/Renderer/Scene.h index ded0828a..0c20e208 100644 --- a/Karma/src/Karma/Renderer/Scene.h +++ b/Karma/src/Karma/Renderer/Scene.h @@ -92,14 +92,6 @@ namespace Karma void SetWindowToRenderWithinResize(bool bStatus) { m_WindowResize = bStatus; } // Getters - /** - * @brief Getter for the VertexArray - * - * @note to be deprecated - * @since Karma 1.0.0 - */ - std::shared_ptr GetRenderableVertexArray() const; - /** * @brief Get the list of StaticMeshActors in the scene * @@ -121,13 +113,6 @@ namespace Karma */ inline const glm::vec4& GetClearColor() const { return m_ClearColor; } - /** - * @brief Get the list of VertexArrays - * - * @since Karma 1.0.0 - */ - inline const std::vector>& GetAllVertexArrays() const { return m_VertexArrays; } - /** * @brief Get the list of all Cameras * @@ -150,8 +135,6 @@ namespace Karma inline bool GetWindowToRenderWithinResizeStatus() const { return m_WindowResize; } private: - std::vector> m_VertexArrays; - std::vector m_SMActors; std::vector> m_Cameras; diff --git a/Karma/src/Platform/OpenGL/KarmaGuiOpenGLHandler.cpp b/Karma/src/Platform/OpenGL/KarmaGuiOpenGLHandler.cpp index 6e8ce1c5..13bf48a4 100644 --- a/Karma/src/Platform/OpenGL/KarmaGuiOpenGLHandler.cpp +++ b/Karma/src/Platform/OpenGL/KarmaGuiOpenGLHandler.cpp @@ -349,14 +349,14 @@ namespace Karma sceneToDraw = static_cast(pcmd->UserCallbackData); if (sceneToDraw) { - std::shared_ptr openGLVA = static_pointer_cast(sceneToDraw->GetRenderableVertexArray()); + /*std::shared_ptr openGLVA = static_pointer_cast(sceneToDraw->GetRenderableVertexArray()); openGLVA->UpdateProcessAndSetReadyForSubmission(); openGLVA->Bind(); // A very experimental hack OpenGLImageBuffer::BindTexture(); - glDrawElements(GL_TRIANGLES, openGLVA->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_TRIANGLES, openGLVA->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);*/ } KarmaGuiOpenGLHandler::KarmaGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); } diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index f9e98b26..10ddf5bc 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -7,6 +7,8 @@ #include "Platform/Vulkan/VulkanVertexArray.h" #include "Platform/Vulkan/VulkanBuffer.h" #include "Platform/Vulkan/VulkanTexture.h" +#include "Scene.h" +#include "Engine/StaticMeshActor.h" namespace Karma { @@ -156,11 +158,41 @@ namespace Karma // For glslang Initializeglslang(); + } + + void VulkanContext::CreateVulkanResourcesForScene(std::shared_ptr scene3D) + { + uint32_t smElementsNumber = 0; + + for (const auto element : scene3D->GetSMActors()) + { + smElementsNumber++; + } + + RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); + VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); + + if (!vulkanRendererAPI) + { + KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); + return; + } + + uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); + CreateGeneralShader(); + CreateGeneralTexture(); + + CreateGeneralDescriptorPool(smElementsNumber); CreateGeneralDescriptorSetLayouts(); - CreateGeneralDescriptorPool(); - CreateGeneralDescriptorSets(); + + CreateGeneralDescriptorSets(scene3D, smElementsNumber, maxFramesInFlight); + + for (uint32_t frameIndex = 0; frameIndex < maxFramesInFlight; frameIndex++) + { + UpdateGeneralDescriptorSets(scene3D, frameIndex); + } } void VulkanContext::CreateGeneralShader() @@ -170,6 +202,11 @@ namespace Karma m_GeneralShader.reset(new VulkanShader("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag")); } + void VulkanContext::CreateGeneralTexture() + { + m_GeneralTexture.reset(new Texture(Karma::TextureType::Image, "../Resources/Textures/UnrealGrid.png", "VikingTex", "texSampler")); + } + VkShaderModule VulkanContext::CreateShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo{}; @@ -372,6 +409,39 @@ namespace Karma vkDestroyShaderModule(m_device, vertShaderModule, nullptr); } + void VulkanContext::CreateGeneralDescriptorPool(uint32_t smElementsNumber) + { + RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); + VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); + + if (!vulkanRendererAPI) + { + KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); + return; + } + + uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); + + std::array poolSizes{}; + + // Uniform Buffers : Camera UBO + per object UBOs + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = (1 + smElementsNumber) * maxFramesInFlight; // 1 for camera UBO + 1 for object UBO + + // Combined Image Samplers : Texture + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = smElementsNumber * maxFramesInFlight; // 1 for each object texture + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = static_cast(poolSizes.size()); + poolInfo.pPoolSizes = poolSizes.data(); + poolInfo.maxSets = 3 * maxFramesInFlight; // 3 sets per frame + + VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_GeneralDescriptorPool); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create general descriptor pool!"); + } + void VulkanContext::CreateGeneralDescriptorSetLayouts() { // ===== Set 0: Camera UBO ===== @@ -409,7 +479,7 @@ namespace Karma materialLayoutInfo.bindingCount = 2; // Only 2 bindings now materialLayoutInfo.pBindings = materialBindings.data(); vkCreateDescriptorSetLayout(device, &materialLayoutInfo, nullptr, &materialLayout); - + GLSLANG USAGE EXAMPLE: // Set 1: Material UBO + 1 Texture layout(set = 1, binding = 0) uniform MaterialUBO { @@ -437,10 +507,10 @@ namespace Karma result = vkCreateDescriptorSetLayout(m_device, &texInfo, nullptr, &m_TextureLayout); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create texture descriptor set layout!"); - // ===== Set 2: Per-mesh transform UBO (dynamic so you can pack many) ===== + // ===== Set 2: Per-mesh transform UBO ===== VkDescriptorSetLayoutBinding objectBinding{}; objectBinding.binding = 0; - objectBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + objectBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; objectBinding.descriptorCount = 1; objectBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; objectBinding.pImmutableSamplers = nullptr; @@ -454,84 +524,57 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create object descriptor set layout!"); } - void VulkanContext::CreateGeneralDescriptorPool() - { - RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); - - if (!vulkanRendererAPI) - { - KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); - return; - } - - uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); - - std::array poolSizes{}; - - // Uniform Buffers : Camera UBO + Object UBO - poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSizes[0].descriptorCount = 2 * maxFramesInFlight; // 1 for camera UBO + 1 for object UBO - - // Combined Image Samplers : Texture - poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - poolSizes[1].descriptorCount = 1 * maxFramesInFlight; // 1 for texture - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = static_cast(poolSizes.size()); - poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = 3 * maxFramesInFlight; // 3 sets per frame - - VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_GeneralDescriptorPool); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create general descriptor pool!"); - } - - void VulkanContext::CreateGeneralDescriptorSets() - { - RendererAPI* rendererAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanRendererAPI = static_cast(rendererAPI); + void VulkanContext::CreateGeneralDescriptorSets(std::shared_ptr scene3D, uint32_t smElementsNumber, uint32_t maxFramesInFlight) + { + m_GeneralDescriptorSets.resize(maxFramesInFlight); - if (!vulkanRendererAPI) + for (uint32_t frameIndex = 0; frameIndex < maxFramesInFlight; frameIndex++) { - KR_CORE_ASSERT(false, "VulkanRendererAPI is null!"); - return; - } + std::array frameSets; - uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); + std::array frameLayouts = { + m_ViewLayout, // Set 0 + m_TextureLayout, // Set 1 + m_ObjectLayout // Set 2 + }; - std::vector> m_GeneralDescriptorSets(maxFramesInFlight); - - //m_GeneralDescriptorSets.resize(maxFramesInFlight); - - for (size_t i = 0; i < maxFramesInFlight; i++) - { - std::array setLayouts = { - m_ViewLayout, // set = 0 - m_TextureLayout, // set = 1 - m_ObjectLayout // set = 2 - }; VkDescriptorSetAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = m_GeneralDescriptorPool; - allocInfo.descriptorSetCount = static_cast(setLayouts.size()); - allocInfo.pSetLayouts = setLayouts.data(); + allocInfo.descriptorSetCount = 3; + allocInfo.pSetLayouts = frameLayouts.data(); + + vkAllocateDescriptorSets(m_device, &allocInfo, frameSets.data()); + + // Copy to global struct + m_GeneralDescriptorSets[frameIndex].viewSet = frameSets[0]; + //m_GeneralDescriptorSets[frameIndex].textureSet = frameSets[1]; + //m_GeneralDescriptorSets[frameIndex].objectSet = frameSets[2]; - VkResult result = vkAllocateDescriptorSets(m_device, &allocInfo, m_GeneralDescriptorSets[i].data()); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to allocate general descriptor sets!"); + m_GeneralDescriptorSets[frameIndex].textureSet.resize(smElementsNumber); + m_GeneralDescriptorSets[frameIndex].objectsSet.resize(smElementsNumber); + + uint32_t meshIndex = 0; + for (const auto element : scene3D->GetSMActors()) + { + m_GeneralDescriptorSets[frameIndex].textureSet[/*element->GetSMID()*/meshIndex] = frameSets[1]; + m_GeneralDescriptorSets[frameIndex].objectsSet[/*element->GetSMID()*/meshIndex] = frameSets[2]; + } } } - void VulkanContext::UpdateGeneralDescriptorSets(uint32_t frameIndex, VkBuffer viewBuffer, uint32_t viewBufferSize, std::shared_ptr texture, VkBuffer objectBuffer) + void VulkanContext::UpdateGeneralDescriptorSets(std::shared_ptr scene3D, uint32_t frameIndex) { FrameDescriptorSets& frameDescriptorSets = m_GeneralDescriptorSets[frameIndex]; // Update Set 0: Camera UBO { + std::shared_ptr vUBO = static_pointer_cast(scene3D->GetSceneCamera()->GetViewProjectionUBO()); + VkDescriptorBufferInfo viewInfo{}; - viewInfo.buffer = viewBuffer; - viewInfo.offset = frameIndex * viewBufferSize;// Try 2 * sizeof(glm::mat4) also - viewInfo.range = viewBufferSize; + viewInfo.buffer = vUBO->GetUniformBuffers()[frameIndex]; + viewInfo.offset = 0; + viewInfo.range = vUBO->GetBufferSize(); VkWriteDescriptorSet viewWrite{}; viewWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -544,44 +587,52 @@ namespace Karma vkUpdateDescriptorSets(m_device, 1, &viewWrite, 0, nullptr); } - // Update set 1: Texture - { - VkDescriptorImageInfo texInfo{}; - texInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - texInfo.sampler = texture->GetImageSampler(); - texInfo.imageView = texture->GetImageView(); - - VkWriteDescriptorSet texWrite{}; - texWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - texWrite.dstSet = frameDescriptorSets.textureSet; - texWrite.dstBinding = 0; - texWrite.dstArrayElement = 0; - texWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - texWrite.pImageInfo = &texInfo; - texWrite.descriptorCount = 1; - - vkUpdateDescriptorSets(m_device, 1, &texWrite, 0, nullptr); - } - - // Update set 2: Per-mesh UBO will be updated during mesh rendering + uint32_t smIndex = 0; + for (const auto smElement : scene3D->GetSMActors()) { - uint32_t maxObjects = 100; - - VkDescriptorBufferInfo objectInfo{}; - objectInfo.buffer = objectBuffer; - objectInfo.offset = frameIndex * maxObjects * sizeof(glm::mat4); // Assuming each object's transform is a mat4 - objectInfo.range = maxObjects * sizeof(glm::mat4); + // Update set 1: Texture + { + std::shared_ptr vTexture = m_GeneralTexture->GetVulkanTexture(); + + VkDescriptorImageInfo texInfo{}; + texInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + texInfo.sampler = vTexture->GetImageSampler(); + texInfo.imageView = vTexture->GetImageView(); + + VkWriteDescriptorSet texWrite{}; + texWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texWrite.dstSet = frameDescriptorSets.textureSet[smIndex]; + texWrite.dstBinding = 0; + texWrite.dstArrayElement = 0; + texWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texWrite.pImageInfo = &texInfo; + texWrite.descriptorCount = 1; + + vkUpdateDescriptorSets(m_device, 1, &texWrite, 0, nullptr); + } - VkWriteDescriptorSet objectWrite{}; - objectWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - objectWrite.dstSet = frameDescriptorSets.objectSet; - objectWrite.dstBinding = 0; - objectWrite.dstArrayElement = 0; - objectWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - objectWrite.pBufferInfo = &objectInfo; - objectWrite.descriptorCount = 1; + // Update set 2: Per-mesh UBO will be updated during mesh rendering + { + std::shared_ptr objectUBO = static_pointer_cast(smElement->GetMeshTransformUniform()); + + VkDescriptorBufferInfo objectInfo{}; + objectInfo.buffer = objectUBO->GetUniformBuffers()[frameIndex]; + objectInfo.offset = 0; + objectInfo.range = objectUBO->GetBufferSize(); + + VkWriteDescriptorSet objectWrite{}; + objectWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + objectWrite.dstSet = frameDescriptorSets.objectsSet[smIndex]; + objectWrite.dstBinding = 0; + objectWrite.dstArrayElement = 0; + objectWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + objectWrite.pBufferInfo = &objectInfo; + objectWrite.descriptorCount = 1; + + vkUpdateDescriptorSets(m_device, 1, &objectWrite, 0, nullptr); + } - vkUpdateDescriptorSets(m_device, 1, &objectWrite, 0, nullptr); + smIndex++; } } diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index f90d6147..e85aa4cf 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -21,6 +21,10 @@ namespace Karma { class VulkanShader; + class Texture; + + class Scene; + /** * @brief Forward declaration */ @@ -34,8 +38,8 @@ namespace Karma struct FrameDescriptorSets { VkDescriptorSet viewSet; // Set 0: Camera UBO - VkDescriptorSet textureSet; // Set 1: Texture - VkDescriptorSet objectSet; // Set 2: Per-mesh UBO (dynamic) + std::vector textureSet; // Set 1: Per-mesh Texture + std::vector objectsSet; // Set 2: Per-mesh UBO }; /** @@ -557,8 +561,22 @@ namespace Karma */ VkShaderModule CreateShaderModule(const std::vector& code); + void CreateVulkanResourcesForScene(std::shared_ptr scene3D ); + + /** + * @brief Create a default vulkan shader + * + * @since Karma 1.0.0 + */ void CreateGeneralShader(); + /** + * @brief Creates a general texture used for default texturing in the KarmaGui exhibitor window. + * + * @since Karma 1.0.0 + */ + void CreateGeneralTexture(); + /** * @brief Creates general descriptor set layouts for common resources like camera UBO, texture sampler, and object UBO. * @@ -599,7 +617,7 @@ namespace Karma * * @since Karma 1.0.0 */ - void CreateGeneralDescriptorPool(); + void CreateGeneralDescriptorPool(uint32_t smElementsNumber); /** * @brief Allocates and writes to general descriptor sets for common resources like camera UBO, texture sampler, and object UBO. @@ -608,7 +626,7 @@ namespace Karma * * @since Karma 1.0.0 */ - void CreateGeneralDescriptorSets(); + void CreateGeneralDescriptorSets(std::shared_ptr scene3D, uint32_t smElementsNumber, uint32_t maxFramesInFlight); /** * @brief Updates the general descriptor sets with the provided view buffer for the specified frame index. @@ -620,7 +638,7 @@ namespace Karma * * @since Karma 1.0.0 */ - void UpdateGeneralDescriptorSets(uint32_t frameIndex, VkBuffer viewBuffer, uint32_t viewBufferSize, std::shared_ptr texture, VkBuffer objectBuffer); + void UpdateGeneralDescriptorSets(std::shared_ptr scene3D, uint32_t frameIndex); /** * @brief Uploads data to the registered VulkanUniformBuffers for the specified frame index. @@ -716,7 +734,7 @@ namespace Karma // General vulkan resources std::shared_ptr m_GeneralShader; - + std::shared_ptr m_GeneralTexture; VkPipelineLayout m_KarmaGuiGeneralPipelineLayout; VkPipeline m_KarmaGuiGeneralGraphicsPipeline; VkDescriptorSetLayout m_ViewLayout; // Camera UBO diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 787287e5..8d4bb9b1 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -11,6 +11,7 @@ #include "Engine.h" #include "GameInstance.h" #include "Engine/StaticMeshActor.h" +#include namespace Karma { @@ -63,7 +64,7 @@ namespace Karma m_ModelVertexArray->SetMaterial(m_ModelMaterial);*/ m_EditorScene.reset(new Karma::Scene()); - //m_EditorScene->AddCamera(m_EditorCamera); + m_EditorScene->AddCamera(m_EditorCamera); //m_EditorScene->AddVertexArray(m_ModelVertexArray); m_EditorScene->SetClearColor({ 0.0f, 0.0f, 0.0f, 1 }); } @@ -373,7 +374,9 @@ namespace Karma KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); - //m_EditorScene->AddStaticMeshActor(staticMeshActor); + m_EditorScene->AddStaticMeshActor(staticMeshActor); + + Karma::VulkanHolder::GetVulkanContext()->CreateVulkanResourcesForScene(m_EditorScene); } } } From de7b0a46e90d49b4bbe91915ead06e3eb7aef37a Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 14 Dec 2025 14:08:54 +0530 Subject: [PATCH 024/107] some stuff --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 4 ++-- Karma/src/Platform/Vulkan/VulkanContext.cpp | 10 +++++----- Resources/Shaders/shader.frag | 2 +- Resources/Shaders/shader.vert | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 2558b6f0..56c5dea2 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -467,7 +467,7 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't begin commandbuffer recording"); } - for(auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) + for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { std::shared_ptr scene3D = it->Scene3D; @@ -527,7 +527,7 @@ namespace Karma vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 1, 1, &dSets.textureSet[objectIndex], 0, nullptr); // Set 2: Object UBO - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectsSet[objectIndex], 1, nullptr); + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectsSet[objectIndex], 0, nullptr); // ----Issue Draw Commands---- // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 10ddf5bc..1d3e8f45 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -158,6 +158,9 @@ namespace Karma // For glslang Initializeglslang(); + + CreateGeneralShader(); + CreateGeneralTexture(); } void VulkanContext::CreateVulkanResourcesForScene(std::shared_ptr scene3D) @@ -180,10 +183,6 @@ namespace Karma uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); - - CreateGeneralShader(); - CreateGeneralTexture(); - CreateGeneralDescriptorPool(smElementsNumber); CreateGeneralDescriptorSetLayouts(); @@ -582,6 +581,7 @@ namespace Karma viewWrite.dstBinding = 0; viewWrite.dstArrayElement = 0; viewWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + viewWrite.descriptorCount = 1; viewWrite.pBufferInfo = &viewInfo; vkUpdateDescriptorSets(m_device, 1, &viewWrite, 0, nullptr); @@ -625,7 +625,7 @@ namespace Karma objectWrite.dstSet = frameDescriptorSets.objectsSet[smIndex]; objectWrite.dstBinding = 0; objectWrite.dstArrayElement = 0; - objectWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + objectWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; objectWrite.pBufferInfo = &objectInfo; objectWrite.descriptorCount = 1; diff --git a/Resources/Shaders/shader.frag b/Resources/Shaders/shader.frag index 22641847..12a7e909 100644 --- a/Resources/Shaders/shader.frag +++ b/Resources/Shaders/shader.frag @@ -11,5 +11,5 @@ layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { - outColor = vec4(vec3(fragColor.x, fragColor.y, fragColor.z) * texture(texSampler, fragUVs).rgb, 1.0); + outColor = vec4(vec3(fragColor.x, fragColor.y, fragColor.z), 1.0); } diff --git a/Resources/Shaders/shader.vert b/Resources/Shaders/shader.vert index 13cee965..8e0c6e66 100644 --- a/Resources/Shaders/shader.vert +++ b/Resources/Shaders/shader.vert @@ -25,7 +25,7 @@ layout(set = 2, binding = 0) uniform ModelUniformBufferObject void main() { - gl_Position = u_Projection * u_View * u_Model * vec4(inPosition, 1.0); + gl_Position = u_Projection * u_View * vec4(inPosition, 1.0); fragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); fragUVs = inUV; } From 78806573799def134af08b85884bb1402a026e7a Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 14 Dec 2025 21:49:38 +0530 Subject: [PATCH 025/107] uniform upload seems working --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 +-- Karma/src/Platform/Vulkan/VulkanContext.cpp | 3 ++- Resources/Shaders/shader.frag | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 56c5dea2..d4c1c834 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -471,7 +471,6 @@ namespace Karma { std::shared_ptr scene3D = it->Scene3D; - //scene3D->GetSceneCamera()->GetViewProjectionUBO()->UploadUniformBuffer(windowData->SemaphoreIndex); VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); FrameDescriptorSets dSets; @@ -531,7 +530,7 @@ namespace Karma // ----Issue Draw Commands---- // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) - vkCmdDrawIndexed(frameOnFlightData->CommandBuffer, objectIndex, 1, 0, 0, 0); + vkCmdDrawIndexed(frameOnFlightData->CommandBuffer, std::static_pointer_cast(mesh->GetIndexBuffer())->GetCount(), 1, 0, 0, 0); objectIndex++; } diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 1d3e8f45..b4971711 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -161,6 +161,8 @@ namespace Karma CreateGeneralShader(); CreateGeneralTexture(); + + CreateGeneralDescriptorSetLayouts(); } void VulkanContext::CreateVulkanResourcesForScene(std::shared_ptr scene3D) @@ -184,7 +186,6 @@ namespace Karma uint32_t maxFramesInFlight = static_cast(vulkanRendererAPI->GetMaxFramesInFlight()); CreateGeneralDescriptorPool(smElementsNumber); - CreateGeneralDescriptorSetLayouts(); CreateGeneralDescriptorSets(scene3D, smElementsNumber, maxFramesInFlight); diff --git a/Resources/Shaders/shader.frag b/Resources/Shaders/shader.frag index 12a7e909..22641847 100644 --- a/Resources/Shaders/shader.frag +++ b/Resources/Shaders/shader.frag @@ -11,5 +11,5 @@ layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { - outColor = vec4(vec3(fragColor.x, fragColor.y, fragColor.z), 1.0); + outColor = vec4(vec3(fragColor.x, fragColor.y, fragColor.z) * texture(texSampler, fragUVs).rgb, 1.0); } From bcb78864d60be4b418831091ad45a1651afeb324 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 14 Dec 2025 23:02:00 +0530 Subject: [PATCH 026/107] Vulkan Unreal - Phase VI --- Karma/src/Karma/Renderer/Camera/Camera.cpp | 2 +- Karma/src/Platform/Vulkan/VulkanContext.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index d670e6e9..6974f1c0 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -13,7 +13,7 @@ namespace Karma UBODataPointer uProjection(&m_ProjectionMatrix); UBODataPointer uView(&m_ViewMatrix); - m_ViewProjectionUBO->UpdateUniforms(uView, uProjection); + m_ViewProjectionUBO->UpdateUniforms(uProjection, uView); } Camera::~Camera() diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index b4971711..c8dcfdc8 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -264,6 +264,11 @@ namespace Karma BufferLayout layout; layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Position")); layout.PushElement(BufferElement(ShaderDataType::Float2, "v_UV")); + + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Normal")); + /*layout.PushElement(BufferElement(ShaderDataType::Float4, "v_Color")); + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Normal")); + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Tangent"));*/ VkVertexInputBindingDescription bindingDescription = {}; std::vector attributeDescriptions; From 816fc4f6d3712080d486dabbaafec5fb234d2408 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 15 Dec 2025 04:57:56 +0530 Subject: [PATCH 027/107] tentative transform buffer for SMActor --- Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp | 5 ++++- Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h | 3 +++ Resources/Shaders/shader.vert | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 71189a31..3e22e079 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -16,7 +16,10 @@ namespace Karma // We create uniform buffer object for model's transformation matrix m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization - UBODataPointer uModelMatrix(&GetTransform()); + m_ActorTranformUniform = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 1.f)); + m_ActorTranformUniform = glm::scale(m_ActorTranformUniform, glm::vec3(0.05f)); + + UBODataPointer uModelMatrix(&m_ActorTranformUniform); m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform if (!m_StaticMeshComponent) diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index bb22c542..3499245e 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -40,6 +40,9 @@ namespace Karma * Used to upload the actor's transform to the GPU for rendering */ std::shared_ptr m_MeshTransformUniform; + + //tentative + glm::mat4 m_ActorTranformUniform; public: /** diff --git a/Resources/Shaders/shader.vert b/Resources/Shaders/shader.vert index 8e0c6e66..2b4545e6 100644 --- a/Resources/Shaders/shader.vert +++ b/Resources/Shaders/shader.vert @@ -25,7 +25,7 @@ layout(set = 2, binding = 0) uniform ModelUniformBufferObject void main() { - gl_Position = u_Projection * u_View * vec4(inPosition, 1.0); + gl_Position = u_Projection * u_View * u_Model * vec4(inPosition, 1.0); fragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); fragUVs = inUV; } From 123b65275393c4c3183c2fd9082c93456a7c3748 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 15 Dec 2025 12:43:18 +0530 Subject: [PATCH 028/107] Karma Unreal RHI - Phase I --- GenerateProjects.bat | 2 +- Karma/src/Karma/KarmaRHI/DynamicRHI.cpp | 29 ++++++++ Karma/src/Karma/KarmaRHI/DynamicRHI.h | 69 +++++++++++++++++++ Karma/src/Karma/KarmaRHI/KarmaRHI.cpp | 0 Karma/src/Karma/KarmaRHI/KarmaRHI.h | 31 +++++++++ .../Vulkan/VulkanRHI/VulkanDynamicRHI.cpp | 0 .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 47 +++++++++++++ .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 10 +++ .../src/Platform/Vulkan/VulkanRHI/VulkanRHI.h | 22 ++++++ .../Vulkan/VulkanRHI/VulkanRHIPrivate.cpp | 0 .../Vulkan/VulkanRHI/VulkanRHIPrivate.h | 0 .../Windows/KarmaRHI/WindowsDynamicRHI.cpp | 22 ++++++ .../Windows/KarmaRHI/WindowsDynamicRHI.h | 18 +++++ 13 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 Karma/src/Karma/KarmaRHI/DynamicRHI.cpp create mode 100644 Karma/src/Karma/KarmaRHI/DynamicRHI.h create mode 100644 Karma/src/Karma/KarmaRHI/KarmaRHI.cpp create mode 100644 Karma/src/Karma/KarmaRHI/KarmaRHI.h create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.h create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.h create mode 100644 Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp create mode 100644 Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.h diff --git a/GenerateProjects.bat b/GenerateProjects.bat index c69c2f43..2913843f 100644 --- a/GenerateProjects.bat +++ b/GenerateProjects.bat @@ -8,7 +8,7 @@ set /p Input=Enter y(Yes) or n(No): If /I "%Input%"=="y" goto yes goto no :yes -KarmaBasedProjectName.sln +Game_Of_Lands.slnx exit :no echo Suit yourself! Goodbye! diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp new file mode 100644 index 00000000..06118c34 --- /dev/null +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp @@ -0,0 +1,29 @@ +#include "DynamicRHI.h" +#include "VulkanDynamicRHI.h" + +namespace Karma +{ + // Globals. + FDynamicRHI* GDynamicRHI = nullptr; + + + FDynamicRHI* FDynamicRHI::Create() + { + // if RHI is ERHIInterfaceType::Vulkan + return dynamic_cast(new FVulkanDynamicRHI()); + } + + void RHIInit() + { + if (!GDynamicRHI) + { + GDynamicRHI = PlatformCreateDynamicRHI(); + } + } + + void RHIExit() + { + delete GDynamicRHI; + GDynamicRHI = nullptr; + } +} \ No newline at end of file diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.h b/Karma/src/Karma/KarmaRHI/DynamicRHI.h new file mode 100644 index 00000000..d4e5c941 --- /dev/null +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.h @@ -0,0 +1,69 @@ +/** + * @file DynamicRHI.h + * @brief + * @author Tim Sweeney + * @date December 15, 2025 + * + * @copyright Epic Games, Inc. All Rights Reserved. + */ + +#pragma once + +#include "KarmaRHI.h" + +namespace Karma +{ + /** + * @brief Abstract base class for Dynamic Rendering Hardware Interface (RHI). + * + * Provides an interface for dynamic rendering operations across different graphics APIs. + * + * @since Unreal Engine 6.0.0 + */ + class KARMA_API FDynamicRHI + { + public: + + static FDynamicRHI* Create(); + + /** + * @brief Virtual destructor for FDynamicRHI. + */ + virtual ~FDynamicRHI() = default; + + /** + * @brief Initializes the RHI. + * + * Sets up necessary resources and states for rendering. + * + * @return true if initialization was successful, false otherwise. + */ + virtual bool Init() = 0; + /** + * @brief Shuts down the RHI. + * + * Cleans up resources and states used by the RHI. + */ + virtual void Shutdown() = 0; + + /** + * @brief Presents the rendered frame to the display. + */ + virtual void Present() = 0; + // Additional methods for rendering operations can be added here + + virtual Karma::ERHIInterfaceType GetInterfaceType() const { return Karma::ERHIInterfaceType::Hidden; } + }; + + /** A global pointer to the dynamically bound RHI implementation. */ + extern KARMA_API FDynamicRHI* GDynamicRHI; + + /** + * @brief Each platform that utilizes dynamic RHIs should implement this function + * + * Called to create the instance of the dynamic RHI. + * + * @since Karma 1.0.0 + */ + extern FDynamicRHI* PlatformCreateDynamicRHI(); +} \ No newline at end of file diff --git a/Karma/src/Karma/KarmaRHI/KarmaRHI.cpp b/Karma/src/Karma/KarmaRHI/KarmaRHI.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Karma/src/Karma/KarmaRHI/KarmaRHI.h b/Karma/src/Karma/KarmaRHI/KarmaRHI.h new file mode 100644 index 00000000..04478e8c --- /dev/null +++ b/Karma/src/Karma/KarmaRHI/KarmaRHI.h @@ -0,0 +1,31 @@ +/** + * @file KarmaRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @date December 15, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +namespace Karma +{ + enum class ERHIInterfaceType + { + Hidden, + Null, + D3D11, + D3D12, + Vulkan, + Metal, + Agx, + OpenGL, + }; + + /** Initializes the RHI. */ + extern KARMA_API void RHIInit(); + + /** Shuts down the RHI. */ + extern KARMA_API void RHIExit(); +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h new file mode 100644 index 00000000..f8e964a1 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -0,0 +1,47 @@ +/** + * @file VulkanDynamicRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 15, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "DynamicRHI.h" +#include "VulkanRHI.h" + +namespace Karma +{ + template + FORCEINLINE TRHI* GetDynamicRHI() + { + return static_cast(GDynamicRHI); + } + + class FVulkanDynamicRHI : public IVulkanDynamicRHI + { + public: + static FVulkanDynamicRHI& Get() { return *GetDynamicRHI(); } + + /** Initialization constructor. */ + FVulkanDynamicRHI(); + + virtual bool Init() override { return true; } + /** + * @brief Shuts down the RHI. + * + * Cleans up resources and states used by the RHI. + */ + virtual void Shutdown() override {} + + /** + * @brief Presents the rendered frame to the display. + * + * @since Karma 1.0.0 + */ + virtual void Present() override {} + }; +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp new file mode 100644 index 00000000..dfb6d8a7 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -0,0 +1,10 @@ +#include "VulkanDynamicRHI.h" + +namespace Karma +{ + FVulkanDynamicRHI::FVulkanDynamicRHI() + { + // create instance + // select device + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.h new file mode 100644 index 00000000..8dd43da6 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.h @@ -0,0 +1,22 @@ +/** + * @file VulkanRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 15, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "DynamicRHI.h" + +namespace Karma +{ + struct IVulkanDynamicRHI : public FDynamicRHI + { + virtual ERHIInterfaceType GetInterfaceType() const override { return ERHIInterfaceType::Vulkan; } + }; + +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHIPrivate.h new file mode 100644 index 00000000..e69de29b diff --git a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp new file mode 100644 index 00000000..6e6cb35f --- /dev/null +++ b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp @@ -0,0 +1,22 @@ +#include "WindowsDynamicRHI.h" +#include + +#include "Log.h" + +namespace Karma +{ + +#ifdef KR_WINDOWS_PLATFORM + FDynamicRHI* PlatformCreateDynamicRHI() + { + FDynamicRHI* DynamicRHI = FDynamicRHI::Create(); + + if (DynamicRHI) + { + KR_CORE_INFO("############ Created DynamicRHI"); + } + + return DynamicRHI; + } +#endif +} \ No newline at end of file diff --git a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.h b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.h new file mode 100644 index 00000000..e8255156 --- /dev/null +++ b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.h @@ -0,0 +1,18 @@ +/** + * @file WindowsDynamicRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 15, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "KarmaRHI.h" + +namespace Karma +{ + +} \ No newline at end of file From 22fc67a5924492dd63c3ef882c81b2ff05ddfa1d Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 15 Dec 2025 14:19:35 +0530 Subject: [PATCH 029/107] Added MacDynamicRHI --- .../Platform/Mac/KarmaRHI/MacDynamicRHI.cpp | 22 +++++++++++++++++++ .../src/Platform/Mac/KarmaRHI/MacDynamicRHI.h | 18 +++++++++++++++ .../Windows/KarmaRHI/WindowsDynamicRHI.cpp | 4 ++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp create mode 100644 Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.h diff --git a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp new file mode 100644 index 00000000..b7b59a52 --- /dev/null +++ b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp @@ -0,0 +1,22 @@ +#include "MacDynamicRHI.h" +#include "VulkanDynamicRHI.h" + +#include "Log.h" + +namespace Karma +{ + +#ifdef KR_MAC_PLATFORM + FDynamicRHI* PlatformCreateDynamicRHI() + { + FDynamicRHI* DynamicRHI = FDynamicRHI::Create(); + + if (DynamicRHI) + { + KR_CORE_INFO("############ Created DynamicRHI"); + } + + return DynamicRHI; + } +#endif +} diff --git a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.h b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.h new file mode 100644 index 00000000..4190e56e --- /dev/null +++ b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.h @@ -0,0 +1,18 @@ +/** + * @file MacDynamicRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 15, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "KarmaRHI.h" + +namespace Karma +{ + +} diff --git a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp index 6e6cb35f..c33201e4 100644 --- a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp +++ b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp @@ -1,5 +1,5 @@ #include "WindowsDynamicRHI.h" -#include +#include "VulkanDynamicRHI.h" #include "Log.h" @@ -19,4 +19,4 @@ namespace Karma return DynamicRHI; } #endif -} \ No newline at end of file +} From e1ec214ea836307a2d1898afe2ad2104d0cee8a7 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Tue, 16 Dec 2025 06:46:31 +0530 Subject: [PATCH 030/107] More Unreal like code --- .../Classes/Components/SceneComponent.cpp | 5 +++- .../Engine/Classes/Engine/StaticMeshActor.cpp | 26 +++++++++++++++-- .../Engine/Classes/Engine/StaticMeshActor.h | 3 ++ .../Engine/Classes/GameFramework/Actor.cpp | 22 ++++++++++++++ .../Engine/Classes/GameFramework/Actor.h | 29 ++++++++++++++++++- Karma/src/Karma/Ganit/Transform.cpp | 12 ++++++++ Karma/src/Karma/Ganit/Transform.h | 8 ++++- 7 files changed, 99 insertions(+), 6 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp index 826ebe06..5f5a7a25 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp @@ -62,6 +62,9 @@ namespace Karma void USceneComponent::SetWorldTransform(const FTransform& NewTransform) { + // I couldn't find this in UE code, however perplexity mentions this + m_ComponentToWorld = NewTransform; + // If attached to something, transform into local space if (GetAttachParent() != nullptr) { @@ -91,4 +94,4 @@ namespace Karma SetRelativeTransform(NewTransform/*, bSweep, OutSweepHitResult, Teleport*/); } } -} \ No newline at end of file +} diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 3e22e079..7bf79959 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -8,16 +8,36 @@ namespace Karma { } + void AStaticMeshActor::Tick(float DeltaSeconds) + { + //m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); + + FTransform AnimTransform = FTransform::Identity(); + + AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); + + m_ActorTranformUniform = SetActorTransform(AnimTransform); + + m_RotationAngle += 7.8f * DeltaSeconds; + + if(m_RotationAngle > 360.f) + { + m_RotationAngle -= 360.f; + } + } + void AStaticMeshActor::LoadMeshFromFile(const std::string& filePath) { std::shared_ptr loadedMesh; loadedMesh.reset(new Mesh(filePath)); // We create uniform buffer object for model's transformation matrix - m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 2));// Binding point 2 and gets registered in VulkanContext during its initialization + m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 0));// Binding point 2 and gets registered in VulkanContext during its initialization - m_ActorTranformUniform = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 1.f)); - m_ActorTranformUniform = glm::scale(m_ActorTranformUniform, glm::vec3(0.05f)); + //m_ActorTranformUniform = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 1.f)); + //m_ActorTranformUniform = glm::scale(m_ActorTranformUniform, glm::vec3(0.25f)); + + m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); UBODataPointer uModelMatrix(&m_ActorTranformUniform); m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 3499245e..11a6f057 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -43,6 +43,7 @@ namespace Karma //tentative glm::mat4 m_ActorTranformUniform; + float m_RotationAngle; public: /** @@ -78,5 +79,7 @@ namespace Karma * @since Karma 1.0.0 */ std::shared_ptr GetMeshTransformUniform() const { return m_MeshTransformUniform; } + + virtual void Tick(float DeltaSeconds) override; }; } diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp index f8379ec0..02285cf3 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp @@ -333,6 +333,17 @@ namespace Karma } } + bool AActor::SetActorTransform(const FTransform& NewTransform) + { + if(m_RootComponent) + { + m_RootComponent->SetWorldTransform(NewTransform); + return true; + } + + return false; + } + void AActor::DispatchBeginPlay(bool bFromLevelStreaming) { UWorld* World = (!HasActorBegunPlay() && IsValidChecked(this) ? GetWorld() : nullptr); @@ -544,6 +555,17 @@ namespace Karma return SceneRootComponent; } + bool AActor::SetActorLocation(const glm::vec3& NewLocation) + { + + } + + bool AActor::SetActorRotation(TRotator NewRotator) + {} + + void AActor::SetActorScale3D(glm::vec3 NewScale3D) + {} + bool AActor::SetRootComponent(class USceneComponent* NewRootComponent) { /** Only components owned by this actor can be used as a its root component. */ diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h index 8c8563c8..c9995967 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h @@ -352,8 +352,35 @@ namespace Karma { return ActorToWorld(); } + + /** + * Set the Actors transform to the specified one. + * @param NewTransform The new transform. + * @param bSweep Whether we sweep to the destination location, triggering overlaps along the way and stopping short of the target if blocked by something. + * Only the root component is swept and checked for blocking collision, child components move without sweeping. If collision is off, this has no effect. + * @param bTeleport Whether we teleport the physics state (if physics collision is enabled for this object). + * If true, physics velocity for this object is unchanged (so ragdoll parts are not affected by change in location). + * If false, physics velocity is updated based on the change in position (affecting ragdoll parts). + * If CCD is on and not teleporting, this will affect objects along the entire swept volume. + * Note that when teleporting, any child/attached components will be teleported too, maintaining their current offset even if they are being simulated. + * Setting the transform without teleporting will not update the transform of simulated child/attached components. + * + * @todo Hit detection when collision system is installed + * @since Karma 1.0.0 + */ + bool SetActorTransform(const FTransform& NewTransform/*, bool bSweep=false, FHitResult* OutSweepHitResult=nullptr, ETeleportType Teleport = ETeleportType::None*/); - /** + /** + * @brief Move the actor instantly to the specified location. + */ + bool SetActorLocation(const glm::vec3& NewLocation); + + bool SetActorRotation(TRotator NewRotation); + + /** Set the Actor's world-space scale. */ + void SetActorScale3D(glm::vec3 NewScale3D); + + /** * Get the local-to-world transform of the RootComponent. Identical to GetTransform(). * * @warning Need to test this function rigorously before using diff --git a/Karma/src/Karma/Ganit/Transform.cpp b/Karma/src/Karma/Ganit/Transform.cpp index 72446a99..9e57358f 100644 --- a/Karma/src/Karma/Ganit/Transform.cpp +++ b/Karma/src/Karma/Ganit/Transform.cpp @@ -144,4 +144,16 @@ namespace Karma OutTransform->SetScale3D(glm::vec3(ScaleA.x * ScaleB.x, ScaleA.y * ScaleB.y, ScaleA.z * ScaleB.z));// = VectorMultiply(ScaleA, ScaleB); //} } + + glm::mat4 FTransform::ToMatrixWithScale() const + { + glm::mat4 ReturnMatrix = glm::mat4(1.0f); + + ReturnMatrix = glm::scale(ReturnMatrix, m_Scale3D); + ReturnMatrix *= glm::mat4_cast(m_Rotation.ToQuat()); + + ReturnMatrix = glm::translate(ReturnMatrix, m_Translation); + + return ReturnMatrix; + } } diff --git a/Karma/src/Karma/Ganit/Transform.h b/Karma/src/Karma/Ganit/Transform.h index de251183..d4084864 100644 --- a/Karma/src/Karma/Ganit/Transform.h +++ b/Karma/src/Karma/Ganit/Transform.h @@ -94,7 +94,11 @@ namespace Karma glm::vec3 returnVector(tempResult.x, tempResult.y, tempResult.z); return returnVector; - + } + + inline glm::quat ToQuat() const + { + return glm::quat(glm::vec3(m_Pitch, m_Yaw, m_Roll)); } }; @@ -235,6 +239,8 @@ namespace Karma { m_Scale3D = Other.GetScale3D(); } + + glm::mat4 ToMatrixWithScale() const; public: static FTransform m_Identity; From 321a106c14a6f165738f1eeec21388c3113d6282 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 16 Dec 2025 18:59:04 +0530 Subject: [PATCH 031/107] Set/Get Actor's transform functional more or less --- Karma/src/Karma/Core/KarmaTypes.h | 13 ++++++++ .../Classes/Components/ActorComponent.cpp | 5 +++ .../Classes/Components/ActorComponent.h | 31 +++++++++++++++++++ .../Classes/Components/SceneComponent.cpp | 8 +++++ .../Classes/Components/SceneComponent.h | 11 +++++++ .../Engine/Classes/Engine/StaticMeshActor.cpp | 23 ++++++++------ .../Engine/Classes/GameFramework/Actor.cpp | 6 ++-- 7 files changed, 86 insertions(+), 11 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 8d36e993..e719b1c6 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -457,6 +457,19 @@ enum ETravelType TRAVEL_MAX, }; +/** Whether to teleport physics body or not */ +enum class ETeleportType : uint8_t +{ + /** Do not teleport physics body. This means velocity will reflect the movement between initial and final position, and collisions along the way will occur */ + None, + + /** Teleport physics body so that velocity remains the same and no collision occurs */ + TeleportPhysics, + + /** Teleport physics body and reset physics state completely */ + ResetPhysics, +}; + /** * @brief Helper for obtaining the default Url configuration */ diff --git a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp index 78db2f30..f9893835 100644 --- a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.cpp @@ -61,6 +61,11 @@ namespace Karma return m_OwnerPrivate; } + void UActorComponent::SetOwner(AActor* NewOwner) + { + m_OwnerPrivate = NewOwner; + } + void UActorComponent::Activate(bool bReset) { if (bReset || ShouldActivate() == true) diff --git a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h index e60de3d3..74520879 100644 --- a/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/ActorComponent.h @@ -10,6 +10,7 @@ #pragma once #include "Karma/Core/Object.h" +#include "Core/KarmaTypes.h" class AActor; @@ -30,6 +31,21 @@ namespace Karma Instance, }; + /** + * @brief Information about how to update transform when something is moved + */ + enum class EUpdateTransformFlags : int32 + { + /** Default options */ + None = 0x0, + /** Don't update the underlying physics */ + SkipPhysicsUpdate = 0x1, + /** The update is coming as a result of the parent updating (i.e. not called directly) */ + PropagateFromParent = 0x2, + /** Only update child transform if attached to parent via a socket */ + OnlyUpdateIfUsingSocket = 0x4 + }; + /** * @brief ActorComponent is the base class for components that define reusable behavior that can be added to different types of Actors. * ActorComponents that have a transform are known as SceneComponents and those that can be rendered are PrimitiveComponents. @@ -114,6 +130,14 @@ namespace Karma */ AActor* GetOwner() const; + /** + * @brief Sets the owner of this component + * + * @param NewOwner - The new owner actor for this component + * @since Karma 1.0.0 + */ + void SetOwner(AActor* NewOwner); + /** * @brief Indicates that OnCreatedComponent has been called, but OnDestroyedComponent has not yet * @@ -135,6 +159,13 @@ namespace Karma */ inline bool IsRegistered() const { return m_bRegistered; } + /** + * @brief Recalculate the value of our component to world transform + * + * @since Karma 1.0.0 + */ + virtual void UpdateComponentToWorld(EUpdateTransformFlags UpdateTransformFlags = EUpdateTransformFlags::None, ETeleportType Teleport = ETeleportType::None) {} + /** * @brief Returns whether the component is active or not * diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp index 5f5a7a25..271d78d2 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.cpp @@ -8,6 +8,8 @@ namespace Karma USceneComponent::USceneComponent() : UActorComponent() { m_AttachParent = nullptr; + + m_ComponentToWorld = FTransform::Identity(); } void USceneComponent::SetWorldLocation(glm::vec3 newLocation) @@ -63,6 +65,12 @@ namespace Karma void USceneComponent::SetWorldTransform(const FTransform& NewTransform) { // I couldn't find this in UE code, however perplexity mentions this + // Ok in the UE, they first do the computation to get relative transform + + // SetWorldTransform -> SetRelativeTransform -> SetRelativeLocationAndRotation (and Scale) -> MoveComponent + // MoveComponentImpl -> ConditionalUpdateComponentToWorld ->UpdateComponentToWorld + // -> UpdateComponentToWorldWithParent -> ComponentToWorld = NewTransform; + // So we directly set the ComponentToWorld here first since we don't have attach to parent logic yet m_ComponentToWorld = NewTransform; // If attached to something, transform into local space diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h index 8cc1ed39..4153f6d1 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h @@ -266,6 +266,17 @@ namespace Karma */ void SetRelativeTransform(const FTransform& NewTransform/*, bool bSweep = false, FHitResult* OutSweepHitResult = nullptr, ETeleportType Teleport = ETeleportType::None*/); + /** + * @brief Recalculate the value of our component to world transform + * + * @since Karma 1.0.0 + */ + virtual void UpdateComponentToWorld(EUpdateTransformFlags UpdateTransformFlags = EUpdateTransformFlags::None, ETeleportType Teleport = ETeleportType::None) override final + { + //UpdateComponentToWorldWithParent(GetAttachParent(), GetAttachSocketName(), UpdateTransformFlags, RelativeRotationCache.RotatorToQuat(GetRelativeRotation()), Teleport); + + } + /** * @brief Getter for the m_AttachSocektName * diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 7bf79959..fedfe7e8 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -6,6 +6,15 @@ namespace Karma AStaticMeshActor::AStaticMeshActor(const std::string& name) : AActor() { + if (!m_StaticMeshComponent) + { + // Unreal uses CreateDefaultSubobject for creating components in Actors + // may need to implement similar functionality later + m_StaticMeshComponent = NewObject(this, UStaticMeshComponent::StaticClass(), "StaticMeshComponent"); + m_StaticMeshComponent->SetOwner(this); + } + + m_RootComponent = m_StaticMeshComponent; } void AStaticMeshActor::Tick(float DeltaSeconds) @@ -14,11 +23,13 @@ namespace Karma FTransform AnimTransform = FTransform::Identity(); - AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); + AnimTransform.SetRotation(TRotator(glm::vec3(m_RotationAngle, 0.f, 0.f))); - m_ActorTranformUniform = SetActorTransform(AnimTransform); + SetActorTransform(AnimTransform); + + m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); - m_RotationAngle += 7.8f * DeltaSeconds; + m_RotationAngle += 0.78f * DeltaSeconds; if(m_RotationAngle > 360.f) { @@ -42,12 +53,6 @@ namespace Karma UBODataPointer uModelMatrix(&m_ActorTranformUniform); m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform - if (!m_StaticMeshComponent) - { - // need to write code to destroy UObjects later - m_StaticMeshComponent = NewObject(nullptr, UStaticMeshComponent::StaticClass(), "StaticMeshComponent"); - } - m_StaticMeshComponent->SetStaticMesh(loadedMesh); } } diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp index 02285cf3..e6bc16fb 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp @@ -557,11 +557,13 @@ namespace Karma bool AActor::SetActorLocation(const glm::vec3& NewLocation) { - + return true; } bool AActor::SetActorRotation(TRotator NewRotator) - {} + { + return true; + } void AActor::SetActorScale3D(glm::vec3 NewScale3D) {} From 225ecfe4eb5d8ef07363a816e36eae78aa8c9593 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 16 Dec 2025 19:49:56 +0530 Subject: [PATCH 032/107] Minor additions with rectificaitons --- Karma/src/Karma/Core/UObjectGlobals.cpp | 12 ++++++++++++ Karma/src/Karma/Core/UObjectGlobals.h | 14 ++++++++++++++ .../Engine/Classes/Engine/StaticMeshActor.cpp | 2 +- Karma/src/Karma/Ganit/Transform.cpp | 6 +++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Karma/src/Karma/Core/UObjectGlobals.cpp b/Karma/src/Karma/Core/UObjectGlobals.cpp index b9c47274..3aae4af4 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.cpp +++ b/Karma/src/Karma/Core/UObjectGlobals.cpp @@ -503,6 +503,18 @@ namespace Karma return Object; } + UObject* CreateDefaultSubobject(UObject* Outer, const std::string& SubobjectFName, const UClass* SubobjectClass, bool bIsRequired, bool bIsAbstract) + { + if (SubobjectFName == "") + { + KR_CORE_WARN("CreateDefaultSubobject called with invalid name"); + } + + UObject* Result = nullptr; + + return Result; + } + UPackage* CreatePackage(const std::string& PackageName) { std::string InName; diff --git a/Karma/src/Karma/Core/UObjectGlobals.h b/Karma/src/Karma/Core/UObjectGlobals.h index 2215009e..ab501867 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.h +++ b/Karma/src/Karma/Core/UObjectGlobals.h @@ -814,6 +814,20 @@ FUNCTION_NON_NULL_RETURN_START*/ return static_cast(StaticConstructObject_Internal(Params)); }*/ +/** + * @brief Create a component or subobject that will be instanced inside all instances of this class. + * + * @param Outer outer to construct the subobject in + * @param SubobjectName name of the new component + * @param ReturnType class of return type, all overrides must be of this type + * @param ClassToConstructByDefault if the derived class has not overridden, create a component of this type + * @param bIsRequired true if the component is required and will always be created even if DoNotCreateDefaultSubobject was specified. + * @param bIsTransient true if the component is being assigned to a transient property + * + * @note This is work in progress, not fully functional yet + */ +KARMA_API UObject* CreateDefaultSubobject(UObject* Outer, std::string SubobjectFName, const UClass* ReturnType, const UClass* ClassToCreateByDefault, bool bIsRequired = true, bool bIsTransient = false); + /** * A routine to find if the object is instantiated already. May need to modify in accordance with thread safety in future * UE name StaticFindObjectFastInternal diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index fedfe7e8..7f06c986 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -23,7 +23,7 @@ namespace Karma FTransform AnimTransform = FTransform::Identity(); - AnimTransform.SetRotation(TRotator(glm::vec3(m_RotationAngle, 0.f, 0.f))); + AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); SetActorTransform(AnimTransform); diff --git a/Karma/src/Karma/Ganit/Transform.cpp b/Karma/src/Karma/Ganit/Transform.cpp index 9e57358f..7f5d6071 100644 --- a/Karma/src/Karma/Ganit/Transform.cpp +++ b/Karma/src/Karma/Ganit/Transform.cpp @@ -13,9 +13,9 @@ namespace Karma TRotator::TRotator(glm::vec3 eulerAngles) { - m_Yaw = eulerAngles.y; - m_Pitch = eulerAngles.z; - m_Roll = eulerAngles.x; + m_Yaw = eulerAngles.x; + m_Pitch = eulerAngles.y; + m_Roll = eulerAngles.z; } TRotator TRotator::Inverse() const From 08e66e02676a4abbff163d51588930e4c5f0a30a Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 17 Dec 2025 12:17:22 +0530 Subject: [PATCH 033/107] VulkanRHI Windows : Phase I --- Karma/src/Karma/Application.cpp | 8 + Karma/src/Karma/KarmaRHI/DynamicRHI.cpp | 20 +- Karma/src/Karma/KarmaRHI/DynamicRHI.h | 14 +- Karma/src/Platform/Vulkan/VulkanContext.cpp | 3 + .../Vulkan/VulkanRHI/VulkanDevice.cpp | 13 ++ .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 59 ++++++ .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 96 +++++++++ .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 200 ++++++++++++++++++ .../Windows/KarmaRHI/WindowsDynamicRHI.cpp | 2 +- Karma/src/Platform/Windows/WindowsWindow.cpp | 3 +- 10 files changed, 410 insertions(+), 8 deletions(-) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index d2e8d7aa..62b697d7 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -5,6 +5,7 @@ #include "Engine/Engine.h" #include "Core/UObjectGlobals.h"// to be bundled appropriately in core.h #include "Core/Package.h" +#include "KarmaRHI/KarmaRHI.h" namespace Karma { @@ -30,6 +31,9 @@ namespace Karma m_Window = Window::Create(); m_Window->SetEventCallback(KR_BIND_EVENT_FN(Application::OnEvent)); // Setting the listener + // Initialize RHI (Render Hardware Interface) + RHIInit(); + m_LayerStack = new LayerStack(); // Graphics API Vulkan or OpenGL should have been completely initialized by here @@ -47,6 +51,10 @@ namespace Karma // and its context. KR_CORE_INFO("Deleting stacks"); delete m_LayerStack; + + // Deinitialize RHI + RHIExit(); + KR_CORE_INFO("Deleting window"); delete m_Window; s_Instance = nullptr; diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp index 06118c34..dca90950 100644 --- a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp @@ -5,12 +5,20 @@ namespace Karma { // Globals. FDynamicRHI* GDynamicRHI = nullptr; + ERHIInterfaceType GRHIInterfaceType = ERHIInterfaceType::Vulkan; - - FDynamicRHI* FDynamicRHI::Create() + FDynamicRHI* FDynamicRHI::CreateRHI() { // if RHI is ERHIInterfaceType::Vulkan - return dynamic_cast(new FVulkanDynamicRHI()); + if (GRHIInterfaceType == ERHIInterfaceType::Vulkan) + { + return static_cast(new FVulkanDynamicRHI()); + } + else + { + KR_CORE_WARN("RHI Interface Type not recognized!"); + return nullptr; + } } void RHIInit() @@ -18,6 +26,10 @@ namespace Karma if (!GDynamicRHI) { GDynamicRHI = PlatformCreateDynamicRHI(); + if (GDynamicRHI) + { + KR_CORE_INFO("############ Initialized DynamicRHI"); + } } } @@ -25,5 +37,7 @@ namespace Karma { delete GDynamicRHI; GDynamicRHI = nullptr; + + KR_CORE_INFO("############ Shutdown DynamicRHI"); } } \ No newline at end of file diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.h b/Karma/src/Karma/KarmaRHI/DynamicRHI.h index d4e5c941..4e387f39 100644 --- a/Karma/src/Karma/KarmaRHI/DynamicRHI.h +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.h @@ -24,7 +24,7 @@ namespace Karma { public: - static FDynamicRHI* Create(); + static FDynamicRHI* CreateRHI(); /** * @brief Virtual destructor for FDynamicRHI. @@ -59,7 +59,17 @@ namespace Karma extern KARMA_API FDynamicRHI* GDynamicRHI; /** - * @brief Each platform that utilizes dynamic RHIs should implement this function + * @brief The current RHI interface type in use. + * + * @remark This variable indicates which graphics API is currently active. Later + * we can tie this to a configuration system (for instance loading configurable setting from file). + * + * @since Karma 1.0.0 + */ + extern KARMA_API ERHIInterfaceType GRHIInterfaceType; + + /** + * @brief Each platform that utilizes dynamic RHIs should implement this function * * Called to create the instance of the dynamic RHI. * diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index c8dcfdc8..593d19b1 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -70,6 +70,9 @@ namespace Karma vkDestroyDescriptorPool(m_device, m_GeneralDescriptorPool, nullptr); + // Tentative + static_pointer_cast(m_GeneralTexture)->~VulkanTexture(); + m_vulkanRendererAPI->ClearVulkanRendererAPI(); // Tentative for the moment diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp new file mode 100644 index 00000000..bf8f5538 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -0,0 +1,13 @@ +#include "VulkanDevice.h" + +namespace Karma +{ + FVulkanDevice::FVulkanDevice(VkPhysicalDevice physicalDevice, VkInstance instance) + { + // Implementation for creating a Vulkan logical device + } + FVulkanDevice::~FVulkanDevice() + { + // Implementation for cleaning up Vulkan device resources + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h new file mode 100644 index 00000000..30077e51 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -0,0 +1,59 @@ +/** + * @file VulkanDevice.h + * @brief Declaration of the FVulkanDevice class for managing Vulkan device resources. + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 17, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include + +namespace Karma +{ + /** + * @class FVulkanDevice + * @brief Manages Vulkan device resources and operations. + * + * This class encapsulates the functionality related to a Vulkan logical device, + * including initialization, resource management, and cleanup. + */ + class FVulkanDevice + { + public: + /** + * @brief Constructor for FVulkanDevice. + * + * Initializes the Vulkan device with the specified physical device and instance. + * + * @param physicalDevice The Vulkan physical device to create the logical device from. + * @param instance The Vulkan instance associated with this device. + * + * @since Karma 1.0.0 + */ + FVulkanDevice(VkPhysicalDevice physicalDevice, VkInstance instance); + + /** + * @brief Destructor for FVulkanDevice. + * + * Cleans up resources associated with the Vulkan device. + * + * @since Karma 1.0.0 + */ + ~FVulkanDevice(); + + /** + * @brief Retrieves the Vulkan logical device handle. + * + * @return VkDevice The Vulkan logical device. + */ + VkDevice GetLogicalDevice() const { m_LogicalDevice; } + + private: + VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. + // Additional members for managing queues, command pools, etc. can be added here. + }; +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index f8e964a1..04366699 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -13,6 +13,8 @@ #include "DynamicRHI.h" #include "VulkanRHI.h" +#include "VulkanDevice.h" + namespace Karma { template @@ -43,5 +45,99 @@ namespace Karma * @since Karma 1.0.0 */ virtual void Present() override {} + + inline VkInstance GetInstance() const + { + return m_VulkanInstance; + } + + inline FVulkanDevice* GetDevice() const + { + return m_Device; + } + + protected: + // Vulkan-specific members and methods can be added here + + /** + * @brief Creates a Vulkan instance. + * + * @since Karma 1.0.0 + */ + void CreateInstance(); + + /** + * @brief Prints all the available extensions supported by the system's Vulkan implementation (VkInstance). + * + * Vulkan extensions provide additional functionality beyond the core Vulkan API. + * + * For instance VK_KHR_Surface (for window system integration) or VK_KHR_get_physical_device_properties2 (for querying extended physical device properties). + * + * @see VulkanContext::CreateInstance() + * @since Karma 1.0.0 + */ + void PrintAvailableExtensions(); + + /** + * @brief Retrieves the required Vulkan instance extensions based on the platform and validation layer settings. + * + * 1. For window system integration, queries GLFW for required extensions. + * 2. For MacOS, adds VK_KHR_portability_enumeration extension if available. + * + * @param flagsToBeSet Reference to VkInstanceCreateFlags to be set based on required extensions. + * + * @return A vector of required extension names. + * + * @see VulkanContext::CreateInstance() + * @since Karma 1.0.0 + */ + std::vector GetRequiredExtensions(VkInstanceCreateFlags& flagsToBeSet); + + /** + * @brief Checks if the requested validation layers are supported by the Vulkan implementation. + * + * Validation layers are used for debugging and development purposes to catch errors and provide additional information during Vulkan API usage. + * + * @return true if all requested validation layers are supported, false otherwise. + * + * @since Karma 1.0.0 + */ + bool CheckValidationLayerSupport(); + + /** + * @brief Populates the debug messenger create info structure for setting up validation layers. + * + * @param createInfo Reference to the VkDebugUtilsMessengerCreateInfoEXT structure to be populated. + * + * @note This function sets DebugCallback as the callback function for handling (printing, for instance) debug messages. + * @see DebugCallback() + * + * @since Karma 1.0.0 + */ + void PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo); + + /** + * @brief Callback function for Vulkan debug messages. + * + * @param messageSeverity Severity of the debug message. + * @param messageType Type of the debug message. + * @param pCallbackData Pointer to the callback data containing the debug message. + * @param pUserData User-defined data passed to the callback. + * + * @since Karma 1.0.0 + */ + static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + + protected: + // Vulkan instance handle and other Vulkan-specific members can be declared here + + uint32_t m_APIVersion; + VkInstance m_VulkanInstance; + + FVulkanDevice* m_Device; + static bool bEnableValidationLayers; }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index dfb6d8a7..9b79ed7e 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -1,10 +1,210 @@ #include "VulkanDynamicRHI.h" +#include "GLFW/glfw3.h" namespace Karma { + const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; + +#ifdef KR_DEBUG + bool FVulkanDynamicRHI::bEnableValidationLayers = true; +#else + bool FVulkanDynamicRHI::bEnableValidationLayers = false; +#endif + FVulkanDynamicRHI::FVulkanDynamicRHI() { + KR_CORE_INFO("Initializing Vulkan RHI..."); + // create instance + CreateInstance(); // select device } + + void FVulkanDynamicRHI::CreateInstance() + { + // Implementation for creating a Vulkan instance + if (bEnableValidationLayers) + { + PrintAvailableExtensions(); + } + + if (bEnableValidationLayers && !CheckValidationLayerSupport()) + { + KR_CORE_WARN("Validation layers requested, but not available"); + } + + // Optional information about the application (or Engine in our case) + // TODO: Fill this info from Engine config (if/when we have the config) + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; +#ifdef KR_APPLICATION_NAME + appInfo.pApplicationName = KR_APPLICATION_NAME; +#else + appInfo.pApplicationName = "No Name"; +#endif + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "Karma"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_2; + + // Tell Vulkan which global extensions and validation layers we want to use + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + // Validation layers + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo; + if (bEnableValidationLayers) + { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + + PopulateDebugMessengerCreateInfo(debugCreateInfo); + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)(&debugCreateInfo); + } + else + { + createInfo.enabledLayerCount = 0; + createInfo.pNext = nullptr; + } + + VkInstanceCreateFlags flagsToBeSet{}; + auto extensions = GetRequiredExtensions(flagsToBeSet); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + createInfo.flags |= flagsToBeSet; + + VkResult result = vkCreateInstance(&createInfo, nullptr, &m_VulkanInstance); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create Vulkan m_Instance."); + + KR_CORE_INFO("Vulkan Instance created successfully."); + } + + void FVulkanDynamicRHI::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) + { + createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = DebugCallback; + } + + VKAPI_ATTR VkBool32 VKAPI_CALL FVulkanDynamicRHI::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + switch (messageSeverity) + { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + KR_CORE_INFO("Validation Layer: {0}", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + KR_CORE_WARN("Validation Layer: {0}", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + KR_CORE_ERROR("Validation Layer: {0}", pCallbackData->pMessage); + break; + default: + //KR_CORE_TRACE("Validation Layer: {0}", pCallbackData->pMessage); + break; + } + + return VK_FALSE; + } + + // Return the required list of instance extensions based on whether validation layers are + // enabled or not + std::vector FVulkanDynamicRHI::GetRequiredExtensions(VkInstanceCreateFlags& flagsToBeSet) + { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + +#ifdef KR_MAC_PLATFORM + // Case by case query for required instance extensions + // One for MacOS: VK_KHR_portability_enumeration + uint32_t extensionCount = 0; + + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + std::vector vulkanExtensions(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, vulkanExtensions.data()); + + uint32_t index = 1; + for (auto anExtension : vulkanExtensions) + { + if (strcmp(anExtension.extensionName, "VK_KHR_portability_enumeration")) + { + extensions.push_back("VK_KHR_portability_enumeration"); + flagsToBeSet = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + break; + } + } +#endif + if (bEnableValidationLayers)// Enable printing of extensions when validation layers are enabled (debug mode) + { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + uint32_t index = 1; + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| GLFW and other required instance extensions:"); + for (auto extension : extensions) + { + KR_CORE_INFO("| {0}. {1}", index++, extension); + } + KR_CORE_INFO("+-------------------------------------------------"); + } + + return extensions; + } + + void FVulkanDynamicRHI::PrintAvailableExtensions() + { + uint32_t extensionCount = 0; + + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + std::vector vulkanExtensions(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, vulkanExtensions.data()); + + uint32_t index = 1; + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| Available Vulkan instance extensions:"); + for (auto extension : vulkanExtensions) + { + KR_CORE_INFO("| {0}. {1}", index++, extension.extensionName); + } + KR_CORE_INFO("+-------------------------------------------------"); + } + + bool FVulkanDynamicRHI::CheckValidationLayerSupport() + { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) + { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) + { + if (strcmp(layerName, layerProperties.layerName) == 0) + { + layerFound = true; + break; + } + } + + if (!layerFound) + { + return false; + } + } + + return true; + } } \ No newline at end of file diff --git a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp index c33201e4..21eea6a8 100644 --- a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp +++ b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp @@ -9,7 +9,7 @@ namespace Karma #ifdef KR_WINDOWS_PLATFORM FDynamicRHI* PlatformCreateDynamicRHI() { - FDynamicRHI* DynamicRHI = FDynamicRHI::Create(); + FDynamicRHI* DynamicRHI = FDynamicRHI::CreateRHI(); if (DynamicRHI) { diff --git a/Karma/src/Platform/Windows/WindowsWindow.cpp b/Karma/src/Platform/Windows/WindowsWindow.cpp index 82057cdd..58d373ce 100644 --- a/Karma/src/Platform/Windows/WindowsWindow.cpp +++ b/Karma/src/Platform/Windows/WindowsWindow.cpp @@ -76,8 +76,7 @@ namespace Karma break; } - m_Context->Init(); - SetVSync(true); + //SetVSync(true); // Used for event callbacks glfwSetWindowUserPointer(m_Window, &m_Data); From 4c1d69dc99353366f90dc0b97ef327d1fc2fd3be Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 17 Dec 2025 13:13:01 +0530 Subject: [PATCH 034/107] Basic functional without graphics context In process of replacement by RHI. Instance creation and destruction functional in VulkanDynamicRHI. --- Karma/src/Karma/Application.cpp | 8 ++++---- Karma/src/Karma/KarmaRHI/DynamicRHI.cpp | 13 +++++++++---- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 4 ++-- .../src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 17 ++++++++++++++++- Karma/src/Platform/Windows/WindowsWindow.cpp | 13 +++++++------ Pranjal/Source/Private/EditorLayer.cpp | 3 ++- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index 62b697d7..f3bbf03b 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -37,8 +37,8 @@ namespace Karma m_LayerStack = new LayerStack(); // Graphics API Vulkan or OpenGL should have been completely initialized by here - m_KarmaGuiLayer = new KarmaGuiLayer(m_Window); - PushOverlay(m_KarmaGuiLayer); + //m_KarmaGuiLayer = new KarmaGuiLayer(m_Window); <-- to be uncommented when we have RHI + //PushOverlay(m_KarmaGuiLayer);<-- to be uncommented when we have RHI } Application::~Application() @@ -120,14 +120,14 @@ namespace Karma } // KarmaGui rendering sequence cue trickling through stack - m_KarmaGuiLayer->Begin(); + //m_KarmaGuiLayer->Begin(); <-- to be uncommented when we have RHI for (auto layer : *m_LayerStack) { layer->KarmaGuiRender(deltaTime); } - m_KarmaGuiLayer->End(); + //m_KarmaGuiLayer->End();<-- to be uncommented when we have RHI m_Window->OnUpdate(); } diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp index dca90950..79fc2535 100644 --- a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp @@ -28,16 +28,21 @@ namespace Karma GDynamicRHI = PlatformCreateDynamicRHI(); if (GDynamicRHI) { - KR_CORE_INFO("############ Initialized DynamicRHI"); + GDynamicRHI->Init(); } } } void RHIExit() { - delete GDynamicRHI; - GDynamicRHI = nullptr; + if (GDynamicRHI != nullptr) + { + GDynamicRHI->Shutdown(); + + delete GDynamicRHI; + GDynamicRHI = nullptr; + } - KR_CORE_INFO("############ Shutdown DynamicRHI"); + KR_CORE_INFO("Deleted DynamicRHI object"); } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 04366699..07444450 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -31,13 +31,13 @@ namespace Karma /** Initialization constructor. */ FVulkanDynamicRHI(); - virtual bool Init() override { return true; } + virtual bool Init() override; /** * @brief Shuts down the RHI. * * Cleans up resources and states used by the RHI. */ - virtual void Shutdown() override {} + virtual void Shutdown() override; /** * @brief Presents the rendered frame to the display. diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 9b79ed7e..04db1e45 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -15,11 +15,26 @@ namespace Karma { KR_CORE_INFO("Initializing Vulkan RHI..."); - // create instance CreateInstance(); + // select device } + bool FVulkanDynamicRHI::Init() + { + // initialize device + KR_CORE_INFO("Vulkan RHI initialized successfully."); + return true; + } + + void FVulkanDynamicRHI::Shutdown() + { + // destroy device + + vkDestroyInstance(m_VulkanInstance, nullptr); + KR_CORE_INFO("Vulkan RHI shutdown complete by destroying Vulkan Instance."); + } + void FVulkanDynamicRHI::CreateInstance() { // Implementation for creating a Vulkan instance diff --git a/Karma/src/Platform/Windows/WindowsWindow.cpp b/Karma/src/Platform/Windows/WindowsWindow.cpp index 58d373ce..299f56f3 100644 --- a/Karma/src/Platform/Windows/WindowsWindow.cpp +++ b/Karma/src/Platform/Windows/WindowsWindow.cpp @@ -63,7 +63,8 @@ namespace Karma m_Window = glfwCreateWindow((int)m_Data.Width, (int)m_Data.Height, m_Data.Title.c_str(), nullptr, nullptr); - switch (currentAPI) + // To be replaced with RHI + /*switch (currentAPI) { case RendererAPI::API::None: KR_CORE_ASSERT(false, "RendererAPI::None is not supported"); @@ -74,7 +75,7 @@ namespace Karma case RendererAPI::API::Vulkan: m_Context = new VulkanContext(m_Window); break; - } + }*/ //SetVSync(true); @@ -93,7 +94,7 @@ namespace Karma bool WindowsWindow::OnResize(WindowResizeEvent& event) { - return m_Context->OnWindowResize(event); + return true;//m_Context->OnWindowResize(event); <-- to be removed when RHI is in place } void WindowsWindow::SetGLFWCallbacks(GLFWwindow* glfwWindow) @@ -200,17 +201,17 @@ namespace Karma { glfwDestroyWindow(m_Window); glfwTerminate(); - if (m_Context) + /*if (m_Context) { delete m_Context; m_Context = 0; - } + }*/ //<--to be removed when we have RHI } void WindowsWindow::OnUpdate() { glfwPollEvents(); - m_Context->SwapBuffers(); + //m_Context->SwapBuffers();<-- to be removed when RHI is in place? } void WindowsWindow::SetVSync(bool enabled) diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 8d4bb9b1..fa94ba6a 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -410,7 +410,8 @@ class KarmaApp : public Karma::Application public: KarmaApp() { - PushLayer(new Karma::EditorLayer()); + // To be un commented when we have fully functional RHI + //PushLayer(new Karma::EditorLayer()); } }; From ffc173960671f4b6dc0074f2139926ca2ba1b9c7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 17 Dec 2025 15:57:38 +0530 Subject: [PATCH 035/107] Vulkan Surface creation functional --- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 95 ++++++++++++++++--- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 71 +++++++++++++- 2 files changed, 150 insertions(+), 16 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 07444450..7c9262c5 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -15,6 +15,8 @@ #include "VulkanDevice.h" +struct GLFWwindow; + namespace Karma { template @@ -66,11 +68,71 @@ namespace Karma */ void CreateInstance(); + /** + * @brief Sets up the debug messenger for Vulkan instance. + * + * This function initializes the debug messenger that will handle debug messages from the Vulkan API. + * It populates the debug messenger create info structure and creates the debug messenger using + * the CreateDebugUtilsMessengerEXT function. + * + * @see FVulkanDynamicRHI::CreateDebugUtilsMessengerEXT() + * @since Karma 1.0.0 + */ + void SetupDebugMessenger(); + + /** + * @brief Platform agnostic creation of surface to present rendered images to. Typically they are backed by the + * category of glfw windows (on Linux, MacOS, and Windows). + * + * @since Karma 1.0.0 + */ + void CreateSurface(); + + void SelectDevice(); + + private: + + /** + * @brief Creates a debug utils messenger for Vulkan instance. + * + * This function is used to set up a debug messenger that will handle debug messages from the Vulkan API. + * Basically looks for the vkCreateDebugUtilsMessengerEXT extension, and if found, uses it to create the debug messenger by + * creating function pointer to it using vkGetInstanceProcAddr. + * + * @param instance The Vulkan instance. + * @param pCreateInfo Pointer to the debug utils messenger create info structure. + * @param pAllocator Optional custom allocator. + * @param pDebugMessenger Pointer to the debug utils messenger handle to be created. + * + * @return VkResult indicating success or failure of the operation. + * + * @see FVulkanDynamicRHI::SetupDebugMessenger() + * @since Karma 1.0.0 + */ + VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger); + + /** + * @brief Destroys the debug utils messenger for Vulkan instance. + * + * @param instance The Vulkan instance. + * @param debugMessenger The debug utils messenger to be destroyed. + * + * @param pAllocator Optional custom allocator. + * + * @since Karma 1.0.0 + */ + void DestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator); + /** * @brief Prints all the available extensions supported by the system's Vulkan implementation (VkInstance). * * Vulkan extensions provide additional functionality beyond the core Vulkan API. - * + * * For instance VK_KHR_Surface (for window system integration) or VK_KHR_get_physical_device_properties2 (for querying extended physical device properties). * * @see VulkanContext::CreateInstance() @@ -80,14 +142,14 @@ namespace Karma /** * @brief Retrieves the required Vulkan instance extensions based on the platform and validation layer settings. - * + * * 1. For window system integration, queries GLFW for required extensions. * 2. For MacOS, adds VK_KHR_portability_enumeration extension if available. - * + * * @param flagsToBeSet Reference to VkInstanceCreateFlags to be set based on required extensions. - * + * * @return A vector of required extension names. - * + * * @see VulkanContext::CreateInstance() * @since Karma 1.0.0 */ @@ -95,23 +157,25 @@ namespace Karma /** * @brief Checks if the requested validation layers are supported by the Vulkan implementation. - * + * * Validation layers are used for debugging and development purposes to catch errors and provide additional information during Vulkan API usage. - * + * * @return true if all requested validation layers are supported, false otherwise. - * + * * @since Karma 1.0.0 */ bool CheckValidationLayerSupport(); /** * @brief Populates the debug messenger create info structure for setting up validation layers. + * + * Basically for choosing which type of debug messages we want to be displayed * * @param createInfo Reference to the VkDebugUtilsMessengerCreateInfoEXT structure to be populated. - * + * * @note This function sets DebugCallback as the callback function for handling (printing, for instance) debug messages. - * @see DebugCallback() - * + * @see FVulkanDynamicRHI::SetupDebugMessenger(), FVulkanDynamicRHI::CreateInstance() + * * @since Karma 1.0.0 */ void PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo); @@ -119,11 +183,13 @@ namespace Karma /** * @brief Callback function for Vulkan debug messages. * + * Here we define the actions to be taken, by the Engine, when a debug message is received from the Vulkan API. + * * @param messageSeverity Severity of the debug message. * @param messageType Type of the debug message. * @param pCallbackData Pointer to the callback data containing the debug message. * @param pUserData User-defined data passed to the callback. - * + * * @since Karma 1.0.0 */ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -131,13 +197,16 @@ namespace Karma const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); + protected: // Vulkan instance handle and other Vulkan-specific members can be declared here uint32_t m_APIVersion; VkInstance m_VulkanInstance; - FVulkanDevice* m_Device; static bool bEnableValidationLayers; + VkDebugUtilsMessengerEXT m_DebugMessenger; + GLFWwindow* m_WindowHandle; + VkSurfaceKHR m_Surface; }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 04db1e45..d65ef074 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -1,6 +1,8 @@ #include "VulkanDynamicRHI.h" #include "GLFW/glfw3.h" +#include "Application.h" + namespace Karma { const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; @@ -16,8 +18,9 @@ namespace Karma KR_CORE_INFO("Initializing Vulkan RHI..."); CreateInstance(); - - // select device + SetupDebugMessenger(); + CreateSurface(); + SelectDevice(); } bool FVulkanDynamicRHI::Init() @@ -30,7 +33,8 @@ namespace Karma void FVulkanDynamicRHI::Shutdown() { // destroy device - + vkDestroySurfaceKHR(m_VulkanInstance, m_Surface, nullptr); + DestroyDebugUtilsMessengerEXT(m_VulkanInstance, m_DebugMessenger, nullptr); vkDestroyInstance(m_VulkanInstance, nullptr); KR_CORE_INFO("Vulkan RHI shutdown complete by destroying Vulkan Instance."); } @@ -95,6 +99,40 @@ namespace Karma KR_CORE_INFO("Vulkan Instance created successfully."); } + void FVulkanDynamicRHI::SetupDebugMessenger() + { + if (!bEnableValidationLayers) + { + return; + } + + VkDebugUtilsMessengerCreateInfoEXT createInfo; + PopulateDebugMessengerCreateInfo(createInfo); + + VkResult result = CreateDebugUtilsMessengerEXT(m_VulkanInstance, &createInfo, nullptr, &m_DebugMessenger); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to set up debug messenger!"); + } + + void FVulkanDynamicRHI::CreateSurface() + { + m_WindowHandle = static_cast(Application::Get().GetWindow().GetNativeWindow()); + VkResult result; + + if (m_WindowHandle != nullptr) + { + result = glfwCreateWindowSurface(m_VulkanInstance, m_WindowHandle, nullptr, &m_Surface); + } + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create window surface"); + } + + void FVulkanDynamicRHI::SelectDevice() + { + // Implementation for selecting a physical device (GPU) + + } + void FVulkanDynamicRHI::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) { createInfo = {}; @@ -222,4 +260,31 @@ namespace Karma return true; } + + VkResult FVulkanDynamicRHI::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) + { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (func) + { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } + else + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + + void FVulkanDynamicRHI::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) + { + if (!bEnableValidationLayers) + { + return; + } + + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) + { + func(instance, debugMessenger, pAllocator); + } + } } \ No newline at end of file From dc13f62e7888acd066c71295a08943ade1bcb309 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 17 Dec 2025 20:27:32 +0530 Subject: [PATCH 036/107] Logical device creation and destruction --- Karma/src/Platform/Vulkan/VulkanContext.cpp | 22 +- Karma/src/Platform/Vulkan/VulkanContext.h | 2 +- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 76 ++++++- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 18 +- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 146 ++++++++++++- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 199 +++++++++++++++++- 6 files changed, 440 insertions(+), 23 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 593d19b1..d2fc5bf9 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -12,9 +12,9 @@ namespace Karma { - const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; + const std::vector ValidationLayers = { "VK_LAYER_KHRONOS_validation" }; // Subject to change based on available hardware scrutiny - std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + std::vector DeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; #ifdef KR_DEBUG bool VulkanContext::bEnableValidationLayers = true; @@ -1129,13 +1129,13 @@ namespace Karma createInfo.pQueueCreateInfos = queueCreateInfos.data(); createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + createInfo.enabledExtensionCount = static_cast(DeviceExtensions.size()); + createInfo.ppEnabledExtensionNames = DeviceExtensions.data(); if (bEnableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); + createInfo.enabledLayerCount = static_cast(ValidationLayers.size()); + createInfo.ppEnabledLayerNames = ValidationLayers.data(); } else { @@ -1231,13 +1231,13 @@ namespace Karma { if (strcmp(anExtention.extensionName, "VK_KHR_portability_subset") != 0) { - deviceExtensions.push_back("VK_KHR_portability_subset"); + DeviceExtensions.push_back("VK_KHR_portability_subset"); break; } } #endif - std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + std::set requiredExtensions(DeviceExtensions.begin(), DeviceExtensions.end()); if (bEnableValidationLayers) { @@ -1365,8 +1365,8 @@ namespace Karma VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo; if (bEnableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); + createInfo.enabledLayerCount = static_cast(ValidationLayers.size()); + createInfo.ppEnabledLayerNames = ValidationLayers.data(); PopulateDebugMessengerCreateInfo(debugCreateInfo); createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)(&debugCreateInfo); @@ -1414,7 +1414,7 @@ namespace Karma std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - for (const char* layerName : validationLayers) + for (const char* layerName : ValidationLayers) { bool layerFound = false; diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index e85aa4cf..62d1e0dc 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -319,7 +319,7 @@ namespace Karma * Calls vkEnumerateDeviceExtensionProperties for list of supported extensions for instance VK_KHR_swapchain which is * required for, well, swapchain * - * @ + * @since Karma 1.0.0 */ bool CheckDeviceExtensionSupport(VkPhysicalDevice device); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index bf8f5538..421535da 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -1,13 +1,87 @@ #include "VulkanDevice.h" +#include "VulkanDynamicRHI.h" namespace Karma { - FVulkanDevice::FVulkanDevice(VkPhysicalDevice physicalDevice, VkInstance instance) + FVulkanDevice::FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu) : m_VulkanDynamicRHI(InRHI), + m_GPU(InGpu) { // Implementation for creating a Vulkan logical device } + FVulkanDevice::~FVulkanDevice() { // Implementation for cleaning up Vulkan device resources } + + void FVulkanDevice::Destroy() + { + // vkdestroy default buffers etc + + vkDestroyDevice(m_LogicalDevice, nullptr); + } + + void FVulkanDevice::InitGPU() + { + QueueFamilyIndices indices = m_VulkanDynamicRHI->FindQueueFamilies(m_GPU); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), + indices.presentFamily.value() }; + + if (m_VulkanDynamicRHI->GetValidationLayersSetting()) + { + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| Available Unique Queue Family Indices (Graphics Card):"); + uint32_t index = 1; + for (uint32_t queueFamily : uniqueQueueFamilies) + { + KR_CORE_INFO("| {0}. {1}", index++, queueFamily); + } + KR_CORE_INFO("+-------------------------------------------------"); + } + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) + { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures{}; + deviceFeatures.samplerAnisotropy = VK_TRUE; + if (m_VulkanDynamicRHI->GetGpuDeviceFeatures().logicOp) + { + deviceFeatures.logicOp = VK_TRUE; + } + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.enabledExtensionCount = static_cast(FVulkanDynamicRHI::deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = FVulkanDynamicRHI::deviceExtensions.data(); + + if (m_VulkanDynamicRHI->GetValidationLayersSetting()) + { + createInfo.enabledLayerCount = static_cast(FVulkanDynamicRHI::validationLayers.size()); + createInfo.ppEnabledLayerNames = FVulkanDynamicRHI::validationLayers.data(); + } + else + { + createInfo.enabledLayerCount = 0; + } + + VkResult result = vkCreateDevice(m_GPU, &createInfo, nullptr, &m_LogicalDevice); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create logical device!"); + + vkGetDeviceQueue(m_LogicalDevice, indices.graphicsFamily.value(), 0, &m_GraphicsQueue); + vkGetDeviceQueue(m_LogicalDevice, indices.presentFamily.value(), 0, &m_GraphicsQueue); + } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 30077e51..2d909d40 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -14,6 +14,8 @@ namespace Karma { + class FVulkanDynamicRHI; + /** * @class FVulkanDevice * @brief Manages Vulkan device resources and operations. @@ -27,14 +29,13 @@ namespace Karma /** * @brief Constructor for FVulkanDevice. * - * Initializes the Vulkan device with the specified physical device and instance. + * * - * @param physicalDevice The Vulkan physical device to create the logical device from. - * @param instance The Vulkan instance associated with this device. + * @param * * @since Karma 1.0.0 */ - FVulkanDevice(VkPhysicalDevice physicalDevice, VkInstance instance); + FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu); /** * @brief Destructor for FVulkanDevice. @@ -45,6 +46,10 @@ namespace Karma */ ~FVulkanDevice(); + void InitGPU(); + + void Destroy(); + /** * @brief Retrieves the Vulkan logical device handle. * @@ -54,6 +59,11 @@ namespace Karma private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. + FVulkanDynamicRHI* m_VulkanDynamicRHI; + + VkPhysicalDevice m_GPU; + VkQueue m_GraphicsQueue; + // Additional members for managing queues, command pools, etc. can be added here. }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 7c9262c5..ddf8ae96 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -25,6 +25,88 @@ namespace Karma return static_cast(GDynamicRHI); } + /** + * @brief A structure for graphics and present queuefamilies + * + * Most operations performed with Vulkan, like draw commands and memory operations, are + * asynchronously executed by submitting them to a VkQueue. Queues are allocated from queue + * families, where each queue family supports a specific set of operations in its queues. For example, + * there could be separate queue families for graphics, compute and memory transfer operations. + * + * Used for creating logical device, swapchain, and commandpool + * + * @see FVulkanDynamicRHI::FindQueueFamilies() + * @since Karma 1.0.0 + */ + struct QueueFamilyIndices + { + /** + * @brief The queues in this queue family support graphics operations. + * + * @note The optional is used to make the query of availibility easier + * @since Karma 1.0.0 + */ + std::optional graphicsFamily; + + /** + * @brief The queues in this queue family support image presentation + * + * The image is presented to the surface + * + * @note The optional is used to make the query of availibility easier + * @see VulkanContext::CreateSurface() + * + * @since Karma 1.0.0 + */ + std::optional presentFamily; + + /** + * @brief Routine for querying if appropriate queue families (graphicsFamily and presentFamily) are available. + * + * @see VulkanContext::IsDeviceSuitable + * @since Karma 1.0.0 + */ + bool IsComplete() + { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; + + /** + * @brief Structure with data required for appropriate creation and working of swapchain. + * + * Vulkan does not have the concept of a "default framebuffer", hence it requires an infrastructure that will own + * the buffers we will render to before we visualize them on the screen. This infrastructure is known as the swap chain + * and must be created explicitly in Vulkan. The swap chain is essentially a queue of images that are waiting to be + * presented to the screen. + * + * @since Karma 1.0.0 + */ + struct SwapChainSupportDetails + { + /** + * @brief Basic surface capabilities (min/max number of images in swap chain, min/max width + * and height of images) + * + * @since Karma 1.0.0 + */ + VkSurfaceCapabilitiesKHR capabilities; + + /** + * @brief Surface formats (pixel format, color space) + * + * @since Karma 1.0.0 + */ + std::vector formats; + + /** + * @brief Available presentation modes + * + * @since Karma 1.0.0 + */ + std::vector presentModes; + }; + class FVulkanDynamicRHI : public IVulkanDynamicRHI { public: @@ -41,6 +123,8 @@ namespace Karma */ virtual void Shutdown() override; + void InitInstance(); + /** * @brief Presents the rendered frame to the display. * @@ -58,6 +142,19 @@ namespace Karma return m_Device; } + /** + * @brief Queries the graphics card for available queue families and compares against the availability of graphics and + * presentation queues + * + * @param device The graphics card to be queired for queue family + * @since Karma 1.0.0 + */ + QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device); + + bool GetValidationLayersSetting() const { return bEnableValidationLayers; } + + const VkPhysicalDeviceFeatures& GetGpuDeviceFeatures() const{ return m_SupportedDeviceFeatures; } + protected: // Vulkan-specific members and methods can be added here @@ -88,10 +185,51 @@ namespace Karma */ void CreateSurface(); + void SelectDevice(); private: + /** + * @brief Uses Two-Pass Query to gather surface formats (physical device and surface paired color space or pixel format data) and present modes data. + * + * @see VulkanContext::IsDeviceSuitable(), and VulkanContext::CreateSwapChain() + * @since Karma 1.0.0 + */ + SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); + + /** + * @brief Looks for extension properties supported by the GPU + * + * Calls vkEnumerateDeviceExtensionProperties for list of supported extensions for instance VK_KHR_swapchain which is + * required for, well, swapchain + * + * @note These are different from instance extensions printed in PrintAvailableExtensions() + * + * @since Karma 1.0.0 + */ + bool CheckDeviceExtensionSupport(VkPhysicalDevice device); + + /** + * @brief Checks if the physical device (graphics card) is suitable for the Engine to use based on + * avaibality of required queue families (graphics and presentation), required device extensions (like VK_KHR_swapchain), sampler anisotropy support (which is + * anisotropic filtering support in samplers, allowing higher-quality texture sampling at oblique angles to reduce blurring and aliasing artifacts seen in standard bilinear filtering.) + * + * @param device The graphics card to be checked for suitability + * @since Karma 1.0.0 + */ + bool IsDeviceSuitable(VkPhysicalDevice device); + + /** + * @brief Prints all the available physical devices (graphics cards) supported by the system's Vulkan implementation. + * + * @param physicalDevices The list of GPUs detected + * + * @see FVulkanDynamicRHI::SelectDevice() + * @since Karma 1.0.0 + */ + void PrintAvailablePhysicalDevices(const std::vector& physicalDevices); + /** * @brief Creates a debug utils messenger for Vulkan instance. * @@ -129,7 +267,7 @@ namespace Karma const VkAllocationCallbacks* pAllocator); /** - * @brief Prints all the available extensions supported by the system's Vulkan implementation (VkInstance). + * @brief Prints all the available instance extensions supported by the system's Vulkan implementation (VkInstance). * * Vulkan extensions provide additional functionality beyond the core Vulkan API. * @@ -197,6 +335,10 @@ namespace Karma const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); + public: + // To be set manually by coder + static std::vector deviceExtensions; + static const std::vector validationLayers; protected: // Vulkan instance handle and other Vulkan-specific members can be declared here @@ -208,5 +350,7 @@ namespace Karma VkDebugUtilsMessengerEXT m_DebugMessenger; GLFWwindow* m_WindowHandle; VkSurfaceKHR m_Surface; + VkPhysicalDeviceFeatures m_SupportedDeviceFeatures; + VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;// GPU }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index d65ef074..733bf4bd 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -5,7 +5,8 @@ namespace Karma { - const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; + const std::vector FVulkanDynamicRHI::validationLayers = {"VK_LAYER_KHRONOS_validation"}; + std::vector FVulkanDynamicRHI:: deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; #ifdef KR_DEBUG bool FVulkanDynamicRHI::bEnableValidationLayers = true; @@ -26,16 +27,27 @@ namespace Karma bool FVulkanDynamicRHI::Init() { // initialize device + InitInstance(); + KR_CORE_INFO("Vulkan RHI initialized successfully."); return true; } + void FVulkanDynamicRHI::InitInstance() + { + m_Device->InitGPU(); + } + void FVulkanDynamicRHI::Shutdown() { - // destroy device + m_Device->Destroy(); vkDestroySurfaceKHR(m_VulkanInstance, m_Surface, nullptr); DestroyDebugUtilsMessengerEXT(m_VulkanInstance, m_DebugMessenger, nullptr); vkDestroyInstance(m_VulkanInstance, nullptr); + + delete m_Device; + m_Device = nullptr; + KR_CORE_INFO("Vulkan RHI shutdown complete by destroying Vulkan Instance."); } @@ -127,10 +139,187 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create window surface"); } - void FVulkanDynamicRHI::SelectDevice() + void FVulkanDynamicRHI::SelectDevice()// Pick physical device (GPU) and create Vulkan logical device { - // Implementation for selecting a physical device (GPU) - + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, nullptr); + + if (deviceCount == 0) + { + KR_CORE_ASSERT(false, "Failed to load GPU with Vulkan support"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, devices.data()); + + if (bEnableValidationLayers) + { + PrintAvailablePhysicalDevices(devices); + } + + for (const auto& device : devices) + { + if (IsDeviceSuitable(device)) + { + m_PhysicalDevice = device; + break; + } + } + + if (m_PhysicalDevice == VK_NULL_HANDLE) + { + KR_CORE_ASSERT(false, "Failed to find a suitable GPU!"); + } + + m_Device = new FVulkanDevice(this, m_PhysicalDevice); + } + + bool FVulkanDynamicRHI::IsDeviceSuitable(VkPhysicalDevice device) + { + QueueFamilyIndices indices = FindQueueFamilies(device); + + bool bExtensionsSupported = CheckDeviceExtensionSupport(device); + + bool swapChainAdequate = false; + if (bExtensionsSupported) + { + SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } + + vkGetPhysicalDeviceFeatures(device, &m_SupportedDeviceFeatures); + + return indices.IsComplete() && bExtensionsSupported && swapChainAdequate && m_SupportedDeviceFeatures.samplerAnisotropy; + } + + SwapChainSupportDetails FVulkanDynamicRHI::QuerySwapChainSupport(VkPhysicalDevice device) + { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_Surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_Surface, &formatCount, nullptr); + + if (formatCount != 0) + { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_Surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_Surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) + { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_Surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + // Check if all the required extensions are there + bool FVulkanDynamicRHI::CheckDeviceExtensionSupport(VkPhysicalDevice device) + { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + +#ifdef KR_MAC_PLATFORM + // Case by case query for required extensions + // One for MacOS: VK_KHR_portability_subset + for (auto anExtention : availableExtensions) + { + if (strcmp(anExtention.extensionName, "VK_KHR_portability_subset") != 0) + { + deviceExtensions.push_back("VK_KHR_portability_subset"); + break; + } + } +#endif + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + if (bEnableValidationLayers) + { + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| Available Device (GPU) Extensions:"); + uint32_t index = 1; + for (auto anExtension : availableExtensions) + { + KR_CORE_INFO("| {0}. {1}", index++, anExtension.extensionName); + } + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| Required Extensions (shall be enabled...):"); + index = 1; + for (auto swapchainExtension : requiredExtensions) + { + KR_CORE_INFO("| {0}. {1}", index++, swapchainExtension); + } + KR_CORE_INFO("+-------------------------------------------------"); + } + + for (const auto& extension : availableExtensions) + { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + QueueFamilyIndices FVulkanDynamicRHI::FindQueueFamilies(VkPhysicalDevice device) + { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) + { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, m_Surface, &presentSupport); + + if (presentSupport) + { + indices.presentFamily = i; + } + + if (indices.IsComplete()) + { + break; + } + + i++; + } + + return indices; + } + + void FVulkanDynamicRHI::PrintAvailablePhysicalDevices(const std::vector& physicalDevices) + { + uint32_t index = 1; + KR_CORE_INFO("+-------------------------------------------------"); + KR_CORE_INFO("| Available Graphics cards:"); + for (auto physicalDevice : physicalDevices) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + KR_CORE_INFO("| {0}. Device Name: {1}", index++, deviceProperties.deviceName); + } + KR_CORE_INFO("+-------------------------------------------------"); } void FVulkanDynamicRHI::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) From 7e969ac9e90e0eea14c03a793b40092eade51c3f Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 19 Dec 2025 09:01:26 +0530 Subject: [PATCH 037/107] Minor logging edits for better visuals in logs --- Karma/src/Karma/KarmaRHI/DynamicRHI.cpp | 7 +++++-- .../Platform/Mac/KarmaRHI/MacDynamicRHI.cpp | 2 +- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 3 ++- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 19 ++++++++++++++++--- .../Windows/KarmaRHI/WindowsDynamicRHI.cpp | 2 +- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp index 79fc2535..a8edcb60 100644 --- a/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.cpp @@ -25,6 +25,8 @@ namespace Karma { if (!GDynamicRHI) { + KR_CORE_INFO("Karma RHI initializing"); + GDynamicRHI = PlatformCreateDynamicRHI(); if (GDynamicRHI) { @@ -37,12 +39,13 @@ namespace Karma { if (GDynamicRHI != nullptr) { + KR_CORE_INFO("Shutting down the RHI"); GDynamicRHI->Shutdown(); delete GDynamicRHI; GDynamicRHI = nullptr; - } - KR_CORE_INFO("Deleted DynamicRHI object"); + KR_CORE_INFO("Karma RHI exodus"); + } } } \ No newline at end of file diff --git a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp index b7b59a52..5aa2dfb6 100644 --- a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp +++ b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp @@ -13,7 +13,7 @@ namespace Karma if (DynamicRHI) { - KR_CORE_INFO("############ Created DynamicRHI"); + KR_CORE_INFO("Created DynamicRHI MacOS"); } return DynamicRHI; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 421535da..505a6d0d 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -32,7 +32,7 @@ namespace Karma if (m_VulkanDynamicRHI->GetValidationLayersSetting()) { KR_CORE_INFO("+-------------------------------------------------"); - KR_CORE_INFO("| Available Unique Queue Family Indices (Graphics Card):"); + KR_CORE_INFO("| Available Unique Queue Family Indices (GPU):"); uint32_t index = 1; for (uint32_t queueFamily : uniqueQueueFamilies) { @@ -80,6 +80,7 @@ namespace Karma VkResult result = vkCreateDevice(m_GPU, &createInfo, nullptr, &m_LogicalDevice); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create logical device!"); + KR_CORE_INFO("Successfully created a Vulkan logical device"); vkGetDeviceQueue(m_LogicalDevice, indices.graphicsFamily.value(), 0, &m_GraphicsQueue); vkGetDeviceQueue(m_LogicalDevice, indices.presentFamily.value(), 0, &m_GraphicsQueue); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 733bf4bd..d7b0af84 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -48,7 +48,7 @@ namespace Karma delete m_Device; m_Device = nullptr; - KR_CORE_INFO("Vulkan RHI shutdown complete by destroying Vulkan Instance."); + KR_CORE_INFO("Vulkan RHI shutdown complete"); } void FVulkanDynamicRHI::CreateInstance() @@ -124,6 +124,7 @@ namespace Karma VkResult result = CreateDebugUtilsMessengerEXT(m_VulkanInstance, &createInfo, nullptr, &m_DebugMessenger); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to set up debug messenger!"); + KR_CORE_INFO("Vulkan RHI finished setting up Vulkan debug messenger with appropriate logging callback"); } void FVulkanDynamicRHI::CreateSurface() @@ -134,9 +135,14 @@ namespace Karma if (m_WindowHandle != nullptr) { result = glfwCreateWindowSurface(m_VulkanInstance, m_WindowHandle, nullptr, &m_Surface); - } + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create window surface"); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create window surface"); + KR_CORE_INFO("Vulkan RHI successfully created abstract surface to present rendered images"); + } + else + { + KR_CORE_ASSERT(false, "No GLFW window handle found for surface creation"); + } } void FVulkanDynamicRHI::SelectDevice()// Pick physical device (GPU) and create Vulkan logical device @@ -170,6 +176,13 @@ namespace Karma { KR_CORE_ASSERT(false, "Failed to find a suitable GPU!"); } + else + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(m_PhysicalDevice, &deviceProperties); + + KR_CORE_INFO("Physical Device (GPU) {0} has appropriate support for Karma Engine", deviceProperties.deviceName); + } m_Device = new FVulkanDevice(this, m_PhysicalDevice); } diff --git a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp index 21eea6a8..35f40bcb 100644 --- a/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp +++ b/Karma/src/Platform/Windows/KarmaRHI/WindowsDynamicRHI.cpp @@ -13,7 +13,7 @@ namespace Karma if (DynamicRHI) { - KR_CORE_INFO("############ Created DynamicRHI"); + KR_CORE_INFO("Created DynamicRHI for Windows OS"); } return DynamicRHI; From e9bb3df838a8debb0ec97934779851494de4eed5 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 19 Dec 2025 15:12:20 +0530 Subject: [PATCH 038/107] VulkanRHI Windows : Phase II --- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 24 ++++++++- Karma/src/Platform/Vulkan/VulkanBuffer.h | 13 +++++ .../Vulkan/VulkanRHI/VulkanDevice.cpp | 7 ++- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 23 ++++++++- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 2 +- Karma/src/Platform/Vulkan/VulkanTexture.cpp | 50 +++++++++++++++++-- Karma/src/Platform/Vulkan/VulkanTexture.h | 24 +++++++++ 7 files changed, 135 insertions(+), 8 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index ee5fb36a..354b0c1a 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -2,6 +2,7 @@ #include "Platform/Vulkan/VulkanHolder.h" #include "Karma/Renderer/RenderCommand.h" #include "Karma/KarmaUtilities.h" +#include "VulkanRHI/VulkanDevice.h" namespace Karma { @@ -378,6 +379,27 @@ namespace Karma stbi_image_free(pixels); } + VulkanImageBuffer::VulkanImageBuffer(FVulkanDevice* InDevice, const char* filename) + { + stbi_uc* pixels = KarmaUtilities::GetImagePixelData(filename, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + + // Need more consideration on image size + VkDeviceSize imageSize = texWidth * texHeight * 4; + + KR_CORE_ASSERT(pixels, "Failed to load textures image!"); + + m_Device = InDevice->GetLogicalDevice(); + m_PhysicalDevice = InDevice->GetGPU(); + + CreateBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_StagingBuffer, m_StagingBufferMemory); + void* data; + vkMapMemory(m_Device, m_StagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(m_Device, m_StagingBufferMemory); + + stbi_image_free(pixels); + } + VulkanImageBuffer::~VulkanImageBuffer() { vkDestroyBuffer(m_Device, m_StagingBuffer, nullptr); @@ -414,7 +436,7 @@ namespace Karma uint32_t VulkanImageBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(VulkanHolder::GetVulkanContext()->GetPhysicalDevice(), &memProperties); + vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProperties); for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index 3b73e418..e57e604d 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -14,6 +14,8 @@ namespace Karma { + class FVulkanDevice; + /** * @brief Vulkan's vertex buffer class. Vertex buffer is used in agnostic Mesh instance * @@ -397,6 +399,16 @@ namespace Karma */ VulkanImageBuffer(const char* filename); + /** + * @brief Creates GPU memory buffer for storing image texture with VulkanRHI support + * + * @param InDevice The FVulkanDevice object + * @param filename The path to the file, including filename, containing the image texture + * + * @since Karma 1.0.0 + */ + VulkanImageBuffer(FVulkanDevice* InDevice, const char* filename); + /** * @brief Frees up device resources * @@ -454,6 +466,7 @@ namespace Karma private: VkDevice m_Device; + VkPhysicalDevice m_PhysicalDevice; VkBuffer m_StagingBuffer; VkDeviceMemory m_StagingBufferMemory; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 505a6d0d..9ecb56dd 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -1,6 +1,8 @@ #include "VulkanDevice.h" #include "VulkanDynamicRHI.h" +#include "Vulkan/VulkanTexture.h" + namespace Karma { FVulkanDevice::FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu) : m_VulkanDynamicRHI(InRHI), @@ -17,7 +19,7 @@ namespace Karma void FVulkanDevice::Destroy() { // vkdestroy default buffers etc - + delete m_DefaultTexture; vkDestroyDevice(m_LogicalDevice, nullptr); } @@ -84,5 +86,8 @@ namespace Karma vkGetDeviceQueue(m_LogicalDevice, indices.graphicsFamily.value(), 0, &m_GraphicsQueue); vkGetDeviceQueue(m_LogicalDevice, indices.presentFamily.value(), 0, &m_GraphicsQueue); + + // Default texture (unreal grid) + m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 2d909d40..e36a571b 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -15,6 +15,7 @@ namespace Karma { class FVulkanDynamicRHI; + class VulkanTexture; /** * @class FVulkanDevice @@ -46,8 +47,19 @@ namespace Karma */ ~FVulkanDevice(); + /** + * @brief Creates the Vulkan logical device and default vulkan resources + * + * @note Called from FVulkanDynamicRHI::InitInstance() + * @since Karma 1.0.0 + */ void InitGPU(); + /** + * @brief Destroys the logical vkdevice by making Vulkan API call + * + * @since Karma 1.0.0 + */ void Destroy(); /** @@ -55,7 +67,14 @@ namespace Karma * * @return VkDevice The Vulkan logical device. */ - VkDevice GetLogicalDevice() const { m_LogicalDevice; } + VkDevice GetLogicalDevice() const { return m_LogicalDevice; } + + /** + * @biref Retrieves the Vulkan physical device handle. + * + * @return VkPhysicalDevice The Vulkan physical device (GPU) + */ + VkPhysicalDevice GetGPU() const { return m_GPU; } private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. @@ -64,6 +83,8 @@ namespace Karma VkPhysicalDevice m_GPU; VkQueue m_GraphicsQueue; + VulkanTexture* m_DefaultTexture; + // Additional members for managing queues, command pools, etc. can be added here. }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index d7b0af84..0fe4259e 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -21,7 +21,7 @@ namespace Karma CreateInstance(); SetupDebugMessenger(); CreateSurface(); - SelectDevice(); + SelectDevice();// physical device selection and logical device creation } bool FVulkanDynamicRHI::Init() diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.cpp b/Karma/src/Platform/Vulkan/VulkanTexture.cpp index 5d8f8325..9e383059 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.cpp +++ b/Karma/src/Platform/Vulkan/VulkanTexture.cpp @@ -1,6 +1,8 @@ #include "VulkanTexture.h" #include "VulkanHolder.h" +#include "VulkanRHI/VulkanDevice.h" + namespace Karma { VulkanTexture::VulkanTexture() @@ -9,6 +11,29 @@ namespace Karma m_PhysicalDevice = VulkanHolder::GetVulkanContext()->GetPhysicalDevice(); } + VulkanTexture::VulkanTexture(FVulkanDevice* InDevice, const char* filename) : m_TextureImage(VK_NULL_HANDLE), + m_TextureImageMemory(VK_NULL_HANDLE), m_TextureImageView(VK_NULL_HANDLE), m_TextureSampler(VK_NULL_HANDLE) + { + m_Device = InDevice->GetLogicalDevice(); + m_PhysicalDevice = InDevice->GetGPU(); + + /*m_TextureImage = VK_NULL_HANDLE; + m_TextureImageMemory = VK_NULL_HANDLE; + m_TextureImageView = VK_NULL_HANDLE; + m_TextureSampler = VK_NULL_HANDLE;*/ + + VulkanImageBuffer* vImageBuffer = new VulkanImageBuffer(InDevice, filename); + if (vImageBuffer != nullptr) + { + GenerateVulkanTexture(vImageBuffer); + } + else + { + KR_CORE_WARN("Couldn't load the image texture file {0}", filename); + } + delete vImageBuffer; + } + VulkanTexture::~VulkanTexture() { vkDestroySampler(m_Device, m_TextureSampler, nullptr); @@ -50,15 +75,32 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = VulkanHolder::GetVulkanContext()->FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VkResult result1 = vkAllocateMemory(m_Device, &allocInfo, nullptr, &m_TextureImageMemory); KR_CORE_ASSERT(result1 == VK_SUCCESS, "Failed to allocate image memeory"); vkBindImageMemory(m_Device, m_TextureImage, m_TextureImageMemory, 0); - VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - VulkanHolder::GetVulkanContext()->CopyBufferToImage(vImageBuffer->GetBuffer(), m_TextureImage, static_cast(vImageBuffer->GetTextureWidth()), static_cast(vImageBuffer->GetTextureHeight())); - VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + //VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + //VulkanHolder::GetVulkanContext()->CopyBufferToImage(vImageBuffer->GetBuffer(), m_TextureImage, static_cast(vImageBuffer->GetTextureWidth()), static_cast(vImageBuffer->GetTextureHeight())); + //VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + uint32_t VulkanTexture::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) + { + if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + return i; + } + } + + KR_CORE_ASSERT(false, "Failed to find suitable memory type for imagebuffer"); + return 0; } void VulkanTexture::CreateTextureImageView() diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.h b/Karma/src/Platform/Vulkan/VulkanTexture.h index d2e47202..c1ef9e6e 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.h +++ b/Karma/src/Platform/Vulkan/VulkanTexture.h @@ -14,6 +14,8 @@ namespace Karma { + class FVulkanDevice; + /** * @brief Vulkan specific implementation of Texture class. * @@ -34,6 +36,17 @@ namespace Karma */ VulkanTexture(); + /** + * @brief Constructor with VulkanRHI support + * + * @brief InDevice The FVulkanDevice object + * @brief filename The location of the texture file along with location + * + * @see FVulkanDevice::InitGPU() + * @since Karma 1.0.0 + */ + VulkanTexture(FVulkanDevice* InDevice, const char* filename); + /** * @brief A destructor * @@ -92,6 +105,17 @@ namespace Karma * @since Karma 1.0.0 */ void GenerateVulkanTexture(VulkanImageBuffer* vImageBuffer); + + /** + * @brief Finds a suitable memory type on the physical device (graphics card) based on the type filter and desired properties. + * + * @param typeFilter Bitmask specifying the acceptable memory types + * @param properties Desired memory properties (like VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT for CPU access) + * + * @see VulkanBuffer::VulkanBuffer() + * @since Karma 1.0.0 + */ + uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); // Getters VkImageView GetImageView() const { return m_TextureImageView; } From f51d60e0cd8f74917b4aafd53322e090fdc0d4d9 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 19 Dec 2025 17:28:21 +0530 Subject: [PATCH 039/107] Modified VulkanTexture in accordance with VulkanRHI --- Karma/src/Platform/Vulkan/VulkanContext.cpp | 20 --- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 170 +++++++++++++++++- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 46 ++++- Karma/src/Platform/Vulkan/VulkanTexture.cpp | 43 +++-- Karma/src/Platform/Vulkan/VulkanTexture.h | 7 +- 5 files changed, 239 insertions(+), 47 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index d2fc5bf9..6771f955 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -857,26 +857,6 @@ namespace Karma vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer); } - /* - void VulkanContext::CreateTextureImageView() - { - VkImageViewCreateInfo viewInfo{}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = m_TextureImage; - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - - VkResult result = vkCreateImageView(m_device, &viewInfo, nullptr, &m_TextureImageView); - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create texture image view"); - } - */ - void VulkanContext::CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { VkCommandBufferAllocateInfo allocInfo{}; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 9ecb56dd..03f17e95 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -20,6 +20,7 @@ namespace Karma { // vkdestroy default buffers etc delete m_DefaultTexture; + vkDestroyCommandPool(m_LogicalDevice, m_CommandPool, nullptr); vkDestroyDevice(m_LogicalDevice, nullptr); } @@ -85,9 +86,176 @@ namespace Karma KR_CORE_INFO("Successfully created a Vulkan logical device"); vkGetDeviceQueue(m_LogicalDevice, indices.graphicsFamily.value(), 0, &m_GraphicsQueue); - vkGetDeviceQueue(m_LogicalDevice, indices.presentFamily.value(), 0, &m_GraphicsQueue); + vkGetDeviceQueue(m_LogicalDevice, indices.presentFamily.value(), 0, &m_PresentQueue); + + // CommandPool (UE has FVulkanDescriptorPoolsManager class for managing commandpools) + QueueFamilyIndices queueFamilyIndices = m_VulkanDynamicRHI->FindQueueFamilies(m_GPU); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + result = vkCreateCommandPool(m_LogicalDevice, &poolInfo, nullptr, &m_CommandPool); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create command pool!"); + + KR_CORE_INFO("Created Vulkan commandpool"); // Default texture (unreal grid) m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); } + + void FVulkanDevice::TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) + { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = m_CommandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(m_LogicalDevice, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && HasStencilComponent(format)) + { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + else + { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; // TODO + barrier.dstAccessMask = 0; // TODO + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) + { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + } + else + { + KR_CORE_ASSERT(false, "Unsupported layout transition!"); + } + + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(m_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(m_GraphicsQueue); + + vkFreeCommandBuffers(m_LogicalDevice, m_CommandPool, 1, &commandBuffer); + } + + void FVulkanDevice::CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) + { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = m_CommandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(m_LogicalDevice, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = { 0, 0, 0 }; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage( + commandBuffer, + buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion + ); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(m_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(m_GraphicsQueue); + + vkFreeCommandBuffers(m_LogicalDevice, m_CommandPool, 1, &commandBuffer); + } + + bool FVulkanDevice::HasStencilComponent(VkFormat format) + { + return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; + } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index e36a571b..e8bb6326 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -76,15 +76,59 @@ namespace Karma */ VkPhysicalDevice GetGPU() const { return m_GPU; } + ///////////////// Utility Functions ///////////////// + /** + * @brief Transitions the layout of an image from oldLayout to newLayout. + * + * Image layout transitions are crucial in Vulkan to ensure that images are in the correct state for different operations, such as rendering, sampling, or transferring data. + * + * @param image The image to be transitioned + * @param format The format of the image + * @param oldLayout The current layout of the image + * @param newLayout The desired layout of the image + * + * @see VulkanTexture::CreateTextureImage() + * @since Karma 1.0.0 + */ + void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout); + + /** + * @brief Copies data from a buffer to a Vulkan image. + * + * This is typically used for uploading texture data from a staging buffer to a Vulkan image. + * + * @param buffer The source buffer containing the data + * @param image The destination image + * @param width The width of the image + * @param height The height of the image + * + * @see VulkanTexture::CreateTextureImage() + * @since Karma 1.0.0 + */ + void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height); + + /** + * @brief Checks if the given format has a stencil component. + * + * Sees if the format is VK_FORMAT_D32_SFLOAT_S8_UINT or VK_FORMAT_D24_UNORM_S8_UINT + * + * @param format The format to be checked + * + * @see VulkanContext::TransitionImageLayout() + * @since Karma 1.0.0 + */ + bool HasStencilComponent(VkFormat format); + private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. FVulkanDynamicRHI* m_VulkanDynamicRHI; VkPhysicalDevice m_GPU; VkQueue m_GraphicsQueue; - + VkQueue m_PresentQueue; VulkanTexture* m_DefaultTexture; // Additional members for managing queues, command pools, etc. can be added here. + VkCommandPool m_CommandPool; }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.cpp b/Karma/src/Platform/Vulkan/VulkanTexture.cpp index 9e383059..47fa992c 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.cpp +++ b/Karma/src/Platform/Vulkan/VulkanTexture.cpp @@ -7,20 +7,17 @@ namespace Karma { VulkanTexture::VulkanTexture() { - m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); - m_PhysicalDevice = VulkanHolder::GetVulkanContext()->GetPhysicalDevice(); + m_LogicalDevice = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); + m_GPU = VulkanHolder::GetVulkanContext()->GetPhysicalDevice(); } VulkanTexture::VulkanTexture(FVulkanDevice* InDevice, const char* filename) : m_TextureImage(VK_NULL_HANDLE), m_TextureImageMemory(VK_NULL_HANDLE), m_TextureImageView(VK_NULL_HANDLE), m_TextureSampler(VK_NULL_HANDLE) { - m_Device = InDevice->GetLogicalDevice(); - m_PhysicalDevice = InDevice->GetGPU(); + m_Device = InDevice; - /*m_TextureImage = VK_NULL_HANDLE; - m_TextureImageMemory = VK_NULL_HANDLE; - m_TextureImageView = VK_NULL_HANDLE; - m_TextureSampler = VK_NULL_HANDLE;*/ + m_LogicalDevice = InDevice->GetLogicalDevice(); + m_GPU = InDevice->GetGPU(); VulkanImageBuffer* vImageBuffer = new VulkanImageBuffer(InDevice, filename); if (vImageBuffer != nullptr) @@ -36,10 +33,10 @@ namespace Karma VulkanTexture::~VulkanTexture() { - vkDestroySampler(m_Device, m_TextureSampler, nullptr); - vkDestroyImageView(m_Device, m_TextureImageView, nullptr); - vkDestroyImage(m_Device, m_TextureImage, nullptr); - vkFreeMemory(m_Device, m_TextureImageMemory, nullptr); + vkDestroySampler(m_LogicalDevice, m_TextureSampler, nullptr); + vkDestroyImageView(m_LogicalDevice, m_TextureImageView, nullptr); + vkDestroyImage(m_LogicalDevice, m_TextureImage, nullptr); + vkFreeMemory(m_LogicalDevice, m_TextureImageMemory, nullptr); } void VulkanTexture::GenerateVulkanTexture(VulkanImageBuffer* vImageBuffer) @@ -66,30 +63,30 @@ namespace Karma imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.flags = 0; - VkResult result = vkCreateImage(m_Device, &imageInfo, nullptr, &m_TextureImage); + VkResult result = vkCreateImage(m_LogicalDevice, &imageInfo, nullptr, &m_TextureImage); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create image!"); VkMemoryRequirements memRequirements; - vkGetImageMemoryRequirements(m_Device, m_TextureImage, &memRequirements); + vkGetImageMemoryRequirements(m_LogicalDevice, m_TextureImage, &memRequirements); VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VkResult result1 = vkAllocateMemory(m_Device, &allocInfo, nullptr, &m_TextureImageMemory); + VkResult result1 = vkAllocateMemory(m_LogicalDevice, &allocInfo, nullptr, &m_TextureImageMemory); KR_CORE_ASSERT(result1 == VK_SUCCESS, "Failed to allocate image memeory"); - vkBindImageMemory(m_Device, m_TextureImage, m_TextureImageMemory, 0); + vkBindImageMemory(m_LogicalDevice, m_TextureImage, m_TextureImageMemory, 0); - //VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - //VulkanHolder::GetVulkanContext()->CopyBufferToImage(vImageBuffer->GetBuffer(), m_TextureImage, static_cast(vImageBuffer->GetTextureWidth()), static_cast(vImageBuffer->GetTextureHeight())); - //VulkanHolder::GetVulkanContext()->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + m_Device->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + m_Device->CopyBufferToImage(vImageBuffer->GetBuffer(), m_TextureImage, static_cast(vImageBuffer->GetTextureWidth()), static_cast(vImageBuffer->GetTextureHeight())); + m_Device->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } uint32_t VulkanTexture::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProperties); + vkGetPhysicalDeviceMemoryProperties(m_GPU, &memProperties); for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { @@ -116,7 +113,7 @@ namespace Karma viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - VkResult result = vkCreateImageView(m_Device, &viewInfo, nullptr, &m_TextureImageView); + VkResult result = vkCreateImageView(m_LogicalDevice, &viewInfo, nullptr, &m_TextureImageView); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create texture image view"); } @@ -133,7 +130,7 @@ namespace Karma samplerInfo.anisotropyEnable = VK_TRUE; VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(m_PhysicalDevice, &properties); + vkGetPhysicalDeviceProperties(m_GPU, &properties); samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; @@ -145,7 +142,7 @@ namespace Karma samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 0.0f; - VkResult result = vkCreateSampler(m_Device, &samplerInfo, nullptr, &m_TextureSampler); + VkResult result = vkCreateSampler(m_LogicalDevice, &samplerInfo, nullptr, &m_TextureSampler); KR_CORE_ASSERT(result == false, "Failed to create texture sampler!"); } diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.h b/Karma/src/Platform/Vulkan/VulkanTexture.h index c1ef9e6e..0cee25a0 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.h +++ b/Karma/src/Platform/Vulkan/VulkanTexture.h @@ -122,9 +122,12 @@ namespace Karma VkSampler GetImageSampler() const { return m_TextureSampler; } private: + // VulkanRHI stuff + FVulkanDevice* m_Device; + // Vulkan context relevant stuff - VkDevice m_Device; - VkPhysicalDevice m_PhysicalDevice; + VkDevice m_LogicalDevice; + VkPhysicalDevice m_GPU; // Texture relevant stuff VkImage m_TextureImage; From 48d86389914dcaa513dcd059b2afe7028adb8438 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 20 Dec 2025 06:40:04 +0530 Subject: [PATCH 040/107] More doxygen commenting for VulkanDynamicRHI --- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index ddf8ae96..1883f290 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -110,19 +110,60 @@ namespace Karma class FVulkanDynamicRHI : public IVulkanDynamicRHI { public: + /** + * @brief Getter for the FVulkanDynamicRHI instance + * + * @since Karma 1.0.0 + */ static FVulkanDynamicRHI& Get() { return *GetDynamicRHI(); } - /** Initialization constructor. */ + /** + * @brief Initialization constructor. + * + * Does the following + * 1. Creates vkInstance + * 2. Sets up debug messenger (with Karma's logging callback function) + * 3. Creates a Vulkan surface to interface with the Engine's window (Editor window for instance) + * 4. Selects a physical device (GPU) based upon the availability of device extensions, swapchain support, + * and samplerAnisotropy + * 5. Creates Vulkan logical device + * + * @note Smapler anisotorpy is a technique that makes textures on surfaces viewed at steep angles (like roads or floors + * stretching into the distance) look clearer and sharper, preventing blurriness by taking multiple samples along the + * texture's axis of elongation rather than a single one. + * + * @since Karma 1.0.0 + */ FVulkanDynamicRHI(); + /** + * @brief For initializing VulkanDynamic RHI + * + * Called from RHIInit() declared in KarmaRHI.h and defined in DynamicRHI.cpp + * + * @see FVulkanDynamicRHI::InitInstance() + * + * @since Karma 1.0.0 + */ virtual bool Init() override; + /** * @brief Shuts down the RHI. - * + * + * Called from RHIExit() declared in KarmaRHI.h and defined in DynamicRHI.cpp + * * Cleans up resources and states used by the RHI. + * @since Karma 1.0.0 */ virtual void Shutdown() override; + /** + * @brief Calls FVulkanDevice::InitGPU() to create Vulkan logical device from + * selected GPU, and created appropriate resources (commandpool, default texture etc). + * + * @see FVulkanDevice::InitGPU() + * @since Karma 1.0.0 + */ void InitInstance(); /** @@ -132,11 +173,21 @@ namespace Karma */ virtual void Present() override {} + /** + * @brief Getter for Vulkan instance + * + * @since Karma 1.0.0 + */ inline VkInstance GetInstance() const { return m_VulkanInstance; } + /** + * @brief Getter for FVulkanDevice object created in FVulkanDynamicRHI::SelectDevice() + * + * @since Karma 1.0.0 + */ inline FVulkanDevice* GetDevice() const { return m_Device; @@ -150,9 +201,22 @@ namespace Karma * @since Karma 1.0.0 */ QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device); - + + /** + * @brief Getter for the boolean bEnableValidationLayers + * + * For Debug builds bEnableValidationLayers is set to true + * + * @since Karma 1.0.0 + */ bool GetValidationLayersSetting() const { return bEnableValidationLayers; } + /** + * @brief Getter for m_SupportedDeviceFeatures which contains boolean values for supported features like + * availability of geometry shader or sampler anisotropy + * + * @since Karma 1.0.0 + */ const VkPhysicalDeviceFeatures& GetGpuDeviceFeatures() const{ return m_SupportedDeviceFeatures; } protected: @@ -185,7 +249,11 @@ namespace Karma */ void CreateSurface(); - + /** + * @brief Picks an Engine appropriate GPU and creates FVulkanDevice + * + * @since Karma 1.0.0 + */ void SelectDevice(); private: From 9186996d6109f85c2c910437eb9dcc2d2269d167 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 20 Dec 2025 13:31:10 +0530 Subject: [PATCH 041/107] VulkanRHI Windows : Phase III --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 16 +++++----- Karma/src/Platform/Vulkan/VulkanContext.cpp | 20 ++++++------- Karma/src/Platform/Vulkan/VulkanContext.h | 8 ++--- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 9 ++++++ .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 30 +++++++++++++++++-- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 16 ++++++++-- .../Vulkan/VulkanRHI/VulkanSwapChain.cpp | 0 .../Vulkan/VulkanRHI/VulkanSwapChain.h | 19 ++++++++++++ 8 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index d4c1c834..0212a443 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -4,6 +4,7 @@ #include "Renderer/RenderCommand.h" #include "StaticMeshActor.h" #include "Platform/Vulkan/VulkanVertexArray.h" +#include "VulkanRHI/VulkanDynamicRHI.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -27,15 +28,14 @@ namespace Karma KarmaGui_ImplGlfw_InitForVulkan(window, true); KarmaGui_ImplVulkan_InitInfo initInfo = {}; - // An inter-class communication - initInfo.Instance = VulkanHolder::GetVulkanContext()->GetInstance(); - initInfo.PhysicalDevice = VulkanHolder::GetVulkanContext()->GetPhysicalDevice(); - initInfo.Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); - initInfo.QueueFamily = VulkanHolder::GetVulkanContext()->FindQueueFamilies(initInfo.PhysicalDevice).graphicsFamily.value(); - initInfo.Queue = VulkanHolder::GetVulkanContext()->GetGraphicsQueue(); + initInfo.Instance = FVulkanDynamicRHI::Get().GetInstance(); + initInfo.PhysicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetGPU(); + initInfo.Device = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); + initInfo.QueueFamily = FVulkanDynamicRHI::Get().FindQueueFamilies(initInfo.PhysicalDevice).graphicsFamily.value(); + initInfo.Queue = FVulkanDynamicRHI::Get().GetDevice()->GetGraphicsQueue(); initInfo.PipelineCache = VK_NULL_HANDLE; - initInfo.MinImageCount = VulkanHolder::GetVulkanContext()->GetMinImageCount(); - initInfo.ImageCount = VulkanHolder::GetVulkanContext()->GetImageCount(); + initInfo.MinImageCount = FVulkanDynamicRHI::Get().GetGpuSwapChainSupportDetails().capabilities.minImageCount; + initInfo.ImageCount = FVulkanDynamicRHI::Get().SwapChainImageCount(); initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT; // Stuff created and dedicated to KarmaGui diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 6771f955..4725529f 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -915,7 +915,7 @@ namespace Karma void VulkanContext::CreateCommandPool() { - QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(m_physicalDevice); + QueueFamilyIndicesDepricated queueFamilyIndices = FindQueueFamilies(m_physicalDevice); VkCommandPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -971,7 +971,7 @@ namespace Karma void VulkanContext::CreateSwapChain() { - SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(m_physicalDevice); + SwapChainSupportDetailsDepricated swapChainSupport = QuerySwapChainSupport(m_physicalDevice); // KarmaGui may have, MAY, different requirements. m_surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats); @@ -997,7 +997,7 @@ namespace Karma createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - QueueFamilyIndices indices = FindQueueFamilies(m_physicalDevice); + QueueFamilyIndicesDepricated indices = FindQueueFamilies(m_physicalDevice); uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; @@ -1068,7 +1068,7 @@ namespace Karma void VulkanContext::CreateLogicalDevice() { - QueueFamilyIndices indices = FindQueueFamilies(m_physicalDevice); + QueueFamilyIndicesDepricated indices = FindQueueFamilies(m_physicalDevice); std::vector queueCreateInfos; std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), @@ -1179,14 +1179,14 @@ namespace Karma bool VulkanContext::IsDeviceSuitable(VkPhysicalDevice device) { - QueueFamilyIndices indices = FindQueueFamilies(device); + QueueFamilyIndicesDepricated indices = FindQueueFamilies(device); bool bExtensionsSupported = CheckDeviceExtensionSupport(device); bool swapChainAdequate = false; if (bExtensionsSupported) { - SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device); + SwapChainSupportDetailsDepricated swapChainSupport = QuerySwapChainSupport(device); swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } @@ -1247,9 +1247,9 @@ namespace Karma return requiredExtensions.empty(); } - QueueFamilyIndices VulkanContext::FindQueueFamilies(VkPhysicalDevice device) + QueueFamilyIndicesDepricated VulkanContext::FindQueueFamilies(VkPhysicalDevice device) { - QueueFamilyIndices indices; + QueueFamilyIndicesDepricated indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); @@ -1284,9 +1284,9 @@ namespace Karma return indices; } - SwapChainSupportDetails VulkanContext::QuerySwapChainSupport(VkPhysicalDevice device) + SwapChainSupportDetailsDepricated VulkanContext::QuerySwapChainSupport(VkPhysicalDevice device) { - SwapChainSupportDetails details; + SwapChainSupportDetailsDepricated details; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_surface, &details.capabilities); diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index 62d1e0dc..c558c9f1 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -55,7 +55,7 @@ namespace Karma * @see VulkanContext::FindQueueFamilies() * @since Karma 1.0.0 */ - struct QueueFamilyIndices + struct QueueFamilyIndicesDepricated { /** * @brief The queues in this queue family support graphics operations. @@ -99,7 +99,7 @@ namespace Karma * * @since Karma 1.0.0 */ - struct SwapChainSupportDetails + struct SwapChainSupportDetailsDepricated { /** * @brief Basic surface capabilities (min/max number of images in swap chain, min/max width @@ -284,7 +284,7 @@ namespace Karma * @param device The graphics card to be queired for queue family * @since Karma 1.0.0 */ - QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device); + QueueFamilyIndicesDepricated FindQueueFamilies(VkPhysicalDevice device); /** * @brief The so called logical device for interfacing with the physical device. All the machinery (swapchain, graphicspipeline, and all that) are created from logical device. Following is done: @@ -329,7 +329,7 @@ namespace Karma * @see VulkanContext::IsDeviceSuitable(), and VulkanContext::CreateSwapChain() * @since Karma 1.0.0 */ - SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); + SwapChainSupportDetailsDepricated QuerySwapChainSupport(VkPhysicalDevice device); /** * @brief Chooses the best surface format (pixel format and color space) for the swapchain from the available formats. diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index e8bb6326..0fdfa463 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -119,6 +119,15 @@ namespace Karma */ bool HasStencilComponent(VkFormat format); + ///////////////// Getters ///////////////// + + /** + * @brief Getter for the graphics queue created in FVulkanDevice::InitGPU() + * + * @since Karma 1.0.0 + */ + inline VkQueue GetGraphicsQueue() const { return m_GraphicsQueue; } + private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. FVulkanDynamicRHI* m_VulkanDynamicRHI; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 1883f290..47501bc1 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -209,7 +209,7 @@ namespace Karma * * @since Karma 1.0.0 */ - bool GetValidationLayersSetting() const { return bEnableValidationLayers; } + inline bool GetValidationLayersSetting() const { return bEnableValidationLayers; } /** * @brief Getter for m_SupportedDeviceFeatures which contains boolean values for supported features like @@ -217,7 +217,21 @@ namespace Karma * * @since Karma 1.0.0 */ - const VkPhysicalDeviceFeatures& GetGpuDeviceFeatures() const{ return m_SupportedDeviceFeatures; } + inline const VkPhysicalDeviceFeatures& GetGpuDeviceFeatures() const{ return m_SupportedDeviceFeatures; } + + /** + * @brief Getter for m_GPUSwapChainSupport struct which gets initialized in FVulkanDynamicRHI::IsDeviceSuitable() + * + * @since Karma 1.0.0 + */ + inline const SwapChainSupportDetails& GetGpuSwapChainSupportDetails() const { return m_GPUSwapChainSupport; } + + /** + * @brief Getter for the number of supported swapchain images + * + * @since Karma 1.0.0 + */ + inline uint32_t SwapChainImageCount() const { return m_SwapChainImageCount; } protected: // Vulkan-specific members and methods can be added here @@ -258,6 +272,16 @@ namespace Karma private: + /** + * @brief Computes the number of images supported by the GPU for swapchain creation + * + * Makes sure that the m_SwapChainImageCount remains is in the interval [capabilities.minImageCount + 1, + * capabilities.maxImageCount]. + * + * @since Karma 1.0.0 + */ + void ComputeNumberOfSwapchainImagesSupported(); + /** * @brief Uses Two-Pass Query to gather surface formats (physical device and surface paired color space or pixel format data) and present modes data. * @@ -420,5 +444,7 @@ namespace Karma VkSurfaceKHR m_Surface; VkPhysicalDeviceFeatures m_SupportedDeviceFeatures; VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;// GPU + SwapChainSupportDetails m_GPUSwapChainSupport; + uint32_t m_SwapChainImageCount; }; } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 0fe4259e..c5163f8e 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -145,6 +145,16 @@ namespace Karma } } + void FVulkanDynamicRHI::ComputeNumberOfSwapchainImagesSupported() + { + m_SwapChainImageCount = m_GPUSwapChainSupport.capabilities.minImageCount + 1; + + if (m_GPUSwapChainSupport.capabilities.maxImageCount > 0 && m_SwapChainImageCount > m_GPUSwapChainSupport.capabilities.maxImageCount) + { + m_SwapChainImageCount = m_GPUSwapChainSupport.capabilities.maxImageCount; + } + } + void FVulkanDynamicRHI::SelectDevice()// Pick physical device (GPU) and create Vulkan logical device { uint32_t deviceCount = 0; @@ -168,6 +178,8 @@ namespace Karma if (IsDeviceSuitable(device)) { m_PhysicalDevice = device; + ComputeNumberOfSwapchainImagesSupported(); + break; } } @@ -196,8 +208,8 @@ namespace Karma bool swapChainAdequate = false; if (bExtensionsSupported) { - SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device); - swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + m_GPUSwapChainSupport = QuerySwapChainSupport(device); + swapChainAdequate = !m_GPUSwapChainSupport.formats.empty() && !m_GPUSwapChainSupport.presentModes.empty(); } vkGetPhysicalDeviceFeatures(device, &m_SupportedDeviceFeatures); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h new file mode 100644 index 00000000..42817353 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -0,0 +1,19 @@ +/** + * @file VulkanSwapChain.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date 20 December, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +namespace Karma +{ + class FVulkanSwapChain + { + + }; +} \ No newline at end of file From 308c6c67187790bd7867ac5832cc4b3deaeaec36 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 20 Dec 2025 14:38:33 +0530 Subject: [PATCH 042/107] FVulkanSwapChain's swapchain making/creation --- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 2 + .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 20 +-- .../Vulkan/VulkanRHI/VulkanSwapChain.cpp | 125 ++++++++++++++++++ .../Vulkan/VulkanRHI/VulkanSwapChain.h | 71 ++++++++++ 4 files changed, 210 insertions(+), 8 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 0fdfa463..debc5790 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -128,6 +128,8 @@ namespace Karma */ inline VkQueue GetGraphicsQueue() const { return m_GraphicsQueue; } + inline FVulkanDynamicRHI* GetVulkanDynamicRHI() const { return m_VulkanDynamicRHI; } + private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. FVulkanDynamicRHI* m_VulkanDynamicRHI; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 47501bc1..88199797 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -173,6 +173,14 @@ namespace Karma */ virtual void Present() override {} + /** + * @brief Uses Two-Pass Query to gather surface formats (physical device and surface paired color space or pixel format data) and present modes data. + * + * @see VulkanContext::IsDeviceSuitable(), and VulkanContext::CreateSwapChain() + * @since Karma 1.0.0 + */ + SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); + /** * @brief Getter for Vulkan instance * @@ -233,6 +241,10 @@ namespace Karma */ inline uint32_t SwapChainImageCount() const { return m_SwapChainImageCount; } + inline GLFWwindow* GetSurfaceWindow() const { return m_WindowHandle; } + + inline VkSurfaceKHR GetSurface() const { return m_Surface; } + protected: // Vulkan-specific members and methods can be added here @@ -282,14 +294,6 @@ namespace Karma */ void ComputeNumberOfSwapchainImagesSupported(); - /** - * @brief Uses Two-Pass Query to gather surface formats (physical device and surface paired color space or pixel format data) and present modes data. - * - * @see VulkanContext::IsDeviceSuitable(), and VulkanContext::CreateSwapChain() - * @since Karma 1.0.0 - */ - SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); - /** * @brief Looks for extension properties supported by the GPU * diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp index e69de29b..1ebe01a3 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp @@ -0,0 +1,125 @@ +#include "VulkanSwapChain.h" +#include "VulkanRHI/VulkanDynamicRHI.h" + +#include "GLFW/glfw3.h" + +namespace Karma +{ + FVulkanSwapChain* FVulkanSwapChain::Create(FVulkanDevice* InDevice) + { + FVulkanSwapChain* swapChain = new FVulkanSwapChain(InDevice); + + return swapChain; + } + + FVulkanSwapChain::FVulkanSwapChain(FVulkanDevice* InDevice) : m_Instance(InDevice->GetVulkanDynamicRHI()->GetInstance()) + { + m_WindowHandle = InDevice->GetVulkanDynamicRHI()->GetSurfaceWindow(); + + SwapChainSupportDetails swapChainSupport = InDevice->GetVulkanDynamicRHI()->QuerySwapChainSupport(InDevice->GetGPU()); + + // KarmaGui may have, MAY, different requirements. + m_SurfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats); + m_PresentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);// Analogous to v-sync + + VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = InDevice->GetVulkanDynamicRHI()->SwapChainImageCount(); + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = InDevice->GetVulkanDynamicRHI()->GetSurface(); + createInfo.minImageCount = imageCount; + createInfo.imageFormat = m_SurfaceFormat.format; + createInfo.imageColorSpace = m_SurfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = InDevice->GetVulkanDynamicRHI()->FindQueueFamilies(InDevice->GetGPU()); + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), + indices.presentFamily.value() }; + + if (indices.graphicsFamily != indices.presentFamily) + { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else + { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = m_PresentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + VkResult result = vkCreateSwapchainKHR(InDevice->GetLogicalDevice(), &createInfo, nullptr, &m_SwapChain); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create swapchain!"); + + vkGetSwapchainImagesKHR(InDevice->GetLogicalDevice(), m_SwapChain, &imageCount, nullptr); + m_SwapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(InDevice->GetLogicalDevice(), m_SwapChain, &imageCount, m_SwapChainImages.data()); + + m_SwapChainImageFormat = m_SurfaceFormat.format; + m_SwapChainExtent = extent; + } + + VkSurfaceFormatKHR FVulkanSwapChain::ChooseSwapSurfaceFormat(const std::vector& availableFormats) + { + for (const auto& availableFormat : availableFormats) + { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB + && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) + { + return availableFormat; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR FVulkanSwapChain::ChooseSwapPresentMode(const std::vector& availablePresentModes) + { + for (const auto& availablePresentMode : availablePresentModes) + { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D FVulkanSwapChain::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) + { + if (capabilities.currentExtent.width != UINT32_MAX) + { + return capabilities.currentExtent; + } + else + { + int width, height; + glfwGetFramebufferSize(m_WindowHandle, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::max(capabilities.minImageExtent.width, + std::min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = std::max(capabilities.minImageExtent.height, + std::min(capabilities.maxImageExtent.height, actualExtent.height)); + + return actualExtent; + } + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 42817353..8a17b1dc 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -10,10 +10,81 @@ #pragma once +#include "VulkanRHI/VulkanDevice.h" +#include + +struct GLFWwindow; + namespace Karma { class FVulkanSwapChain { + public: + static FVulkanSwapChain* Create(FVulkanDevice* InDevice); + + private: + FVulkanSwapChain(FVulkanDevice* InDevice); + + /** + * @brief Chooses the best surface format (pixel format and color space) for the swapchain from the available formats. + * + * Basically looks for VK_FORMAT_B8G8R8A8_SRGB and VK_COLOR_SPACE_SRGB_NONLINEAR_KHR combination. + * + * VK_FORMAT_B8G8R8A8_SRGB : represents a 32-bit format with 8 bits for each of the blue, green, red, and alpha channels in sRGB color space. + * This format is widely used for swapchain images and color attachments. + * + * VK_COLOR_SPACE_SRGB_NONLINEAR_KHR : represents the sRGB color space with a nonlinear gamma curve. This color space is commonly used for displaying images on standard monitors. + * + * @param availableFormats The available surface formats (from QuerySwapChainSupport()) + * + * @see VulkanContext::CreateSwapChain() + * @since Karma 1.0.0 + */ + static VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector& availableFormats); + + /** + * @brief Chooses the best presentation mode for the swapchain from the available present modes. + * + * Basically looks for VK_PRESENT_MODE_MAILBOX_KHR (triple buffering) first, then VK_PRESENT_MODE_IMMEDIATE_KHR (tearing possible), and finally defaults to VK_PRESENT_MODE_FIFO_KHR (always available, v-sync) + * + * @param availablePresentModes The available presentation modes (from QuerySwapChainSupport()) + * + * @see VulkanContext::CreateSwapChain() + * @since Karma 1.0.0 + */ + VkPresentModeKHR ChooseSwapPresentMode(const std::vector& availablePresentModes); + + /** + * @brief Chooses the swap extent (resolution of the swapchain images) based on the capabilities of the surface and the actual window size. + * + * @param capabilities The surface capabilities (from QuerySwapChainSupport()) + * + * @see VulkanContext::CreateSwapChain() + * @since Karma 1.0.0 + */ + VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + + protected: + + const VkInstance m_Instance; + FVulkanDevice* m_Device; + + VkSurfaceKHR m_Surface = VK_NULL_HANDLE; + VkSwapchainKHR m_SwapChain = VK_NULL_HANDLE; + + std::vector m_SwapChainImages; + + VkFormat m_SwapChainImageFormat; + VkExtent2D m_SwapChainExtent; + + VkSurfaceFormatKHR m_SurfaceFormat; + VkPresentModeKHR m_PresentMode; + + GLFWwindow* m_WindowHandle; + + uint32_t m_InternalWidth = 0; + uint32_t m_InternalHeight = 0; + bool bInternalFullScreen = false; }; } \ No newline at end of file From f8b5481f1dca69878904f8fd849cb5eb612241c1 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 21 Dec 2025 09:41:07 +0530 Subject: [PATCH 043/107] Swapchain creation/destruction functional --- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 6 +++++ .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 8 ++++++ .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 7 ++++++ .../Vulkan/VulkanRHI/VulkanSwapChain.cpp | 14 ++++++++++- .../Vulkan/VulkanRHI/VulkanSwapChain.h | 25 +++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 03f17e95..251c4ac8 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -105,6 +105,12 @@ namespace Karma m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); } + void FVulkanDevice::WaitUntilIdle() + { + VkResult result = vkDeviceWaitIdle(m_LogicalDevice); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); + } + void FVulkanDevice::TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) { VkCommandBufferAllocateInfo allocInfo{}; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index debc5790..01a3fb54 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -62,6 +62,14 @@ namespace Karma */ void Destroy(); + /** + * @brief Waits on the host by blocking the calling CPU thread until the + * Vulkan logical device completes all pending GPU operations across all queues. + * + * @since Karma 1.0.0 + */ + void WaitUntilIdle(); + /** * @brief Retrieves the Vulkan logical device handle. * diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index c5163f8e..dcde5970 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -30,6 +30,7 @@ namespace Karma InitInstance(); KR_CORE_INFO("Vulkan RHI initialized successfully."); + return true; } @@ -40,6 +41,12 @@ namespace Karma void FVulkanDynamicRHI::Shutdown() { + // How to destroy swapchain + /*FVulkanSwapChainRecreateInfo RI{}; + experimentalSwapChain->Destroy(&RI); + delete experimentalSwapChain; + experimentalSwapChain = nullptr;*/ + m_Device->Destroy(); vkDestroySurfaceKHR(m_VulkanInstance, m_Surface, nullptr); DestroyDebugUtilsMessengerEXT(m_VulkanInstance, m_DebugMessenger, nullptr); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp index 1ebe01a3..602556ac 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp @@ -12,7 +12,8 @@ namespace Karma return swapChain; } - FVulkanSwapChain::FVulkanSwapChain(FVulkanDevice* InDevice) : m_Instance(InDevice->GetVulkanDynamicRHI()->GetInstance()) + FVulkanSwapChain::FVulkanSwapChain(FVulkanDevice* InDevice) : m_Instance(InDevice->GetVulkanDynamicRHI()->GetInstance()), + m_Device(InDevice) { m_WindowHandle = InDevice->GetVulkanDynamicRHI()->GetSurfaceWindow(); @@ -62,6 +63,7 @@ namespace Karma VkResult result = vkCreateSwapchainKHR(InDevice->GetLogicalDevice(), &createInfo, nullptr, &m_SwapChain); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create swapchain!"); + KR_CORE_INFO("Created a Vulkan swapchain"); vkGetSwapchainImagesKHR(InDevice->GetLogicalDevice(), m_SwapChain, &imageCount, nullptr); m_SwapChainImages.resize(imageCount); @@ -71,6 +73,12 @@ namespace Karma m_SwapChainExtent = extent; } + void FVulkanSwapChain::Destroy(FVulkanSwapChainRecreateInfo * RecreateInfo) + { + m_Device->WaitUntilIdle(); + vkDestroySwapchainKHR(m_Device->GetLogicalDevice(), m_SwapChain, nullptr); + } + VkSurfaceFormatKHR FVulkanSwapChain::ChooseSwapSurfaceFormat(const std::vector& availableFormats) { for (const auto& availableFormat : availableFormats) @@ -91,10 +99,14 @@ namespace Karma { if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + KR_CORE_INFO("Vulkan found surface present mode: VK_PRESENT_MODE_MAILBOX_KHR"); + KR_CORE_INFO("Using VK_PRESENT_MODE_MAILBOX_KHR for swapchain present mode"); + return availablePresentMode; } } + KR_CORE_INFO("Using the fallback VK_PRESENT_MODE_FIFO_KHR for swapchain"); return VK_PRESENT_MODE_FIFO_KHR; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 8a17b1dc..73f5e81f 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -17,12 +17,32 @@ struct GLFWwindow; namespace Karma { + struct FVulkanSwapChainRecreateInfo + { + VkSwapchainKHR SwapChain; + VkSurfaceKHR Surface; + }; + class FVulkanSwapChain { public: static FVulkanSwapChain* Create(FVulkanDevice* InDevice); + /** + * @brief Destroys the swapchain appropriately + * + * @since Karma 1.0.0 + */ + void Destroy(FVulkanSwapChainRecreateInfo* RecreateInfo); + private: + + /** + * @brief Creates Vulkan swapchain based upon the surface format and present mode + * + * @param InDevice The FVulkanDevice containing the LogicalDevice and GPU + * @since Karma 1.0.0 + */ FVulkanSwapChain(FVulkanDevice* InDevice); /** @@ -35,6 +55,8 @@ namespace Karma * * VK_COLOR_SPACE_SRGB_NONLINEAR_KHR : represents the sRGB color space with a nonlinear gamma curve. This color space is commonly used for displaying images on standard monitors. * + * @note If this combination is not found, returns the first available format. + * * @param availableFormats The available surface formats (from QuerySwapChainSupport()) * * @see VulkanContext::CreateSwapChain() @@ -86,5 +108,8 @@ namespace Karma uint32_t m_InternalHeight = 0; bool bInternalFullScreen = false; + + uint32_t m_CurrentImageIndex; + uint32_t m_SemaphoreIndex; }; } \ No newline at end of file From b657ba0d9fa3874da9a134a54d8bad431a1051dc Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 22 Dec 2025 14:48:16 +0530 Subject: [PATCH 044/107] VulkanRHI Windows : Phase IV 1. VulkanSwapchain in the KarmaGuiVulkanHandler 2. VulkanRenderPass.h/.cpp --- Karma/src/Karma/Core.h | 8 + .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 20 ++ .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 19 ++ .../Vulkan/VulkanRHI/VulkanRenderPass.cpp | 1 + .../Vulkan/VulkanRHI/VulkanRenderPass.h | 189 ++++++++++++++++++ .../Vulkan/VulkanRHI/VulkanSwapChain.h | 10 + 6 files changed, 247 insertions(+) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h diff --git a/Karma/src/Karma/Core.h b/Karma/src/Karma/Core.h index 801f0153..df9ce42a 100644 --- a/Karma/src/Karma/Core.h +++ b/Karma/src/Karma/Core.h @@ -109,6 +109,14 @@ #define KR_CORE_ASSERT(expr, ...) #endif + /** + * @brief Denotes code paths that should never be reached. + * + * @note Taken from the UE + */ +#define checkNoEntry() KR_CORE_ASSERT(false, "Enclosing block should never be called") + + /** * @brief Macro for std::bind routine. See https://en.cppreference.com/w/cpp/utility/functional/bind * diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 109c0450..c22b20ba 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -4,6 +4,8 @@ #include "KarmaUtilities.h" #include "Platform/Vulkan/VulkanVertexArray.h" #include "Karma/KarmaGui/KarmaGuiRenderer.h" +#include "VulkanRHI/VulkanSwapChain.h" +#include "VulkanRHI/VulkanDynamicRHI.h" // Visual Studio warnings /*#ifdef _MSC_VER @@ -1494,6 +1496,18 @@ namespace Karma } } + void KarmaGuiVulkanHandler::FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) + { + windowData->RHIResources.VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); + + windowData->Swapchain = windowData->RHIResources.VulkanSwapChain->GetSwapChainHandle(); + windowData->TotalImageCount = FVulkanDynamicRHI::Get().GetDevice()->GetVulkanDynamicRHI()->SwapChainImageCount(); + windowData->RenderArea.extent = windowData->RHIResources.VulkanSwapChain->GetSwapChainExtent(); + windowData->RenderArea.offset = { 0, 0 }; + //windowData->RenderPass = ; + windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + } + void KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) { KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); @@ -1693,6 +1707,12 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { + FVulkanSwapChainRecreateInfo RI{}; + windowData->RHIResources.VulkanSwapChain->Destroy(&RI); + delete windowData->RHIResources.VulkanSwapChain; + windowData->RHIResources.VulkanSwapChain = nullptr; + + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 94357c21..16dbacd2 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -283,6 +283,16 @@ namespace Karma VkFramebuffer Framebuffer; }; + /** + * @brief Structure to hold references to Vulkan RHI resources + * + * @since Karma 1.0.0 + */ + struct KarmaGui_ImplVulkanH_RHIResources + { + class FVulkanSwapChain* VulkanSwapChain; + }; + /** * @brief Helper structure to hold the data needed by entire KarmaGui primitive rendering including commandpools, renderpass, commandbuffers, * ImageFrameCount (swapchain), and SemaphoreIndex etc. @@ -439,6 +449,13 @@ namespace Karma */ VkRect2D RenderArea; + /** + * @brief Vulkan RHI resources + * + * @since Karma 1.0.0 + */ + KarmaGui_ImplVulkanH_RHIResources RHIResources; + /** * @brief Constructor for zero clearence and relevant stuff. * @@ -1216,6 +1233,8 @@ namespace Karma */ static void KarmaGui_ImplVulkan_SwapBuffers(KarmaGuiViewport* viewport, void*); + static void FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity); + /** * @brief The purpose of the routine is two-fold * diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp new file mode 100644 index 00000000..6bcb84b7 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp @@ -0,0 +1 @@ +#include "VulkanRenderPass.h" \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h new file mode 100644 index 00000000..684dc32f --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -0,0 +1,189 @@ +/** + * @file VulkanRenderPass.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 21, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "VulkanRHI/VulkanDevice.h" +#include "Core/KarmaTypes.h" + +#include "Core.h" + +namespace Karma +{ + /** + * @brief Using the Curiously Recurring Template Pattern (CRTP) reversal?. + * + * In CRTP we are do static polymorphism i.e base class gains information about the derived class without virtual functions + */ + template + struct FVulkanAttachmentReference : public TAttachmentReferenceType + { + FVulkanAttachmentReference() + { + ZeroStruct(); + } + + FVulkanAttachmentReference(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) + { + SetAttachment(AttachmentReferenceIn, AspectMask); + } + + /** + * @brief Disable method enforced here (checkNoEntry()) + * + * This "poison" is to ensure correct use (or trap the misuse) at call site + * + * ❌ Input attachments: mutation blocked (checkNoEntry()) + * FVulkanAttachmentReference inputRef; + * inputRef.SetAttachment(...); // Asserts/crashes - prevents bugs + * + * ✅ Color/depth attachments: mutation allowed + * FVulkanAttachmentReference colorRef; + * colorRef.SetAttachment(ref, mask); // Copies attachment/layout safely + * + * This selectively enables mutation only for standard attachments + * + * @since Karma 1.0.0 + */ + inline void SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { checkNoEntry(); } + inline void SetAttachment(const FVulkanAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { *this = AttachmentReferenceIn; } + inline void SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { checkNoEntry(); } + inline void ZeroStruct() {} + inline void SetAspect(uint32_t Aspect) {} + }; + + /** + * @brief This is template specialization where we define or implement the generic SetAttachment for VkAttachmentReference + */ + template <> + inline void FVulkanAttachmentReference::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) + { + attachment = AttachmentReferenceIn.attachment; + layout = AttachmentReferenceIn.layout; + } + + template <> + inline void FVulkanAttachmentReference::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, + const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) + { + attachment = AttachmentReferenceIn.attachment; + const VkImageLayout StencilLayout = StencilReference ? StencilReference->stencilLayout : VK_IMAGE_LAYOUT_UNDEFINED; + + layout = AttachmentReferenceIn.layout; //GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilLayout); + } + + /** + * @brief The number of render-targets that may be simultaneously written to. + * + * @note May move to different header file with appropriate definitions + */ + enum + { + MaxSimultaneousRenderTargets = 8, + MaxSimultaneousRenderTargets_NumBits = 3, + }; + static_assert(MaxSimultaneousRenderTargets <= (1 << MaxSimultaneousRenderTargets_NumBits), "MaxSimultaneousRenderTargets will not fit on MaxSimultaneousRenderTargets_NumBits"); + + class FVulkanRenderTargetLayout + { + public: + //FVulkanRenderTargetLayout(const FGraphicsPipelineStateInitializer& Initializer); + + //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHISetRenderTargetsInfo& RTInfo); + //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHIRenderPassInfo& RPInfo, VkImageLayout CurrentDepthLayout, VkImageLayout CurrentStencilLayout); + + inline const VkAttachmentReference* GetColorAttachmentReferences() const { return m_NumColorAttachments > 0 ? m_ColorReferences : nullptr; } + inline const VkAttachmentReference* GetResolveAttachmentReferences() const { return bHasResolveAttachments ? m_ResolveReferences : nullptr; } + inline const VkAttachmentReference* GetDepthAttachmentReference() const { return bHasDepthStencil ? &m_DepthReference : nullptr; } + inline const VkAttachmentReferenceStencilLayout* GetStencilAttachmentReference() const { return bHasDepthStencil ? &m_StencilReference : nullptr; } + //inline const VkAttachmentReference* GetFragmentDensityAttachmentReference() const { return bHasFragmentDensityAttachment ? &FragmentDensityReference : nullptr; } + + inline uint32_t GetNumColorAttachments() const { return m_NumColorAttachments; } + + private: + VkAttachmentReference m_ColorReferences[MaxSimultaneousRenderTargets]; + VkAttachmentReference m_ResolveReferences[MaxSimultaneousRenderTargets]; + VkAttachmentReference m_DepthReference; + VkAttachmentReferenceStencilLayout m_StencilReference; + + uint8_t m_NumColorAttachments; + uint8_t bHasResolveAttachments; + + uint8_t bHasDepthStencil; + }; + + + class FVulkanRenderPass + { + public: + inline VkRenderPass GetHandle() const { return m_RenderPass; } + + private: + VkRenderPass m_RenderPass; + }; + + template + class FVulkanRenderPassBuilder + { + public: + FVulkanRenderPassBuilder(FVulkanDevice& InDevice) + : m_Device(InDevice) + , m_CorrelationMask(0) + { + } + + void BuildCreateInfo(const FVulkanRenderTargetLayout& RTLayout) + { + uint32_t NumSubpasses = 0; + uint32_t NumDependencies = 0; + + // Grab (and optionally convert) attachment references. + uint32_t NumColorAttachments = RTLayout.GetNumColorAttachments(); + + const bool bHasDepthStencilAttachmentReference = (RTLayout.GetDepthAttachmentReference() != nullptr); + + for (uint32_t ColorAttachment = 0; ColorAttachment < NumColorAttachments; ++ColorAttachment) + { + m_ColorAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0)); + + // For MSAA + /*if (RTLayout.GetResolveAttachmentReferences() != nullptr) + { + m_ResolveAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetResolveAttachmentReferences()[ColorAttachment], 0)); + }*/ + } + + uint32_t DepthInputAttachment = VK_ATTACHMENT_UNUSED; + VkImageLayout DepthInputAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkImageAspectFlags DepthInputAspectMask = 0; + + if (bHasDepthStencilAttachmentReference) + { + m_DepthStencilAttachmentReference.SetDepthStencilAttachment(*RTLayout.GetDepthAttachmentReference(), RTLayout.GetStencilAttachmentReference(), 0, false/*m_Device.SupportsParallelRendering()*/); + } + } + + private: + TSubpassDescriptionClass m_SubpassDescriptions[8]; + TSubpassDependencyClass m_SubpassDependencies[8]; + + KarmaVector m_ColorAttachmentReferences; + KarmaVector m_ResolveAttachmentReferences; + + TAttachmentReferenceClass m_DepthStencilAttachmentReference; + + + TRenderPassCreateInfoClass m_CreateInfo; + FVulkanDevice& m_Device; + + uint32_t m_CorrelationMask; + }; +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 73f5e81f..ae8eaa46 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -35,6 +35,12 @@ namespace Karma */ void Destroy(FVulkanSwapChainRecreateInfo* RecreateInfo); + ///////////////// Getters ///////////////// + inline VkSwapchainKHR GetSwapChainHandle() const { return m_SwapChain; } + + inline VkExtent2D GetSwapChainExtent() const { return m_SwapChainExtent; } + inline uint32_t GetMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } + private: /** @@ -111,5 +117,9 @@ namespace Karma uint32_t m_CurrentImageIndex; uint32_t m_SemaphoreIndex; + + // Number of images (to work upon (CPU side) whilst an image is being rendered (GPU side processing)) + 1 + // Clearly, MAX_FRAMES_IN_FLIGHT shouldn't exceed m_SwapChainImages.size() + const uint32_t MAX_FRAMES_IN_FLIGHT = 2; }; } \ No newline at end of file From c12c0f5ac30defb8e783ff8a3cf9eaa9642e8876 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 24 Dec 2025 06:56:00 +0530 Subject: [PATCH 045/107] Little progress on VulkanRenderpass with BuildCreateInfo --- Karma/src/Karma/Core/KarmaTypes.h | 7 + .../Vulkan/VulkanRHI/VulkanRenderPass.h | 179 +++++++++++++++++- 2 files changed, 182 insertions(+), 4 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index e719b1c6..c43a7055 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -434,6 +434,13 @@ class KarmaVector return Index >= 0 && Index < m_Elements.size(); } + /** + * @brief Helper function to return typed pointer to the first entry of array + * + * @since Karma 1.0.0 + */ + FORCEINLINE BuildingBlock const* GetData() const { return m_Elements.data(); } + protected: std::vector m_Elements; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 684dc32f..a903944c 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -12,7 +12,7 @@ #include "VulkanRHI/VulkanDevice.h" #include "Core/KarmaTypes.h" - +#include "KarmaMemory.h" #include "Core.h" namespace Karma @@ -79,6 +79,125 @@ namespace Karma layout = AttachmentReferenceIn.layout; //GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilLayout); } + /** + * @brief Template (for VkAttachmentDescription) definition of FVulkanAttachmentDescription + * + * Is this the CRTP reversal? + * + * @since Karma 1.0.0 + */ + template + struct FVulkanAttachmentDescription + { + }; + + /** + * @brief Specializing (?) FVulkanAttachmentDescription for VkAttachmentDescription + */ + template<> + struct FVulkanAttachmentDescription + : public VkAttachmentDescription + { + FVulkanAttachmentDescription() + { + FMemory::Memzero(this, sizeof(VkAttachmentDescription)); + } + + FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc) + { + flags = InDesc.flags; + format = InDesc.format; + samples = InDesc.samples; + loadOp = InDesc.loadOp; + storeOp = InDesc.storeOp; + stencilLoadOp = InDesc.stencilLoadOp; + stencilStoreOp = InDesc.stencilStoreOp; + initialLayout = InDesc.initialLayout; + finalLayout = InDesc.finalLayout; + } + + /** + * @brief Seems like we are not supporting VkAttachmentDescriptionStencilLayout + */ + FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering) + { + flags = InDesc.flags; + format = InDesc.format; + samples = InDesc.samples; + loadOp = InDesc.loadOp; + storeOp = InDesc.storeOp; + stencilLoadOp = InDesc.stencilLoadOp; + stencilStoreOp = InDesc.stencilStoreOp; + + /*const bool bHasStencilLayout = VulkanFormatHasStencil(InDesc.format) && (InStencilDesc != nullptr); + const VkImageLayout StencilInitialLayout = bHasStencilLayout ? InStencilDesc->stencilInitialLayout : VK_IMAGE_LAYOUT_UNDEFINED; + initialLayout = GetMergedDepthStencilLayout(InDesc.initialLayout, StencilInitialLayout); + const VkImageLayout StencilFinalLayout = bHasStencilLayout ? InStencilDesc->stencilFinalLayout : VK_IMAGE_LAYOUT_UNDEFINED; + finalLayout = GetMergedDepthStencilLayout(InDesc.finalLayout, StencilFinalLayout); + */ + } + }; + + /** + * @brief Template (for VkSubpassDescription) definition of FVulkanSubpassDescription + * + * @since Karma 1.0.0 + */ + template + class FVulkanSubpassDescription + { + }; + + /** + * @brief Specialized definition of FVulkanSubpassDescription + */ + template<> + struct FVulkanSubpassDescription + : public VkSubpassDescription + { + FVulkanSubpassDescription() + { + FMemory::Memzero(this, sizeof(VkSubpassDescription)); + pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + } + + void SetColorAttachments(const KarmaVector>& ColorAttachmentReferences, int OverrideCount = -1) + { + colorAttachmentCount = (OverrideCount == -1) ? ColorAttachmentReferences.Num() : OverrideCount; + pColorAttachments = ColorAttachmentReferences.GetData(); + } + + /*void SetResolveAttachments(const KarmaVector>& ResolveAttachmentReferences) + { + if (ResolveAttachmentReferences.Num() > 0) + { + check(colorAttachmentCount == ResolveAttachmentReferences.Num()); + pResolveAttachments = ResolveAttachmentReferences.GetData(); + } + }*/ + + void SetDepthStencilAttachment(FVulkanAttachmentReference* DepthStencilAttachmentReference) + { + pDepthStencilAttachment = static_cast(DepthStencilAttachmentReference); + } + + /*void SetInputAttachments(FVulkanAttachmentReference* InputAttachmentReferences, uint32 NumInputAttachmentReferences) + { + pInputAttachments = static_cast(InputAttachmentReferences); + inputAttachmentCount = NumInputAttachmentReferences; + } + + void SetShadingRateAttachment(void* /* ShadingRateAttachmentInfo) + { + // No-op without VK_KHR_create_renderpass2 + } + + void SetMultiViewMask(uint32_t Mask) + { + // No-op without VK_KHR_create_renderpass2 + }*/ + }; + /** * @brief The number of render-targets that may be simultaneously written to. * @@ -106,20 +225,33 @@ namespace Karma //inline const VkAttachmentReference* GetFragmentDensityAttachmentReference() const { return bHasFragmentDensityAttachment ? &FragmentDensityReference : nullptr; } inline uint32_t GetNumColorAttachments() const { return m_NumColorAttachments; } + inline uint32_t GetNumAttachmentDescriptions() const { return m_NumAttachmentDescriptions; } + + inline const VkAttachmentDescription* GetAttachmentDescriptions() const { return m_AttachmentDescriptions; } + //inline const VkAttachmentDescriptionStencilLayout* GetStencilDescription() const { return bHasDepthStencil ? &m_StencilDescription : nullptr; } private: VkAttachmentReference m_ColorReferences[MaxSimultaneousRenderTargets]; + + // Probably not required because of we are using default MSAA? VkAttachmentReference m_ResolveReferences[MaxSimultaneousRenderTargets]; + + // Depth attachment ref VkAttachmentReference m_DepthReference; VkAttachmentReferenceStencilLayout m_StencilReference; uint8_t m_NumColorAttachments; - uint8_t bHasResolveAttachments; + uint8_t m_NumAttachmentDescriptions; + VkAttachmentDescription m_AttachmentDescriptions[MaxSimultaneousRenderTargets * 2 + 2]; + //VkAttachmentDescriptionStencilLayout m_StencilDescription; + + uint8_t bHasResolveAttachments; + + // Do we have a depth stencil uint8_t bHasDepthStencil; }; - class FVulkanRenderPass { public: @@ -147,6 +279,7 @@ namespace Karma // Grab (and optionally convert) attachment references. uint32_t NumColorAttachments = RTLayout.GetNumColorAttachments(); + // Do we have a depth attachment const bool bHasDepthStencilAttachmentReference = (RTLayout.GetDepthAttachmentReference() != nullptr); for (uint32_t ColorAttachment = 0; ColorAttachment < NumColorAttachments; ++ColorAttachment) @@ -167,8 +300,44 @@ namespace Karma if (bHasDepthStencilAttachmentReference) { + // For depth attachment reference, attachment and layout should also be given by GetDepthAttachmentReference only m_DepthStencilAttachmentReference.SetDepthStencilAttachment(*RTLayout.GetDepthAttachmentReference(), RTLayout.GetStencilAttachmentReference(), 0, false/*m_Device.SupportsParallelRendering()*/); + + // Why are these needed when attachment and layout are assigned in m_DepthStencilAttachmentReference + m_DepthStencilAttachment.attachment = RTLayout.GetDepthAttachmentReference()->attachment; + m_DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } + + // main subpass + { + TSubpassDescriptionClass& SubpassDesc = m_SubpassDescriptions[NumSubpasses++]; + + SubpassDesc.SetColorAttachments(m_ColorAttachmentReferences, NumColorAttachments); + + if (bHasDepthStencilAttachmentReference) + { + SubpassDesc.SetDepthStencilAttachment(&m_DepthStencilAttachmentReference); + } + } + + for (uint32_t Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment) + { + if (bHasDepthStencilAttachmentReference && (Attachment == m_DepthStencilAttachmentReference.attachment)) + { + //m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDescription(), false/*Device.SupportsParallelRendering()*/)); + } + else + { + m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); + } + } + + /*m_CreateInfo.attachmentCount = m_AttachmentDescriptions.Num(); + m_CreateInfo.pAttachments = m_AttachmentDescriptions.GetData(); + m_CreateInfo.subpassCount = NumSubpasses; + m_CreateInfo.pSubpasses = m_SubpassDescriptions; + m_CreateInfo.dependencyCount = NumDependencies; + m_CreateInfo.pDependencies = m_SubpassDependencies;*/ } private: @@ -178,8 +347,10 @@ namespace Karma KarmaVector m_ColorAttachmentReferences; KarmaVector m_ResolveAttachmentReferences; - TAttachmentReferenceClass m_DepthStencilAttachmentReference; + KarmaVector m_AttachmentDescriptions; + TAttachmentReferenceClass m_DepthStencilAttachmentReference; + TAttachmentReferenceClass m_DepthStencilAttachment; TRenderPassCreateInfoClass m_CreateInfo; FVulkanDevice& m_Device; From 53bb1f967f6f0b038ce3560584512298fefc1bc4 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 26 Dec 2025 20:34:49 +0530 Subject: [PATCH 046/107] VulkanRHI Windows: Phase V Renderpass creation --- Karma/src/Karma/Application.cpp | 4 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 19 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 54 ++++- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 5 + Karma/src/Platform/Vulkan/VulkanContext.cpp | 8 +- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 9 + .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 20 ++ .../Vulkan/VulkanRHI/VulkanRenderPass.cpp | 56 ++++- .../Vulkan/VulkanRHI/VulkanRenderPass.h | 217 ++++++++++++++---- .../Vulkan/VulkanRHI/VulkanSwapChain.h | 2 + Pranjal/Source/Private/EditorLayer.cpp | 4 +- 12 files changed, 339 insertions(+), 61 deletions(-) diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index f3bbf03b..26b34ed6 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -37,8 +37,8 @@ namespace Karma m_LayerStack = new LayerStack(); // Graphics API Vulkan or OpenGL should have been completely initialized by here - //m_KarmaGuiLayer = new KarmaGuiLayer(m_Window); <-- to be uncommented when we have RHI - //PushOverlay(m_KarmaGuiLayer);<-- to be uncommented when we have RHI + m_KarmaGuiLayer = new KarmaGuiLayer(m_Window);// <-- to be uncommented when we have RHI + PushOverlay(m_KarmaGuiLayer);//<-- to be uncommented when we have RHI } Application::~Application() diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 0212a443..ac2d2052 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -39,13 +39,13 @@ namespace Karma initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT; // Stuff created and dedicated to KarmaGui - CreateDescriptorPool(); + CreateDescriptorPool(initInfo.Device); initInfo.DescriptorPool = m_KarmaGuiDescriptorPool; - initInfo.RenderPass = VulkanHolder::GetVulkanContext()->GetRenderPass(); + // initInfo.RenderPass = VulkanHolder::GetVulkanContext()->GetRenderPass(); // Not sure about the use of Subpass so setting to 0 - initInfo.Subpass = 0; + // initInfo.Subpass = 0; // Settingup backend in KarmaGui // KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_Init(&initInfo); @@ -53,7 +53,8 @@ namespace Karma // Fresh start with newly instantiated Vulkan data // Since VulkanContext has already instantiated fresh swapchain and commandbuffers, we send that false - KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); + // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); + KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); // Load default font KGFontConfig fontConfig; @@ -251,13 +252,13 @@ namespace Karma KR_CORE_ASSERT(info->DescriptorPool != VK_NULL_HANDLE, "No descriptor pool found"); KR_CORE_ASSERT(info->MinImageCount >= 2, "Minimum image count exceeding limit"); KR_CORE_ASSERT(info->ImageCount >= info->MinImageCount, "Not enough pitch for ImageCount"); - KR_CORE_ASSERT(info->RenderPass != VK_NULL_HANDLE, "No renderpass assigned"); + //(info->RenderPass != VK_NULL_HANDLE, "No renderpass assigned"); backendData->VulkanInitInfo = *info; //backendData->VulkanInitInfo.Device = info->Device; - backendData->RenderPass = info->RenderPass; - backendData->Subpass = info->Subpass; + //backendData->RenderPass = info->RenderPass; + //backendData->Subpass = info->Subpass; // Font, descriptor, and pipeline KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); @@ -396,7 +397,7 @@ namespace Karma vkDestroyDescriptorPool(vulkanInfo->Device, m_KarmaGuiDescriptorPool, VK_NULL_HANDLE); } - void KarmaGuiRenderer::CreateDescriptorPool() + void KarmaGuiRenderer::CreateDescriptorPool(VkDevice VulkanDevice) { VkDescriptorPoolSize pool_sizes[] = { @@ -420,7 +421,7 @@ namespace Karma poolInfo.poolSizeCount = uint32_t(std::size(pool_sizes)); poolInfo.pPoolSizes = pool_sizes; - VkResult result = vkCreateDescriptorPool(VulkanHolder::GetVulkanContext()->GetLogicalDevice(), &poolInfo, nullptr, &m_KarmaGuiDescriptorPool); + VkResult result = vkCreateDescriptorPool(VulkanDevice, &poolInfo, nullptr, &m_KarmaGuiDescriptorPool); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create descriptor pool for KarmaGui"); } diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h index 17a31f05..0ba56a4c 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h @@ -69,7 +69,7 @@ namespace Karma * @since Karma 1.0.0 */ static void KarmaGui_ImplVulkan_Init(KarmaGui_ImplVulkan_InitInfo* initInfo); - static void CreateDescriptorPool(); + static void CreateDescriptorPool(VkDevice VulkanDevice); /** * @brief Calls CleanUpVulkanAndWindowData() and does the shutting of GLFW and KarmaGui (KarmaGui::DestroyContext) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index c22b20ba..a9e68969 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -6,6 +6,7 @@ #include "Karma/KarmaGui/KarmaGuiRenderer.h" #include "VulkanRHI/VulkanSwapChain.h" #include "VulkanRHI/VulkanDynamicRHI.h" +#include "VulkanRHI/VulkanRenderPass.h" // Visual Studio warnings /*#ifdef _MSC_VER @@ -1504,8 +1505,59 @@ namespace Karma windowData->TotalImageCount = FVulkanDynamicRHI::Get().GetDevice()->GetVulkanDynamicRHI()->SwapChainImageCount(); windowData->RenderArea.extent = windowData->RHIResources.VulkanSwapChain->GetSwapChainExtent(); windowData->RenderArea.offset = { 0, 0 }; - //windowData->RenderPass = ; windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + + FVulkanRenderPassInfo RPInfo; + KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources.VulkanSwapChain, RPInfo); + + FVulkanRenderTargetLayout RTLayout(RPInfo); + windowData->RenderPass = CreateVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); + + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + + backendData->RenderPass = windowData->RenderPass; + } + + void KarmaGuiVulkanHandler::MakeRenderPassInfo(FVulkanSwapChain* SwapChain, FVulkanRenderPassInfo& RPInfo) + { + FVulkanRenderPassInfo::FAttachmentInfo colorAttachmentInfo; + colorAttachmentInfo.AttachmentFlags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; + colorAttachmentInfo.AttachmentFormat = SwapChain->GetSwapChainImageFormat(); + colorAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; + colorAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachmentInfo.AttachmentStoreOperation = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachmentInfo.AttachmentStencilLoadOperation = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachmentInfo.AttachmentStencilStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachmentInfo.AttachmentInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachmentInfo.AttachmentFinalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + RPInfo.m_AttachmentsInfo.Add(colorAttachmentInfo); + + FVulkanRenderPassInfo::FAttachmentInfo depthAttachmentInfo; + depthAttachmentInfo.AttachmentFlags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; + depthAttachmentInfo.AttachmentFormat = FVulkanDynamicRHI::Get().FindSupportedFormat({ VK_FORMAT_D32_SFLOAT, + VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + depthAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; + depthAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachmentInfo.AttachmentStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachmentInfo.AttachmentStencilLoadOperation = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachmentInfo.AttachmentStencilStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachmentInfo.AttachmentInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachmentInfo.AttachmentFinalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + RPInfo.m_AttachmentsInfo.Add(depthAttachmentInfo); + + RPInfo.bHasDepthAttachment = true; + + FVulkanRenderPassInfo::FAttachmentRefInfo colorAttachmentReference; + colorAttachmentReference.attachment = 0; + colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + RPInfo.m_ColorAttachmentsRefInfo.Add(colorAttachmentReference); + + RPInfo.m_DepthAttachmentReference.attachment = 1; + RPInfo.m_DepthAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } void KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 16dbacd2..139436ff 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -1233,8 +1233,13 @@ namespace Karma */ static void KarmaGui_ImplVulkan_SwapBuffers(KarmaGuiViewport* viewport, void*); + /** + * @brief Replacement for ShareVulkanContextResourcesOfMainWindow + */ static void FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity); + static void MakeRenderPassInfo(class FVulkanSwapChain* SwapChain, struct FVulkanRenderPassInfo& RPInfo); + /** * @brief The purpose of the routine is two-fold * diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 4725529f..5bc916f1 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -1593,10 +1593,10 @@ namespace Karma VkAttachmentDescription colorAttachment{}; colorAttachment.format = m_swapChainImageFormat; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // what to do with attachment data before rendering: clear to clearcolor + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;// what to do with attachment data after rendering: store in the memory so we can read later + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;// this is a color buffer only not stencil data + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;// this is color buffer only not stencil data colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 88199797..2856e4d9 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -241,6 +241,15 @@ namespace Karma */ inline uint32_t SwapChainImageCount() const { return m_SwapChainImageCount; } + /** + * @brief Finds a supported format from the list of candidate formats based on the desired tiling and features. + * Used in depth resource creation. + * + * @see KarmaGuiVulkanHandler::MakeRenderPassInfo + * @since Karma 1.0.0 + */ + VkFormat FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const; + inline GLFWwindow* GetSurfaceWindow() const { return m_WindowHandle; } inline VkSurfaceKHR GetSurface() const { return m_Surface; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index dcde5970..8a08d671 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -162,6 +162,26 @@ namespace Karma } } + VkFormat FVulkanDynamicRHI::FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const + { + for (VkFormat format : candidates) + { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(m_PhysicalDevice, format, &props); + if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) + { + return format; + } + else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) + { + return format; + } + } + + KR_CORE_ASSERT(false, "Failed to find supported format!"); + return VkFormat{}; + } + void FVulkanDynamicRHI::SelectDevice()// Pick physical device (GPU) and create Vulkan logical device { uint32_t deviceCount = 0; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp index 6bcb84b7..edab6f88 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp @@ -1 +1,55 @@ -#include "VulkanRenderPass.h" \ No newline at end of file +#include "VulkanRenderPass.h" + +namespace Karma +{ + VkRenderPass CreateVulkanRenderPass(FVulkanDevice& InDevice, const FVulkanRenderTargetLayout& RTLayout) + { + VkRenderPass OutRenderpass; + + FVulkanRenderPassBuilder, FVulkanSubpassDependency, FVulkanAttachmentReference, FVulkanAttachmentDescription, FVulkanRenderPassCreateInfo> Creator(InDevice); + OutRenderpass = Creator.Create(RTLayout); + + return OutRenderpass; + } + + FVulkanRenderTargetLayout::FVulkanRenderTargetLayout(FVulkanRenderPassInfo& VRPInfo) : m_NumAttachmentDescriptions(0), m_NumColorAttachments(0) + { + // Color + depth attachment descriptions + for (const auto& AttachmentInfo : VRPInfo.m_AttachmentsInfo) + { + VkAttachmentDescription& CurrentDescription = m_AttachmentDescriptions[m_NumAttachmentDescriptions++]; + + CurrentDescription.flags = AttachmentInfo.AttachmentFlags; + CurrentDescription.format = AttachmentInfo.AttachmentFormat; + CurrentDescription.samples = AttachmentInfo.AttachmentSampleCount; + CurrentDescription.loadOp = AttachmentInfo.AttachmentLoadOperation; + CurrentDescription.storeOp = AttachmentInfo.AttachmentStoreOperation; + CurrentDescription.stencilLoadOp = AttachmentInfo.AttachmentStencilLoadOperation; + CurrentDescription.stencilStoreOp = AttachmentInfo.AttachmentStencilStoreOperation; + CurrentDescription.initialLayout = AttachmentInfo.AttachmentInitialLayout; + CurrentDescription.finalLayout = AttachmentInfo.AttachmentFinalLayout; + } + + // Color attachment reference + for (const auto& AttachmentRefInfo : VRPInfo.m_ColorAttachmentsRefInfo) + { + VkAttachmentReference& CurrentReference = m_ColorReferences[m_NumColorAttachments++]; + + CurrentReference.attachment = AttachmentRefInfo.attachment; + CurrentReference.layout = AttachmentRefInfo.layout; + } + + // Depth attachment reference + if (VRPInfo.bHasDepthAttachment) + { + bHasDepthStencil = true; + + m_DepthReference.attachment = VRPInfo.m_DepthAttachmentReference.attachment; + m_DepthReference.layout = VRPInfo.m_DepthAttachmentReference.layout; + } + else + { + bHasDepthStencil = false; + } + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index a903944c..02577dae 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -121,7 +121,7 @@ namespace Karma */ FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering) { - flags = InDesc.flags; + //flags = InDesc.flags; format = InDesc.format; samples = InDesc.samples; loadOp = InDesc.loadOp; @@ -198,6 +198,52 @@ namespace Karma }*/ }; + template + struct FVulkanSubpassDependency + : public TSubpassDependencyType + { + }; + + template<> + struct FVulkanSubpassDependency + : public VkSubpassDependency + { + FVulkanSubpassDependency() + { + FMemory::Memzero(this, sizeof(VkSubpassDependency)); + } + }; + + template + struct FVulkanRenderPassCreateInfo + { + }; + + template<> + struct FVulkanRenderPassCreateInfo + : public VkRenderPassCreateInfo + { + FVulkanRenderPassCreateInfo() + { + //ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO); + this->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + FMemory::Memzero(((uint8_t*)&(*this)) + sizeof(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO), sizeof(VkRenderPassCreateInfo) - sizeof(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO)); + } + + VkRenderPass Create(FVulkanDevice& Device) + { + VkRenderPass Handle = VK_NULL_HANDLE; + //VERIFYVULKANRESULT_EXPANDED(VulkanRHI::vkCreateRenderPass(Device.GetInstanceHandle(), this, VULKAN_CPU_ALLOCATOR, &Handle)); + VkResult result = vkCreateRenderPass(Device.GetLogicalDevice(), this, nullptr, &Handle); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create render pass"); + + KR_CORE_INFO("Renderpass created successfully"); + + return Handle; + } + }; + /** * @brief The number of render-targets that may be simultaneously written to. * @@ -210,43 +256,124 @@ namespace Karma }; static_assert(MaxSimultaneousRenderTargets <= (1 << MaxSimultaneousRenderTargets_NumBits), "MaxSimultaneousRenderTargets will not fit on MaxSimultaneousRenderTargets_NumBits"); + struct FVulkanRenderPassInfo + { + struct FAttachmentInfo + { + /** + * @brief + */ + VkAttachmentDescriptionFlags AttachmentFlags; + + /** + * @brief Format of the attachment (color format VK_FORMAT_R8G8B8A8_UNORM for instance) + */ + VkFormat AttachmentFormat; + + /** + * @brief Attachment sample count + */ + VkSampleCountFlagBits AttachmentSampleCount; + + /** + * @brief what to do with attachment data before rendering + */ + VkAttachmentLoadOp AttachmentLoadOperation; + + /** + * @brief what to do with attachment data after rendering + */ + VkAttachmentStoreOp AttachmentStoreOperation; + + /** + * @brief + */ + VkAttachmentLoadOp AttachmentStencilLoadOperation; + VkAttachmentStoreOp AttachmentStencilStoreOperation; + + /** + * @brief Attachment layout before renderpass begins + * + * Image State Timeline: + * ┌─────────────────────-┐ + * │ PRE-RENDER PASS │ ← initialLayout (what you provide) + * ├─────────────────────-┤ + * │ vkCmdBeginRenderPass │ + * │ ↓ Implicit transition│ + * │ FIRST subpass layout │ (e.g. COLOR_ATTACHMENT_OPTIMAL) + * ├─ subpass transitions─┤ + * │ LAST subpass layout │ + * │ ↑ Implicit transition│ + * │ POST-RENDER PASS │ → finalLayout (what you get) + * └─────────────────────-┘ + * │ vkCmdEndRenderPass │ + * + * @since Karma 1.0.0 + */ + VkImageLayout AttachmentInitialLayout; + + /** + * @brief Attachment layout at the end of renderpass + */ + VkImageLayout AttachmentFinalLayout; + }; + + struct FAttachmentRefInfo + { + /** + * @brief The parameter specifies which attachment to reference by its index in the attachment descriptions array. + */ + uint32_t attachment; + + /** + * @brief The variable specifies which layout we would like the attachment to have during a subpass that uses this + * reference. Vulkan will automatically transition the attachment to this layout when the subpass is started. + * + * Examples: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + * + * @note This is the layout inside a subpass while shader accesses the attachment + */ + VkImageLayout layout; + }; + + KarmaVector m_AttachmentsInfo;// color + depth + KarmaVector m_ColorAttachmentsRefInfo; + bool bHasDepthAttachment; + FAttachmentRefInfo m_DepthAttachmentReference; + }; + + class FVulkanRenderTargetLayout { public: //FVulkanRenderTargetLayout(const FGraphicsPipelineStateInitializer& Initializer); + // Experimental layout constructor (layout as in VulkanContext). We need to make this generic somehow + FVulkanRenderTargetLayout(FVulkanRenderPassInfo& VRPInfo); + //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHISetRenderTargetsInfo& RTInfo); //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHIRenderPassInfo& RPInfo, VkImageLayout CurrentDepthLayout, VkImageLayout CurrentStencilLayout); inline const VkAttachmentReference* GetColorAttachmentReferences() const { return m_NumColorAttachments > 0 ? m_ColorReferences : nullptr; } - inline const VkAttachmentReference* GetResolveAttachmentReferences() const { return bHasResolveAttachments ? m_ResolveReferences : nullptr; } inline const VkAttachmentReference* GetDepthAttachmentReference() const { return bHasDepthStencil ? &m_DepthReference : nullptr; } inline const VkAttachmentReferenceStencilLayout* GetStencilAttachmentReference() const { return bHasDepthStencil ? &m_StencilReference : nullptr; } - //inline const VkAttachmentReference* GetFragmentDensityAttachmentReference() const { return bHasFragmentDensityAttachment ? &FragmentDensityReference : nullptr; } - inline uint32_t GetNumColorAttachments() const { return m_NumColorAttachments; } inline uint32_t GetNumAttachmentDescriptions() const { return m_NumAttachmentDescriptions; } - inline const VkAttachmentDescription* GetAttachmentDescriptions() const { return m_AttachmentDescriptions; } - //inline const VkAttachmentDescriptionStencilLayout* GetStencilDescription() const { return bHasDepthStencil ? &m_StencilDescription : nullptr; } private: VkAttachmentReference m_ColorReferences[MaxSimultaneousRenderTargets]; - // Probably not required because of we are using default MSAA? - VkAttachmentReference m_ResolveReferences[MaxSimultaneousRenderTargets]; - // Depth attachment ref VkAttachmentReference m_DepthReference; + + // Probabbly not required VkAttachmentReferenceStencilLayout m_StencilReference; uint8_t m_NumColorAttachments; uint8_t m_NumAttachmentDescriptions; VkAttachmentDescription m_AttachmentDescriptions[MaxSimultaneousRenderTargets * 2 + 2]; - //VkAttachmentDescriptionStencilLayout m_StencilDescription; - - uint8_t bHasResolveAttachments; // Do we have a depth stencil uint8_t bHasDepthStencil; @@ -267,7 +394,6 @@ namespace Karma public: FVulkanRenderPassBuilder(FVulkanDevice& InDevice) : m_Device(InDevice) - , m_CorrelationMask(0) { } @@ -281,23 +407,27 @@ namespace Karma // Do we have a depth attachment const bool bHasDepthStencilAttachmentReference = (RTLayout.GetDepthAttachmentReference() != nullptr); + + // VkAttachmentDescription for color/depth attachments + for (uint32_t Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment) + { + if (bHasDepthStencilAttachmentReference && (Attachment == m_DepthStencilAttachmentReference.attachment)) + { + //m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDescription(), false/*Device.SupportsParallelRendering()*/)); + } + else + { + m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); + } + } + // VkAttachmentReference for color attachments for (uint32_t ColorAttachment = 0; ColorAttachment < NumColorAttachments; ++ColorAttachment) { m_ColorAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0)); - - // For MSAA - /*if (RTLayout.GetResolveAttachmentReferences() != nullptr) - { - m_ResolveAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetResolveAttachmentReferences()[ColorAttachment], 0)); - }*/ } - uint32_t DepthInputAttachment = VK_ATTACHMENT_UNUSED; - VkImageLayout DepthInputAttachmentLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VkImageAspectFlags DepthInputAspectMask = 0; - + // VkAttachmentReference for depth attachment if (bHasDepthStencilAttachmentReference) { // For depth attachment reference, attachment and layout should also be given by GetDepthAttachmentReference only @@ -308,7 +438,7 @@ namespace Karma m_DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } - // main subpass + // main subpass (using only single pass) { TSubpassDescriptionClass& SubpassDesc = m_SubpassDescriptions[NumSubpasses++]; @@ -320,24 +450,29 @@ namespace Karma } } - for (uint32_t Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment) - { - if (bHasDepthStencilAttachmentReference && (Attachment == m_DepthStencilAttachmentReference.attachment)) - { - //m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDescription(), false/*Device.SupportsParallelRendering()*/)); - } - else - { - m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); - } - } - - /*m_CreateInfo.attachmentCount = m_AttachmentDescriptions.Num(); + // using single dependency (vulkancontext experimental) + TSubpassDependencyClass& SubpassDep = m_SubpassDependencies[NumDependencies++]; + SubpassDep.srcSubpass = VK_SUBPASS_EXTERNAL; + SubpassDep.dstSubpass = 0; + SubpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + SubpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + SubpassDep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + //SubpassDep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + SubpassDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + m_CreateInfo.attachmentCount = m_AttachmentDescriptions.Num(); m_CreateInfo.pAttachments = m_AttachmentDescriptions.GetData(); m_CreateInfo.subpassCount = NumSubpasses; m_CreateInfo.pSubpasses = m_SubpassDescriptions; m_CreateInfo.dependencyCount = NumDependencies; - m_CreateInfo.pDependencies = m_SubpassDependencies;*/ + m_CreateInfo.pDependencies = m_SubpassDependencies; + } + + VkRenderPass Create(const FVulkanRenderTargetLayout& RTLayout) + { + BuildCreateInfo(RTLayout); + + return m_CreateInfo.Create(m_Device); } private: @@ -354,7 +489,7 @@ namespace Karma TRenderPassCreateInfoClass m_CreateInfo; FVulkanDevice& m_Device; - - uint32_t m_CorrelationMask; }; + + VkRenderPass CreateVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout); } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index ae8eaa46..8d6e1c14 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -40,6 +40,8 @@ namespace Karma inline VkExtent2D GetSwapChainExtent() const { return m_SwapChainExtent; } inline uint32_t GetMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } + + inline VkFormat GetSwapChainImageFormat() const { return m_SwapChainImageFormat; } private: diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index fa94ba6a..55e223cb 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -410,8 +410,8 @@ class KarmaApp : public Karma::Application public: KarmaApp() { - // To be un commented when we have fully functional RHI - //PushLayer(new Karma::EditorLayer()); + // To be uncommented when we have fully functional RHI + PushLayer(new Karma::EditorLayer()); } }; From 6344588046ecee498e91b6d3ef128072f7cb59f8 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 26 Dec 2025 21:07:35 +0530 Subject: [PATCH 047/107] Minor comments --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 13 ++++++------- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 7 ++++--- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index ac2d2052..f1dcf816 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -41,11 +41,6 @@ namespace Karma // Stuff created and dedicated to KarmaGui CreateDescriptorPool(initInfo.Device); initInfo.DescriptorPool = m_KarmaGuiDescriptorPool; - - // initInfo.RenderPass = VulkanHolder::GetVulkanContext()->GetRenderPass(); - - // Not sure about the use of Subpass so setting to 0 - // initInfo.Subpass = 0; // Settingup backend in KarmaGui // KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_Init(&initInfo); @@ -56,6 +51,9 @@ namespace Karma // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); + // Font, descriptor, and pipeline + KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); + // Load default font KGFontConfig fontConfig; fontConfig.FontDataOwnedByAtlas = false; @@ -260,8 +258,9 @@ namespace Karma //backendData->RenderPass = info->RenderPass; //backendData->Subpass = info->Subpass; - // Font, descriptor, and pipeline - KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); + // Font, descriptor, and pipeline (moved to the KarmaGuiRenderer::SetUpKarmaGuiRenderer, after KarmaGuiVulkanHandler::FillWindowData + // because RenderPass is required for pipeline creation) + // KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) KarmaGuiViewport* mainViewport = KarmaGui::GetMainViewport(); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index a9e68969..e31bae63 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -841,7 +841,7 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't create pipeline layout"); } - void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) + void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) { KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -1516,12 +1516,13 @@ namespace Karma KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); backendData->RenderPass = windowData->RenderPass; + backendData->Subpass = 0; } void KarmaGuiVulkanHandler::MakeRenderPassInfo(FVulkanSwapChain* SwapChain, FVulkanRenderPassInfo& RPInfo) { FVulkanRenderPassInfo::FAttachmentInfo colorAttachmentInfo; - colorAttachmentInfo.AttachmentFlags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; + colorAttachmentInfo.AttachmentFlags = 0; colorAttachmentInfo.AttachmentFormat = SwapChain->GetSwapChainImageFormat(); colorAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; colorAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -1534,7 +1535,7 @@ namespace Karma RPInfo.m_AttachmentsInfo.Add(colorAttachmentInfo); FVulkanRenderPassInfo::FAttachmentInfo depthAttachmentInfo; - depthAttachmentInfo.AttachmentFlags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; + depthAttachmentInfo.AttachmentFlags = 0; depthAttachmentInfo.AttachmentFormat = FVulkanDynamicRHI::Get().FindSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 139436ff..380a4b41 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -927,7 +927,7 @@ namespace Karma VkPipeline Pipeline; /** - * @brief Not sure what the use of this Subpass is. Seems vestigial? + * @brief This is the index of the subpass in the render pass where this pipeline (KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline) will be used * * @since Karma 1.0.0 */ From 0af01547fabdba88aeca351f00edd2a866a0abec Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 27 Dec 2025 13:46:00 +0530 Subject: [PATCH 048/107] Adding assertion for appropriate initialization and minor comments --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 12 +++------- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 22 ++++++++++++++++++- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 13 ++++++++++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index f1dcf816..f386dbac 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -51,6 +51,9 @@ namespace Karma // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); + // See if all the Vulkan resources have been instantiated + KarmaGuiVulkanHandler::CheckInitialization(); + // Font, descriptor, and pipeline KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); @@ -243,15 +246,6 @@ namespace Karma // Maybe chore for toofani mood! // io.BackendFlags |= KarmaGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - KR_CORE_ASSERT(info->Instance != VK_NULL_HANDLE, "No instance found"); - KR_CORE_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE, "No physical device found"); - KR_CORE_ASSERT(info->Device != VK_NULL_HANDLE, "No device found"); - KR_CORE_ASSERT(info->Queue != VK_NULL_HANDLE, "No queue assigned"); - KR_CORE_ASSERT(info->DescriptorPool != VK_NULL_HANDLE, "No descriptor pool found"); - KR_CORE_ASSERT(info->MinImageCount >= 2, "Minimum image count exceeding limit"); - KR_CORE_ASSERT(info->ImageCount >= info->MinImageCount, "Not enough pitch for ImageCount"); - //(info->RenderPass != VK_NULL_HANDLE, "No renderpass assigned"); - backendData->VulkanInitInfo = *info; //backendData->VulkanInitInfo.Device = info->Device; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index e31bae63..96154f67 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1497,6 +1497,21 @@ namespace Karma } } + void KarmaGuiVulkanHandler::CheckInitialization() + { + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + KarmaGui_ImplVulkan_InitInfo& vulkanInitInfo = backendData->VulkanInitInfo; + + KR_CORE_ASSERT(vulkanInitInfo.Instance != VK_NULL_HANDLE, "No instance found"); + KR_CORE_ASSERT(vulkanInitInfo.PhysicalDevice != VK_NULL_HANDLE, "No physical device found"); + KR_CORE_ASSERT(vulkanInitInfo.Device != VK_NULL_HANDLE, "No device found"); + KR_CORE_ASSERT(vulkanInitInfo.Queue != VK_NULL_HANDLE, "No queue assigned"); + KR_CORE_ASSERT(vulkanInitInfo.DescriptorPool != VK_NULL_HANDLE, "No descriptor pool found"); + KR_CORE_ASSERT(vulkanInitInfo.MinImageCount >= 2, "Minimum image count exceeding limit"); + KR_CORE_ASSERT(vulkanInitInfo.ImageCount >= vulkanInitInfo.MinImageCount, "Not enough pitch for ImageCount"); + KR_CORE_ASSERT(vulkanInitInfo.RenderPass != VK_NULL_HANDLE, "No renderpass assigned"); + } + void KarmaGuiVulkanHandler::FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) { windowData->RHIResources.VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); @@ -1515,7 +1530,9 @@ namespace Karma KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + backendData->VulkanInitInfo.RenderPass = windowData->RenderPass; backendData->RenderPass = windowData->RenderPass; + backendData->Subpass = 0; } @@ -1760,12 +1777,15 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { + + + + FVulkanSwapChainRecreateInfo RI{}; windowData->RHIResources.VulkanSwapChain->Destroy(&RI); delete windowData->RHIResources.VulkanSwapChain; windowData->RHIResources.VulkanSwapChain = nullptr; - KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 380a4b41..b1e9251c 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -1233,8 +1233,19 @@ namespace Karma */ static void KarmaGui_ImplVulkan_SwapBuffers(KarmaGuiViewport* viewport, void*); + static void CheckInitialization(); + /** - * @brief Replacement for ShareVulkanContextResourcesOfMainWindow + * @brief Replacement for ShareVulkanContextResourcesOfMainWindow. Does the following + * + * - Creates Vulkan swapchain and sets the swapchain handle of windowData along with imagecount, MAX_FRAMES_IN_FLIGHT, + * and renderarea. + * - Creates Vulkan renderpass (see KarmaGuiVulkanHandler::MakeRenderPassInfo) for KarmaGui's Vulkan backend and sets + * backend->VulkanInitInfo.RenderPass with this renderpass (of which KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline + * is created) + * + * @note SwapChain and renderpass resources are cleared in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow + * @since Karma 1.0.0 */ static void FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity); From 61b9ec7c9105f2f7c28ff7ed74037a2b8458fc0e Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 29 Dec 2025 17:17:56 +0530 Subject: [PATCH 049/107] Some documentation, and renderpass class --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 26 +++++++++-- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 1 + .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 9 +++- .../Vulkan/VulkanRHI/VulkanRenderPass.cpp | 11 +++++ .../Vulkan/VulkanRHI/VulkanRenderPass.h | 44 ++++++++++++++++++- 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 96154f67..f56ec72c 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1526,7 +1526,8 @@ namespace Karma KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources.VulkanSwapChain, RPInfo); FVulkanRenderTargetLayout RTLayout(RPInfo); - windowData->RenderPass = CreateVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); + windowData->RHIResources.VulkanRenderPass = new FVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); + windowData->RenderPass = windowData->RHIResources.VulkanRenderPass->GetHandle(); KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); @@ -1534,6 +1535,25 @@ namespace Karma backendData->RenderPass = windowData->RenderPass; backendData->Subpass = 0; + + windowData->CommandPool = FVulkanDynamicRHI::Get().GetDevice()->GetCommandPool(); + + KR_CORE_ASSERT(windowData->ImageFrames == nullptr, "Somehow frames are still occupied. Please clear them."); + windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; + + for (uint32_t counter = 0; counter < windowData->TotalImageCount; counter++) + { + KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[counter]; + + // VulkanContext ImageView equivalent + frameData->BackbufferView = VulkanHolder::GetVulkanContext()->GetSwapChainImageViews()[counter]; + + // Framebuffer + frameData->Framebuffer = VulkanHolder::GetVulkanContext()->GetSwapChainFrameBuffer()[counter]; + + // Backbuffers could be VulkanContext m_swapChainImages equivalent + frameData->Backbuffer = VulkanHolder::GetVulkanContext()->GetSwapChainImages()[counter]; + } } void KarmaGuiVulkanHandler::MakeRenderPassInfo(FVulkanSwapChain* SwapChain, FVulkanRenderPassInfo& RPInfo) @@ -1777,9 +1797,7 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { - - - + delete windowData->RHIResources.VulkanRenderPass; FVulkanSwapChainRecreateInfo RI{}; windowData->RHIResources.VulkanSwapChain->Destroy(&RI); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index b1e9251c..62464980 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -291,6 +291,7 @@ namespace Karma struct KarmaGui_ImplVulkanH_RHIResources { class FVulkanSwapChain* VulkanSwapChain; + class FVulkanRenderPass* VulkanRenderPass; }; /** diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 01a3fb54..160678aa 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -78,12 +78,19 @@ namespace Karma VkDevice GetLogicalDevice() const { return m_LogicalDevice; } /** - * @biref Retrieves the Vulkan physical device handle. + * @brief Retrieves the Vulkan physical device handle. * * @return VkPhysicalDevice The Vulkan physical device (GPU) */ VkPhysicalDevice GetGPU() const { return m_GPU; } + /** + * @brief Retrives the Vulkan command pool for the commandbuffers + * + * @since Karma 1.0.0 + */ + VkCommandPool GetCommandPool() const { return m_CommandPool; } + ///////////////// Utility Functions ///////////////// /** * @brief Transitions the layout of an image from oldLayout to newLayout. diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp index edab6f88..b9d140c5 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp @@ -52,4 +52,15 @@ namespace Karma bHasDepthStencil = false; } } + + FVulkanRenderPass::FVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout) : m_Device(Device) + { + m_RenderPass = CreateVulkanRenderPass(m_Device, RTLayout); + m_Layout = RTLayout; + } + + FVulkanRenderPass::~FVulkanRenderPass() + { + vkDestroyRenderPass(m_Device.GetLogicalDevice(), m_RenderPass, nullptr); + } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 02577dae..2179d993 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -342,10 +342,20 @@ namespace Karma FAttachmentRefInfo m_DepthAttachmentReference; }; - + /** + * @brief Data structure for Vulkan's render targets (color buffers and depth buffer) + * + * @since Karma 1.0.0 + */ class FVulkanRenderTargetLayout { public: + /** + * @brief Default constructor provided for FVulkanRenderPass class constructor + * + * @since Karma 1.0.0 + */ + FVulkanRenderTargetLayout() = default; //FVulkanRenderTargetLayout(const FGraphicsPipelineStateInitializer& Initializer); // Experimental layout constructor (layout as in VulkanContext). We need to make this generic somehow @@ -354,6 +364,7 @@ namespace Karma //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHISetRenderTargetsInfo& RTInfo); //FVulkanRenderTargetLayout(FVulkanDevice& InDevice, const FRHIRenderPassInfo& RPInfo, VkImageLayout CurrentDepthLayout, VkImageLayout CurrentStencilLayout); + // Getters inline const VkAttachmentReference* GetColorAttachmentReferences() const { return m_NumColorAttachments > 0 ? m_ColorReferences : nullptr; } inline const VkAttachmentReference* GetDepthAttachmentReference() const { return bHasDepthStencil ? &m_DepthReference : nullptr; } inline const VkAttachmentReferenceStencilLayout* GetStencilAttachmentReference() const { return bHasDepthStencil ? &m_StencilReference : nullptr; } @@ -373,19 +384,50 @@ namespace Karma uint8_t m_NumColorAttachments; uint8_t m_NumAttachmentDescriptions; + /** + * @brief Vulkan attachment descriptions for the rendertargets + * + * @note Just MaxSimultaneousRenderTargets should be fine. Try doing this + */ VkAttachmentDescription m_AttachmentDescriptions[MaxSimultaneousRenderTargets * 2 + 2]; // Do we have a depth stencil uint8_t bHasDepthStencil; }; + /** + * @brief The actual Vulkan renderpass Engine class + * + * @since Karma 1.0.0 + */ class FVulkanRenderPass { public: inline VkRenderPass GetHandle() const { return m_RenderPass; } + inline const FVulkanRenderTargetLayout& GetLayout() const { return m_Layout; } + + // May become private once we have FVulkanRenderPassManager + + //FVulkanRenderPass() = default; + + /** + * @brief Constructor to create a renderpass with supplied rendertarget layout + * + * @since Karma 1.0.0 + */ + FVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout); + + /** + * @brief Destroys renderpass + */ + ~FVulkanRenderPass(); + private: VkRenderPass m_RenderPass; + FVulkanRenderTargetLayout m_Layout; + + FVulkanDevice& m_Device; }; template From f6a7c8a868dc0733a3334cac926ede11057d8f70 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 31 Dec 2025 05:07:56 +0530 Subject: [PATCH 050/107] VulkanRHI Windows: Phase VI --- .../Vulkan/VulkanRHI/VulkanFramebuffer.cpp | 17 ++++++++ .../Vulkan/VulkanRHI/VulkanFramebuffer.h | 42 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp new file mode 100644 index 00000000..a2ea253d --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp @@ -0,0 +1,17 @@ +#include "VulkanFramebuffer.h" + +#include "VulkanDevice.h" +#include "VulkanRenderPass.h" + +namespace Karma +{ + FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass) + { + VkFramebufferCreateInfo CreateInfo; + CreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + CreateInfo.renderPass = RenderPass.GetHandle(); + CreateInfo.attachmentCount = m_ImageViews.Num(); + CreateInfo.pAttachments = m_ImageViews.GetData(); + //CreateInfo.width = + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h new file mode 100644 index 00000000..7c3cc177 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h @@ -0,0 +1,42 @@ +/** + * @file VulkanFramebuffer.h + * @brief Contains the Vulkan framebuffer class to hold pixel color/depth data + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date December 30, 2025 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include + +namespace Karma +{ + // Forward declaration + class FVulkanDevice; + class FVulkanRenderTargetLayout; + + class FVulkanRenderPass; + + /** + * @brief Actual Vulkan framebuffer class + * + * Framebuffers consists of rendertargets which are filled with per pixel information (color/depth). + * Specifically framebuffer consists of VkImages with appropriate VkImageViews which receive fragment shader output + * data. In terms of graphics pipeline stages, this output is stored in framebuffer imageviews after fragment shader + * stage and before color blending stage. + */ + class FVulkanFramebuffer + { + public: + FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass); + + private: + VkFramebuffer m_FrameBuffer; + VkRect2D m_RenderArea; + + KarmaVector m_ImageViews; + }; +} \ No newline at end of file From 37c5b5e556a57311f0367cad46435db47f00c9bd Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 31 Dec 2025 11:04:25 +0530 Subject: [PATCH 051/107] Single framebuffer making done --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 2 ++ .../Vulkan/VulkanRHI/VulkanFramebuffer.cpp | 19 +++++++++++++------ .../Vulkan/VulkanRHI/VulkanFramebuffer.h | 4 +++- .../Vulkan/VulkanRHI/VulkanRenderPass.cpp | 2 ++ .../Vulkan/VulkanRHI/VulkanRenderPass.h | 4 ++++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index f56ec72c..e1d93c23 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1596,6 +1596,8 @@ namespace Karma RPInfo.m_DepthAttachmentReference.attachment = 1; RPInfo.m_DepthAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + RPInfo.m_RenderArea.extent = SwapChain->GetSwapChainExtent(); } void KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp index a2ea253d..ec21c3e6 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp @@ -7,11 +7,18 @@ namespace Karma { FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass) { - VkFramebufferCreateInfo CreateInfo; - CreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - CreateInfo.renderPass = RenderPass.GetHandle(); - CreateInfo.attachmentCount = m_ImageViews.Num(); - CreateInfo.pAttachments = m_ImageViews.GetData(); - //CreateInfo.width = + VkFramebufferCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + createInfo.renderPass = RenderPass.GetHandle(); + createInfo.attachmentCount = m_ImageViews.Num(); + createInfo.pAttachments = m_ImageViews.GetData(); + createInfo.width = RTLayout.GetRenderArea().extent.width; + createInfo.height = RTLayout.GetRenderArea().extent.height; + createInfo.layers = 1; + + VkResult result = vkCreateFramebuffer(Device.GetLogicalDevice(), &createInfo, nullptr, &m_Framebuffer); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create frame buffer"); + + m_RenderArea = RTLayout.GetRenderArea(); } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h index 7c3cc177..bea5abe5 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h @@ -33,8 +33,10 @@ namespace Karma public: FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass); + VkFramebuffer GetHandle() const { return m_Framebuffer; } + private: - VkFramebuffer m_FrameBuffer; + VkFramebuffer m_Framebuffer; VkRect2D m_RenderArea; KarmaVector m_ImageViews; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp index b9d140c5..04be5429 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.cpp @@ -51,6 +51,8 @@ namespace Karma { bHasDepthStencil = false; } + + m_RenderArea = VRPInfo.m_RenderArea; } FVulkanRenderPass::FVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout) : m_Device(Device) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 2179d993..d387216d 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -340,6 +340,7 @@ namespace Karma KarmaVector m_ColorAttachmentsRefInfo; bool bHasDepthAttachment; FAttachmentRefInfo m_DepthAttachmentReference; + VkRect2D m_RenderArea; }; /** @@ -371,6 +372,7 @@ namespace Karma inline uint32_t GetNumColorAttachments() const { return m_NumColorAttachments; } inline uint32_t GetNumAttachmentDescriptions() const { return m_NumAttachmentDescriptions; } inline const VkAttachmentDescription* GetAttachmentDescriptions() const { return m_AttachmentDescriptions; } + inline const VkRect2D& GetRenderArea() const { return m_RenderArea; } private: VkAttachmentReference m_ColorReferences[MaxSimultaneousRenderTargets]; @@ -393,6 +395,8 @@ namespace Karma // Do we have a depth stencil uint8_t bHasDepthStencil; + + VkRect2D m_RenderArea; }; /** From f92455372f09944add36fe2fcbc2e4b2a10c89b5 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 2 Jan 2026 06:13:56 +0530 Subject: [PATCH 052/107] Framebuffer stuff --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 7 +++-- .../Vulkan/VulkanRHI/VulkanFramebuffer.cpp | 12 ++++++- .../Vulkan/VulkanRHI/VulkanFramebuffer.h | 31 ++++++++++++++++++- .../Vulkan/VulkanRHI/VulkanSwapChain.h | 2 +- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index e1d93c23..4079174d 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1538,6 +1538,8 @@ namespace Karma windowData->CommandPool = FVulkanDynamicRHI::Get().GetDevice()->GetCommandPool(); + + KR_CORE_ASSERT(windowData->ImageFrames == nullptr, "Somehow frames are still occupied. Please clear them."); windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; @@ -1545,14 +1547,15 @@ namespace Karma { KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[counter]; + // Backbuffers are swapchain images + frameData->Backbuffer = windowData->RHIResources.VulkanSwapChain->GetSwapChainImages()[counter]; + // VulkanContext ImageView equivalent frameData->BackbufferView = VulkanHolder::GetVulkanContext()->GetSwapChainImageViews()[counter]; // Framebuffer frameData->Framebuffer = VulkanHolder::GetVulkanContext()->GetSwapChainFrameBuffer()[counter]; - // Backbuffers could be VulkanContext m_swapChainImages equivalent - frameData->Backbuffer = VulkanHolder::GetVulkanContext()->GetSwapChainImages()[counter]; } } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp index ec21c3e6..62d35cd4 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp @@ -5,8 +5,18 @@ namespace Karma { - FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass) + FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass) { + for (const auto& renderTarget : InRTInfo.m_ColorRenderTargets.m_ColorRenderTargetViews) + { + m_ImageViews.Add(renderTarget); + } + + if (InRTInfo.bDepthRenderTarget) + { + m_ImageViews.Add(InRTInfo.m_DepthRenderTarget.m_DepthRenderTargetView); + } + VkFramebufferCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; createInfo.renderPass = RenderPass.GetHandle(); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h index bea5abe5..e9fa5c73 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h @@ -20,6 +20,35 @@ namespace Karma class FVulkanRenderPass; + /** + * @brief May move to more abstract since any graphic api should use such render targets' information + * + * @note UE uses FRHISetRenderTargetsInfo + */ + class FVulkanRenderTargetsInfo + { + public: + struct ColorRenderTarget + { + VkImage m_ColorRenderTargetImages[MaxSimultaneousRenderTargets]; + VkImageView m_ColorRenderTargetViews[MaxSimultaneousRenderTargets]; + }; + + struct DepthRenderTarget + { + VkImage m_DepthRenderTargetImage; + VkImageView m_DepthRenderTargetView; + }; + + // Color rendertargets information + ColorRenderTarget m_ColorRenderTargets; + uint32_t m_NumColorRenderTargets; + + // Depth render target information + bool bDepthRenderTarget = false; + DepthRenderTarget m_DepthRenderTarget; + }; + /** * @brief Actual Vulkan framebuffer class * @@ -31,7 +60,7 @@ namespace Karma class FVulkanFramebuffer { public: - FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass); + FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass); VkFramebuffer GetHandle() const { return m_Framebuffer; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 8d6e1c14..ca558ac5 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -40,7 +40,7 @@ namespace Karma inline VkExtent2D GetSwapChainExtent() const { return m_SwapChainExtent; } inline uint32_t GetMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } - + inline const std::vector& GetSwapChainImages() const { return m_SwapChainImages; } inline VkFormat GetSwapChainImageFormat() const { return m_SwapChainImageFormat; } private: From 20763bdc0c30fdb3ff466ca22cd126e4136d7f14 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Tue, 6 Jan 2026 16:49:57 +0530 Subject: [PATCH 053/107] VulkanRHI Mac - Phase VII 1. Modified engine code for entry/exit of RHI context to help organise proper creation/destruction of VulkanResources 2. Using Macbook to further the VulkanRHI work --- Karma/src/Karma/EntryPoint.h | 2 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 + Karma/src/Karma/LayerStack.cpp | 2 +- .../Platform/Mac/KarmaRHI/MacDynamicRHI.cpp | 3 +- Karma/src/Platform/Mac/MacWindow.cpp | 9 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 105 +++++++++++++++++- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 32 +++++- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 13 ++- .../Vulkan/VulkanRHI/VulkanFramebuffer.cpp | 20 +++- .../Vulkan/VulkanRHI/VulkanFramebuffer.h | 29 +++-- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 19 +++- .../Vulkan/VulkanRHI/VulkanSwapChain.cpp | 31 +++++- .../Vulkan/VulkanRHI/VulkanSwapChain.h | 4 +- Pranjal/Source/Private/EditorLayer.cpp | 2 +- 14 files changed, 236 insertions(+), 38 deletions(-) diff --git a/Karma/src/Karma/EntryPoint.h b/Karma/src/Karma/EntryPoint.h index 9b49b590..e53a2f84 100644 --- a/Karma/src/Karma/EntryPoint.h +++ b/Karma/src/Karma/EntryPoint.h @@ -117,7 +117,7 @@ int main(int argc, char** argv) Karma::Input::Init(); app->PrepareApplicationForRun(); - app->Run(); + //app->Run();<-- to be uncommented when we have RHI delete app; Karma::RenderCommand::DeInit(); diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index f386dbac..c2100aba 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -50,6 +50,9 @@ namespace Karma // Since VulkanContext has already instantiated fresh swapchain and commandbuffers, we send that false // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); + + // To be removed later when RHI is functional + return; // <-- to be uncommented when we have RHI (actually removed) // See if all the Vulkan resources have been instantiated KarmaGuiVulkanHandler::CheckInitialization(); diff --git a/Karma/src/Karma/LayerStack.cpp b/Karma/src/Karma/LayerStack.cpp index ecd2aaf9..a7ca1287 100644 --- a/Karma/src/Karma/LayerStack.cpp +++ b/Karma/src/Karma/LayerStack.cpp @@ -5,7 +5,7 @@ * In destructor, now those pointers were no longer there, that memory will never * be freed. Isn't this a memory leak. * - * The Cherno talk about this stuff here https://youtu.be/_Kj6BSfM6P4?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT&t=717 + * The Cherno talks about this stuff here https://youtu.be/_Kj6BSfM6P4?list=PLlrATfBNZ98dC-V-N3m0Go4deliWHPFwT&t=717 * but the above issue doesn't seem to get resolved. */ diff --git a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp index 5aa2dfb6..91625ecd 100644 --- a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp +++ b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp @@ -5,11 +5,10 @@ namespace Karma { - #ifdef KR_MAC_PLATFORM FDynamicRHI* PlatformCreateDynamicRHI() { - FDynamicRHI* DynamicRHI = FDynamicRHI::Create(); + FDynamicRHI* DynamicRHI = FDynamicRHI::CreateRHI(); if (DynamicRHI) { diff --git a/Karma/src/Platform/Mac/MacWindow.cpp b/Karma/src/Platform/Mac/MacWindow.cpp index e15e91ca..0b18344a 100644 --- a/Karma/src/Platform/Mac/MacWindow.cpp +++ b/Karma/src/Platform/Mac/MacWindow.cpp @@ -81,7 +81,8 @@ namespace Karma m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr); - switch (currentAPI) + // To be replaced with RHI + /*switch (currentAPI) { case RendererAPI::API::None: KR_CORE_ASSERT(false, "RendererAPI::None is not supported"); @@ -94,7 +95,7 @@ namespace Karma break; } - m_Context->Init(); + m_Context->Init();*/ SetVSync(true); // Used for event callbacks @@ -248,8 +249,8 @@ namespace Karma } case RendererAPI::API::Vulkan: { - VulkanContext* vContext = static_cast(m_Context); - vContext->SetVSync(enabled); + /*VulkanContext* vContext = static_cast(m_Context); + vContext->SetVSync(enabled);*/ break; } case RendererAPI::API::None: diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 4079174d..5706abe6 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -7,6 +7,7 @@ #include "VulkanRHI/VulkanSwapChain.h" #include "VulkanRHI/VulkanDynamicRHI.h" #include "VulkanRHI/VulkanRenderPass.h" +#include "VulkanRHI/VulkanFramebuffer.h" // Visual Studio warnings /*#ifdef _MSC_VER @@ -1538,11 +1539,17 @@ namespace Karma windowData->CommandPool = FVulkanDynamicRHI::Get().GetDevice()->GetCommandPool(); - - KR_CORE_ASSERT(windowData->ImageFrames == nullptr, "Somehow frames are still occupied. Please clear them."); windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; + FVulkanRenderTargetsInfo* rT = new FVulkanRenderTargetsInfo(); + windowData->RHIResources.RenderTargets.Add(rT); + + FVulkanRenderTargetsInfo& renderTargets = *rT; + CreateDepthRenderTarget(renderTargets, windowData->RHIResources.VulkanSwapChain); + + renderTargets.m_ColorRenderTargets.resize(windowData->TotalImageCount); + for (uint32_t counter = 0; counter < windowData->TotalImageCount; counter++) { KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[counter]; @@ -1550,15 +1557,81 @@ namespace Karma // Backbuffers are swapchain images frameData->Backbuffer = windowData->RHIResources.VulkanSwapChain->GetSwapChainImages()[counter]; - // VulkanContext ImageView equivalent - frameData->BackbufferView = VulkanHolder::GetVulkanContext()->GetSwapChainImageViews()[counter]; + // BackbufferViews are swapchain image views + frameData->BackbufferView = windowData->RHIResources.VulkanSwapChain->GetSwapChainImageViews()[counter]; + GatherSwapChainColorRenderTargets(renderTargets, windowData->RHIResources.VulkanSwapChain, counter); + + FVulkanFramebuffer* frameBuffer = new FVulkanFramebuffer(*FVulkanDynamicRHI::Get().GetDevice(), renderTargets, RTLayout, *windowData->RHIResources.VulkanRenderPass, counter); + + windowData->RHIResources.VulkanFrameBuffers.Add(frameBuffer); + // Framebuffer - frameData->Framebuffer = VulkanHolder::GetVulkanContext()->GetSwapChainFrameBuffer()[counter]; - + frameData->Framebuffer = frameBuffer->GetHandle(); } } + void KarmaGuiVulkanHandler::CreateDepthRenderTarget(FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain) + { + VkFormat depthFormat = FVulkanDynamicRHI::Get().FindSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = SwapChain->GetSwapChainExtent().width; + imageInfo.extent.height = SwapChain->GetSwapChainExtent().height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = depthFormat; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; + + VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); + + VkResult result = vkCreateImage(logicalDevice, &imageInfo, nullptr, &RTInfo.m_DepthRenderTarget.m_DepthRTImage); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create depthimage!"); + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(logicalDevice, RTInfo.m_DepthRenderTarget.m_DepthRTImage, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VkResult result1 = vkAllocateMemory(logicalDevice, &allocInfo, nullptr, &RTInfo.m_DepthRenderTarget.m_DepthRTDeviceMemory); + KR_CORE_ASSERT(result1 == VK_SUCCESS, "Failed to allocate depth image memeory"); + + vkBindImageMemory(logicalDevice, RTInfo.m_DepthRenderTarget.m_DepthRTImage, RTInfo.m_DepthRenderTarget.m_DepthRTDeviceMemory, 0); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = RTInfo.m_DepthRenderTarget.m_DepthRTImage; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = depthFormat; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + result = vkCreateImageView(logicalDevice, &viewInfo, nullptr, &RTInfo.m_DepthRenderTarget.m_DepthRTView); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create depth imageview"); + + RTInfo.bDepthRenderTarget = true; + } + + void KarmaGuiVulkanHandler::GatherSwapChainColorRenderTargets(class FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain, uint32_t SwapChainImageIndex) + { + //RTInfo.m_ColorRenderTargets.m_ColorRenderTargetViews[0] = SwapChain->GetSwapChainImageViews()[0]; + + // m_ColorRTViews[0] is the first color attachment + RTInfo.m_ColorRenderTargets[SwapChainImageIndex].m_ColorRTViews[0] = SwapChain->GetSwapChainImageViews()[SwapChainImageIndex]; + } + void KarmaGuiVulkanHandler::MakeRenderPassInfo(FVulkanSwapChain* SwapChain, FVulkanRenderPassInfo& RPInfo) { FVulkanRenderPassInfo::FAttachmentInfo colorAttachmentInfo; @@ -1802,12 +1875,32 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { + for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers) + { + delete vulkanFramebuffer; + } + + VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); + + // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy + for(const auto& renderTargets : windowData->RHIResources.RenderTargets) + { + if(renderTargets->bDepthRenderTarget) + { + vkDestroyImageView(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTView, nullptr); + vkDestroyImage(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTImage, nullptr); + vkFreeMemory(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTDeviceMemory, nullptr); + } + } + delete windowData->RHIResources.VulkanRenderPass; FVulkanSwapChainRecreateInfo RI{}; windowData->RHIResources.VulkanSwapChain->Destroy(&RI); delete windowData->RHIResources.VulkanSwapChain; windowData->RHIResources.VulkanSwapChain = nullptr; + + return; // <-- to be uncommented when we have RHI (actually removed) KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 62464980..47bc2428 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -24,6 +24,10 @@ namespace Karma { + class FVulkanSwapChain; + class FVulkanRenderPass; + class FVulkanRenderTargetsInfo; + class FVulkanFramebuffer; /** * @brief Macro for vulkan's alignment needs. @@ -290,8 +294,10 @@ namespace Karma */ struct KarmaGui_ImplVulkanH_RHIResources { - class FVulkanSwapChain* VulkanSwapChain; - class FVulkanRenderPass* VulkanRenderPass; + FVulkanSwapChain* VulkanSwapChain; + FVulkanRenderPass* VulkanRenderPass; + KarmaVector VulkanFrameBuffers; + KarmaVector RenderTargets; // RTs corresponding to framebuffers of a type }; /** @@ -426,8 +432,10 @@ namespace Karma uint32_t MAX_FRAMES_IN_FLIGHT; /** - * @brief Just a container for buffers (framebuffers, images, and imageviews etc) and all those sizes depending on VulkanHolder::GetVulkanContext()->GetSwapChainImages().size(); + * @brief Just a container for rendertarget buffers (framebuffers, images, and imageviews etc) and all those sizes depending on number of swapchain images * + * @note Only SwapChain rendertargets are stored. The grand list of rendertargets (all rendertargets) is in KarmaGui_ImplVulkanH_RHIResources::RenderTargets + * @see KarmaGuiVulkanHandler::GatherSwapChainColorRenderTargets * @since Karma 1.0.0 */ KarmaGui_ImplVulkanH_ImageFrame* ImageFrames; @@ -451,7 +459,7 @@ namespace Karma VkRect2D RenderArea; /** - * @brief Vulkan RHI resources + * @brief Vulkan RHI resources containing pointers to FVulkanSwpaChain etc * * @since Karma 1.0.0 */ @@ -1250,7 +1258,21 @@ namespace Karma */ static void FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity); - static void MakeRenderPassInfo(class FVulkanSwapChain* SwapChain, struct FVulkanRenderPassInfo& RPInfo); + static void MakeRenderPassInfo(FVulkanSwapChain* SwapChain, struct FVulkanRenderPassInfo& RPInfo); + + /** + * @brief Fills the depth render target information + * + * @since Karma 1.0.0 + */ + static void CreateDepthRenderTarget(FVulkanRenderTargetsInfo& RTInfo, FVulkanSwapChain* SwapChain); + + /** + * @brief Prepares the RTInfo for framebuffer creation + * + * @since Karma 1.0.0 + */ + static void GatherSwapChainColorRenderTargets(FVulkanRenderTargetsInfo& RTInfo, FVulkanSwapChain* SwapChain, uint32_t SwapChainImageIndex); /** * @brief The purpose of the routine is two-fold diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 2856e4d9..f2a938b4 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -250,6 +250,17 @@ namespace Karma */ VkFormat FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const; + /** + * @brief Finds a suitable memory type on the physical device (graphics card) based on the type filter and desired properties. + * + * @param typeFilter Bitmask specifying the acceptable memory types + * @param properties Desired memory properties (like VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT for CPU access) + * + * @see KarmaGuiVulkanHandler::CreateDepthRenderTarget + * @since Karma 1.0.0 + */ + uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + inline GLFWwindow* GetSurfaceWindow() const { return m_WindowHandle; } inline VkSurfaceKHR GetSurface() const { return m_Surface; } @@ -460,4 +471,4 @@ namespace Karma SwapChainSupportDetails m_GPUSwapChainSupport; uint32_t m_SwapChainImageCount; }; -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp index 62d35cd4..7f2282c7 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.cpp @@ -5,19 +5,22 @@ namespace Karma { - FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass) + FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass, uint32_t SwapchainImageIndex) : m_Device(Device) { - for (const auto& renderTarget : InRTInfo.m_ColorRenderTargets.m_ColorRenderTargetViews) + for (const auto& renderTarget : InRTInfo.m_ColorRenderTargets[SwapchainImageIndex].m_ColorRTViews) { - m_ImageViews.Add(renderTarget); + if(renderTarget != 0) + { + m_ImageViews.Add(renderTarget); + } } if (InRTInfo.bDepthRenderTarget) { - m_ImageViews.Add(InRTInfo.m_DepthRenderTarget.m_DepthRenderTargetView); + m_ImageViews.Add(InRTInfo.m_DepthRenderTarget.m_DepthRTView); } - VkFramebufferCreateInfo createInfo; + VkFramebufferCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; createInfo.renderPass = RenderPass.GetHandle(); createInfo.attachmentCount = m_ImageViews.Num(); @@ -31,4 +34,9 @@ namespace Karma m_RenderArea = RTLayout.GetRenderArea(); } -} \ No newline at end of file + + FVulkanFramebuffer::~FVulkanFramebuffer() + { + vkDestroyFramebuffer(m_Device.GetLogicalDevice(), m_Framebuffer, nullptr); + } +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h index e9fa5c73..5786f88a 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h @@ -11,6 +11,7 @@ #pragma once #include +#include "VulkanRenderPass.h" namespace Karma { @@ -30,18 +31,26 @@ namespace Karma public: struct ColorRenderTarget { - VkImage m_ColorRenderTargetImages[MaxSimultaneousRenderTargets]; - VkImageView m_ColorRenderTargetViews[MaxSimultaneousRenderTargets]; + VkImage m_ColorRTImages[MaxSimultaneousRenderTargets]; + + VkDeviceMemory m_ColorRTDeviceMemory[MaxSimultaneousRenderTargets]; + VkImageView m_ColorRTViews[MaxSimultaneousRenderTargets]; }; struct DepthRenderTarget { - VkImage m_DepthRenderTargetImage; - VkImageView m_DepthRenderTargetView; + VkImage m_DepthRTImage; + + VkDeviceMemory m_DepthRTDeviceMemory; + VkImageView m_DepthRTView; }; - // Color rendertargets information - ColorRenderTarget m_ColorRenderTargets; + /** + * @brief Array of of color rendertargets for each swapchain image. + * + * @note number of swapchain images (FVulkanDynamicRHI::m_SwapChainImageCount) is size of this vector + */ + std::vector m_ColorRenderTargets; uint32_t m_NumColorRenderTargets; // Depth render target information @@ -60,14 +69,18 @@ namespace Karma class FVulkanFramebuffer { public: - FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass); + FVulkanFramebuffer(FVulkanDevice& Device, const FVulkanRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass, uint32_t SwapchainImageIndex); VkFramebuffer GetHandle() const { return m_Framebuffer; } + + ~FVulkanFramebuffer(); private: + class FVulkanDevice& m_Device; + VkFramebuffer m_Framebuffer; VkRect2D m_RenderArea; KarmaVector m_ImageViews; }; -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 8a08d671..ebde63ae 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -162,6 +162,23 @@ namespace Karma } } + uint32_t FVulkanDynamicRHI::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) + { + if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + return i; + } + } + + KR_CORE_ASSERT(false, "Failed to find suitable memory type for imagebuffer"); + return 0; + } + VkFormat FVulkanDynamicRHI::FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const { for (VkFormat format : candidates) @@ -528,4 +545,4 @@ namespace Karma func(instance, debugMessenger, pAllocator); } } -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp index 602556ac..0b2facf9 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.cpp @@ -71,11 +71,40 @@ namespace Karma m_SwapChainImageFormat = m_SurfaceFormat.format; m_SwapChainExtent = extent; + + m_SwapChainImageViews.resize(m_SwapChainImages.size()); + + for (size_t i = 0; i < m_SwapChainImages.size(); i++) + { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = m_SwapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_SwapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + VkResult result = vkCreateImageView(InDevice->GetLogicalDevice(), &createInfo, nullptr, &m_SwapChainImageViews[i]); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create image views!"); + } } void FVulkanSwapChain::Destroy(FVulkanSwapChainRecreateInfo * RecreateInfo) { m_Device->WaitUntilIdle(); + for (auto imageView : m_SwapChainImageViews) + { + vkDestroyImageView(m_Device->GetLogicalDevice(), imageView, nullptr); + } + vkDestroySwapchainKHR(m_Device->GetLogicalDevice(), m_SwapChain, nullptr); } @@ -134,4 +163,4 @@ namespace Karma return actualExtent; } } -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index ca558ac5..0d0a994e 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -41,6 +41,7 @@ namespace Karma inline VkExtent2D GetSwapChainExtent() const { return m_SwapChainExtent; } inline uint32_t GetMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } inline const std::vector& GetSwapChainImages() const { return m_SwapChainImages; } + inline const std::vector& GetSwapChainImageViews() const { return m_SwapChainImageViews; } inline VkFormat GetSwapChainImageFormat() const { return m_SwapChainImageFormat; } private: @@ -103,6 +104,7 @@ namespace Karma VkSwapchainKHR m_SwapChain = VK_NULL_HANDLE; std::vector m_SwapChainImages; + std::vector m_SwapChainImageViews; VkFormat m_SwapChainImageFormat; VkExtent2D m_SwapChainExtent; @@ -124,4 +126,4 @@ namespace Karma // Clearly, MAX_FRAMES_IN_FLIGHT shouldn't exceed m_SwapChainImages.size() const uint32_t MAX_FRAMES_IN_FLIGHT = 2; }; -} \ No newline at end of file +} diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 55e223cb..732cec8c 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -410,7 +410,7 @@ class KarmaApp : public Karma::Application public: KarmaApp() { - // To be uncommented when we have fully functional RHI + return; // <-- to be uncommented when we have RHI (actually removed) PushLayer(new Karma::EditorLayer()); } }; From 0a3fcc8cf9b0993571af813a1514c1fe449aff03 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Wed, 7 Jan 2026 03:24:36 +0530 Subject: [PATCH 054/107] Minor logging, and other code enhancements --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 25 ++++++++++++++++--- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 2 +- .../Vulkan/VulkanRHI/VulkanFramebuffer.h | 2 ++ Karma/src/Platform/Vulkan/VulkanTexture.cpp | 23 +++-------------- Karma/src/Platform/Vulkan/VulkanTexture.h | 11 -------- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 5706abe6..5de73bd6 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1518,11 +1518,12 @@ namespace Karma windowData->RHIResources.VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); windowData->Swapchain = windowData->RHIResources.VulkanSwapChain->GetSwapChainHandle(); - windowData->TotalImageCount = FVulkanDynamicRHI::Get().GetDevice()->GetVulkanDynamicRHI()->SwapChainImageCount(); + windowData->TotalImageCount = FVulkanDynamicRHI::Get().SwapChainImageCount(); windowData->RenderArea.extent = windowData->RHIResources.VulkanSwapChain->GetSwapChainExtent(); windowData->RenderArea.offset = { 0, 0 }; windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + KR_CORE_INFO("Attempting to create primary Vulkan renderpass for on screen presentation"); FVulkanRenderPassInfo RPInfo; KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources.VulkanSwapChain, RPInfo); @@ -1626,10 +1627,10 @@ namespace Karma void KarmaGuiVulkanHandler::GatherSwapChainColorRenderTargets(class FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain, uint32_t SwapChainImageIndex) { - //RTInfo.m_ColorRenderTargets.m_ColorRenderTargetViews[0] = SwapChain->GetSwapChainImageViews()[0]; - - // m_ColorRTViews[0] is the first color attachment RTInfo.m_ColorRenderTargets[SwapChainImageIndex].m_ColorRTViews[0] = SwapChain->GetSwapChainImageViews()[SwapChainImageIndex]; + + RTInfo.m_ColorRenderTargets[SwapChainImageIndex].bSwapChainColorRenderTarget = true; + RTInfo.m_NumColorRenderTargets = 1; } void KarmaGuiVulkanHandler::MakeRenderPassInfo(FVulkanSwapChain* SwapChain, FVulkanRenderPassInfo& RPInfo) @@ -1883,14 +1884,30 @@ namespace Karma VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy + // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ for(const auto& renderTargets : windowData->RHIResources.RenderTargets) { + for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) + { + if(!renderTargets->m_ColorRenderTargets[counter].bSwapChainColorRenderTarget) + { + for(uint32_t colorRT = 0; colorRT < renderTargets->m_NumColorRenderTargets; colorRT++) + { + vkDestroyImageView(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTViews[colorRT], nullptr); + vkDestroyImage(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTImages[colorRT], nullptr); + vkFreeMemory(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTDeviceMemory[colorRT], nullptr); + } + } + } + if(renderTargets->bDepthRenderTarget) { vkDestroyImageView(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTView, nullptr); vkDestroyImage(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTImage, nullptr); vkFreeMemory(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTDeviceMemory, nullptr); } + + delete renderTargets; } delete windowData->RHIResources.VulkanRenderPass; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 47bc2428..c288355e 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -295,7 +295,7 @@ namespace Karma struct KarmaGui_ImplVulkanH_RHIResources { FVulkanSwapChain* VulkanSwapChain; - FVulkanRenderPass* VulkanRenderPass; + FVulkanRenderPass* VulkanRenderPass; // Primary renderpass for on-screen presentation KarmaVector VulkanFrameBuffers; KarmaVector RenderTargets; // RTs corresponding to framebuffers of a type }; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h index 5786f88a..4aa79e25 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanFramebuffer.h @@ -35,6 +35,8 @@ namespace Karma VkDeviceMemory m_ColorRTDeviceMemory[MaxSimultaneousRenderTargets]; VkImageView m_ColorRTViews[MaxSimultaneousRenderTargets]; + + bool bSwapChainColorRenderTarget = false; }; struct DepthRenderTarget diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.cpp b/Karma/src/Platform/Vulkan/VulkanTexture.cpp index 47fa992c..cdee5960 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.cpp +++ b/Karma/src/Platform/Vulkan/VulkanTexture.cpp @@ -1,7 +1,7 @@ #include "VulkanTexture.h" #include "VulkanHolder.h" - #include "VulkanRHI/VulkanDevice.h" +#include "VulkanRHI/VulkanDynamicRHI.h" namespace Karma { @@ -72,7 +72,7 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VkResult result1 = vkAllocateMemory(m_LogicalDevice, &allocInfo, nullptr, &m_TextureImageMemory); KR_CORE_ASSERT(result1 == VK_SUCCESS, "Failed to allocate image memeory"); @@ -83,23 +83,6 @@ namespace Karma m_Device->TransitionImageLayout(m_TextureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } - uint32_t VulkanTexture::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(m_GPU, &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) - { - if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - - KR_CORE_ASSERT(false, "Failed to find suitable memory type for imagebuffer"); - return 0; - } - void VulkanTexture::CreateTextureImageView() { VkImageViewCreateInfo viewInfo{}; @@ -146,4 +129,4 @@ namespace Karma KR_CORE_ASSERT(result == false, "Failed to create texture sampler!"); } -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanTexture.h b/Karma/src/Platform/Vulkan/VulkanTexture.h index 0cee25a0..0cf48825 100644 --- a/Karma/src/Platform/Vulkan/VulkanTexture.h +++ b/Karma/src/Platform/Vulkan/VulkanTexture.h @@ -105,17 +105,6 @@ namespace Karma * @since Karma 1.0.0 */ void GenerateVulkanTexture(VulkanImageBuffer* vImageBuffer); - - /** - * @brief Finds a suitable memory type on the physical device (graphics card) based on the type filter and desired properties. - * - * @param typeFilter Bitmask specifying the acceptable memory types - * @param properties Desired memory properties (like VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT for CPU access) - * - * @see VulkanBuffer::VulkanBuffer() - * @since Karma 1.0.0 - */ - uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); // Getters VkImageView GetImageView() const { return m_TextureImageView; } From 4b0f70c47dc7e7d647c2bfae7a687fa5a1b45c8a Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Wed, 7 Jan 2026 18:30:58 +0530 Subject: [PATCH 055/107] KarmaGuiVulkanHandler is KarmaGuiRenderer::SetUpKarmaGuiRenderer happy 1. KarmaGuiRenderer::SetUpKarmaGuiRenderer is functional --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 - .../Platform/Mac/KarmaRHI/MacDynamicRHI.cpp | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 78 +++++++++++++++++-- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 2 +- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index c2100aba..f386dbac 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -50,9 +50,6 @@ namespace Karma // Since VulkanContext has already instantiated fresh swapchain and commandbuffers, we send that false // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); - - // To be removed later when RHI is functional - return; // <-- to be uncommented when we have RHI (actually removed) // See if all the Vulkan resources have been instantiated KarmaGuiVulkanHandler::CheckInitialization(); diff --git a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp index 91625ecd..74e92f6a 100644 --- a/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp +++ b/Karma/src/Platform/Mac/KarmaRHI/MacDynamicRHI.cpp @@ -12,7 +12,7 @@ namespace Karma if (DynamicRHI) { - KR_CORE_INFO("Created DynamicRHI MacOS"); + KR_CORE_INFO("Created DynamicRHI for MacOS"); } return DynamicRHI; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 5de73bd6..dd8c56bc 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1523,7 +1523,7 @@ namespace Karma windowData->RenderArea.offset = { 0, 0 }; windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); - KR_CORE_INFO("Attempting to create primary Vulkan renderpass for on screen presentation"); + KR_CORE_INFO("Attempting to create primary Vulkan renderpass for on-screen presentation"); FVulkanRenderPassInfo RPInfo; KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources.VulkanSwapChain, RPInfo); @@ -1570,6 +1570,61 @@ namespace Karma // Framebuffer frameData->Framebuffer = frameBuffer->GetHandle(); } + + // We create seperate syncronicity resources for Dear KarmaGui + if (bCreateSyncronicity) + { + // For syncronicity, we instantiate FramesOnFlight with MAX_FRAMES_IN_FLIGHT number and label them the Real-Frames-On-Flight in our mind + KR_CORE_ASSERT(windowData->FramesOnFlight == nullptr, "Somehow frames-on-flight are still occupied. Please clear them."); + + windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; + + for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) + { + KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; + + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + VkResult result = vkCreateFence(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &fenceInfo, VK_NULL_HANDLE, &frameOnFlight->Fence); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence"); + + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + result = vkCreateSemaphore(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &semaphoreInfo, VK_NULL_HANDLE, &frameOnFlight->ImageAcquiredSemaphore); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create ImageAcquiredSemaphore"); + + result = vkCreateSemaphore(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &semaphoreInfo, VK_NULL_HANDLE, &frameOnFlight->RenderCompleteSemaphore); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create RenderCompleteSemaphore"); + } + + windowData->SemaphoreIndex = 0; + } + + KR_CORE_ASSERT(windowData->FramesOnFlight != nullptr, "Commandbuffers are being assigned without enough resources"); + + std::vector commandBuffers; + commandBuffers.resize(windowData->MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = windowData->CommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); + + VkResult result = vkAllocateCommandBuffers(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &allocInfo, commandBuffers.data()); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create command buffers!"); + + for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) + { + KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; + frameOnFlight->CommandBuffer = commandBuffers[counter]; + } } void KarmaGuiVulkanHandler::CreateDepthRenderTarget(FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain) @@ -1876,13 +1931,28 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { + VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); + + std::vector commandBuffers; + commandBuffers.resize(windowData->MAX_FRAMES_IN_FLIGHT); + + for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) + { + KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; + commandBuffers[counter] = frameOnFlight->CommandBuffer; + } + + vkFreeCommandBuffers(logicalDevice, windowData->CommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + // 1. Removes synchronicity + // 2. Deletes imageframes and frames on flight objects + ClearVulkanWindowData(windowData, true); + for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers) { delete vulkanFramebuffer; } - VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); - // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ for(const auto& renderTargets : windowData->RHIResources.RenderTargets) @@ -1948,8 +2018,6 @@ namespace Karma } // Assuming only on graphicspipeline VulkanHolder::GetVulkanContext()->CleanUpKarmaGuiGeneralGraphicsPipeline(); - - ClearVulkanWindowData(windowData, true); } //-------------------------------------------------------------------------------------------------------- diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index ebde63ae..ae25c8c8 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -16,7 +16,7 @@ namespace Karma FVulkanDynamicRHI::FVulkanDynamicRHI() { - KR_CORE_INFO("Initializing Vulkan RHI..."); + KR_CORE_INFO("Initializing Vulkan RHI.."); CreateInstance(); SetupDebugMessenger(); From ec3cfdc15781e86ee432590a293d8dd0ddc90051 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Thu, 8 Jan 2026 05:30:01 +0530 Subject: [PATCH 056/107] Moving forward with KarmaGuiLayer and ApplicationLayer --- Karma/src/Karma/Application.cpp | 8 ++++---- Karma/src/Karma/EntryPoint.h | 2 +- Karma/src/Platform/Windows/WindowsWindow.cpp | 2 +- Pranjal/Source/Private/EditorLayer.cpp | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index 26b34ed6..62b697d7 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -37,8 +37,8 @@ namespace Karma m_LayerStack = new LayerStack(); // Graphics API Vulkan or OpenGL should have been completely initialized by here - m_KarmaGuiLayer = new KarmaGuiLayer(m_Window);// <-- to be uncommented when we have RHI - PushOverlay(m_KarmaGuiLayer);//<-- to be uncommented when we have RHI + m_KarmaGuiLayer = new KarmaGuiLayer(m_Window); + PushOverlay(m_KarmaGuiLayer); } Application::~Application() @@ -120,14 +120,14 @@ namespace Karma } // KarmaGui rendering sequence cue trickling through stack - //m_KarmaGuiLayer->Begin(); <-- to be uncommented when we have RHI + m_KarmaGuiLayer->Begin(); for (auto layer : *m_LayerStack) { layer->KarmaGuiRender(deltaTime); } - //m_KarmaGuiLayer->End();<-- to be uncommented when we have RHI + m_KarmaGuiLayer->End(); m_Window->OnUpdate(); } diff --git a/Karma/src/Karma/EntryPoint.h b/Karma/src/Karma/EntryPoint.h index e53a2f84..9b49b590 100644 --- a/Karma/src/Karma/EntryPoint.h +++ b/Karma/src/Karma/EntryPoint.h @@ -117,7 +117,7 @@ int main(int argc, char** argv) Karma::Input::Init(); app->PrepareApplicationForRun(); - //app->Run();<-- to be uncommented when we have RHI + app->Run(); delete app; Karma::RenderCommand::DeInit(); diff --git a/Karma/src/Platform/Windows/WindowsWindow.cpp b/Karma/src/Platform/Windows/WindowsWindow.cpp index 299f56f3..3cecfc2e 100644 --- a/Karma/src/Platform/Windows/WindowsWindow.cpp +++ b/Karma/src/Platform/Windows/WindowsWindow.cpp @@ -63,7 +63,7 @@ namespace Karma m_Window = glfwCreateWindow((int)m_Data.Width, (int)m_Data.Height, m_Data.Title.c_str(), nullptr, nullptr); - // To be replaced with RHI + // To be uncommented when we have RHI /*switch (currentAPI) { case RendererAPI::API::None: diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 732cec8c..8d4bb9b1 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -410,7 +410,6 @@ class KarmaApp : public Karma::Application public: KarmaApp() { - return; // <-- to be uncommented when we have RHI (actually removed) PushLayer(new Karma::EditorLayer()); } }; From 879d00888d14cafb2b3f0463ee7bec5204165def Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 10 Jan 2026 16:21:33 +0530 Subject: [PATCH 057/107] Vulkanbuffer: VulkanContext replaced with VulkanRHI --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 5 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h | 5 + Karma/src/Karma/KarmaRHI/DynamicRHI.h | 8 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 1 + .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 16 ++- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 123 ++++-------------- Karma/src/Platform/Vulkan/VulkanBuffer.h | 51 -------- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 6 +- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 65 ++++++++- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 15 ++- 10 files changed, 127 insertions(+), 168 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index f386dbac..250847f6 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -461,12 +461,13 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't begin commandbuffer recording"); } + //VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); + FVulkanDynamicRHI::Get().UploadUniformBufferObjects(windowData->SemaphoreIndex); + for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { std::shared_ptr scene3D = it->Scene3D; - VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); - FrameDescriptorSets dSets; if (VulkanHolder::GetVulkanContext()->GetGeneralDescriptorSets().size() > 0) { diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h index 0ba56a4c..fb6d1ff3 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h @@ -120,6 +120,11 @@ namespace Karma * @since Karma 1.0.0 */ static KGTextureID Add3DSceneFor2DRendering(std::shared_ptr scene, KGVec2 dimensions); + + /** + * @brief Getter for m_VulkanWindowData + */ + static const KarmaGui_ImplVulkanH_Window& GetWindowData() { return m_VulkanWindowData; } private: diff --git a/Karma/src/Karma/KarmaRHI/DynamicRHI.h b/Karma/src/Karma/KarmaRHI/DynamicRHI.h index 4e387f39..31e4defd 100644 --- a/Karma/src/Karma/KarmaRHI/DynamicRHI.h +++ b/Karma/src/Karma/KarmaRHI/DynamicRHI.h @@ -41,8 +41,10 @@ namespace Karma virtual bool Init() = 0; /** * @brief Shuts down the RHI. - * - * Cleans up resources and states used by the RHI. + * + * Handles shutdown and resource destruction before the RHI's actual destructor is called (so that all resources of the RHI are still available for shutdown). + * + * @note Cleans up resources and states used by the RHI. */ virtual void Shutdown() = 0; @@ -76,4 +78,4 @@ namespace Karma * @since Karma 1.0.0 */ extern FDynamicRHI* PlatformCreateDynamicRHI(); -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index dd8c56bc..5fc82a36 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1518,6 +1518,7 @@ namespace Karma windowData->RHIResources.VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); windowData->Swapchain = windowData->RHIResources.VulkanSwapChain->GetSwapChainHandle(); + windowData->TotalImageCount = FVulkanDynamicRHI::Get().SwapChainImageCount(); windowData->RenderArea.extent = windowData->RHIResources.VulkanSwapChain->GetSwapChainExtent(); windowData->RenderArea.offset = { 0, 0 }; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index c288355e..f01a297f 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -294,10 +294,22 @@ namespace Karma */ struct KarmaGui_ImplVulkanH_RHIResources { + /** + * @brief Primary swapchain created in KarmaGuiVulkanHandler::FillWindowData + */ FVulkanSwapChain* VulkanSwapChain; - FVulkanRenderPass* VulkanRenderPass; // Primary renderpass for on-screen presentation + + /** + * @brief Primary renderpass for on-screen presentation + */ + FVulkanRenderPass* VulkanRenderPass; KarmaVector VulkanFrameBuffers; - KarmaVector RenderTargets; // RTs corresponding to framebuffers of a type + + /** + * @brief Collection of render targets organized in KarmaVector. Meaning each KarmaVector element contains + * a collection of rendertarget for a framebuffer + */ + KarmaVector RenderTargets; }; /** diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 354b0c1a..e430442a 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -1,15 +1,17 @@ #include "VulkanBuffer.h" -#include "Platform/Vulkan/VulkanHolder.h" #include "Karma/Renderer/RenderCommand.h" #include "Karma/KarmaUtilities.h" +#include "VulkanRHI/VulkanDynamicRHI.h" #include "VulkanRHI/VulkanDevice.h" +#include "VulkanRHI/VulkanSwapChain.h" +#include "KarmaGui/KarmaGuiRenderer.h" namespace Karma { // Vertex Buffer VulkanVertexBuffer::VulkanVertexBuffer(float* vertices, uint32_t size) { - m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); + m_Device = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); VkDeviceSize bufferSize = size; m_BufferSize = size; @@ -45,7 +47,7 @@ namespace Karma VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = VulkanHolder::GetVulkanContext()->GetCommandPool(); + allocInfo.commandPool = FVulkanDynamicRHI::Get().GetDevice()->GetCommandPool(); allocInfo.commandBufferCount = 1; VkCommandBuffer commandBuffer; @@ -68,10 +70,10 @@ namespace Karma submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; - vkQueueSubmit(VulkanHolder::GetVulkanContext()->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(VulkanHolder::GetVulkanContext()->GetGraphicsQueue()); + vkQueueSubmit(FVulkanDynamicRHI::Get().GetDevice()->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(FVulkanDynamicRHI::Get().GetDevice()->GetGraphicsQueue()); - vkFreeCommandBuffers(m_Device, VulkanHolder::GetVulkanContext()->GetCommandPool(), 1, &commandBuffer); + vkFreeCommandBuffers(m_Device, allocInfo.commandPool, 1, &commandBuffer); } void VulkanVertexBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, @@ -93,7 +95,7 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, properties); VkResult resultm = vkAllocateMemory(m_Device, &allocInfo, nullptr, &bufferMemory); @@ -101,23 +103,6 @@ namespace Karma vkBindBufferMemory(m_Device, buffer, bufferMemory, 0); } - uint32_t VulkanVertexBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(VulkanHolder::GetVulkanContext()->GetPhysicalDevice(), &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) - { - if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - - KR_CORE_ASSERT(false, "Failed to find suitable memory type for vertexbuffer"); - return 0; - } - void VulkanVertexBuffer::Bind() const { @@ -127,11 +112,10 @@ namespace Karma { } - // Index buffer VulkanIndexBuffer::VulkanIndexBuffer(uint32_t* indices, uint32_t count) : m_Count(count) { - m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); + m_Device = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); VkDeviceSize bufferSize = sizeof(uint32_t) * count; m_BufferSize = bufferSize; @@ -167,7 +151,7 @@ namespace Karma VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = VulkanHolder::GetVulkanContext()->GetCommandPool(); + allocInfo.commandPool = FVulkanDynamicRHI::Get().GetDevice()->GetCommandPool(); allocInfo.commandBufferCount = 1; VkCommandBuffer commandBuffer; @@ -190,10 +174,10 @@ namespace Karma submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; - vkQueueSubmit(VulkanHolder::GetVulkanContext()->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(VulkanHolder::GetVulkanContext()->GetGraphicsQueue()); + vkQueueSubmit(FVulkanDynamicRHI::Get().GetDevice()->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(FVulkanDynamicRHI::Get().GetDevice()->GetGraphicsQueue()); - vkFreeCommandBuffers(m_Device, VulkanHolder::GetVulkanContext()->GetCommandPool(), 1, &commandBuffer); + vkFreeCommandBuffers(m_Device, allocInfo.commandPool, 1, &commandBuffer); } void VulkanIndexBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, @@ -215,7 +199,7 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, properties); VkResult resultm = vkAllocateMemory(m_Device, &allocInfo, nullptr, &bufferMemory); @@ -223,23 +207,6 @@ namespace Karma vkBindBufferMemory(m_Device, buffer, bufferMemory, 0); } - uint32_t VulkanIndexBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(VulkanHolder::GetVulkanContext()->GetPhysicalDevice(), &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) - { - if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - - KR_CORE_ASSERT(false, "Failed to find suitable memory type for indexbuffer"); - return 0; - } - void VulkanIndexBuffer::Bind() const { } @@ -252,11 +219,13 @@ namespace Karma VulkanUniformBuffer::VulkanUniformBuffer(std::vector dataTypes, uint32_t bindingPointIndex) : UniformBufferObject(dataTypes, bindingPointIndex) { - m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); + m_Device = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); BufferCreation(); //std::shared_ptr uboPtr(this); - VulkanHolder::GetVulkanContext()->RegisterUBO(this); + //VulkanHolder::GetVulkanContext()->RegisterUBO(this); + + FVulkanDynamicRHI::Get().RegisterUniformBufferObject(this); } VulkanUniformBuffer::~VulkanUniformBuffer() @@ -268,19 +237,7 @@ namespace Karma { VkDeviceSize bufferSize = GetBufferSize(); - RendererAPI* rAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanAPI = nullptr; - - if(rAPI->GetAPI() == RendererAPI::API::Vulkan) - { - vulkanAPI = static_cast(rAPI); - } - else - { - KR_CORE_ASSERT(false, "How is this even possible?"); - } - - int maxFramesInFlight = vulkanAPI->GetMaxFramesInFlight(); + int maxFramesInFlight = KarmaGuiRenderer::GetWindowData().RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); m_UniformBuffers.resize(maxFramesInFlight); m_UniformBuffersMemory.resize(maxFramesInFlight); @@ -334,7 +291,7 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, properties); VkResult resultm = vkAllocateMemory(m_Device, &allocInfo, nullptr, &bufferMemory); @@ -342,23 +299,6 @@ namespace Karma vkBindBufferMemory(m_Device, buffer, bufferMemory, 0); } - uint32_t VulkanUniformBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(VulkanHolder::GetVulkanContext()->GetPhysicalDevice(), &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) - { - if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - - KR_CORE_ASSERT(false, "Failed to find suitable memory type for uniformbuffer"); - return 0; - } - // ImageBuffer VulkanImageBuffer::VulkanImageBuffer(const char* filename) { @@ -369,7 +309,7 @@ namespace Karma KR_CORE_ASSERT(pixels, "Failed to load textures image!"); - m_Device = VulkanHolder::GetVulkanContext()->GetLogicalDevice(); + m_Device = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); CreateBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_StagingBuffer, m_StagingBufferMemory); void* data; vkMapMemory(m_Device, m_StagingBufferMemory, 0, imageSize, 0, &data); @@ -425,28 +365,11 @@ namespace Karma VkMemoryAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(memRequirements.memoryTypeBits, properties); VkResult resultm = vkAllocateMemory(m_Device, &allocInfo, nullptr, &bufferMemory); KR_CORE_ASSERT(resultm == VK_SUCCESS, "Failed to allocate imagebuffer memory"); vkBindBufferMemory(m_Device, buffer, bufferMemory, 0); } - - uint32_t VulkanImageBuffer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memProperties; - vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProperties); - - for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) - { - if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - - KR_CORE_ASSERT(false, "Failed to find suitable memory type for imagebuffer"); - return 0; - } } diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index e57e604d..52c25388 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -120,22 +120,6 @@ namespace Karma */ void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); - /** - * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount - * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. - * - * Graphics cards can offer different types of memory to allocate from. Each type of memory varies in terms of allowed operations and performance characteristics. - * We need to combine the requirements of the buffer and our own application requirements to find the right type of memory to use. - * - * @param typeFilter A bitmask, and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource. - * @param properties The demanded properties (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferUsageFlagBits.html). - * - * @return Memory type index i - * @see VulkanUniformBuffer::CreateBuffer - * @since Karma 1.0.0 - */ - uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); - /** * @brief Getter for vertex buffer. * @@ -233,18 +217,6 @@ namespace Karma */ void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); - /** - * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount - * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. - * - * @param typeFilter A bitmask, and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource. - * @param properties The demanded properties (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferUsageFlagBits.html). - * - * @return Memory type index i - * @since Karma 1.0.0 - */ - uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); - /** * @brief Getter for the number of vertices to draw. * @@ -327,18 +299,6 @@ namespace Karma void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); - /** - * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount - * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. - * - * @param typeFilter A bitmask, and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource. - * @param properties The demanded properties (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferUsageFlagBits.html). - * - * @return Memory type index i - * @since Karma 1.0.0 - */ - uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); - /** * @brief Getter for the uniform buffers * @@ -428,17 +388,6 @@ namespace Karma void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); - /** - * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount - * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. - * - * @param typeFilter A bitmask, and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource. - * @param properties The demanded properties (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferUsageFlagBits.html). - * - * @return Memory type index i - * @since Karma 1.0.0 - */ - uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); const inline VkBuffer& GetBuffer() const { return m_StagingBuffer; } // Getters diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 160678aa..ebfaa723 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -17,6 +17,8 @@ namespace Karma class FVulkanDynamicRHI; class VulkanTexture; + class FVulkanSwapChain; + /** * @class FVulkanDevice * @brief Manages Vulkan device resources and operations. @@ -144,7 +146,7 @@ namespace Karma inline VkQueue GetGraphicsQueue() const { return m_GraphicsQueue; } inline FVulkanDynamicRHI* GetVulkanDynamicRHI() const { return m_VulkanDynamicRHI; } - + private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. FVulkanDynamicRHI* m_VulkanDynamicRHI; @@ -157,4 +159,4 @@ namespace Karma // Additional members for managing queues, command pools, etc. can be added here. VkCommandPool m_CommandPool; }; -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index f2a938b4..9d4c29ac 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -19,6 +19,8 @@ struct GLFWwindow; namespace Karma { + class VulkanUniformBuffer; + template FORCEINLINE TRHI* GetDynamicRHI() { @@ -251,18 +253,67 @@ namespace Karma VkFormat FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const; /** - * @brief Finds a suitable memory type on the physical device (graphics card) based on the type filter and desired properties. + * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount + * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. + * + * Graphics cards can offer different types of memory to allocate from. Each type of memory varies in terms of allowed operations and performance characteristics. + * We need to combine the requirements of the buffer and our own application requirements to find the right type of memory to use. * - * @param typeFilter Bitmask specifying the acceptable memory types - * @param properties Desired memory properties (like VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT for CPU access) + * @param typeFilter A bitmask, and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource. + * @param properties The demanded properties (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferUsageFlagBits.html). * - * @see KarmaGuiVulkanHandler::CreateDepthRenderTarget + * @return Memory type index i + * @see VulkanUniformBuffer::CreateBuffer * @since Karma 1.0.0 */ uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + // These may be abstracted to FDynamicRHI + /** + * @brief Register a uniformbuffer object to curate an array or uniformbuffers + * + * @note May be abstracted to FDynamicRHI. + * @note In Unreal Engine, there is no single global “array of uniform buffers” that UpdateUniformBuffer walks; instead, each system owns its own uniform buffer(s) and explicitly passes them into UpdateUniformBuffer when something changes. + * Where the inputs come from + * Uniform buffers are created and owned by higher-level engine systems: + * Per-primitive: via FPrimitiveSceneInfo for object transforms, lighting, etc. + * Per-view / per-pass: view uniforms, fog, shadow parameters, etc. + * Per-material: FMaterialParameterCollectionInstance and other material parameter structs. + */ + void RegisterUniformBufferObject(VulkanUniformBuffer* ubo); + + /** + * @brief Uploads data to the registered VulkanUniformBuffers for the specified frame index. + * + * Typically called during rendering to update uniform buffer data for the current frame like so + * + * @code{.cpp} + * vkCmdBeginRenderPass + * vkCmdBindPipeline + * vkCmdBindVertexBuffers + * vkCmdBindIndexBuffer + * UploadUniformBufferObjects() + * vkCmdBindDescriptorSets // Descriptor sets include UBOs + * vkCmdDrawIndexed + * vkCmdEndRenderPass + * @endcode + * + * @param frameIndex The index of the frame for which to upload UBO data + * + * @note The frameIndex is used to determine which uniform buffer to update, as multiple buffers + * may be used for double or triple buffering to avoid synchronization issues between CPU and GPU. + * + * @note The uniform buffers are resized automatically based on MAX_FRAMES_IN_FLIGHT (number of images (to work upon (CPU side) + * whilst an image is being rendered (GPU side processing)) + 1) + * @see VulkanUniformBuffer::BufferCreation() + * + * @see KarmaGuiRenderer::FrameRender, VulkanBuffer::UploadUniformBuffer + * @since Karma 1.0.0 + */ + void UploadUniformBufferObjects(size_t frameIndex); + + ///////////////////////// Getters ///////////////////////// inline GLFWwindow* GetSurfaceWindow() const { return m_WindowHandle; } - inline VkSurfaceKHR GetSurface() const { return m_Surface; } protected: @@ -457,8 +508,6 @@ namespace Karma static const std::vector validationLayers; protected: - // Vulkan instance handle and other Vulkan-specific members can be declared here - uint32_t m_APIVersion; VkInstance m_VulkanInstance; FVulkanDevice* m_Device; @@ -470,5 +519,7 @@ namespace Karma VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;// GPU SwapChainSupportDetails m_GPUSwapChainSupport; uint32_t m_SwapChainImageCount; + + std::set m_VulkanUBO; }; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index ae25c8c8..3f924d5b 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -1,7 +1,7 @@ #include "VulkanDynamicRHI.h" #include "GLFW/glfw3.h" - #include "Application.h" +#include "VulkanBuffer.h" namespace Karma { @@ -545,4 +545,17 @@ namespace Karma func(instance, debugMessenger, pAllocator); } } + + void FVulkanDynamicRHI::RegisterUniformBufferObject(VulkanUniformBuffer* ubo) + { + m_VulkanUBO.insert(ubo); + } + + void FVulkanDynamicRHI::UploadUniformBufferObjects(size_t frameIndex) + { + for (auto ubo : m_VulkanUBO) + { + ubo->UploadUniformBuffer(frameIndex); + } + } } From 5356e5723fd78a13b155ef1ec15f3f33841214e7 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 11 Jan 2026 07:01:44 +0530 Subject: [PATCH 058/107] Vulkan RHI Mac - Phase VIII --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 ++- Karma/src/Platform/Mac/MacWindow.cpp | 2 +- Karma/src/Platform/Vulkan/VulkanBuffer.h | 9 +++++++-- Karma/src/Platform/Vulkan/VulkanVertexArray.cpp | 4 +++- Pranjal/Source/Private/KarmaGuiMesa.cpp | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 250847f6..10878301 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -5,6 +5,7 @@ #include "StaticMeshActor.h" #include "Platform/Vulkan/VulkanVertexArray.h" #include "VulkanRHI/VulkanDynamicRHI.h" +#include "VulkanRHI/VulkanSwapChain.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -569,7 +570,7 @@ namespace Karma presentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; presentBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - presentBarrier.image = VulkanHolder::GetVulkanContext()->GetSwapChainImages()[windowData->ImageFrameIndex]; // Swapchain only! + presentBarrier.image = windowData->RHIResources.VulkanSwapChain->GetSwapChainImages()[windowData->ImageFrameIndex]; presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; presentBarrier.subresourceRange.levelCount = 1; presentBarrier.subresourceRange.layerCount = 1; diff --git a/Karma/src/Platform/Mac/MacWindow.cpp b/Karma/src/Platform/Mac/MacWindow.cpp index 0b18344a..a88d057e 100644 --- a/Karma/src/Platform/Mac/MacWindow.cpp +++ b/Karma/src/Platform/Mac/MacWindow.cpp @@ -227,7 +227,7 @@ namespace Karma void MacWindow::OnUpdate() { glfwPollEvents(); - m_Context->SwapBuffers(); + //m_Context->SwapBuffers(); } void MacWindow::SetVSync(bool enabled) diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index 52c25388..264a180c 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -322,7 +322,7 @@ namespace Karma void BufferCreation(); /** - * @brief Uploads (copies) the data from buffer memory (m_UniformBuffersMemory) to host-accessible (CPU) pointer, m_UniformList, to the beginning of the mapped range + * @brief Uploads (copies) the data to GPU buffer memory (m_UniformBuffersMemory) from host-accessible (CPU) pointer, m_UniformList, to the beginning of the mapped range * * Basically copies the data from CPU side (uniforms) to GPU side (m_UniformBuffersMemory). * @@ -331,7 +331,12 @@ namespace Karma * - The memory of the uniform buffer (a VkBuffer) is created with CPU-visible properties and mapped to a CPU pointer. * - The updateUniformBuffer function copies the CPU-side data (like transformation matrices) into the mapped GPU buffer memory via a memcpy call. * - This allows the GPU to read the fresh uniform data every frame during rendering without needing to re-record command buffers. - * + * + * Mechanics: + * vkMapMemory returns a CPU-accessible pointer (data) to a specific range within m_UniformBuffersMemory[frameIndex], which is VkDeviceMemory allocated for + * GPU use but with VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT (see VulkanUniformBuffer::BufferCreation()). This pointer points directly into GPU memory, so + * memcpy(data, it.GetDataPointer(), uniformSize) overlays CPU data bytes onto that GPU buffer region + * * @param frameIndex The m_CurrentFrame index representing index of MAX_FRAMES_IN_FLIGHT (number of images (to work upon (CPU side) whilst an image is being rendered (GPU side processing)) + 1) * * @see VulkanRendererAPI::SubmitCommandBuffers() diff --git a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp index 24bc8c80..ba45009c 100644 --- a/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp +++ b/Karma/src/Platform/Vulkan/VulkanVertexArray.cpp @@ -2,11 +2,13 @@ #include "Platform/Vulkan/VulkanHolder.h" #include "Platform/Vulkan/VulkanTexture.h" #include "Karma/Renderer/RenderCommand.h" +#include "VulkanRHI/VulkanDynamicRHI.h" +#include "VulkanRHI/VulkanDevice.h" namespace Karma { VulkanVertexArray::VulkanVertexArray() : m_SupportedDeviceFeatures(VulkanHolder::GetVulkanContext()->GetSupportedDeviceFeatures()), - m_device(VulkanHolder::GetVulkanContext()->GetLogicalDevice()) + m_device(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice()) { } diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 5c226206..d82473bc 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -525,13 +525,13 @@ namespace Karma KarmaGuiBackendRendererUserData* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); backgroundImageTextureID = backendData->GetTextureIDAtIndex(1); - KGTextureID textureID3D = KarmaGuiRenderer::Add3DSceneFor2DRendering(scene, KGVec2(window->Size.x, window->Size.y)); + //KGTextureID textureID3D = KarmaGuiRenderer::Add3DSceneFor2DRendering(scene, KGVec2(window->Size.x, window->Size.y)); KGDrawList* drawList = KarmaGui::GetWindowDrawList(); KGVec2 pos = KarmaGui::GetCursorScreenPos(); drawList->AddImage((void*)backgroundImageTextureID, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); - drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); + //drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); scene->SetRenderWindow(window); From 716772f6573ca06a9ffd6555ac49e50c50701969 Mon Sep 17 00:00:00 2001 From: pcboy Date: Sun, 11 Jan 2026 08:52:41 +0530 Subject: [PATCH 059/107] RHI compile on Linux --- .../Linux/KarmaRHI/LinuxDynamicRHI.cpp | 21 +++++++++++++++++++ .../Platform/Linux/KarmaRHI/LinuxDynamicRHI.h | 18 ++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.cpp create mode 100644 Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.h diff --git a/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.cpp b/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.cpp new file mode 100644 index 00000000..7cfd4d64 --- /dev/null +++ b/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.cpp @@ -0,0 +1,21 @@ +#include "LinuxDynamicRHI.h" +#include "VulkanDynamicRHI.h" + +#include "Log.h" + +namespace Karma +{ +#ifdef KR_LINUX_PLATFORM + FDynamicRHI* PlatformCreateDynamicRHI() + { + FDynamicRHI* DynamicRHI = FDynamicRHI::CreateRHI(); + + if (DynamicRHI) + { + KR_CORE_INFO("Created DynamicRHI for Linux OS"); + } + + return DynamicRHI; + } +#endif +} diff --git a/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.h b/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.h new file mode 100644 index 00000000..04d6aa51 --- /dev/null +++ b/Karma/src/Platform/Linux/KarmaRHI/LinuxDynamicRHI.h @@ -0,0 +1,18 @@ +/** + * @file LinuxDynamicRHI.h + * @brief + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * @date January 11, 2026 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "KarmaRHI.h" + +namespace Karma +{ + +} From fcc167db939babf8db4d19d91dcfe20655f4d651 Mon Sep 17 00:00:00 2001 From: pcboy Date: Sun, 11 Jan 2026 09:09:31 +0530 Subject: [PATCH 060/107] Buggy working on linux maybe because of graphics card --- Karma/src/Platform/Linux/LinuxWindow.cpp | 10 +++++----- Karma/src/Platform/Vulkan/VulkanContext.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Karma/src/Platform/Linux/LinuxWindow.cpp b/Karma/src/Platform/Linux/LinuxWindow.cpp index 51d794e6..38ef25ae 100644 --- a/Karma/src/Platform/Linux/LinuxWindow.cpp +++ b/Karma/src/Platform/Linux/LinuxWindow.cpp @@ -70,7 +70,7 @@ namespace Karma m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr); - switch (currentAPI) + /*switch (currentAPI) { case RendererAPI::API::None: KR_CORE_ASSERT(false, "RendererAPI::None is not supported"); @@ -81,10 +81,10 @@ namespace Karma case RendererAPI::API::Vulkan: m_Context = new VulkanContext(m_Window); break; - } + } m_Context->Init(); - SetVSync(true); + SetVSync(true);*/ // Used for event callbacks glfwSetWindowUserPointer(m_Window, &m_Data); @@ -101,7 +101,7 @@ namespace Karma bool LinuxWindow::OnResize(WindowResizeEvent& event) { - return m_Context->OnWindowResize(event); + return false;//m_Context->OnWindowResize(event); } void LinuxWindow::SetGLFWCallbacks(GLFWwindow* glfwWindow) @@ -225,7 +225,7 @@ namespace Karma void LinuxWindow::OnUpdate() { glfwPollEvents(); - m_Context->SwapBuffers(); + //m_Context->SwapBuffers(); } void LinuxWindow::SetVSync(bool enabled) diff --git a/Karma/src/Platform/Vulkan/VulkanContext.h b/Karma/src/Platform/Vulkan/VulkanContext.h index c558c9f1..f4b818aa 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.h +++ b/Karma/src/Platform/Vulkan/VulkanContext.h @@ -743,7 +743,7 @@ namespace Karma VkDescriptorPool m_GeneralDescriptorPool; std::vector m_GeneralDescriptorSets; - bool bVSync = false; + bool bVSync = true; VkImage m_DepthImage; VkDeviceMemory m_DepthImageMemory; From 217f309f1dfbef1eea299ac837789ac9ece5b73c Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 11 Jan 2026 21:08:46 +0530 Subject: [PATCH 061/107] MSVC receptive code for std::iterator --- Karma/src/Karma/Core/KarmaTypes.h | 13 ++++++++++++- .../src/Karma/Engine/Classes/GameFramework/Actor.h | 2 ++ Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index c43a7055..c75e39d0 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -1,4 +1,4 @@ -/** +/** * @file KarmaTypes.h * @author Ravi Mohan (the_cowboy) * @brief This file contains the types used in game logic @@ -381,6 +381,17 @@ class KarmaVector /** * @brief Getter for first vector element * + * @note The range based loop breaks for MSVC atm + * for(const auto& element : SomeKarmaVector) + * {} + * + * Perplexity gives answer: + * MSVC's _ITERATOR_DEBUG_LEVEL=2 tracks global iterator ownership and gets confused by + * forwarded iterators from KarmaVector::begin(). Direct m_Elements access bypasses the wrapper entirely → no forwarding + * → no corruption → works perfectly. + * + * Also note KarmaVector::begin() works just fine on Mac and Linux (Clang and gcc) + * * @since Karma 1.0.0 */ typename std::vector::iterator begin() diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h index c9995967..b4a82192 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.h @@ -307,6 +307,8 @@ namespace Karma { OutComponents.Add(tempSceneComponent); } + + // shouldn't we have iterator++ } } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 5fc82a36..0c9781e4 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1949,14 +1949,14 @@ namespace Karma // 2. Deletes imageframes and frames on flight objects ClearVulkanWindowData(windowData, true); - for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers) + for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers.GetElements()) { delete vulkanFramebuffer; } // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ - for(const auto& renderTargets : windowData->RHIResources.RenderTargets) + for(const auto& renderTargets : windowData->RHIResources.RenderTargets.GetElements()) { for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) { From 381ed51103766b047f5bbb75ec63dbd50ba44446 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 11 Jan 2026 21:33:13 +0530 Subject: [PATCH 062/107] Minor comment --- Karma/src/Platform/Windows/WindowsWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Karma/src/Platform/Windows/WindowsWindow.cpp b/Karma/src/Platform/Windows/WindowsWindow.cpp index 3cecfc2e..efc41185 100644 --- a/Karma/src/Platform/Windows/WindowsWindow.cpp +++ b/Karma/src/Platform/Windows/WindowsWindow.cpp @@ -205,13 +205,13 @@ namespace Karma { delete m_Context; m_Context = 0; - }*/ //<--to be removed when we have RHI + }*/ } void WindowsWindow::OnUpdate() { glfwPollEvents(); - //m_Context->SwapBuffers();<-- to be removed when RHI is in place? + //m_Context->SwapBuffers(); } void WindowsWindow::SetVSync(bool enabled) From b99778d6db890970cc6e08e38152a79f678078b6 Mon Sep 17 00:00:00 2001 From: pcboy Date: Mon, 12 Jan 2026 09:24:25 +0530 Subject: [PATCH 063/107] Presentation gracefully working on Linux 1. Also issue with gcc compiling resolved --- Karma/src/Karma/Core/UObjectAllocator.h | 2 ++ Karma/src/Karma/Core/UObjectGlobals.h | 2 ++ Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 15 +++++++-------- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h | 1 + Karma/src/krpch.h | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Karma/src/Karma/Core/UObjectAllocator.h b/Karma/src/Karma/Core/UObjectAllocator.h index ce5d9c02..14e95a1c 100644 --- a/Karma/src/Karma/Core/UObjectAllocator.h +++ b/Karma/src/Karma/Core/UObjectAllocator.h @@ -17,6 +17,8 @@ #pragma once +#include "KarmaTypes.h" + namespace Karma { class UObjectBase; diff --git a/Karma/src/Karma/Core/UObjectGlobals.h b/Karma/src/Karma/Core/UObjectGlobals.h index ab501867..9c42b631 100644 --- a/Karma/src/Karma/Core/UObjectGlobals.h +++ b/Karma/src/Karma/Core/UObjectGlobals.h @@ -10,6 +10,8 @@ #pragma once +#include "KarmaTypes.h" + namespace Karma { class UObject; diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 10878301..8f5e0597 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -430,12 +430,12 @@ namespace Karma KarmaGui_Vulkan_Frame_On_Flight* frameOnFlightData = &windowData->FramesOnFlight[windowData->SemaphoreIndex]; VkResult result; - // Pointer to the container of framebuffers (based on number of swapchain images) - KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; - + // IDK why for resizing vkWaitForFences before vkQueueSubmit works. Needs investigation result = vkWaitForFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence, VK_TRUE, UINT64_MAX); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); + result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); // ImageAcquiredSemaphore is m_ImageAvailableSemaphores equivalent VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; @@ -450,6 +450,9 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to acquire next image"); + // Pointer to the container of framebuffers (based on number of swapchain images) + KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; + vkResetCommandBuffer(frameOnFlightData->CommandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); // recording begins: @@ -545,7 +548,7 @@ namespace Karma renderPassInfo.renderArea.extent = windowData->RenderArea.extent; std::array clearValues{}; - clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; + clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; clearValues[1].depthStencil = { 1.0f, 0 }; renderPassInfo.clearValueCount = static_cast(clearValues.size()); @@ -596,10 +599,6 @@ namespace Karma submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphore; - // We reset the fence here or else the swapchain rebuild seemingly fails - result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); - result = vkQueueSubmit(vulkanInfo->Queue, 1, &submitInfo, frameOnFlightData->Fence); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to submit queue"); } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index f01a297f..bacae0f9 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -20,6 +20,7 @@ #include "KarmaGui.h" #include "KarmaGuiInternal.h" #include "Karma/Renderer/Scene.h" +#include "KarmaTypes.h" #include namespace Karma diff --git a/Karma/src/krpch.h b/Karma/src/krpch.h index 383f070b..91cd60be 100644 --- a/Karma/src/krpch.h +++ b/Karma/src/krpch.h @@ -32,4 +32,4 @@ #include "Karma/Core.h" #include "Karma/Log.h" -#include "Karma/Core/KarmaTypes.h" +//#include "Karma/Core/KarmaTypes.h" From 4ef7344295464d9037c5faf0ffe2b05cf0e60def Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 13 Jan 2026 14:08:49 +0530 Subject: [PATCH 064/107] Prevented race condition on windows by MAX_FRAMES_IN_FLIGHT to 4 a flush issue remains VkPhysicalDeviceProperties props; vkGetPhysicalDeviceProperties(physicalDevice, &props); uint64_t atomSize = props.limits.nonCoherentAtomSize; VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; range.memory = memory; range.offset = 0; range.size = VK_WHOLE_SIZE; // Simplest: flushes entire allocation (must align naturally) vkFlushMappedMemoryRanges(device, 1, &range); For partial ranges, compute range.size = (requestedSize + atomSize - 1) / atomSize * atomSize; --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 8 +++----- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 17 ++--------------- .../src/Platform/Vulkan/KarmaGuiVulkanHandler.h | 1 - .../Platform/Vulkan/VulkanRHI/VulkanSwapChain.h | 2 +- 4 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 8f5e0597..24c2a504 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -548,7 +548,7 @@ namespace Karma renderPassInfo.renderArea.extent = windowData->RenderArea.extent; std::array clearValues{}; - clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; + clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; clearValues[1].depthStencil = { 1.0f, 0 }; renderPassInfo.clearValueCount = static_cast(clearValues.size()); @@ -565,9 +565,7 @@ namespace Karma // Transition the swapchain image from VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_PRESENT_SRC_KHR // else there'd be validation error - // NOTE: We are using VulkanHolder::GetVulkanContext()->GetSwapChainImages() in case we decide to create - // different swapchain images for KarmaGui - VkImageMemoryBarrier presentBarrier = {}; + /*VkImageMemoryBarrier presentBarrier = {}; presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; presentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; presentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; @@ -579,7 +577,7 @@ namespace Karma presentBarrier.subresourceRange.layerCount = 1; vkCmdPipelineBarrier(frameOnFlightData->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &presentBarrier); + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &presentBarrier);*/ result = vkEndCommandBuffer(frameOnFlightData->CommandBuffer); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to end command buffer"); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 0c9781e4..199758e3 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -240,21 +240,7 @@ namespace Karma if (windowRenderBuffers->FrameRenderBuffers == nullptr) { - RendererAPI* rAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanAPI = nullptr; - - if (rAPI->GetAPI() == RendererAPI::API::Vulkan) - { - vulkanAPI = static_cast(rAPI); - } - else - { - KR_CORE_ASSERT(false, "How is this even possible?"); - } - - KR_CORE_ASSERT(vulkanAPI != nullptr, "Casting to VulkanAPI failed"); - - windowRenderBuffers->Count = vulkanAPI->GetMaxFramesInFlight(); + windowRenderBuffers->Count = KarmaGuiRenderer::GetWindowData().RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); // Caution: Need to think about the object instantiation and resource management // Cowboy's Note: delete is done in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_ShivaWindowRenderBuffers @@ -534,6 +520,7 @@ namespace Karma range[0].memory = imageData->UploadBufferMemory; range[0].size = uploadSize; + // Flush issue result = vkFlushMappedMemoryRanges(vulkanInfo->Device, 1, range); KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't flush memory range"); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index bacae0f9..0d40ca9e 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -138,7 +138,6 @@ namespace Karma /** * @brief The number of elements in the pSwapchainImages array, i.e number of presentable swapchain images available (vkGetSwapchainImagesKHR). * - * @note Taken from VulkanContext where (VulkanContext::CreateSwapChain()) ImageCount is defined MinImageCount + 1. * @since Karma 1.0.0 */ uint32_t ImageCount; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 0d0a994e..5c32e670 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -124,6 +124,6 @@ namespace Karma // Number of images (to work upon (CPU side) whilst an image is being rendered (GPU side processing)) + 1 // Clearly, MAX_FRAMES_IN_FLIGHT shouldn't exceed m_SwapChainImages.size() - const uint32_t MAX_FRAMES_IN_FLIGHT = 2; + const uint32_t MAX_FRAMES_IN_FLIGHT = 4; }; } From fb03caee27cab3f388a26e86ea509308a91802d9 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 13 Jan 2026 12:46:26 +0530 Subject: [PATCH 065/107] More documentation --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 51 +++++++++---------- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 20 ++++++++ 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 24c2a504..69eea84e 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -52,7 +52,7 @@ namespace Karma // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); - // See if all the Vulkan resources have been instantiated + // See if all the appropriate Vulkan resources have been instantiated KarmaGuiVulkanHandler::CheckInitialization(); // Font, descriptor, and pipeline diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 199758e3..be0ac9c5 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1558,15 +1558,35 @@ namespace Karma // Framebuffer frameData->Framebuffer = frameBuffer->GetHandle(); } + + // For syncronicity, we instantiate FramesOnFlight with MAX_FRAMES_IN_FLIGHT number and label them the Real-Frames-On-Flight in our mind + KR_CORE_ASSERT(windowData->FramesOnFlight == nullptr, "Somehow frames-on-flight are still occupied. Please clear them."); + windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; + + KR_CORE_ASSERT(windowData->FramesOnFlight != nullptr, "Commandbuffers are being assigned without enough resources"); + + std::vector commandBuffers; + commandBuffers.resize(windowData->MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = windowData->CommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); + + VkResult result = vkAllocateCommandBuffers(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &allocInfo, commandBuffers.data()); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create command buffers!"); + + for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) + { + KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; + frameOnFlight->CommandBuffer = commandBuffers[counter]; + } // We create seperate syncronicity resources for Dear KarmaGui if (bCreateSyncronicity) { - // For syncronicity, we instantiate FramesOnFlight with MAX_FRAMES_IN_FLIGHT number and label them the Real-Frames-On-Flight in our mind - KR_CORE_ASSERT(windowData->FramesOnFlight == nullptr, "Somehow frames-on-flight are still occupied. Please clear them."); - - windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; - for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) { KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; @@ -1592,27 +1612,6 @@ namespace Karma windowData->SemaphoreIndex = 0; } - - KR_CORE_ASSERT(windowData->FramesOnFlight != nullptr, "Commandbuffers are being assigned without enough resources"); - - std::vector commandBuffers; - commandBuffers.resize(windowData->MAX_FRAMES_IN_FLIGHT); - - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = windowData->CommandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); - - VkResult result = vkAllocateCommandBuffers(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &allocInfo, commandBuffers.data()); - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create command buffers!"); - - for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) - { - KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; - frameOnFlight->CommandBuffer = commandBuffers[counter]; - } } void KarmaGuiVulkanHandler::CreateDepthRenderTarget(FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 0d40ca9e..c01d29e5 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -1254,6 +1254,12 @@ namespace Karma */ static void KarmaGui_ImplVulkan_SwapBuffers(KarmaGuiViewport* viewport, void*); + /** + * @brief Checks whether KarmaGui_ImplVulkan_InitInfo has been initialized properly. Crashes if not. + * + * @see KarmaGuiRenderer::SetUpKarmaGuiRenderer + * @since Karma 1.0.0 + */ static void CheckInitialization(); /** @@ -1264,12 +1270,26 @@ namespace Karma * - Creates Vulkan renderpass (see KarmaGuiVulkanHandler::MakeRenderPassInfo) for KarmaGui's Vulkan backend and sets * backend->VulkanInitInfo.RenderPass with this renderpass (of which KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreatePipeline * is created) + * - Creates depth render target and uses swapchain images for color render targets and packages them into FVulkanRenderTargetsInfo + * - Creates framebuffers for each swapchain image and sets windowData->Framebuffers + * - Sets windowData->CommandPool with the pool created in FVulkanDynamiRHI::m_Device (FVulkanDevice) + * - Creates semaphores and fences if bCreateSyncronicity is set to true + * * * @note SwapChain and renderpass resources are cleared in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow * @since Karma 1.0.0 */ static void FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity); + /** + * @brief Prepares the renderpass information for KarmaGui's Vulkan backend + * + * Basically fills FVulkanRenderPassInfo struct with two attachments, one for color and one for depth. + * + * @todo Ponder upon the configurable subpass configuration. Currently done in FVulkanRenderPassBuilder::BuildCreateInfo with basic + * subpass description. + * @since Karma 1.0.0 + */ static void MakeRenderPassInfo(FVulkanSwapChain* SwapChain, struct FVulkanRenderPassInfo& RPInfo); /** From a8ae9362e4f4726ef8d2c636071dedd4fc2930d5 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 14 Jan 2026 06:37:53 +0530 Subject: [PATCH 066/107] Dedicated routine for vulkan depthformat and minor documentation --- Karma/src/Karma/EntryPoint.h | 6 ---- Karma/src/Karma/KarmaRHI/KarmaRHI.h | 10 +++++++ .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 6 ++-- .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 29 ++++++++++++++++++- .../Platform/Vulkan/VulkanRHI/VulkanRHI.cpp | 9 ++++++ 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/Karma/src/Karma/EntryPoint.h b/Karma/src/Karma/EntryPoint.h index 9b49b590..63dcd529 100644 --- a/Karma/src/Karma/EntryPoint.h +++ b/Karma/src/Karma/EntryPoint.h @@ -36,7 +36,6 @@ int main(int argc, char** argv) { // TODO: add engine initialization code for various systems Karma::Log::Init(); - Karma::RenderCommand::Init(); KR_INFO("Hello Cowboy. Your lucky number is {0}", 7); auto app = Karma::CreateApplication(); @@ -48,7 +47,6 @@ int main(int argc, char** argv) delete app; - Karma::RenderCommand::DeInit(); Karma::Input::DeInit(); return 0; @@ -72,7 +70,6 @@ int main(int argc, char** argv) { // TODO: add engine initialization code for various systems Karma::Log::Init(); - Karma::RenderCommand::Init(); KR_INFO("Hello Cowboy. Your lucky number is {0}", 7); auto app = Karma::CreateApplication(); @@ -84,7 +81,6 @@ int main(int argc, char** argv) delete app; - Karma::RenderCommand::DeInit(); Karma::Input::DeInit(); return 0; @@ -109,7 +105,6 @@ int main(int argc, char** argv) { // TODO: add engine initialization code for various systems Karma::Log::Init(); - Karma::RenderCommand::Init(); KR_INFO("Hello Cowboy. Your lucky number is {0}", 7); auto app = Karma::CreateApplication(); @@ -120,7 +115,6 @@ int main(int argc, char** argv) app->Run(); delete app; - Karma::RenderCommand::DeInit(); Karma::Input::DeInit(); return 0; diff --git a/Karma/src/Karma/KarmaRHI/KarmaRHI.h b/Karma/src/Karma/KarmaRHI/KarmaRHI.h index 04478e8c..cd0c3a6a 100644 --- a/Karma/src/Karma/KarmaRHI/KarmaRHI.h +++ b/Karma/src/Karma/KarmaRHI/KarmaRHI.h @@ -11,12 +11,22 @@ namespace Karma { + /** + * @brief Enumeration of supported RHI interface types. + */ enum class ERHIInterfaceType { + /** + * @brief Hidden or null RHI interface type. + */ Hidden, Null, D3D11, D3D12, + + /** + * @brief Vulkan RHI interface type. + */ Vulkan, Metal, Agx, diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index be0ac9c5..626bd398 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1616,7 +1616,7 @@ namespace Karma void KarmaGuiVulkanHandler::CreateDepthRenderTarget(FVulkanRenderTargetsInfo &RTInfo, FVulkanSwapChain *SwapChain) { - VkFormat depthFormat = FVulkanDynamicRHI::Get().FindSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + VkFormat depthFormat = FVulkanDynamicRHI::Get().FindDepthFormat(); VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -1692,9 +1692,7 @@ namespace Karma FVulkanRenderPassInfo::FAttachmentInfo depthAttachmentInfo; depthAttachmentInfo.AttachmentFlags = 0; - depthAttachmentInfo.AttachmentFormat = FVulkanDynamicRHI::Get().FindSupportedFormat({ VK_FORMAT_D32_SFLOAT, - VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + depthAttachmentInfo.AttachmentFormat = FVulkanDynamicRHI::Get().FindDepthFormat(); depthAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; depthAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachmentInfo.AttachmentStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 9d4c29ac..3eded396 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -19,8 +19,19 @@ struct GLFWwindow; namespace Karma { + /** + * @brief Forward declaration of VulkanUniformBuffer class. + */ class VulkanUniformBuffer; + /** + * @brief Template function to get the dynamic RHI instance casted to the specified type. + * + * @tparam TRHI The type to which the dynamic RHI instance should be casted. + * @return Pointer to the dynamic RHI instance of type TRHI. + * + * @since Karma 1.0.0 + */ template FORCEINLINE TRHI* GetDynamicRHI() { @@ -33,7 +44,7 @@ namespace Karma * Most operations performed with Vulkan, like draw commands and memory operations, are * asynchronously executed by submitting them to a VkQueue. Queues are allocated from queue * families, where each queue family supports a specific set of operations in its queues. For example, - * there could be separate queue families for graphics, compute and memory transfer operations. + * there could be separate queue families for graphics, compute, and memory transfer operations. * * Used for creating logical device, swapchain, and commandpool * @@ -109,6 +120,14 @@ namespace Karma std::vector presentModes; }; + /** + * @brief Vulkan implementation of the Dynamic RHI. + * + * Provides Vulkan-specific implementations for initializing/deinitializing RHI along with Vulkan resources + * along with rendering operations. + * + * @since Karma 1.0.0 + */ class FVulkanDynamicRHI : public IVulkanDynamicRHI { public: @@ -252,6 +271,14 @@ namespace Karma */ VkFormat FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) const; + /** + * @brief Finds an appropriate depth format for depth resources. + * + * @see KarmaGuiVulkanHandler::MakeRenderPassInfo + * @since Karma 1.0.0 + */ + VkFormat FindDepthFormat() const; + /** * @brief Finds appropriate memory type with demanded properties. Basically a loop is run from counter i = 0 to VkPhysicalDeviceMemoryProperties.memoryTypeCount * (number of valid elements in the memoryTypes array) and memoryType[i] is queried for appropriate properties. On condition satisfaction, counter i is returned. diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp index 3f924d5b..70350abc 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRHI.cpp @@ -199,6 +199,15 @@ namespace Karma return VkFormat{}; } + VkFormat FVulkanDynamicRHI::FindDepthFormat() const + { + return FindSupportedFormat( + {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT + ); + } + void FVulkanDynamicRHI::SelectDevice()// Pick physical device (GPU) and create Vulkan logical device { uint32_t deviceCount = 0; From a9ca078f52a9a4cd70d1001693e8755195da6b91 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 15 Jan 2026 05:28:32 +0530 Subject: [PATCH 067/107] More documentation --- Karma/src/Karma/KarmaRHI/KarmaRHI.h | 14 ++++- .../Vulkan/VulkanRHI/VulkanRenderPass.h | 57 ++++++++++++++++++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Karma/src/Karma/KarmaRHI/KarmaRHI.h b/Karma/src/Karma/KarmaRHI/KarmaRHI.h index cd0c3a6a..f7a74809 100644 --- a/Karma/src/Karma/KarmaRHI/KarmaRHI.h +++ b/Karma/src/Karma/KarmaRHI/KarmaRHI.h @@ -33,9 +33,19 @@ namespace Karma OpenGL, }; - /** Initializes the RHI. */ + /** + * @brief Initializes the RHI. + * + * @see Application::Application() + * @since Karma 1.0.0 + */ extern KARMA_API void RHIInit(); - /** Shuts down the RHI. */ + /** + * @brief Shuts down the RHI. + * + * @see Application::~Application() + * @since Karma 1.0.0 + */ extern KARMA_API void RHIExit(); } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index d387216d..3fe64c55 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -149,7 +149,7 @@ namespace Karma }; /** - * @brief Specialized definition of FVulkanSubpassDescription + * @brief Specialized definition of FVulkanSubpassDescription */ template<> struct FVulkanSubpassDescription @@ -214,15 +214,24 @@ namespace Karma } }; + /** + * @brief Template definition of FVulkanRenderPassCreateInfo + */ template struct FVulkanRenderPassCreateInfo { }; + /** + * @brief Specialized definition of FVulkanRenderPassCreateInfo + */ template<> struct FVulkanRenderPassCreateInfo : public VkRenderPassCreateInfo { + /** + * @brief Constructor to initialize the renderpass create info structure + */ FVulkanRenderPassCreateInfo() { //ZeroVulkanStruct(*this, VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO); @@ -230,6 +239,11 @@ namespace Karma FMemory::Memzero(((uint8_t*)&(*this)) + sizeof(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO), sizeof(VkRenderPassCreateInfo) - sizeof(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO)); } + /** + * @brief Actual renderpass creation function + * + * @since Karma 1.0.0 + */ VkRenderPass Create(FVulkanDevice& Device) { VkRenderPass Handle = VK_NULL_HANDLE; @@ -256,12 +270,22 @@ namespace Karma }; static_assert(MaxSimultaneousRenderTargets <= (1 << MaxSimultaneousRenderTargets_NumBits), "MaxSimultaneousRenderTargets will not fit on MaxSimultaneousRenderTargets_NumBits"); + /** + * @brief Data structure to hold information about Vulkan renderpass attachments (like color and depth attachments). + * + * This is used to create FVulkanRenderTargetLayout which in turn is used to create Vulkan renderpasses. + * + * @since Karma 1.0.0 + */ struct FVulkanRenderPassInfo { + /** + * @brief Information about a single attachment in the renderpass + */ struct FAttachmentInfo { /** - * @brief + * @brief Attachment description flags */ VkAttachmentDescriptionFlags AttachmentFlags; @@ -286,9 +310,13 @@ namespace Karma VkAttachmentStoreOp AttachmentStoreOperation; /** - * @brief + * @brief what to do with stencil data before rendering */ VkAttachmentLoadOp AttachmentStencilLoadOperation; + + /** + * @brief what to do with stencil data after rendering + */ VkAttachmentStoreOp AttachmentStencilStoreOperation; /** @@ -318,6 +346,9 @@ namespace Karma VkImageLayout AttachmentFinalLayout; }; + /** + * @brief Reference to an attachment in the renderpass + */ struct FAttachmentRefInfo { /** @@ -434,15 +465,30 @@ namespace Karma FVulkanDevice& m_Device; }; + /** + * @brief Builder class to create Vulkan renderpass create info structure and generate renderpass + * + * @since Karma 1.0.0 + */ template class FVulkanRenderPassBuilder { public: + /** + * @brief Constructor + * + * @since Karma 1.0.0 + */ FVulkanRenderPassBuilder(FVulkanDevice& InDevice) : m_Device(InDevice) { } + /** + * @brief Build the VkRenderPassCreateInfo structure from the supplied FVulkanRenderTargetLayout + * + * @since Karma 1.0.0 + */ void BuildCreateInfo(const FVulkanRenderTargetLayout& RTLayout) { uint32_t NumSubpasses = 0; @@ -514,6 +560,11 @@ namespace Karma m_CreateInfo.pDependencies = m_SubpassDependencies; } + /** + * @brief Create the Vulkan renderpass + * + * @see Template definition of FVulkanRenderPassCreateInfo + */ VkRenderPass Create(const FVulkanRenderTargetLayout& RTLayout) { BuildCreateInfo(RTLayout); From f2a92723259a9c6214eec9b3962f71663fca9c2c Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 17 Jan 2026 11:29:05 +0530 Subject: [PATCH 068/107] More documentation --- .../Vulkan/VulkanRHI/VulkanRenderPass.h | 72 ++++++++++++++++--- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 3fe64c55..13c38cec 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -1,6 +1,6 @@ /** * @file VulkanRenderPass.h - * @brief + * @brief Karma Engine Vulkan RHI Render Pass definitions * @author Ravi Mohan (the_cowboy) * @version 1.0 * @date December 21, 2025 @@ -18,9 +18,27 @@ namespace Karma { /** - * @brief Using the Curiously Recurring Template Pattern (CRTP) reversal?. - * - * In CRTP we are do static polymorphism i.e base class gains information about the derived class without virtual functions + * @brief Template definition of FVulkanAttachmentReference with specializations done later + * + * This is primary class template for attachment references like color, depth, and input attachments + * + * See + * VkRenderPass CreateVulkanRenderPass() + * { + * .. + * FVulkanRenderPassBuilder<.., FVulkanAttachmentReference, ..> Creator(InDevice); + * Creator.Create(RTLayout); + * .. + * } + * + * where FVulkanRenderPassBuilder::m_ColorAttachmentReferences and FVulkanRenderPassBuilder::m_DepthStencilAttachmentReference are called + * + * m_ColorAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0)); + * m_DepthStencilAttachmentReference.SetDepthStencilAttachment + * + * triggering class template specializations out-of-class definitions of SetAttachment and SetDepthStencilAttachment + * + * @since Karma 1.0.0 */ template struct FVulkanAttachmentReference : public TAttachmentReferenceType @@ -30,6 +48,11 @@ namespace Karma ZeroStruct(); } + /** + * @brief Constructor to copy from VkAttachmentReference + * + * @since Karma 1.0.0 + */ FVulkanAttachmentReference(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { SetAttachment(AttachmentReferenceIn, AspectMask); @@ -48,19 +71,44 @@ namespace Karma * FVulkanAttachmentReference colorRef; * colorRef.SetAttachment(ref, mask); // Copies attachment/layout safely * - * This selectively enables mutation only for standard attachments + * This selectively enables mutation only for standard attachments. Or this discourages certain uses * * @since Karma 1.0.0 */ inline void SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { checkNoEntry(); } + + /** + * @brief Template definition of SetAttachment (specialized out-of-class definition exists) + * + * @since Karma 1.0.0 + */ inline void SetAttachment(const FVulkanAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) { *this = AttachmentReferenceIn; } + + /** + * @brief Disable method enforced here (checkNoEntry()). + * + * This is to prevent misuse for non-depth-stencil attachments + * + * @note Specialized out-of-class definition exists for VkAttachmentReference + * + * @since Karma 1.0.0 + */ inline void SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { checkNoEntry(); } + + /** + * @brief Template definition for zero initialization + * + * @since Karma 1.0.0 + */ inline void ZeroStruct() {} inline void SetAspect(uint32_t Aspect) {} }; /** - * @brief This is template specialization where we define or implement the generic SetAttachment for VkAttachmentReference + * @brief This is template specialization out-of-class definition triggered by + * m_ColorAttachmentReferences.Add(TAttachmentReferenceClass(RTLayout.GetColorAttachmentReferences()[ColorAttachment], 0)); + * + * @since Karma 1.0.0 */ template <> inline void FVulkanAttachmentReference::SetAttachment(const VkAttachmentReference& AttachmentReferenceIn, VkImageAspectFlags AspectMask) @@ -69,12 +117,18 @@ namespace Karma layout = AttachmentReferenceIn.layout; } + /** + * @brief This is template specialization out-of-class definition triggered by + * m_DepthStencilAttachmentReference.SetDepthStencilAttachment + * + * @since Karma 1.0.0 + */ template <> inline void FVulkanAttachmentReference::SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { attachment = AttachmentReferenceIn.attachment; - const VkImageLayout StencilLayout = StencilReference ? StencilReference->stencilLayout : VK_IMAGE_LAYOUT_UNDEFINED; + //const VkImageLayout StencilLayout = StencilReference ? StencilReference->stencilLayout : VK_IMAGE_LAYOUT_UNDEFINED; layout = AttachmentReferenceIn.layout; //GetMergedDepthStencilLayout(AttachmentReferenceIn.layout, StencilLayout); } @@ -526,8 +580,8 @@ namespace Karma m_DepthStencilAttachmentReference.SetDepthStencilAttachment(*RTLayout.GetDepthAttachmentReference(), RTLayout.GetStencilAttachmentReference(), 0, false/*m_Device.SupportsParallelRendering()*/); // Why are these needed when attachment and layout are assigned in m_DepthStencilAttachmentReference - m_DepthStencilAttachment.attachment = RTLayout.GetDepthAttachmentReference()->attachment; - m_DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + //m_DepthStencilAttachment.attachment = RTLayout.GetDepthAttachmentReference()->attachment; + //m_DepthStencilAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } // main subpass (using only single pass) From 30c233ac7a4652f749ad4c4d01d15f53fcdb3f5b Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 17 Jan 2026 12:37:10 +0530 Subject: [PATCH 069/107] Vulkan texture fading issue --- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 8 ++++++-- Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 626bd398..3e51b684 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -417,7 +417,7 @@ namespace Karma VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.format = VK_FORMAT_R8G8B8A8_SRGB;// VK_FORMAT_R8G8B8A8_UNORM; info.extent.width = width; info.extent.height = height; info.extent.depth = 1; @@ -452,10 +452,14 @@ namespace Karma info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = imageData->TextureImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.format = VK_FORMAT_R8G8B8A8_SRGB;//VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; + info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; result = vkCreateImageView(vulkanInfo->Device, &info, vulkanInfo->Allocator, &imageData->TextureView); KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't create image view"); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 13c38cec..f0b10068 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -1,4 +1,4 @@ -/** +/** * @file VulkanRenderPass.h * @brief Karma Engine Vulkan RHI Render Pass definitions * @author Ravi Mohan (the_cowboy) @@ -643,4 +643,4 @@ namespace Karma }; VkRenderPass CreateVulkanRenderPass(FVulkanDevice& Device, const FVulkanRenderTargetLayout& RTLayout); -} \ No newline at end of file +} From ee1804b2ebdf4e4da8e51db5d2500fb0bb89aa27 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 18 Jan 2026 10:56:54 +0530 Subject: [PATCH 070/107] More VulkanRenderPass documentation with help from AI --- .../Vulkan/VulkanRHI/VulkanRenderPass.h | 118 ++++++++++++++++-- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 13c38cec..e7eaa1f1 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -96,12 +96,12 @@ namespace Karma inline void SetDepthStencilAttachment(const VkAttachmentReference& AttachmentReferenceIn, const VkAttachmentReferenceStencilLayout* StencilReference, VkImageAspectFlags AspectMask, bool bSupportsParallelRendering) { checkNoEntry(); } /** - * @brief Template definition for zero initialization + * @brief Template definition for zero initialization. Specialized out-of-class definition exists * * @since Karma 1.0.0 */ inline void ZeroStruct() {} - inline void SetAspect(uint32_t Aspect) {} + //inline void SetAspect(uint32_t Aspect) {} }; /** @@ -134,9 +134,28 @@ namespace Karma } /** - * @brief Template (for VkAttachmentDescription) definition of FVulkanAttachmentDescription + * @brief Template specialization out-of-class definition for zero initialization + * + * This function is triggered by FVulkanAttachmentReference constructor + * + * @since Karma 1.0.0 + */ + template<> + inline void FVulkanAttachmentReference::ZeroStruct() + { + attachment = 0; + layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + /** + * @brief Template (for VkAttachmentDescription(2)) definition of FVulkanAttachmentDescription + * + * This is primary class template for attachment descriptions like color, depth, and input attachments. These + * attachments (FVulkanRenderPassBuilder::m_AttachmentDescriptions) are used in FVulkanRenderPassBuilder, to create + * VkRenderPassCreateInfo, structure like so + * + * FVulkanRenderPassBuilder<.., FVulkanAttachmentDescription, ..> Creator(InDevice); * - * Is this the CRTP reversal? * * @since Karma 1.0.0 */ @@ -146,17 +165,31 @@ namespace Karma }; /** - * @brief Specializing (?) FVulkanAttachmentDescription for VkAttachmentDescription + * @brief Specializing FVulkanAttachmentDescription for VkAttachmentDescription + * + * @since Karma 1.0.0 */ template<> struct FVulkanAttachmentDescription : public VkAttachmentDescription { + /** + * @brief Default constructor to zero initialize the structure + * + * @since Karma 1.0.0 + */ FVulkanAttachmentDescription() { FMemory::Memzero(this, sizeof(VkAttachmentDescription)); } + /** + * @brief Constructor to copy from VkAttachmentDescription + * + * Triggered by m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); + * + * @since Karma 1.0.0 + */ FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc) { flags = InDesc.flags; @@ -172,6 +205,10 @@ namespace Karma /** * @brief Seems like we are not supporting VkAttachmentDescriptionStencilLayout + * + * For understanding purposes only atm + * + * @since Karma 1.0.0 */ FVulkanAttachmentDescription(const VkAttachmentDescription& InDesc, const VkAttachmentDescriptionStencilLayout* InStencilDesc, bool bSupportsParallelRendering) { @@ -193,7 +230,13 @@ namespace Karma }; /** - * @brief Template (for VkSubpassDescription) definition of FVulkanSubpassDescription + * @brief Template (for VkSubpassDescription(2)) definition of FVulkanSubpassDescription + * + * Subpasses in Vulkan are passes in RenderPass that define how attachments are used. There is one main subpass + * which is used for standard rendering, which we are going to implement in FVulkanRenderPassBuilder::BuildCreateInfo() + * + * Unreal Engine has multiple subpasses for various effects. For instance color write / depth read subpass for deferred shading + * optimization etc. We will keep this simple. * * @since Karma 1.0.0 */ @@ -203,18 +246,33 @@ namespace Karma }; /** - * @brief Specialized definition of FVulkanSubpassDescription + * @brief Specialized definition of FVulkanSubpassDescription for VkSubpassDescription */ template<> struct FVulkanSubpassDescription : public VkSubpassDescription { + /** + * @brief Default constructor to zero initialize the structure + * + * Triggered by the FVulkanRenderPassBuilder constructor for variable m_SubpassDescriptions + * + * @since Karma 1.0.0 + */ FVulkanSubpassDescription() { FMemory::Memzero(this, sizeof(VkSubpassDescription)); pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; } + /** + * @brief Set color attachments for the subpass + * + * @param ColorAttachmentReferences Array of color attachment references for the subpass + * @param OverrideCount Optional override for number of color attachments + * + * @since Karma 1.0.0 + */ void SetColorAttachments(const KarmaVector>& ColorAttachmentReferences, int OverrideCount = -1) { colorAttachmentCount = (OverrideCount == -1) ? ColorAttachmentReferences.Num() : OverrideCount; @@ -230,6 +288,13 @@ namespace Karma } }*/ + /** + * @brief Set depth-stencil attachment for the subpass + * + * @param DepthStencilAttachmentReference Depth-stencil attachment reference for the subpass + * + * @since Karma 1.0.0 + */ void SetDepthStencilAttachment(FVulkanAttachmentReference* DepthStencilAttachmentReference) { pDepthStencilAttachment = static_cast(DepthStencilAttachmentReference); @@ -252,16 +317,37 @@ namespace Karma }*/ }; + /** + * @brief Template definition of FVulkanSubpassDependency + * + * SubpassDependencies define memory and execution dependencies between subpasses in a renderpass. These are + * conifgured according to the subpasses defined in the renderpass. Since we are using only one subpass, we will + * use single dependency in FVulkanRenderPassBuilder::BuildCreateInfo() + * + * @since Karma 1.0.0 + */ template struct FVulkanSubpassDependency : public TSubpassDependencyType { }; + /** + * @brief Specialized definition of FVulkanSubpassDependency for VkSubpassDependency(2) + * + * @since Karma 1.0.0 + */ template<> struct FVulkanSubpassDependency : public VkSubpassDependency { + /** + * @brief Default constructor to zero initialize the structure + * + * Triggered by FVulkanRenderPassBuilder constructor for variable m_SubpassDependencies + * + * @since Karma 1.0.0 + */ FVulkanSubpassDependency() { FMemory::Memzero(this, sizeof(VkSubpassDependency)); @@ -269,7 +355,9 @@ namespace Karma }; /** - * @brief Template definition of FVulkanRenderPassCreateInfo + * @brief Template definition of FVulkanRenderPassCreateInfo for VkRenderPassCreateInfo(2) + * + * @since Karma 1.0.0 */ template struct FVulkanRenderPassCreateInfo @@ -285,6 +373,10 @@ namespace Karma { /** * @brief Constructor to initialize the renderpass create info structure + * + * The constructor basically sets sType and zero initializes the rest of the structure + * + * @since Karma 1.0.0 */ FVulkanRenderPassCreateInfo() { @@ -557,14 +649,14 @@ namespace Karma // VkAttachmentDescription for color/depth attachments for (uint32_t Attachment = 0; Attachment < RTLayout.GetNumAttachmentDescriptions(); ++Attachment) { - if (bHasDepthStencilAttachmentReference && (Attachment == m_DepthStencilAttachmentReference.attachment)) + /*if (bHasDepthStencilAttachmentReference && (Attachment == m_DepthStencilAttachmentReference.attachment)) { - //m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDescription(), false/*Device.SupportsParallelRendering()*/)); + //m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment], RTLayout.GetStencilDescription(), false/*Device.SupportsParallelRendering())); } - else - { + else*/ + //{ m_AttachmentDescriptions.Add(TAttachmentDescriptionClass(RTLayout.GetAttachmentDescriptions()[Attachment])); - } + //} } // VkAttachmentReference for color attachments From 10a2587fab57b3b59c0441899ed3435b1cf73495 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 18 Jan 2026 15:30:48 +0530 Subject: [PATCH 071/107] minor documentation for renderpass --- .../Vulkan/VulkanRHI/VulkanRenderPass.h | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h index 84769564..76da1876 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanRenderPass.h @@ -20,8 +20,8 @@ namespace Karma /** * @brief Template definition of FVulkanAttachmentReference with specializations done later * - * This is primary class template for attachment references like color, depth, and input attachments - * + * This is primary struct template for attachment references like color, depth, and input attachments + * * See * VkRenderPass CreateVulkanRenderPass() * { @@ -150,7 +150,7 @@ namespace Karma /** * @brief Template (for VkAttachmentDescription(2)) definition of FVulkanAttachmentDescription * - * This is primary class template for attachment descriptions like color, depth, and input attachments. These + * This is primary struct template for attachment descriptions like color, depth, and input attachments. These * attachments (FVulkanRenderPassBuilder::m_AttachmentDescriptions) are used in FVulkanRenderPassBuilder, to create * VkRenderPassCreateInfo, structure like so * @@ -231,7 +231,9 @@ namespace Karma /** * @brief Template (for VkSubpassDescription(2)) definition of FVulkanSubpassDescription - * + * + * This is primary class template for subpasses. + * * Subpasses in Vulkan are passes in RenderPass that define how attachments are used. There is one main subpass * which is used for standard rendering, which we are going to implement in FVulkanRenderPassBuilder::BuildCreateInfo() * @@ -319,9 +321,11 @@ namespace Karma /** * @brief Template definition of FVulkanSubpassDependency - * + * + * This is primary struct template for subpass dependency. + * * SubpassDependencies define memory and execution dependencies between subpasses in a renderpass. These are - * conifgured according to the subpasses defined in the renderpass. Since we are using only one subpass, we will + * configured according to the subpasses defined in the renderpass. Since we are using only one subpass, we will * use single dependency in FVulkanRenderPassBuilder::BuildCreateInfo() * * @since Karma 1.0.0 @@ -356,7 +360,9 @@ namespace Karma /** * @brief Template definition of FVulkanRenderPassCreateInfo for VkRenderPassCreateInfo(2) - * + * + * This is primary struct template for renderpass create info. + * * @since Karma 1.0.0 */ template @@ -414,7 +420,7 @@ namespace Karma MaxSimultaneousRenderTargets = 8, MaxSimultaneousRenderTargets_NumBits = 3, }; - static_assert(MaxSimultaneousRenderTargets <= (1 << MaxSimultaneousRenderTargets_NumBits), "MaxSimultaneousRenderTargets will not fit on MaxSimultaneousRenderTargets_NumBits"); + static_assert(MaxSimultaneousRenderTargets <= (1 << MaxSimultaneousRenderTargets_NumBits), "MaxSimultaneousRenderTargets will not fit on MaxSimultaneousRenderTargets_NumBits"); /** * @brief Data structure to hold information about Vulkan renderpass attachments (like color and depth attachments). From 4ce55dbdacae90df47b4c5322cbcee5cbd52c652 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 19 Jan 2026 08:22:18 +0530 Subject: [PATCH 072/107] Swapchain documentation and Perplexity's debugging --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 ++ .../Vulkan/VulkanRHI/VulkanDynamicRHI.h | 3 +- .../Vulkan/VulkanRHI/VulkanSwapChain.h | 38 +++++++++++++++++-- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 69eea84e..1e0b4cb4 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -626,6 +626,9 @@ namespace Karma if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { m_SwapChainRebuild = true; + } + if (result == VK_ERROR_OUT_OF_DATE_KHR) + { return; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h index 3eded396..75976c6a 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDynamicRHI.h @@ -123,8 +123,7 @@ namespace Karma /** * @brief Vulkan implementation of the Dynamic RHI. * - * Provides Vulkan-specific implementations for initializing/deinitializing RHI along with Vulkan resources - * along with rendering operations. + * Provides Vulkan-specific implementations for initializing/deinitializing RHI along with Vulkan resources and rendering operations. * * @since Karma 1.0.0 */ diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h index 5c32e670..de4cc016 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSwapChain.h @@ -17,20 +17,53 @@ struct GLFWwindow; namespace Karma { + /** + * @brief Information required to recreate a Vulkan swapchain. + * + * @note To be battle tested during swapchain recreation on window resize + * @since Karma 1.0.0 + */ struct FVulkanSwapChainRecreateInfo { + /** + * @brief + */ VkSwapchainKHR SwapChain; + + /** + * @brief + */ VkSurfaceKHR Surface; }; + /** + * @brief Represents a Vulkan swapchain, managing the images used for rendering and presentation. + * + * A swapchain is a series of images that are presented to the screen in a specific order. It is a crucial component in Vulkan for rendering graphics to a window. + * Since there is not default framebuffer in Vulkan, the swapchain provides the images that will be used as the framebuffer for rendering. + * + * + * @since Karma 1.0.0 + */ class FVulkanSwapChain { public: + /** + * @brief Creates a Vulkan swapchain based on the provided device. + * + * @param InDevice The FVulkanDevice containing the LogicalDevice and GPU + * + * @see KarmaGuiVulkanHandler::FillWindowData + * @since Karma 1.0.0 + */ static FVulkanSwapChain* Create(FVulkanDevice* InDevice); /** * @brief Destroys the swapchain appropriately * + * @param RecreateInfo Information required to recreate the swapchain + * + * @note m_SwapChainImages are cleared automatically when vkDestroySwapchainKHR is called * @since Karma 1.0.0 */ void Destroy(FVulkanSwapChainRecreateInfo* RecreateInfo); @@ -68,7 +101,6 @@ namespace Karma * * @param availableFormats The available surface formats (from QuerySwapChainSupport()) * - * @see VulkanContext::CreateSwapChain() * @since Karma 1.0.0 */ static VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector& availableFormats); @@ -80,7 +112,6 @@ namespace Karma * * @param availablePresentModes The available presentation modes (from QuerySwapChainSupport()) * - * @see VulkanContext::CreateSwapChain() * @since Karma 1.0.0 */ VkPresentModeKHR ChooseSwapPresentMode(const std::vector& availablePresentModes); @@ -90,7 +121,6 @@ namespace Karma * * @param capabilities The surface capabilities (from QuerySwapChainSupport()) * - * @see VulkanContext::CreateSwapChain() * @since Karma 1.0.0 */ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); @@ -123,7 +153,7 @@ namespace Karma uint32_t m_SemaphoreIndex; // Number of images (to work upon (CPU side) whilst an image is being rendered (GPU side processing)) + 1 - // Clearly, MAX_FRAMES_IN_FLIGHT shouldn't exceed m_SwapChainImages.size() + // Clearly, m_SwapChainImages.size() shouldn't exceed MAX_FRAMES_IN_FLIGHT const uint32_t MAX_FRAMES_IN_FLIGHT = 4; }; } From 4223f6f29489b5257512e87d4d162f921c90357c Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 19 Jan 2026 18:25:29 +0530 Subject: [PATCH 073/107] Editor Window : Resizing functional --- Karma/src/Karma/Core/KarmaTypes.h | 9 +- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 35 ++-- Karma/src/Platform/Mac/MacWindow.cpp | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 161 +++++++----------- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 19 +-- 5 files changed, 81 insertions(+), 145 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index c75e39d0..a31255b9 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -1,4 +1,4 @@ -/** +/** * @file KarmaTypes.h * @author Ravi Mohan (the_cowboy) * @brief This file contains the types used in game logic @@ -363,6 +363,13 @@ class KarmaVector m_Elements[Index] = Value; } + /** + * @brief Clears the vector from all the elements + * + * @since Karma 1.0.0 + */ + inline void Clear() { m_Elements.clear(); } + /** * @brief Getter for the elements of vector * diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 1e0b4cb4..96a0a5ae 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -280,23 +280,9 @@ namespace Karma if (width > 0 && height > 0) { - RendererAPI* rAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanAPI = nullptr; - - if (rAPI->GetAPI() == RendererAPI::API::Vulkan) - { - vulkanAPI = static_cast(rAPI); - } - else - { - KR_CORE_ASSERT(false, "How is this even possible?"); - } - - KR_CORE_ASSERT(vulkanAPI != nullptr, "Casting to VulkanAPI failed"); - - vulkanAPI->RecreateCommandBuffersAndSwapChain(); - - KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, false); + KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(&m_VulkanWindowData); + KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, false); + m_SwapChainRebuild = false; } } @@ -430,13 +416,10 @@ namespace Karma KarmaGui_Vulkan_Frame_On_Flight* frameOnFlightData = &windowData->FramesOnFlight[windowData->SemaphoreIndex]; VkResult result; - // IDK why for resizing vkWaitForFences before vkQueueSubmit works. Needs investigation + // IDK why for resizing vkResetFences before vkQueueSubmit works. Needs investigation result = vkWaitForFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence, VK_TRUE, UINT64_MAX); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); - result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); - // ImageAcquiredSemaphore is m_ImageAvailableSemaphores equivalent VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; VkSemaphore renderCompleteSemaphore = frameOnFlightData->RenderCompleteSemaphore; @@ -450,8 +433,8 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to acquire next image"); - // Pointer to the container of framebuffers (based on number of swapchain images) - KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; + // Pointer to the container of framebuffers (based on number of swapchain images) + KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; vkResetCommandBuffer(frameOnFlightData->CommandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); @@ -548,7 +531,7 @@ namespace Karma renderPassInfo.renderArea.extent = windowData->RenderArea.extent; std::array clearValues{}; - clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; + clearValues[0] = { windowData->ClearValue.color.float32[0], windowData->ClearValue.color.float32[1], windowData->ClearValue.color.float32[2], windowData->ClearValue.color.float32[3] }; clearValues[1].depthStencil = { 1.0f, 0 }; renderPassInfo.clearValueCount = static_cast(clearValues.size()); @@ -597,6 +580,10 @@ namespace Karma submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphore; + // We reset the fence here or else the swapchain rebuild seemingly fails + result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); + result = vkQueueSubmit(vulkanInfo->Queue, 1, &submitInfo, frameOnFlightData->Fence); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to submit queue"); } diff --git a/Karma/src/Platform/Mac/MacWindow.cpp b/Karma/src/Platform/Mac/MacWindow.cpp index a88d057e..517d88f5 100644 --- a/Karma/src/Platform/Mac/MacWindow.cpp +++ b/Karma/src/Platform/Mac/MacWindow.cpp @@ -36,7 +36,7 @@ namespace Karma bool MacWindow::OnResize(WindowResizeEvent& event) { - return m_Context->OnWindowResize(event); + return true; //m_Context->OnWindowResize(event); } void MacWindow::Init(const WindowProps& props) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 3e51b684..d466787d 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1563,9 +1563,10 @@ namespace Karma frameData->Framebuffer = frameBuffer->GetHandle(); } - // For syncronicity, we instantiate FramesOnFlight with MAX_FRAMES_IN_FLIGHT number and label them the Real-Frames-On-Flight in our mind - KR_CORE_ASSERT(windowData->FramesOnFlight == nullptr, "Somehow frames-on-flight are still occupied. Please clear them."); - windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; + if(windowData->FramesOnFlight == nullptr) + { + windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; + } KR_CORE_ASSERT(windowData->FramesOnFlight != nullptr, "Commandbuffers are being assigned without enough resources"); @@ -1721,98 +1722,66 @@ namespace Karma RPInfo.m_RenderArea.extent = SwapChain->GetSwapChainExtent(); } - void KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) + void KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(KarmaGui_ImplVulkanH_Window* windowData) { - KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); - KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; - - // Clear the structure with now redundant information - ClearVulkanWindowData(windowData, bCreateSyncronicity); - - RendererAPI* rAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanAPI = nullptr; - - if (rAPI->GetAPI() == RendererAPI::API::Vulkan) - { - vulkanAPI = static_cast(rAPI); - } - else + VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); + + std::vector commandBuffers; + commandBuffers.resize(windowData->MAX_FRAMES_IN_FLIGHT); + + for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) { - KR_CORE_ASSERT(false, "How is this even possible?"); + KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; + commandBuffers[counter] = frameOnFlight->CommandBuffer; } - - KR_CORE_ASSERT(vulkanAPI != nullptr, "Casting to VulkanAPI failed"); - - // Fill relevant information gathered from VulkanContext - // Assuming new swapchain and all that has been created in the KarmaGuiLayer::GiveLoopBeginControlToVulkan - windowData->Swapchain = VulkanHolder::GetVulkanContext()->GetSwapChain(); - windowData->TotalImageCount = (uint32_t)VulkanHolder::GetVulkanContext()->GetSwapChainImages().size(); - windowData->RenderArea.extent = VulkanHolder::GetVulkanContext()->GetSwapChainExtent(); - windowData->RenderArea.offset = { 0, 0 }; - windowData->RenderPass = VulkanHolder::GetVulkanContext()->GetRenderPass(); - windowData->MAX_FRAMES_IN_FLIGHT = vulkanAPI->GetMaxFramesInFlight(); - windowData->CommandPool = VulkanHolder::GetVulkanContext()->GetCommandPool(); - windowData->ImageFrameIndex = 0; - - KR_CORE_ASSERT(windowData->ImageFrames == nullptr, "Somehow frames are still occupied. Please clear them."); - windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; - - for (uint32_t counter = 0; counter < windowData->TotalImageCount; counter++) + + vkDeviceWaitIdle(logicalDevice); + + vkFreeCommandBuffers(logicalDevice, windowData->CommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + // 1. Deletes imageframes and frames on flight objects + ClearVulkanWindowData(windowData, false); + + for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers.GetElements()) { - KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[counter]; - - // VulkanContext ImageView equivalent - frameData->BackbufferView = VulkanHolder::GetVulkanContext()->GetSwapChainImageViews()[counter]; - - // Framebuffer - frameData->Framebuffer = VulkanHolder::GetVulkanContext()->GetSwapChainFrameBuffer()[counter]; - - // Backbuffers could be VulkanContext m_swapChainImages equivalent - frameData->Backbuffer = VulkanHolder::GetVulkanContext()->GetSwapChainImages()[counter]; + delete vulkanFramebuffer; } - - // We create seperate syncronicity resources for Dear KarmaGui - if (bCreateSyncronicity) + windowData->RHIResources.VulkanFrameBuffers.Clear(); + + // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy + // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ + for(const auto& renderTargets : windowData->RHIResources.RenderTargets.GetElements()) { - // For syncronicity, we instantiate FramesOnFlight with MAX_FRAMES_IN_FLIGHT number and label them the Real-Frames-On-Flight in our mind - KR_CORE_ASSERT(windowData->FramesOnFlight == nullptr, "Somehow frames-on-flight are still occupied. Please clear them."); - - windowData->FramesOnFlight = new KarmaGui_Vulkan_Frame_On_Flight[windowData->MAX_FRAMES_IN_FLIGHT]; - - for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) + for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) { - KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; - - VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - VkResult result = vkCreateFence(vulkanInfo->Device, &fenceInfo, VK_NULL_HANDLE, &frameOnFlight->Fence); - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence"); - - VkSemaphoreCreateInfo semaphoreInfo = {}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - result = vkCreateSemaphore(vulkanInfo->Device, &semaphoreInfo, VK_NULL_HANDLE, &frameOnFlight->ImageAcquiredSemaphore); - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create ImageAcquiredSemaphore"); - - result = vkCreateSemaphore(vulkanInfo->Device, &semaphoreInfo, VK_NULL_HANDLE, &frameOnFlight->RenderCompleteSemaphore); - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create RenderCompleteSemaphore"); + if(!renderTargets->m_ColorRenderTargets[counter].bSwapChainColorRenderTarget) + { + for(uint32_t colorRT = 0; colorRT < renderTargets->m_NumColorRenderTargets; colorRT++) + { + vkDestroyImageView(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTViews[colorRT], nullptr); + vkDestroyImage(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTImages[colorRT], nullptr); + vkFreeMemory(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTDeviceMemory[colorRT], nullptr); + } + } } - - windowData->SemaphoreIndex = 0; + + if(renderTargets->bDepthRenderTarget) + { + vkDestroyImageView(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTView, nullptr); + vkDestroyImage(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTImage, nullptr); + vkFreeMemory(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTDeviceMemory, nullptr); + } + + delete renderTargets; } + windowData->RHIResources.RenderTargets.Clear(); + + delete windowData->RHIResources.VulkanRenderPass; - KR_CORE_ASSERT(windowData->FramesOnFlight != nullptr, "Commandbuffers are being assigned without enough resources"); - vulkanAPI->AllocateCommandBuffers(); - - for (uint32_t counter = 0; counter < windowData->MAX_FRAMES_IN_FLIGHT; counter++) - { - KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; - frameOnFlight->CommandBuffer = vulkanAPI->GetCommandBuffers()[counter]; - } + FVulkanSwapChainRecreateInfo RI{}; + windowData->RHIResources.VulkanSwapChain->Destroy(&RI); + delete windowData->RHIResources.VulkanSwapChain; + windowData->RHIResources.VulkanSwapChain = nullptr; } void KarmaGuiVulkanHandler::ClearVulkanWindowData(KarmaGui_ImplVulkanH_Window* vulkanWindowData, bool bDestroySyncronicity) @@ -1898,24 +1867,10 @@ namespace Karma { if (bRecreateSwapChainAndCommandBuffers) { - RendererAPI* rAPI = RenderCommand::GetRendererAPI(); - VulkanRendererAPI* vulkanAPI = nullptr; - - if (rAPI->GetAPI() == RendererAPI::API::Vulkan) - { - vulkanAPI = static_cast(rAPI); - } - else - { - KR_CORE_ASSERT(false, "How is this even possible?"); - } - - KR_CORE_ASSERT(vulkanAPI != nullptr, "Casting to VulkanAPI failed"); - - vulkanAPI->RecreateCommandBuffersAndSwapChain(); + KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(windowData); } - KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(windowData, bCreateSyncronicity); + KarmaGuiVulkanHandler::FillWindowData(windowData, bCreateSyncronicity); } void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) @@ -1941,6 +1896,7 @@ namespace Karma { delete vulkanFramebuffer; } + windowData->RHIResources.VulkanFrameBuffers.Clear(); // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ @@ -1968,6 +1924,7 @@ namespace Karma delete renderTargets; } + windowData->RHIResources.RenderTargets.Clear(); delete windowData->RHIResources.VulkanRenderPass; @@ -1976,7 +1933,7 @@ namespace Karma delete windowData->RHIResources.VulkanSwapChain; windowData->RHIResources.VulkanSwapChain = nullptr; - return; // <-- to be uncommented when we have RHI (actually removed) + return; // <-- to be uncommented when we have offscreen rendering logic KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index c01d29e5..5773567d 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -1305,23 +1305,8 @@ namespace Karma * @since Karma 1.0.0 */ static void GatherSwapChainColorRenderTargets(FVulkanRenderTargetsInfo& RTInfo, FVulkanSwapChain* SwapChain, uint32_t SwapChainImageIndex); - - /** - * @brief The purpose of the routine is two-fold - * - * - Share the Vulkan resources, including Swapchain, Renderpass, CommandPool, Framebuffer, and all that, of MainWindow (stored in VulkanContext of VulkanHolder, initialized during VulkanContext::Init() which gets called - * on Platform(Windows, Mac, and Linux)Window during their specific creation implementation, for instance LinuxWindow::Init) to KarmaGui's backend, after appropriate clearence. - * - Ground work for establishing syncronicity, in GPU side functions' execution, by creating semaphores for (https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation#page_Synchronization) - * -# to signal that image has been acquired from the swapchain and ready for rendering - * -# to signal that rendering has finished and presentation (the last step of drawing a frame is submitting the result back to the swap chain to have it eventually show up on the screen) can happen - * The set number of semaphores (we are using two types of them) are for number of "frames in flight" which represent the number of frames which can be processed on CPU whilst rendering is done on GPU - * - * @param windowData The datastructure to hold all the data needed by one rendering Vulkan context. - * @param bCreateSyncronicity Should we allocate semaphores and fences. For instance true during KarmaGuiRenderer::SetUpKarmaGuiRenderer and false during KarmaGuiRenderer::GiveLoopBeginControlToVulkan - * - * @since Karma 1.0.0 - */ - static void ShareVulkanContextResourcesOfMainWindow(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity = false); + + static void ShivaSwapChainForRebuild(KarmaGui_ImplVulkanH_Window* windowData); /** * @brief Creates framebuffers, renderpass, and vertexarray (for KarmaGui) pipeline for 2D texture creation from 3D scene From f51a544d07e91c6623af09336b567ed00e50a3e4 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 19 Jan 2026 20:36:17 +0530 Subject: [PATCH 074/107] Minor resizing tweaks to prevent validation error --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 12 +++++------- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 10 ++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 96a0a5ae..ee55655c 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -280,9 +280,7 @@ namespace Karma if (width > 0 && height > 0) { - KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(&m_VulkanWindowData); - KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, false); - + KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateOrResizeWindow(&m_VulkanWindowData, true, true); m_SwapChainRebuild = false; } } @@ -412,15 +410,13 @@ namespace Karma KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; // Pointer to the per frame data for instance fence, semaphores, and commandbuffer - // Remember windowData->SemaphoreIndex is m_CurrentFrame equivalent of VulkanRendererAPI KarmaGui_Vulkan_Frame_On_Flight* frameOnFlightData = &windowData->FramesOnFlight[windowData->SemaphoreIndex]; VkResult result; - // IDK why for resizing vkResetFences before vkQueueSubmit works. Needs investigation + // Fence needs to be signaled to pass the vkWaitForFences. result = vkWaitForFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence, VK_TRUE, UINT64_MAX); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); - // ImageAcquiredSemaphore is m_ImageAvailableSemaphores equivalent VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; VkSemaphore renderCompleteSemaphore = frameOnFlightData->RenderCompleteSemaphore; @@ -580,10 +576,12 @@ namespace Karma submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphore; - // We reset the fence here or else the swapchain rebuild seemingly fails + // vkResetFences unsignals the Fence + // Fixing a deadlock: https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation#page_Fixing-a-deadlock result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); + // vkQueueSubmit signals the Fence once commandbuffers finish execution result = vkQueueSubmit(vulkanInfo->Queue, 1, &submitInfo, frameOnFlightData->Fence); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to submit queue"); } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index d466787d..9feacbaa 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1739,8 +1739,7 @@ namespace Karma vkFreeCommandBuffers(logicalDevice, windowData->CommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - // 1. Deletes imageframes and frames on flight objects - ClearVulkanWindowData(windowData, false); + ClearVulkanWindowData(windowData, true); for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers.GetElements()) { @@ -1805,7 +1804,6 @@ namespace Karma { if (vulkanWindowData->FramesOnFlight != nullptr) { - // Remove syncronicity resources using Vulkan API DestroyFramesOnFlightData(&vulkanWindowData->FramesOnFlight[counter]); } } @@ -1867,10 +1865,10 @@ namespace Karma { if (bRecreateSwapChainAndCommandBuffers) { - KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(windowData); + ShivaSwapChainForRebuild(windowData); } - KarmaGuiVulkanHandler::FillWindowData(windowData, bCreateSyncronicity); + FillWindowData(windowData, bCreateSyncronicity); } void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) @@ -2129,7 +2127,7 @@ namespace Karma KarmaGui_ImplVulkanH_Window* windowData = &viewportData->Window; KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; - //KarmaGuiViewport* mainViewPort = KarmaGui::GetMainViewport(); + //KarmaGuiViewport* mainViewPort = KarmaGui::GetMainViewport(); VkResult result; uint32_t presentIndex = windowData->ImageFrameIndex; From f22b15b5a0e3f496db25b4a7cbc9b069706ded82 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 20 Jan 2026 09:54:16 +0530 Subject: [PATCH 075/107] Using reference instead of entire KarmaGui_ImplVulkanH_RHIResources structure --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 68 ++++++++++--------- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 6 +- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 2 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 9feacbaa..4eaaed04 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -240,7 +240,7 @@ namespace Karma if (windowRenderBuffers->FrameRenderBuffers == nullptr) { - windowRenderBuffers->Count = KarmaGuiRenderer::GetWindowData().RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + windowRenderBuffers->Count = KarmaGuiRenderer::GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); // Caution: Need to think about the object instantiation and resource management // Cowboy's Note: delete is done in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_ShivaWindowRenderBuffers @@ -1506,22 +1506,24 @@ namespace Karma void KarmaGuiVulkanHandler::FillWindowData(KarmaGui_ImplVulkanH_Window* windowData, bool bCreateSyncronicity) { - windowData->RHIResources.VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); + windowData->RHIResources = new KarmaGui_ImplVulkanH_RHIResources(); - windowData->Swapchain = windowData->RHIResources.VulkanSwapChain->GetSwapChainHandle(); + windowData->RHIResources->VulkanSwapChain = FVulkanSwapChain::Create(FVulkanDynamicRHI::Get().GetDevice()); + + windowData->Swapchain = windowData->RHIResources->VulkanSwapChain->GetSwapChainHandle(); windowData->TotalImageCount = FVulkanDynamicRHI::Get().SwapChainImageCount(); - windowData->RenderArea.extent = windowData->RHIResources.VulkanSwapChain->GetSwapChainExtent(); + windowData->RenderArea.extent = windowData->RHIResources->VulkanSwapChain->GetSwapChainExtent(); windowData->RenderArea.offset = { 0, 0 }; - windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + windowData->MAX_FRAMES_IN_FLIGHT = windowData->RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); KR_CORE_INFO("Attempting to create primary Vulkan renderpass for on-screen presentation"); FVulkanRenderPassInfo RPInfo; - KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources.VulkanSwapChain, RPInfo); + KarmaGuiVulkanHandler::MakeRenderPassInfo(windowData->RHIResources->VulkanSwapChain, RPInfo); FVulkanRenderTargetLayout RTLayout(RPInfo); - windowData->RHIResources.VulkanRenderPass = new FVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); - windowData->RenderPass = windowData->RHIResources.VulkanRenderPass->GetHandle(); + windowData->RHIResources->VulkanRenderPass = new FVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); + windowData->RenderPass = windowData->RHIResources->VulkanRenderPass->GetHandle(); KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); @@ -1536,10 +1538,10 @@ namespace Karma windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; FVulkanRenderTargetsInfo* rT = new FVulkanRenderTargetsInfo(); - windowData->RHIResources.RenderTargets.Add(rT); + windowData->RHIResources->RenderTargets.push_back(rT); FVulkanRenderTargetsInfo& renderTargets = *rT; - CreateDepthRenderTarget(renderTargets, windowData->RHIResources.VulkanSwapChain); + CreateDepthRenderTarget(renderTargets, windowData->RHIResources->VulkanSwapChain); renderTargets.m_ColorRenderTargets.resize(windowData->TotalImageCount); @@ -1548,16 +1550,16 @@ namespace Karma KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[counter]; // Backbuffers are swapchain images - frameData->Backbuffer = windowData->RHIResources.VulkanSwapChain->GetSwapChainImages()[counter]; + frameData->Backbuffer = windowData->RHIResources->VulkanSwapChain->GetSwapChainImages()[counter]; // BackbufferViews are swapchain image views - frameData->BackbufferView = windowData->RHIResources.VulkanSwapChain->GetSwapChainImageViews()[counter]; + frameData->BackbufferView = windowData->RHIResources->VulkanSwapChain->GetSwapChainImageViews()[counter]; - GatherSwapChainColorRenderTargets(renderTargets, windowData->RHIResources.VulkanSwapChain, counter); + GatherSwapChainColorRenderTargets(renderTargets, windowData->RHIResources->VulkanSwapChain, counter); - FVulkanFramebuffer* frameBuffer = new FVulkanFramebuffer(*FVulkanDynamicRHI::Get().GetDevice(), renderTargets, RTLayout, *windowData->RHIResources.VulkanRenderPass, counter); + FVulkanFramebuffer* frameBuffer = new FVulkanFramebuffer(*FVulkanDynamicRHI::Get().GetDevice(), renderTargets, RTLayout, *windowData->RHIResources->VulkanRenderPass, counter); - windowData->RHIResources.VulkanFrameBuffers.Add(frameBuffer); + windowData->RHIResources->VulkanFrameBuffers.push_back(frameBuffer);// Add(frameBuffer); // Framebuffer frameData->Framebuffer = frameBuffer->GetHandle(); @@ -1741,15 +1743,15 @@ namespace Karma ClearVulkanWindowData(windowData, true); - for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers.GetElements()) + for(const auto& vulkanFramebuffer : windowData->RHIResources->VulkanFrameBuffers) { delete vulkanFramebuffer; } - windowData->RHIResources.VulkanFrameBuffers.Clear(); + windowData->RHIResources->VulkanFrameBuffers.clear(); // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ - for(const auto& renderTargets : windowData->RHIResources.RenderTargets.GetElements()) + for(const auto& renderTargets : windowData->RHIResources->RenderTargets) { for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) { @@ -1773,14 +1775,16 @@ namespace Karma delete renderTargets; } - windowData->RHIResources.RenderTargets.Clear(); + windowData->RHIResources->RenderTargets.clear(); - delete windowData->RHIResources.VulkanRenderPass; + delete windowData->RHIResources->VulkanRenderPass; FVulkanSwapChainRecreateInfo RI{}; - windowData->RHIResources.VulkanSwapChain->Destroy(&RI); - delete windowData->RHIResources.VulkanSwapChain; - windowData->RHIResources.VulkanSwapChain = nullptr; + windowData->RHIResources->VulkanSwapChain->Destroy(&RI); + delete windowData->RHIResources->VulkanSwapChain; + windowData->RHIResources->VulkanSwapChain = nullptr; + + delete windowData->RHIResources; } void KarmaGuiVulkanHandler::ClearVulkanWindowData(KarmaGui_ImplVulkanH_Window* vulkanWindowData, bool bDestroySyncronicity) @@ -1890,15 +1894,15 @@ namespace Karma // 2. Deletes imageframes and frames on flight objects ClearVulkanWindowData(windowData, true); - for(const auto& vulkanFramebuffer : windowData->RHIResources.VulkanFrameBuffers.GetElements()) + for(const auto& vulkanFramebuffer : windowData->RHIResources->VulkanFrameBuffers) { delete vulkanFramebuffer; } - windowData->RHIResources.VulkanFrameBuffers.Clear(); + windowData->RHIResources->VulkanFrameBuffers.clear(); // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ - for(const auto& renderTargets : windowData->RHIResources.RenderTargets.GetElements()) + for(const auto& renderTargets : windowData->RHIResources->RenderTargets) { for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) { @@ -1922,14 +1926,16 @@ namespace Karma delete renderTargets; } - windowData->RHIResources.RenderTargets.Clear(); + windowData->RHIResources->RenderTargets.clear(); - delete windowData->RHIResources.VulkanRenderPass; + delete windowData->RHIResources->VulkanRenderPass; FVulkanSwapChainRecreateInfo RI{}; - windowData->RHIResources.VulkanSwapChain->Destroy(&RI); - delete windowData->RHIResources.VulkanSwapChain; - windowData->RHIResources.VulkanSwapChain = nullptr; + windowData->RHIResources->VulkanSwapChain->Destroy(&RI); + delete windowData->RHIResources->VulkanSwapChain; + windowData->RHIResources->VulkanSwapChain = nullptr; + + delete windowData->RHIResources; return; // <-- to be uncommented when we have offscreen rendering logic diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 5773567d..e4c94292 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -303,13 +303,13 @@ namespace Karma * @brief Primary renderpass for on-screen presentation */ FVulkanRenderPass* VulkanRenderPass; - KarmaVector VulkanFrameBuffers; + std::vector VulkanFrameBuffers; /** * @brief Collection of render targets organized in KarmaVector. Meaning each KarmaVector element contains * a collection of rendertarget for a framebuffer */ - KarmaVector RenderTargets; + std::vector RenderTargets; }; /** @@ -475,7 +475,7 @@ namespace Karma * * @since Karma 1.0.0 */ - KarmaGui_ImplVulkanH_RHIResources RHIResources; + KarmaGui_ImplVulkanH_RHIResources* RHIResources; /** * @brief Constructor for zero clearence and relevant stuff. diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index e430442a..3bda0890 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -237,7 +237,7 @@ namespace Karma { VkDeviceSize bufferSize = GetBufferSize(); - int maxFramesInFlight = KarmaGuiRenderer::GetWindowData().RHIResources.VulkanSwapChain->GetMaxFramesInFlight(); + int maxFramesInFlight = KarmaGuiRenderer::GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); m_UniformBuffers.resize(maxFramesInFlight); m_UniformBuffersMemory.resize(maxFramesInFlight); From acfdda1c0910b6fb209447d48e31fb3653b3c13c Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 20 Jan 2026 10:27:27 +0530 Subject: [PATCH 076/107] Restoring KarmaVector wrapper for framebuffers and rendertargets --- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 28 +++++-------------- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 4 +-- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 4eaaed04..ace1bba9 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1538,7 +1538,7 @@ namespace Karma windowData->ImageFrames = new KarmaGui_ImplVulkanH_ImageFrame[windowData->TotalImageCount]; FVulkanRenderTargetsInfo* rT = new FVulkanRenderTargetsInfo(); - windowData->RHIResources->RenderTargets.push_back(rT); + windowData->RHIResources->RenderTargets.Add(rT); FVulkanRenderTargetsInfo& renderTargets = *rT; CreateDepthRenderTarget(renderTargets, windowData->RHIResources->VulkanSwapChain); @@ -1559,7 +1559,7 @@ namespace Karma FVulkanFramebuffer* frameBuffer = new FVulkanFramebuffer(*FVulkanDynamicRHI::Get().GetDevice(), renderTargets, RTLayout, *windowData->RHIResources->VulkanRenderPass, counter); - windowData->RHIResources->VulkanFrameBuffers.push_back(frameBuffer);// Add(frameBuffer); + windowData->RHIResources->VulkanFrameBuffers.Add(frameBuffer); // Framebuffer frameData->Framebuffer = frameBuffer->GetHandle(); @@ -1747,25 +1747,11 @@ namespace Karma { delete vulkanFramebuffer; } - windowData->RHIResources->VulkanFrameBuffers.clear(); + windowData->RHIResources->VulkanFrameBuffers.Clear(); - // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy - // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ + // DepthRenderTarget of the swapchain should be destroyed here. This assumes depth render target is only used for swapchain for(const auto& renderTargets : windowData->RHIResources->RenderTargets) { - for(uint32_t counter = 0; counter < FVulkanDynamicRHI::Get().SwapChainImageCount(); counter++) - { - if(!renderTargets->m_ColorRenderTargets[counter].bSwapChainColorRenderTarget) - { - for(uint32_t colorRT = 0; colorRT < renderTargets->m_NumColorRenderTargets; colorRT++) - { - vkDestroyImageView(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTViews[colorRT], nullptr); - vkDestroyImage(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTImages[colorRT], nullptr); - vkFreeMemory(logicalDevice, renderTargets->m_ColorRenderTargets[counter].m_ColorRTDeviceMemory[colorRT], nullptr); - } - } - } - if(renderTargets->bDepthRenderTarget) { vkDestroyImageView(logicalDevice, renderTargets->m_DepthRenderTarget.m_DepthRTView, nullptr); @@ -1775,7 +1761,7 @@ namespace Karma delete renderTargets; } - windowData->RHIResources->RenderTargets.clear(); + windowData->RHIResources->RenderTargets.Clear(); delete windowData->RHIResources->VulkanRenderPass; @@ -1898,7 +1884,7 @@ namespace Karma { delete vulkanFramebuffer; } - windowData->RHIResources->VulkanFrameBuffers.clear(); + windowData->RHIResources->VulkanFrameBuffers.Clear(); // What to do with the rendertargets of swapchain which are destroyed in FVulkanSwapChain::Destroy // A heuristic is used and swapchain rendertargets are not cleared here, they are cleared in ^^ @@ -1926,7 +1912,7 @@ namespace Karma delete renderTargets; } - windowData->RHIResources->RenderTargets.clear(); + windowData->RHIResources->RenderTargets.Clear(); delete windowData->RHIResources->VulkanRenderPass; diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index e4c94292..d4d48c87 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -303,13 +303,13 @@ namespace Karma * @brief Primary renderpass for on-screen presentation */ FVulkanRenderPass* VulkanRenderPass; - std::vector VulkanFrameBuffers; + KarmaVector VulkanFrameBuffers; /** * @brief Collection of render targets organized in KarmaVector. Meaning each KarmaVector element contains * a collection of rendertarget for a framebuffer */ - std::vector RenderTargets; + KarmaVector RenderTargets; }; /** From 355c575a694bacffd94653a74791827d86503400 Mon Sep 17 00:00:00 2001 From: pcboy Date: Tue, 20 Jan 2026 16:12:40 +0530 Subject: [PATCH 077/107] Minor adjustments for Linux und Mac window --- Karma/src/Platform/Linux/LinuxWindow.cpp | 4 ++-- Karma/src/Platform/Mac/MacWindow.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Karma/src/Platform/Linux/LinuxWindow.cpp b/Karma/src/Platform/Linux/LinuxWindow.cpp index 38ef25ae..265ba950 100644 --- a/Karma/src/Platform/Linux/LinuxWindow.cpp +++ b/Karma/src/Platform/Linux/LinuxWindow.cpp @@ -215,11 +215,11 @@ namespace Karma { glfwDestroyWindow(m_Window); glfwTerminate(); - if (m_Context) + /*if (m_Context) { delete m_Context; m_Context = 0; - } + }*/ } void LinuxWindow::OnUpdate() diff --git a/Karma/src/Platform/Mac/MacWindow.cpp b/Karma/src/Platform/Mac/MacWindow.cpp index 517d88f5..7f1d1181 100644 --- a/Karma/src/Platform/Mac/MacWindow.cpp +++ b/Karma/src/Platform/Mac/MacWindow.cpp @@ -36,7 +36,7 @@ namespace Karma bool MacWindow::OnResize(WindowResizeEvent& event) { - return true; //m_Context->OnWindowResize(event); + return false; //m_Context->OnWindowResize(event); } void MacWindow::Init(const WindowProps& props) From 56e4ffda7c86cc2d766717596a1d523398468f30 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 21 Jan 2026 06:57:59 +0530 Subject: [PATCH 078/107] Minor documentation for swapchain destruction routine in KarmaGuiHandler --- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index d4d48c87..bd94e362 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -1306,6 +1306,12 @@ namespace Karma */ static void GatherSwapChainColorRenderTargets(FVulkanRenderTargetsInfo& RTInfo, FVulkanSwapChain* SwapChain, uint32_t SwapChainImageIndex); + /** + * @brief Destroys swapchain and related resources for KarmaGui's Vulkan backend + * + * @param windowData Container for information required for swapchain destruction + * @since Karma 1.0.0 + */ static void ShivaSwapChainForRebuild(KarmaGui_ImplVulkanH_Window* windowData); /** From c3277c095086123ede479de58b76ee92a54d1f1c Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 23 Jan 2026 09:51:13 +0530 Subject: [PATCH 079/107] Vulkan Synchronization : Phase I --- Karma/src/Karma/Core/KarmaTypes.h | 101 ++++++++++++- .../VulkanRHI/VulkanSynchronization.cpp | 95 +++++++++++++ .../Vulkan/VulkanRHI/VulkanSynchronization.h | 134 ++++++++++++++++++ 3 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index a31255b9..b70f5e32 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -16,6 +16,15 @@ #include #include +enum class EAllowShrinking +{ + /** Allow the array to shrink its allocation if possible */ + Yes, + + /** Prevent the array from shrinking its allocation */ + No +}; + /** @brief Specifies why an actor is being deleted/removed from a level */ namespace EEndPlayReason { @@ -156,7 +165,7 @@ class KarmaVector * * @since Karma 1.0.0 */ - KarmaVector() + KarmaVector() : m_ArrayNum(0) { } @@ -172,6 +181,10 @@ class KarmaVector /** * @brief Removes an element from the vector * + * The function removes all occurrences of the specified element from the vector + * + * @note The vector size is reduced by the number of removed elements + * * @param aBlock The element to be removed * @since Karma 1.0.0 */ @@ -186,6 +199,8 @@ class KarmaVector { iterator = m_Elements.erase(iterator); occurences++; + + m_ArrayNum--; } else { @@ -205,6 +220,12 @@ class KarmaVector void Add(BuildingBlock aBlock) { m_Elements.push_back(aBlock); + m_ArrayNum++; + } + + void RangeCheck(uint32_t Index) const + { + KR_CORE_ASSERT((Index >= 0 && Index < m_ArrayNum), "KarmaVector: Range check failed"); } /** @@ -294,6 +315,28 @@ class KarmaVector return false; } + /** + * @brief Removes an element at given location, swapping the last element into its place, and + * optionally shrinking the array. + * + * @param Index The index of the element to be removed + * @param AllowShrinking Whether to allow the array to shrink its allocation if possible + * + * + * @note AllowShrinking is not functional yet + * @since Karma 1.0.0 + */ + void RemoveAtSwap(int32_t Index, EAllowShrinking AllowShrinking = EAllowShrinking::Yes) + { + RangeCheck(Index); + RemoveAtSwapImpl(Index); + + if (AllowShrinking == EAllowShrinking::Yes) + { + // not functional yet + } + } + /** * @brief Returns the total number of elements in a vector * @@ -301,7 +344,8 @@ class KarmaVector */ uint32_t Num() const { - return (uint32_t) m_Elements.size(); + //return (uint32_t) m_Elements.size(); + return m_ArrayNum; } /** @@ -335,6 +379,7 @@ class KarmaVector } m_Elements.clear(); + m_ArrayNum = 0; } /** @@ -348,6 +393,7 @@ class KarmaVector void SmartReset() { m_Elements.clear(); + m_ArrayNum = 0; } /** @@ -366,9 +412,10 @@ class KarmaVector /** * @brief Clears the vector from all the elements * + * @note Duplicate of SmartReset * @since Karma 1.0.0 */ - inline void Clear() { m_Elements.clear(); } + inline void Clear() { m_Elements.clear(); m_ArrayNum = 0; } /** * @brief Getter for the elements of vector @@ -428,7 +475,7 @@ class KarmaVector { KR_CORE_ASSERT(Index >= 0, ""); - if(Index < m_Elements.size()) + if(Index < m_ArrayNum) { return m_Elements.at(Index); } @@ -449,7 +496,7 @@ class KarmaVector */ FORCEINLINE bool IsValidIndex(int32_t Index) const { - return Index >= 0 && Index < m_Elements.size(); + return Index >= 0 && Index < m_ArrayNum; } /** @@ -459,9 +506,53 @@ class KarmaVector */ FORCEINLINE BuildingBlock const* GetData() const { return m_Elements.data(); } +private: + /** + * @brief Internal implementation of RemoveAtSwap + * + * @param Index The index of the element to be removed + * @since Karma 1.0.0 + */ + void RemoveAtSwapImpl(int32_t Index) + { + const BuildingBlock* Data = GetData(); + const BuildingBlock* Dest = Data + Index; + + DestructItem(Dest); + + // Number of elements (after the generated vacancy) to move + int32_t NumToMove = Num() - Index - 1; + + if (NumToMove) + { + // std::memcpy((void*)Dest, (const void*)(Dest + 1), sizeof(BuildingBlock) * NumToMove); + // Perplexity's suggestion for overlapping memory regions + // we are moving all elements (from Dest + 1), to Dest + std::memmove((void *) Dest, (const void*)(Dest + 1), + sizeof(BuildingBlock) * NumToMove); + } + + m_ArrayNum--; + } + + /** + * @brief Destructs an item of given ElementType + * + * @note May be placed in different space if need arises + * + * @param Element The element to be destructed + * @since Karma 1.0.0 + */ + template + FORCEINLINE void DestructItem(ElementType* Element) + { + Element->~ElementType(); + } protected: std::vector m_Elements; + + uint32_t m_ArrayNum; }; /** diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp new file mode 100644 index 00000000..6b101145 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -0,0 +1,95 @@ +#include "VulkanSynchronization.h" + +namespace Karma +{ + FVulkanFence::FVulkanFence(FVulkanDevice& InDevice, FVulkanFenceManager& InOwner, bool bCreateSignaled) : + m_Owner(InOwner), m_State(bCreateSignaled ? EState::Signaled : EState::NotReady) + { + VkFenceCreateInfo fenceInfo = {}; + + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = bCreateSignaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0; + + VkResult result = vkCreateFence(InDevice.GetLogicalDevice(), &fenceInfo, nullptr, &m_Handle); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence"); + + } + + FVulkanFence::~FVulkanFence() + { + KR_CORE_ASSERT(m_Handle == VK_NULL_HANDLE, "Fence must be destroyed by owner before being deleted"); + } + + void FVulkanFenceManager::DestroyFence(FVulkanFence* Fence) + { + vkDestroyFence(m_Device.GetLogicalDevice(), Fence->m_Handle, nullptr); + + Fence->m_Handle = VK_NULL_HANDLE; + delete Fence; + } + + FVulkanFenceManager::~FVulkanFenceManager() + { + KR_CORE_ASSERT(m_UsedFences.Num() == 0, "All used fences must be released before destroying the manager"); + } + + FVulkanFence* FVulkanFenceManager::AllocateFence(bool bCreateSignaled) + { + if (m_FreeFences.Num() != 0) + { + FVulkanFence* Fence = m_FreeFences.IndexToObject(0); + + m_FreeFences.RemoveAtSwap(0, EAllowShrinking::No); + m_UsedFences.Add(Fence); + + if (bCreateSignaled) + { + Fence->m_State = FVulkanFence::EState::Signaled; + } + else + { + Fence->m_State = FVulkanFence::EState::NotReady; + } + + return Fence; + } + + FVulkanFence* NewFence = new FVulkanFence(m_Device, *this, bCreateSignaled); + m_UsedFences.Add(NewFence); + + return NewFence; + } + + bool FVulkanFenceManager::IsFenceSignaled(FVulkanFence* InFence) + { + if (InFence->IsSignaled()) + { + return true; + } + + return CheckFenceState(InFence); + } + + bool FVulkanFenceManager::CheckFenceState(FVulkanFence* Fence) + { + KR_CORE_ASSERT(m_UsedFences.Contains(Fence), "Fence must be within m_UsedFences array"); + KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Fence state must be NotReady to check status"); + + VkResult result = vkGetFenceStatus(m_Device.GetLogicalDevice(), Fence->m_Handle); + if (result == VK_SUCCESS) + { + Fence->m_State = FVulkanFence::EState::Signaled; + return true; + } + else if (result == VK_NOT_READY) + { + Fence->m_State = FVulkanFence::EState::NotReady; + return false; + } + else + { + KR_CORE_ASSERT(false, "Failed to get fence status"); + return false; + } + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h new file mode 100644 index 00000000..96ef7e37 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h @@ -0,0 +1,134 @@ +/** + * @file VulkanSynchronization.h + * @brief Header file for Vulkan synchronization primitives. + * + * This file declares functions and types related to synchronization in Vulkan, + * including fences, semaphores, and barriers. + * + * @author Ravi Mohan (the_cowboy) + * @version 1.0 + * + * @date 22 January, 2026 + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "VulkanRHI/VulkanDevice.h" +#include "KarmaTypes.h" + +namespace Karma +{ + class FVulkanFence; + + class FVulkanFenceManager + { + public: + FVulkanFenceManager(FVulkanDevice& InDevice) + : m_Device(InDevice) + { + } + + ~FVulkanFenceManager(); + + void Denit() {} + + FVulkanFence* AllocateFence(bool bCreateSignaled = false); + + bool IsFenceSignaled(FVulkanFence* InFence); + + bool WaitForFence(FVulkanFence* Fence) {} + + void ResetFence(FVulkanFence* Fence) {} + + void ReleaseFence(FVulkanFence*& Fence) {} + + void WaitAndReleaseFence(FVulkanFence*& Fence, uint64_t TimeNanoSeconds) {} + + protected: + /** + * @brief Returns true if the fence is signaled, false otherwise. + * + * Sets the state of the fence accordingly i.e + * 1. If vkGetFenceStatus returns VK_SUCCESS, fence state is set to Signaled + * 2. If vkGetFenceStatus returns VK_NOT_READY, fence state is set to NotReady, + * meaning associated GPU operations are still pending + * + * Assumptions: + * 1. The fence is in NotReady state when this function is called. So this function + * is used for fences which are supposed to be signaled later after GPU operations complete. + * 2. The fence is owned by this manager and is present in m_UsedFences array + * + * @note Typically usage is in polling scenarios where CPU wants to check completion status + * opportunistically without blocking. For blocking waits, use WaitForFence(). + * + * @param Fence The fence to be checked + * @since Karma 1.0.0 + */ + bool CheckFenceState(FVulkanFence* Fence); + + /** + * @brief Destroys the given fence and releases its resources. + * + * @param InFence The fence to be destroyed + * @since Karma 1.0.0 + */ + void DestroyFence(FVulkanFence* InFence); + + protected: + FVulkanDevice& m_Device; + + KarmaVector m_FreeFences; + KarmaVector m_UsedFences; + }; + + /** + * @brief Represents a Vulkan fence used for synchronization between the CPU and GPU. + * + * A fence is a synchronization primitive that can be used to coordinate operations between the CPU and GPU. + * It allows the CPU to wait for the completion of GPU operations, ensuring that resources are not accessed + * prematurely. + * + * @since Karma 1.0.0 + */ + class FVulkanFence + { + public: + FVulkanFence(FVulkanDevice& InDevice, FVulkanFenceManager& InOwner, bool bCreateSignaled); + + inline bool IsSignaled() const + { + return m_State == EState::Signaled; + } + + // Only owner can create and manage fences + ~FVulkanFence(); + + protected: + VkFence m_Handle; + + enum class EState + { + /** + * @brief The fence is not signaled, initial state after reset + * + * @note Fence must be signaled when created + * https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation#waiting-for-the-previous-frame + */ + NotReady, + + /** + * @brief The fence is signaled, indicating that the associated operations have completed + * + * @note vkWaitForFences will pass when fence is in this state + */ + Signaled, + }; + + EState m_State; + + FVulkanFenceManager& m_Owner; + + friend FVulkanFenceManager; + }; +} \ No newline at end of file From 4b704e17911e837c06298b19c5210fccaf516d4d Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 24 Jan 2026 05:24:56 +0530 Subject: [PATCH 080/107] Minor code addition in VulkanSynacronization and documentation --- .../VulkanRHI/VulkanSynchronization.cpp | 33 ++++++++++++++++- .../Vulkan/VulkanRHI/VulkanSynchronization.h | 36 +++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index 6b101145..c3ed97d7 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -92,4 +92,35 @@ namespace Karma return false; } } -} \ No newline at end of file + + void FVulkanFenceManager::Denit() + { + KR_CORE_ASSERT(m_UsedFences.Num() == 0, "Not all fences are done") + + for(FVulkanFence* fence : m_FreeFences) + { + DestroyFence(fence); + } + } + + bool FVulkanFenceManager::WaitForFence(FVulkanFence* Fence) + { + KR_CORE_ASSERT(m_UsedFences.Contains(Fence), "Not a usable fence"); + KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Not a suitable fence state for wait"); + + VkResult result = vkWaitForFences(m_Device.GetLogicalDevice(), 1, &Fence->m_Handle, true, UINT64_MAX); + + switch (result) + { + case VK_SUCCESS: + Fence->m_State = FVulkanFence::EState::Signaled; + return true; + case VK_TIMEOUT: + break; + default: + break; + } + + return false; + } +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h index 96ef7e37..12770887 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h @@ -29,15 +29,34 @@ namespace Karma { } + /** + * @brief Makes sure m_UsedFences are zero i.e no fences are currently in use by + * GPU or inflight-work + * + * @since Karma 1.0.0 + */ ~FVulkanFenceManager(); - void Denit() {} + /** + * @brief Purpose is two-fold + * - 1. Makes sure no fences are currently in use (m_UsedFences.Num ==0) + * - 2. Clear off m_FreeFences + * + * @since Karma 1.0.0 + */ + void Denit(); + /** + * @brief Looks in m_FreeFences, if array is not empty, uses the first fence and fills m_UsedFence. + * If m_FreeFences is empty, creates new FVulkanFence and fills m_UsedFence. + * + * @param bCreateSignaled Should the fence be in Signaled state + */ FVulkanFence* AllocateFence(bool bCreateSignaled = false); bool IsFenceSignaled(FVulkanFence* InFence); - bool WaitForFence(FVulkanFence* Fence) {} + bool WaitForFence(FVulkanFence* Fence); void ResetFence(FVulkanFence* Fence) {} @@ -78,7 +97,18 @@ namespace Karma protected: FVulkanDevice& m_Device; + /** + * @brief Array of free fences to be used. + * + * @note This gets filled mainly by ReleaseFence and WaitAndReleaseFence once + * work is finished. + */ KarmaVector m_FreeFences; + + /** + * @brief Array of fences currently in use by GPU or in-flight + * work. + */ KarmaVector m_UsedFences; }; @@ -131,4 +161,4 @@ namespace Karma friend FVulkanFenceManager; }; -} \ No newline at end of file +} From 3495d71b666c1f67dda0f46cc49076a4e7c5f70a Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 25 Jan 2026 13:22:53 +0530 Subject: [PATCH 081/107] Vulkan Synchronization : Phase II --- Karma/src/Karma/Core/KarmaTypes.h | 25 ++++++++ Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 17 +++-- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 25 +++----- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 3 +- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 3 +- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 5 ++ .../VulkanRHI/VulkanSynchronization.cpp | 34 +++++++++- .../Vulkan/VulkanRHI/VulkanSynchronization.h | 63 +++++++++++++++++-- 8 files changed, 143 insertions(+), 32 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index b70f5e32..615eb375 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -337,6 +337,31 @@ class KarmaVector } } + /** + * @brief Removes a first appearence of single element from the array, swapping the last element + * into its place, and optionally shrinking the array. + * + * @param Item The item to be removed + * @param AllowShrinking Whether to allow the array to shrink its allocation if possible + * @returns The number of elements removed (0 or 1) + * + * @note AllowShrinking is not functional yet + * @since Karma 1.0.0 + */ + uint32_t RemoveSingleSwap(const BuildingBlock& Item, EAllowShrinking AllowShrinking = EAllowShrinking::Yes) + { + int32_t Index = Find(Item); + + if (Index == INDEX_NONE) + { + return 0; + } + + RemoveAtSwap(Index, AllowShrinking); + + return 1; + } + /** * @brief Returns the total number of elements in a vector * diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index ee55655c..6397044f 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -6,6 +6,7 @@ #include "Platform/Vulkan/VulkanVertexArray.h" #include "VulkanRHI/VulkanDynamicRHI.h" #include "VulkanRHI/VulkanSwapChain.h" +#include "VulkanRHI/VulkanSynchronization.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -414,8 +415,10 @@ namespace Karma VkResult result; // Fence needs to be signaled to pass the vkWaitForFences. - result = vkWaitForFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence, VK_TRUE, UINT64_MAX); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); + // VkFence fenceHandle = frameOnFlightData->Fence->GetHandle(); + // result = vkWaitForFences(vulkanInfo->Device, 1, &fenceHandle, VK_TRUE, UINT64_MAX); + // KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; VkSemaphore renderCompleteSemaphore = frameOnFlightData->RenderCompleteSemaphore; @@ -578,11 +581,13 @@ namespace Karma // vkResetFences unsignals the Fence // Fixing a deadlock: https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation#page_Fixing-a-deadlock - result = vkResetFences(vulkanInfo->Device, 1, &frameOnFlightData->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); - + // result = vkResetFences(vulkanInfo->Device, 1, &fence); + // KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().ResetFence(frameOnFlightData->Fence); + + VkFence fence = frameOnFlightData->Fence->GetHandle(); // vkQueueSubmit signals the Fence once commandbuffers finish execution - result = vkQueueSubmit(vulkanInfo->Queue, 1, &submitInfo, frameOnFlightData->Fence); + result = vkQueueSubmit(vulkanInfo->Queue, 1, &submitInfo, fence); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to submit queue"); } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index ace1bba9..9b1b0471 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1598,12 +1598,13 @@ namespace Karma { KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; - VkFenceCreateInfo fenceInfo = {}; + /*VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; VkResult result = vkCreateFence(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &fenceInfo, VK_NULL_HANDLE, &frameOnFlight->Fence); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence");*/ - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence"); + frameOnFlight->Fence = FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().AllocateFence(true); VkSemaphoreCreateInfo semaphoreInfo = {}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -1839,8 +1840,9 @@ namespace Karma vkDeviceWaitIdle(vulkanInfo->Device); - vkDestroyFence(vulkanInfo->Device, frameSyncronicityData->Fence, VK_NULL_HANDLE); - frameSyncronicityData->Fence = VK_NULL_HANDLE; + //vkDestroyFence(vulkanInfo->Device, frameSyncronicityData->Fence, VK_NULL_HANDLE); + //frameSyncronicityData->Fence = VK_NULL_HANDLE; + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().ReleaseFence(frameSyncronicityData->Fence); vkDestroySemaphore(vulkanInfo->Device, frameSyncronicityData->ImageAcquiredSemaphore, VK_NULL_HANDLE); vkDestroySemaphore(vulkanInfo->Device, frameSyncronicityData->RenderCompleteSemaphore, VK_NULL_HANDLE); @@ -1879,6 +1881,8 @@ namespace Karma // 1. Removes synchronicity // 2. Deletes imageframes and frames on flight objects ClearVulkanWindowData(windowData, true); + + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().Denit(); for(const auto& vulkanFramebuffer : windowData->RHIResources->VulkanFrameBuffers) { @@ -2042,14 +2046,6 @@ namespace Karma frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; } - for (;;) - { - result = vkWaitForFences(vulkanInfo->Device, 1, &frameFlightData->Fence, VK_TRUE, 100); - if (result == VK_SUCCESS) break; - if (result == VK_TIMEOUT) continue; - - KR_CORE_ASSERT((result == VK_SUCCESS || VK_TIMEOUT), "Something not right"); - } { //result = vkResetCommandPool(vulkanInfo->Device, windowData->CommandPool, 0); @@ -2103,11 +2099,6 @@ namespace Karma result = vkEndCommandBuffer(frameFlightData->CommandBuffer); KR_CORE_ASSERT(result == VK_SUCCESS, "Coudn't finish the commandbuffer recording") - result = vkResetFences(vulkanInfo->Device, 1, &frameFlightData->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't reset the fences"); - - result = vkQueueSubmit(vulkanInfo->Queue, 1, &info, frameFlightData->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't submit the queue"); } } } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index bd94e362..4ca502ac 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -29,6 +29,7 @@ namespace Karma class FVulkanRenderPass; class FVulkanRenderTargetsInfo; class FVulkanFramebuffer; + class FVulkanFence; /** * @brief Macro for vulkan's alignment needs. @@ -214,7 +215,7 @@ namespace Karma * * @since Karma 1.0.0 */ - VkFence Fence; + FVulkanFence* Fence; /** * @brief Handle to semaphore object for next available presentable image (vkAcquireNextImageKHR). diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 251c4ac8..c87600c7 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -6,7 +6,8 @@ namespace Karma { FVulkanDevice::FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu) : m_VulkanDynamicRHI(InRHI), - m_GPU(InGpu) + m_GPU(InGpu), m_FenceManager(*this), m_CommandPool(VK_NULL_HANDLE), m_DefaultTexture(nullptr), m_LogicalDevice(VK_NULL_HANDLE), + m_GraphicsQueue(VK_NULL_HANDLE), m_PresentQueue(VK_NULL_HANDLE) { // Implementation for creating a Vulkan logical device } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index ebfaa723..d43c0374 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -11,6 +11,7 @@ #pragma once #include +#include "VulkanSynchronization.h" namespace Karma { @@ -146,6 +147,8 @@ namespace Karma inline VkQueue GetGraphicsQueue() const { return m_GraphicsQueue; } inline FVulkanDynamicRHI* GetVulkanDynamicRHI() const { return m_VulkanDynamicRHI; } + + inline FVulkanFenceManager& GetFenceManager() { return m_FenceManager; } private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. @@ -158,5 +161,7 @@ namespace Karma // Additional members for managing queues, command pools, etc. can be added here. VkCommandPool m_CommandPool; + + FVulkanFenceManager m_FenceManager; }; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index c3ed97d7..b760a024 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -1,4 +1,5 @@ #include "VulkanSynchronization.h" +#include "VulkanDevice.h" namespace Karma { @@ -106,7 +107,7 @@ namespace Karma bool FVulkanFenceManager::WaitForFence(FVulkanFence* Fence) { KR_CORE_ASSERT(m_UsedFences.Contains(Fence), "Not a usable fence"); - KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Not a suitable fence state for wait"); + //KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Not a suitable fence state for wait"); VkResult result = vkWaitForFences(m_Device.GetLogicalDevice(), 1, &Fence->m_Handle, true, UINT64_MAX); @@ -123,4 +124,35 @@ namespace Karma return false; } + + void FVulkanFenceManager::ResetFence(FVulkanFence* Fence) + { + if (Fence->m_State != FVulkanFence::EState::NotReady) + { + VkResult result = vkResetFences(m_Device.GetLogicalDevice(), 1, &Fence->m_Handle); + Fence->m_State = FVulkanFence::EState::NotReady; + } + } + + void FVulkanFenceManager::ReleaseFence(FVulkanFence*& Fence) + { + ResetFence(Fence); + m_UsedFences.RemoveSingleSwap(Fence, EAllowShrinking::No); + + m_FreeFences.Add(Fence);// add copy of the pointer to free list + Fence = nullptr;// nullify the caller's pointer + } + + void FVulkanFenceManager::WaitAndReleaseFence(FVulkanFence*& Fence) + { + if (!Fence->IsSignaled()) + { + WaitForFence(Fence); + } + + ResetFence(Fence); + m_UsedFences.RemoveSingleSwap(Fence, EAllowShrinking::No); + m_FreeFences.Add(Fence);// add copy of the pointer to free list + Fence = nullptr;// nullify the caller's pointer + } } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h index 12770887..9961cdf6 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h @@ -14,12 +14,13 @@ #pragma once -#include "VulkanRHI/VulkanDevice.h" +#include #include "KarmaTypes.h" namespace Karma { class FVulkanFence; + class FVulkanDevice; class FVulkanFenceManager { @@ -39,7 +40,7 @@ namespace Karma /** * @brief Purpose is two-fold - * - 1. Makes sure no fences are currently in use (m_UsedFences.Num ==0) + * - 1. Makes sure no fences are currently in use (m_UsedFences.Num == 0) * - 2. Clear off m_FreeFences * * @since Karma 1.0.0 @@ -54,15 +55,60 @@ namespace Karma */ FVulkanFence* AllocateFence(bool bCreateSignaled = false); + /** + * @brief Checks if the given fence is signaled. + * + * First checks the CPU-side state of the fence. If the fence is already marked as signaled, + * returns true immediately. If not, queries the Vulkan API (via CheckFenceState) to check the + * actual status of the fence. + * + * @param InFence The fence to be checked + * @return true if the fence is signaled, false otherwise + * @see CheckFenceState + * @since Karma 1.0.0 + */ bool IsFenceSignaled(FVulkanFence* InFence); + /** + * @brief Waits for the given fence to be signaled. + * + * @todo Ponder over the use of this funciton + * @param Fence The fence to wait for + * + * @since Karma 1.0.0 + */ bool WaitForFence(FVulkanFence* Fence); - void ResetFence(FVulkanFence* Fence) {} + /** + * @brief Resets the given fence to the unsignaled state. + * + * @note Both CPU and GPU side states are reset. + * + * @param Fence The fence to be reset + * @since Karma 1.0.0 + */ + void ResetFence(FVulkanFence* Fence); - void ReleaseFence(FVulkanFence*& Fence) {} + /** + * @brief Releases the given fence back to the manager. + * + * Moves the fence from m_UsedFences to m_FreeFences for future reuse. + * + * @note This is kind of reverse of AllocateFence + * @param Fence The fence to be released + * @since Karma 1.0.0 + */ + void ReleaseFence(FVulkanFence*& Fence); - void WaitAndReleaseFence(FVulkanFence*& Fence, uint64_t TimeNanoSeconds) {} + /** + * @brief Waits for the given fence to be signaled and then releases it. + * + * Combines the functionality of WaitForFence and ReleaseFence. + * + * @param Fence The fence to wait for and release + * @since Karma 1.0.0 + */ + void WaitAndReleaseFence(FVulkanFence*& Fence); protected: /** @@ -131,6 +177,11 @@ namespace Karma return m_State == EState::Signaled; } + inline VkFence GetHandle() const + { + return m_Handle; + } + // Only owner can create and manage fences ~FVulkanFence(); @@ -143,7 +194,7 @@ namespace Karma * @brief The fence is not signaled, initial state after reset * * @note Fence must be signaled when created - * https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation#waiting-for-the-previous-frame + * https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation#page_Waiting-for-the-previous-frame */ NotReady, From 44abeb9c7c5a3e434bf677492545f35f20a1c34b Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 26 Jan 2026 06:14:12 +0530 Subject: [PATCH 082/107] API modification for FenceManager's needs --- Karma/src/Karma/Core/KarmaTypes.h | 59 ++++++++----------- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 4 +- .../VulkanRHI/VulkanSynchronization.cpp | 6 +- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 615eb375..608aa544 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -15,6 +15,7 @@ #include #include #include +#include enum class EAllowShrinking { @@ -165,7 +166,7 @@ class KarmaVector * * @since Karma 1.0.0 */ - KarmaVector() : m_ArrayNum(0) + KarmaVector() { } @@ -199,8 +200,6 @@ class KarmaVector { iterator = m_Elements.erase(iterator); occurences++; - - m_ArrayNum--; } else { @@ -220,12 +219,11 @@ class KarmaVector void Add(BuildingBlock aBlock) { m_Elements.push_back(aBlock); - m_ArrayNum++; } void RangeCheck(uint32_t Index) const { - KR_CORE_ASSERT((Index >= 0 && Index < m_ArrayNum), "KarmaVector: Range check failed"); + KR_CORE_ASSERT((Index >= 0 && Index < Num()), "KarmaVector: Range check failed"); } /** @@ -317,38 +315,38 @@ class KarmaVector /** * @brief Removes an element at given location, swapping the last element into its place, and - * optionally shrinking the array. + * shrinking the array. * * @param Index The index of the element to be removed * @param AllowShrinking Whether to allow the array to shrink its allocation if possible * * - * @note AllowShrinking is not functional yet + * @note This implementation is differenct from UE and here vector shrinks automatically * @since Karma 1.0.0 */ - void RemoveAtSwap(int32_t Index, EAllowShrinking AllowShrinking = EAllowShrinking::Yes) + void RemoveAtSwap(int32_t Index/*, EAllowShrinking AllowShrinking = EAllowShrinking::Yes*/) { RangeCheck(Index); RemoveAtSwapImpl(Index); - if (AllowShrinking == EAllowShrinking::Yes) - { + //if (AllowShrinking == EAllowShrinking::Yes) + //{ // not functional yet - } + //} } /** * @brief Removes a first appearence of single element from the array, swapping the last element - * into its place, and optionally shrinking the array. + * into its place, and shrinking the array. * * @param Item The item to be removed * @param AllowShrinking Whether to allow the array to shrink its allocation if possible * @returns The number of elements removed (0 or 1) * - * @note AllowShrinking is not functional yet + * @note AllowShrinking is automatic different from UE * @since Karma 1.0.0 */ - uint32_t RemoveSingleSwap(const BuildingBlock& Item, EAllowShrinking AllowShrinking = EAllowShrinking::Yes) + uint32_t RemoveSingleSwap(const BuildingBlock& Item/*, EAllowShrinking AllowShrinking = EAllowShrinking::Yes*/) { int32_t Index = Find(Item); @@ -357,7 +355,7 @@ class KarmaVector return 0; } - RemoveAtSwap(Index, AllowShrinking); + RemoveAtSwap(Index/*, AllowShrinking*/); return 1; } @@ -369,8 +367,7 @@ class KarmaVector */ uint32_t Num() const { - //return (uint32_t) m_Elements.size(); - return m_ArrayNum; + return (uint32_t) m_Elements.size(); } /** @@ -404,7 +401,6 @@ class KarmaVector } m_Elements.clear(); - m_ArrayNum = 0; } /** @@ -418,7 +414,6 @@ class KarmaVector void SmartReset() { m_Elements.clear(); - m_ArrayNum = 0; } /** @@ -440,7 +435,7 @@ class KarmaVector * @note Duplicate of SmartReset * @since Karma 1.0.0 */ - inline void Clear() { m_Elements.clear(); m_ArrayNum = 0; } + inline void Clear() { m_Elements.clear(); } /** * @brief Getter for the elements of vector @@ -500,7 +495,7 @@ class KarmaVector { KR_CORE_ASSERT(Index >= 0, ""); - if(Index < m_ArrayNum) + if(Index < Num()) { return m_Elements.at(Index); } @@ -521,7 +516,7 @@ class KarmaVector */ FORCEINLINE bool IsValidIndex(int32_t Index) const { - return Index >= 0 && Index < m_ArrayNum; + return Index >= 0 && Index < Num(); } /** @@ -546,18 +541,18 @@ class KarmaVector DestructItem(Dest); // Number of elements (after the generated vacancy) to move - int32_t NumToMove = Num() - Index - 1; + const int32_t NumElementsAfterHole = Num() - Index - 1; + const int32_t NumElementsToMoveIntoHole = glm::min(1, NumElementsAfterHole); - if (NumToMove) + if (NumElementsToMoveIntoHole) { - // std::memcpy((void*)Dest, (const void*)(Dest + 1), sizeof(BuildingBlock) * NumToMove); - // Perplexity's suggestion for overlapping memory regions - // we are moving all elements (from Dest + 1), to Dest - std::memmove((void *) Dest, (const void*)(Dest + 1), - sizeof(BuildingBlock) * NumToMove); + //std::memmove((void *) Dest, (const void*)(Data + Num() - NumElementsToMoveIntoHole), + // sizeof(BuildingBlock) * NumElementsToMoveIntoHole); + const int32_t LastElementIndex = Num() - 1; + + m_Elements[Index] = m_Elements[LastElementIndex]; } - - m_ArrayNum--; + m_Elements.pop_back(); } /** @@ -576,8 +571,6 @@ class KarmaVector protected: std::vector m_Elements; - - uint32_t m_ArrayNum; }; /** diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 6397044f..faca11b0 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -418,7 +418,7 @@ namespace Karma // VkFence fenceHandle = frameOnFlightData->Fence->GetHandle(); // result = vkWaitForFences(vulkanInfo->Device, 1, &fenceHandle, VK_TRUE, UINT64_MAX); // KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); - FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); + //FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; VkSemaphore renderCompleteSemaphore = frameOnFlightData->RenderCompleteSemaphore; @@ -431,6 +431,8 @@ namespace Karma } KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to acquire next image"); + + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); // Pointer to the container of framebuffers (based on number of swapchain images) KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index b760a024..97982942 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -40,7 +40,7 @@ namespace Karma { FVulkanFence* Fence = m_FreeFences.IndexToObject(0); - m_FreeFences.RemoveAtSwap(0, EAllowShrinking::No); + m_FreeFences.RemoveAtSwap(0/*, EAllowShrinking::No*/); m_UsedFences.Add(Fence); if (bCreateSignaled) @@ -137,7 +137,7 @@ namespace Karma void FVulkanFenceManager::ReleaseFence(FVulkanFence*& Fence) { ResetFence(Fence); - m_UsedFences.RemoveSingleSwap(Fence, EAllowShrinking::No); + m_UsedFences.RemoveSingleSwap(Fence/*, EAllowShrinking::No*/); m_FreeFences.Add(Fence);// add copy of the pointer to free list Fence = nullptr;// nullify the caller's pointer @@ -151,7 +151,7 @@ namespace Karma } ResetFence(Fence); - m_UsedFences.RemoveSingleSwap(Fence, EAllowShrinking::No); + m_UsedFences.RemoveSingleSwap(Fence/*, EAllowShrinking::No*/); m_FreeFences.Add(Fence);// add copy of the pointer to free list Fence = nullptr;// nullify the caller's pointer } From 64503959fa7a15e5f54ee5cf0c0de4c1cce5316a Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 26 Jan 2026 06:47:18 +0530 Subject: [PATCH 083/107] Resolving vkwaitforfence stall for macos --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 15 ++++++--------- .../Vulkan/VulkanRHI/VulkanSynchronization.cpp | 1 + 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index faca11b0..4c21b62f 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -415,10 +415,7 @@ namespace Karma VkResult result; // Fence needs to be signaled to pass the vkWaitForFences. - // VkFence fenceHandle = frameOnFlightData->Fence->GetHandle(); - // result = vkWaitForFences(vulkanInfo->Device, 1, &fenceHandle, VK_TRUE, UINT64_MAX); - // KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); - //FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); + FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); VkSemaphore imageAcquiredSemaphore = frameOnFlightData->ImageAcquiredSemaphore; VkSemaphore renderCompleteSemaphore = frameOnFlightData->RenderCompleteSemaphore; @@ -427,12 +424,12 @@ namespace Karma if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { m_SwapChainRebuild = true; - return; + //return; we provide work to do anyway so the fence can be signaled properly. This was causing a stall on MacOS + } + else + { + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to acquire next image"); } - - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to acquire next image"); - - FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().WaitForFence(frameOnFlightData->Fence); // Pointer to the container of framebuffers (based on number of swapchain images) KarmaGui_ImplVulkanH_ImageFrame* frameData = &windowData->ImageFrames[windowData->ImageFrameIndex]; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index 97982942..053a8e6c 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -110,6 +110,7 @@ namespace Karma //KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Not a suitable fence state for wait"); VkResult result = vkWaitForFences(m_Device.GetLogicalDevice(), 1, &Fence->m_Handle, true, UINT64_MAX); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); switch (result) { From 5e0e7d2564484749616b03788b343e0576d28fb2 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 26 Jan 2026 13:18:27 +0530 Subject: [PATCH 084/107] Minor documentation --- Karma/src/Karma/Core/KarmaTypes.h | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 608aa544..023c3d44 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -1,7 +1,7 @@ /** * @file KarmaTypes.h * @author Ravi Mohan (the_cowboy) - * @brief This file contains the types used in game logic + * @brief This file contains the custom types used in Engine's logic * @version 1.0 * @date March 30, 2023 * @@ -46,7 +46,8 @@ namespace EEndPlayReason /** * @brief euphemism for no index situations - * @todo usage hasn't caught on in usual engine routines + * + * @see KarmaVector::Find, KarmaVector::RemoveSingleSwap etc */ enum { INDEX_NONE = -1 }; @@ -155,7 +156,9 @@ class KarmaMap }; /** - * @brief Karma's std::vector wrapper + * @brief Karma's std::vector wrapper with additional functionalities + * + * @since Karma 1.0.0 */ template class KarmaVector @@ -221,17 +224,22 @@ class KarmaVector m_Elements.push_back(aBlock); } + /** + * @brief Sanity check for the index + * + * @since Karma 1.0.0 + */ void RangeCheck(uint32_t Index) const { KR_CORE_ASSERT((Index >= 0 && Index < Num()), "KarmaVector: Range check failed"); } /** - * Adds unique element to array if it doesn't exist. + * Adds unique element to array if the element doesn't exist. * * Move semantics version. * - * @param Item Item to add. + * @param Item Item to add * @returns Index of the element in the array. * * @see Add, AddDefaulted, AddZeroed, Append, Insert @@ -253,7 +261,7 @@ class KarmaVector } /** - * Finds element within the array. + * Finds first occurance of an element within the array. * * @param Item Item to look for. * @returns Index of the found element. INDEX_NONE otherwise. @@ -454,17 +462,6 @@ class KarmaVector /** * @brief Getter for first vector element - * - * @note The range based loop breaks for MSVC atm - * for(const auto& element : SomeKarmaVector) - * {} - * - * Perplexity gives answer: - * MSVC's _ITERATOR_DEBUG_LEVEL=2 tracks global iterator ownership and gets confused by - * forwarded iterators from KarmaVector::begin(). Direct m_Elements access bypasses the wrapper entirely → no forwarding - * → no corruption → works perfectly. - * - * Also note KarmaVector::begin() works just fine on Mac and Linux (Clang and gcc) * * @since Karma 1.0.0 */ From a1eee0b550b4052100f96b00613df9b92a72f5c9 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 27 Jan 2026 06:49:05 +0530 Subject: [PATCH 085/107] More documentation and cleaning of redundant code --- CMakeLists.txt | 4 +- Karma/src/Karma/Core/KarmaTypes.h | 16 ---- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 2 - .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 6 -- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 15 ++- .../VulkanRHI/VulkanSynchronization.cpp | 47 ---------- .../Vulkan/VulkanRHI/VulkanSynchronization.h | 92 +++++++++---------- 7 files changed, 58 insertions(+), 124 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39569602..d16db6af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,13 +87,13 @@ add_subdirectory(Karma) list(APPEND ESSENTIAL_LIBS Karma) # Build the Client -add_subdirectory(Application) +# add_subdirectory(Application) # Build the Editor add_subdirectory(Pranjal) # Relevant linking of finished Application with the Engine! -target_link_libraries(SandBox PUBLIC KarmaEngine) +# target_link_libraries(SandBox PUBLIC KarmaEngine) target_link_libraries(Pranjal PUBLIC KarmaEngine) if (APPLE AND MAC_PACKAGE) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 023c3d44..3925a96e 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -535,8 +535,6 @@ class KarmaVector const BuildingBlock* Data = GetData(); const BuildingBlock* Dest = Data + Index; - DestructItem(Dest); - // Number of elements (after the generated vacancy) to move const int32_t NumElementsAfterHole = Num() - Index - 1; const int32_t NumElementsToMoveIntoHole = glm::min(1, NumElementsAfterHole); @@ -552,20 +550,6 @@ class KarmaVector m_Elements.pop_back(); } - /** - * @brief Destructs an item of given ElementType - * - * @note May be placed in different space if need arises - * - * @param Element The element to be destructed - * @since Karma 1.0.0 - */ - template - FORCEINLINE void DestructItem(ElementType* Element) - { - Element->~ElementType(); - } - protected: std::vector m_Elements; }; diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 4c21b62f..e13af69f 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -580,8 +580,6 @@ namespace Karma // vkResetFences unsignals the Fence // Fixing a deadlock: https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation#page_Fixing-a-deadlock - // result = vkResetFences(vulkanInfo->Device, 1, &fence); - // KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to reset fence"); FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().ResetFence(frameOnFlightData->Fence); VkFence fence = frameOnFlightData->Fence->GetHandle(); diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 9b1b0471..f03e1336 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1598,12 +1598,6 @@ namespace Karma { KarmaGui_Vulkan_Frame_On_Flight* frameOnFlight = &windowData->FramesOnFlight[counter]; - /*VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - VkResult result = vkCreateFence(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(), &fenceInfo, VK_NULL_HANDLE, &frameOnFlight->Fence); - KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create fence");*/ - frameOnFlight->Fence = FVulkanDynamicRHI::Get().GetDevice()->GetFenceManager().AllocateFence(true); VkSemaphoreCreateInfo semaphoreInfo = {}; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index d43c0374..4c58f991 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -33,9 +33,10 @@ namespace Karma /** * @brief Constructor for FVulkanDevice. * - * + * Initializes the Vulkan device with the given physical device (GPU) and dynamic RHI instance. * - * @param + * @param InRHI Pointer to the FVulkanDynamicRHI instance + * @param InGpu The Vulkan physical device (GPU) handle * * @since Karma 1.0.0 */ @@ -146,8 +147,18 @@ namespace Karma */ inline VkQueue GetGraphicsQueue() const { return m_GraphicsQueue; } + /** + * @brief Getter for the present queue created in FVulkanDevice::InitGPU() + * + * @since Karma 1.0.0 + */ inline FVulkanDynamicRHI* GetVulkanDynamicRHI() const { return m_VulkanDynamicRHI; } + /** + * @brief Getter for the default texture (unreal grid) + * + * @since Karma 1.0.0 + */ inline FVulkanFenceManager& GetFenceManager() { return m_FenceManager; } private: diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index 053a8e6c..8dcc6498 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -61,39 +61,6 @@ namespace Karma return NewFence; } - bool FVulkanFenceManager::IsFenceSignaled(FVulkanFence* InFence) - { - if (InFence->IsSignaled()) - { - return true; - } - - return CheckFenceState(InFence); - } - - bool FVulkanFenceManager::CheckFenceState(FVulkanFence* Fence) - { - KR_CORE_ASSERT(m_UsedFences.Contains(Fence), "Fence must be within m_UsedFences array"); - KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Fence state must be NotReady to check status"); - - VkResult result = vkGetFenceStatus(m_Device.GetLogicalDevice(), Fence->m_Handle); - if (result == VK_SUCCESS) - { - Fence->m_State = FVulkanFence::EState::Signaled; - return true; - } - else if (result == VK_NOT_READY) - { - Fence->m_State = FVulkanFence::EState::NotReady; - return false; - } - else - { - KR_CORE_ASSERT(false, "Failed to get fence status"); - return false; - } - } - void FVulkanFenceManager::Denit() { KR_CORE_ASSERT(m_UsedFences.Num() == 0, "Not all fences are done") @@ -107,7 +74,6 @@ namespace Karma bool FVulkanFenceManager::WaitForFence(FVulkanFence* Fence) { KR_CORE_ASSERT(m_UsedFences.Contains(Fence), "Not a usable fence"); - //KR_CORE_ASSERT(Fence->m_State == FVulkanFence::EState::NotReady, "Not a suitable fence state for wait"); VkResult result = vkWaitForFences(m_Device.GetLogicalDevice(), 1, &Fence->m_Handle, true, UINT64_MAX); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait"); @@ -143,17 +109,4 @@ namespace Karma m_FreeFences.Add(Fence);// add copy of the pointer to free list Fence = nullptr;// nullify the caller's pointer } - - void FVulkanFenceManager::WaitAndReleaseFence(FVulkanFence*& Fence) - { - if (!Fence->IsSignaled()) - { - WaitForFence(Fence); - } - - ResetFence(Fence); - m_UsedFences.RemoveSingleSwap(Fence/*, EAllowShrinking::No*/); - m_FreeFences.Add(Fence);// add copy of the pointer to free list - Fence = nullptr;// nullify the caller's pointer - } } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h index 9961cdf6..074fd277 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h @@ -25,6 +25,12 @@ namespace Karma class FVulkanFenceManager { public: + /** + * @brief Constructor + * + * @param InDevice Reference to the owning FVulkanDevice + * @since Karma 1.0.0 + */ FVulkanFenceManager(FVulkanDevice& InDevice) : m_Device(InDevice) { @@ -40,7 +46,7 @@ namespace Karma /** * @brief Purpose is two-fold - * - 1. Makes sure no fences are currently in use (m_UsedFences.Num == 0) + * - 1. Makes sure no fences are currently in use (m_UsedFences.Num() == 0) * - 2. Clear off m_FreeFences * * @since Karma 1.0.0 @@ -48,31 +54,23 @@ namespace Karma void Denit(); /** - * @brief Looks in m_FreeFences, if array is not empty, uses the first fence and fills m_UsedFence. + * @brief Allocates fence for use + * + * Looks in m_FreeFences, if array is not empty, uses the first fence and fills m_UsedFence. * If m_FreeFences is empty, creates new FVulkanFence and fills m_UsedFence. * * @param bCreateSignaled Should the fence be in Signaled state - */ - FVulkanFence* AllocateFence(bool bCreateSignaled = false); - - /** - * @brief Checks if the given fence is signaled. + * @see KarmaGuiVulkanHandler::FillWindowData * - * First checks the CPU-side state of the fence. If the fence is already marked as signaled, - * returns true immediately. If not, queries the Vulkan API (via CheckFenceState) to check the - * actual status of the fence. - * - * @param InFence The fence to be checked - * @return true if the fence is signaled, false otherwise - * @see CheckFenceState * @since Karma 1.0.0 */ - bool IsFenceSignaled(FVulkanFence* InFence); + FVulkanFence* AllocateFence(bool bCreateSignaled = false); /** * @brief Waits for the given fence to be signaled. * - * @todo Ponder over the use of this funciton + * Makes sure the fence is in m_UsedFences before waiting. + * * @param Fence The fence to wait for * * @since Karma 1.0.0 @@ -96,42 +94,14 @@ namespace Karma * * @note This is kind of reverse of AllocateFence * @param Fence The fence to be released - * @since Karma 1.0.0 - */ - void ReleaseFence(FVulkanFence*& Fence); - - /** - * @brief Waits for the given fence to be signaled and then releases it. * - * Combines the functionality of WaitForFence and ReleaseFence. + * @see KarmaGuiVulkanHandler::DestroyFramesOnFlightData * - * @param Fence The fence to wait for and release * @since Karma 1.0.0 */ - void WaitAndReleaseFence(FVulkanFence*& Fence); + void ReleaseFence(FVulkanFence*& Fence); protected: - /** - * @brief Returns true if the fence is signaled, false otherwise. - * - * Sets the state of the fence accordingly i.e - * 1. If vkGetFenceStatus returns VK_SUCCESS, fence state is set to Signaled - * 2. If vkGetFenceStatus returns VK_NOT_READY, fence state is set to NotReady, - * meaning associated GPU operations are still pending - * - * Assumptions: - * 1. The fence is in NotReady state when this function is called. So this function - * is used for fences which are supposed to be signaled later after GPU operations complete. - * 2. The fence is owned by this manager and is present in m_UsedFences array - * - * @note Typically usage is in polling scenarios where CPU wants to check completion status - * opportunistically without blocking. For blocking waits, use WaitForFence(). - * - * @param Fence The fence to be checked - * @since Karma 1.0.0 - */ - bool CheckFenceState(FVulkanFence* Fence); - /** * @brief Destroys the given fence and releases its resources. * @@ -170,21 +140,42 @@ namespace Karma class FVulkanFence { public: + /** + * @brief Constructor for FVulkanFence. + * + * @param InDevice Reference to the owning FVulkanDevice + * @param InOwner Reference to the owning FVulkanFenceManager + * + * @param bCreateSignaled Whether to create the fence in a signaled state + * + * @since Karma 1.0.0 + */ FVulkanFence(FVulkanDevice& InDevice, FVulkanFenceManager& InOwner, bool bCreateSignaled); + /** + * @brief Checks if the fence is currently signaled. + * + * @return true if the fence is in the signaled state, false otherwise + * + * @since Karma 1.0.0 + */ inline bool IsSignaled() const { return m_State == EState::Signaled; } + /** + * @brief Retrieves the Vulkan fence handle. + * + * @return VkFence The Vulkan fence handle. + * + * @since Karma 1.0.0 + */ inline VkFence GetHandle() const { return m_Handle; } - // Only owner can create and manage fences - ~FVulkanFence(); - protected: VkFence m_Handle; @@ -210,6 +201,9 @@ namespace Karma FVulkanFenceManager& m_Owner; + // Only owner can create and manage fences + ~FVulkanFence(); + friend FVulkanFenceManager; }; } From 87dd4a8a1692d7886ed282dd7d156711dc68e659 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 27 Jan 2026 11:53:07 +0530 Subject: [PATCH 086/107] Class for semaphore --- .../Vulkan/VulkanRHI/VulkanSynchronization.cpp | 14 ++++++++++++++ .../Vulkan/VulkanRHI/VulkanSynchronization.h | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp index 8dcc6498..2f42875d 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.cpp @@ -109,4 +109,18 @@ namespace Karma m_FreeFences.Add(Fence);// add copy of the pointer to free list Fence = nullptr;// nullify the caller's pointer } + + FVulkanSemaphore::FVulkanSemaphore(FVulkanDevice& InDevice) : m_Device(InDevice) + { + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkResult result = vkCreateSemaphore(m_Device.GetLogicalDevice(), &semaphoreInfo, VK_NULL_HANDLE, &m_Handle); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create Semaphore"); + } + + FVulkanSemaphore::~FVulkanSemaphore() + { + vkDestroySemaphore(m_Device.GetLogicalDevice(), m_Handle, VK_NULL_HANDLE); + } } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h index 074fd277..f61f11a0 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanSynchronization.h @@ -206,4 +206,16 @@ namespace Karma friend FVulkanFenceManager; }; + + class FVulkanSemaphore + { + public: + FVulkanSemaphore(FVulkanDevice& InDevice); + + ~FVulkanSemaphore(); + + protected: + VkSemaphore m_Handle; + FVulkanDevice& m_Device; + }; } From be1939a908697e5a4952aa34ffc31bc56cd20a0a Mon Sep 17 00:00:00 2001 From: pcboy Date: Wed, 28 Jan 2026 12:58:11 +0530 Subject: [PATCH 087/107] resolved continuous scrolling in linux --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 11 ++++++++--- Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index e13af69f..6fd7c2a8 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -281,8 +281,11 @@ namespace Karma if (width > 0 && height > 0) { - KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateOrResizeWindow(&m_VulkanWindowData, true, true); - m_SwapChainRebuild = false; + //KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateOrResizeWindow(&m_VulkanWindowData, true, true); + KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(&m_VulkanWindowData); + KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, false); + + m_SwapChainRebuild = false; } } @@ -424,7 +427,9 @@ namespace Karma if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { m_SwapChainRebuild = true; - //return; we provide work to do anyway so the fence can be signaled properly. This was causing a stall on MacOS + vkDeviceWaitIdle(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice()); + + return; } else { diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index f03e1336..aa37497f 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1736,7 +1736,7 @@ namespace Karma vkFreeCommandBuffers(logicalDevice, windowData->CommandPool, static_cast(commandBuffers.size()), commandBuffers.data()); - ClearVulkanWindowData(windowData, true); + ClearVulkanWindowData(windowData, false); for(const auto& vulkanFramebuffer : windowData->RHIResources->VulkanFrameBuffers) { From 5d32ebdbe6a0e8fd3355bbf5a413de3a7762a805 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Fri, 30 Jan 2026 11:44:31 +0530 Subject: [PATCH 088/107] Vulkan descriptorset --- Karma/src/Karma/Core/KarmaTypes.h | 53 ++++++++++++ .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 1 + .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 84 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 3925a96e..918e1803 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -101,6 +101,29 @@ class KarmaMap { } + /** + * @brief Addes a key-value pair to the map + * + * @since Karma 1.0.0 + */ + void Add(const KeyType& Key, const ValueType& Value) + { + m_KeyValuePair.insert(typename AMap::value_type(Key, Value)); + } + + /** + * @brief Checks if the map contains a specified key + * + * @param Key The key to search for. + * @return true if the map contains the specified key, false otherwise. + * + * @since Karma 1.0.0 + */ + bool Contains(const KeyType& Key) const + { + return m_KeyValuePair.find(Key) != m_KeyValuePair.end(); + } + /** * @brief Find the value associated with a specified key, or if none exists, * adds a value using the default constructor. @@ -151,6 +174,36 @@ class KarmaMap return nullptr; } + + /** + * @brief Overloaded subscript operator for accessing values by key + * + * @param Key The key whose associated value is to be accessed. + * @return A reference to the value associated with the specified key. + * + * @note If the key does not exist in the map, a new entry is created with a default-constructed value. A mutable reference + * is returned. + * @since Karma 1.0.0 + */ + ValueType& operator[](const KeyType& Key) + { + return m_KeyValuePair[Key]; + } + + /** + * @brief Overloaded subscript operator for accessing values by key (const version) + * + * @param Key The key whose associated value is to be accessed. + * @return A const reference to the value associated with the specified key. + * + * @note Returns immutable reference + * @since Karma 1.0.0 + */ + const ValueType& operator[](const KeyType& Key) const + { + return m_KeyValuePair.at(Key); + } + protected: AMap m_KeyValuePair; }; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp new file mode 100644 index 00000000..6baaf945 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -0,0 +1 @@ +#include "VulkanDescriptorSets.h" \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h new file mode 100644 index 00000000..d08252ae --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -0,0 +1,84 @@ +/** + * @file VulkanDescriptorSets.h + * @brief Header file for Vulkan descriptor sets management in a Vulkan rendering context. + * @author Ravi Mohan (the_cowboy) + * @date January 30, 2026 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include "KarmaTypes.h" +#include + +class FVulkanDescriptorSetsLayoutInfo +{ +public: + /** + * @brief Constructor + * + * Adds all the Vulkan descriptor types currently supported to the m_LayoutTypes map with initial count of 0. + * + * @since Karma 1.0.0 + */ + FVulkanDescriptorSetsLayoutInfo() + { + for (uint32_t i = VK_DESCRIPTOR_TYPE_SAMPLER; i <= VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; ++i) + { + m_LayoutTypes.Add(static_cast(i), 0); + } + + m_LayoutTypes.Add(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0); + } + + /** + * @brief Returns the number of descriptors of a specific type used in the layout. + * + * @param Type The Vulkan descriptor type to query. + * @return The number of descriptors of the specified type used in the layout. + * + * @since Karma 1.0.0 + */ + inline uint32_t GetTypesUsed(VkDescriptorType Type) const + { + if (m_LayoutTypes.Contains(Type)) + { + return m_LayoutTypes[Type]; + } + else + { + return 0; + } + } + + struct FSetLayout + { + KarmaVector m_LayoutBindings; + uint32_t m_Hash; + + inline void GenerateHash() + { + // Simple hash generation based on bindings + m_Hash = 0; + for (const auto& binding : m_LayoutBindings) + { + m_Hash ^= std::hash()(binding.binding) ^ + std::hash()(binding.descriptorType) ^ + std::hash()(binding.descriptorCount) ^ + std::hash()(binding.stageFlags); + } + } + + friend uint32_t GetTypeHash(const FSetLayout& Layout) + { + return Layout.m_Hash; + } + + + }; + +private: + KarmaMap m_LayoutTypes; + +}; \ No newline at end of file From a54f82772f46f99b9ee9f3e2af2c719331074126 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 31 Jan 2026 05:32:35 +0530 Subject: [PATCH 089/107] More descriptorsets --- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 130 +++++++++++------- 1 file changed, 77 insertions(+), 53 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index d08252ae..c7135a95 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -12,73 +12,97 @@ #include "KarmaTypes.h" #include -class FVulkanDescriptorSetsLayoutInfo +namespace Karma { -public: - /** - * @brief Constructor - * - * Adds all the Vulkan descriptor types currently supported to the m_LayoutTypes map with initial count of 0. - * - * @since Karma 1.0.0 - */ - FVulkanDescriptorSetsLayoutInfo() + class FVulkanDevice; + + class FVulkanDescriptorSetsLayoutInfo { - for (uint32_t i = VK_DESCRIPTOR_TYPE_SAMPLER; i <= VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; ++i) + public: + /** + * @brief Constructor + * + * Adds all the Vulkan descriptor types currently supported to the m_LayoutTypes map with initial count of 0. + * + * @since Karma 1.0.0 + */ + FVulkanDescriptorSetsLayoutInfo() { - m_LayoutTypes.Add(static_cast(i), 0); + for (uint32_t i = VK_DESCRIPTOR_TYPE_SAMPLER; i <= VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; ++i) + { + m_LayoutTypes.Add(static_cast(i), 0); + } + + m_LayoutTypes.Add(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0); } - - m_LayoutTypes.Add(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0); - } - - /** - * @brief Returns the number of descriptors of a specific type used in the layout. - * - * @param Type The Vulkan descriptor type to query. - * @return The number of descriptors of the specified type used in the layout. - * - * @since Karma 1.0.0 - */ - inline uint32_t GetTypesUsed(VkDescriptorType Type) const - { - if (m_LayoutTypes.Contains(Type)) + + /** + * @brief Returns the number of descriptors of a specific type used in the layout. + * + * @param Type The Vulkan descriptor type to query. + * @return The number of descriptors of the specified type used in the layout. + * + * @since Karma 1.0.0 + */ + inline uint32_t GetTypesUsed(VkDescriptorType Type) const { - return m_LayoutTypes[Type]; + if (m_LayoutTypes.Contains(Type)) + { + return m_LayoutTypes[Type]; + } + else + { + return 0; + } } - else + + struct FSetLayout { - return 0; - } - } - - struct FSetLayout - { - KarmaVector m_LayoutBindings; - uint32_t m_Hash; - - inline void GenerateHash() - { - // Simple hash generation based on bindings - m_Hash = 0; - for (const auto& binding : m_LayoutBindings) + KarmaVector m_LayoutBindings; + uint32_t m_Hash; + + inline void GenerateHash() { - m_Hash ^= std::hash()(binding.binding) ^ + // Simple hash generation based on bindings + m_Hash = 0; + for (const auto& binding : m_LayoutBindings) + { + m_Hash ^= std::hash()(binding.binding) ^ std::hash()(binding.descriptorType) ^ std::hash()(binding.descriptorCount) ^ std::hash()(binding.stageFlags); + } + } + + friend uint32_t GetTypeHash(const FSetLayout& Layout) + { + return Layout.m_Hash; } + + }; + + inline const KarmaVector& GetLayouts() const + { + return m_SetLayouts; } - - friend uint32_t GetTypeHash(const FSetLayout& Layout) + + inline const KarmaMap& GetLayoutTypes() const { - return Layout.m_Hash; + return m_LayoutTypes; } - - + + + private: + KarmaMap m_LayoutTypes; + KarmaVector m_SetLayouts; + + VkPipelineBindPoint m_BindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; }; -private: - KarmaMap m_LayoutTypes; - -}; \ No newline at end of file + class FVulkanDescriptorSetsLayout : public FVulkanDescriptorSetsLayoutInfo + { + public: + FVulkanDescriptorSetsLayout(FVulkanDevice* InDevice); + ~FVulkanDescriptorSetsLayout(); + }; +} From d5f8b80c5ee19c6ecf459ef9baca111ade766866 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 5 Feb 2026 08:42:53 +0530 Subject: [PATCH 090/107] Basic descriptorsets functional with pool creation only --- Karma/src/Karma/Core/KarmaTypes.h | 46 +++++++ Karma/src/Platform/Vulkan/VulkanContext.cpp | 4 +- .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 97 +++++++++++++- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 119 ++++++++++++++++-- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 51 +++++++- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 6 +- 6 files changed, 309 insertions(+), 14 deletions(-) diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 918e1803..00e3d6ae 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -204,6 +204,31 @@ class KarmaMap return m_KeyValuePair.at(Key); } + uint32_t Num() const + { + return (uint32_t) m_KeyValuePair.size(); + } + + auto begin() + { + return m_KeyValuePair.begin(); + } + + auto end() + { + return m_KeyValuePair.end(); + } + + auto begin() const + { + return m_KeyValuePair.cbegin(); + } + + auto end() const + { + return m_KeyValuePair.cend(); + } + protected: AMap m_KeyValuePair; }; @@ -464,6 +489,11 @@ class KarmaVector m_Elements.clear(); } + void Resize(uint32_t NewSize) + { + m_Elements.resize(NewSize); + } + /** * @brief Just clear the elements * @@ -533,6 +563,16 @@ class KarmaVector return m_Elements.end(); } + typename std::vector::const_iterator begin() const + { + return m_Elements.cbegin(); + } + + typename std::vector::const_iterator end() const + { + return m_Elements.cend(); + } + /** * Returns the UObject corresponding to index. Be advised this is only for very low level use. * @@ -576,6 +616,12 @@ class KarmaVector */ FORCEINLINE BuildingBlock const* GetData() const { return m_Elements.data(); } + BuildingBlock& operator[](int32_t Index) + { + RangeCheck(Index); + return m_Elements[Index]; + } + private: /** * @brief Internal implementation of RemoveAtSwap diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 5bc916f1..32853649 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -503,7 +503,7 @@ namespace Karma VkDescriptorSetLayoutBinding texBinding{}; texBinding.binding = 0; texBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - texBinding.descriptorCount = 1; + texBinding.descriptorCount = 1; // <----------------------------------- Number of such descriptors, for per mesh should be number of meshes texBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; texBinding.pImmutableSamplers = nullptr; @@ -519,7 +519,7 @@ namespace Karma VkDescriptorSetLayoutBinding objectBinding{}; objectBinding.binding = 0; objectBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - objectBinding.descriptorCount = 1; + objectBinding.descriptorCount = 1;// <----------------------------------- number of uniform buffers , for per mesh should be number of meshes objectBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; objectBinding.pImmutableSamplers = nullptr; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index 6baaf945..b89fea02 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -1 +1,96 @@ -#include "VulkanDescriptorSets.h" \ No newline at end of file +#include "VulkanDescriptorSets.h" +#include "VulkanDevice.h" + +namespace Karma +{ + void FVulkanDescriptorSetsLayoutInfo::AddDescriptor(int32_t DescriptorSetIndex, const VkDescriptorSetLayoutBinding& Descriptor) + { + if (m_LayoutTypes.Contains(Descriptor.descriptorType)) + { + m_LayoutTypes[Descriptor.descriptorType]++; + } + else + { + m_LayoutTypes.Add(Descriptor.descriptorType, 1); + } + + if (DescriptorSetIndex > m_SetLayouts.Num()) + { + m_SetLayouts.Resize(DescriptorSetIndex + 1); + } + + FSetLayout& layout = m_SetLayouts[DescriptorSetIndex]; + layout.m_LayoutBindings.Add(Descriptor); + } + + void FVulkanDescriptorSetsLayoutInfo::AddDescriptorSet(FSetLayout SetLayout) + { + m_SetLayouts.Add(SetLayout); + } + + FVulkanDescriptorSetsLayout::FVulkanDescriptorSetsLayout(FVulkanDevice* InDevice) : m_Device(InDevice), + m_DescriptorSetsAllocateInfo({}) + { + } + + FVulkanDescriptorSetsLayout::~FVulkanDescriptorSetsLayout() + { + // Hanldles are owned by FVulkanPipleLineStateCacheManager + m_LayoutHandleIds.Clear(); + } + + FVulkanDescriptorPool::FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& Layout, uint32_t MaxSetsAllocations) + : m_Device(InDevice), m_MaxDescriptorSets(0), m_NumAllocatedDescriptorSets(0), m_PeakAllocatedDescriptorSets(0), m_Layout(Layout), + m_DescriptorPool(VK_NULL_HANDLE) + { + // Descriptor sets number required to allocate max number of descriptor sets layout + m_MaxDescriptorSets = MaxSetsAllocations * Layout.GetLayouts().Num(); + KarmaVector types; + + for (const auto& layoutType : Layout.GetLayoutTypes()) + { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VkDescriptorType(layoutType.first); + poolSize.descriptorCount = Layout.GetTypesUsed(poolSize.type) * MaxSetsAllocations; + types.Add(poolSize); + } + + VkDescriptorPoolCreateInfo PoolInfoP{}; + PoolInfoP.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + PoolInfoP.maxSets = m_MaxDescriptorSets; + PoolInfoP.poolSizeCount = static_cast(types.Num()); + PoolInfoP.pPoolSizes = types.GetData(); + + VkResult result = vkCreateDescriptorPool(m_Device->GetLogicalDevice(), &PoolInfoP, nullptr, &m_DescriptorPool); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create descriptor pool!"); + } + + FVulkanDescriptorPool::~FVulkanDescriptorPool() + { + if (m_DescriptorPool != VK_NULL_HANDLE) + { + vkDestroyDescriptorPool(m_Device->GetLogicalDevice(), m_DescriptorPool, nullptr); + m_DescriptorPool = VK_NULL_HANDLE; + + KR_CORE_INFO("Destroying descriptorpool"); + } + } + + VkDescriptorSet FVulkanDescriptorPool::AllocateDescriptorSet(const VkDescriptorSetAllocateInfo& InDescriptorSetAllocateInfo, VkDescriptorSet* OutSets) + { + VkDescriptorSetAllocateInfo AllocateInfo = InDescriptorSetAllocateInfo; + AllocateInfo.descriptorPool = m_DescriptorPool; + VkResult Result = vkAllocateDescriptorSets(m_Device->GetLogicalDevice(), &AllocateInfo, OutSets); + if (Result != VK_SUCCESS) + { + KR_CORE_ERROR("Failed to allocate descriptor sets from the pool!"); + return VK_NULL_HANDLE; + } + m_NumAllocatedDescriptorSets += AllocateInfo.descriptorSetCount; + if (m_NumAllocatedDescriptorSets > m_PeakAllocatedDescriptorSets) + { + m_PeakAllocatedDescriptorSets = m_NumAllocatedDescriptorSets; + } + return *OutSets; + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index c7135a95..a231eac3 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -19,21 +19,23 @@ namespace Karma class FVulkanDescriptorSetsLayoutInfo { public: - /** + /** * @brief Constructor * - * Adds all the Vulkan descriptor types currently supported to the m_LayoutTypes map with initial count of 0. + * Adds all the Vulkan descriptor types currently needed. * * @since Karma 1.0.0 */ FVulkanDescriptorSetsLayoutInfo() { - for (uint32_t i = VK_DESCRIPTOR_TYPE_SAMPLER; i <= VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; ++i) + } + + void SetLayoutTypes(const KarmaVector& NeededDescriptorTypes) + { + for (const VkDescriptorType& type : NeededDescriptorTypes) { - m_LayoutTypes.Add(static_cast(i), 0); + m_LayoutTypes.Add(type, 1); } - - m_LayoutTypes.Add(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0); } /** @@ -56,10 +58,17 @@ namespace Karma } } + /** + * @brief Structure representing a descriptor set layout. + */ struct FSetLayout { + /** + * @brief Layout bindings for this descriptor set, representing the individual + * descriptors and their configurations within the set. + */ KarmaVector m_LayoutBindings; - uint32_t m_Hash; + uint32_t m_Hash = 0; inline void GenerateHash() { @@ -91,11 +100,18 @@ namespace Karma return m_LayoutTypes; } - - private: + /*protected:*/ + void AddDescriptor(int32_t DescriptorSetIndex, const VkDescriptorSetLayoutBinding& Descriptor); + void AddDescriptorSet(FSetLayout SetLayout); + + protected: + /** + * @brief Map storing the count of each Vulkan descriptor type used in the layout. + */ KarmaMap m_LayoutTypes; KarmaVector m_SetLayouts; + uint32_t m_Hash = 0; VkPipelineBindPoint m_BindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; }; @@ -104,5 +120,90 @@ namespace Karma public: FVulkanDescriptorSetsLayout(FVulkanDevice* InDevice); ~FVulkanDescriptorSetsLayout(); + + inline const KarmaVector& GetHandles() const + { + return m_LayoutHandles; + } + + inline const KarmaVector& GetHandleIds() const + { + return m_LayoutHandleIds; + } + + inline const VkDescriptorSetAllocateInfo& GetAllocateInfo() const + { + return m_DescriptorSetsAllocateInfo; + } + + inline uint32_t GetHash() const + { + return m_Hash; + } + + private: + FVulkanDevice* m_Device; + KarmaVector m_LayoutHandles; + KarmaVector m_LayoutHandleIds; + VkDescriptorSetAllocateInfo m_DescriptorSetsAllocateInfo; + }; + + class FVulkanDescriptorPool + { + public: + FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& InLayout, uint32_t MaxSetsAllocations); + ~FVulkanDescriptorPool(); + + /** + * @brief Retrieves the Vulkan descriptor pool handle. + * + * @return VkDescriptorPool The Vulkan descriptor pool handle. + * @since Karma 1.0.0 + */ + inline VkDescriptorPool GetHandle() const + { + return m_DescriptorPool; + } + + /** + * @brief Checks if the descriptor pool can allocate more descriptor sets based on the provided layout. + * + * @param InLayout The layout of the descriptor sets to be allocated + * @since Karma 1.0.0 + */ + inline bool CanAllocate(const FVulkanDescriptorSetsLayout& InLayout) const + { + return m_MaxDescriptorSets > m_NumAllocatedDescriptorSets + InLayout.GetLayouts().Num(); + } + + /** + * @brief Allocates descriptor sets from the pool based on the provided allocation info. + * + * @param InDescriptorSetAllocateInfo Information about the descriptor set allocation + * @param OutSets Pointer to an array where allocated descriptor sets will be stored + * @return VkDescriptorSet The first allocated descriptor set handle + * @since Karma 1.0.0 + */ + VkDescriptorSet AllocateDescriptorSet(const VkDescriptorSetAllocateInfo& InDescriptorSetAllocateInfo, VkDescriptorSet* OutSets); + + private: + FVulkanDevice* m_Device; + + uint32_t m_MaxDescriptorSets; + uint32_t m_NumAllocatedDescriptorSets; + + uint32_t m_PeakAllocatedDescriptorSets; + const FVulkanDescriptorSetsLayout& m_Layout; + + VkDescriptorPool m_DescriptorPool; + + //friend class FVulkanCommandListContext; + }; + + class FVulkanDescriptorPoolsManager + { + private: + FVulkanDevice* m_Device = nullptr; + }; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index c87600c7..53e4e6cd 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -1,7 +1,7 @@ #include "VulkanDevice.h" #include "VulkanDynamicRHI.h" - #include "Vulkan/VulkanTexture.h" +#include "VulkanRHI/VulkanDescriptorSets.h" namespace Karma { @@ -21,6 +21,7 @@ namespace Karma { // vkdestroy default buffers etc delete m_DefaultTexture; + delete m_DescriptorPool; vkDestroyCommandPool(m_LogicalDevice, m_CommandPool, nullptr); vkDestroyDevice(m_LogicalDevice, nullptr); } @@ -102,10 +103,58 @@ namespace Karma KR_CORE_INFO("Created Vulkan commandpool"); + // Tentative descriptor pool + FVulkanDescriptorSetsLayout descriptorSetLayout(this); + KarmaVector neededTypes; + neededTypes.Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); // for camera UBO and per-mesh UBO + neededTypes.Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);// for mesh textures (samplers + image views) + + descriptorSetLayout.SetLayoutTypes(neededTypes); + + PopulateWithDescriptorSetsLayout(descriptorSetLayout); + m_DescriptorPool = new FVulkanDescriptorPool(this, descriptorSetLayout, 16); + // Default texture (unreal grid) m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); } + void FVulkanDevice::PopulateWithDescriptorSetsLayout(FVulkanDescriptorSetsLayout& InLayout) + { + // Set 0: Global descriptors (camera UBO, texture sampler) + FVulkanDescriptorSetsLayoutInfo::FSetLayout setLayout; + + // Camera UBO at binding 0 (vertex shader) + setLayout.m_LayoutBindings.Add({ + 0, // binding + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 1, // descriptorCount + VK_SHADER_STAGE_VERTEX_BIT, + nullptr + }); + // Texture sampler at binding 1 (fragment shader) with default sampler and image view + setLayout.m_LayoutBindings.Add({ + 1, // binding + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + 1, // descriptorCount + VK_SHADER_STAGE_FRAGMENT_BIT, + nullptr + }); + + InLayout.AddDescriptorSet(setLayout); + + // Set 1: Per-mesh descriptors (per-mesh UBO) + FVulkanDescriptorSetsLayoutInfo::FSetLayout meshSetLayout; + // Per mesh UBO at binding 0 (vertex shader) + meshSetLayout.m_LayoutBindings.Add({ + 0, // binding + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 100, // descriptorCount + VK_SHADER_STAGE_VERTEX_BIT, + }); + + InLayout.AddDescriptorSet(setLayout); + } + void FVulkanDevice::WaitUntilIdle() { VkResult result = vkDeviceWaitIdle(m_LogicalDevice); diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 4c58f991..963c50ba 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -17,7 +17,7 @@ namespace Karma { class FVulkanDynamicRHI; class VulkanTexture; - + class FVulkanDescriptorSetsLayout; class FVulkanSwapChain; /** @@ -160,6 +160,9 @@ namespace Karma * @since Karma 1.0.0 */ inline FVulkanFenceManager& GetFenceManager() { return m_FenceManager; } + + private: + void PopulateWithDescriptorSetsLayout(FVulkanDescriptorSetsLayout& OutLayout); private: VkDevice m_LogicalDevice; ///< The Vulkan logical device handle. @@ -172,6 +175,7 @@ namespace Karma // Additional members for managing queues, command pools, etc. can be added here. VkCommandPool m_CommandPool; + class FVulkanDescriptorPool* m_DescriptorPool; FVulkanFenceManager m_FenceManager; }; From 77403d5b0f148a7a99f4ff222790ca774f9ca151 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Tue, 10 Feb 2026 12:47:21 +0530 Subject: [PATCH 091/107] Descriptor set allocation with appropriate pool size computation --- Karma/src/Karma/Core/KarmaTypes.h | 6 + .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 107 +++++++++++++++--- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 20 +++- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 27 +++-- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 22 ++++ .../Vulkan/VulkanRHI/VulkanPipeline.cpp | 32 ++++++ .../Vulkan/VulkanRHI/VulkanPipeline.h | 46 ++++++++ 7 files changed, 231 insertions(+), 29 deletions(-) create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.cpp create mode 100644 Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.h diff --git a/Karma/src/Karma/Core/KarmaTypes.h b/Karma/src/Karma/Core/KarmaTypes.h index 00e3d6ae..9bd508fb 100644 --- a/Karma/src/Karma/Core/KarmaTypes.h +++ b/Karma/src/Karma/Core/KarmaTypes.h @@ -622,6 +622,12 @@ class KarmaVector return m_Elements[Index]; } + const BuildingBlock& operator[](int32_t Index) const + { + RangeCheck(Index); + return m_Elements[Index]; + } + private: /** * @brief Internal implementation of RemoveAtSwap diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index b89fea02..bf758960 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -26,6 +26,18 @@ namespace Karma void FVulkanDescriptorSetsLayoutInfo::AddDescriptorSet(FSetLayout SetLayout) { m_SetLayouts.Add(SetLayout); + + for (const auto& layoutBindings : SetLayout.m_LayoutBindings) + { + if (m_LayoutTypes.Contains(layoutBindings.descriptorType)) + { + m_LayoutTypes[layoutBindings.descriptorType]++; + } + else + { + m_LayoutTypes.Add(layoutBindings.descriptorType, 1); + } + } } FVulkanDescriptorSetsLayout::FVulkanDescriptorSetsLayout(FVulkanDevice* InDevice) : m_Device(InDevice), @@ -35,29 +47,75 @@ namespace Karma FVulkanDescriptorSetsLayout::~FVulkanDescriptorSetsLayout() { - // Hanldles are owned by FVulkanPipleLineStateCacheManager - m_LayoutHandleIds.Clear(); + for (const auto& handle : m_LayoutHandles) + { + vkDestroyDescriptorSetLayout(m_Device->GetLogicalDevice(), handle, nullptr); + } + + m_LayoutHandles.Clear(); } + void FVulkanDescriptorSetsLayout::Compile() + { + for (const auto& setLayout : m_SetLayouts) + { + VkDescriptorSetLayout handle = VK_NULL_HANDLE; + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(setLayout.m_LayoutBindings.Num()); + layoutInfo.pBindings = setLayout.m_LayoutBindings.GetData(); // <------ contains the information of how many descriptors of each type are used in the layout, and their shader stage flags + + VkResult result = vkCreateDescriptorSetLayout(m_Device->GetLogicalDevice(), &layoutInfo, nullptr, &handle); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create descriptor set layout!"); + + m_LayoutHandles.Add(handle); + } + } + FVulkanDescriptorPool::FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& Layout, uint32_t MaxSetsAllocations) : m_Device(InDevice), m_MaxDescriptorSets(0), m_NumAllocatedDescriptorSets(0), m_PeakAllocatedDescriptorSets(0), m_Layout(Layout), m_DescriptorPool(VK_NULL_HANDLE) { // Descriptor sets number required to allocate max number of descriptor sets layout - m_MaxDescriptorSets = MaxSetsAllocations * Layout.GetLayouts().Num(); + //m_MaxDescriptorSets = MaxSetsAllocations * Layout.GetLayouts().Num(); + + KarmaMap descriptorTypeCounts; + + for (const auto& layout : Layout.GetLayouts()) + { + m_MaxDescriptorSets += layout.m_NumberOfDescriptorSets; + + for (const auto& layoutBindings : layout.m_LayoutBindings) + { + if (descriptorTypeCounts.Contains(layoutBindings.descriptorType)) + { + descriptorTypeCounts[layoutBindings.descriptorType] += layoutBindings.descriptorCount * layout.m_NumberOfDescriptorSets; + } + else + { + descriptorTypeCounts.Add(layoutBindings.descriptorType, layoutBindings.descriptorCount * layout.m_NumberOfDescriptorSets); + } + } + } + KarmaVector types; for (const auto& layoutType : Layout.GetLayoutTypes()) { VkDescriptorPoolSize poolSize{}; poolSize.type = VkDescriptorType(layoutType.first); - poolSize.descriptorCount = Layout.GetTypesUsed(poolSize.type) * MaxSetsAllocations; + //poolSize.descriptorCount = Layout.GetTypesUsed(poolSize.type) * MaxSetsAllocations;// the number of descriptors of this type across all the descriptor sets + poolSize.descriptorCount = descriptorTypeCounts[layoutType.first];// the number of descriptors of this type across all the descriptor sets types.Add(poolSize); } + // Note: A descriptor set may have multiple types of descriptors. + // For example, a descriptor set may have both uniform buffers and combined image samplers. + VkDescriptorPoolCreateInfo PoolInfoP{}; PoolInfoP.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - PoolInfoP.maxSets = m_MaxDescriptorSets; + PoolInfoP.maxSets = m_MaxDescriptorSets;// Max number of descriptor sets that can be allocated from this pool (via vkAllocateDescriptorSets) PoolInfoP.poolSizeCount = static_cast(types.Num()); PoolInfoP.pPoolSizes = types.GetData(); @@ -76,21 +134,38 @@ namespace Karma } } - VkDescriptorSet FVulkanDescriptorPool::AllocateDescriptorSet(const VkDescriptorSetAllocateInfo& InDescriptorSetAllocateInfo, VkDescriptorSet* OutSets) + void FVulkanDescriptorPool::AllocateDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout, FVulkanDescriptorSets& InDSets) { - VkDescriptorSetAllocateInfo AllocateInfo = InDescriptorSetAllocateInfo; - AllocateInfo.descriptorPool = m_DescriptorPool; - VkResult Result = vkAllocateDescriptorSets(m_Device->GetLogicalDevice(), &AllocateInfo, OutSets); - if (Result != VK_SUCCESS) + uint32_t setIndex = 0; + for (const auto& layout : InLayout.GetLayouts()) { - KR_CORE_ERROR("Failed to allocate descriptor sets from the pool!"); - return VK_NULL_HANDLE; + KarmaVector& dSets = InDSets.m_DescriptorSets[setIndex]; + + for (auto& descriptorSet : dSets) + { + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_DescriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &InLayout.GetHandles()[setIndex]; + + VkResult result = vkAllocateDescriptorSets(m_Device->GetLogicalDevice(), &allocInfo, &descriptorSet); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to allocate descriptor set!"); + } + + setIndex++; } - m_NumAllocatedDescriptorSets += AllocateInfo.descriptorSetCount; - if (m_NumAllocatedDescriptorSets > m_PeakAllocatedDescriptorSets) + } + + FVulkanDescriptorSets::FVulkanDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout) + { + m_DescriptorSets.Resize(InLayout.GetLayouts().Num()); + + uint32_t setIndex = 0; + + for (const auto& layout : InLayout.GetLayouts()) { - m_PeakAllocatedDescriptorSets = m_NumAllocatedDescriptorSets; + m_DescriptorSets[setIndex++].Resize(layout.m_NumberOfDescriptorSets); } - return *OutSets; } } \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index a231eac3..4b5fc5b2 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -39,7 +39,7 @@ namespace Karma } /** - * @brief Returns the number of descriptors of a specific type used in the layout. + * @brief Returns the number of descriptors of a specific type used in the layout, across all descriptor sets * * @param Type The Vulkan descriptor type to query. * @return The number of descriptors of the specified type used in the layout. @@ -69,6 +69,13 @@ namespace Karma */ KarmaVector m_LayoutBindings; uint32_t m_Hash = 0; + + /** + * @brief The number of descriptor sets that use this layout. This is used for calculating the total number of descriptors + * needed for the pool allocation and appropriating the number of descriptor sets to allocate (vkAllocateDescriptorSets), from + * the pool, using this layout. + */ + uint32_t m_NumberOfDescriptorSets = 0; inline void GenerateHash() { @@ -121,6 +128,8 @@ namespace Karma FVulkanDescriptorSetsLayout(FVulkanDevice* InDevice); ~FVulkanDescriptorSetsLayout(); + void Compile(); + inline const KarmaVector& GetHandles() const { return m_LayoutHandles; @@ -148,6 +157,13 @@ namespace Karma VkDescriptorSetAllocateInfo m_DescriptorSetsAllocateInfo; }; + struct FVulkanDescriptorSets + { + FVulkanDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout); + + KarmaVector> m_DescriptorSets; + }; + class FVulkanDescriptorPool { public: @@ -184,7 +200,7 @@ namespace Karma * @return VkDescriptorSet The first allocated descriptor set handle * @since Karma 1.0.0 */ - VkDescriptorSet AllocateDescriptorSet(const VkDescriptorSetAllocateInfo& InDescriptorSetAllocateInfo, VkDescriptorSet* OutSets); + void AllocateDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout, FVulkanDescriptorSets& InDSets); private: FVulkanDevice* m_Device; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 53e4e6cd..30f9d34b 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -6,8 +6,8 @@ namespace Karma { FVulkanDevice::FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice InGpu) : m_VulkanDynamicRHI(InRHI), - m_GPU(InGpu), m_FenceManager(*this), m_CommandPool(VK_NULL_HANDLE), m_DefaultTexture(nullptr), m_LogicalDevice(VK_NULL_HANDLE), - m_GraphicsQueue(VK_NULL_HANDLE), m_PresentQueue(VK_NULL_HANDLE) + m_GPU(InGpu), m_FenceManager(*this), m_CommandPool(VK_NULL_HANDLE), m_DefaultTexture(nullptr), m_DefaultDescriptorSetLayout(nullptr), + m_LogicalDevice(VK_NULL_HANDLE), m_GraphicsQueue(VK_NULL_HANDLE), m_PresentQueue(VK_NULL_HANDLE) { // Implementation for creating a Vulkan logical device } @@ -21,6 +21,8 @@ namespace Karma { // vkdestroy default buffers etc delete m_DefaultTexture; + delete m_DefaultDescriptorSetLayout; + delete m_DefaultDescriptorSets; delete m_DescriptorPool; vkDestroyCommandPool(m_LogicalDevice, m_CommandPool, nullptr); vkDestroyDevice(m_LogicalDevice, nullptr); @@ -104,15 +106,15 @@ namespace Karma KR_CORE_INFO("Created Vulkan commandpool"); // Tentative descriptor pool - FVulkanDescriptorSetsLayout descriptorSetLayout(this); - KarmaVector neededTypes; - neededTypes.Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); // for camera UBO and per-mesh UBO - neededTypes.Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);// for mesh textures (samplers + image views) + m_DefaultDescriptorSetLayout = new FVulkanDescriptorSetsLayout(this); - descriptorSetLayout.SetLayoutTypes(neededTypes); + PopulateWithDescriptorSetsLayout(*m_DefaultDescriptorSetLayout); + m_DescriptorPool = new FVulkanDescriptorPool(this, *m_DefaultDescriptorSetLayout, 16); - PopulateWithDescriptorSetsLayout(descriptorSetLayout); - m_DescriptorPool = new FVulkanDescriptorPool(this, descriptorSetLayout, 16); + m_DefaultDescriptorSetLayout->Compile(); + m_DefaultDescriptorSets = new FVulkanDescriptorSets(*m_DefaultDescriptorSetLayout); + + m_DescriptorPool->AllocateDescriptorSets(*m_DefaultDescriptorSetLayout, *m_DefaultDescriptorSets); // Default texture (unreal grid) m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); @@ -140,6 +142,8 @@ namespace Karma nullptr }); + setLayout.m_NumberOfDescriptorSets = 1; + InLayout.AddDescriptorSet(setLayout); // Set 1: Per-mesh descriptors (per-mesh UBO) @@ -148,11 +152,12 @@ namespace Karma meshSetLayout.m_LayoutBindings.Add({ 0, // binding VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 100, // descriptorCount + 1, // descriptorCount VK_SHADER_STAGE_VERTEX_BIT, }); + meshSetLayout.m_NumberOfDescriptorSets = 16; - InLayout.AddDescriptorSet(setLayout); + InLayout.AddDescriptorSet(meshSetLayout); } void FVulkanDevice::WaitUntilIdle() diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 963c50ba..3c928b99 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -18,6 +18,7 @@ namespace Karma class FVulkanDynamicRHI; class VulkanTexture; class FVulkanDescriptorSetsLayout; + class FVulkanDescriptorSets; class FVulkanSwapChain; /** @@ -162,6 +163,23 @@ namespace Karma inline FVulkanFenceManager& GetFenceManager() { return m_FenceManager; } private: + /** + * @brief Populates the provided descriptor sets layout with the necessary bindings for the device. + * + * This function is responsible for defining the descriptor set layouts that will be used for resource binding in shaders. It + * adds the required bindings for global resources (like camera UBO and texture sampler) and per-mesh resources (like per-mesh UBO). + * + * From shader POV, the sets are like so + * set = 0, binding = 0: Camera UBO (vertex shader) + * set = 0, binding = 1: Texture sampler (fragment shader) + * + * set = 1, binding = 0: Per-mesh UBO (vertex and fragment shader) + * + * @param OutLayout The descriptor sets layout to be populated with the necessary bindings. + * + * @see FVulkanDescriptorSetsLayout + * @since Karma 1.0.0 + */ void PopulateWithDescriptorSetsLayout(FVulkanDescriptorSetsLayout& OutLayout); private: @@ -177,6 +195,10 @@ namespace Karma VkCommandPool m_CommandPool; class FVulkanDescriptorPool* m_DescriptorPool; + // Default descriptor set layout for the device, which can be used for common resources like camera UBO and default texture sampler. + FVulkanDescriptorSetsLayout* m_DefaultDescriptorSetLayout; + FVulkanDescriptorSets* m_DefaultDescriptorSets; + FVulkanFenceManager m_FenceManager; }; } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.cpp new file mode 100644 index 00000000..966ac0a9 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.cpp @@ -0,0 +1,32 @@ +#include "VulkanPipeline.h" +#include "VulkanDevice.h" + +namespace Karma +{ + FVulkanPipelineStateCacheManager::FVulkanPipelineStateCacheManager(FVulkanDevice* InDevice) : m_Device(InDevice) + { + } + + FVulkanPipelineStateCacheManager::~FVulkanPipelineStateCacheManager() + { + } + + bool FVulkanPipelineStateCacheManager::CreateGfxPipeline() + { + return false; + } + + FVulkanPipeline::FVulkanPipeline(FVulkanDevice* InDevice) : + m_Device(InDevice), m_Pipeline(VK_NULL_HANDLE) + { + } + + FVulkanPipeline::~FVulkanPipeline() + { + if (m_Pipeline != VK_NULL_HANDLE) + { + vkDestroyPipeline(m_Device->GetLogicalDevice(), m_Pipeline, nullptr); + m_Pipeline = VK_NULL_HANDLE; + } + } +} \ No newline at end of file diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.h new file mode 100644 index 00000000..0d1b8e76 --- /dev/null +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanPipeline.h @@ -0,0 +1,46 @@ +/** + * @file VulkanPipeline.h + * @author Ravi Mohan (the_cowboy) + * @brief This file contains FVulkanPipeline class and relevant data structures. + * @version 1.0 + * @date February 6, 2026 + * + * @copyright Karma Engine copyright(c) People of India + */ + +#pragma once + +#include + +namespace Karma +{ + class FVulkanDevice; + + class FVulkanPipelineStateCacheManager + { + public: + FVulkanPipelineStateCacheManager(FVulkanDevice* InDevice); + ~FVulkanPipelineStateCacheManager(); + + private: + bool CreateGfxPipeline(); + + private: + FVulkanDevice* m_Device; + + }; + + class FVulkanPipeline + { + public: + FVulkanPipeline(FVulkanDevice* InDevice); + + /*virtual*/ ~FVulkanPipeline(); + + protected: + FVulkanDevice* m_Device; + VkPipeline m_Pipeline; + + //FVulkanLayout* m_Layout; + }; +} \ No newline at end of file From 0bb0dc18171c2a3670bc9f5175d6f922ffc2dc42 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Tue, 10 Feb 2026 13:48:13 +0530 Subject: [PATCH 092/107] Minor tuning of the code --- .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 8 ++++---- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 19 +------------------ .../Vulkan/VulkanRHI/VulkanDevice.cpp | 12 ++++++------ 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index bf758960..e735ee34 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -73,9 +73,9 @@ namespace Karma } } - FVulkanDescriptorPool::FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& Layout, uint32_t MaxSetsAllocations) - : m_Device(InDevice), m_MaxDescriptorSets(0), m_NumAllocatedDescriptorSets(0), m_PeakAllocatedDescriptorSets(0), m_Layout(Layout), - m_DescriptorPool(VK_NULL_HANDLE) + FVulkanDescriptorPool::FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& Layout/*, uint32_t MaxSetsAllocations*/) + : m_Device(InDevice), m_Layout(Layout), + m_MaxDescriptorSets(0), m_DescriptorPool(VK_NULL_HANDLE) { // Descriptor sets number required to allocate max number of descriptor sets layout //m_MaxDescriptorSets = MaxSetsAllocations * Layout.GetLayouts().Num(); @@ -168,4 +168,4 @@ namespace Karma m_DescriptorSets[setIndex++].Resize(layout.m_NumberOfDescriptorSets); } } -} \ No newline at end of file +} diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index 4b5fc5b2..c7892f39 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -167,7 +167,7 @@ namespace Karma class FVulkanDescriptorPool { public: - FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& InLayout, uint32_t MaxSetsAllocations); + FVulkanDescriptorPool(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& InLayout/*, uint32_t MaxSetsAllocations*/); ~FVulkanDescriptorPool(); /** @@ -181,16 +181,6 @@ namespace Karma return m_DescriptorPool; } - /** - * @brief Checks if the descriptor pool can allocate more descriptor sets based on the provided layout. - * - * @param InLayout The layout of the descriptor sets to be allocated - * @since Karma 1.0.0 - */ - inline bool CanAllocate(const FVulkanDescriptorSetsLayout& InLayout) const - { - return m_MaxDescriptorSets > m_NumAllocatedDescriptorSets + InLayout.GetLayouts().Num(); - } /** * @brief Allocates descriptor sets from the pool based on the provided allocation info. @@ -204,16 +194,9 @@ namespace Karma private: FVulkanDevice* m_Device; - uint32_t m_MaxDescriptorSets; - uint32_t m_NumAllocatedDescriptorSets; - - uint32_t m_PeakAllocatedDescriptorSets; const FVulkanDescriptorSetsLayout& m_Layout; - VkDescriptorPool m_DescriptorPool; - - //friend class FVulkanCommandListContext; }; class FVulkanDescriptorPoolsManager diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 30f9d34b..020013a6 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -105,15 +105,15 @@ namespace Karma KR_CORE_INFO("Created Vulkan commandpool"); - // Tentative descriptor pool + // Default descriptorsets m_DefaultDescriptorSetLayout = new FVulkanDescriptorSetsLayout(this); - + PopulateWithDescriptorSetsLayout(*m_DefaultDescriptorSetLayout); - m_DescriptorPool = new FVulkanDescriptorPool(this, *m_DefaultDescriptorSetLayout, 16); - m_DefaultDescriptorSetLayout->Compile(); + + m_DescriptorPool = new FVulkanDescriptorPool(this, *m_DefaultDescriptorSetLayout); m_DefaultDescriptorSets = new FVulkanDescriptorSets(*m_DefaultDescriptorSetLayout); - + m_DescriptorPool->AllocateDescriptorSets(*m_DefaultDescriptorSetLayout, *m_DefaultDescriptorSets); // Default texture (unreal grid) @@ -319,4 +319,4 @@ namespace Karma { return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; } -} \ No newline at end of file +} From 702c46ec1be10a416022430f666a70295658fc1d Mon Sep 17 00:00:00 2001 From: pc_boy Date: Wed, 11 Feb 2026 14:04:49 +0530 Subject: [PATCH 093/107] Progress with descriptor sets --- GenerateProjects.bat | 2 +- Karma/src/Karma/Renderer/Buffer.h | 3 ++ Karma/src/Platform/Vulkan/VulkanBuffer.h | 2 + .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 43 ++++++++++++++++++- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 41 +++++++++++++++++- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 30 ++++++++++--- .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 9 +++- 7 files changed, 118 insertions(+), 12 deletions(-) diff --git a/GenerateProjects.bat b/GenerateProjects.bat index 2913843f..eaea3e58 100644 --- a/GenerateProjects.bat +++ b/GenerateProjects.bat @@ -8,7 +8,7 @@ set /p Input=Enter y(Yes) or n(No): If /I "%Input%"=="y" goto yes goto no :yes -Game_Of_Lands.slnx +start "" Game_Of_Lands.slnx exit :no echo Suit yourself! Goodbye! diff --git a/Karma/src/Karma/Renderer/Buffer.h b/Karma/src/Karma/Renderer/Buffer.h index c12d9903..6e3b5561 100644 --- a/Karma/src/Karma/Renderer/Buffer.h +++ b/Karma/src/Karma/Renderer/Buffer.h @@ -410,8 +410,11 @@ namespace Karma void UpdateUniforms(T&&... uniforms) { m_UniformList = { uniforms... }; + UpdateDriverSpecificUBOData(); } + virtual void UpdateDriverSpecificUBOData() = 0; + /** * @brief An overridable function to upload the uniform buffer * diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index 264a180c..8d21e9ea 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -321,6 +321,8 @@ namespace Karma */ void BufferCreation(); + virtual void UpdateDriverSpecificUBOData() override; + /** * @brief Uploads (copies) the data to GPU buffer memory (m_UniformBuffersMemory) from host-accessible (CPU) pointer, m_UniformList, to the beginning of the mapped range * diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index e735ee34..3170b10a 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -1,5 +1,7 @@ #include "VulkanDescriptorSets.h" #include "VulkanDevice.h" +#include "VulkanBuffer.h" +#include "VulkanTexture.h" namespace Karma { @@ -157,8 +159,10 @@ namespace Karma } } - FVulkanDescriptorSets::FVulkanDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout) + FVulkanDescriptorSets::FVulkanDescriptorSets(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& InLayout) { + m_Device = InDevice; + m_DescriptorSets.Resize(InLayout.GetLayouts().Num()); uint32_t setIndex = 0; @@ -168,4 +172,41 @@ namespace Karma m_DescriptorSets[setIndex++].Resize(layout.m_NumberOfDescriptorSets); } } + + void FVulkanDescriptorSets::UpdateUniformBufferDescriptorSet(std::shared_ptr Uniform, uint32_t SetLayoutIndex, + uint32_t DescriptorSetIndex, uint32_t FrameIndex) + { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = Uniform->GetUniformBuffers()[FrameIndex]; + bufferInfo.offset = 0; + bufferInfo.range = Uniform->GetBufferSize(); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = m_DescriptorSets[SetLayoutIndex][DescriptorSetIndex]; + descriptorWrite.dstBinding = 0; // Assuming the uniform buffer is bound to binding 0 in the shader + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + vkUpdateDescriptorSets(m_Device->GetLogicalDevice(), 1, &descriptorWrite, 0, nullptr); + } + + void FVulkanDescriptorSets::UpdateTextureDescriptorSet(std::shared_ptr Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex) + { + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = Texture->GetImageView(); + imageInfo.sampler = Texture->GetImageSampler(); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = m_DescriptorSets[SetLayoutIndex][DescriptorSetIndex]; + descriptorWrite.dstBinding = 0; // Assuming the texture is bound to binding 0 in the shader + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pImageInfo = &imageInfo; + vkUpdateDescriptorSets(m_Device->GetLogicalDevice(), 1, &descriptorWrite, 0, nullptr); + } } diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index c7892f39..588f0cde 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -159,8 +159,47 @@ namespace Karma struct FVulkanDescriptorSets { - FVulkanDescriptorSets(const FVulkanDescriptorSetsLayout& InLayout); + /** + * @brief Constructor for FVulkanDescriptorSets. + * + * Initializes the descriptor sets based on the provided Vulkan device and descriptor set layout. + * + * The descriptor sets are allocated based on the layout information, and are stored in a vector of vectors, where each inner + * vector corresponds to a specific descriptor set layout, and contains the allocated descriptor sets for that layout. + * + * @param InDevice The Vulkan device used for creating descriptor sets. + * @param InLayout The descriptor set layout defining the structure of the descriptor sets. + * + * @since Karma 1.0.0 + */ + FVulkanDescriptorSets(FVulkanDevice* InDevice, const FVulkanDescriptorSetsLayout& InLayout); + + /** + * @brief Assigns an Engine's uniform buffer to a specific descriptor set of a specific layout. + * + * @param Uniform The uniform buffer to be assigned to the descriptor set + * @param SetLayoutIndex The index of the descriptor set layout to which the descriptor set belongs + * @param DescriptorSetIndex The index of the descriptor set within the specified layout to which the uniform buffer will be assigned + * @param FrameIndex The index of the the frame for which the descriptor set will be updated with the uniform buffer. Uniform + * buffers are for each frame in flight, so the descriptor sets must be updated for each frame in flight, with the corresponding uniform buffer for that frame + * + * @since Karma 1.0.0 + */ + void UpdateUniformBufferDescriptorSet(std::shared_ptr Uniform, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex, uint32_t FrameIndex); + /** + * @brief Assigns an Engine's texture to a specific descriptor set of a specific layout. + * + * @param Texture The texture to be assigned to the descriptor set + * @param SetLayoutIndex The index of the descriptor set layout to which the descriptor set belongs + * + * @param DescriptorSetIndex The index of the descriptor set within the specified layout to which the texture will be assigned + * + * @since Karma 1.0.0 + */ + void UpdateTextureDescriptorSet(std::shared_ptr Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex); + + FVulkanDevice* m_Device; KarmaVector> m_DescriptorSets; }; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index 020013a6..b91772c7 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -20,10 +20,14 @@ namespace Karma void FVulkanDevice::Destroy() { // vkdestroy default buffers etc + for (uint32_t counter = 0; counter < m_MaxFramesInFlight; counter++) + { + delete m_DefaultDescriptorSets[counter]; + delete m_DescriptorPool[counter]; + } + delete m_DefaultTexture; delete m_DefaultDescriptorSetLayout; - delete m_DefaultDescriptorSets; - delete m_DescriptorPool; vkDestroyCommandPool(m_LogicalDevice, m_CommandPool, nullptr); vkDestroyDevice(m_LogicalDevice, nullptr); } @@ -110,16 +114,28 @@ namespace Karma PopulateWithDescriptorSetsLayout(*m_DefaultDescriptorSetLayout); m_DefaultDescriptorSetLayout->Compile(); - - m_DescriptorPool = new FVulkanDescriptorPool(this, *m_DefaultDescriptorSetLayout); - m_DefaultDescriptorSets = new FVulkanDescriptorSets(*m_DefaultDescriptorSetLayout); - - m_DescriptorPool->AllocateDescriptorSets(*m_DefaultDescriptorSetLayout, *m_DefaultDescriptorSets); + // Default texture (unreal grid) m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); } + void FVulkanDevice::InitializeDefaultDescriptorSets(uint32_t MaxFramesInFlight) + { + m_DescriptorPool.Resize(MaxFramesInFlight); + m_DefaultDescriptorSets.Resize(MaxFramesInFlight); + + for (uint32_t counter = 0; counter < MaxFramesInFlight; counter++) + { + m_DescriptorPool[counter] = new FVulkanDescriptorPool(this, *m_DefaultDescriptorSetLayout); + m_DefaultDescriptorSets[counter] = new FVulkanDescriptorSets(this, *m_DefaultDescriptorSetLayout); + + m_DescriptorPool[counter]->AllocateDescriptorSets(*m_DefaultDescriptorSetLayout, *m_DefaultDescriptorSets[counter]); + } + + m_MaxFramesInFlight = MaxFramesInFlight; + } + void FVulkanDevice::PopulateWithDescriptorSetsLayout(FVulkanDescriptorSetsLayout& InLayout) { // Set 0: Global descriptors (camera UBO, texture sampler) diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index 3c928b99..e94ef219 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -139,6 +139,8 @@ namespace Karma */ bool HasStencilComponent(VkFormat format); + void InitializeDefaultDescriptorSets(uint32_t MaxFramesInFlight); + ///////////////// Getters ///////////////// /** @@ -162,6 +164,8 @@ namespace Karma */ inline FVulkanFenceManager& GetFenceManager() { return m_FenceManager; } + inline KarmaVector& GetDefaultDescriptorSets() { return m_DefaultDescriptorSets; } + private: /** * @brief Populates the provided descriptor sets layout with the necessary bindings for the device. @@ -193,11 +197,12 @@ namespace Karma // Additional members for managing queues, command pools, etc. can be added here. VkCommandPool m_CommandPool; - class FVulkanDescriptorPool* m_DescriptorPool; // Default descriptor set layout for the device, which can be used for common resources like camera UBO and default texture sampler. FVulkanDescriptorSetsLayout* m_DefaultDescriptorSetLayout; - FVulkanDescriptorSets* m_DefaultDescriptorSets; + KarmaVector m_DescriptorPool; + KarmaVector m_DefaultDescriptorSets; + uint32_t m_MaxFramesInFlight = 0; FVulkanFenceManager m_FenceManager; }; From 28af070d0fd6e14746701ac9fc9a7d69c5aa4b31 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Thu, 12 Feb 2026 05:27:13 +0530 Subject: [PATCH 094/107] Allocating camera ubo to ubo descriptor set --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 5 +++-- Karma/src/Karma/Renderer/Buffer.h | 3 +-- Karma/src/Karma/Renderer/Camera/Camera.cpp | 2 +- Karma/src/Platform/OpenGL/OpenGLBuffer.h | 2 ++ Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 11 +++++++++++ Karma/src/Platform/Vulkan/VulkanBuffer.h | 2 +- .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 4 ++-- .../Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h | 2 +- 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 6fd7c2a8..cf4fe0ac 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -58,6 +58,7 @@ namespace Karma // Font, descriptor, and pipeline KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(); + FVulkanDynamicRHI::Get().GetDevice()->InitializeDefaultDescriptorSets(m_VulkanWindowData.RHIResources->VulkanSwapChain->GetMaxFramesInFlight()); // Load default font KGFontConfig fontConfig; @@ -427,9 +428,9 @@ namespace Karma if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { m_SwapChainRebuild = true; - vkDeviceWaitIdle(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice()); + vkDeviceWaitIdle(FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice()); - return; + return; } else { diff --git a/Karma/src/Karma/Renderer/Buffer.h b/Karma/src/Karma/Renderer/Buffer.h index 6e3b5561..d60cbeaf 100644 --- a/Karma/src/Karma/Renderer/Buffer.h +++ b/Karma/src/Karma/Renderer/Buffer.h @@ -410,10 +410,9 @@ namespace Karma void UpdateUniforms(T&&... uniforms) { m_UniformList = { uniforms... }; - UpdateDriverSpecificUBOData(); } - virtual void UpdateDriverSpecificUBOData() = 0; + virtual void UpdateCameraUniform() = 0; /** * @brief An overridable function to upload the uniform buffer diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index 6974f1c0..78504962 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -2,7 +2,6 @@ #include "glm/gtc/matrix_transform.hpp" #include "Karma/Input.h" #include "Buffer.h" -#include "Platform/Vulkan/VulkanHolder.h" namespace Karma { @@ -14,6 +13,7 @@ namespace Karma UBODataPointer uView(&m_ViewMatrix); m_ViewProjectionUBO->UpdateUniforms(uProjection, uView); + m_ViewProjectionUBO->UpdateCameraUniform(); } Camera::~Camera() diff --git a/Karma/src/Platform/OpenGL/OpenGLBuffer.h b/Karma/src/Platform/OpenGL/OpenGLBuffer.h index e220fa43..efd5372b 100644 --- a/Karma/src/Platform/OpenGL/OpenGLBuffer.h +++ b/Karma/src/Platform/OpenGL/OpenGLBuffer.h @@ -206,6 +206,8 @@ namespace Karma * @since Karma 1.0.0 */ virtual void UploadUniformBuffer(size_t frameIndex) override; + + virtual void UpdateCameraUniform() override {} private: /** diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 3bda0890..1e8faf2e 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -5,6 +5,7 @@ #include "VulkanRHI/VulkanDevice.h" #include "VulkanRHI/VulkanSwapChain.h" #include "KarmaGui/KarmaGuiRenderer.h" +#include "VulkanRHI/VulkanDescriptorSets.h" namespace Karma { @@ -233,6 +234,16 @@ namespace Karma ClearBuffer(); } + void VulkanUniformBuffer::UpdateCameraUniform() + { + uint32_t maxFramesInFlight = KarmaGuiRenderer::GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); + + for(uint32_t counter = 0; counter < maxFramesInFlight; counter++) + { + FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(this, 0, 0, counter); + } + } + void VulkanUniformBuffer::BufferCreation() { VkDeviceSize bufferSize = GetBufferSize(); diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.h b/Karma/src/Platform/Vulkan/VulkanBuffer.h index 8d21e9ea..ae6a8cec 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.h +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.h @@ -321,7 +321,7 @@ namespace Karma */ void BufferCreation(); - virtual void UpdateDriverSpecificUBOData() override; + virtual void UpdateCameraUniform() override; /** * @brief Uploads (copies) the data to GPU buffer memory (m_UniformBuffersMemory) from host-accessible (CPU) pointer, m_UniformList, to the beginning of the mapped range diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index 3170b10a..32cc8c7a 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -132,7 +132,7 @@ namespace Karma vkDestroyDescriptorPool(m_Device->GetLogicalDevice(), m_DescriptorPool, nullptr); m_DescriptorPool = VK_NULL_HANDLE; - KR_CORE_INFO("Destroying descriptorpool"); + //KR_CORE_INFO("Destroying descriptorpool"); } } @@ -173,7 +173,7 @@ namespace Karma } } - void FVulkanDescriptorSets::UpdateUniformBufferDescriptorSet(std::shared_ptr Uniform, uint32_t SetLayoutIndex, + void FVulkanDescriptorSets::UpdateUniformBufferDescriptorSet(VulkanUniformBuffer* Uniform, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex, uint32_t FrameIndex) { VkDescriptorBufferInfo bufferInfo{}; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index 588f0cde..125dfaff 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -185,7 +185,7 @@ namespace Karma * * @since Karma 1.0.0 */ - void UpdateUniformBufferDescriptorSet(std::shared_ptr Uniform, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex, uint32_t FrameIndex); + void UpdateUniformBufferDescriptorSet(class VulkanUniformBuffer* Uniform, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex, uint32_t FrameIndex); /** * @brief Assigns an Engine's texture to a specific descriptor set of a specific layout. From 6c55065782b10bb4a04e3470535bcc6a8d46c6d7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 12 Feb 2026 14:58:31 +0530 Subject: [PATCH 095/107] Proceeding with renderpass creation and appropriate resources for offscreen rendering --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 7 +- Karma/src/Karma/Renderer/Camera/Camera.cpp | 2 +- .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 187 +++++++----------- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 2 +- Pranjal/Source/Private/EditorLayer.cpp | 2 +- Pranjal/Source/Private/KarmaGuiMesa.cpp | 13 +- 6 files changed, 91 insertions(+), 122 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index cf4fe0ac..34e55eb7 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -7,6 +7,8 @@ #include "VulkanRHI/VulkanDynamicRHI.h" #include "VulkanRHI/VulkanSwapChain.h" #include "VulkanRHI/VulkanSynchronization.h" +#include "KarmaRHI/DynamicRHI.h" +#include "VulkanRHI/VulkanRenderPass.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -457,6 +459,7 @@ namespace Karma for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { + continue;// For now, skipping the 3D scene rendering on 2D render target, to focus on getting the main render pass right. Will come back to this later. std::shared_ptr scene3D = it->Scene3D; FrameDescriptorSets dSets; @@ -472,7 +475,7 @@ namespace Karma { VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = backendData->OffScreenRR.RenderPass; + renderPassInfo.renderPass = backendData->OffScreenRR.RenderPass->GetHandle(); renderPassInfo.framebuffer = it->FrameBuffer; renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent.width = it->Size.x; @@ -632,7 +635,7 @@ namespace Karma KGTextureID KarmaGuiRenderer::Add3DSceneFor2DRendering(std::shared_ptr scene, KGVec2 dimensions) { - if(RendererAPI::GetAPI() == RendererAPI::API::Vulkan) + if(GRHIInterfaceType == ERHIInterfaceType::Vulkan) { KarmaGui_ImplVulkan_Data* backendData = GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; diff --git a/Karma/src/Karma/Renderer/Camera/Camera.cpp b/Karma/src/Karma/Renderer/Camera/Camera.cpp index 78504962..95b7195d 100644 --- a/Karma/src/Karma/Renderer/Camera/Camera.cpp +++ b/Karma/src/Karma/Renderer/Camera/Camera.cpp @@ -13,7 +13,7 @@ namespace Karma UBODataPointer uView(&m_ViewMatrix); m_ViewProjectionUBO->UpdateUniforms(uProjection, uView); - m_ViewProjectionUBO->UpdateCameraUniform(); + m_ViewProjectionUBO->UpdateCameraUniform();// maybe move to Scene::AddCamera } Camera::~Camera() diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index aa37497f..84d640d6 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -1306,16 +1306,14 @@ namespace Karma for(auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { + CreateOffScreenTextureDepthResource(&(*it)); - //CreateOffScreenTextureRenderpassResource(&(*it)); - + CreateOffScreenTextureFrameBufferResource(&(*it)); - // std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); - // vulkanVA->CreateKarmaGuiGraphicsPipeline(backendData->OffScreenRR.RenderPass, it->Size.x, it->Size.y); - + return;// for experimental // assuming that all 3D scenes use same pipeline layout. This may change later - VulkanHolder::GetVulkanContext()->CreateKarmaGuiGeneralGraphicsPipeline(backendData->OffScreenRR.RenderPass, it->Size.x, it->Size.y); + VulkanHolder::GetVulkanContext()->CreateKarmaGuiGeneralGraphicsPipeline(backendData->OffScreenRR.RenderPass->GetHandle(), it->Size.x, it->Size.y); } } @@ -1323,86 +1321,51 @@ namespace Karma { KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; - + + KR_CORE_INFO("Attempting to Vulkan renderpass for offscreen texture rendering of 3D scene"); + FVulkanRenderPassInfo RPInfo; + // Color Attachment - VkAttachmentDescription colorAttachment{}; - colorAttachment.format = VK_FORMAT_R8G8B8A8_UNORM; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clear the image at start - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // Store the result for sampling later - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Layout when render pass starts - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;//VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // Layout when render pass ends (we transition *after* the pass) - + FVulkanRenderPassInfo::FAttachmentInfo colorAttachmentInfo; + colorAttachmentInfo.AttachmentFlags = 0; + colorAttachmentInfo.AttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; + colorAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; + colorAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clear the image at start + colorAttachmentInfo.AttachmentStoreOperation = VK_ATTACHMENT_STORE_OP_STORE; // Store the result for sampling later + colorAttachmentInfo.AttachmentStencilLoadOperation = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachmentInfo.AttachmentStencilStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachmentInfo.AttachmentInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Layout when render pass starts + colorAttachmentInfo.AttachmentFinalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;// Layout when render pass ends (we transition *after* the pass) + RPInfo.m_AttachmentsInfo.Add(colorAttachmentInfo); + // Depth Attachment - VkAttachmentDescription depthAttachment{}; - depthAttachment.format = VulkanHolder::GetVulkanContext()->FindDepthFormat(); - depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // --- Subpass and References --- - - // Reference for the color attachment in the subpass - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; // Index 0 in the pAttachments array - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - // Reference for the depth attachment in the subpass - VkAttachmentReference depthAttachmentRef{}; - depthAttachmentRef.attachment = 1; // Index 1 in the pAttachments array - depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // The subpass itself - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - subpass.pDepthStencilAttachment = &depthAttachmentRef; - - // Subpass dependencies for layout transitions ########## seems IMPORTANT for layout transitions ################ - // https://github.com/1111mp/Vulkan/blob/7e65729c9782d00ee87e70fb25b711ccc0b71b64/src/Application.cpp#L1627 - std::array dependencies; - - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].dstSubpass = 0; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + FVulkanRenderPassInfo::FAttachmentInfo depthAttachmentInfo; + depthAttachmentInfo.AttachmentFlags = 0; + depthAttachmentInfo.AttachmentFormat = FVulkanDynamicRHI::Get().FindDepthFormat(); + depthAttachmentInfo.AttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; + depthAttachmentInfo.AttachmentLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clear the image at start + depthAttachmentInfo.AttachmentStoreOperation = VK_ATTACHMENT_STORE_OP_STORE; // Store the result for sampling later + depthAttachmentInfo.AttachmentStencilLoadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachmentInfo.AttachmentStencilStoreOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachmentInfo.AttachmentInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Layout when render pass starts + depthAttachmentInfo.AttachmentFinalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;// Layout when render pass ends (we transition *after* the pass) + RPInfo.m_AttachmentsInfo.Add(depthAttachmentInfo); - - // --- Render Pass Creation --- - - std::vector attachments = {colorAttachment, depthAttachment}; - VkRenderPassCreateInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = static_cast(attachments.size()); - renderPassInfo.pAttachments = attachments.data(); - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = static_cast(dependencies.size()); - renderPassInfo.pDependencies = dependencies.data(); - - // Subpass dependencies for synchronization could be added here, - // but the manual pipeline barrier after the pass is generally cleaner for this use case. + RPInfo.bHasDepthAttachment = true; + + FVulkanRenderPassInfo::FAttachmentRefInfo colorAttachmentReference; + colorAttachmentReference.attachment = 0; + colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + RPInfo.m_ColorAttachmentsRefInfo.Add(colorAttachmentReference); + + RPInfo.m_DepthAttachmentReference.attachment = 1; + RPInfo.m_DepthAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + FVulkanRenderTargetLayout RTLayout(RPInfo); + backendData->OffScreenRR.RenderPass = new FVulkanRenderPass(*FVulkanDynamicRHI::Get().GetDevice(), RTLayout); - VkResult result = vkCreateRenderPass(vulkanInfo->Device, &renderPassInfo, nullptr, &backendData->OffScreenRR.RenderPass); - KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't off screen render pass"); + // --- had couple of dependencies for layouts change --- } void KarmaGuiVulkanHandler::CreateOffScreenTextureFrameBufferResource(KarmaGui_3DScene_To_2DTexture_Data* textureData) @@ -1417,7 +1380,7 @@ namespace Karma VkFramebufferCreateInfo framebufferInfo{}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = backendData->OffScreenRR.RenderPass; + framebufferInfo.renderPass = backendData->OffScreenRR.RenderPass->GetHandle(); framebufferInfo.attachmentCount = static_cast(attachments.size()); framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = textureData->Size.x; @@ -1442,7 +1405,7 @@ namespace Karma VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; - info.format = VulkanHolder::GetVulkanContext()->FindDepthFormat(); + info.format = FVulkanDynamicRHI::Get().FindDepthFormat(); info.extent.width = textureData->Size.x; info.extent.height = textureData->Size.y; info.extent.depth = 1; @@ -1462,7 +1425,7 @@ namespace Karma VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = req.size; - allocInfo.memoryTypeIndex = VulkanHolder::GetVulkanContext()->FindMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);//KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + allocInfo.memoryTypeIndex = FVulkanDynamicRHI::Get().FindMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); result = vkAllocateMemory(vulkanInfo->Device, &allocInfo, vulkanInfo->Allocator, &textureData->DepthDeviceMemory); KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't allocate memory"); @@ -1477,7 +1440,7 @@ namespace Karma info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = textureData->DepthImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = VulkanHolder::GetVulkanContext()->FindDepthFormat(); + info.format = FVulkanDynamicRHI::Get().FindDepthFormat(); info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; @@ -1859,6 +1822,29 @@ namespace Karma void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_DestroyWindow(KarmaGui_ImplVulkanH_Window* windowData) { + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; + + if (backendData->OffScreenRR.bAllocationDoneOnce) + { + delete backendData->OffScreenRR.RenderPass; + backendData->OffScreenRR.RenderPass = nullptr; + + vkDestroySampler(vulkanInfo->Device, backendData->OffScreenRR.Sampler, nullptr); + } + + // Clear vulkan resources from KarmaGui_3DScene_To_2DTexture_Data + for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) + { + vkDestroyImageView(vulkanInfo->Device, it->DepthImage_View, nullptr); + vkDestroyImage(vulkanInfo->Device, it->DepthImage, nullptr); + vkFreeMemory(vulkanInfo->Device, it->DepthDeviceMemory, nullptr); + vkDestroyImageView(vulkanInfo->Device, it->Image_View, nullptr); + vkDestroyImage(vulkanInfo->Device, it->Image, nullptr); + vkFreeMemory(vulkanInfo->Device, it->DeviceMemory, nullptr); + vkDestroyFramebuffer(vulkanInfo->Device, it->FrameBuffer, nullptr); + } + VkDevice logicalDevice = FVulkanDynamicRHI::Get().GetDevice()->GetLogicalDevice(); std::vector commandBuffers; @@ -1922,34 +1908,7 @@ namespace Karma delete windowData->RHIResources; return; // <-- to be uncommented when we have offscreen rendering logic - - KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); - KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; - - vkDestroySampler(vulkanInfo->Device, backendData->OffScreenRR.Sampler, nullptr); - vkDestroyRenderPass(vulkanInfo->Device, backendData->OffScreenRR.RenderPass, nullptr); - - // Clear vulkan resources from KarmaGui_3DScene_To_2DTexture_Data - for(auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) - { - // Order of destruction to be taken in account - - //vkDestroySampler(vulkanInfo->Device, it->Sampler, nullptr); - vkDestroyImageView(vulkanInfo->Device, it->DepthImage_View, nullptr); - - vkDestroyImage(vulkanInfo->Device, it->DepthImage, nullptr); - vkFreeMemory(vulkanInfo->Device, it->DepthDeviceMemory, nullptr); - - vkDestroyImageView(vulkanInfo->Device, it->Image_View, nullptr); - vkDestroyImage(vulkanInfo->Device, it->Image, nullptr); - vkFreeMemory(vulkanInfo->Device, it->DeviceMemory, nullptr); - vkDestroyFramebuffer(vulkanInfo->Device, it->FrameBuffer, nullptr); - - //std::shared_ptr vulkanVA = static_pointer_cast(it->Scene3D->GetRenderableVertexArray()); - //vulkanVA->CleanupKarmaGuiGraphicsPipeline(); - - //vkDestroyRenderPass(vulkanInfo->Device, it->RenderPass, nullptr); - } + // Assuming only on graphicspipeline VulkanHolder::GetVulkanContext()->CleanUpKarmaGuiGeneralGraphicsPipeline(); } diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index 4ca502ac..e7e76faf 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -851,7 +851,7 @@ namespace Karma * * @see KarmaGuiRenderer::Add3DSceneFor2DRendering */ - VkRenderPass RenderPass; + FVulkanRenderPass* RenderPass; /** * @brief Flag indicating whether the allocation of offscreen rendering resources has been done at least once. diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 8d4bb9b1..ad7876e5 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -376,7 +376,7 @@ namespace Karma staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); m_EditorScene->AddStaticMeshActor(staticMeshActor); - Karma::VulkanHolder::GetVulkanContext()->CreateVulkanResourcesForScene(m_EditorScene); + //Karma::VulkanHolder::GetVulkanContext()->CreateVulkanResourcesForScene(m_EditorScene); } } } diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index d82473bc..654b761f 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -524,14 +524,21 @@ namespace Karma KarmaGuiBackendRendererUserData* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); backgroundImageTextureID = backendData->GetTextureIDAtIndex(1); - - //KGTextureID textureID3D = KarmaGuiRenderer::Add3DSceneFor2DRendering(scene, KGVec2(window->Size.x, window->Size.y)); + + KGTextureID textureID3D; + if (scene->GetSMActors().size() > 0) + { + textureID3D = KarmaGuiRenderer::Add3DSceneFor2DRendering(scene, KGVec2(window->Size.x, window->Size.y)); + } KGDrawList* drawList = KarmaGui::GetWindowDrawList(); KGVec2 pos = KarmaGui::GetCursorScreenPos(); drawList->AddImage((void*)backgroundImageTextureID, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); - //drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); + if (scene->GetSMActors().size() > 0 && textureID3D != nullptr) + { + drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); + } scene->SetRenderWindow(window); From 5c2f98c171dde2a32c70e955ca98e2dd7ff4bef7 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 12 Feb 2026 19:52:57 +0530 Subject: [PATCH 096/107] Single mesh loading working --- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 60 ++-- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h | 4 + Karma/src/Karma/Renderer/Scene.cpp | 3 + .../Platform/Vulkan/KarmaGuiVulkanHandler.cpp | 265 +++++++++++++++++- .../Platform/Vulkan/KarmaGuiVulkanHandler.h | 9 +- .../Vulkan/VulkanRHI/VulkanDescriptorSets.cpp | 4 +- .../Vulkan/VulkanRHI/VulkanDescriptorSets.h | 2 +- .../Vulkan/VulkanRHI/VulkanDevice.cpp | 4 + .../Platform/Vulkan/VulkanRHI/VulkanDevice.h | 8 + Resources/Shaders/shader.frag | 2 +- Resources/Shaders/shader.vert | 4 +- 11 files changed, 316 insertions(+), 49 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 34e55eb7..9c281f84 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -9,6 +9,8 @@ #include "VulkanRHI/VulkanSynchronization.h" #include "KarmaRHI/DynamicRHI.h" #include "VulkanRHI/VulkanRenderPass.h" +#include "VulkanRHI/VulkanDescriptorSets.h" +#include "StaticMeshActor.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -27,7 +29,7 @@ namespace Karma m_GLFWwindow = window; - if (RendererAPI::GetAPI() == RendererAPI::API::Vulkan) + if (GRHIInterfaceType == ERHIInterfaceType::Vulkan) { KarmaGui_ImplGlfw_InitForVulkan(window, true); @@ -51,8 +53,6 @@ namespace Karma KarmaGui_ImplVulkan_Init(&initInfo); // Fresh start with newly instantiated Vulkan data - // Since VulkanContext has already instantiated fresh swapchain and commandbuffers, we send that false - // KarmaGuiVulkanHandler::ShareVulkanContextResourcesOfMainWindow(&m_VulkanWindowData, true); KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, true); // See if all the appropriate Vulkan resources have been instantiated @@ -124,11 +124,21 @@ namespace Karma } } + void KarmaGuiRenderer::OnAdditionOfStaticMesh(AStaticMeshActor* smActor) + { + uint32_t maxFramesInFlight = GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); + + for (uint32_t counter = 0; counter < maxFramesInFlight; counter++) + { + FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(smActor->GetMeshTransformUniform().get()), 1, 0, counter); + } + } + void KarmaGuiRenderer::AddImageTexture(char const* fileName, const std::string& label) { - switch(RendererAPI::GetAPI()) + switch(GRHIInterfaceType) { - case RendererAPI::API::Vulkan: + case ERHIInterfaceType::Vulkan: { KarmaGuiIO& io = KarmaGui::GetIO(); KarmaGuiBackendRendererUserData* backendData = (KarmaGuiBackendRendererUserData*) io.BackendRendererUserData; @@ -162,12 +172,12 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to wait!"); } break; - case RendererAPI::API::OpenGL: + case ERHIInterfaceType::OpenGL: { KarmaGuiOpenGLHandler::KarmaGui_ImplOpenGL3_CreateTexture(fileName, label); } break; - case RendererAPI::API::None: + case ERHIInterfaceType::Null: KR_CORE_ASSERT(false, "RendererAPI::None is not supported"); break; default: @@ -178,16 +188,16 @@ namespace Karma void KarmaGuiRenderer::OnKarmaGuiLayerBegin() { - switch (RendererAPI::GetAPI()) + switch (GRHIInterfaceType) { - case RendererAPI::API::Vulkan: + case ERHIInterfaceType::Vulkan: GiveLoopBeginControlToVulkan(); break; - case RendererAPI::API::OpenGL: + case ERHIInterfaceType::OpenGL: KarmaGuiOpenGLHandler::KarmaGui_ImplOpenGL3_NewFrame(); KarmaGui_ImplGlfw_NewFrame(); break; - case RendererAPI::API::None: + case ERHIInterfaceType::Null: KR_CORE_ASSERT(false, "RendererAPI::None is not supported"); break; default: @@ -459,18 +469,9 @@ namespace Karma for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { - continue;// For now, skipping the 3D scene rendering on 2D render target, to focus on getting the main render pass right. Will come back to this later. std::shared_ptr scene3D = it->Scene3D; - FrameDescriptorSets dSets; - if (VulkanHolder::GetVulkanContext()->GetGeneralDescriptorSets().size() > 0) - { - dSets = VulkanHolder::GetVulkanContext()->GetGeneralDescriptorSets()[windowData->SemaphoreIndex]; - } - else - { - continue; - } + FVulkanDescriptorSets* descriptorSets = FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[windowData->SemaphoreIndex]; { VkRenderPassBeginInfo renderPassInfo{}; @@ -493,16 +494,18 @@ namespace Karma vkCmdBeginRenderPass(frameOnFlightData->CommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); // ---- Bind Graphics Pipeline ---- - vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralGraphicsPipeline()); + vkCmdBindPipeline(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, backendData->OffScreenRR.OffscreenGraphicsPipeline); // === BIND GLOBAL DESCRIPTOR SETS (once per frame) === - // Set 0: Camera UBO - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 0, 1, &dSets.viewSet, 0, nullptr); + // Set 0, Binding 0 :Camera UBO + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, backendData->OffScreenRR.OffscreenPipelineLayout, 0, 1, &descriptorSets->m_DescriptorSets[0][0], 0, nullptr); + // Set 0, Binding 1: Texture + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, backendData->OffScreenRR.OffscreenPipelineLayout, 0, 1, &descriptorSets->m_DescriptorSets[0][0], 0, nullptr); uint32_t objectIndex = 0; - + // ---- Bind 3D Vertex And Index Buffers ---- - for(const auto& smActor : scene3D->GetSMActors()) + for (const auto& smActor : scene3D->GetSMActors()) { std::shared_ptr mesh = smActor->GetStaticMeshComponent()->GetStaticMesh(); @@ -511,11 +514,8 @@ namespace Karma vkCmdBindVertexBuffers(frameOnFlightData->CommandBuffer, 0, 1, vertexBuffers, vertexOffset); vkCmdBindIndexBuffer(frameOnFlightData->CommandBuffer, std::static_pointer_cast(mesh->GetIndexBuffer())->GetIndexBuffer(), 0, VK_INDEX_TYPE_UINT32); - // Set 1: Texture - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 1, 1, &dSets.textureSet[objectIndex], 0, nullptr); - // Set 2: Object UBO - vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanHolder::GetVulkanContext()->GetKarmaGuiGeneralPipelineLayout(), 2, 1, &dSets.objectsSet[objectIndex], 0, nullptr); + vkCmdBindDescriptorSets(frameOnFlightData->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, backendData->OffScreenRR.OffscreenPipelineLayout, 1, 1, &descriptorSets->m_DescriptorSets[1][objectIndex], 0, nullptr); // ----Issue Draw Commands---- // Draw 3D scene geometry on 2D rendertarget (it->FrameBuffers) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h index fb6d1ff3..94ff0c50 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h @@ -17,6 +17,8 @@ namespace Karma { + class AStaticMeshActor; + /** * @brief A multiply inherited class for supporting both OpenGL and Vulkan API's. * @@ -51,6 +53,8 @@ namespace Karma static void OnKarmaGuiLayerBegin(); static void OnKarmaGuiLayerEnd(); + static void OnAdditionOfStaticMesh(AStaticMeshActor* smActor); + /** * @brief Acessor function for KarmaGui's renderer backend (BackendRendererUserData). * diff --git a/Karma/src/Karma/Renderer/Scene.cpp b/Karma/src/Karma/Renderer/Scene.cpp index 4ebded8c..9b4b55d6 100644 --- a/Karma/src/Karma/Renderer/Scene.cpp +++ b/Karma/src/Karma/Renderer/Scene.cpp @@ -1,6 +1,8 @@ #include "Scene.h" #include "StaticMeshActor.h" +#include "KarmaGui/KarmaGuiRenderer.h" + namespace Karma { Scene::Scene() @@ -21,6 +23,7 @@ namespace Karma void Scene::AddStaticMeshActor(AStaticMeshActor* smActor) { m_SMActors.push_back(smActor); + KarmaGuiRenderer::OnAdditionOfStaticMesh(smActor); } void Scene::AddCamera(std::shared_ptr camera) diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp index 84d640d6..ef24986b 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.cpp @@ -8,6 +8,7 @@ #include "VulkanRHI/VulkanDynamicRHI.h" #include "VulkanRHI/VulkanRenderPass.h" #include "VulkanRHI/VulkanFramebuffer.h" +#include "VulkanRHI/VulkanDescriptorSets.h" // Visual Studio warnings /*#ifdef _MSC_VER @@ -732,6 +733,24 @@ namespace Karma return true; } + VkShaderModule KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateShaderModule(const std::vector& code) + { + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size() * sizeof(uint32_t); + createInfo.pCode = code.data(); + + VkShaderModule shaderModule; + VkResult result = vkCreateShaderModule(vulkanInfo->Device, &createInfo, nullptr, &shaderModule); + + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create shader module!"); + + return shaderModule; + } + void KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) { // Create the shader modules @@ -1299,24 +1318,246 @@ namespace Karma if(!backendData->OffScreenRR.bAllocationDoneOnce) { CreateOffScreenTextureRenderpassResource(); - - // This variable is also used to create sampler in KarmaGuiRenderer::Add3DSceneFor2DRendering once - backendData->OffScreenRR.bAllocationDoneOnce = true; } for(auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) { + if (!backendData->OffScreenRR.bAllocationDoneOnce) + { + // assuming that all 3D scenes use same pipeline layout. This may change later + CreateOffScreenTextureGraphicsPipelineResource(backendData->OffScreenRR.RenderPass->GetHandle(), it->Size.x, it->Size.y); - CreateOffScreenTextureDepthResource(&(*it)); + // This variable is also used to create sampler in KarmaGuiRenderer::Add3DSceneFor2DRendering once + backendData->OffScreenRR.bAllocationDoneOnce = true; + } + CreateOffScreenTextureDepthResource(&(*it)); CreateOffScreenTextureFrameBufferResource(&(*it)); - - return;// for experimental - // assuming that all 3D scenes use same pipeline layout. This may change later - VulkanHolder::GetVulkanContext()->CreateKarmaGuiGeneralGraphicsPipeline(backendData->OffScreenRR.RenderPass->GetHandle(), it->Size.x, it->Size.y); } } + static VkFormat ShaderDataTypeToVulkanType(ShaderDataType type) + { + switch (type) + { + case ShaderDataType::Float: + return VK_FORMAT_R32_SFLOAT; + case ShaderDataType::Float2: + return VK_FORMAT_R32G32_SFLOAT; + case ShaderDataType::Float3: + return VK_FORMAT_R32G32B32_SFLOAT; + case ShaderDataType::Float4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case ShaderDataType::None: + case ShaderDataType::Mat3: + case ShaderDataType::Mat4: + case ShaderDataType::Int: + case ShaderDataType::Int2: + case ShaderDataType::Int3: + case ShaderDataType::Int4: + case ShaderDataType::Bool: + // Refer Mesh::GaugeVertexDataLayout for usual datatype + // to be used in the context of vertex buffer + KR_CORE_ASSERT(false, "Weird ShaderDataType is being used") + return VK_FORMAT_UNDEFINED; + break; + } + + KR_CORE_ASSERT(false, "Vulkan doesn't support this ShaderDatatype"); + return VK_FORMAT_UNDEFINED; + } + + void KarmaGuiVulkanHandler::CreateOffScreenTextureGraphicsPipelineResource(VkRenderPass renderPassKG, float windowKGWidth, float windowKGHeight) + { + KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); + KarmaGui_ImplVulkan_InitInfo* vulkanInfo = &backendData->VulkanInitInfo; + + const KarmaVector& setLayouts = FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSetLayout()->GetHandles(); + + VkPipelineLayoutCreateInfo plInfo{}; + plInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + plInfo.setLayoutCount = static_cast(setLayouts.Num()); + plInfo.pSetLayouts = setLayouts.GetData(); + plInfo.pushConstantRangeCount = 0; + plInfo.pPushConstantRanges = nullptr; + + VkResult result = vkCreatePipelineLayout(vulkanInfo->Device, &plInfo, nullptr, &backendData->OffScreenRR.OffscreenPipelineLayout); + KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to create pipeline layout!"); + + VkShaderModule vertShaderModule = KarmaGui_ImplVulkan_CreateShaderModule(FVulkanDynamicRHI::Get().GetDevice()->GetDefaultShader()->GetVertSpirV()); + VkShaderModule fragShaderModule = KarmaGui_ImplVulkan_CreateShaderModule(FVulkanDynamicRHI::Get().GetDevice()->GetDefaultShader()->GetFragSpirV()); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + // See Mesh::GaugeVertexDataLayout to understand the layout of vertex data we are using + BufferLayout layout; + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Position")); + layout.PushElement(BufferElement(ShaderDataType::Float2, "v_UV")); + + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Normal")); + /*layout.PushElement(BufferElement(ShaderDataType::Float4, "v_Color")); + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Normal")); + layout.PushElement(BufferElement(ShaderDataType::Float3, "v_Tangent"));*/ + + VkVertexInputBindingDescription bindingDescription = {}; + std::vector attributeDescriptions; + + bindingDescription.binding = 0; + bindingDescription.stride = layout.GetStride(); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + // Telling vulkan what to expect from vertex data in terms of attributes and their rate of loading + uint32_t index = 0; + + for (const auto& element : layout) + { + VkVertexInputAttributeDescription elementAttributeDescription{}; + elementAttributeDescription.binding = 0; + elementAttributeDescription.location = index; + elementAttributeDescription.format = ShaderDataTypeToVulkanType(element.Type); + elementAttributeDescription.offset = static_cast(element.Offset); + + attributeDescriptions.push_back(elementAttributeDescription); + index++; + } + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = windowKGWidth; + viewport.height = windowKGHeight; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent.width = windowKGWidth; + scissor.extent.height = windowKGHeight; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + // Antialiasing + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineDepthStencilStateCreateInfo depthStencil{}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + + VkBool32 bLogicalOperationsAllowed = FVulkanDynamicRHI::Get().GetGpuDeviceFeatures().logicOp; + + // Mix the old and new value to produce a final color + // finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor; + // finalColor.a = newAlpha.a; + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + if (!bLogicalOperationsAllowed) + { + colorBlendAttachment.blendEnable = VK_TRUE; + } + else + { + colorBlendAttachment.blendEnable = VK_FALSE; + } + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + // Combine the old and new value using a bitwise operation + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + if (bLogicalOperationsAllowed) + { + colorBlending.logicOpEnable = VK_TRUE; + } + else + { + colorBlending.logicOpEnable = VK_FALSE; + } + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = backendData->OffScreenRR.OffscreenPipelineLayout; + pipelineInfo.renderPass = renderPassKG; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.pDepthStencilState = &depthStencil; + + VkResult resultGP = vkCreateGraphicsPipelines(vulkanInfo->Device, VK_NULL_HANDLE, + 1, &pipelineInfo, nullptr, &backendData->OffScreenRR.OffscreenGraphicsPipeline); + + KR_CORE_ASSERT(resultGP == VK_SUCCESS, "Failed to create graphics pipeline!"); + + + vkDestroyShaderModule(vulkanInfo->Device, fragShaderModule, nullptr); + vkDestroyShaderModule(vulkanInfo->Device, vertShaderModule, nullptr); + } + void KarmaGuiVulkanHandler::CreateOffScreenTextureRenderpassResource() { KarmaGui_ImplVulkan_Data* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); @@ -1827,6 +2068,9 @@ namespace Karma if (backendData->OffScreenRR.bAllocationDoneOnce) { + vkDestroyPipeline(vulkanInfo->Device, backendData->OffScreenRR.OffscreenGraphicsPipeline, nullptr); + vkDestroyPipelineLayout(vulkanInfo->Device, backendData->OffScreenRR.OffscreenPipelineLayout, nullptr); + delete backendData->OffScreenRR.RenderPass; backendData->OffScreenRR.RenderPass = nullptr; @@ -1906,11 +2150,6 @@ namespace Karma windowData->RHIResources->VulkanSwapChain = nullptr; delete windowData->RHIResources; - - return; // <-- to be uncommented when we have offscreen rendering logic - - // Assuming only on graphicspipeline - VulkanHolder::GetVulkanContext()->CleanUpKarmaGuiGeneralGraphicsPipeline(); } //-------------------------------------------------------------------------------------------------------- diff --git a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h index e7e76faf..4ae8d6eb 100644 --- a/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h +++ b/Karma/src/Platform/Vulkan/KarmaGuiVulkanHandler.h @@ -308,7 +308,7 @@ namespace Karma /** * @brief Collection of render targets organized in KarmaVector. Meaning each KarmaVector element contains - * a collection of rendertarget for a framebuffer + * a collection of rendertargets for a framebuffer */ KarmaVector RenderTargets; }; @@ -859,6 +859,9 @@ namespace Karma * @see KarmaGuiRenderer::Add3DSceneFor2DRendering */ bool bAllocationDoneOnce; + + VkPipelineLayout OffscreenPipelineLayout; + VkPipeline OffscreenGraphicsPipeline; KarmaGui_OffScreen_Render_Resources() : bAllocationDoneOnce(false) {} @@ -1114,6 +1117,8 @@ namespace Karma */ static void KarmaGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator); + static VkShaderModule KarmaGui_ImplVulkan_CreateShaderModule(const std::vector& code); + /** * @brief Bilinear sampling object is created for font and stored in KarmaGui_ImplVulkan_Data.FontSampler if not done already in KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateDeviceObjects(). * @@ -1345,6 +1350,8 @@ namespace Karma */ static void CreateOffScreenTextureFrameBufferResource(KarmaGui_3DScene_To_2DTexture_Data* textureData); + static void CreateOffScreenTextureGraphicsPipelineResource(VkRenderPass renderPassKG, float windowKGWidth, float windowKGHeight); + /** * @brief Clears appropriate buffers which are used for KarmaGui's rendering. They include: * diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp index 32cc8c7a..7c54acb1 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.cpp @@ -192,7 +192,7 @@ namespace Karma vkUpdateDescriptorSets(m_Device->GetLogicalDevice(), 1, &descriptorWrite, 0, nullptr); } - void FVulkanDescriptorSets::UpdateTextureDescriptorSet(std::shared_ptr Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex) + void FVulkanDescriptorSets::UpdateTextureDescriptorSet(VulkanTexture* Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex) { VkDescriptorImageInfo imageInfo{}; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -202,7 +202,7 @@ namespace Karma VkWriteDescriptorSet descriptorWrite{}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = m_DescriptorSets[SetLayoutIndex][DescriptorSetIndex]; - descriptorWrite.dstBinding = 0; // Assuming the texture is bound to binding 0 in the shader + descriptorWrite.dstBinding = 1; // Assuming the texture is bound to binding 1 in the shader descriptorWrite.dstArrayElement = 0; descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrite.descriptorCount = 1; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h index 125dfaff..f27981e0 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDescriptorSets.h @@ -197,7 +197,7 @@ namespace Karma * * @since Karma 1.0.0 */ - void UpdateTextureDescriptorSet(std::shared_ptr Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex); + void UpdateTextureDescriptorSet(class VulkanTexture* Texture, uint32_t SetLayoutIndex, uint32_t DescriptorSetIndex); FVulkanDevice* m_Device; KarmaVector> m_DescriptorSets; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp index b91772c7..c7aca176 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.cpp @@ -1,6 +1,7 @@ #include "VulkanDevice.h" #include "VulkanDynamicRHI.h" #include "Vulkan/VulkanTexture.h" +#include "Vulkan/VulkanShader.h" #include "VulkanRHI/VulkanDescriptorSets.h" namespace Karma @@ -26,6 +27,7 @@ namespace Karma delete m_DescriptorPool[counter]; } + delete m_DefaultShader; delete m_DefaultTexture; delete m_DefaultDescriptorSetLayout; vkDestroyCommandPool(m_LogicalDevice, m_CommandPool, nullptr); @@ -118,6 +120,7 @@ namespace Karma // Default texture (unreal grid) m_DefaultTexture = new VulkanTexture(this, "../Resources/Textures/UnrealGrid.png"); + m_DefaultShader = new VulkanShader("../Resources/Shaders/shader.vert", "../Resources/Shaders/shader.frag"); } void FVulkanDevice::InitializeDefaultDescriptorSets(uint32_t MaxFramesInFlight) @@ -131,6 +134,7 @@ namespace Karma m_DefaultDescriptorSets[counter] = new FVulkanDescriptorSets(this, *m_DefaultDescriptorSetLayout); m_DescriptorPool[counter]->AllocateDescriptorSets(*m_DefaultDescriptorSetLayout, *m_DefaultDescriptorSets[counter]); + m_DefaultDescriptorSets[counter]->UpdateTextureDescriptorSet(m_DefaultTexture, 0, 0); } m_MaxFramesInFlight = MaxFramesInFlight; diff --git a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h index e94ef219..05003390 100644 --- a/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h +++ b/Karma/src/Platform/Vulkan/VulkanRHI/VulkanDevice.h @@ -20,6 +20,7 @@ namespace Karma class FVulkanDescriptorSetsLayout; class FVulkanDescriptorSets; class FVulkanSwapChain; + class VulkanShader; /** * @class FVulkanDevice @@ -166,6 +167,12 @@ namespace Karma inline KarmaVector& GetDefaultDescriptorSets() { return m_DefaultDescriptorSets; } + inline FVulkanDescriptorSetsLayout* GetDefaultDescriptorSetLayout() const { return m_DefaultDescriptorSetLayout; } + + inline VulkanTexture* GetDefaultTexture() const { return m_DefaultTexture; } + + inline VulkanShader* GetDefaultShader() const { return m_DefaultShader; } + private: /** * @brief Populates the provided descriptor sets layout with the necessary bindings for the device. @@ -194,6 +201,7 @@ namespace Karma VkQueue m_GraphicsQueue; VkQueue m_PresentQueue; VulkanTexture* m_DefaultTexture; + VulkanShader* m_DefaultShader; // Additional members for managing queues, command pools, etc. can be added here. VkCommandPool m_CommandPool; diff --git a/Resources/Shaders/shader.frag b/Resources/Shaders/shader.frag index 22641847..83b09aa8 100644 --- a/Resources/Shaders/shader.frag +++ b/Resources/Shaders/shader.frag @@ -7,7 +7,7 @@ layout(location = 1) in vec2 fragUVs; layout(location = 0) out vec4 outColor; // set 1: texture -layout(set = 1, binding = 0) uniform sampler2D texSampler; +layout(set = 0, binding = 1) uniform sampler2D texSampler; void main() { diff --git a/Resources/Shaders/shader.vert b/Resources/Shaders/shader.vert index 2b4545e6..f1872f88 100644 --- a/Resources/Shaders/shader.vert +++ b/Resources/Shaders/shader.vert @@ -6,6 +6,8 @@ layout(location = 0) in vec3 inPosition; layout(location = 1) in vec2 inUV; +layout(location = 2) in vec3 inNormal; + layout(location = 0) out vec4 fragColor; layout(location = 1) out vec2 fragUVs; @@ -17,7 +19,7 @@ layout(set = 0, binding = 0) uniform VPUniformBufferObject }; // set 2: per-mesh transform -layout(set = 2, binding = 0) uniform ModelUniformBufferObject +layout(set = 1, binding = 0) uniform ModelUniformBufferObject { mat4 u_Model; }; From 0755a85d75a6234fd106913c3aac7f9857bd7b8a Mon Sep 17 00:00:00 2001 From: pc_boy Date: Thu, 12 Feb 2026 20:03:19 +0530 Subject: [PATCH 097/107] Minor fix to address validation warning about buffer in use by command buffer --- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 1e8faf2e..988f9b73 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -262,6 +262,8 @@ namespace Karma void VulkanUniformBuffer::ClearBuffer() { + vkDeviceWaitIdle(m_Device); + for (size_t i = 0; i < m_UniformBuffers.size(); i++) { vkDestroyBuffer(m_Device, m_UniformBuffers[i], nullptr); From 6458cf433ff8dd2987c39ac6c0436f71d5a745ee Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Thu, 12 Feb 2026 21:24:14 +0530 Subject: [PATCH 098/107] Couple of mesh in scene loading --- .../Engine/Classes/Engine/StaticMeshActor.cpp | 15 ++++---- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 31 +++++----------- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h | 2 + Pranjal/Source/Private/EditorLayer.cpp | 37 ++++++++++++++++++- Pranjal/Source/Public/EditorLayer.h | 1 + 5 files changed, 57 insertions(+), 29 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 7f06c986..977524e7 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -19,22 +19,22 @@ namespace Karma void AStaticMeshActor::Tick(float DeltaSeconds) { - //m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); + m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); - FTransform AnimTransform = FTransform::Identity(); + //FTransform AnimTransform = FTransform::Identity(); - AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); + //AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); - SetActorTransform(AnimTransform); + //SetActorTransform(AnimTransform); - m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); + //m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); - m_RotationAngle += 0.78f * DeltaSeconds; + /*m_RotationAngle += 0.78f * DeltaSeconds; if(m_RotationAngle > 360.f) { m_RotationAngle -= 360.f; - } + }*/ } void AStaticMeshActor::LoadMeshFromFile(const std::string& filePath) @@ -48,6 +48,7 @@ namespace Karma //m_ActorTranformUniform = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 1.f)); //m_ActorTranformUniform = glm::scale(m_ActorTranformUniform, glm::vec3(0.25f)); + //SetActorTransform(FTransform::Identity()); m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); UBODataPointer uModelMatrix(&m_ActorTranformUniform); diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 9c281f84..86c5255c 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -18,6 +18,7 @@ namespace Karma { VkDescriptorPool KarmaGuiRenderer::m_KarmaGuiDescriptorPool; + uint32_t KarmaGuiRenderer::m_SMCounter = 0; KarmaGui_ImplVulkanH_Window KarmaGuiRenderer::m_VulkanWindowData; bool KarmaGuiRenderer::m_SwapChainRebuild; GLFWwindow* KarmaGuiRenderer::m_GLFWwindow = nullptr; @@ -126,11 +127,16 @@ namespace Karma void KarmaGuiRenderer::OnAdditionOfStaticMesh(AStaticMeshActor* smActor) { - uint32_t maxFramesInFlight = GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); - - for (uint32_t counter = 0; counter < maxFramesInFlight; counter++) + if(GRHIInterfaceType == ERHIInterfaceType::Vulkan) { - FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(smActor->GetMeshTransformUniform().get()), 1, 0, counter); + uint32_t maxFramesInFlight = GetWindowData().RHIResources->VulkanSwapChain->GetMaxFramesInFlight(); + + for (uint32_t counter = 0; counter < maxFramesInFlight; counter++) + { + FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(smActor->GetMeshTransformUniform().get()), 1, m_SMCounter, counter); + } + + m_SMCounter++; } } @@ -464,7 +470,6 @@ namespace Karma KR_CORE_ASSERT(result == VK_SUCCESS, "Couldn't begin commandbuffer recording"); } - //VulkanHolder::GetVulkanContext()->UploadUBO(windowData->SemaphoreIndex); FVulkanDynamicRHI::Get().UploadUniformBufferObjects(windowData->SemaphoreIndex); for (auto it = backendData->Elements3DTo2D.begin(); it != backendData->Elements3DTo2D.end(); ++it) @@ -552,22 +557,6 @@ namespace Karma } vkCmdEndRenderPass(frameOnFlightData->CommandBuffer); - - // Transition the swapchain image from VK_IMAGE_LAYOUT_UNDEFINED -> VK_IMAGE_LAYOUT_PRESENT_SRC_KHR - // else there'd be validation error - /*VkImageMemoryBarrier presentBarrier = {}; - presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - presentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - presentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - presentBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - presentBarrier.image = windowData->RHIResources.VulkanSwapChain->GetSwapChainImages()[windowData->ImageFrameIndex]; - presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - presentBarrier.subresourceRange.levelCount = 1; - presentBarrier.subresourceRange.layerCount = 1; - - vkCmdPipelineBarrier(frameOnFlightData->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &presentBarrier);*/ result = vkEndCommandBuffer(frameOnFlightData->CommandBuffer); KR_CORE_ASSERT(result == VK_SUCCESS, "Failed to end command buffer"); diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h index 94ff0c50..f9b8eb7f 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.h @@ -138,6 +138,8 @@ namespace Karma // Vulkan specific members static VkDescriptorPool m_KarmaGuiDescriptorPool; static KarmaGui_ImplVulkanH_Window m_VulkanWindowData; + + static uint32_t m_SMCounter; static bool m_SwapChainRebuild; }; diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index ad7876e5..f082edaa 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -209,6 +209,10 @@ namespace Karma { SpawnStaticMeshActor(); } + else if (e.GetKeyCode() == GLFW_KEY_B) + { + SpawnStaticMeshActor2(); + } return false; } @@ -373,10 +377,41 @@ namespace Karma { KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); + staticMeshActor->SetActorTransform(smActorTransform); + staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); m_EditorScene->AddStaticMeshActor(staticMeshActor); + } + } + } + + void EditorLayer::SpawnStaticMeshActor2() + { + UWorld* currentWorld = GEngine->GetCurrentGameInstance()->GetWorldContext()->World(); + + if (currentWorld) + { + KR_INFO("Current World is : {0}", currentWorld->GetName()); + + FTransform smActorTransform = FTransform::m_Identity; + smActorTransform.SetTranslation(glm::vec3(1.f, 0.f, 1.5f)); + + FActorSpawnParameters smActorParams; + smActorParams.m_Owner = nullptr; + smActorParams.m_Name = "StaticMeshActor2"; + smActorParams.m_OverrideLevel = currentWorld->GetCurrentLevel(); + - //Karma::VulkanHolder::GetVulkanContext()->CreateVulkanResourcesForScene(m_EditorScene); + AStaticMeshActor* staticMeshActor = currentWorld->SpawnActor(AStaticMeshActor::StaticClass(), &smActorTransform, smActorParams); + + if (staticMeshActor) + { + KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); + + staticMeshActor->SetActorTransform(smActorTransform); + + staticMeshActor->LoadMeshFromFile("../Resources/Models/FORZombie.obj"); + m_EditorScene->AddStaticMeshActor(staticMeshActor); } } } diff --git a/Pranjal/Source/Public/EditorLayer.h b/Pranjal/Source/Public/EditorLayer.h index 80cbd228..a1e20473 100644 --- a/Pranjal/Source/Public/EditorLayer.h +++ b/Pranjal/Source/Public/EditorLayer.h @@ -29,6 +29,7 @@ namespace Karma // Tentative routine for incorporating spawning of full fledged StaticMeshActor (SM actor) // with model void SpawnStaticMeshActor(); + void SpawnStaticMeshActor2(); private: std::shared_ptr m_ModelShader; From 0d4d2b9208159f2a1b96acfba848630047b50a8f Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Fri, 13 Feb 2026 04:46:53 +0530 Subject: [PATCH 099/107] Experimental meddling --- Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp | 5 ++++- Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h | 2 ++ Pranjal/Source/Private/EditorLayer.cpp | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 977524e7..715b9c35 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -1,6 +1,8 @@ #include "StaticMeshActor.h" #include "Mesh.h" +#include + namespace Karma { AStaticMeshActor::AStaticMeshActor(const std::string& name) @@ -19,7 +21,7 @@ namespace Karma void AStaticMeshActor::Tick(float DeltaSeconds) { - m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); + m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(m_RotationSpeed * 7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); //FTransform AnimTransform = FTransform::Identity(); @@ -55,5 +57,6 @@ namespace Karma m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform m_StaticMeshComponent->SetStaticMesh(loadedMesh); + m_RotationSpeed = glm::linearRand(0.2f, 4.5f); } } diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 11a6f057..28b82457 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -44,6 +44,8 @@ namespace Karma //tentative glm::mat4 m_ActorTranformUniform; float m_RotationAngle; + + float m_RotationSpeed; public: /** diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index f082edaa..17aae48f 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -394,7 +394,6 @@ namespace Karma KR_INFO("Current World is : {0}", currentWorld->GetName()); FTransform smActorTransform = FTransform::m_Identity; - smActorTransform.SetTranslation(glm::vec3(1.f, 0.f, 1.5f)); FActorSpawnParameters smActorParams; smActorParams.m_Owner = nullptr; From b1ec0fa6de8a3348d1e63c12401dd87035ab1c80 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sat, 14 Feb 2026 07:18:31 +0530 Subject: [PATCH 100/107] Minor adjustments --- Karma/src/Platform/Vulkan/VulkanShader.cpp | 2 +- Pranjal/Source/Private/EditorLayer.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Karma/src/Platform/Vulkan/VulkanShader.cpp b/Karma/src/Platform/Vulkan/VulkanShader.cpp index e62e93b9..77f04fe3 100644 --- a/Karma/src/Platform/Vulkan/VulkanShader.cpp +++ b/Karma/src/Platform/Vulkan/VulkanShader.cpp @@ -21,7 +21,7 @@ namespace Karma std::vector VulkanShader::Compile(const std::string& src, const std::string& source, EShLanguage lang) { - KR_CORE_INFO("Compiling {0} {1} for Vulkan ...", lang == EShLangVertex ? "vertex shader" : "fragment shader", src); + KR_CORE_INFO("Compiling {0} {1} for Vulkan ..", lang == EShLangVertex ? "vertex shader" : "fragment shader", src); const char* sString = source.c_str(); diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 17aae48f..14dfe491 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -394,6 +394,7 @@ namespace Karma KR_INFO("Current World is : {0}", currentWorld->GetName()); FTransform smActorTransform = FTransform::m_Identity; + smActorTransform.SetTranslation(glm::vec3(0.f, 0.f, 1.f)); FActorSpawnParameters smActorParams; smActorParams.m_Owner = nullptr; From d445490d834341594a6f3e9d31a3689e4ac7ee07 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 14 Feb 2026 20:59:23 +0530 Subject: [PATCH 101/107] Game code of transforms from primitivecomponent --- .../Classes/Components/PrimitiveComponent.cpp | 21 ++++++++++++++- .../Classes/Components/PrimitiveComponent.h | 25 ++++++++++++++++++ .../Classes/Components/SceneComponent.h | 2 +- .../Engine/Classes/Engine/StaticMeshActor.cpp | 26 ++++--------------- .../Engine/Classes/Engine/StaticMeshActor.h | 20 +------------- .../Engine/Classes/GameFramework/Actor.cpp | 4 +-- Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 3 ++- Karma/src/Platform/Vulkan/VulkanContext.cpp | 2 +- Pranjal/Source/Private/EditorLayer.cpp | 4 --- 9 files changed, 57 insertions(+), 50 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp index ee61a65e..f624fa53 100644 --- a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp +++ b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.cpp @@ -1,7 +1,18 @@ #include "PrimitiveComponent.h" +#include "Buffer.h" namespace Karma { + UPrimitiveComponent::UPrimitiveComponent() : USceneComponent() + { + m_ComponentTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 0)); + + m_UniformTranformMatrix = GetComponentTransform().ToMatrixWithScale(); + + UBODataPointer uModelMatrix(&m_UniformTranformMatrix); + m_ComponentTransformUniform->UpdateUniforms(uModelMatrix); + } + void UPrimitiveComponent::BeginPlay() { } @@ -9,4 +20,12 @@ namespace Karma void UPrimitiveComponent::SetCachedMaxDrawDistance(const float newCachedMaxDrawDistance) { } -} \ No newline at end of file + + void UPrimitiveComponent::SetWorldTransform(const FTransform& NewTransform) + { + Super::SetWorldTransform(NewTransform); + + m_UniformTranformMatrix = GetComponentTransform().ToMatrixWithScale(); + } +} + diff --git a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h index 29e0b018..19d024a4 100644 --- a/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/PrimitiveComponent.h @@ -13,6 +13,8 @@ namespace Karma { + class UniformBufferObject; + /** * @brief PrimitiveComponents are SceneComponents that contain or generate some sort of geometry, generally to be rendered or used as collision data. * @@ -27,6 +29,9 @@ namespace Karma DECLARE_KARMA_CLASS(UPrimitiveComponent, USceneComponent) public: + + UPrimitiveComponent(); + /** Controls whether the primitive component should cast a shadow or not. */ uint8_t CastShadow : 1; @@ -49,6 +54,15 @@ namespace Karma mutable float LastRenderTimeOnScreen; //friend class FPrimitiveSceneInfo; + + /** + * @brief Uniform buffer object for the mesh's transformation matrix + * + * Used to upload the actor's transform to the GPU for rendering + */ + std::shared_ptr m_ComponentTransformUniform; + + glm::mat4 m_UniformTranformMatrix; public: /** @@ -58,5 +72,16 @@ namespace Karma * @since Karma 1.0.0 */ void SetCachedMaxDrawDistance(const float newCachedMaxDrawDistance); + + /** + * @brief Getter for the transform uniform buffer object + * + * @return std::shared_ptr The uniform buffer object for the mesh's transformation matrix. + * + * @since Karma 1.0.0 + */ + std::shared_ptr GetComponentTransformUniform() const { return m_ComponentTransformUniform; } + + virtual void SetWorldTransform(const FTransform& NewTransform) override; }; } diff --git a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h index 4153f6d1..04757e39 100644 --- a/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h +++ b/Karma/src/Karma/Engine/Classes/Components/SceneComponent.h @@ -175,7 +175,7 @@ namespace Karma * If CCD is on and not teleporting, this will affect objects along the entire sweep volume. * @since Karma 1.0.0 */ - void SetWorldTransform(const FTransform& NewTransform/*, bool bSweep = false, FHitResult* OutSweepHitResult = nullptr, ETeleportType Teleport = ETeleportType::None*/); + virtual void SetWorldTransform(const FTransform& NewTransform/*, bool bSweep = false, FHitResult* OutSweepHitResult = nullptr, ETeleportType Teleport = ETeleportType::None*/); /** * @brief Get the SceneComponent we are attached to. diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 715b9c35..870640f2 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -21,22 +21,18 @@ namespace Karma void AStaticMeshActor::Tick(float DeltaSeconds) { - m_ActorTranformUniform = glm::rotate(m_ActorTranformUniform, glm::radians(m_RotationSpeed * 7.8f * DeltaSeconds), glm::vec3(0.f, 0.f, 1.f)); + FTransform AnimTransform = GetTransform(); - //FTransform AnimTransform = FTransform::Identity(); + AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); - //AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); - - //SetActorTransform(AnimTransform); + SetActorTransform(AnimTransform); - //m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); - - /*m_RotationAngle += 0.78f * DeltaSeconds; + m_RotationAngle += 0.78f * DeltaSeconds * m_RotationSpeed; if(m_RotationAngle > 360.f) { m_RotationAngle -= 360.f; - }*/ + } } void AStaticMeshActor::LoadMeshFromFile(const std::string& filePath) @@ -44,18 +40,6 @@ namespace Karma std::shared_ptr loadedMesh; loadedMesh.reset(new Mesh(filePath)); - // We create uniform buffer object for model's transformation matrix - m_MeshTransformUniform.reset(UniformBufferObject::Create({ ShaderDataType::Mat4 }, 0));// Binding point 2 and gets registered in VulkanContext during its initialization - - //m_ActorTranformUniform = glm::translate(glm::mat4(1.f), glm::vec3(0.f, 0.f, 1.f)); - //m_ActorTranformUniform = glm::scale(m_ActorTranformUniform, glm::vec3(0.25f)); - - //SetActorTransform(FTransform::Identity()); - m_ActorTranformUniform = GetTransform().ToMatrixWithScale(); - - UBODataPointer uModelMatrix(&m_ActorTranformUniform); - m_MeshTransformUniform->UpdateUniforms(uModelMatrix);// set the model matrix (Actor Transform) uniform - m_StaticMeshComponent->SetStaticMesh(loadedMesh); m_RotationSpeed = glm::linearRand(0.2f, 4.5f); } diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h index 28b82457..2c23a7f9 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.h @@ -34,17 +34,8 @@ namespace Karma //std::shared_ptr m_StaticMeshComponent; UStaticMeshComponent* m_StaticMeshComponent; - /** - * @brief Uniform buffer object for the mesh's transformation matrix - * - * Used to upload the actor's transform to the GPU for rendering - */ - std::shared_ptr m_MeshTransformUniform; - - //tentative - glm::mat4 m_ActorTranformUniform; + // Experimental float m_RotationAngle; - float m_RotationSpeed; public: @@ -72,15 +63,6 @@ namespace Karma * @since Karma 1.0.0 */ UStaticMeshComponent* GetStaticMeshComponent() const { return m_StaticMeshComponent; } - - /** - * @brief Getter for the mesh transform uniform buffer object - * - * @return std::shared_ptr The uniform buffer object for the mesh's transformation matrix. - * - * @since Karma 1.0.0 - */ - std::shared_ptr GetMeshTransformUniform() const { return m_MeshTransformUniform; } virtual void Tick(float DeltaSeconds) override; }; diff --git a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp index e6bc16fb..daf5273a 100644 --- a/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp +++ b/Karma/src/Karma/Engine/Classes/GameFramework/Actor.cpp @@ -472,8 +472,8 @@ namespace Karma // Respect any non-default transform value that the root component may have received from the archetype that's owned // by the native CDO, so the final transform might not always necessarily equate to the passed-in UserSpawnTransform. - const FTransform RootTransform(SceneRootComponent->GetRelativeRotation(), SceneRootComponent->GetRelativeLocation(), SceneRootComponent->GetRelativeScale3D()); - const FTransform FinalRootComponentTransform = RootTransform * UserSpawnTransform; + /*const FTransform RootTransform(SceneRootComponent->GetRelativeRotation(), SceneRootComponent->GetRelativeLocation(), SceneRootComponent->GetRelativeScale3D());*/ + const FTransform FinalRootComponentTransform = /*RootTransform */ UserSpawnTransform; SceneRootComponent->SetWorldTransform(FinalRootComponentTransform/*, false, nullptr, ETeleportType::ResetPhysics*/); } diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index 86c5255c..f4780599 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -11,6 +11,7 @@ #include "VulkanRHI/VulkanRenderPass.h" #include "VulkanRHI/VulkanDescriptorSets.h" #include "StaticMeshActor.h" +#include "PrimitiveComponent.h" // Emedded font #include "Karma/KarmaGui/Roboto-Regular.h" @@ -133,7 +134,7 @@ namespace Karma for (uint32_t counter = 0; counter < maxFramesInFlight; counter++) { - FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(smActor->GetMeshTransformUniform().get()), 1, m_SMCounter, counter); + FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(static_cast(smActor->GetRootComponent())->GetComponentTransformUniform() .get()), 1, m_SMCounter, counter); } m_SMCounter++; diff --git a/Karma/src/Platform/Vulkan/VulkanContext.cpp b/Karma/src/Platform/Vulkan/VulkanContext.cpp index 32853649..6a985729 100644 --- a/Karma/src/Platform/Vulkan/VulkanContext.cpp +++ b/Karma/src/Platform/Vulkan/VulkanContext.cpp @@ -622,7 +622,7 @@ namespace Karma // Update set 2: Per-mesh UBO will be updated during mesh rendering { - std::shared_ptr objectUBO = static_pointer_cast(smElement->GetMeshTransformUniform()); + std::shared_ptr objectUBO = nullptr;//static_pointer_cast(smElement->GetMeshTransformUniform()); VkDescriptorBufferInfo objectInfo{}; objectInfo.buffer = objectUBO->GetUniformBuffers()[frameIndex]; diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index 14dfe491..b49798a7 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -376,8 +376,6 @@ namespace Karma if (staticMeshActor) { KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); - - staticMeshActor->SetActorTransform(smActorTransform); staticMeshActor->LoadMeshFromFile("../Resources/Models/BonedCylinder.obj"); m_EditorScene->AddStaticMeshActor(staticMeshActor); @@ -407,8 +405,6 @@ namespace Karma if (staticMeshActor) { KR_INFO("Spawned Actor: {0}", staticMeshActor->GetName()); - - staticMeshActor->SetActorTransform(smActorTransform); staticMeshActor->LoadMeshFromFile("../Resources/Models/FORZombie.obj"); m_EditorScene->AddStaticMeshActor(staticMeshActor); From 2bc4d82b5ba4141cb66cb88631346e4624c6fde6 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sat, 14 Feb 2026 23:10:03 +0530 Subject: [PATCH 102/107] KarmaGuizmo functional --- .../Engine/Classes/Engine/StaticMeshActor.cpp | 4 ++-- Karma/src/Karma/KarmaGui/KarmaGuiLayer.cpp | 3 +++ Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp | 10 +++++----- Pranjal/Source/Private/EditorLayer.cpp | 1 + Pranjal/Source/Private/KarmaGuiMesa.cpp | 20 +++++++++++++++++++ 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp index 870640f2..b645c3dd 100644 --- a/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp +++ b/Karma/src/Karma/Engine/Classes/Engine/StaticMeshActor.cpp @@ -25,7 +25,7 @@ namespace Karma AnimTransform.SetRotation(TRotator(glm::vec3(0.f, 0.f, m_RotationAngle))); - SetActorTransform(AnimTransform); + //SetActorTransform(AnimTransform); m_RotationAngle += 0.78f * DeltaSeconds * m_RotationSpeed; @@ -41,6 +41,6 @@ namespace Karma loadedMesh.reset(new Mesh(filePath)); m_StaticMeshComponent->SetStaticMesh(loadedMesh); - m_RotationSpeed = glm::linearRand(0.2f, 4.5f); + m_RotationSpeed = glm::linearRand(0.2f, 1.0f); } } diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiLayer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiLayer.cpp index 509b7d35..14d864b3 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiLayer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiLayer.cpp @@ -5,6 +5,7 @@ #include "glm/glm.hpp" #include "Renderer/Renderer.h" #include "KarmaGuiRenderer.h" +#include "KarmaGui/KarmaGuizmo.h" namespace Karma { @@ -67,6 +68,8 @@ namespace Karma { KarmaGuiRenderer::OnKarmaGuiLayerBegin(); KarmaGui::NewFrame(); + + KarmaGuizmo::BeginFrame(); } void KarmaGuiLayer::KarmaGuiRender(float deltaTime) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp index f4780599..d27d8ba0 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp +++ b/Karma/src/Karma/KarmaGui/KarmaGuiRenderer.cpp @@ -134,7 +134,7 @@ namespace Karma for (uint32_t counter = 0; counter < maxFramesInFlight; counter++) { - FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(static_cast(smActor->GetRootComponent())->GetComponentTransformUniform() .get()), 1, m_SMCounter, counter); + FVulkanDynamicRHI::Get().GetDevice()->GetDefaultDescriptorSets()[counter]->UpdateUniformBufferDescriptorSet(static_cast(static_cast(smActor->GetRootComponent())->GetComponentTransformUniform().get()), 1, m_SMCounter, counter); } m_SMCounter++; @@ -301,11 +301,11 @@ namespace Karma if (width > 0 && height > 0) { - //KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateOrResizeWindow(&m_VulkanWindowData, true, true); - KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(&m_VulkanWindowData); - KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, false); + //KarmaGuiVulkanHandler::KarmaGui_ImplVulkan_CreateOrResizeWindow(&m_VulkanWindowData, true, true); + KarmaGuiVulkanHandler::ShivaSwapChainForRebuild(&m_VulkanWindowData); + KarmaGuiVulkanHandler::FillWindowData(&m_VulkanWindowData, false); - m_SwapChainRebuild = false; + m_SwapChainRebuild = false; } } diff --git a/Pranjal/Source/Private/EditorLayer.cpp b/Pranjal/Source/Private/EditorLayer.cpp index b49798a7..9c68027d 100644 --- a/Pranjal/Source/Private/EditorLayer.cpp +++ b/Pranjal/Source/Private/EditorLayer.cpp @@ -364,6 +364,7 @@ namespace Karma KR_INFO("Current World is : {0}", currentWorld->GetName()); FTransform smActorTransform = FTransform::m_Identity; + smActorTransform.SetTranslation(glm::vec3(1.f, 0, 1.f)); FActorSpawnParameters smActorParams; smActorParams.m_Owner = nullptr; diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 654b761f..8fcc5e8c 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -14,6 +14,8 @@ #include "hwinfo/hwinfo.h" #include "hwinfo/utils/unit.h" #include "spdlog/sinks/callback_sink.h" +#include "KarmaGui/KarmaGuizmo.h" +#include "StaticMeshActor.h" // Experimental //#include "Karma/Core/UObjectAllocator.h" @@ -540,6 +542,24 @@ namespace Karma drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); } + KarmaGuizmo::SetDrawlist(KarmaGui::GetWindowDrawList()); + KarmaGuizmo::SetRect(pos.x, pos.y, window->Size.x, window->Size.y); + + if(scene->GetSMActors().size() > 0) + { + std::shared_ptr sceneCamera = scene->GetSceneCamera(); + glm::mat4 viewMatrix = sceneCamera->GetViewMatirx(); + + glm::mat4 projectionMatrix = sceneCamera->GetProjectionMatrix(); + + float* projectionPtr = glm::value_ptr(projectionMatrix); + projectionPtr[5] *= -1.f; + + glm::mat4 objectMatrix = scene->GetSMActors()[0]->GetTransform().ToMatrixWithScale(); + + KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::WORLD, glm::value_ptr(objectMatrix)); + } + scene->SetRenderWindow(window); if(window->Size.x != m_3DExhibitor.widthCache || window->Size.y != m_3DExhibitor.heightCache) From 1997bf6e9b0cc92ee5703a1936dddaaefbe0fef3 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Sun, 15 Feb 2026 08:51:16 +0530 Subject: [PATCH 103/107] buffer destroy and MSVC decl --- Karma/src/Karma/KarmaGui/KarmaGuizmo.h | 2 +- Karma/src/Platform/Vulkan/VulkanBuffer.cpp | 4 ++++ Pranjal/Source/Private/KarmaGuiMesa.cpp | 22 ++++++++++++++++------ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Karma/src/Karma/KarmaGui/KarmaGuizmo.h b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h index 75e9d57c..ca3fe978 100644 --- a/Karma/src/Karma/KarmaGui/KarmaGuizmo.h +++ b/Karma/src/Karma/KarmaGui/KarmaGuizmo.h @@ -107,7 +107,7 @@ void EditTransform(const Camera& camera, matrix_t& matrix) #include "KarmaGui.h" #ifndef KARMAGUI_API -#define KARMAGUI_API +#define KARMAGUI_API KARMA_API #endif #ifndef KARMAGUIZMO_NAMESPACE diff --git a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp index 988f9b73..e4299bf5 100644 --- a/Karma/src/Platform/Vulkan/VulkanBuffer.cpp +++ b/Karma/src/Platform/Vulkan/VulkanBuffer.cpp @@ -39,6 +39,8 @@ namespace Karma VulkanVertexBuffer::~VulkanVertexBuffer() { + vkDeviceWaitIdle(m_Device); + vkDestroyBuffer(m_Device, m_VertexBuffer, nullptr); vkFreeMemory(m_Device, m_VertexBufferMemory, nullptr); } @@ -143,6 +145,8 @@ namespace Karma VulkanIndexBuffer::~VulkanIndexBuffer() { + vkDeviceWaitIdle(m_Device); + vkDestroyBuffer(m_Device, m_IndexBuffer, nullptr); vkFreeMemory(m_Device, m_IndexBufferMemory, nullptr); } diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 8fcc5e8c..feefccfd 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -514,8 +514,16 @@ namespace Karma bShouldRefresh = true; } - m_ViewportFocused = KarmaGui::IsWindowFocused(); - m_ViewportHovered = KarmaGui::IsWindowHovered() && !((window->Pos.y + window->TitleBarHeight()) * KarmaGui::GetIO().DisplayFramebufferScale.y > KarmaGui::GetMousePos().y); + if (!KarmaGuizmo::IsUsing()) + { + m_ViewportFocused = KarmaGui::IsWindowFocused(); + m_ViewportHovered = KarmaGui::IsWindowHovered() && !((window->Pos.y + window->TitleBarHeight()) * KarmaGui::GetIO().DisplayFramebufferScale.y > KarmaGui::GetMousePos().y); + } + else + { + m_ViewportFocused = false; + m_ViewportHovered = false; + } KarmaGuiIO& io = KarmaGui::GetIO(); @@ -527,14 +535,15 @@ namespace Karma KarmaGuiBackendRendererUserData* backendData = KarmaGuiRenderer::GetBackendRendererUserData(); backgroundImageTextureID = backendData->GetTextureIDAtIndex(1); - KGTextureID textureID3D; + KGTextureID textureID3D = nullptr; if (scene->GetSMActors().size() > 0) { textureID3D = KarmaGuiRenderer::Add3DSceneFor2DRendering(scene, KGVec2(window->Size.x, window->Size.y)); + KarmaGui::SetItemAllowOverlap(); } - - KGDrawList* drawList = KarmaGui::GetWindowDrawList(); + KGVec2 pos = KarmaGui::GetCursorScreenPos(); + KGDrawList* drawList = KarmaGui::GetWindowDrawList(); drawList->AddImage((void*)backgroundImageTextureID, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); if (scene->GetSMActors().size() > 0 && textureID3D != nullptr) @@ -542,7 +551,7 @@ namespace Karma drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); } - KarmaGuizmo::SetDrawlist(KarmaGui::GetWindowDrawList()); + KarmaGuizmo::SetDrawlist(KarmaGui::GetBackgroundDrawList()); KarmaGuizmo::SetRect(pos.x, pos.y, window->Size.x, window->Size.y); if(scene->GetSMActors().size() > 0) @@ -557,6 +566,7 @@ namespace Karma glm::mat4 objectMatrix = scene->GetSMActors()[0]->GetTransform().ToMatrixWithScale(); + KarmaGuizmo::Enable(true); KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::WORLD, glm::value_ptr(objectMatrix)); } From 9e07e703407d045bdf70480d8b61b45b32a23df3 Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 15 Feb 2026 15:44:47 +0530 Subject: [PATCH 104/107] KarmaGizmo induced translation courtesy cherno --- Karma/src/Karma/Application.cpp | 6 ++++-- Pranjal/Source/Private/KarmaGuiMesa.cpp | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Karma/src/Karma/Application.cpp b/Karma/src/Karma/Application.cpp index 62b697d7..19408b55 100644 --- a/Karma/src/Karma/Application.cpp +++ b/Karma/src/Karma/Application.cpp @@ -113,6 +113,10 @@ namespace Karma { GEngine->Tick(deltaTime, false); } + + // Ponder over the sequence of this updation + // this includes glfwPollEvents() + m_Window->OnUpdate(); for (auto layer : *m_LayerStack) { @@ -128,8 +132,6 @@ namespace Karma } m_KarmaGuiLayer->End(); - - m_Window->OnUpdate(); } } diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index feefccfd..4817b983 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -542,7 +542,7 @@ namespace Karma KarmaGui::SetItemAllowOverlap(); } - KGVec2 pos = KarmaGui::GetCursorScreenPos(); + KGVec2 pos = KarmaGui::GetWindowPos(); KGDrawList* drawList = KarmaGui::GetWindowDrawList(); drawList->AddImage((void*)backgroundImageTextureID, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); @@ -551,11 +551,13 @@ namespace Karma drawList->AddImage((void*)textureID3D, pos, KGVec2(pos.x + window->Size.x, pos.y + window->Size.y)); } - KarmaGuizmo::SetDrawlist(KarmaGui::GetBackgroundDrawList()); - KarmaGuizmo::SetRect(pos.x, pos.y, window->Size.x, window->Size.y); + KarmaGuizmo::SetDrawlist(); + KarmaGuizmo::SetRect(pos.x, pos.y + window->TitleBarHeight(), window->Size.x, window->Size.y - window->TitleBarHeight()); if(scene->GetSMActors().size() > 0) { + KarmaGuizmo::SetOrthographic(false); + std::shared_ptr sceneCamera = scene->GetSceneCamera(); glm::mat4 viewMatrix = sceneCamera->GetViewMatirx(); @@ -567,7 +569,15 @@ namespace Karma glm::mat4 objectMatrix = scene->GetSMActors()[0]->GetTransform().ToMatrixWithScale(); KarmaGuizmo::Enable(true); - KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::WORLD, glm::value_ptr(objectMatrix)); + KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::LOCAL, glm::value_ptr(objectMatrix)); + + if(KarmaGuizmo::IsUsing()) + { + FTransform transform = scene->GetSMActors()[0]->GetTransform(); + transform.SetTranslation(glm::vec3(objectMatrix[3])); + + scene->GetSMActors()[0]->SetActorTransform(transform); + } } scene->SetRenderWindow(window); From 30aa22a6f477796b0e0f17463ee952c7b930d11a Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Sun, 15 Feb 2026 17:48:30 +0530 Subject: [PATCH 105/107] Scene hierarchy basic functional --- Pranjal/Source/Private/KarmaGuiMesa.cpp | 37 +++++++++++++++++-------- Pranjal/Source/Public/KarmaGuiMesa.h | 3 +- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 4817b983..3f658b6c 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -38,6 +38,7 @@ namespace Karma std::shared_ptr s_MesaLogFormatter = nullptr; bool KarmaGuiMesa::m_EditorInitialized = false; bool KarmaGuiMesa::m_RefreshRenderingResources = false; + AStaticMeshActor* KarmaGuiMesa::m_SelectedSMActor = nullptr; WindowManipulationGaugeData KarmaGuiMesa::m_3DExhibitor; WindowManipulationGaugeData KarmaGuiMesa::m_MemoryExhibitor; @@ -102,7 +103,7 @@ namespace Karma // 4. A panel for scene hierarchy and whatnot { - DrawKarmaSceneHierarchyPanelMesa(); + DrawKarmaSceneHierarchyPanelMesa(scene); } // 5. A window for 3D rendering part @@ -554,10 +555,8 @@ namespace Karma KarmaGuizmo::SetDrawlist(); KarmaGuizmo::SetRect(pos.x, pos.y + window->TitleBarHeight(), window->Size.x, window->Size.y - window->TitleBarHeight()); - if(scene->GetSMActors().size() > 0) + if(scene->GetSMActors().size() > 0 && m_SelectedSMActor != nullptr) { - KarmaGuizmo::SetOrthographic(false); - std::shared_ptr sceneCamera = scene->GetSceneCamera(); glm::mat4 viewMatrix = sceneCamera->GetViewMatirx(); @@ -566,17 +565,17 @@ namespace Karma float* projectionPtr = glm::value_ptr(projectionMatrix); projectionPtr[5] *= -1.f; - glm::mat4 objectMatrix = scene->GetSMActors()[0]->GetTransform().ToMatrixWithScale(); + glm::mat4 objectMatrix = m_SelectedSMActor->GetTransform().ToMatrixWithScale(); KarmaGuizmo::Enable(true); KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::LOCAL, glm::value_ptr(objectMatrix)); if(KarmaGuizmo::IsUsing()) { - FTransform transform = scene->GetSMActors()[0]->GetTransform(); + FTransform transform = m_SelectedSMActor->GetTransform(); transform.SetTranslation(glm::vec3(objectMatrix[3])); - scene->GetSMActors()[0]->SetActorTransform(transform); + m_SelectedSMActor->SetActorTransform(transform); } } @@ -618,14 +617,30 @@ namespace Karma KarmaGui::End(); } - void KarmaGuiMesa::DrawKarmaSceneHierarchyPanelMesa() + void KarmaGuiMesa::DrawKarmaSceneHierarchyPanelMesa(std::shared_ptr scene) { KarmaGui::SetNextWindowSize(KGVec2(500, 400), KGGuiCond_FirstUseEver); KarmaGui::Begin("Scene Hierarchy"); - KarmaGui::Text("Some Stuff 1"); - KarmaGui::Text("Some stuff 2"); - KarmaGui::Text("lumbdaa"); + + for(const auto& smActor : scene->GetSMActors()) + { + const char* sceneElement = smActor->GetName().c_str(); + + KGGuiTreeNodeFlags_ flags = (smActor == m_SelectedSMActor) ? KGGuiTreeNodeFlags_Selected : KGGuiTreeNodeFlags_Bullet; + bool opened = KarmaGui::TreeNodeEx(sceneElement, flags); + + if(KarmaGui::IsItemClicked()) + { + m_SelectedSMActor = smActor; + } + + if(opened) + { + KarmaGui::TreePop(); + } + } + KarmaGui::End(); } diff --git a/Pranjal/Source/Public/KarmaGuiMesa.h b/Pranjal/Source/Public/KarmaGuiMesa.h index f156e232..b12e24e3 100644 --- a/Pranjal/Source/Public/KarmaGuiMesa.h +++ b/Pranjal/Source/Public/KarmaGuiMesa.h @@ -280,7 +280,7 @@ namespace Karma static void DrawKarmaMainMenuBarMesa(); static void DrawMainMenuFileListMesa(); static void DrawKarmaLogMesa(KGGuiID mainMesaDockID); - static void DrawKarmaSceneHierarchyPanelMesa(); + static void DrawKarmaSceneHierarchyPanelMesa(std::shared_ptr scene); static void Draw3DModelExhibitorMesa(std::shared_ptr scene); static void DrawContentBrowser(const std::function< void(std::string) >& openSceneCallback); static void DrawMemoryExhibitor(); @@ -320,6 +320,7 @@ namespace Karma static WindowManipulationGaugeData m_MemoryExhibitor; static bool m_EditorInitialized; static bool m_RefreshRenderingResources; + static class AStaticMeshActor* m_SelectedSMActor; // Content browser static std::filesystem::path m_CurrentDirectory; From 950606aa7fdf7c4547c47ca303aca09534e3d9bc Mon Sep 17 00:00:00 2001 From: the_cowboy Date: Mon, 16 Feb 2026 06:35:14 +0530 Subject: [PATCH 106/107] transform widget : Phase I --- Karma/src/Karma/Ganit/Transform.cpp | 21 ++++++++++- Karma/src/Karma/Ganit/Transform.h | 2 + Pranjal/Source/Private/KarmaGuiMesa.cpp | 50 +++++++++++++++++++++++-- Pranjal/Source/Public/KarmaGuiMesa.h | 1 + 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/Karma/src/Karma/Ganit/Transform.cpp b/Karma/src/Karma/Ganit/Transform.cpp index 7f5d6071..7784553d 100644 --- a/Karma/src/Karma/Ganit/Transform.cpp +++ b/Karma/src/Karma/Ganit/Transform.cpp @@ -1,4 +1,5 @@ #include "Transform.h" +#include namespace Karma { @@ -153,7 +154,25 @@ namespace Karma ReturnMatrix *= glm::mat4_cast(m_Rotation.ToQuat()); ReturnMatrix = glm::translate(ReturnMatrix, m_Translation); - + return ReturnMatrix; } + + void FTransform::ToTransform(const glm::mat4 Matrix) + { + glm::vec3 scale(0), translation(0), skew(0); + glm::quat rotation; + glm::vec4 perspective(0); + + glm::decompose(Matrix, scale, rotation, translation, skew, perspective); + + TRotator rotator = TRotator(glm::eulerAngles(rotation)); + + //FTransform transform = FTransform::Identity(); + + m_Translation = translation; + m_Rotation = rotator; + + m_Scale3D = scale; + } } diff --git a/Karma/src/Karma/Ganit/Transform.h b/Karma/src/Karma/Ganit/Transform.h index d4084864..53a16bcc 100644 --- a/Karma/src/Karma/Ganit/Transform.h +++ b/Karma/src/Karma/Ganit/Transform.h @@ -241,6 +241,8 @@ namespace Karma } glm::mat4 ToMatrixWithScale() const; + + void ToTransform(const glm::mat4 Matrix); public: static FTransform m_Identity; diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 3f658b6c..63312941 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -53,6 +53,47 @@ namespace Karma DropRectsDraw[n] = KGRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } } + + void KarmaGuiMesa::EditTransform(std::shared_ptr scene) + { + if(m_SelectedSMActor == nullptr) + { + return; + } + + float matrixTranslation[3], matrixRotation[3], matrixScale[3]; + FTransform transform = m_SelectedSMActor->GetTransform(); + + matrixTranslation[0] = transform.GetTranslation().x; + matrixTranslation[1] = transform.GetTranslation().y; + matrixTranslation[2] = transform.GetTranslation().z; + + matrixRotation[0] = transform.GetRotation().m_Roll; + matrixRotation[1] = transform.GetRotation().m_Pitch; + matrixRotation[2] = transform.GetRotation().m_Yaw; + + matrixScale[0] = transform.GetScale3D().x; + matrixScale[1] = transform.GetScale3D().y; + matrixScale[2] = transform.GetScale3D().z; + + KarmaGui::InputFloat3("Tr", matrixTranslation, "%.2f"); + KarmaGui::InputFloat3("Rt", matrixRotation, "%.2f"); + + KarmaGui::InputFloat3("Sc", matrixScale, "%.2f"); + + glm::vec3 translation(matrixTranslation[0], matrixTranslation[1], matrixTranslation[2]); + transform.SetTranslation(translation); + + glm::vec3 euler(matrixRotation[0], matrixRotation[1], matrixRotation[2]); + TRotator rotation(euler); + + transform.SetRotation(rotation); + + glm::vec3 scale(matrixScale[0], matrixScale[1], matrixScale[2]); + transform.SetScale3D(scale); + + m_SelectedSMActor->SetActorTransform(transform); + } void KarmaGuiMesa::RevealMainFrame(KGGuiID mainMesaDockID, std::shared_ptr scene, const CallbacksFromEditor& editorCallbacks) { @@ -61,15 +102,16 @@ namespace Karma // 2. Show a simple sampling and experiment window { - static bool show = true; + static bool show1 = true; + static bool show2 = true; static float fValue = 0.0f; static int counter = 0; KarmaGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and appeninto it KarmaGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - KarmaGui::Checkbox("Demo Window", &show); // Edit bools storing our window open/close state - KarmaGui::Checkbox("Another Window", &show); + KarmaGui::Checkbox("Demo Window", &show1); // Edit bools storing our window open/close state + KarmaGui::Checkbox("Another Window", &show2); KarmaGui::SliderFloat("float", &fValue, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f //ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a colo @@ -89,6 +131,8 @@ namespace Karma node = nullptr;//ImGui::FindAppropriateNode(window, payloadWindow, boxNumber); KarmaGui::Text("Node ID = %d at position x = %f, y = %f on docking box %d", node != nullptr ? node->ID : 0, KarmaGui::GetMousePos().x, KarmaGui::GetMousePos().y, boxNumber); + + EditTransform(scene); if (payloadWindow) KarmaGui::Text("Karma: Log window is of dimension width = %f und height = %f", payloadWindow->Size.x, payloadWindow->Size.y); diff --git a/Pranjal/Source/Public/KarmaGuiMesa.h b/Pranjal/Source/Public/KarmaGuiMesa.h index b12e24e3..0e513889 100644 --- a/Pranjal/Source/Public/KarmaGuiMesa.h +++ b/Pranjal/Source/Public/KarmaGuiMesa.h @@ -277,6 +277,7 @@ namespace Karma public: // Showtime! static void RevealMainFrame(KGGuiID mainMesaDockID, std::shared_ptr scene, const CallbacksFromEditor& editorCallbacks); + static void EditTransform(std::shared_ptr scene); static void DrawKarmaMainMenuBarMesa(); static void DrawMainMenuFileListMesa(); static void DrawKarmaLogMesa(KGGuiID mainMesaDockID); From 63a74eb671f3fd3619d4d1bd45cec93a3fe0d764 Mon Sep 17 00:00:00 2001 From: pc_boy Date: Mon, 16 Feb 2026 16:57:17 +0530 Subject: [PATCH 107/107] dirty flat und perplexity's position recovery from non uniform scale --- Karma/src/Karma/Ganit/Transform.cpp | 5 ++ Pranjal/Source/Private/KarmaGuiMesa.cpp | 80 ++++++++++++++++--------- Pranjal/Source/Public/KarmaGuiMesa.h | 11 ++++ 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Karma/src/Karma/Ganit/Transform.cpp b/Karma/src/Karma/Ganit/Transform.cpp index 7784553d..a7d1d570 100644 --- a/Karma/src/Karma/Ganit/Transform.cpp +++ b/Karma/src/Karma/Ganit/Transform.cpp @@ -174,5 +174,10 @@ namespace Karma m_Rotation = rotator; m_Scale3D = scale; + + m_Translation[0] = (m_Scale3D[0] != 0) ? m_Translation[0] / m_Scale3D[0] : m_Translation[0]; + m_Translation[1] = (m_Scale3D[1] != 0) ? m_Translation[1] / m_Scale3D[1] : m_Translation[1]; + + m_Translation[2] = (m_Scale3D[2] != 0) ? m_Translation[2] / m_Scale3D[2] : m_Translation[2]; } } diff --git a/Pranjal/Source/Private/KarmaGuiMesa.cpp b/Pranjal/Source/Private/KarmaGuiMesa.cpp index 63312941..b95c3525 100644 --- a/Pranjal/Source/Private/KarmaGuiMesa.cpp +++ b/Pranjal/Source/Private/KarmaGuiMesa.cpp @@ -39,6 +39,7 @@ namespace Karma bool KarmaGuiMesa::m_EditorInitialized = false; bool KarmaGuiMesa::m_RefreshRenderingResources = false; AStaticMeshActor* KarmaGuiMesa::m_SelectedSMActor = nullptr; + TransformCache KarmaGuiMesa::m_SelectedSMActorTransformCache; WindowManipulationGaugeData KarmaGuiMesa::m_3DExhibitor; WindowManipulationGaugeData KarmaGuiMesa::m_MemoryExhibitor; @@ -64,35 +65,58 @@ namespace Karma float matrixTranslation[3], matrixRotation[3], matrixScale[3]; FTransform transform = m_SelectedSMActor->GetTransform(); - matrixTranslation[0] = transform.GetTranslation().x; - matrixTranslation[1] = transform.GetTranslation().y; - matrixTranslation[2] = transform.GetTranslation().z; + matrixTranslation[0] = m_SelectedSMActorTransformCache.translation[0] = transform.GetTranslation().x; + matrixTranslation[1] = m_SelectedSMActorTransformCache.translation[1] = transform.GetTranslation().y; + matrixTranslation[2] = m_SelectedSMActorTransformCache.translation[2] = transform.GetTranslation().z; - matrixRotation[0] = transform.GetRotation().m_Roll; - matrixRotation[1] = transform.GetRotation().m_Pitch; - matrixRotation[2] = transform.GetRotation().m_Yaw; + matrixRotation[0] = m_SelectedSMActorTransformCache.rotation[0] = transform.GetRotation().m_Roll; + matrixRotation[1] = m_SelectedSMActorTransformCache.rotation[1] = transform.GetRotation().m_Pitch; + matrixRotation[2] = m_SelectedSMActorTransformCache.rotation[2] = transform.GetRotation().m_Yaw; - matrixScale[0] = transform.GetScale3D().x; - matrixScale[1] = transform.GetScale3D().y; - matrixScale[2] = transform.GetScale3D().z; + matrixScale[0] = m_SelectedSMActorTransformCache.scale[0] = transform.GetScale3D().x; + matrixScale[1] = m_SelectedSMActorTransformCache.scale[1] = transform.GetScale3D().y; + matrixScale[2] = m_SelectedSMActorTransformCache.scale[2] = transform.GetScale3D().z; KarmaGui::InputFloat3("Tr", matrixTranslation, "%.2f"); KarmaGui::InputFloat3("Rt", matrixRotation, "%.2f"); KarmaGui::InputFloat3("Sc", matrixScale, "%.2f"); + + if (matrixTranslation[0] != m_SelectedSMActorTransformCache.translation[0] || matrixTranslation[1] != m_SelectedSMActorTransformCache.translation[1] || matrixTranslation[2] != m_SelectedSMActorTransformCache.translation[2] || + matrixRotation[0] != m_SelectedSMActorTransformCache.rotation[0] || matrixRotation[1] != m_SelectedSMActorTransformCache.rotation[1] || matrixRotation[2] != m_SelectedSMActorTransformCache.rotation[2] || + matrixScale[0] != m_SelectedSMActorTransformCache.scale[0] || matrixScale[1] != m_SelectedSMActorTransformCache.scale[1] || matrixScale[2] != m_SelectedSMActorTransformCache.scale[2]) + { + m_SelectedSMActorTransformCache.translation[0] = matrixTranslation[0]; + m_SelectedSMActorTransformCache.translation[1] = matrixTranslation[1]; + m_SelectedSMActorTransformCache.translation[2] = matrixTranslation[2]; + + m_SelectedSMActorTransformCache.rotation[0] = matrixRotation[0]; + m_SelectedSMActorTransformCache.rotation[1] = matrixRotation[1]; + m_SelectedSMActorTransformCache.rotation[2] = matrixRotation[2]; + + m_SelectedSMActorTransformCache.scale[0] = matrixScale[0]; + m_SelectedSMActorTransformCache.scale[1] = matrixScale[1]; + m_SelectedSMActorTransformCache.scale[2] = matrixScale[2]; + + m_SelectedSMActorTransformCache.bIsDirty = true; + } - glm::vec3 translation(matrixTranslation[0], matrixTranslation[1], matrixTranslation[2]); - transform.SetTranslation(translation); - - glm::vec3 euler(matrixRotation[0], matrixRotation[1], matrixRotation[2]); - TRotator rotation(euler); - - transform.SetRotation(rotation); - - glm::vec3 scale(matrixScale[0], matrixScale[1], matrixScale[2]); - transform.SetScale3D(scale); - - m_SelectedSMActor->SetActorTransform(transform); + if (m_SelectedSMActorTransformCache.bIsDirty) + { + glm::vec3 translation(matrixTranslation[0], matrixTranslation[1], matrixTranslation[2]); + transform.SetTranslation(translation); + + glm::vec3 euler(matrixRotation[0], matrixRotation[1], matrixRotation[2]); + TRotator rotation(euler); + + transform.SetRotation(rotation); + + glm::vec3 scale(matrixScale[0], matrixScale[1], matrixScale[2]); + transform.SetScale3D(scale); + + m_SelectedSMActor->SetActorTransform(transform); + m_SelectedSMActorTransformCache.bIsDirty = false; + } } void KarmaGuiMesa::RevealMainFrame(KGGuiID mainMesaDockID, std::shared_ptr scene, const CallbacksFromEditor& editorCallbacks) @@ -609,15 +633,15 @@ namespace Karma float* projectionPtr = glm::value_ptr(projectionMatrix); projectionPtr[5] *= -1.f; - glm::mat4 objectMatrix = m_SelectedSMActor->GetTransform().ToMatrixWithScale(); - - KarmaGuizmo::Enable(true); - KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::TRANSLATE, KarmaGuizmo::LOCAL, glm::value_ptr(objectMatrix)); + FTransform operationalTransform = m_SelectedSMActor->GetTransform(); + + glm::mat4 objectMatrix = operationalTransform.ToMatrixWithScale(); + bool bManipulate = KarmaGuizmo::Manipulate(glm::value_ptr(viewMatrix), projectionPtr, KarmaGuizmo::UNIVERSAL, KarmaGuizmo::WORLD, glm::value_ptr(objectMatrix)); - if(KarmaGuizmo::IsUsing()) + if(KarmaGuizmo::IsUsing() && bManipulate) { - FTransform transform = m_SelectedSMActor->GetTransform(); - transform.SetTranslation(glm::vec3(objectMatrix[3])); + FTransform transform; + transform.ToTransform(objectMatrix); m_SelectedSMActor->SetActorTransform(transform); } diff --git a/Pranjal/Source/Public/KarmaGuiMesa.h b/Pranjal/Source/Public/KarmaGuiMesa.h index 0e513889..580b9b80 100644 --- a/Pranjal/Source/Public/KarmaGuiMesa.h +++ b/Pranjal/Source/Public/KarmaGuiMesa.h @@ -272,6 +272,15 @@ namespace Karma } }; + struct TransformCache + { + float translation[3]; + float rotation[3]; + float scale[3]; + + bool bIsDirty = false; + }; + class KarmaGuiMesa { public: @@ -322,6 +331,8 @@ namespace Karma static bool m_EditorInitialized; static bool m_RefreshRenderingResources; static class AStaticMeshActor* m_SelectedSMActor; + static TransformCache m_SelectedSMActorTransformCache; + // Content browser static std::filesystem::path m_CurrentDirectory;