aboutsummaryrefslogtreecommitdiff
path: root/src/Platform
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-20 02:27:02 +0200
committerhachem <im@hachem.wtf>2026-08-20 02:40:07 +0200
commit5b361d81dbd2af0d0e99b9eb1adebea76ff05db0 (patch)
tree34db1df17e8c7248aeeb92156d635d31c2484e3f /src/Platform
parente3abaaf59777258ba06705135a0cafcb5918ad12 (diff)
[chore]: MASSIVE refactor + more vulkan bs
Diffstat (limited to 'src/Platform')
-rw-r--r--src/Platform/Metal/MetalContext.h16
-rw-r--r--src/Platform/Metal/MetalContext.mm124
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.cpp246
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.h35
-rw-r--r--src/Platform/OpenGL/OpenGLIndexBuffer.cpp28
-rw-r--r--src/Platform/OpenGL/OpenGLIndexBuffer.h22
-rw-r--r--src/Platform/OpenGL/OpenGLRendererAPI.cpp118
-rw-r--r--src/Platform/OpenGL/OpenGLRendererAPI.h44
-rw-r--r--src/Platform/OpenGL/OpenGLShader.cpp302
-rw-r--r--src/Platform/OpenGL/OpenGLShader.h58
-rw-r--r--src/Platform/OpenGL/OpenGLTexture.cpp272
-rw-r--r--src/Platform/OpenGL/OpenGLTexture.h68
-rw-r--r--src/Platform/OpenGL/OpenGLUniformBuffer.cpp29
-rw-r--r--src/Platform/OpenGL/OpenGLUniformBuffer.h21
-rw-r--r--src/Platform/OpenGL/OpenGLVertexArray.cpp58
-rw-r--r--src/Platform/OpenGL/OpenGLVertexArray.h41
-rw-r--r--src/Platform/OpenGL/OpenGLVertexBuffer.cpp35
-rw-r--r--src/Platform/OpenGL/OpenGLVertexBuffer.h25
-rw-r--r--src/Platform/Vulkan/VulkanContext.cpp789
-rw-r--r--src/Platform/Vulkan/VulkanContext.h41
-rw-r--r--src/Platform/Vulkan/VulkanIndexBuffer.cpp25
-rw-r--r--src/Platform/Vulkan/VulkanIndexBuffer.h22
-rw-r--r--src/Platform/Vulkan/VulkanRenderer.cpp1235
-rw-r--r--src/Platform/Vulkan/VulkanRenderer.h40
-rw-r--r--src/Platform/Vulkan/VulkanRendererAPI.cpp86
-rw-r--r--src/Platform/Vulkan/VulkanRendererAPI.h38
-rw-r--r--src/Platform/Vulkan/VulkanShader.cpp146
-rw-r--r--src/Platform/Vulkan/VulkanShader.h52
-rw-r--r--src/Platform/Vulkan/VulkanTexture.cpp73
-rw-r--r--src/Platform/Vulkan/VulkanTexture.h60
-rw-r--r--src/Platform/Vulkan/VulkanUniformBuffer.cpp25
-rw-r--r--src/Platform/Vulkan/VulkanUniformBuffer.h19
-rw-r--r--src/Platform/Vulkan/VulkanVertexArray.cpp36
-rw-r--r--src/Platform/Vulkan/VulkanVertexArray.h28
-rw-r--r--src/Platform/Vulkan/VulkanVertexBuffer.cpp34
-rw-r--r--src/Platform/Vulkan/VulkanVertexBuffer.h26
36 files changed, 0 insertions, 4317 deletions
diff --git a/src/Platform/Metal/MetalContext.h b/src/Platform/Metal/MetalContext.h
deleted file mode 100644
index 5d9eb08..0000000
--- a/src/Platform/Metal/MetalContext.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-// Pure-C++ interface to the Metal backend (no Objective-C leaks into the rest
-// of the engine). Implemented in MetalContext.mm.
-namespace Donut
-{
- // Phase 1 bring-up: creates the default Metal device + command queue and
- // logs its capabilities, proving the Metal toolchain and build integration
- // work natively on Apple Silicon. This grows into the Metal compute island
- // that runs the geodesic ray tracer in real time.
- bool MetalProbe();
-
- // Compiles + dispatches a trivial compute kernel and validates the read-back,
- // proving the Metal compute path the geodesic ray tracer will run on.
- bool MetalComputeSelfTest();
-}
diff --git a/src/Platform/Metal/MetalContext.mm b/src/Platform/Metal/MetalContext.mm
deleted file mode 100644
index 1bb9e70..0000000
--- a/src/Platform/Metal/MetalContext.mm
+++ /dev/null
@@ -1,124 +0,0 @@
-#import <Metal/Metal.h>
-#import <Foundation/Foundation.h>
-
-#include "MetalContext.h"
-#include "Core/Log.h"
-
-#include <vector>
-
-namespace Donut
-{
- bool MetalProbe()
- {
- @autoreleasepool
- {
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
- if (device == nil)
- {
- DONUT_ERROR("Metal: no default device available");
- return false;
- }
-
- id<MTLCommandQueue> queue = [device newCommandQueue];
- const char* name = [[device name] UTF8String];
-
- DONUT_INFO("Metal device: {}", name ? name : "(unknown)");
- DONUT_INFO("Metal: unified memory = {}, max threads/threadgroup = {}",
- device.hasUnifiedMemory ? "yes" : "no",
- (unsigned long)device.maxThreadsPerThreadgroup.width);
-
- if (queue == nil)
- {
- DONUT_WARN("Metal: failed to create command queue");
- return false;
- }
-
- return true;
- }
- }
-
- // Compiles a trivial compute kernel, dispatches it over a small texture, and
- // reads the result back to confirm the full Metal compute path works: source
- // compilation, pipeline state, command encoding, dispatch, and shared-memory
- // read-back. This is the mechanism the geodesic ray tracer will run on.
- bool MetalComputeSelfTest()
- {
- @autoreleasepool
- {
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
- id<MTLCommandQueue> queue = [device newCommandQueue];
- if (device == nil || queue == nil)
- return false;
-
- NSString* src =
- @"#include <metal_stdlib>\n"
- "using namespace metal;\n"
- "kernel void selfTest(texture2d<float, access::write> outTex [[texture(0)]],\n"
- " uint2 gid [[thread_position_in_grid]])\n"
- "{\n"
- " uint w = outTex.get_width();\n"
- " uint h = outTex.get_height();\n"
- " if (gid.x >= w || gid.y >= h) return;\n"
- " outTex.write(float4(float(gid.x) / float(w - 1),\n"
- " float(gid.y) / float(h - 1), 0.5, 1.0), gid);\n"
- "}\n";
-
- NSError* err = nil;
- id<MTLLibrary> lib = [device newLibraryWithSource:src options:nil error:&err];
- if (lib == nil)
- {
- DONUT_ERROR("Metal self-test: kernel compile failed: {}",
- err ? [[err localizedDescription] UTF8String] : "unknown");
- return false;
- }
-
- id<MTLFunction> fn = [lib newFunctionWithName:@"selfTest"];
- id<MTLComputePipelineState> pipeline =
- [device newComputePipelineStateWithFunction:fn error:&err];
- if (pipeline == nil)
- {
- DONUT_ERROR("Metal self-test: pipeline creation failed");
- return false;
- }
-
- const uint32_t W = 64, H = 64;
- MTLTextureDescriptor* desc =
- [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
- width:W
- height:H
- mipmapped:NO];
- desc.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
- desc.storageMode = MTLStorageModeShared;
- id<MTLTexture> tex = [device newTextureWithDescriptor:desc];
-
- id<MTLCommandBuffer> cb = [queue commandBuffer];
- id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
- [enc setComputePipelineState:pipeline];
- [enc setTexture:tex atIndex:0];
-
- MTLSize tg = MTLSizeMake(16, 16, 1);
- MTLSize grid = MTLSizeMake(W, H, 1);
- [enc dispatchThreads:grid threadsPerThreadgroup:tg];
- [enc endEncoding];
- [cb commit];
- [cb waitUntilCompleted];
-
- // Read back the far corner; the kernel writes (~1, ~1, 0.5, 1) there.
- std::vector<uint8_t> px(W * H * 4);
- [tex getBytes:px.data()
- bytesPerRow:W * 4
- fromRegion:MTLRegionMake2D(0, 0, W, H)
- mipmapLevel:0];
-
- size_t corner = ((size_t)(H - 1) * W + (W - 1)) * 4;
- DONUT_INFO("Metal compute self-test: corner pixel RGBA = ({}, {}, {}, {})",
- (int)px[corner + 0], (int)px[corner + 1],
- (int)px[corner + 2], (int)px[corner + 3]);
-
- bool ok = px[corner + 0] > 250 && px[corner + 1] > 250 &&
- px[corner + 3] == 255;
- DONUT_INFO("Metal compute self-test: {}", ok ? "PASS" : "FAIL");
- return ok;
- }
- }
-}
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/Platform/OpenGL/OpenGLFramebuffer.cpp
deleted file mode 100644
index c46e0e6..0000000
--- a/src/Platform/OpenGL/OpenGLFramebuffer.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-#include "OpenGLFramebuffer.h"
-#include "Core/Log.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- namespace Utils
- {
- static GLenum TextureTarget(bool multisampled)
- {
- return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
- }
-
- static void BindTexture(bool multisampled, uint32_t id)
- {
- glBindTexture(TextureTarget(multisampled), id);
- }
-
- static void CreateTextures(bool multisampled, uint32_t* outID, uint32_t count)
- {
- // glCreateTextures is 4.5 DSA; macOS caps at 4.1. Callers bind each
- // texture (with the correct target) before use.
- glGenTextures(count, outID);
- }
-
- static void AttachColorTexture(uint32_t id, int samples, GLenum internalFormat, GLenum format, uint32_t width, uint32_t height, int index)
- {
- bool multisampled = samples > 1;
- if (multisampled)
- {
- glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_FALSE);
- }
- else
- {
- // Integer color formats require an integer pixel type even when
- // data is null, or macOS's strict core profile rejects the call.
- GLenum type = (format == GL_RED_INTEGER) ? GL_INT : GL_UNSIGNED_BYTE;
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0);
- }
-
- static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachmentType, uint32_t width, uint32_t height)
- {
- bool multisampled = samples > 1;
- if (multisampled)
- {
- glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE);
- }
- else
- {
- // glTexStorage2D is 4.2; use mutable storage for macOS (4.1).
- GLenum depthFormat = (format == GL_DEPTH24_STENCIL8) ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
- GLenum depthType = (format == GL_DEPTH24_STENCIL8) ? GL_UNSIGNED_INT_24_8 : GL_FLOAT;
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, depthFormat, depthType, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, TextureTarget(multisampled), id, 0);
- }
-
- static bool IsDepthFormat(FramebufferTextureFormat format)
- {
- switch (format)
- {
- case FramebufferTextureFormat::DEPTH24STENCIL8: return true;
- }
- return false;
- }
-
- static GLenum DonutFBTextureFormatToGL(FramebufferTextureFormat format)
- {
- switch (format)
- {
- case FramebufferTextureFormat::RGBA8: return GL_RGBA8;
- case FramebufferTextureFormat::RED_INTEGER: return GL_RED_INTEGER;
- }
-
- return 0;
- }
- }
-
- OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec)
- : m_Specification(spec)
- {
- for (auto spec : m_Specification.Attachments.Attachments)
- {
- if (!Utils::IsDepthFormat(spec.TextureFormat))
- m_ColorAttachmentSpecifications.emplace_back(spec);
- else
- m_DepthAttachmentSpecification = spec;
- }
-
- Invalidate();
- }
-
- OpenGLFramebuffer::~OpenGLFramebuffer()
- {
- glDeleteFramebuffers(1, &m_RendererID);
- glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data());
- glDeleteTextures(1, &m_DepthAttachment);
- }
-
- void OpenGLFramebuffer::Invalidate()
- {
- if (m_RendererID)
- {
- glDeleteFramebuffers(1, &m_RendererID);
- glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data());
- glDeleteTextures(1, &m_DepthAttachment);
-
- m_ColorAttachments.clear();
- m_DepthAttachment = 0;
- }
-
- glGenFramebuffers(1, &m_RendererID); // glCreateFramebuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
-
- bool multisample = m_Specification.Samples > 1;
-
- if (m_ColorAttachmentSpecifications.size())
- {
- m_ColorAttachments.resize(m_ColorAttachmentSpecifications.size());
- Utils::CreateTextures(multisample, m_ColorAttachments.data(), static_cast<uint32_t>(m_ColorAttachments.size()));
-
- for (size_t i = 0; i < m_ColorAttachments.size(); i++)
- {
- Utils::BindTexture(multisample, m_ColorAttachments[i]);
- switch (m_ColorAttachmentSpecifications[i].TextureFormat)
- {
- case FramebufferTextureFormat::RGBA8:
- Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_RGBA8, GL_RGBA, m_Specification.Width, m_Specification.Height, static_cast<int>(i));
- break;
- case FramebufferTextureFormat::RED_INTEGER:
- Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_R32I, GL_RED_INTEGER, m_Specification.Width, m_Specification.Height, static_cast<int>(i));
- break;
- }
- }
- }
-
- if (m_DepthAttachmentSpecification.TextureFormat != FramebufferTextureFormat::None)
- {
- Utils::CreateTextures(multisample, &m_DepthAttachment, 1);
- Utils::BindTexture(multisample, m_DepthAttachment);
- switch (m_DepthAttachmentSpecification.TextureFormat)
- {
- case FramebufferTextureFormat::DEPTH24STENCIL8:
- Utils::AttachDepthTexture(m_DepthAttachment, m_Specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_Specification.Width, m_Specification.Height);
- break;
- }
- }
-
- if (m_ColorAttachments.size() > 1)
- {
- GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
- glDrawBuffers(static_cast<GLsizei>(m_ColorAttachments.size()), buffers);
- }
- else if (m_ColorAttachments.empty())
- glDrawBuffer(GL_NONE);
-
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
- {
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- switch (status)
- {
- case GL_FRAMEBUFFER_UNDEFINED:
- DONUT_ERROR("Framebuffer is undefined");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- DONUT_ERROR("Framebuffer has incomplete attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- DONUT_ERROR("Framebuffer is missing attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
- DONUT_ERROR("Framebuffer has incomplete draw buffer");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
- DONUT_ERROR("Framebuffer has incomplete read buffer");
- break;
- case GL_FRAMEBUFFER_UNSUPPORTED:
- DONUT_ERROR("Framebuffer format is unsupported");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
- DONUT_ERROR("Framebuffer has incomplete multisample");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
- DONUT_ERROR("Framebuffer has incomplete layer targets");
- break;
- default:
- DONUT_ERROR("Framebuffer is incomplete (unknown error: {})", status);
- break;
- }
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- }
-
- void OpenGLFramebuffer::Bind()
- {
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
- glViewport(0, 0, m_Specification.Width, m_Specification.Height);
- }
-
- void OpenGLFramebuffer::Unbind()
- {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- }
-
- void OpenGLFramebuffer::Resize(uint32_t width, uint32_t height)
- {
- m_Specification.Width = width;
- m_Specification.Height = height;
-
- Invalidate();
- }
-
- int OpenGLFramebuffer::ReadPixel(uint32_t attachmentIndex, int x, int y)
- {
- glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex);
- int pixelData;
- glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixelData);
- return pixelData;
- }
-
- void OpenGLFramebuffer::ClearAttachment(uint32_t attachmentIndex, int value)
- {
- // glClearTexImage is 4.4 and unavailable on macOS. Clear the integer
- // attachment by binding this framebuffer and clearing its draw buffer.
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
- glClearBufferiv(GL_COLOR, static_cast<GLint>(attachmentIndex), &value);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.h b/src/Platform/OpenGL/OpenGLFramebuffer.h
deleted file mode 100644
index 8cd376c..0000000
--- a/src/Platform/OpenGL/OpenGLFramebuffer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "Rendering/Framebuffer.h"
-
-namespace Donut
-{
- class OpenGLFramebuffer : public Framebuffer
- {
- public:
- OpenGLFramebuffer(const FramebufferSpecification& spec);
- virtual ~OpenGLFramebuffer();
-
- void Invalidate();
-
- virtual void Bind() override;
- virtual void Unbind() override;
-
- virtual void Resize(uint32_t width, uint32_t height) override;
- virtual int ReadPixel(uint32_t attachmentIndex, int x, int y) override;
-
- virtual void ClearAttachment(uint32_t attachmentIndex, int value) override;
- virtual uint32_t GetColorAttachmentRendererID(uint32_t index = 0) const override { return m_ColorAttachments[index]; }
-
- virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; }
- private:
- uint32_t m_RendererID = 0;
- FramebufferSpecification m_Specification;
-
- std::vector<FramebufferTextureSpecification> m_ColorAttachmentSpecifications;
- FramebufferTextureSpecification m_DepthAttachmentSpecification = FramebufferTextureFormat::None;
-
- std::vector<uint32_t> m_ColorAttachments;
- uint32_t m_DepthAttachment = 0;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.cpp b/src/Platform/OpenGL/OpenGLIndexBuffer.cpp
deleted file mode 100644
index 668a898..0000000
--- a/src/Platform/OpenGL/OpenGLIndexBuffer.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "OpenGLIndexBuffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count)
- : m_Count(count)
- {
- glGenBuffers(1, &m_RendererID); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
- }
-
- OpenGLIndexBuffer::~OpenGLIndexBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLIndexBuffer::Bind() const
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
- }
-
- void OpenGLIndexBuffer::Unbind() const
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.h b/src/Platform/OpenGL/OpenGLIndexBuffer.h
deleted file mode 100644
index b3048a3..0000000
--- a/src/Platform/OpenGL/OpenGLIndexBuffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- class OpenGLIndexBuffer
- : public IndexBuffer
- {
- public:
- OpenGLIndexBuffer(const uint32_t* indices, uint32_t count);
- virtual ~OpenGLIndexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
- virtual uint32_t GetCount() const override { return m_Count; }
-
- private:
- uint32_t m_RendererID;
- uint32_t m_Count;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/Platform/OpenGL/OpenGLRendererAPI.cpp
deleted file mode 100644
index fd3c491..0000000
--- a/src/Platform/OpenGL/OpenGLRendererAPI.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-#include "OpenGLRendererAPI.h"
-
-#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-
-namespace Donut
-{
- void OpenGLRendererAPI::Init()
- {
- if (!glfwGetCurrentContext())
- {
- DONUT_ERROR("No OpenGL context is current! Cannot initialize GLAD.");
- return;
- }
-
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
- {
- DONUT_ERROR("Failed to initialize GLAD!");
- return;
- }
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
-
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glFrontFace(GL_CCW);
- }
-
- void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
- {
- glViewport(x, y, width, height);
- }
-
- void OpenGLRendererAPI::SetClearColor(const glm::vec4& color)
- {
- glClearColor(color.r, color.g, color.b, color.a);
- }
-
- void OpenGLRendererAPI::Clear()
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- }
-
- void OpenGLRendererAPI::EnableDepthTest()
- {
- glEnable(GL_DEPTH_TEST);
- }
-
- void OpenGLRendererAPI::DisableDepthTest()
- {
- glDisable(GL_DEPTH_TEST);
- }
-
- void OpenGLRendererAPI::SetFaceCulling(bool enabled)
- {
- if (enabled)
- {
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glFrontFace(GL_CCW);
- }
- else
- glDisable(GL_CULL_FACE);
- }
-
- void OpenGLRendererAPI::EnableBlending()
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- void OpenGLRendererAPI::DisableBlending()
- {
- glDisable(GL_BLEND);
- }
-
- void OpenGLRendererAPI::DrawIndexed(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
- {
- uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount();
- glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- void OpenGLRendererAPI::DrawArrays(uint32_t vertexCount, uint32_t first)
- {
- glDrawArrays(GL_TRIANGLES, first, vertexCount);
- }
-
- void OpenGLRendererAPI::DrawLines(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
- {
- uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount();
- glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, nullptr);
- }
-
- void OpenGLRendererAPI::BindTexture(uint32_t textureID, uint32_t slot)
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, textureID);
- }
-
- void OpenGLRendererAPI::BindImageTexture(uint32_t textureID, uint32_t slot, bool readOnly)
- {
- // Image load/store is OpenGL 4.2; the pointer is null on macOS (4.1).
- if (glBindImageTexture == nullptr)
- return;
- glBindImageTexture(slot, textureID, 0, GL_FALSE, 0,
- readOnly ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
- }
-
- void OpenGLRendererAPI::ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels)
- {
- glReadPixels(x, y, width, height, format, type, pixels);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h
deleted file mode 100644
index 5a68622..0000000
--- a/src/Platform/OpenGL/OpenGLRendererAPI.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include "Core/Log.h"
-
-#include "Rendering/Renderer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLRendererAPI
- : public RendererAPI
- {
- public:
- virtual void Init() override;
- virtual void SetViewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual void SetClearColor(const glm::vec4& color) override;
- virtual void Clear() override;
- virtual void EnableDepthTest() override;
- virtual void DisableDepthTest() override;
- virtual void SetFaceCulling(bool enabled) override;
- virtual void EnableBlending() override;
- virtual void DisableBlending() override;
-
- virtual void DrawIndexed(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
-
- virtual void DrawArrays(uint32_t vertexCount,
- uint32_t first = 0) override;
- virtual void DrawLines(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
- virtual void BindTexture(uint32_t textureID,
- uint32_t slot = 0) override;
- virtual void BindImageTexture(uint32_t textureID,
- uint32_t slot = 0,
- bool readOnly = false) override;
- virtual void ReadPixels(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height,
- uint32_t format, uint32_t type,
- void* pixels) override;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLShader.cpp b/src/Platform/OpenGL/OpenGLShader.cpp
deleted file mode 100644
index 5d6dd66..0000000
--- a/src/Platform/OpenGL/OpenGLShader.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-#include "OpenGLShader.h"
-
-#include <glad/glad.h>
-#include <glm/gtc/type_ptr.hpp>
-
-#include <fstream>
-#include <iostream>
-
-namespace Donut
-{
- static uint32_t ShaderTypeFromString(const std::string& type)
- {
- if (type == "vertex")
- return GL_VERTEX_SHADER;
- if (type == "fragment" || type == "pixel")
- return GL_FRAGMENT_SHADER;
- if (type == "compute")
- return GL_COMPUTE_SHADER;
- return 0;
- }
-
- // Shaders are authored in Slang and compiled to Assets/Shaders/generated/
- // <name>.glsl by Tools/compile-shaders.sh. Given a legacy ".../<name>.glsl"
- // path, prefer that generated file when present; otherwise fall back to the
- // hand-written GLSL (e.g. shaders not yet ported to Slang).
- static std::string ResolveShaderPath(const std::string& filepath)
- {
- size_t slash = filepath.find_last_of("/\\");
- std::string dir = (slash == std::string::npos) ? std::string() : filepath.substr(0, slash + 1);
- std::string file = (slash == std::string::npos) ? filepath : filepath.substr(slash + 1);
- size_t dot = file.rfind('.');
- std::string base = (dot == std::string::npos) ? file : file.substr(0, dot);
-
- std::string generated = dir + "generated/" + base + ".glsl";
- std::ifstream test(generated);
- if (test.good())
- return generated;
- return filepath;
- }
-
- OpenGLShader::OpenGLShader(const std::string& filepath)
- {
- std::string resolved = ResolveShaderPath(filepath);
- m_IsSlang = (resolved != filepath);
- std::string source = ReadFile(resolved);
- auto shaderSources = PreProcess(source);
- Compile(shaderSources);
-
- auto lastSlash = filepath.find_last_of("/\\");
- lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1;
- auto lastDot = filepath.rfind('.');
- auto count = lastDot == std::string::npos ? filepath.size() - lastSlash : lastDot - lastSlash;
- m_Name = filepath.substr(lastSlash, count);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc)
- : m_Name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_VERTEX_SHADER] = vertexSrc;
- sources[GL_FRAGMENT_SHADER] = fragmentSrc;
- Compile(sources);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& computeSrc)
- : m_Name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_COMPUTE_SHADER] = computeSrc;
- Compile(sources);
- }
-
- OpenGLShader::~OpenGLShader()
- {
- glDeleteProgram(m_RendererID);
- }
-
- std::string OpenGLShader::ReadFile(const std::string& filepath)
- {
- std::string result;
- std::ifstream in(filepath, std::ios::in |
- std::ios::binary);
-
- if (in)
- {
- in.seekg(0, std::ios::end);
- size_t size = in.tellg();
- if (size != -1)
- {
- result.resize(size);
- in.seekg(0, std::ios::beg);
- in.read(&result[0], size);
- }
- }
- return result;
- }
-
- std::unordered_map<uint32_t, std::string> OpenGLShader::PreProcess(const std::string& source)
- {
- std::unordered_map<uint32_t, std::string> shaderSources;
-
- const char* typeToken = "#type";
- size_t typeTokenLength = strlen(typeToken);
- size_t pos = source.find(typeToken, 0);
-
- while (pos != std::string::npos)
- {
- size_t eol = source.find_first_of("\r\n", pos);
- size_t begin = pos + typeTokenLength + 1;
- std::string type = source.substr(begin, eol - begin);
-
- size_t nextLinePos = source.find_first_not_of("\r\n", eol);
- pos = source.find(typeToken, nextLinePos);
- shaderSources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(nextLinePos) :
- source.substr(nextLinePos, pos - nextLinePos);
- }
-
- return shaderSources;
- }
-
- void OpenGLShader::Compile(const std::unordered_map<uint32_t, std::string>& shaderSources)
- {
- uint32_t program = glCreateProgram();
- std::vector<uint32_t> glShaderIDs(shaderSources.size());
- for (auto& kv : shaderSources)
- {
- uint32_t type = kv.first;
- const std::string& source = kv.second;
-
- uint32_t shader = glCreateShader(type);
- const char* sourceCStr = source.c_str();
- glShaderSource(shader, 1, &sourceCStr, 0);
- glCompileShader(shader);
-
- int isCompiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
- if (isCompiled == GL_FALSE)
- {
- int maxLength = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
- std::vector<char> infoLog(maxLength);
- glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);
- glDeleteShader(shader);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- glDeleteProgram(program);
- m_RendererID = 0;
- // infoLog.data() is null when the driver returns an empty log
- // (e.g. macOS rejecting a compute shader); streaming a null
- // char* into std::cout calls strlen(NULL) and crashes.
- const char* log = infoLog.empty() ? "" : infoLog.data();
- std::cout << "Shader compilation failure!" << std::endl << log << std::endl;
- return;
- }
- glAttachShader(program, shader);
- glShaderIDs.push_back(shader);
- }
-
- m_RendererID = program;
- glLinkProgram(m_RendererID);
-
- int isLinked = 0;
- glGetProgramiv(m_RendererID, GL_LINK_STATUS, (int*)&isLinked);
- if (isLinked == GL_FALSE)
- {
- int maxLength = 0;
- glGetProgramiv(m_RendererID, GL_INFO_LOG_LENGTH, &maxLength);
- std::vector<char> infoLog(maxLength);
- glGetProgramInfoLog(m_RendererID, maxLength, &maxLength, &infoLog[0]);
- glDeleteProgram(m_RendererID);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- m_RendererID = 0;
- const char* log = infoLog.empty() ? "" : infoLog.data();
- std::cout << "Shader link failure!" << std::endl << log << std::endl;
- return;
- }
-
- for (auto id : glShaderIDs)
- {
- glDetachShader(m_RendererID, id);
- glDeleteShader(id);
- }
- }
-
- void OpenGLShader::Bind() const
- {
- glUseProgram(m_RendererID);
- }
-
- void OpenGLShader::Unbind() const
- {
- glUseProgram(0);
- }
-
- void OpenGLShader::SetInt(const std::string& name, int value)
- {
- UploadUniformInt(name, value);
- }
-
- void OpenGLShader::SetIntArray(const std::string& name, int* values, uint32_t count)
- {
- UploadUniformIntArray(name, values, count);
- }
-
- void OpenGLShader::SetFloat(const std::string& name, float value)
- {
- UploadUniformFloat(name, value);
- }
-
- void OpenGLShader::SetFloat2(const std::string& name, const glm::vec2& value)
- {
- UploadUniformFloat2(name, value);
- }
-
- void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value)
- {
- UploadUniformFloat3(name, value);
- }
-
- void OpenGLShader::SetFloat4(const std::string& name, const glm::vec4& value)
- {
- UploadUniformFloat4(name, value);
- }
-
- void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value)
- {
- UploadUniformMat4(name, value);
- }
-
- void OpenGLShader::UploadUniformInt(const std::string& name, int value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1i(location, value);
- }
-
- void OpenGLShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1iv(location, count, values);
- }
-
- void OpenGLShader::UploadUniformFloat(const std::string& name, float value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1f(location, value);
- }
-
- void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform2f(location, value.x, value.y);
- }
-
- void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform3f(location, value.x, value.y, value.z);
- }
-
- void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform4f(location, value.x, value.y, value.z, value.w);
- }
-
- void OpenGLShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniformMatrix3fv(location, 1, m_IsSlang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniformMatrix4fv(location, 1, m_IsSlang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- void OpenGLShader::Dispatch(uint32_t x, uint32_t y, uint32_t z)
- {
- // Compute shaders require OpenGL 4.3+. On drivers that cap out earlier
- // (e.g. macOS, which is frozen at 4.1) glDispatchCompute is never
- // loaded and the pointer is null. Guard so we no-op instead of crash.
- if (m_RendererID == 0 || glDispatchCompute == nullptr)
- return;
- glDispatchCompute(x, y, z);
- }
-
- void OpenGLShader::DispatchIndirect(uint32_t offset)
- {
- if (m_RendererID == 0 || glDispatchComputeIndirect == nullptr)
- return;
- glDispatchComputeIndirect(offset);
- }
-
- void OpenGLShader::MemoryBarrier(uint32_t barriers)
- {
- if (glMemoryBarrier == nullptr)
- return;
- glMemoryBarrier(barriers);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLShader.h b/src/Platform/OpenGL/OpenGLShader.h
deleted file mode 100644
index 4b63597..0000000
--- a/src/Platform/OpenGL/OpenGLShader.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-
-#include "Rendering/Shader.h"
-
-#include <unordered_map>
-#include <glm/glm.hpp>
-
-namespace Donut
-{
- class OpenGLShader
- : public Shader
- {
- public:
- OpenGLShader(const std::string& filepath);
- OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
- OpenGLShader(const std::string& name, const std::string& computeSrc);
- virtual ~OpenGLShader();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetInt( const std::string& name, int value) override;
- virtual void SetIntArray(const std::string& name, int* values, uint32_t count) override;
- virtual void SetFloat( const std::string& name, float value) override;
- virtual void SetFloat2( const std::string& name, const glm::vec2& value) override;
- virtual void SetFloat3( const std::string& name, const glm::vec3& value) override;
- virtual void SetFloat4( const std::string& name, const glm::vec4& value) override;
- virtual void SetMat4( const std::string& name, const glm::mat4& value) override;
-
- virtual void Dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) override;
- virtual void DispatchIndirect(uint32_t offset = 0) override;
- virtual void MemoryBarrier(uint32_t barriers) override;
-
- virtual const std::string& GetName() const override { return m_Name; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- void UploadUniformInt( const std::string& name, int value);
- void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
- void UploadUniformFloat( const std::string& name, float value);
- void UploadUniformFloat2( const std::string& name, const glm::vec2& value);
- void UploadUniformFloat3( const std::string& name, const glm::vec3& value);
- void UploadUniformFloat4( const std::string& name, const glm::vec4& value);
- void UploadUniformMat3( const std::string& name, const glm::mat3& matrix);
- void UploadUniformMat4( const std::string& name, const glm::mat4& matrix);
-
- private:
- std::string ReadFile(const std::string& filepath);
- std::unordered_map<uint32_t, std::string> PreProcess(const std::string& source);
- void Compile(const std::unordered_map<uint32_t, std::string>& shaderSources);
- private:
- uint32_t m_RendererID = 0;
- std::string m_Name;
- // True when loaded from a Slang-compiled GLSL. Slang expects row-major
- // matrix data, so matrix uniforms are transposed on upload (glm is
- // column-major) to keep all matrix math correct.
- bool m_IsSlang = false;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/Platform/OpenGL/OpenGLTexture.cpp
deleted file mode 100644
index 16892c6..0000000
--- a/src/Platform/OpenGL/OpenGLTexture.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-#include "OpenGLTexture.h"
-
-#include "Rendering/Shader.h"
-
-#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h"
-#include <glm/glm.hpp>
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-// NOTE: This file targets OpenGL 4.1 (the maximum macOS exposes). It uses the
-// classic bind-based texture API rather than 4.5 Direct State Access
-// (glCreateTextures / glTextureStorage2D / glTextureParameteri / glBindTextureUnit),
-// none of which exist on macOS.
-
-namespace Donut
-{
- OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = GL_RGBA8;
- m_DataFormat = GL_RGBA;
-
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- }
-
- OpenGLTexture2D::OpenGLTexture2D(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1;
- m_Height = 1;
- m_InternalFormat = GL_RGBA8;
- m_DataFormat = GL_RGBA;
-
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
- uint32_t whitePixel = 0xFFFFFFFF;
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, &whitePixel);
-
- DONUT_INFO("Created default texture (stb_image not available for loading: ", path, ")");
- }
-
- OpenGLTexture2D::~OpenGLTexture2D()
- {
- glDeleteTextures(1, &m_RendererID);
- }
-
- void OpenGLTexture2D::SetData(void* data, uint32_t size)
- {
- uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3;
- if (size != m_Width * m_Height * bpp)
- {
- DONUT_ERROR("Data must be entire texture!");
- return;
- }
-
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data);
- }
-
- void OpenGLTexture2D::Bind(uint32_t slot) const
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- }
-
- void OpenGLTexture2D::BindAsImage(uint32_t slot, bool readOnly) const
- {
- // Image load/store is OpenGL 4.2 and unavailable on macOS. Guard the
- // function pointer so this degrades to a no-op instead of crashing.
- if (glBindImageTexture == nullptr)
- return;
- GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_RendererID, 0, GL_FALSE, 0, access, m_InternalFormat);
- }
-
- OpenGLCubemapTexture::OpenGLCubemapTexture(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = GL_RGBA16F;
- m_DataFormat = GL_RGBA;
-
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
- for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_FLOAT, nullptr);
-
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- }
-
- OpenGLCubemapTexture::OpenGLCubemapTexture(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1024;
- m_Height = 1024;
- m_InternalFormat = GL_RGBA16F;
- m_DataFormat = GL_RGBA;
-
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
- for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_FLOAT, nullptr);
-
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
-
- LoadHDRI(path);
- }
-
- OpenGLCubemapTexture::~OpenGLCubemapTexture()
- {
- glDeleteTextures(1, &m_RendererID);
- }
-
- void OpenGLCubemapTexture::LoadHDRI(const std::string& path)
- {
- stbi_set_flip_vertically_on_load(true);
- int width, height, channels;
- float* hdrData = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
-
- if (!hdrData)
- {
- DONUT_ERROR("Failed to load HDRI: {}", path);
- float defaultSky[6 * 4] =
- {
- 0.5f, 0.7f, 1.0f, 1.0f, // Right
- 0.5f, 0.7f, 1.0f, 1.0f, // Left
- 0.5f, 0.7f, 1.0f, 1.0f, // Top
- 0.5f, 0.7f, 1.0f, 1.0f, // Bottom
- 0.5f, 0.7f, 1.0f, 1.0f, // Front
- 0.5f, 0.7f, 1.0f, 1.0f // Back
- };
-
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
- for (int i = 0; i < 6; ++i)
- glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, &defaultSky[i * 4]);
- return;
- }
-
- ConvertEquirectangularToCubemap(hdrData, width, height);
- stbi_image_free(hdrData);
-
- DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height);
- }
-
- void OpenGLCubemapTexture::ConvertEquirectangularToCubemap(float* hdrData, int width, int height)
- {
- uint32_t captureFBO, captureRBO;
- glGenFramebuffers(1, &captureFBO);
- glGenRenderbuffers(1, &captureRBO);
-
- glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
- glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_Width, m_Height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
-
- uint32_t hdrTexture;
- glGenTextures(1, &hdrTexture);
- glBindTexture(GL_TEXTURE_2D, hdrTexture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdrData);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- auto equirectShader = Shader::Create("Assets/Shaders/EquirectToCubemap.glsl");
- if (!equirectShader)
- {
- DONUT_ERROR("Failed to create equirectangular to cubemap shader");
- return;
- }
-
- uint32_t shaderProgram = equirectShader->GetRendererID();
-
- float vertices[] =
- {
- -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
- -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
- 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
- -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
- -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
- -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f
- };
-
- uint32_t cubeVAO, cubeVBO;
- glGenVertexArrays(1, &cubeVAO);
- glGenBuffers(1, &cubeVBO);
- glBindVertexArray(cubeVAO);
- glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
-
- glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- glm::mat4 captureViews[] =
- {
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
- };
-
- glUseProgram(shaderProgram);
- glUniform1i(glGetUniformLocation(shaderProgram, "u_EquirectangularMap"), 0);
- // EquirectToCubemap is authored in Slang (row-major); transpose glm's
- // column-major matrices on upload (GL_TRUE) to match.
- glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "u_Projection"), 1, GL_TRUE, &captureProjection[0][0]);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, hdrTexture);
-
- glViewport(0, 0, m_Width, m_Height);
- glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
- for (unsigned int i = 0; i < 6; ++i)
- {
- glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "u_View"), 1, GL_TRUE, &captureViews[i][0][0]);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_RendererID, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glBindVertexArray(cubeVAO);
- glDrawArrays(GL_TRIANGLES, 0, 36);
- }
- glBindVertexArray(0);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- glDeleteVertexArrays(1, &cubeVAO);
- glDeleteBuffers(1, &cubeVBO);
- glDeleteTextures(1, &hdrTexture);
- glDeleteFramebuffers(1, &captureFBO);
- glDeleteRenderbuffers(1, &captureRBO);
- }
-
- void OpenGLCubemapTexture::SetData(void* data, uint32_t size)
- {
- DONUT_WARN("SetData not implemented for cubemaps");
- }
-
- void OpenGLCubemapTexture::Bind(uint32_t slot) const
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
- }
-
- void OpenGLCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
- {
- if (glBindImageTexture == nullptr)
- return;
- GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_RendererID, 0, GL_TRUE, 0, access, m_InternalFormat);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h
deleted file mode 100644
index 220df09..0000000
--- a/src/Platform/OpenGL/OpenGLTexture.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include "Rendering/Texture.h"
-#include "Core/Log.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLTexture2D
- : public Texture2D
- {
- public:
- OpenGLTexture2D(uint32_t width, uint32_t height);
- OpenGLTexture2D(const std::string& path);
- virtual ~OpenGLTexture2D();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == other.GetRendererID();
- }
-
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- GLenum m_InternalFormat, m_DataFormat;
- };
-
- class OpenGLCubemapTexture
- : public CubemapTexture
- {
- public:
- OpenGLCubemapTexture(uint32_t width, uint32_t height);
- OpenGLCubemapTexture(const std::string& path);
- virtual ~OpenGLCubemapTexture();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == other.GetRendererID();
- }
-
- private:
- void LoadHDRI(const std::string& path);
- void ConvertEquirectangularToCubemap(float* hdrData, int width, int height);
-
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- GLenum m_InternalFormat, m_DataFormat;
- };
-}
diff --git a/src/Platform/OpenGL/OpenGLUniformBuffer.cpp b/src/Platform/OpenGL/OpenGLUniformBuffer.cpp
deleted file mode 100644
index 631b863..0000000
--- a/src/Platform/OpenGL/OpenGLUniformBuffer.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "OpenGLUniformBuffer.h"
-
-namespace Donut
-{
- OpenGLUniformBuffer::OpenGLUniformBuffer(uint32_t size, uint32_t binding)
- : m_Size(size), m_Binding(binding)
- {
- glGenBuffers(1, &m_RendererID);
- glBindBuffer(GL_UNIFORM_BUFFER, m_RendererID);
- glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_RendererID);
- }
-
- OpenGLUniformBuffer::~OpenGLUniformBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLUniformBuffer::SetData(const void* data, uint32_t size, uint32_t offset)
- {
- glBindBuffer(GL_UNIFORM_BUFFER, m_RendererID);
- glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
- }
-
- void OpenGLUniformBuffer::Bind(uint32_t binding)
- {
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_RendererID);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLUniformBuffer.h b/src/Platform/OpenGL/OpenGLUniformBuffer.h
deleted file mode 100644
index fd96f37..0000000
--- a/src/Platform/OpenGL/OpenGLUniformBuffer.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include "Rendering/UniformBuffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLUniformBuffer : public UniformBuffer
- {
- public:
- OpenGLUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~OpenGLUniformBuffer();
-
- virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override;
- virtual void Bind(uint32_t binding) override;
- private:
- uint32_t m_RendererID = 0;
- uint32_t m_Size = 0;
- uint32_t m_Binding = 0;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexArray.cpp b/src/Platform/OpenGL/OpenGLVertexArray.cpp
deleted file mode 100644
index afe5fe2..0000000
--- a/src/Platform/OpenGL/OpenGLVertexArray.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <glad/glad.h>
-
-#include "OpenGLVertexArray.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- OpenGLVertexArray::OpenGLVertexArray()
- {
- // glCreateVertexArrays is 4.5 DSA; macOS caps at 4.1. glGenVertexArrays
- // reserves the name and the VAO is created on first bind (done below).
- glGenVertexArrays(1, &m_RendererID);
- }
-
- OpenGLVertexArray::~OpenGLVertexArray()
- {
- glDeleteVertexArrays(1, &m_RendererID);
- }
-
- void OpenGLVertexArray::Bind() const
- {
- glBindVertexArray(m_RendererID);
- }
-
- void OpenGLVertexArray::Unbind() const
- {
- glBindVertexArray(0);
- }
-
- void OpenGLVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
- {
- glBindVertexArray(m_RendererID);
- vertexBuffer->Bind();
-
- const auto& layout = vertexBuffer->GetLayout();
- for (const auto& element : layout.GetElements())
- {
- glEnableVertexAttribArray(m_VertexBufferIndex);
- glVertexAttribPointer(m_VertexBufferIndex,
- element.count,
- element.type,
- element.normalized ? GL_TRUE : GL_FALSE,
- layout.GetStride(),
- reinterpret_cast<const void*>(static_cast<uintptr_t>(element.offset)));
- m_VertexBufferIndex++;
- }
-
- m_VertexBuffers.push_back(vertexBuffer);
- }
-
- void OpenGLVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer)
- {
- glBindVertexArray(m_RendererID);
- indexBuffer->Bind();
- m_IndexBuffer = indexBuffer;
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexArray.h b/src/Platform/OpenGL/OpenGLVertexArray.h
deleted file mode 100644
index b6ae82f..0000000
--- a/src/Platform/OpenGL/OpenGLVertexArray.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-
-#include "Rendering/VertexArray.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.h"
-
-#include <vector>
-
-namespace Donut
-{
- class OpenGLVertexArray
- : public VertexArray
- {
- public:
- OpenGLVertexArray();
- virtual ~OpenGLVertexArray();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) override;
- virtual void SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer) override;
-
- virtual const std::vector<Ref<VertexBuffer>>& GetVertexBuffers() const override
- {
- return m_VertexBuffers;
- }
-
- virtual const Ref<IndexBuffer>& GetIndexBuffer() const override
- {
- return m_IndexBuffer;
- }
- private:
- uint32_t m_RendererID;
- uint32_t m_VertexBufferIndex = 0;
- std::vector<Ref<VertexBuffer>> m_VertexBuffers;
- Ref<IndexBuffer> m_IndexBuffer;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.cpp b/src/Platform/OpenGL/OpenGLVertexBuffer.cpp
deleted file mode 100644
index 4f5e48f..0000000
--- a/src/Platform/OpenGL/OpenGLVertexBuffer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "OpenGLVertexBuffer.h"
-#include "Rendering/VertexBuffer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size)
- {
- glGenBuffers(1, &m_RendererID); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
- }
-
- OpenGLVertexBuffer::~OpenGLVertexBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLVertexBuffer::Bind() const
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- }
-
- void OpenGLVertexBuffer::Unbind() const
- {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- void OpenGLVertexBuffer::SetData(const void* data, uint32_t size)
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.h b/src/Platform/OpenGL/OpenGLVertexBuffer.h
deleted file mode 100644
index 6244f7d..0000000
--- a/src/Platform/OpenGL/OpenGLVertexBuffer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexBuffer.h"
-
-namespace Donut
-{
- class OpenGLVertexBuffer
- : public VertexBuffer
- {
- public:
- OpenGLVertexBuffer(const void* data, uint32_t size);
- virtual ~OpenGLVertexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
- virtual void SetData(const void* data, uint32_t size) override;
-
- virtual const VertexBufferLayout& GetLayout() const override { return m_Layout; }
- virtual void SetLayout(const VertexBufferLayout& layout) override { m_Layout = layout; }
-
- private:
- uint32_t m_RendererID;
- VertexBufferLayout m_Layout;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanContext.cpp b/src/Platform/Vulkan/VulkanContext.cpp
deleted file mode 100644
index 9a67fe8..0000000
--- a/src/Platform/Vulkan/VulkanContext.cpp
+++ /dev/null
@@ -1,789 +0,0 @@
-#include "VulkanContext.h"
-#include "Core/Log.h"
-
-#include <vulkan/vulkan.h>
-
-#include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
-#include "stb_image_write.h"
-
-#include <vector>
-#include <cstring>
-#include <cstdlib>
-#include <fstream>
-
-namespace Donut
-{
- // Logs and returns false from the enclosing function on any non-success result.
- #define VK_CHECK(expr) \
- do { \
- VkResult _r = (expr); \
- if (_r != VK_SUCCESS) { \
- DONUT_ERROR("Vulkan: {} failed ({})", #expr, (int)_r); \
- return false; \
- } \
- } while (0)
-
- struct VulkanContext::Impl
- {
- VkInstance instance = VK_NULL_HANDLE;
- VkPhysicalDevice physical = VK_NULL_HANDLE;
- VkDevice device = VK_NULL_HANDLE;
- VkQueue graphicsQueue = VK_NULL_HANDLE;
- uint32_t graphicsFamily = 0;
- VkPhysicalDeviceMemoryProperties memProps{};
-
- uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const
- {
- for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
- if ((typeFilter & (1u << i)) &&
- (memProps.memoryTypes[i].propertyFlags & flags) == flags)
- return i;
- return UINT32_MAX;
- }
-
- bool CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
- VkBuffer& buf, VkDeviceMemory& mem) const
- {
- VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
- bci.size = size; bci.usage = usage; bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- if (vkCreateBuffer(device, &bci, nullptr, &buf) != VK_SUCCESS) return false;
- VkMemoryRequirements req{}; vkGetBufferMemoryRequirements(device, buf, &req);
- VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- ai.allocationSize = req.size;
- ai.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, props);
- if (vkAllocateMemory(device, &ai, nullptr, &mem) != VK_SUCCESS) return false;
- vkBindBufferMemory(device, buf, mem, 0);
- return true;
- }
- };
-
- VulkanContext::VulkanContext() { m_Impl = new Impl(); }
- VulkanContext::~VulkanContext() { Shutdown(); delete m_Impl; m_Impl = nullptr; }
-
- bool VulkanContext::Init()
- {
- Impl& v = *m_Impl;
-
-#ifdef __APPLE__
- // The Homebrew Vulkan loader does not auto-discover MoltenVK or the
- // validation layers; point it at both unless already configured.
- if (!getenv("VK_ICD_FILENAMES"))
- setenv("VK_ICD_FILENAMES", "/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json", 0);
- if (!getenv("VK_LAYER_PATH"))
- setenv("VK_LAYER_PATH", "/opt/homebrew/share/vulkan/explicit_layer.d", 0);
-#endif
-
- // Instance
- VkApplicationInfo app{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
- app.pApplicationName = "Donut";
- app.apiVersion = VK_API_VERSION_1_2;
-
- // MoltenVK is a portability driver: without the portability-enumeration
- // extension + flag, vkEnumeratePhysicalDevices returns zero devices.
- std::vector<const char*> exts = {
- VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
- VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
- };
-
- // Enable validation layers when they are installed (optional).
- std::vector<const char*> layers;
- uint32_t layerCount = 0;
- vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
- std::vector<VkLayerProperties> avail(layerCount);
- vkEnumerateInstanceLayerProperties(&layerCount, avail.data());
- for (const auto& l : avail)
- if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0)
- layers.push_back("VK_LAYER_KHRONOS_validation");
-
- VkInstanceCreateInfo ici{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
- ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
- ici.pApplicationInfo = &app;
- ici.enabledExtensionCount = (uint32_t)exts.size();
- ici.ppEnabledExtensionNames = exts.data();
- ici.enabledLayerCount = (uint32_t)layers.size();
- ici.ppEnabledLayerNames = layers.data();
- VK_CHECK(vkCreateInstance(&ici, nullptr, &v.instance));
-
- // Physical device
- uint32_t deviceCount = 0;
- vkEnumeratePhysicalDevices(v.instance, &deviceCount, nullptr);
- if (deviceCount == 0) { DONUT_ERROR("Vulkan: no physical devices"); return false; }
- std::vector<VkPhysicalDevice> devices(deviceCount);
- vkEnumeratePhysicalDevices(v.instance, &deviceCount, devices.data());
- v.physical = devices[0];
-
- VkPhysicalDeviceProperties props{};
- vkGetPhysicalDeviceProperties(v.physical, &props);
- vkGetPhysicalDeviceMemoryProperties(v.physical, &v.memProps);
-
- // Graphics queue family
- uint32_t qCount = 0;
- vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &qCount, nullptr);
- std::vector<VkQueueFamilyProperties> qfams(qCount);
- vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &qCount, qfams.data());
- bool found = false;
- for (uint32_t i = 0; i < qCount; ++i)
- if (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { v.graphicsFamily = i; found = true; break; }
- if (!found) { DONUT_ERROR("Vulkan: no graphics queue family"); return false; }
-
- // Logical device
- // MoltenVK requires VK_KHR_portability_subset to be enabled if present.
- std::vector<const char*> devExts;
- uint32_t devExtCount = 0;
- vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &devExtCount, nullptr);
- std::vector<VkExtensionProperties> devExtProps(devExtCount);
- vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &devExtCount, devExtProps.data());
- for (const auto& e : devExtProps)
- if (std::strcmp(e.extensionName, "VK_KHR_portability_subset") == 0)
- devExts.push_back("VK_KHR_portability_subset");
-
- float priority = 1.0f;
- VkDeviceQueueCreateInfo qci{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
- qci.queueFamilyIndex = v.graphicsFamily;
- qci.queueCount = 1;
- qci.pQueuePriorities = &priority;
-
- VkDeviceCreateInfo dci{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
- dci.queueCreateInfoCount = 1;
- dci.pQueueCreateInfos = &qci;
- dci.enabledExtensionCount = (uint32_t)devExts.size();
- dci.ppEnabledExtensionNames = devExts.data();
- VK_CHECK(vkCreateDevice(v.physical, &dci, nullptr, &v.device));
- vkGetDeviceQueue(v.device, v.graphicsFamily, 0, &v.graphicsQueue);
-
- DONUT_INFO("Vulkan device: {} (API {}.{}.{}, validation {})",
- props.deviceName,
- VK_API_VERSION_MAJOR(props.apiVersion),
- VK_API_VERSION_MINOR(props.apiVersion),
- VK_API_VERSION_PATCH(props.apiVersion),
- layers.empty() ? "off" : "on");
- return true;
- }
-
- bool VulkanContext::SelfTestClear()
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) return false;
-
- const uint32_t W = 64, H = 64;
- const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
-
- // Offscreen colour image
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
- VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- ici.imageType = VK_IMAGE_TYPE_2D;
- ici.format = fmt;
- ici.extent = { W, H, 1 };
- ici.mipLevels = 1;
- ici.arrayLayers = 1;
- ici.samples = VK_SAMPLE_COUNT_1_BIT;
- ici.tiling = VK_IMAGE_TILING_OPTIMAL;
- ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
- ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
-
- VkMemoryRequirements imReq{};
- vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
-
- VkImageView view = VK_NULL_HANDLE;
- VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = image;
- vci.viewType = VK_IMAGE_VIEW_TYPE_2D;
- vci.format = fmt;
- vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(v.device, &vci, nullptr, &view));
-
- // Render pass (clear -> store, leave in TRANSFER_SRC for readback)
- VkAttachmentDescription color{};
- color.format = fmt;
- color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
-
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{};
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1;
- subpass.pColorAttachments = &colorRef;
-
- // Ensure colour writes finish before the read-back copy.
- VkSubpassDependency dep{};
- dep.srcSubpass = 0;
- dep.dstSubpass = VK_SUBPASS_EXTERNAL;
- dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
- dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-
- VkRenderPass renderPass = VK_NULL_HANDLE;
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color;
- rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
- rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
-
- VkFramebuffer fb = VK_NULL_HANDLE;
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass;
- fbci.attachmentCount = 1; fbci.pAttachments = &view;
- fbci.width = W; fbci.height = H; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
-
- // Host-visible staging buffer for read-back
- VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory stagingMem = VK_NULL_HANDLE;
- VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
- bci.size = (VkDeviceSize)W * H * 4;
- bci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- VK_CHECK(vkCreateBuffer(v.device, &bci, nullptr, &staging));
- VkMemoryRequirements bReq{};
- vkGetBufferMemoryRequirements(v.device, staging, &bReq);
- VkMemoryAllocateInfo bAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- bAlloc.allocationSize = bReq.size;
- bAlloc.memoryTypeIndex = v.FindMemoryType(bReq.memoryTypeBits,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &bAlloc, nullptr, &stagingMem));
- VK_CHECK(vkBindBufferMemory(v.device, staging, stagingMem, 0));
-
- // Command buffer: clear via render pass, then copy image -> buffer
- VkCommandPool pool = VK_NULL_HANDLE;
- VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
- pci.queueFamilyIndex = v.graphicsFamily;
- VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &pool));
-
- VkCommandBuffer cmd = VK_NULL_HANDLE;
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
- VK_CHECK(vkAllocateCommandBuffers(v.device, &cbai, &cmd));
-
- VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
- begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
-
- VkClearValue clear{};
- clear.color = { { 0.2f, 0.4f, 0.8f, 1.0f } }; // -> RGBA8 (51, 102, 204, 255)
- VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb;
- rpbi.renderArea = { { 0, 0 }, { W, H } };
- rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
- vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdEndRenderPass(cmd);
-
- VkBufferImageCopy region{};
- region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
- region.imageExtent = { W, H, 1 };
- vkCmdCopyImageToBuffer(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging, 1, &region);
- VK_CHECK(vkEndCommandBuffer(cmd));
-
- VkFence fence = VK_NULL_HANDLE;
- VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
- VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
- VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
- submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
- VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
-
- // Read back + verify
- void* mapped = nullptr;
- VK_CHECK(vkMapMemory(v.device, stagingMem, 0, bci.size, 0, &mapped));
- const uint8_t* px = (const uint8_t*)mapped;
- DONUT_INFO("Vulkan clear self-test: pixel RGBA = ({}, {}, {}, {})",
- (int)px[0], (int)px[1], (int)px[2], (int)px[3]);
- bool ok = px[0] > 45 && px[0] < 60 && px[1] > 95 && px[1] < 110 &&
- px[2] > 195 && px[2] < 210 && px[3] == 255;
- vkUnmapMemory(v.device, stagingMem);
- DONUT_INFO("Vulkan clear self-test: {}", ok ? "PASS" : "FAIL");
-
- // Cleanup
- vkDestroyFence(v.device, fence, nullptr);
- vkDestroyCommandPool(v.device, pool, nullptr);
- vkDestroyBuffer(v.device, staging, nullptr);
- vkFreeMemory(v.device, stagingMem, nullptr);
- vkDestroyFramebuffer(v.device, fb, nullptr);
- vkDestroyRenderPass(v.device, renderPass, nullptr);
- vkDestroyImageView(v.device, view, nullptr);
- vkDestroyImage(v.device, image, nullptr);
- vkFreeMemory(v.device, imageMem, nullptr);
- return ok;
- }
-
- static std::vector<uint32_t> LoadSpirv(const std::string& path)
- {
- std::ifstream f(path, std::ios::binary | std::ios::ate);
- if (!f) return {};
- size_t size = (size_t)f.tellg();
- std::vector<uint32_t> data(size / 4);
- f.seekg(0);
- f.read((char*)data.data(), (std::streamsize)size);
- return data;
- }
-
- bool VulkanContext::SelfTestTriangle()
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) return false;
-
- const uint32_t W = 64, H = 64;
- const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
-
- // Offscreen image + view (as in the clear test)
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
- VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { W, H, 1 };
- ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
- ici.tiling = VK_IMAGE_TILING_OPTIMAL;
- ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
- VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
- VkImageView view = VK_NULL_HANDLE;
- VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
- vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(v.device, &vci, nullptr, &view));
-
- // Render pass + framebuffer
- VkAttachmentDescription color{};
- color.format = fmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{};
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorRef;
- VkSubpassDependency dep{};
- dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL;
- dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
- VkRenderPass renderPass = VK_NULL_HANDLE;
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color;
- rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
- rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
- VkFramebuffer fb = VK_NULL_HANDLE;
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
- fbci.width = W; fbci.height = H; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
-
- // Shader modules from Slang SPIR-V
- auto vspv = LoadSpirv("Assets/Shaders/generated/VkPipelineTest.vertexMain.spv");
- auto fspv = LoadSpirv("Assets/Shaders/generated/VkPipelineTest.fragmentMain.spv");
- if (vspv.empty() || fspv.empty()) { DONUT_ERROR("Vulkan: VkPipelineTest SPIR-V not found"); return false; }
- VkShaderModule vmod = VK_NULL_HANDLE, fmod = VK_NULL_HANDLE;
- VkShaderModuleCreateInfo smci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
- smci.codeSize = vspv.size() * 4; smci.pCode = vspv.data();
- VK_CHECK(vkCreateShaderModule(v.device, &smci, nullptr, &vmod));
- smci.codeSize = fspv.size() * 4; smci.pCode = fspv.data();
- VK_CHECK(vkCreateShaderModule(v.device, &smci, nullptr, &fmod));
-
- // Graphics pipeline
- VkPipelineShaderStageCreateInfo stages[2]{};
- stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
- stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
- stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
- stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
-
- VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
- VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
- ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- VkViewport vp{ 0, 0, (float)W, (float)H, 0, 1 };
- VkRect2D scissor{ { 0, 0 }, { W, H } };
- VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
- vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = &scissor;
- VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
- rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
- VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
- ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
- VkPipelineColorBlendAttachmentState cba{};
- cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
- VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
- cb.attachmentCount = 1; cb.pAttachments = &cba;
-
- VkPipelineLayout layout = VK_NULL_HANDLE;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- VK_CHECK(vkCreatePipelineLayout(v.device, &plci, nullptr, &layout));
-
- VkPipeline pipeline = VK_NULL_HANDLE;
- VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
- gpci.stageCount = 2; gpci.pStages = stages;
- gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia;
- gpci.pViewportState = &vps; gpci.pRasterizationState = &rs;
- gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = layout; gpci.renderPass = renderPass; gpci.subpass = 0;
- VK_CHECK(vkCreateGraphicsPipelines(v.device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline));
-
- // Readback staging buffer
- VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory stagingMem = VK_NULL_HANDLE;
- VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
- bci.size = (VkDeviceSize)W * H * 4; bci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- VK_CHECK(vkCreateBuffer(v.device, &bci, nullptr, &staging));
- VkMemoryRequirements bReq{}; vkGetBufferMemoryRequirements(v.device, staging, &bReq);
- VkMemoryAllocateInfo bAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- bAlloc.allocationSize = bReq.size;
- bAlloc.memoryTypeIndex = v.FindMemoryType(bReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &bAlloc, nullptr, &stagingMem));
- VK_CHECK(vkBindBufferMemory(v.device, staging, stagingMem, 0));
-
- // Record + submit
- VkCommandPool pool = VK_NULL_HANDLE;
- VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
- pci.queueFamilyIndex = v.graphicsFamily;
- VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &pool));
- VkCommandBuffer cmd = VK_NULL_HANDLE;
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
- VK_CHECK(vkAllocateCommandBuffers(v.device, &cbai, &cmd));
- VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
- begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
- VkClearValue clear{}; clear.color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
- VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb;
- rpbi.renderArea = { { 0, 0 }, { W, H } };
- rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
- vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- vkCmdDraw(cmd, 3, 1, 0, 0);
- vkCmdEndRenderPass(cmd);
- VkBufferImageCopy region{};
- region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
- region.imageExtent = { W, H, 1 };
- vkCmdCopyImageToBuffer(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging, 1, &region);
- VK_CHECK(vkEndCommandBuffer(cmd));
-
- VkFence fence = VK_NULL_HANDLE;
- VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
- VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
- VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
- submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
- VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
-
- // Verify: centre pixel should be the mid-gradient (not black)
- void* mapped = nullptr;
- VK_CHECK(vkMapMemory(v.device, stagingMem, 0, bci.size, 0, &mapped));
- const uint8_t* px = (const uint8_t*)mapped;
- size_t c = ((size_t)(H / 2) * W + (W / 2)) * 4;
- DONUT_INFO("Vulkan triangle self-test: centre pixel RGBA = ({}, {}, {}, {})",
- (int)px[c + 0], (int)px[c + 1], (int)px[c + 2], (int)px[c + 3]);
- bool ok = (px[c + 0] > 40 || px[c + 1] > 40) && px[c + 3] == 255;
- vkUnmapMemory(v.device, stagingMem);
- DONUT_INFO("Vulkan triangle self-test: {}", ok ? "PASS" : "FAIL");
-
- // Cleanup
- vkDestroyFence(v.device, fence, nullptr);
- vkDestroyCommandPool(v.device, pool, nullptr);
- vkDestroyBuffer(v.device, staging, nullptr);
- vkFreeMemory(v.device, stagingMem, nullptr);
- vkDestroyPipeline(v.device, pipeline, nullptr);
- vkDestroyPipelineLayout(v.device, layout, nullptr);
- vkDestroyShaderModule(v.device, vmod, nullptr);
- vkDestroyShaderModule(v.device, fmod, nullptr);
- vkDestroyFramebuffer(v.device, fb, nullptr);
- vkDestroyRenderPass(v.device, renderPass, nullptr);
- vkDestroyImageView(v.device, view, nullptr);
- vkDestroyImage(v.device, image, nullptr);
- vkFreeMemory(v.device, imageMem, nullptr);
- return ok;
- }
-
- bool VulkanContext::RenderGeodesic(const char* pngPath)
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) return false;
-
- const uint32_t W = 384, H = 216;
- const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
- const float SagA_rs = 1.269e10f;
-
- // Offscreen colour target
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
- VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { W, H, 1 };
- ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
- ici.tiling = VK_IMAGE_TILING_OPTIMAL;
- ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
- VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
- VkImageView view = VK_NULL_HANDLE;
- VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
- vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(v.device, &vci, nullptr, &view));
-
- VkAttachmentDescription color{};
- color.format = fmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorRef;
- VkSubpassDependency dep{};
- dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL;
- dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
- VkRenderPass renderPass = VK_NULL_HANDLE;
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color;
- rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
- rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
- VkFramebuffer fb = VK_NULL_HANDLE;
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
- fbci.width = W; fbci.height = H; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
-
- // Uniform buffers (host-visible), filled to match the shader's std140 layout
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- VkBuffer camBuf, diskBuf, objBuf, simBuf;
- VkDeviceMemory camMem, diskMem, objMem, simMem;
- v.CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, camBuf, camMem);
- v.CreateBuffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, diskBuf, diskMem);
- v.CreateBuffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, objBuf, objMem);
- v.CreateBuffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, simBuf, simMem);
-
- struct CamUBO {
- glm::vec3 pos; float p0; glm::vec3 right; float p1;
- glm::vec3 up; float p2; glm::vec3 fwd; float p3;
- float tanHalfFov; float aspect; uint32_t moving; int p4;
- } cam{};
- glm::vec3 camPos(1e11f, 0.32e11f, 0.0f);
- glm::vec3 fwd = glm::normalize(glm::vec3(0.0f) - camPos);
- glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
- glm::vec3 up = glm::cross(right, fwd);
- cam.pos = camPos; cam.right = right; cam.up = up; cam.fwd = fwd;
- cam.tanHalfFov = 0.57735f; cam.aspect = (float)W / (float)H; cam.moving = 0;
- void* p = nullptr;
- vkMapMemory(v.device, camMem, 0, 128, 0, &p); memcpy(p, &cam, sizeof(cam)); vkUnmapMemory(v.device, camMem);
-
- float disk[8] = { SagA_rs * 2.2f, SagA_rs * 5.2f, 2.0f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 };
- vkMapMemory(v.device, diskMem, 0, 32, 0, &p); memcpy(p, disk, sizeof(disk)); vkUnmapMemory(v.device, diskMem);
-
- std::vector<uint8_t> objData(800, 0);
- int numObjects = 1; memcpy(objData.data(), &numObjects, 4);
- float posRadius[4] = { 0, 0, 0, SagA_rs }; memcpy(objData.data() + 16, posRadius, 16);
- float objColor[4] = { 0, 0, 0, 1 }; memcpy(objData.data() + 272, objColor, 16);
- vkMapMemory(v.device, objMem, 0, 800, 0, &p); memcpy(p, objData.data(), 800); vkUnmapMemory(v.device, objMem);
-
- struct SimUBO { int stepsMoving; int stepsStatic; float earlyExit; float time; } sim{ 6000, 6000, 5e12f, 0.0f };
- vkMapMemory(v.device, simMem, 0, 16, 0, &p); memcpy(p, &sim, sizeof(sim)); vkUnmapMemory(v.device, simMem);
-
- // Dark cubemap (stands in for the HDRI for now)
- VkImage cube = VK_NULL_HANDLE; VkDeviceMemory cubeMem = VK_NULL_HANDLE;
- VkImageCreateInfo cci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- cci.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- cci.imageType = VK_IMAGE_TYPE_2D; cci.format = fmt; cci.extent = { 1, 1, 1 };
- cci.mipLevels = 1; cci.arrayLayers = 6; cci.samples = VK_SAMPLE_COUNT_1_BIT;
- cci.tiling = VK_IMAGE_TILING_OPTIMAL;
- cci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
- VK_CHECK(vkCreateImage(v.device, &cci, nullptr, &cube));
- VkMemoryRequirements cubeReq{}; vkGetImageMemoryRequirements(v.device, cube, &cubeReq);
- VkMemoryAllocateInfo cubeAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- cubeAlloc.allocationSize = cubeReq.size;
- cubeAlloc.memoryTypeIndex = v.FindMemoryType(cubeReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &cubeAlloc, nullptr, &cubeMem));
- VK_CHECK(vkBindImageMemory(v.device, cube, cubeMem, 0));
- VkImageView cubeView = VK_NULL_HANDLE;
- VkImageViewCreateInfo cvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- cvci.image = cube; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = fmt;
- cvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- VK_CHECK(vkCreateImageView(v.device, &cvci, nullptr, &cubeView));
- VkSampler sampler = VK_NULL_HANDLE;
- VkSamplerCreateInfo smci{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
- smci.magFilter = VK_FILTER_LINEAR; smci.minFilter = VK_FILTER_LINEAR;
- smci.addressModeU = smci.addressModeV = smci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(v.device, &smci, nullptr, &sampler));
-
- VkBuffer cubeStaging; VkDeviceMemory cubeStagingMem;
- v.CreateBuffer(6 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, hostVis, cubeStaging, cubeStagingMem);
- uint8_t cubePixels[6 * 4];
- for (int i = 0; i < 6; ++i) { cubePixels[i * 4 + 0] = 6; cubePixels[i * 4 + 1] = 6; cubePixels[i * 4 + 2] = 14; cubePixels[i * 4 + 3] = 255; }
- vkMapMemory(v.device, cubeStagingMem, 0, 24, 0, &p); memcpy(p, cubePixels, 24); vkUnmapMemory(v.device, cubeStagingMem);
-
- // Descriptor set: 4 UBOs (bindings 0-3) + cubemap sampler (binding 4)
- VkDescriptorSetLayoutBinding binds[5]{};
- for (int i = 0; i < 4; ++i) { binds[i].binding = i; binds[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[i].descriptorCount = 1; binds[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; }
- binds[4].binding = 4; binds[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[4].descriptorCount = 1; binds[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
- VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
- dslci.bindingCount = 5; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(v.device, &dslci, nullptr, &setLayout));
- VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } };
- VkDescriptorPool pool = VK_NULL_HANDLE;
- VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
- dpci.maxSets = 1; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
- VK_CHECK(vkCreateDescriptorPool(v.device, &dpci, nullptr, &pool));
- VkDescriptorSet set = VK_NULL_HANDLE;
- VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &setLayout;
- VK_CHECK(vkAllocateDescriptorSets(v.device, &dsai, &set));
-
- // Load geodesic SPIR-V + build the pipeline
- auto vspv = LoadSpirv("Assets/Shaders/generated/Geodesic.vertexMain.spv");
- auto fspv = LoadSpirv("Assets/Shaders/generated/Geodesic.fragmentMain.spv");
- if (vspv.empty() || fspv.empty()) { DONUT_ERROR("Vulkan: geodesic SPIR-V not found"); return false; }
- VkShaderModule vmod, fmod;
- VkShaderModuleCreateInfo smci2{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
- smci2.codeSize = vspv.size() * 4; smci2.pCode = vspv.data(); VK_CHECK(vkCreateShaderModule(v.device, &smci2, nullptr, &vmod));
- smci2.codeSize = fspv.size() * 4; smci2.pCode = fspv.data(); VK_CHECK(vkCreateShaderModule(v.device, &smci2, nullptr, &fmod));
-
- VkPipelineLayout layout = VK_NULL_HANDLE;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &setLayout;
- VK_CHECK(vkCreatePipelineLayout(v.device, &plci, nullptr, &layout));
-
- VkPipelineShaderStageCreateInfo stages[2]{};
- stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
- stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
- VkVertexInputBindingDescription vib{ 0, 16, VK_VERTEX_INPUT_RATE_VERTEX };
- VkVertexInputAttributeDescription via[2] = { { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32_SFLOAT, 8 } };
- VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
- vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib;
- vin.vertexAttributeDescriptionCount = 2; vin.pVertexAttributeDescriptions = via;
- VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- VkViewport vp{ 0, 0, (float)W, (float)H, 0, 1 }; VkRect2D sc{ { 0, 0 }, { W, H } };
- VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = &sc;
- VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
- VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
- VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
- VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba;
- VkPipeline pipeline = VK_NULL_HANDLE;
- VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
- gpci.stageCount = 2; gpci.pStages = stages;
- gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
- gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = layout; gpci.renderPass = renderPass; gpci.subpass = 0;
- VK_CHECK(vkCreateGraphicsPipelines(v.device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline));
-
- // Fullscreen quad (position.xy, texcoord.uv)
- float quad[] = {
- -1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f,
- -1.f, 1.f, 0.f, 1.f, 1.f, -1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
- };
- VkBuffer vbuf; VkDeviceMemory vbufMem;
- v.CreateBuffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, vbuf, vbufMem);
- vkMapMemory(v.device, vbufMem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(v.device, vbufMem);
-
- // Write the descriptor set
- VkDescriptorBufferInfo bi[4] = {
- { camBuf, 0, VK_WHOLE_SIZE }, { diskBuf, 0, VK_WHOLE_SIZE }, { objBuf, 0, VK_WHOLE_SIZE }, { simBuf, 0, VK_WHOLE_SIZE } };
- VkDescriptorImageInfo ii{ sampler, cubeView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
- VkWriteDescriptorSet writes[5]{};
- for (int i = 0; i < 4; ++i) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].dstSet = set; writes[i].dstBinding = i; writes[i].descriptorCount = 1; writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writes[i].pBufferInfo = &bi[i]; }
- writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[4].dstSet = set; writes[4].dstBinding = 4; writes[4].descriptorCount = 1; writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[4].pImageInfo = &ii;
- vkUpdateDescriptorSets(v.device, 5, writes, 0, nullptr);
-
- VkBuffer readback; VkDeviceMemory readbackMem;
- v.CreateBuffer((VkDeviceSize)W * H * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT, hostVis, readback, readbackMem);
-
- // Record + submit
- VkCommandPool cpool = VK_NULL_HANDLE;
- VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; pci.queueFamilyIndex = v.graphicsFamily;
- VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &cpool));
- VkCommandBuffer cmd = VK_NULL_HANDLE;
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; cbai.commandPool = cpool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
- VK_CHECK(vkAllocateCommandBuffers(v.device, &cbai, &cmd));
- VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
-
- VkImageMemoryBarrier toDst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
- toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- toDst.image = cube; toDst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- toDst.srcAccessMask = 0; toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toDst);
- VkBufferImageCopy cubeCopy{}; cubeCopy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6 }; cubeCopy.imageExtent = { 1, 1, 1 };
- vkCmdCopyBufferToImage(cmd, cubeStaging, cube, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cubeCopy);
- VkImageMemoryBarrier toRead = toDst;
- toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
-
- VkClearValue clear{}; clear.color = { { 0, 0, 0, 1 } };
- VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb; rpbi.renderArea = { { 0, 0 }, { W, H } };
- rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
- vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, &set, 0, nullptr);
- VkDeviceSize voff = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, &voff);
- vkCmdDraw(cmd, 6, 1, 0, 0);
- vkCmdEndRenderPass(cmd);
- VkBufferImageCopy region{}; region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; region.imageExtent = { W, H, 1 };
- vkCmdCopyImageToBuffer(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readback, 1, &region);
- VK_CHECK(vkEndCommandBuffer(cmd));
-
- VkFence fence = VK_NULL_HANDLE; VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
- VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
- VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
- VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
-
- vkMapMemory(v.device, readbackMem, 0, (VkDeviceSize)W * H * 4, 0, &p);
- stbi_write_png(pngPath, W, H, 4, p, W * 4);
- vkUnmapMemory(v.device, readbackMem);
- DONUT_INFO("Vulkan geodesic render written to {}", pngPath);
-
- vkDestroyFence(v.device, fence, nullptr);
- vkDestroyCommandPool(v.device, cpool, nullptr);
- vkDestroyBuffer(v.device, readback, nullptr); vkFreeMemory(v.device, readbackMem, nullptr);
- vkDestroyBuffer(v.device, vbuf, nullptr); vkFreeMemory(v.device, vbufMem, nullptr);
- vkDestroyPipeline(v.device, pipeline, nullptr); vkDestroyPipelineLayout(v.device, layout, nullptr);
- vkDestroyShaderModule(v.device, vmod, nullptr); vkDestroyShaderModule(v.device, fmod, nullptr);
- vkDestroyDescriptorPool(v.device, pool, nullptr); vkDestroyDescriptorSetLayout(v.device, setLayout, nullptr);
- vkDestroySampler(v.device, sampler, nullptr); vkDestroyImageView(v.device, cubeView, nullptr);
- vkDestroyImage(v.device, cube, nullptr); vkFreeMemory(v.device, cubeMem, nullptr);
- vkDestroyBuffer(v.device, cubeStaging, nullptr); vkFreeMemory(v.device, cubeStagingMem, nullptr);
- vkDestroyBuffer(v.device, camBuf, nullptr); vkFreeMemory(v.device, camMem, nullptr);
- vkDestroyBuffer(v.device, diskBuf, nullptr); vkFreeMemory(v.device, diskMem, nullptr);
- vkDestroyBuffer(v.device, objBuf, nullptr); vkFreeMemory(v.device, objMem, nullptr);
- vkDestroyBuffer(v.device, simBuf, nullptr); vkFreeMemory(v.device, simMem, nullptr);
- vkDestroyFramebuffer(v.device, fb, nullptr); vkDestroyRenderPass(v.device, renderPass, nullptr);
- vkDestroyImageView(v.device, view, nullptr); vkDestroyImage(v.device, image, nullptr); vkFreeMemory(v.device, imageMem, nullptr);
- return true;
- }
-
- void VulkanContext::Shutdown()
- {
- Impl& v = *m_Impl;
- if (v.device) { vkDestroyDevice(v.device, nullptr); v.device = VK_NULL_HANDLE; }
- if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; }
- }
-
- bool VulkanSelfTest()
- {
- VulkanContext ctx;
- if (!ctx.Init())
- {
- DONUT_ERROR("Vulkan: initialization failed");
- return false;
- }
- bool ok = ctx.SelfTestClear();
- ok = ctx.SelfTestTriangle() && ok;
- ctx.Shutdown();
- return ok;
- }
-}
diff --git a/src/Platform/Vulkan/VulkanContext.h b/src/Platform/Vulkan/VulkanContext.h
deleted file mode 100644
index 71bceda..0000000
--- a/src/Platform/Vulkan/VulkanContext.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-// Pure-C++ interface to the Vulkan backend (no vulkan.h leaks into the rest of
-// the engine; the implementation lives in VulkanContext.cpp). On macOS Vulkan
-// runs through MoltenVK (Vulkan -> Metal).
-namespace Donut
-{
- class VulkanContext
- {
- public:
- VulkanContext();
- ~VulkanContext();
-
- // Creates the instance, picks a physical device, and creates the logical
- // device + graphics queue. Returns false (and logs) on failure.
- bool Init();
- void Shutdown();
-
- // Phase 1 verification: renders a known clear colour into an offscreen
- // image and reads it back, confirming instance -> device -> render pass
- // -> command buffer -> submit -> read-back all work end to end.
- bool SelfTestClear();
-
- // Phase 2/3 verification: builds a graphics pipeline from Slang-compiled
- // SPIR-V and draws a full-screen gradient triangle into the offscreen
- // image, confirming the SPIR-V -> pipeline -> draw path works.
- bool SelfTestTriangle();
-
- // B-3: renders the geodesic (black hole) fragment shader through Vulkan
- // into an offscreen image and writes it to pngPath. Exercises UBOs,
- // descriptor sets, a cubemap sampler and the geodesic pipeline.
- bool RenderGeodesic(const char* pngPath);
-
- private:
- struct Impl;
- Impl* m_Impl = nullptr;
- };
-
- // Convenience one-shot: Init() + SelfTestClear() + Shutdown(). Logs results.
- bool VulkanSelfTest();
-}
diff --git a/src/Platform/Vulkan/VulkanIndexBuffer.cpp b/src/Platform/Vulkan/VulkanIndexBuffer.cpp
deleted file mode 100644
index 930401e..0000000
--- a/src/Platform/Vulkan/VulkanIndexBuffer.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "VulkanIndexBuffer.h"
-
-namespace Donut
-{
- VulkanIndexBuffer::VulkanIndexBuffer(uint32_t* indices, uint32_t count)
- : m_Count(count)
- {
- // TODO(Hachem): Implement Vulkan index buffer creation
- }
-
- VulkanIndexBuffer::~VulkanIndexBuffer()
- {
- // TODO(Hachem): Implement Vulkan index buffer cleanup
- }
-
- void VulkanIndexBuffer::Bind() const
- {
- // TODO(Hachem): Implement Vulkan index buffer binding
- }
-
- void VulkanIndexBuffer::Unbind() const
- {
- // TODO(Hachem): Implement Vulkan index buffer unbinding
- }
-};
diff --git a/src/Platform/Vulkan/VulkanIndexBuffer.h b/src/Platform/Vulkan/VulkanIndexBuffer.h
deleted file mode 100644
index 74e69cf..0000000
--- a/src/Platform/Vulkan/VulkanIndexBuffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- class VulkanIndexBuffer
- : public IndexBuffer
- {
- public:
- VulkanIndexBuffer(uint32_t* indices, uint32_t count);
- virtual ~VulkanIndexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual uint32_t GetCount() const override { return m_Count; }
- private:
- uint32_t m_RendererID;
- uint32_t m_Count;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanRenderer.cpp b/src/Platform/Vulkan/VulkanRenderer.cpp
deleted file mode 100644
index eb92695..0000000
--- a/src/Platform/Vulkan/VulkanRenderer.cpp
+++ /dev/null
@@ -1,1235 +0,0 @@
-#include "VulkanRenderer.h"
-#include "Core/Log.h"
-#include "Core/Camera.h"
-
-#define GLFW_INCLUDE_VULKAN
-#include <GLFW/glfw3.h>
-
-#include <imgui.h>
-#include <imgui_impl_glfw.h>
-#include <imgui_impl_vulkan.h>
-
-#include <glm/glm.hpp>
-#include <glm/gtc/matrix_transform.hpp>
-#include "stb_image.h"
-
-#include <vector>
-#include <algorithm>
-#include <cstring>
-#include <cstdlib>
-#include <fstream>
-
-namespace Donut
-{
- #define VK_CHECK(expr) \
- do { \
- VkResult _r = (expr); \
- if (_r != VK_SUCCESS) { \
- DONUT_ERROR("Vulkan: {} failed ({})", #expr, (int)_r); \
- return false; \
- } \
- } while (0)
-
- static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
-
- void VulkanPrepareGLFW()
- {
-#ifdef __APPLE__
- if (!getenv("VK_ICD_FILENAMES"))
- setenv("VK_ICD_FILENAMES", "/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json", 0);
- if (!getenv("VK_LAYER_PATH"))
- setenv("VK_LAYER_PATH", "/opt/homebrew/share/vulkan/explicit_layer.d", 0);
- if (!getenv("DYLD_LIBRARY_PATH"))
- setenv("DYLD_LIBRARY_PATH", "/opt/homebrew/lib", 0);
-#endif
- // GLFW dlopen's the Vulkan loader by bare name, which fails on
- // macOS/Homebrew; hand it the loader entry point we already link against.
- glfwInitVulkanLoader(vkGetInstanceProcAddr);
- }
-
- struct VulkanRenderer::Impl
- {
- GLFWwindow* window = nullptr;
- int width = 0, height = 0;
- bool framebufferResized = false;
-
- VkInstance instance = VK_NULL_HANDLE;
- VkSurfaceKHR surface = VK_NULL_HANDLE;
- VkPhysicalDevice physical = VK_NULL_HANDLE;
- VkDevice device = VK_NULL_HANDLE;
- uint32_t graphicsFamily = 0, presentFamily = 0;
- VkQueue graphicsQueue = VK_NULL_HANDLE, presentQueue = VK_NULL_HANDLE;
-
- VkSwapchainKHR swapchain = VK_NULL_HANDLE;
- VkFormat swapchainFormat = VK_FORMAT_B8G8R8A8_UNORM;
- VkExtent2D swapchainExtent{};
- std::vector<VkImage> images;
- std::vector<VkImageView> imageViews;
- VkRenderPass renderPass = VK_NULL_HANDLE;
- std::vector<VkFramebuffer> framebuffers;
-
- VkCommandPool commandPool = VK_NULL_HANDLE;
- std::vector<VkCommandBuffer> commandBuffers; // MAX_FRAMES_IN_FLIGHT
-
- std::vector<VkSemaphore> imageAvailable; // per frame in flight
- std::vector<VkSemaphore> renderFinished; // per swapchain image
- std::vector<VkFence> inFlight; // per frame in flight
- std::vector<VkFence> imagesInFlight; // per swapchain image
- uint32_t currentFrame = 0;
-
- VkDescriptorPool imguiPool = VK_NULL_HANDLE;
- bool imguiInit = false;
-
- VkPhysicalDeviceMemoryProperties memProps{};
-
- // Geodesic scene, rendered every frame into a fixed low-resolution
- // offscreen image (keeps each draw well under the Metal GPU watchdog),
- // then upscaled onto the swapchain by the present pass below.
- static constexpr uint32_t GEO_W = 480, GEO_H = 270;
- VkImage geoImage = VK_NULL_HANDLE;
- VkDeviceMemory geoImageMem = VK_NULL_HANDLE;
- VkImageView geoImageView = VK_NULL_HANDLE;
- VkRenderPass geoRenderPass = VK_NULL_HANDLE;
- VkFramebuffer geoFramebuffer = VK_NULL_HANDLE;
- VkBuffer camBuf = VK_NULL_HANDLE, diskBuf = VK_NULL_HANDLE, objBuf = VK_NULL_HANDLE, simBuf = VK_NULL_HANDLE;
- VkDeviceMemory camMem = VK_NULL_HANDLE, diskMem = VK_NULL_HANDLE, objMem = VK_NULL_HANDLE, simMem = VK_NULL_HANDLE;
- void* camMapped = nullptr;
- void* simMapped = nullptr;
- Camera camera{ 60.0f, (float)GEO_W / (float)GEO_H, 0.1f, 100.0f };
- bool leftWasDown = false;
- VkImage cubeImage = VK_NULL_HANDLE; VkDeviceMemory cubeMem = VK_NULL_HANDLE;
- VkImageView cubeView = VK_NULL_HANDLE; VkSampler cubeSampler = VK_NULL_HANDLE;
- VkDescriptorSetLayout geoSetLayout = VK_NULL_HANDLE;
- VkDescriptorPool geoPool = VK_NULL_HANDLE;
- VkDescriptorSet geoSet = VK_NULL_HANDLE;
- VkPipelineLayout geoPipelineLayout = VK_NULL_HANDLE;
- VkPipeline geoPipeline = VK_NULL_HANDLE;
- VkBuffer quadVB = VK_NULL_HANDLE; VkDeviceMemory quadVBMem = VK_NULL_HANDLE;
- double startTime = 0.0;
- VkFence geoInUse = VK_NULL_HANDLE; // previous frame's fence; guards the shared geodesic image
-
- // Present pass: samples the geodesic image with a full-screen textured
- // quad, drawn into the swapchain render pass just before the ImGui UI.
- VkSampler presentSampler = VK_NULL_HANDLE;
- VkDescriptorSetLayout presentSetLayout = VK_NULL_HANDLE;
- VkDescriptorPool presentPool = VK_NULL_HANDLE;
- VkDescriptorSet presentSet = VK_NULL_HANDLE;
- VkPipelineLayout presentPipelineLayout = VK_NULL_HANDLE;
- VkPipeline presentPipeline = VK_NULL_HANDLE;
-
- uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const;
- bool CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buf, VkDeviceMemory& mem) const;
- static std::vector<uint32_t> LoadSpirv(const std::string& path);
- bool CreateShaderModule(const std::string& path, VkShaderModule& out) const;
-
- bool CreateInstance();
- bool PickPhysicalAndDevice();
- bool CreateSwapchain();
- bool CreateImageViews();
- bool CreateRenderPass();
- bool CreateFramebuffers();
- bool CreateCommandBuffers();
- bool CreateSyncObjects();
- bool CreateGeodesicResources();
- bool CreateHDRICubemap(const char* path);
- bool CreatePresentResources();
- void ProcessInput();
- void UpdateGeodesicUniforms();
- void DestroyGeodesicResources();
- bool RecreateSwapchain();
- void CleanupSwapchain();
- bool RecordCommandBuffer(VkCommandBuffer cmd, uint32_t imageIndex, const glm::vec4& clear, ImDrawData* drawData);
- };
-
- bool VulkanRenderer::Impl::CreateInstance()
- {
-#ifdef __APPLE__
- if (!getenv("VK_ICD_FILENAMES"))
- setenv("VK_ICD_FILENAMES", "/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json", 0);
- if (!getenv("VK_LAYER_PATH"))
- setenv("VK_LAYER_PATH", "/opt/homebrew/share/vulkan/explicit_layer.d", 0);
- // The Homebrew validation-layer manifest names the dylib without a path;
- // let dlopen find it in the Homebrew lib dir.
- if (!getenv("DYLD_LIBRARY_PATH"))
- setenv("DYLD_LIBRARY_PATH", "/opt/homebrew/lib", 0);
-#endif
- VkApplicationInfo app{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
- app.pApplicationName = "Donut";
- app.apiVersion = VK_API_VERSION_1_2;
-
- uint32_t glfwExtCount = 0;
- const char** glfwExts = glfwGetRequiredInstanceExtensions(&glfwExtCount);
- if (!glfwExts) { DONUT_ERROR("Vulkan: GLFW reports no surface support"); return false; }
- std::vector<const char*> exts;
- for (uint32_t i = 0; i < glfwExtCount; ++i) exts.push_back(glfwExts[i]);
- exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
- exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
-
- std::vector<const char*> layers;
- uint32_t layerCount = 0;
- vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
- std::vector<VkLayerProperties> avail(layerCount);
- vkEnumerateInstanceLayerProperties(&layerCount, avail.data());
- for (const auto& l : avail)
- if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0)
- layers.push_back("VK_LAYER_KHRONOS_validation");
-
- VkInstanceCreateInfo ici{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
- ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
- ici.pApplicationInfo = &app;
- ici.enabledExtensionCount = (uint32_t)exts.size();
- ici.ppEnabledExtensionNames = exts.data();
- ici.enabledLayerCount = (uint32_t)layers.size();
- ici.ppEnabledLayerNames = layers.data();
-
- VkResult r = vkCreateInstance(&ici, nullptr, &instance);
- if (r != VK_SUCCESS && !layers.empty())
- {
- // The validation layer failed to load (its dylib isn't on the loader
- // search path); it is optional, so retry without it.
- DONUT_WARN("Vulkan: validation layer unavailable, continuing without it");
- layers.clear();
- ici.enabledLayerCount = 0;
- ici.ppEnabledLayerNames = nullptr;
- r = vkCreateInstance(&ici, nullptr, &instance);
- }
- if (r != VK_SUCCESS) { DONUT_ERROR("Vulkan: vkCreateInstance failed ({})", (int)r); return false; }
-
- VK_CHECK(glfwCreateWindowSurface(instance, window, nullptr, &surface));
- DONUT_INFO("Vulkan: instance + surface created (validation {})", layers.empty() ? "off" : "on");
- return true;
- }
-
- bool VulkanRenderer::Impl::PickPhysicalAndDevice()
- {
- uint32_t count = 0;
- vkEnumeratePhysicalDevices(instance, &count, nullptr);
- if (count == 0) { DONUT_ERROR("Vulkan: no physical devices"); return false; }
- std::vector<VkPhysicalDevice> devices(count);
- vkEnumeratePhysicalDevices(instance, &count, devices.data());
- physical = devices[0];
-
- uint32_t qCount = 0;
- vkGetPhysicalDeviceQueueFamilyProperties(physical, &qCount, nullptr);
- std::vector<VkQueueFamilyProperties> qfams(qCount);
- vkGetPhysicalDeviceQueueFamilyProperties(physical, &qCount, qfams.data());
- bool foundG = false, foundP = false;
- for (uint32_t i = 0; i < qCount; ++i)
- {
- if (!foundG && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { graphicsFamily = i; foundG = true; }
- VkBool32 present = VK_FALSE;
- vkGetPhysicalDeviceSurfaceSupportKHR(physical, i, surface, &present);
- if (!foundP && present) { presentFamily = i; foundP = true; }
- }
- if (!foundG || !foundP) { DONUT_ERROR("Vulkan: no graphics/present queue"); return false; }
-
- std::vector<const char*> devExts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
- uint32_t devExtCount = 0;
- vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, nullptr);
- std::vector<VkExtensionProperties> devExtProps(devExtCount);
- vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, devExtProps.data());
- for (const auto& e : devExtProps)
- if (std::strcmp(e.extensionName, "VK_KHR_portability_subset") == 0)
- devExts.push_back("VK_KHR_portability_subset");
-
- float priority = 1.0f;
- std::vector<VkDeviceQueueCreateInfo> qcis;
- uint32_t families[2] = { graphicsFamily, presentFamily };
- for (uint32_t i = 0; i < (graphicsFamily == presentFamily ? 1u : 2u); ++i)
- {
- VkDeviceQueueCreateInfo qci{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
- qci.queueFamilyIndex = families[i];
- qci.queueCount = 1;
- qci.pQueuePriorities = &priority;
- qcis.push_back(qci);
- }
- VkDeviceCreateInfo dci{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
- dci.queueCreateInfoCount = (uint32_t)qcis.size();
- dci.pQueueCreateInfos = qcis.data();
- dci.enabledExtensionCount = (uint32_t)devExts.size();
- dci.ppEnabledExtensionNames = devExts.data();
- VK_CHECK(vkCreateDevice(physical, &dci, nullptr, &device));
- vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
- vkGetDeviceQueue(device, presentFamily, 0, &presentQueue);
-
- VkPhysicalDeviceProperties props{};
- vkGetPhysicalDeviceProperties(physical, &props);
- vkGetPhysicalDeviceMemoryProperties(physical, &memProps);
- DONUT_INFO("Vulkan device: {}", props.deviceName);
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateSwapchain()
- {
- VkSurfaceCapabilitiesKHR caps{};
- vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical, surface, &caps);
-
- uint32_t fmtCount = 0;
- vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, nullptr);
- std::vector<VkSurfaceFormatKHR> formats(fmtCount);
- vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, formats.data());
- VkSurfaceFormatKHR chosen = formats[0];
- for (const auto& f : formats)
- if (f.format == VK_FORMAT_B8G8R8A8_UNORM && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
- chosen = f;
- swapchainFormat = chosen.format;
-
- if (caps.currentExtent.width != UINT32_MAX)
- swapchainExtent = caps.currentExtent;
- else
- {
- swapchainExtent.width = std::clamp((uint32_t)width, caps.minImageExtent.width, caps.maxImageExtent.width);
- swapchainExtent.height = std::clamp((uint32_t)height, caps.minImageExtent.height, caps.maxImageExtent.height);
- }
-
- uint32_t imageCount = caps.minImageCount + 1;
- if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount)
- imageCount = caps.maxImageCount;
-
- VkSwapchainCreateInfoKHR sci{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
- sci.surface = surface;
- sci.minImageCount = imageCount;
- sci.imageFormat = chosen.format;
- sci.imageColorSpace = chosen.colorSpace;
- sci.imageExtent = swapchainExtent;
- sci.imageArrayLayers = 1;
- sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
- sci.preTransform = caps.currentTransform;
- sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
- sci.presentMode = VK_PRESENT_MODE_FIFO_KHR; // always supported, vsync
- sci.clipped = VK_TRUE;
-
- uint32_t famIdx[2] = { graphicsFamily, presentFamily };
- if (graphicsFamily != presentFamily)
- {
- sci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
- sci.queueFamilyIndexCount = 2;
- sci.pQueueFamilyIndices = famIdx;
- }
- else
- sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-
- VK_CHECK(vkCreateSwapchainKHR(device, &sci, nullptr, &swapchain));
- uint32_t n = 0;
- vkGetSwapchainImagesKHR(device, swapchain, &n, nullptr);
- images.resize(n);
- vkGetSwapchainImagesKHR(device, swapchain, &n, images.data());
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateImageViews()
- {
- imageViews.resize(images.size());
- for (size_t i = 0; i < images.size(); ++i)
- {
- VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = images[i];
- vci.viewType = VK_IMAGE_VIEW_TYPE_2D;
- vci.format = swapchainFormat;
- vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &vci, nullptr, &imageViews[i]));
- }
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateRenderPass()
- {
- VkAttachmentDescription color{};
- color.format = swapchainFormat;
- color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- color.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-
- VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{};
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1;
- subpass.pColorAttachments = &ref;
-
- VkSubpassDependency dep{};
- dep.srcSubpass = VK_SUBPASS_EXTERNAL;
- dep.dstSubpass = 0;
- dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color;
- rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
- rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &renderPass));
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateFramebuffers()
- {
- framebuffers.resize(imageViews.size());
- for (size_t i = 0; i < imageViews.size(); ++i)
- {
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass;
- fbci.attachmentCount = 1; fbci.pAttachments = &imageViews[i];
- fbci.width = swapchainExtent.width; fbci.height = swapchainExtent.height; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &framebuffers[i]));
- }
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateCommandBuffers()
- {
- VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
- pci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- pci.queueFamilyIndex = graphicsFamily;
- VK_CHECK(vkCreateCommandPool(device, &pci, nullptr, &commandPool));
-
- commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool;
- cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
- cbai.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
- VK_CHECK(vkAllocateCommandBuffers(device, &cbai, commandBuffers.data()));
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateSyncObjects()
- {
- imageAvailable.resize(MAX_FRAMES_IN_FLIGHT);
- inFlight.resize(MAX_FRAMES_IN_FLIGHT);
- renderFinished.resize(images.size());
- imagesInFlight.assign(images.size(), VK_NULL_HANDLE);
-
- VkSemaphoreCreateInfo sci{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
- VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
- fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
- for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
- {
- VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &imageAvailable[i]));
- VK_CHECK(vkCreateFence(device, &fci, nullptr, &inFlight[i]));
- }
- for (size_t i = 0; i < images.size(); ++i)
- VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &renderFinished[i]));
- return true;
- }
-
- uint32_t VulkanRenderer::Impl::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const
- {
- for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
- if ((typeFilter & (1u << i)) && (memProps.memoryTypes[i].propertyFlags & flags) == flags)
- return i;
- return UINT32_MAX;
- }
-
- bool VulkanRenderer::Impl::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
- VkBuffer& buf, VkDeviceMemory& mem) const
- {
- VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
- bci.size = size; bci.usage = usage; bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- if (vkCreateBuffer(device, &bci, nullptr, &buf) != VK_SUCCESS) return false;
- VkMemoryRequirements req{}; vkGetBufferMemoryRequirements(device, buf, &req);
- VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- ai.allocationSize = req.size;
- ai.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, props);
- if (vkAllocateMemory(device, &ai, nullptr, &mem) != VK_SUCCESS) return false;
- vkBindBufferMemory(device, buf, mem, 0);
- return true;
- }
-
- std::vector<uint32_t> VulkanRenderer::Impl::LoadSpirv(const std::string& path)
- {
- std::ifstream file(path, std::ios::ate | std::ios::binary);
- if (!file.is_open()) return {};
- size_t size = (size_t)file.tellg();
- std::vector<uint32_t> data(size / 4);
- file.seekg(0);
- file.read(reinterpret_cast<char*>(data.data()), size);
- return data;
- }
-
- bool VulkanRenderer::Impl::CreateShaderModule(const std::string& path, VkShaderModule& out) const
- {
- auto spv = LoadSpirv(path);
- if (spv.empty()) { DONUT_ERROR("Vulkan: failed to load SPIR-V {}", path); return false; }
- VkShaderModuleCreateInfo ci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
- ci.codeSize = spv.size() * 4; ci.pCode = spv.data();
- return vkCreateShaderModule(device, &ci, nullptr, &out) == VK_SUCCESS;
- }
-
- bool VulkanRenderer::Impl::CreateGeodesicResources()
- {
- const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- const float SagA_rs = 1.269e10f;
-
- VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { GEO_W, GEO_H, 1 };
- ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
- ici.tiling = VK_IMAGE_TILING_OPTIMAL;
- ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
- VK_CHECK(vkCreateImage(device, &ici, nullptr, &geoImage));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(device, geoImage, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &imAlloc, nullptr, &geoImageMem));
- VK_CHECK(vkBindImageMemory(device, geoImage, geoImageMem, 0));
- VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = geoImage; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
- vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &vci, nullptr, &geoImageView));
-
- VkAttachmentDescription color{};
- color.format = fmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &ref;
- VkSubpassDependency deps[2]{};
- deps[0].srcSubpass = VK_SUBPASS_EXTERNAL; deps[0].dstSubpass = 0;
- deps[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
- deps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- deps[1].srcSubpass = 0; deps[1].dstSubpass = VK_SUBPASS_EXTERNAL;
- deps[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; deps[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- deps[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; deps[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color;
- rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
- rpci.dependencyCount = 2; rpci.pDependencies = deps;
- VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &geoRenderPass));
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = geoRenderPass; fbci.attachmentCount = 1; fbci.pAttachments = &geoImageView;
- fbci.width = GEO_W; fbci.height = GEO_H; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &geoFramebuffer));
-
- CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, camBuf, camMem);
- CreateBuffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, diskBuf, diskMem);
- CreateBuffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, objBuf, objMem);
- CreateBuffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, simBuf, simMem);
-
- camera.SetCameraMode(CameraMode::Orbital);
- camera.SetOrbitalTarget(glm::vec3(0.0f));
- camera.SetOrbitalRadius(1e11);
- camera.SetOrbitalLimits(4e10, 3e11);
- camera.SetOrbitalSpeed(0.01f);
- camera.SetZoomSpeed(1e10);
- camera.SetAzimuth(0.0f);
- camera.SetElevation(1.25f);
-
- void* p = nullptr;
- vkMapMemory(device, camMem, 0, 128, 0, &camMapped); // camera UBO is refilled every frame
-
- float diskData[8] = { SagA_rs * 2.2f, SagA_rs * 5.2f, 2.0f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 };
- vkMapMemory(device, diskMem, 0, 32, 0, &p); memcpy(p, diskData, sizeof(diskData)); vkUnmapMemory(device, diskMem);
-
- std::vector<uint8_t> objData(800, 0);
- int numObjects = 1; memcpy(objData.data(), &numObjects, 4);
- float posRadius[4] = { 0, 0, 0, SagA_rs }; memcpy(objData.data() + 16, posRadius, 16);
- float objColor[4] = { 0, 0, 0, 1 }; memcpy(objData.data() + 272, objColor, 16);
- vkMapMemory(device, objMem, 0, 800, 0, &p); memcpy(p, objData.data(), 800); vkUnmapMemory(device, objMem);
-
- vkMapMemory(device, simMem, 0, 16, 0, &simMapped);
-
- if (!CreateHDRICubemap("Assets/HDRI/HDR_blue_nebulae-1.hdr")) return false;
-
- VkDescriptorSetLayoutBinding binds[5]{};
- for (int i = 0; i < 4; ++i) { binds[i].binding = i; binds[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[i].descriptorCount = 1; binds[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; }
- binds[4].binding = 4; binds[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[4].descriptorCount = 1; binds[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
- dslci.bindingCount = 5; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &geoSetLayout));
- VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } };
- VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
- dpci.maxSets = 1; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
- VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &geoPool));
- VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = geoPool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &geoSetLayout;
- VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &geoSet));
- VkDescriptorBufferInfo bi[4] = { { camBuf, 0, VK_WHOLE_SIZE }, { diskBuf, 0, VK_WHOLE_SIZE }, { objBuf, 0, VK_WHOLE_SIZE }, { simBuf, 0, VK_WHOLE_SIZE } };
- VkDescriptorImageInfo cubeInfo{ cubeSampler, cubeView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
- VkWriteDescriptorSet writes[5]{};
- for (int i = 0; i < 4; ++i) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].dstSet = geoSet; writes[i].dstBinding = i; writes[i].descriptorCount = 1; writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writes[i].pBufferInfo = &bi[i]; }
- writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[4].dstSet = geoSet; writes[4].dstBinding = 4; writes[4].descriptorCount = 1; writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[4].pImageInfo = &cubeInfo;
- vkUpdateDescriptorSets(device, 5, writes, 0, nullptr);
-
- float quad[] = {
- -1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f,
- -1.f, 1.f, 0.f, 1.f, 1.f, -1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
- };
- CreateBuffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, quadVB, quadVBMem);
- vkMapMemory(device, quadVBMem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(device, quadVBMem);
-
- VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/Geodesic.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/Geodesic.fragmentMain.spv", fmod)) return false;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &geoSetLayout;
- VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &geoPipelineLayout));
- VkPipelineShaderStageCreateInfo stages[2]{};
- stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
- stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
- VkVertexInputBindingDescription vib{ 0, 16, VK_VERTEX_INPUT_RATE_VERTEX };
- VkVertexInputAttributeDescription via[2] = { { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32_SFLOAT, 8 } };
- VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
- vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib;
- vin.vertexAttributeDescriptionCount = 2; vin.pVertexAttributeDescriptions = via;
- VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- VkViewport vp{ 0, 0, (float)GEO_W, (float)GEO_H, 0, 1 }; VkRect2D sc{ { 0, 0 }, { GEO_W, GEO_H } };
- VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = &sc;
- VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
- VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
- VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
- VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba;
- VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
- gpci.stageCount = 2; gpci.pStages = stages;
- gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
- gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = geoPipelineLayout; gpci.renderPass = geoRenderPass; gpci.subpass = 0;
- VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &geoPipeline);
- vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
- if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: geodesic pipeline creation failed ({})", (int)pr); return false; }
-
- startTime = glfwGetTime();
- UpdateGeodesicUniforms();
- DONUT_INFO("Vulkan: geodesic resources ready ({}x{} offscreen)", (int)GEO_W, (int)GEO_H);
- return true;
- }
-
- bool VulkanRenderer::Impl::CreateHDRICubemap(const char* path)
- {
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- const uint32_t FACE = 1024;
- const VkFormat cubeFmt = VK_FORMAT_R16G16B16A16_SFLOAT;
-
- VkImageCreateInfo cci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- cci.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- cci.imageType = VK_IMAGE_TYPE_2D; cci.format = cubeFmt; cci.extent = { FACE, FACE, 1 };
- cci.mipLevels = 1; cci.arrayLayers = 6; cci.samples = VK_SAMPLE_COUNT_1_BIT;
- cci.tiling = VK_IMAGE_TILING_OPTIMAL;
- cci.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- VK_CHECK(vkCreateImage(device, &cci, nullptr, &cubeImage));
- VkMemoryRequirements creq{}; vkGetImageMemoryRequirements(device, cubeImage, &creq);
- VkMemoryAllocateInfo cai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- cai.allocationSize = creq.size; cai.memoryTypeIndex = FindMemoryType(creq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &cai, nullptr, &cubeMem));
- VK_CHECK(vkBindImageMemory(device, cubeImage, cubeMem, 0));
- VkImageViewCreateInfo cvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- cvci.image = cubeImage; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = cubeFmt;
- cvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- VK_CHECK(vkCreateImageView(device, &cvci, nullptr, &cubeView));
- VkSamplerCreateInfo csm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
- csm.magFilter = VK_FILTER_LINEAR; csm.minFilter = VK_FILTER_LINEAR;
- csm.addressModeU = csm.addressModeV = csm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &csm, nullptr, &cubeSampler));
-
- int w = 0, h = 0, ch = 0;
- float* pixels = stbi_loadf(path, &w, &h, &ch, 4);
- if (!pixels)
- {
- DONUT_WARN("Vulkan: HDRI '{}' could not be loaded; using a dark background", path);
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
- VkCommandBuffer cmd; VK_CHECK(vkAllocateCommandBuffers(device, &cbai, &cmd));
- VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- vkBeginCommandBuffer(cmd, &bi);
- VkImageMemoryBarrier tb{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
- tb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; tb.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- tb.image = cubeImage; tb.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- tb.srcAccessMask = 0; tb.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &tb);
- VkClearColorValue dark{}; dark.float32[0] = 0.02f; dark.float32[1] = 0.02f; dark.float32[2] = 0.05f; dark.float32[3] = 1.0f;
- VkImageSubresourceRange rng{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- vkCmdClearColorImage(cmd, cubeImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dark, 1, &rng);
- VkImageMemoryBarrier rb = tb; rb.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; rb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- rb.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; rb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &rb);
- vkEndCommandBuffer(cmd);
- VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd;
- vkQueueSubmit(graphicsQueue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(graphicsQueue);
- vkFreeCommandBuffers(device, commandPool, 1, &cmd);
- return true;
- }
-
- // Apple GPUs can't linearly filter RGBA32F, so store the equirect as
- // RGBA16F (convert the loaded floats to half on the way into staging).
- const VkFormat eqFmt = VK_FORMAT_R16G16B16A16_SFLOAT;
- size_t texelCount = (size_t)w * h * 4;
- VkDeviceSize eqSize = (VkDeviceSize)texelCount * sizeof(uint16_t);
- VkBuffer eqStaging; VkDeviceMemory eqStagingMem;
- if (!CreateBuffer(eqSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, hostVis, eqStaging, eqStagingMem)) { stbi_image_free(pixels); return false; }
- void* mp = nullptr; vkMapMemory(device, eqStagingMem, 0, eqSize, 0, &mp);
- uint16_t* dst = (uint16_t*)mp;
- for (size_t i = 0; i < texelCount; ++i) { __fp16 hf = (__fp16)pixels[i]; memcpy(&dst[i], &hf, sizeof(uint16_t)); }
- vkUnmapMemory(device, eqStagingMem);
- stbi_image_free(pixels);
-
- VkImage eqImage; VkDeviceMemory eqMem;
- VkImageCreateInfo eci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- eci.imageType = VK_IMAGE_TYPE_2D; eci.format = eqFmt; eci.extent = { (uint32_t)w, (uint32_t)h, 1 };
- eci.mipLevels = 1; eci.arrayLayers = 1; eci.samples = VK_SAMPLE_COUNT_1_BIT;
- eci.tiling = VK_IMAGE_TILING_OPTIMAL; eci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
- VK_CHECK(vkCreateImage(device, &eci, nullptr, &eqImage));
- VkMemoryRequirements ereq{}; vkGetImageMemoryRequirements(device, eqImage, &ereq);
- VkMemoryAllocateInfo eai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- eai.allocationSize = ereq.size; eai.memoryTypeIndex = FindMemoryType(ereq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &eai, nullptr, &eqMem));
- VK_CHECK(vkBindImageMemory(device, eqImage, eqMem, 0));
- VkImageView eqView;
- VkImageViewCreateInfo evci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- evci.image = eqImage; evci.viewType = VK_IMAGE_VIEW_TYPE_2D; evci.format = eqFmt;
- evci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &evci, nullptr, &eqView));
- VkSampler eqSampler;
- VkSamplerCreateInfo esm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
- esm.magFilter = VK_FILTER_LINEAR; esm.minFilter = VK_FILTER_LINEAR;
- esm.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // longitude wraps
- esm.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; // latitude clamps
- esm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &esm, nullptr, &eqSampler));
-
- VkImageView faceViews[6];
- for (uint32_t i = 0; i < 6; ++i)
- {
- VkImageViewCreateInfo fvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- fvci.image = cubeImage; fvci.viewType = VK_IMAGE_VIEW_TYPE_2D; fvci.format = cubeFmt;
- fvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, i, 1 };
- VK_CHECK(vkCreateImageView(device, &fvci, nullptr, &faceViews[i]));
- }
-
- VkAttachmentDescription color{};
- color.format = cubeFmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
- color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- VkAttachmentReference ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &ref;
- VkSubpassDependency dep{}; dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL;
- dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- dep.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dep.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- VkRenderPass rp;
- VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
- rpci.attachmentCount = 1; rpci.pAttachments = &color; rpci.subpassCount = 1; rpci.pSubpasses = &subpass; rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &rp));
- VkFramebuffer faceFB[6];
- for (uint32_t i = 0; i < 6; ++i)
- {
- VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = rp; fbci.attachmentCount = 1; fbci.pAttachments = &faceViews[i]; fbci.width = FACE; fbci.height = FACE; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &faceFB[i]));
- }
-
- VkDescriptorSetLayoutBinding binds[2]{};
- binds[0].binding = 0; binds[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[0].descriptorCount = 1; binds[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
- binds[1].binding = 1; binds[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[1].descriptorCount = 1; binds[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayout setLayout;
- VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dslci.bindingCount = 2; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &setLayout));
- VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 6 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6 } };
- VkDescriptorPool pool;
- VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 6; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
- VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &pool));
-
- VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/EquirectToCubemap.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/EquirectToCubemap.fragmentMain.spv", fmod)) return false;
- VkPipelineLayout playout;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &setLayout;
- VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &playout));
- VkPipelineShaderStageCreateInfo stages[2]{};
- stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
- stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
- VkVertexInputBindingDescription vib{ 0, 12, VK_VERTEX_INPUT_RATE_VERTEX };
- VkVertexInputAttributeDescription via{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 };
- VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
- vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib; vin.vertexAttributeDescriptionCount = 1; vin.pVertexAttributeDescriptions = &via;
- VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- VkViewport vp{ 0, 0, (float)FACE, (float)FACE, 0, 1 }; VkRect2D sc{ { 0, 0 }, { FACE, FACE } };
- VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = &sc;
- VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
- VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
- VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
- VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba;
- VkPipeline pipeline;
- VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
- gpci.stageCount = 2; gpci.pStages = stages; gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
- gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb; gpci.layout = playout; gpci.renderPass = rp; gpci.subpass = 0;
- VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline);
- vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
- if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: equirect pipeline failed ({})", (int)pr); return false; }
-
- float cubeVerts[] = {
- -1,1,-1, -1,-1,-1, 1,-1,-1, 1,-1,-1, 1,1,-1, -1,1,-1,
- -1,-1,1, -1,-1,-1, -1,1,-1, -1,1,-1, -1,1,1, -1,-1,1,
- 1,-1,-1, 1,-1,1, 1,1,1, 1,1,1, 1,1,-1, 1,-1,-1,
- -1,-1,1, -1,1,1, 1,1,1, 1,1,1, 1,-1,1, -1,-1,1,
- -1,1,-1, 1,1,-1, 1,1,1, 1,1,1, -1,1,1, -1,1,-1,
- -1,-1,-1, -1,-1,1, 1,-1,-1, 1,-1,-1, -1,-1,1, 1,-1,1,
- };
- VkBuffer cubeVB; VkDeviceMemory cubeVBMem;
- CreateBuffer(sizeof(cubeVerts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, cubeVB, cubeVBMem);
- vkMapMemory(device, cubeVBMem, 0, sizeof(cubeVerts), 0, &mp); memcpy(mp, cubeVerts, sizeof(cubeVerts)); vkUnmapMemory(device, cubeVBMem);
-
- glm::mat4 proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- proj[1][1] *= -1.0f; // Vulkan clip space is Y-down vs OpenGL
- glm::mat4 views[6] = {
- glm::lookAt(glm::vec3(0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)),
- glm::lookAt(glm::vec3(0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)),
- glm::lookAt(glm::vec3(0), glm::vec3( 0, 1, 0), glm::vec3(0, 0, 1)),
- glm::lookAt(glm::vec3(0), glm::vec3( 0, -1, 0), glm::vec3(0, 0, -1)),
- glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, 1), glm::vec3(0, -1, 0)),
- glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, -1), glm::vec3(0, -1, 0)),
- };
- VkBuffer ubo[6]; VkDeviceMemory uboMem[6]; VkDescriptorSet sets[6];
- for (uint32_t i = 0; i < 6; ++i)
- {
- CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, ubo[i], uboMem[i]);
- glm::mat4 mats[2] = { glm::transpose(proj), glm::transpose(views[i]) }; // SPIR-V expects row-major
- vkMapMemory(device, uboMem[i], 0, 128, 0, &mp); memcpy(mp, mats, 128); vkUnmapMemory(device, uboMem[i]);
- VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &setLayout;
- VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &sets[i]));
- VkDescriptorBufferInfo bufInfo{ ubo[i], 0, VK_WHOLE_SIZE };
- VkDescriptorImageInfo imgInfo{ eqSampler, eqView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
- VkWriteDescriptorSet ws[2]{};
- ws[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[0].dstSet = sets[i]; ws[0].dstBinding = 0; ws[0].descriptorCount = 1; ws[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ws[0].pBufferInfo = &bufInfo;
- ws[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[1].dstSet = sets[i]; ws[1].dstBinding = 1; ws[1].descriptorCount = 1; ws[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; ws[1].pImageInfo = &imgInfo;
- vkUpdateDescriptorSets(device, 2, ws, 0, nullptr);
- }
-
- VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
- VkCommandBuffer cmd; VK_CHECK(vkAllocateCommandBuffers(device, &cbai, &cmd));
- VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- VK_CHECK(vkBeginCommandBuffer(cmd, &bi));
- VkImageMemoryBarrier toDst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
- toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- toDst.image = eqImage; toDst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- toDst.srcAccessMask = 0; toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toDst);
- VkBufferImageCopy copy{}; copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copy.imageExtent = { (uint32_t)w, (uint32_t)h, 1 };
- vkCmdCopyBufferToImage(cmd, eqStaging, eqImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
- VkImageMemoryBarrier toRead = toDst; toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
-
- VkClearValue clear{}; clear.color = { { 0, 0, 0, 1 } };
- for (uint32_t i = 0; i < 6; ++i)
- {
- VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = rp; rpbi.framebuffer = faceFB[i]; rpbi.renderArea = { { 0, 0 }, { FACE, FACE } }; rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
- vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, playout, 0, 1, &sets[i], 0, nullptr);
- VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &cubeVB, &off);
- vkCmdDraw(cmd, 36, 1, 0, 0);
- vkCmdEndRenderPass(cmd);
- }
- VK_CHECK(vkEndCommandBuffer(cmd));
- VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(graphicsQueue, 1, &si, VK_NULL_HANDLE));
- VK_CHECK(vkQueueWaitIdle(graphicsQueue));
-
- vkFreeCommandBuffers(device, commandPool, 1, &cmd);
- for (uint32_t i = 0; i < 6; ++i) { vkDestroyBuffer(device, ubo[i], nullptr); vkFreeMemory(device, uboMem[i], nullptr); vkDestroyFramebuffer(device, faceFB[i], nullptr); vkDestroyImageView(device, faceViews[i], nullptr); }
- vkDestroyBuffer(device, cubeVB, nullptr); vkFreeMemory(device, cubeVBMem, nullptr);
- vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, playout, nullptr);
- vkDestroyDescriptorPool(device, pool, nullptr); vkDestroyDescriptorSetLayout(device, setLayout, nullptr);
- vkDestroyRenderPass(device, rp, nullptr);
- vkDestroySampler(device, eqSampler, nullptr); vkDestroyImageView(device, eqView, nullptr);
- vkDestroyImage(device, eqImage, nullptr); vkFreeMemory(device, eqMem, nullptr);
- vkDestroyBuffer(device, eqStaging, nullptr); vkFreeMemory(device, eqStagingMem, nullptr);
- DONUT_INFO("Vulkan: HDRI cubemap built from {} ({}x{} equirect -> {}^2 cube)", path, w, h, (int)FACE);
- return true;
- }
-
- bool VulkanRenderer::Impl::CreatePresentResources()
- {
- VkSamplerCreateInfo smci{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
- smci.magFilter = VK_FILTER_LINEAR; smci.minFilter = VK_FILTER_LINEAR;
- smci.addressModeU = smci.addressModeV = smci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &smci, nullptr, &presentSampler));
-
- VkDescriptorSetLayoutBinding bind{}; bind.binding = 0; bind.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bind.descriptorCount = 1; bind.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
- dslci.bindingCount = 1; dslci.pBindings = &bind;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &presentSetLayout));
- VkDescriptorPoolSize psize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 };
- VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
- dpci.maxSets = 1; dpci.poolSizeCount = 1; dpci.pPoolSizes = &psize;
- VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &presentPool));
- VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = presentPool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &presentSetLayout;
- VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &presentSet));
- VkDescriptorImageInfo ii{ presentSampler, geoImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
- VkWriteDescriptorSet write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- write.dstSet = presentSet; write.dstBinding = 0; write.descriptorCount = 1; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = &ii;
- vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-
- VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/TexturedQuad.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/TexturedQuad.fragmentMain.spv", fmod)) return false;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &presentSetLayout;
- VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &presentPipelineLayout));
- VkPipelineShaderStageCreateInfo stages[2]{};
- stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
- stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
- VkVertexInputBindingDescription vib{ 0, 16, VK_VERTEX_INPUT_RATE_VERTEX };
- VkVertexInputAttributeDescription via[2] = { { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32_SFLOAT, 8 } };
- VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
- vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib;
- vin.vertexAttributeDescriptionCount = 2; vin.pVertexAttributeDescriptions = via;
- VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.scissorCount = 1;
- VkDynamicState dyn[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
- VkPipelineDynamicStateCreateInfo dsci{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; dsci.dynamicStateCount = 2; dsci.pDynamicStates = dyn;
- VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
- VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
- VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
- VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba;
- VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
- gpci.stageCount = 2; gpci.pStages = stages;
- gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
- gpci.pDynamicState = &dsci;
- gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = presentPipelineLayout; gpci.renderPass = renderPass; gpci.subpass = 0;
- VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &presentPipeline);
- vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
- if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: present pipeline creation failed ({})", (int)pr); return false; }
- DONUT_INFO("Vulkan: present pipeline ready");
- return true;
- }
-
- void VulkanRenderer::Impl::UpdateGeodesicUniforms()
- {
- struct CamUBO {
- glm::vec3 pos; float p0; glm::vec3 right; float p1;
- glm::vec3 up; float p2; glm::vec3 fwd; float p3;
- float tanHalfFov; float aspect; uint32_t moving; int p4;
- } camData{};
- glm::vec3 pos = camera.GetOrbitalPosition();
- glm::vec3 fwd = glm::normalize(camera.GetOrbitalTarget() - pos);
- glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
- camData.pos = pos; camData.right = right; camData.up = glm::cross(right, fwd); camData.fwd = fwd;
- camData.tanHalfFov = (float)tan(glm::radians(60.0f * 0.5f));
- camData.aspect = (float)GEO_W / (float)GEO_H;
- camData.moving = camera.IsDragging() || camera.IsPanning() ? 1u : 0u;
- if (camMapped) memcpy(camMapped, &camData, sizeof(camData));
-
- // Fewer integration steps while the camera moves keeps dragging responsive;
- // more steps once it settles renders the disk in full.
- struct SimUBO { int stepsMoving; int stepsStatic; float earlyExit; float time; } sim;
- sim.stepsMoving = 3500; sim.stepsStatic = 5000; sim.earlyExit = 5e12f;
- sim.time = (float)(glfwGetTime() - startTime);
- if (simMapped) memcpy(simMapped, &sim, sizeof(sim));
- }
-
- static double g_ScrollAccum = 0.0;
- static GLFWscrollfun g_PrevScroll = nullptr;
- static void DonutVkScrollCallback(GLFWwindow* w, double x, double y)
- {
- if (g_PrevScroll) g_PrevScroll(w, x, y); // keep ImGui's scroll handling intact
- g_ScrollAccum += y;
- }
-
- void VulkanRenderer::Impl::ProcessInput()
- {
- bool overUI = imguiInit && ImGui::GetIO().WantCaptureMouse;
-
- bool leftDown = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
- if (leftDown && !leftWasDown && !overUI)
- camera.ProcessOrbitalMouseButton(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);
- else if (!leftDown && leftWasDown)
- camera.ProcessOrbitalMouseButton(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
- leftWasDown = leftDown;
-
- double mx = 0, my = 0;
- glfwGetCursorPos(window, &mx, &my);
- camera.ProcessOrbitalMouseMove(mx, my); // tracks last position internally; orbits only while dragging
-
- double scroll = g_ScrollAccum; g_ScrollAccum = 0.0;
- if (scroll != 0.0 && !overUI)
- camera.ProcessOrbitalScroll(0.0, scroll);
- }
-
- void VulkanRenderer::Impl::DestroyGeodesicResources()
- {
- if (presentPipeline) vkDestroyPipeline(device, presentPipeline, nullptr);
- if (presentPipelineLayout) vkDestroyPipelineLayout(device, presentPipelineLayout, nullptr);
- if (presentPool) vkDestroyDescriptorPool(device, presentPool, nullptr);
- if (presentSetLayout) vkDestroyDescriptorSetLayout(device, presentSetLayout, nullptr);
- if (presentSampler) vkDestroySampler(device, presentSampler, nullptr);
-
- if (geoPipeline) vkDestroyPipeline(device, geoPipeline, nullptr);
- if (geoPipelineLayout) vkDestroyPipelineLayout(device, geoPipelineLayout, nullptr);
- if (geoPool) vkDestroyDescriptorPool(device, geoPool, nullptr);
- if (geoSetLayout) vkDestroyDescriptorSetLayout(device, geoSetLayout, nullptr);
- if (quadVB) vkDestroyBuffer(device, quadVB, nullptr);
- if (quadVBMem) vkFreeMemory(device, quadVBMem, nullptr);
- if (cubeSampler) vkDestroySampler(device, cubeSampler, nullptr);
- if (cubeView) vkDestroyImageView(device, cubeView, nullptr);
- if (cubeImage) vkDestroyImage(device, cubeImage, nullptr);
- if (cubeMem) vkFreeMemory(device, cubeMem, nullptr);
- if (camMapped) { vkUnmapMemory(device, camMem); camMapped = nullptr; }
- if (simMapped) { vkUnmapMemory(device, simMem); simMapped = nullptr; }
- VkBuffer ubos[4] = { camBuf, diskBuf, objBuf, simBuf };
- VkDeviceMemory umem[4] = { camMem, diskMem, objMem, simMem };
- for (int i = 0; i < 4; ++i) { if (ubos[i]) vkDestroyBuffer(device, ubos[i], nullptr); if (umem[i]) vkFreeMemory(device, umem[i], nullptr); }
- if (geoFramebuffer) vkDestroyFramebuffer(device, geoFramebuffer, nullptr);
- if (geoRenderPass) vkDestroyRenderPass(device, geoRenderPass, nullptr);
- if (geoImageView) vkDestroyImageView(device, geoImageView, nullptr);
- if (geoImage) vkDestroyImage(device, geoImage, nullptr);
- if (geoImageMem) vkFreeMemory(device, geoImageMem, nullptr);
- }
-
- bool VulkanRenderer::Impl::RecordCommandBuffer(VkCommandBuffer cmd, uint32_t imageIndex, const glm::vec4& clear, ImDrawData* drawData)
- {
- VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
- VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
-
- // Geodesic offscreen pass
- VkClearValue geoClear{}; geoClear.color = { { 0, 0, 0, 1 } };
- VkRenderPassBeginInfo grp{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- grp.renderPass = geoRenderPass; grp.framebuffer = geoFramebuffer;
- grp.renderArea = { { 0, 0 }, { GEO_W, GEO_H } };
- grp.clearValueCount = 1; grp.pClearValues = &geoClear;
- vkCmdBeginRenderPass(cmd, &grp, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geoPipeline);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geoPipelineLayout, 0, 1, &geoSet, 0, nullptr);
- VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &quadVB, &off);
- vkCmdDraw(cmd, 6, 1, 0, 0);
- vkCmdEndRenderPass(cmd);
-
- // Swapchain pass: upscale the geodesic image, then the ImGui UI on top
- VkClearValue cv{}; cv.color = { { clear.r, clear.g, clear.b, clear.a } };
- VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass;
- rpbi.framebuffer = framebuffers[imageIndex];
- rpbi.renderArea = { { 0, 0 }, swapchainExtent };
- rpbi.clearValueCount = 1; rpbi.pClearValues = &cv;
- vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, presentPipeline);
- // Negative-height viewport flips the geodesic image vertically so the scene
- // reads the same as the OpenGL path (Vulkan's clip space is Y-down). Only
- // this draw is affected; ImGui sets its own viewport.
- VkViewport vp{ 0, (float)swapchainExtent.height, (float)swapchainExtent.width, -(float)swapchainExtent.height, 0, 1 };
- VkRect2D scissor{ { 0, 0 }, swapchainExtent };
- vkCmdSetViewport(cmd, 0, 1, &vp);
- vkCmdSetScissor(cmd, 0, 1, &scissor);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, presentPipelineLayout, 0, 1, &presentSet, 0, nullptr);
- vkCmdBindVertexBuffers(cmd, 0, 1, &quadVB, &off);
- vkCmdDraw(cmd, 6, 1, 0, 0);
- if (drawData)
- ImGui_ImplVulkan_RenderDrawData(drawData, cmd);
- vkCmdEndRenderPass(cmd);
-
- VK_CHECK(vkEndCommandBuffer(cmd));
- return true;
- }
-
- void VulkanRenderer::Impl::CleanupSwapchain()
- {
- for (auto fb : framebuffers) vkDestroyFramebuffer(device, fb, nullptr);
- framebuffers.clear();
- for (auto iv : imageViews) vkDestroyImageView(device, iv, nullptr);
- imageViews.clear();
- if (renderPass) { vkDestroyRenderPass(device, renderPass, nullptr); renderPass = VK_NULL_HANDLE; }
- if (swapchain) { vkDestroySwapchainKHR(device, swapchain, nullptr); swapchain = VK_NULL_HANDLE; }
- }
-
- bool VulkanRenderer::Impl::RecreateSwapchain()
- {
- // Wait until the window has a non-zero size (e.g. after un-minimizing).
- int w = 0, h = 0;
- glfwGetFramebufferSize(window, &w, &h);
- while (w == 0 || h == 0)
- {
- glfwGetFramebufferSize(window, &w, &h);
- glfwWaitEvents();
- }
- width = w; height = h;
- vkDeviceWaitIdle(device);
-
- CleanupSwapchain();
- // renderFinished are tied to image count; recreate below via sync if it changed.
- if (!CreateSwapchain()) return false;
- if (!CreateImageViews()) return false;
- if (!CreateRenderPass()) return false;
- if (!CreateFramebuffers())return false;
- imagesInFlight.assign(images.size(), VK_NULL_HANDLE);
- return true;
- }
-
- VulkanRenderer::VulkanRenderer() { m_Impl = new Impl(); }
- VulkanRenderer::~VulkanRenderer() { Shutdown(); delete m_Impl; m_Impl = nullptr; }
-
- bool VulkanRenderer::Init(void* glfwWindow, int width, int height)
- {
- Impl& v = *m_Impl;
- v.window = (GLFWwindow*)glfwWindow;
- v.width = width; v.height = height;
-
- if (!v.CreateInstance()) return false;
- if (!v.PickPhysicalAndDevice()) return false;
- if (!v.CreateSwapchain()) return false;
- if (!v.CreateImageViews()) return false;
- if (!v.CreateRenderPass()) return false;
- if (!v.CreateFramebuffers()) return false;
- if (!v.CreateCommandBuffers()) return false;
- if (!v.CreateSyncObjects()) return false;
- if (!v.CreateGeodesicResources()) return false;
- if (!v.CreatePresentResources()) return false;
-
- DONUT_INFO("Vulkan renderer ready: {} swapchain images, {}x{}",
- (int)v.images.size(), v.swapchainExtent.width, v.swapchainExtent.height);
- return true;
- }
-
- bool VulkanRenderer::InitImGui()
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) return false;
-
- VkDescriptorPoolSize poolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 };
- VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
- dpci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
- dpci.maxSets = 1000;
- dpci.poolSizeCount = 1; dpci.pPoolSizes = &poolSize;
- VK_CHECK(vkCreateDescriptorPool(v.device, &dpci, nullptr, &v.imguiPool));
-
- IMGUI_CHECKVERSION();
- ImGui::CreateContext();
- ImGuiIO& io = ImGui::GetIO();
- io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
- io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
- ImGui::StyleColorsDark();
-
- ImGui_ImplGlfw_InitForVulkan(v.window, true);
- g_PrevScroll = glfwSetScrollCallback(v.window, DonutVkScrollCallback); // chain ImGui + camera zoom
- ImGui_ImplVulkan_InitInfo info{};
- info.ApiVersion = VK_API_VERSION_1_2;
- info.Instance = v.instance;
- info.PhysicalDevice = v.physical;
- info.Device = v.device;
- info.QueueFamily = v.graphicsFamily;
- info.Queue = v.graphicsQueue;
- info.DescriptorPool = v.imguiPool;
- info.RenderPass = v.renderPass;
- info.MinImageCount = 2;
- info.ImageCount = (uint32_t)v.images.size();
- info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
- if (!ImGui_ImplVulkan_Init(&info))
- {
- DONUT_ERROR("Vulkan: ImGui_ImplVulkan_Init failed");
- return false;
- }
-
- v.imguiInit = true;
- DONUT_INFO("Vulkan: ImGui backend initialized");
- return true;
- }
-
- void VulkanRenderer::OnResize(int width, int height)
- {
- m_Impl->framebufferResized = true;
- m_Impl->width = width; m_Impl->height = height;
- }
-
- void VulkanRenderer::DrawFrame(const glm::vec4& clearColor, const std::function<void()>& buildUI)
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) return;
-
- ImDrawData* drawData = nullptr;
- if (v.imguiInit)
- {
- ImGui_ImplVulkan_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
- if (buildUI) buildUI();
- ImGui::Render();
- drawData = ImGui::GetDrawData();
- }
-
- vkWaitForFences(v.device, 1, &v.inFlight[v.currentFrame], VK_TRUE, UINT64_MAX);
-
- uint32_t imageIndex = 0;
- VkResult r = vkAcquireNextImageKHR(v.device, v.swapchain, UINT64_MAX,
- v.imageAvailable[v.currentFrame], VK_NULL_HANDLE, &imageIndex);
- if (r == VK_ERROR_OUT_OF_DATE_KHR) { v.RecreateSwapchain(); return; }
- if (r != VK_SUCCESS && r != VK_SUBOPTIMAL_KHR) { DONUT_ERROR("Vulkan: acquire failed ({})", (int)r); return; }
-
- if (v.imagesInFlight[imageIndex] != VK_NULL_HANDLE)
- vkWaitForFences(v.device, 1, &v.imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
- v.imagesInFlight[imageIndex] = v.inFlight[v.currentFrame];
-
- // The geodesic offscreen image is shared across frames in flight; wait for
- // the previous frame to finish reading it before overwriting it this frame.
- if (v.geoInUse != VK_NULL_HANDLE)
- vkWaitForFences(v.device, 1, &v.geoInUse, VK_TRUE, UINT64_MAX);
- v.ProcessInput();
- v.UpdateGeodesicUniforms();
-
- vkResetCommandBuffer(v.commandBuffers[v.currentFrame], 0);
- if (!v.RecordCommandBuffer(v.commandBuffers[v.currentFrame], imageIndex, clearColor, drawData)) return;
-
- VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
- submit.waitSemaphoreCount = 1;
- submit.pWaitSemaphores = &v.imageAvailable[v.currentFrame];
- submit.pWaitDstStageMask = &waitStage;
- submit.commandBufferCount = 1;
- submit.pCommandBuffers = &v.commandBuffers[v.currentFrame];
- submit.signalSemaphoreCount = 1;
- submit.pSignalSemaphores = &v.renderFinished[imageIndex];
-
- vkResetFences(v.device, 1, &v.inFlight[v.currentFrame]);
- if (vkQueueSubmit(v.graphicsQueue, 1, &submit, v.inFlight[v.currentFrame]) != VK_SUCCESS)
- { DONUT_ERROR("Vulkan: queue submit failed"); return; }
- v.geoInUse = v.inFlight[v.currentFrame];
-
- VkPresentInfoKHR present{ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
- present.waitSemaphoreCount = 1;
- present.pWaitSemaphores = &v.renderFinished[imageIndex];
- present.swapchainCount = 1;
- present.pSwapchains = &v.swapchain;
- present.pImageIndices = &imageIndex;
- r = vkQueuePresentKHR(v.presentQueue, &present);
- if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR || v.framebufferResized)
- {
- v.framebufferResized = false;
- v.RecreateSwapchain();
- }
-
- v.currentFrame = (v.currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
- }
-
- void VulkanRenderer::Shutdown()
- {
- Impl& v = *m_Impl;
- if (v.device == VK_NULL_HANDLE) { if (v.instance && v.surface) { vkDestroySurfaceKHR(v.instance, v.surface, nullptr); v.surface = VK_NULL_HANDLE; } if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; } return; }
-
- vkDeviceWaitIdle(v.device);
- v.DestroyGeodesicResources();
- if (v.imguiInit)
- {
- ImGui_ImplVulkan_Shutdown();
- ImGui_ImplGlfw_Shutdown();
- ImGui::DestroyContext();
- v.imguiInit = false;
- }
- if (v.imguiPool) { vkDestroyDescriptorPool(v.device, v.imguiPool, nullptr); v.imguiPool = VK_NULL_HANDLE; }
- for (auto s : v.renderFinished) vkDestroySemaphore(v.device, s, nullptr);
- for (auto s : v.imageAvailable) vkDestroySemaphore(v.device, s, nullptr);
- for (auto f : v.inFlight) vkDestroyFence(v.device, f, nullptr);
- v.renderFinished.clear(); v.imageAvailable.clear(); v.inFlight.clear();
- if (v.commandPool) { vkDestroyCommandPool(v.device, v.commandPool, nullptr); v.commandPool = VK_NULL_HANDLE; }
- v.CleanupSwapchain();
- vkDestroyDevice(v.device, nullptr); v.device = VK_NULL_HANDLE;
- if (v.surface) { vkDestroySurfaceKHR(v.instance, v.surface, nullptr); v.surface = VK_NULL_HANDLE; }
- if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; }
- }
-}
diff --git a/src/Platform/Vulkan/VulkanRenderer.h b/src/Platform/Vulkan/VulkanRenderer.h
deleted file mode 100644
index 9b5a23f..0000000
--- a/src/Platform/Vulkan/VulkanRenderer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include <glm/glm.hpp>
-#include <functional>
-
-// Live-window Vulkan backend: owns the instance, surface, device, swapchain,
-// render pass, framebuffers and per-frame synchronization, and drives the
-// acquire -> record -> submit -> present loop. Pure-C++ header (no vulkan.h /
-// glfw leak); the GLFW window is passed as an opaque handle.
-namespace Donut
-{
- // Must be called BEFORE glfwInit() when the Vulkan API is selected: points
- // GLFW at the loader the app links against (GLFW's own dlopen fails on
- // macOS/Homebrew) and configures the MoltenVK ICD / layer paths.
- void VulkanPrepareGLFW();
-
- class VulkanRenderer
- {
- public:
- VulkanRenderer();
- ~VulkanRenderer();
-
- // glfwWindow must be a GLFW window created with GLFW_NO_API.
- bool Init(void* glfwWindow, int width, int height);
- void Shutdown();
-
- // Creates the ImGui context + Vulkan/GLFW backends. Call after Init().
- bool InitImGui();
-
- // Renders + presents one frame: clears to clearColor, then (if InitImGui
- // ran) opens an ImGui frame, invokes buildUI to populate it, and draws it.
- void DrawFrame(const glm::vec4& clearColor, const std::function<void()>& buildUI = {});
-
- void OnResize(int width, int height);
-
- private:
- struct Impl;
- Impl* m_Impl = nullptr;
- };
-}
diff --git a/src/Platform/Vulkan/VulkanRendererAPI.cpp b/src/Platform/Vulkan/VulkanRendererAPI.cpp
deleted file mode 100644
index 79f7633..0000000
--- a/src/Platform/Vulkan/VulkanRendererAPI.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "VulkanRendererAPI.h"
-
-namespace Donut
-{
- void VulkanRendererAPI::Init()
- {
- // TODO(Hachem): Implement Vulkan renderer API initialization
- }
-
- void VulkanRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
- {
- // TODO(Hachem): Implement Vulkan viewport setting
- }
-
- void VulkanRendererAPI::SetClearColor(const glm::vec4& color)
- {
- // TODO(Hachem): Implement Vulkan clear color setting
- }
-
- void VulkanRendererAPI::Clear()
- {
- // TODO(Hachem): Implement Vulkan clear
- }
-
- void VulkanRendererAPI::EnableDepthTest()
- {
- // TODO(Hachem): Implement Vulkan depth test enabling
- }
-
- void VulkanRendererAPI::DisableDepthTest()
- {
- // TODO(Hachem): Implement Vulkan depth test disabling
- }
-
- void VulkanRendererAPI::SetFaceCulling(bool enabled)
- {
- // TODO(Hachem): Implement Vulkan face culling setting
- }
-
- void VulkanRendererAPI::EnableBlending()
- {
- // TODO(Hachem): Implement Vulkan blending enabling
- }
-
- void VulkanRendererAPI::DisableBlending()
- {
- // TODO(Hachem): Implement Vulkan blending disabling
- }
-
- void VulkanRendererAPI::DrawIndexed(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
- {
- // TODO(Hachem): Implement Vulkan indexed drawing
- }
-
- void VulkanRendererAPI::DrawArrays(uint32_t vertexCount, uint32_t first)
- {
- // TODO(Hachem): Implement Vulkan array drawing
- }
-
- void VulkanRendererAPI::DrawLines(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
- {
- // TODO(Hachem): Implement Vulkan line drawing
- }
-
- void VulkanRendererAPI::BindTexture(uint32_t textureID, uint32_t slot)
- {
- // TODO(Hachem): Implement Vulkan texture binding
- }
-
- void VulkanRendererAPI::BindImageTexture(uint32_t textureID, uint32_t slot, bool readOnly)
- {
- // TODO(Hachem): Implement Vulkan image texture binding
- }
-
- void VulkanRendererAPI::ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels)
- {
- // TODO(Hachem): Implement Vulkan pixel reading
- // For now, this is a placeholder implementation
- // In a real Vulkan implementation, this would involve:
- // 1. Creating a staging buffer
- // 2. Copying the framebuffer to the staging buffer
- // 3. Mapping the staging buffer and copying to the pixels array
- // 4. Unmapping and destroying the staging buffer
- }
-};
diff --git a/src/Platform/Vulkan/VulkanRendererAPI.h b/src/Platform/Vulkan/VulkanRendererAPI.h
deleted file mode 100644
index 63a623f..0000000
--- a/src/Platform/Vulkan/VulkanRendererAPI.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include "Rendering/Renderer.h"
-
-namespace Donut
-{
- class VulkanRendererAPI
- : public RendererAPI
- {
- public:
- virtual void Init() override;
- virtual void SetViewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual void SetClearColor(const glm::vec4& color) override;
- virtual void Clear() override;
- virtual void EnableDepthTest() override;
- virtual void DisableDepthTest() override;
- virtual void SetFaceCulling(bool enabled) override;
- virtual void EnableBlending() override;
- virtual void DisableBlending() override;
-
- virtual void DrawIndexed(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
-
- virtual void DrawArrays(uint32_t vertexCount,
- uint32_t first = 0) override;
- virtual void DrawLines(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
- virtual void BindTexture(uint32_t textureID,
- uint32_t slot = 0) override;
- virtual void BindImageTexture(uint32_t textureID,
- uint32_t slot = 0,
- bool readOnly = false) override;
- virtual void ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) override;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanShader.cpp b/src/Platform/Vulkan/VulkanShader.cpp
deleted file mode 100644
index 2117384..0000000
--- a/src/Platform/Vulkan/VulkanShader.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "VulkanShader.h"
-
-#include <fstream>
-#include <glm/gtc/type_ptr.hpp>
-
-namespace Donut
-{
- VulkanShader::VulkanShader(const std::string& filepath)
- {
- // TODO(Hachem): Implement Vulkan shader creation from filepath
- }
-
- VulkanShader::VulkanShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc)
- : m_Name(name)
- {
- // TODO(Hachem): Implement Vulkan shader creation from source
- }
-
- VulkanShader::VulkanShader(const std::string& name, const std::string& computeSrc)
- : m_Name(name)
- {
- // TODO(Hachem): Implement Vulkan compute shader creation
- }
-
- VulkanShader::~VulkanShader()
- {
- // TODO(Hachem): Implement Vulkan shader cleanup
- }
-
- void VulkanShader::Bind() const
- {
- // TODO(Hachem): Implement Vulkan shader binding
- }
-
- void VulkanShader::Unbind() const
- {
- // TODO(Hachem): Implement Vulkan shader unbinding
- }
-
- void VulkanShader::SetInt(const std::string& name, int value)
- {
- // TODO(Hachem): Implement Vulkan shader int uniform setting
- }
-
- void VulkanShader::SetIntArray(const std::string& name, int* values, uint32_t count)
- {
- // TODO(Hachem): Implement Vulkan shader int array uniform setting
- }
-
- void VulkanShader::SetFloat(const std::string& name, float value)
- {
- // TODO(Hachem): Implement Vulkan shader float uniform setting
- }
-
- void VulkanShader::SetFloat2(const std::string& name, const glm::vec2& value)
- {
- // TODO(Hachem): Implement Vulkan shader float2 uniform setting
- }
-
- void VulkanShader::SetFloat3(const std::string& name, const glm::vec3& value)
- {
- // TODO(Hachem): Implement Vulkan shader float3 uniform setting
- }
-
- void VulkanShader::SetFloat4(const std::string& name, const glm::vec4& value)
- {
- // TODO(Hachem): Implement Vulkan shader float4 uniform setting
- }
-
- void VulkanShader::SetMat4(const std::string& name, const glm::mat4& value)
- {
- // TODO(Hachem): Implement Vulkan shader mat4 uniform setting
- }
-
- void VulkanShader::Dispatch(uint32_t x, uint32_t y, uint32_t z)
- {
- // TODO(Hachem): Implement Vulkan compute shader dispatch
- }
-
- void VulkanShader::DispatchIndirect(uint32_t offset)
- {
- // TODO(Hachem): Implement Vulkan indirect compute shader dispatch
- }
-
- void VulkanShader::MemoryBarrier(uint32_t barriers)
- {
- // TODO(Hachem): Implement Vulkan memory barrier
- }
-
- void VulkanShader::UploadUniformInt(const std::string& name, int value)
- {
- // TODO(Hachem): Implement Vulkan uniform int upload
- }
-
- void VulkanShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count)
- {
- // TODO(Hachem): Implement Vulkan uniform int array upload
- }
-
- void VulkanShader::UploadUniformFloat(const std::string& name, float value)
- {
- // TODO(Hachem): Implement Vulkan uniform float upload
- }
-
- void VulkanShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value)
- {
- // TODO(Hachem): Implement Vulkan uniform float2 upload
- }
-
- void VulkanShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value)
- {
- // TODO(Hachem): Implement Vulkan uniform float3 upload
- }
-
- void VulkanShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value)
- {
- // TODO(Hachem): Implement Vulkan uniform float4 upload
- }
-
- void VulkanShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix)
- {
- // TODO(Hachem): Implement Vulkan uniform mat3 upload
- }
-
- void VulkanShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
- {
- // TODO(Hachem): Implement Vulkan uniform mat4 upload
- }
-
- std::string VulkanShader::ReadFile(const std::string& filepath)
- {
- // TODO(Hachem): Implement file reading for Vulkan shader
- return "";
- }
-
- std::unordered_map<uint32_t, std::string> VulkanShader::PreProcess(const std::string& source)
- {
- // TODO(Hachem): Implement shader preprocessing for Vulkan
- return {};
- }
-
- void VulkanShader::Compile(const std::unordered_map<uint32_t, std::string>& shaderSources)
- {
- // TODO(Hachem): Implement Vulkan shader compilation
- }
-};
diff --git a/src/Platform/Vulkan/VulkanShader.h b/src/Platform/Vulkan/VulkanShader.h
deleted file mode 100644
index 138a742..0000000
--- a/src/Platform/Vulkan/VulkanShader.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "Rendering/Shader.h"
-
-#include <string>
-#include <unordered_map>
-
-namespace Donut
-{
- class VulkanShader
- : public Shader
- {
- public:
- VulkanShader(const std::string& filepath);
- VulkanShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
- VulkanShader(const std::string& name, const std::string& computeSrc);
- virtual ~VulkanShader();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetInt(const std::string& name, int value) override;
- virtual void SetIntArray(const std::string& name, int* values, uint32_t count) override;
- virtual void SetFloat(const std::string& name, float value) override;
- virtual void SetFloat2(const std::string& name, const glm::vec2& value) override;
- virtual void SetFloat3(const std::string& name, const glm::vec3& value) override;
- virtual void SetFloat4(const std::string& name, const glm::vec4& value) override;
- virtual void SetMat4(const std::string& name, const glm::mat4& value) override;
-
- virtual void Dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) override;
- virtual void DispatchIndirect(uint32_t offset = 0) override;
- virtual void MemoryBarrier(uint32_t barriers) override;
-
- virtual const std::string& GetName() const override { return m_Name; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- void UploadUniformInt(const std::string& name, int value);
- void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
- void UploadUniformFloat(const std::string& name, float value);
- void UploadUniformFloat2(const std::string& name, const glm::vec2& value);
- void UploadUniformFloat3(const std::string& name, const glm::vec3& value);
- void UploadUniformFloat4(const std::string& name, const glm::vec4& value);
- void UploadUniformMat3(const std::string& name, const glm::mat3& matrix);
- void UploadUniformMat4(const std::string& name, const glm::mat4& matrix);
- private:
- std::string ReadFile(const std::string& filepath);
- std::unordered_map<uint32_t, std::string> PreProcess(const std::string& source);
- void Compile(const std::unordered_map<uint32_t, std::string>& shaderSources);
- uint32_t m_RendererID;
- std::string m_Name;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanTexture.cpp b/src/Platform/Vulkan/VulkanTexture.cpp
deleted file mode 100644
index 4145130..0000000
--- a/src/Platform/Vulkan/VulkanTexture.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "VulkanTexture.h"
-
-namespace Donut
-{
- VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanTexture2D::VulkanTexture2D(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1;
- m_Height = 1;
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanTexture2D::~VulkanTexture2D()
- {
- }
-
- void VulkanTexture2D::SetData(void* data, uint32_t size)
- {
- }
-
- void VulkanTexture2D::Bind(uint32_t slot) const
- {
- }
-
- void VulkanTexture2D::BindAsImage(uint32_t slot, bool readOnly) const
- {
- }
-
- // Vulkan Cubemap Implementation (Placeholder)
- VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1024;
- m_Height = 1024;
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanCubemapTexture::~VulkanCubemapTexture()
- {
- }
-
- void VulkanCubemapTexture::SetData(void* data, uint32_t size)
- {
- }
-
- void VulkanCubemapTexture::Bind(uint32_t slot) const
- {
- }
-
- void VulkanCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
- {
- }
-};
diff --git a/src/Platform/Vulkan/VulkanTexture.h b/src/Platform/Vulkan/VulkanTexture.h
deleted file mode 100644
index dd9e1df..0000000
--- a/src/Platform/Vulkan/VulkanTexture.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include "Rendering/Texture.h"
-
-namespace Donut
-{
- class VulkanTexture2D
- : public Texture2D
- {
- public:
- VulkanTexture2D(uint32_t width, uint32_t height);
- VulkanTexture2D(const std::string& path);
- virtual ~VulkanTexture2D();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == ((VulkanTexture2D&)other).m_RendererID;
- }
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- uint32_t m_InternalFormat, m_DataFormat;
- };
-
- class VulkanCubemapTexture
- : public CubemapTexture
- {
- public:
- VulkanCubemapTexture(uint32_t width, uint32_t height);
- VulkanCubemapTexture(const std::string& path);
- virtual ~VulkanCubemapTexture();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == ((VulkanCubemapTexture&)other).m_RendererID;
- }
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- uint32_t m_InternalFormat, m_DataFormat;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanUniformBuffer.cpp b/src/Platform/Vulkan/VulkanUniformBuffer.cpp
deleted file mode 100644
index 8152478..0000000
--- a/src/Platform/Vulkan/VulkanUniformBuffer.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "VulkanUniformBuffer.h"
-
-namespace Donut
-{
- VulkanUniformBuffer::VulkanUniformBuffer(uint32_t size, uint32_t binding)
- : m_Size(size), m_Binding(binding)
- {
- // TODO: Implement Vulkan uniform buffer
- }
-
- VulkanUniformBuffer::~VulkanUniformBuffer()
- {
- // TODO: Implement Vulkan uniform buffer cleanup
- }
-
- void VulkanUniformBuffer::SetData(const void* data, uint32_t size, uint32_t offset)
- {
- // TODO: Implement Vulkan uniform buffer data setting
- }
-
- void VulkanUniformBuffer::Bind(uint32_t binding)
- {
- // TODO: Implement Vulkan uniform buffer binding
- }
-};
diff --git a/src/Platform/Vulkan/VulkanUniformBuffer.h b/src/Platform/Vulkan/VulkanUniformBuffer.h
deleted file mode 100644
index bbf152d..0000000
--- a/src/Platform/Vulkan/VulkanUniformBuffer.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "Rendering/UniformBuffer.h"
-
-namespace Donut
-{
- class VulkanUniformBuffer : public UniformBuffer
- {
- public:
- VulkanUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~VulkanUniformBuffer();
-
- virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override;
- virtual void Bind(uint32_t binding) override;
- private:
- uint32_t m_Size;
- uint32_t m_Binding;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanVertexArray.cpp b/src/Platform/Vulkan/VulkanVertexArray.cpp
deleted file mode 100644
index de22f05..0000000
--- a/src/Platform/Vulkan/VulkanVertexArray.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "VulkanVertexArray.h"
-
-namespace Donut
-{
- VulkanVertexArray::VulkanVertexArray()
- {
- // TODO(Hachem): Implement Vulkan vertex array creation
- }
-
- VulkanVertexArray::~VulkanVertexArray()
- {
- // TODO(Hachem): Implement Vulkan vertex array cleanup
- }
-
- void VulkanVertexArray::Bind() const
- {
- // TODO(Hachem): Implement Vulkan vertex array binding
- }
-
- void VulkanVertexArray::Unbind() const
- {
- // TODO(Hachem): Implement Vulkan vertex array unbinding
- }
-
- void VulkanVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer addition
- m_VertexBuffers.push_back(vertexBuffer);
- }
-
- void VulkanVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer)
- {
- // TODO(Hachem): Implement Vulkan index buffer setting
- m_IndexBuffer = indexBuffer;
- }
-};
diff --git a/src/Platform/Vulkan/VulkanVertexArray.h b/src/Platform/Vulkan/VulkanVertexArray.h
deleted file mode 100644
index e4484e5..0000000
--- a/src/Platform/Vulkan/VulkanVertexArray.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexArray.h"
-#include "Core/Memory.h"
-
-namespace Donut
-{
- class VulkanVertexArray
- : public VertexArray
- {
- public:
- VulkanVertexArray();
- virtual ~VulkanVertexArray();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) override;
- virtual void SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer) override;
-
- virtual const std::vector<Ref<VertexBuffer>>& GetVertexBuffers() const { return m_VertexBuffers; }
- virtual const Ref<IndexBuffer>& GetIndexBuffer() const { return m_IndexBuffer; }
- private:
- uint32_t m_RendererID;
- std::vector<Ref<VertexBuffer>> m_VertexBuffers;
- Ref<IndexBuffer> m_IndexBuffer;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanVertexBuffer.cpp b/src/Platform/Vulkan/VulkanVertexBuffer.cpp
deleted file mode 100644
index f77cbe4..0000000
--- a/src/Platform/Vulkan/VulkanVertexBuffer.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "VulkanVertexBuffer.h"
-
-namespace Donut
-{
- VulkanVertexBuffer::VulkanVertexBuffer(uint32_t size)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer creation with size
- }
-
- VulkanVertexBuffer::VulkanVertexBuffer(float* vertices, uint32_t size)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer creation with data
- }
-
- VulkanVertexBuffer::~VulkanVertexBuffer()
- {
- // TODO(Hachem): Implement Vulkan vertex buffer cleanup
- }
-
- void VulkanVertexBuffer::Bind() const
- {
- // TODO(Hachem): Implement Vulkan vertex buffer binding
- }
-
- void VulkanVertexBuffer::Unbind() const
- {
- // TODO(Hachem): Implement Vulkan vertex buffer unbinding
- }
-
- void VulkanVertexBuffer::SetData(const void* data, uint32_t size)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer data setting
- }
-}; \ No newline at end of file
diff --git a/src/Platform/Vulkan/VulkanVertexBuffer.h b/src/Platform/Vulkan/VulkanVertexBuffer.h
deleted file mode 100644
index e809e44..0000000
--- a/src/Platform/Vulkan/VulkanVertexBuffer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexBuffer.h"
-
-namespace Donut
-{
- class VulkanVertexBuffer
- : public VertexBuffer
- {
- public:
- VulkanVertexBuffer(uint32_t size);
- VulkanVertexBuffer(float* vertices, uint32_t size);
- virtual ~VulkanVertexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetData(const void* data, uint32_t size) override;
-
- virtual const VertexBufferLayout& GetLayout() const override { return m_Layout; }
- virtual void SetLayout(const VertexBufferLayout& layout) override { m_Layout = layout; }
- private:
- uint32_t m_RendererID;
- VertexBufferLayout m_Layout;
- };
-};