From 5b361d81dbd2af0d0e99b9eb1adebea76ff05db0 Mon Sep 17 00:00:00 2001 From: hachem Date: Thu, 20 Aug 2026 02:27:02 +0200 Subject: [chore]: MASSIVE refactor + more vulkan bs --- src/Platform/Metal/MetalContext.h | 16 - src/Platform/Metal/MetalContext.mm | 124 --- src/Platform/OpenGL/OpenGLFramebuffer.cpp | 246 ------ src/Platform/OpenGL/OpenGLFramebuffer.h | 35 - src/Platform/OpenGL/OpenGLIndexBuffer.cpp | 28 - src/Platform/OpenGL/OpenGLIndexBuffer.h | 22 - src/Platform/OpenGL/OpenGLRendererAPI.cpp | 118 --- src/Platform/OpenGL/OpenGLRendererAPI.h | 44 - src/Platform/OpenGL/OpenGLShader.cpp | 302 ------- src/Platform/OpenGL/OpenGLShader.h | 58 -- src/Platform/OpenGL/OpenGLTexture.cpp | 272 ------ src/Platform/OpenGL/OpenGLTexture.h | 68 -- src/Platform/OpenGL/OpenGLUniformBuffer.cpp | 29 - src/Platform/OpenGL/OpenGLUniformBuffer.h | 21 - src/Platform/OpenGL/OpenGLVertexArray.cpp | 58 -- src/Platform/OpenGL/OpenGLVertexArray.h | 41 - src/Platform/OpenGL/OpenGLVertexBuffer.cpp | 35 - src/Platform/OpenGL/OpenGLVertexBuffer.h | 25 - src/Platform/Vulkan/VulkanContext.cpp | 789 ----------------- src/Platform/Vulkan/VulkanContext.h | 41 - src/Platform/Vulkan/VulkanIndexBuffer.cpp | 25 - src/Platform/Vulkan/VulkanIndexBuffer.h | 22 - src/Platform/Vulkan/VulkanRenderer.cpp | 1235 --------------------------- src/Platform/Vulkan/VulkanRenderer.h | 40 - src/Platform/Vulkan/VulkanRendererAPI.cpp | 86 -- src/Platform/Vulkan/VulkanRendererAPI.h | 38 - src/Platform/Vulkan/VulkanShader.cpp | 146 ---- src/Platform/Vulkan/VulkanShader.h | 52 -- src/Platform/Vulkan/VulkanTexture.cpp | 73 -- src/Platform/Vulkan/VulkanTexture.h | 60 -- src/Platform/Vulkan/VulkanUniformBuffer.cpp | 25 - src/Platform/Vulkan/VulkanUniformBuffer.h | 19 - src/Platform/Vulkan/VulkanVertexArray.cpp | 36 - src/Platform/Vulkan/VulkanVertexArray.h | 28 - src/Platform/Vulkan/VulkanVertexBuffer.cpp | 34 - src/Platform/Vulkan/VulkanVertexBuffer.h | 26 - 36 files changed, 4317 deletions(-) delete mode 100644 src/Platform/Metal/MetalContext.h delete mode 100644 src/Platform/Metal/MetalContext.mm delete mode 100644 src/Platform/OpenGL/OpenGLFramebuffer.cpp delete mode 100644 src/Platform/OpenGL/OpenGLFramebuffer.h delete mode 100644 src/Platform/OpenGL/OpenGLIndexBuffer.cpp delete mode 100644 src/Platform/OpenGL/OpenGLIndexBuffer.h delete mode 100644 src/Platform/OpenGL/OpenGLRendererAPI.cpp delete mode 100644 src/Platform/OpenGL/OpenGLRendererAPI.h delete mode 100644 src/Platform/OpenGL/OpenGLShader.cpp delete mode 100644 src/Platform/OpenGL/OpenGLShader.h delete mode 100644 src/Platform/OpenGL/OpenGLTexture.cpp delete mode 100644 src/Platform/OpenGL/OpenGLTexture.h delete mode 100644 src/Platform/OpenGL/OpenGLUniformBuffer.cpp delete mode 100644 src/Platform/OpenGL/OpenGLUniformBuffer.h delete mode 100644 src/Platform/OpenGL/OpenGLVertexArray.cpp delete mode 100644 src/Platform/OpenGL/OpenGLVertexArray.h delete mode 100644 src/Platform/OpenGL/OpenGLVertexBuffer.cpp delete mode 100644 src/Platform/OpenGL/OpenGLVertexBuffer.h delete mode 100644 src/Platform/Vulkan/VulkanContext.cpp delete mode 100644 src/Platform/Vulkan/VulkanContext.h delete mode 100644 src/Platform/Vulkan/VulkanIndexBuffer.cpp delete mode 100644 src/Platform/Vulkan/VulkanIndexBuffer.h delete mode 100644 src/Platform/Vulkan/VulkanRenderer.cpp delete mode 100644 src/Platform/Vulkan/VulkanRenderer.h delete mode 100644 src/Platform/Vulkan/VulkanRendererAPI.cpp delete mode 100644 src/Platform/Vulkan/VulkanRendererAPI.h delete mode 100644 src/Platform/Vulkan/VulkanShader.cpp delete mode 100644 src/Platform/Vulkan/VulkanShader.h delete mode 100644 src/Platform/Vulkan/VulkanTexture.cpp delete mode 100644 src/Platform/Vulkan/VulkanTexture.h delete mode 100644 src/Platform/Vulkan/VulkanUniformBuffer.cpp delete mode 100644 src/Platform/Vulkan/VulkanUniformBuffer.h delete mode 100644 src/Platform/Vulkan/VulkanVertexArray.cpp delete mode 100644 src/Platform/Vulkan/VulkanVertexArray.h delete mode 100644 src/Platform/Vulkan/VulkanVertexBuffer.cpp delete mode 100644 src/Platform/Vulkan/VulkanVertexBuffer.h (limited to 'src/Platform') 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 -#import - -#include "MetalContext.h" -#include "Core/Log.h" - -#include - -namespace Donut -{ - bool MetalProbe() - { - @autoreleasepool - { - id device = MTLCreateSystemDefaultDevice(); - if (device == nil) - { - DONUT_ERROR("Metal: no default device available"); - return false; - } - - id 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 device = MTLCreateSystemDefaultDevice(); - id queue = [device newCommandQueue]; - if (device == nil || queue == nil) - return false; - - NSString* src = - @"#include \n" - "using namespace metal;\n" - "kernel void selfTest(texture2d 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 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 fn = [lib newFunctionWithName:@"selfTest"]; - id 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 tex = [device newTextureWithDescriptor:desc]; - - id cb = [queue commandBuffer]; - id 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 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 - -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(m_ColorAttachments.size()), m_ColorAttachments.data()); - glDeleteTextures(1, &m_DepthAttachment); - } - - void OpenGLFramebuffer::Invalidate() - { - if (m_RendererID) - { - glDeleteFramebuffers(1, &m_RendererID); - glDeleteTextures(static_cast(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(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(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(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(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(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 m_ColorAttachmentSpecifications; - FramebufferTextureSpecification m_DepthAttachmentSpecification = FramebufferTextureFormat::None; - - std::vector 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 - -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 -#include - -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, 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, 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 - -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, - uint32_t indexCount = 0) override; - - virtual void DrawArrays(uint32_t vertexCount, - uint32_t first = 0) override; - virtual void DrawLines(const Ref& 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 -#include - -#include -#include - -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/ - // .glsl by Tools/compile-shaders.sh. Given a legacy ".../.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 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 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 OpenGLShader::PreProcess(const std::string& source) - { - std::unordered_map 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& shaderSources) - { - uint32_t program = glCreateProgram(); - std::vector 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 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 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 -#include - -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 PreProcess(const std::string& source); - void Compile(const std::unordered_map& 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 -#include -#include - -// 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 - -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 - -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 - -#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) - { - 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(static_cast(element.offset))); - m_VertexBufferIndex++; - } - - m_VertexBuffers.push_back(vertexBuffer); - } - - void OpenGLVertexArray::SetIndexBuffer(const Ref& 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 - -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) override; - virtual void SetIndexBuffer(const Ref& indexBuffer) override; - - virtual const std::vector>& GetVertexBuffers() const override - { - return m_VertexBuffers; - } - - virtual const Ref& GetIndexBuffer() const override - { - return m_IndexBuffer; - } - private: - uint32_t m_RendererID; - uint32_t m_VertexBufferIndex = 0; - std::vector> m_VertexBuffers; - Ref 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 - -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 - -#include -#include -#include "stb_image_write.h" - -#include -#include -#include -#include - -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 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 layers; - uint32_t layerCount = 0; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - std::vector 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 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 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 devExts; - uint32_t devExtCount = 0; - vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &devExtCount, nullptr); - std::vector 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, ®ion); - 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 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 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, ®ion); - 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 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 = ≻ - 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 = ⅈ - 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, ®ion); - 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 - -#include -#include -#include - -#include -#include -#include "stb_image.h" - -#include -#include -#include -#include -#include - -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 images; - std::vector imageViews; - VkRenderPass renderPass = VK_NULL_HANDLE; - std::vector framebuffers; - - VkCommandPool commandPool = VK_NULL_HANDLE; - std::vector commandBuffers; // MAX_FRAMES_IN_FLIGHT - - std::vector imageAvailable; // per frame in flight - std::vector renderFinished; // per swapchain image - std::vector inFlight; // per frame in flight - std::vector 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 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 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 layers; - uint32_t layerCount = 0; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - std::vector 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 devices(count); - vkEnumeratePhysicalDevices(instance, &count, devices.data()); - physical = devices[0]; - - uint32_t qCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical, &qCount, nullptr); - std::vector 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 devExts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - uint32_t devExtCount = 0; - vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, nullptr); - std::vector 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 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 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 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 data(size / 4); - file.seekg(0); - file.read(reinterpret_cast(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 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 = ≻ - 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 = ≻ - 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, ©); - 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 = ⅈ - 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& 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 -#include - -// 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& 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, 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, 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, - uint32_t indexCount = 0) override; - - virtual void DrawArrays(uint32_t vertexCount, - uint32_t first = 0) override; - virtual void DrawLines(const Ref& 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 -#include - -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 VulkanShader::PreProcess(const std::string& source) - { - // TODO(Hachem): Implement shader preprocessing for Vulkan - return {}; - } - - void VulkanShader::Compile(const std::unordered_map& 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 -#include - -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 PreProcess(const std::string& source); - void Compile(const std::unordered_map& 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) - { - // TODO(Hachem): Implement Vulkan vertex buffer addition - m_VertexBuffers.push_back(vertexBuffer); - } - - void VulkanVertexArray::SetIndexBuffer(const Ref& 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) override; - virtual void SetIndexBuffer(const Ref& indexBuffer) override; - - virtual const std::vector>& GetVertexBuffers() const { return m_VertexBuffers; } - virtual const Ref& GetIndexBuffer() const { return m_IndexBuffer; } - private: - uint32_t m_RendererID; - std::vector> m_VertexBuffers; - Ref 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; - }; -}; -- cgit v1.3