# Juliet\include\Graphics\Graphics ## Source Files - Header: `Juliet\include\Graphics\Graphics.h` - Source: `Juliet\src\Graphics\Graphics.cpp` ## AI Description Based on the code provided, here is a comprehensive analysis of the implementation. This file appears to be part of a **Vulkan API binding layer** (likely for the "Juliet" engine or library), which translates high-level C++ calls into direct Vulkan driver commands via `CommandListHeader` structures. ### 1. Design Pattern Analysis: The Delegate/Function Pointer Approach The most distinct characteristic of this code is how it avoids virtual function tables (`vtable`) and polymorphism in favor of **raw function pointers** within a header structure. * **Structure:** You have a `CommandListHeader` (likely defined elsewhere). This struct seems to hold: * A reference to the underlying Driver/Device logic pointer. * Optional Function Pointers (e.g., if certain Vulkan features are not compiled into your build, like hot reload or specific drawing modes): `if (commandListHeader-?Device-?DrawIndexedPrimitives)`. * **Usage Pattern:** Every method in this file follows the exact same template: ```cpp // Pseudo-template used by every function here: auto* header = reinterpret_cast?CommandListHeader*?(ptr); if (header-?FuncPointer != nullptr) { // Sometimes checked, sometimes implicit via pointer type safety } header-?Device-?Function(commandLineArg); ``` **Pros:** * **Extremely Lightweight:** No overhead of vtable lookups or virtual calls. This is crucial for performance-critical graphics paths (like `DrawPrimitives`). * **Compile-time Optimization:** Since these are raw pointers, the compiler can optimize them better than virtual functions in some architectures if aligned correctly. **Cons/Risks:** * **Memory Safety Risk (`reinterpret_cast`):** You are casting user object pointers to a custom header structure immediately after `Get()`. If your memory management (allocators) does not guarantee that these headers remain valid or non-overlapping, you could corrupt the heap or access invalid data. Ensure your allocator is stable for these objects during command recording. * **Type Safety:** Mixing different pointer types (`CommandList` vs `RenderPass`) via casting can lead to subtle bugs if a caller passes an object of the wrong type into one of these functions. --- ### 2. Code Review ? Observations by Category #### A. Command Recording Functions (`SetViewPort`, `DrawPrimitives`, etc.) * **Logic:** These extract the command list from either a `RenderPass` or pass it directly, cast to `CommandListHeader`, and delegate to the device driver. * **Optimization Note:** In your `WaitUntilGPUIsIdle`, you are passing `device-?Driver`. However, most other functions (`SetViewPort`, etc.) likely operate on a specific *queue family*. Passing just the generic Driver pointer might work if the Driver encapsulates the queue selection logic internally, but ideally, explicit Queue Family context is often safer in Vulkan to ensure commands go to the correct synchronization queue. * **Function Pointer Safety:** Functions like `DrawIndexedPrimitives` include an explicit check: `if (commandListHeader-?Device-?DrawIndexedPrimitives)`. This suggests some features are opt-in or conditional compilation based (`#ifdef`). Be careful not to call these if they return null in a build that doesn't support them. #### B. Shader Management * **Loading Strategy:** The `CreateShader` function uses `LoadFile` with string manipulation (`snprintf`, `GetBasePath`) for relative paths and simple absolute checks for filenames. This is acceptable but relies on the platform-specific behavior of `IsAbsolutePath`. * **Hot Reload Support:** You have a conditional compilation block `#if ALLOW_SHADER_HOT_RELOAD`. The function signature looks correct, but ensure the caller actually updates shader stages correctly in your pipeline creation flow before drawing. #### C. Resource Management (Buffers ? Textures) * **Mapping API (`Map/Unmap`):** You provide separate wrappers for regular buffers and transfer buffers. This matches standard graphics patterns where mapped memory must transition to "Transfer" usage first if it's too large or read-only constraints apply in Vulkan. * **Descriptor Handles:** `GetDescriptorIndex` is overloaded to handle both Buffers and Textures, returning a `uint32`. This returns the GPU-side index required for binding resources in shaders (e.g., uniform buffers). #### D. Potential Issues ? Recommendations 1. **Memory Leaks / Double Free Risk:** * The code uses smart pointers (`NonNullPtr`). If your engine relies on RAII to manage these objects, this looks safe. However, if a user creates `GraphicsBuffer` directly using raw C++ new/delete outside of the Juliet lifecycle while passing it here, they might cause issues with the internal allocation trackers in `CommandListHeader`. 2. **Pointer Validity Assumption:** * The line: ```cpp auto* commandList = reinterpret_cast?GPUPass*?(renderPass.Get())-?CommandList; // Cast assumed to exist here? Or is GPUPass a wrapper around CommandListContainer? auto* commandListHeader = reinterpret_cast?CommandListHeader*?(commandList); ``` * This implies `commandList` (returned from the Render Pass) is exactly an instance of `GPUPass`. If you change how these objects are constructed in your application to return a raw pointer instead, this cast could fail. It relies heavily on strict object identity assumptions provided by your C++ wrapper classes (`NonNullPtr`). 3. **Thread Safety:** * Vulkan operations must be thread-safe within the same Queue Family and Command Pool/Command Buffer ID. Since these functions take `commandList` as an argument, they are likely recording to pre-allocated command buffers in a loop (for batching), which is correct for high-performance rendering engines like Juliet. 4. **Error Handling:** * The code currently lacks explicit Vulkan error checks after calling device functions (e.g., checking `vkResult` or internal driver errors). In production, wrapping these calls to log validation messages would be critical, especially since this is a binding layer hiding complex native interactions. ### Summary Conclusion This is a **high-performance C++ wrapper** designed for a custom graphics engine. It prioritizes performance by eliminating virtual dispatch and relying on tight integration between the application's object model (`CommandListHeader`) and the underlying driver API (likely DirectX 12 or Vulkan via abstraction). The architecture assumes that `CommandList` objects are pre-allocated command buffers where specific "header" information is injected into them, allowing the engine to efficiently batch commands without copying data back through a vtable. ## Symbols ### Namespace `Juliet` #### Classes, Structs & Unions - `struct IndirectDrawCommand` - `struct IndexedIndirectDrawCommand` - `struct IndirectDispatchCommand` - `struct GraphicsViewPort` #### Enums - `enum class QueueType` - `enum class IndexFormat` - `enum struct` - `enum struct` #### Functions & Methods - `extern JULIET_API GraphicsDevice* CreateGraphicsDevice(GraphicsConfig config)` - `extern JULIET_API void DestroyGraphicsDevice(NonNullPtr device)` - `// Attach To Window extern JULIET_API bool AttachToWindow(NonNullPtr device, NonNullPtr window)` - `extern JULIET_API void DetachFromWindow(NonNullPtr device, NonNullPtr window)` - `// SwapChain extern JULIET_API bool AcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture)` - `extern JULIET_API bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture)` - `extern JULIET_API bool WaitForSwapchain(NonNullPtr device, NonNullPtr window)` - `extern JULIET_API TextureFormat GetSwapChainTextureFormat(NonNullPtr device, NonNullPtr window)` - `// Textures extern JULIET_API Texture* CreateTexture(NonNullPtr device, const TextureCreateInfo& createInfo)` - `extern JULIET_API void DestroyTexture(NonNullPtr device, NonNullPtr texture)` - `// Command List extern JULIET_API CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType = QueueType::Graphics)` - `extern JULIET_API void SubmitCommandLists(NonNullPtr commandList)` - `// RenderPass extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo, DepthStencilTargetInfo* depthStencilTargetInfo = nullptr)` - `extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount, DepthStencilTargetInfo* depthStencilTargetInfo = nullptr)` - `extern JULIET_API void EndRenderPass(NonNullPtr renderPass)` - `extern JULIET_API void SetGraphicsViewPort(NonNullPtr renderPass, const GraphicsViewPort& viewPort)` - `extern JULIET_API void SetScissorRect(NonNullPtr renderPass, const Rectangle& rectangle)` - `extern JULIET_API void SetBlendConstants(NonNullPtr renderPass, FColor blendConstants)` - `extern JULIET_API void SetStencilReference(NonNullPtr renderPass, uint8 reference)` - `extern JULIET_API void BindGraphicsPipeline(NonNullPtr renderPass, NonNullPtr graphicsPipeline)` - `extern JULIET_API void DrawPrimitives(NonNullPtr renderPass, uint32 numVertices, uint32 numInstances, uint32 firstVertex, uint32 firstInstance)` - `extern JULIET_API void DrawIndexedPrimitives(NonNullPtr renderPass, uint32 numIndices, uint32 numInstances, uint32 firstIndex, uint32 vertexOffset, uint32 firstInstance)` - `extern JULIET_API void SetIndexBuffer(NonNullPtr commandList, NonNullPtr buffer, IndexFormat format, size_t indexCount, index_t offset)` - `extern JULIET_API void SetPushConstants(NonNullPtr commandList, ShaderStage stage, uint32 rootParameterIndex, uint32 numConstants, const void* constants)` - `// Fences extern JULIET_API bool WaitUntilGPUIsIdle(NonNullPtr device)` - `// Shaders extern JULIET_API Shader* CreateShader(NonNullPtr device, String filename, ShaderCreateInfo& shaderCreateInfo)` - `extern JULIET_API void DestroyShader(NonNullPtr device, NonNullPtr shader)` - `// Pipelines extern JULIET_API GraphicsPipeline* CreateGraphicsPipeline(NonNullPtr device, const GraphicsPipelineCreateInfo& createInfo)` - `extern JULIET_API void DestroyGraphicsPipeline(NonNullPtr device, NonNullPtr graphicsPipeline)` - `#if ALLOW_SHADER_HOT_RELOAD // Allows updating the graphics pipeline shaders. Can update either one or both shaders. extern JULIET_API bool UpdateGraphicsPipelineShaders(NonNullPtr device, NonNullPtr graphicsPipeline, Shader* optional_vertexShader, Shader* optional_fragmentShader)` - `#endif // Buffers extern JULIET_API GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr device, const BufferCreateInfo& createInfo)` - `extern JULIET_API GraphicsTransferBuffer* CreateGraphicsTransferBuffer(NonNullPtr device, const TransferBufferCreateInfo& createInfo)` - `extern JULIET_API void* MapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API void UnmapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API void* MapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API void UnmapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API void CopyBuffer(NonNullPtr commandList, NonNullPtr dst, NonNullPtr src, size_t size, size_t dstOffset = 0, size_t srcOffset = 0)` - `extern JULIET_API void CopyBufferToTexture(NonNullPtr commandList, NonNullPtr dst, NonNullPtr src)` - `extern JULIET_API void TransitionBufferToReadable(NonNullPtr commandList, NonNullPtr buffer)` - `extern JULIET_API uint32 GetDescriptorIndex(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API uint32 GetDescriptorIndex(NonNullPtr device, NonNullPtr texture)` - `extern JULIET_API void DestroyGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` - `extern JULIET_API void DestroyGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)`