aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--premake5.lua76
-rw-r--r--src/Core/Application.cpp13
-rw-r--r--src/Engine/Engine.h24
-rw-r--r--src/Platform/Metal/MetalContext.h16
-rw-r--r--src/Platform/Metal/MetalContext.mm124
5 files changed, 207 insertions, 46 deletions
diff --git a/premake5.lua b/premake5.lua
index 742c552..8729d3f 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -8,7 +8,7 @@ workspace "Donut"
"Release",
"Dist"
}
-
+
flags
{
"MultiProcessorCompile"
@@ -20,6 +20,10 @@ workspace "Donut"
defines "DONUT_LINUX"
filter "system:macosx"
defines "DONUT_MACOS"
+ -- Build natively for Apple Silicon (was x86_64 under Rosetta). Native
+ -- arm64 is required for clean Metal work and better performance.
+ architecture "ARM64"
+ filter {}
outputdir = "%{cfg.buildcfg}-%{cfg.system}/%{prj.name}"
@@ -55,7 +59,7 @@ project "GLAD"
{
"Vendor/glad/include"
}
-
+
filter "system:windows"
systemversion "latest"
@@ -101,7 +105,7 @@ project "GLFW"
filter "system:linux"
pic "On"
systemversion "latest"
-
+
files
{
"Vendor/glfw/src/x11_init.c",
@@ -110,22 +114,22 @@ project "GLFW"
"Vendor/glfw/src/x11_window.c",
"Vendor/glfw/src/xkb_unicode.c",
"Vendor/glfw/src/xkb_unicode.h",
-
+
"Vendor/glfw/src/wl_init.c",
"Vendor/glfw/src/wl_monitor.c",
"Vendor/glfw/src/wl_platform.h",
"Vendor/glfw/src/wl_window.c",
-
+
"Vendor/glfw/src/posix_module.c",
"Vendor/glfw/src/posix_time.c",
"Vendor/glfw/src/posix_time.h",
"Vendor/glfw/src/posix_thread.c",
"Vendor/glfw/src/posix_thread.h",
-
+
"Vendor/glfw/src/glx_context.c",
"Vendor/glfw/src/egl_context.c",
"Vendor/glfw/src/osmesa_context.c",
-
+
"Vendor/glfw/src/linux_joystick.c",
"Vendor/glfw/src/linux_joystick.h"
}
@@ -148,11 +152,11 @@ project "GLFW"
"Vendor/glfw/src/cocoa_time.c",
"Vendor/glfw/src/cocoa_time.h",
"Vendor/glfw/src/cocoa_window.m",
-
+
"Vendor/glfw/src/nsgl_context.m",
"Vendor/glfw/src/egl_context.c",
"Vendor/glfw/src/osmesa_context.c",
-
+
"Vendor/glfw/src/posix_module.c",
"Vendor/glfw/src/posix_thread.c",
"Vendor/glfw/src/posix_thread.h"
@@ -184,8 +188,8 @@ project "GLFW"
"Vendor/glfw/src/osmesa_context.c"
}
- defines
- {
+ defines
+ {
"_GLFW_WIN32",
"_CRT_SECURE_NO_WARNINGS",
}
@@ -194,7 +198,7 @@ project "GLFW"
runtime "Debug"
symbols "on"
- filter { "system:windows", "configurations:Debug-AS" }
+ filter { "system:windows", "configurations:Debug-AS" }
runtime "Debug"
symbols "on"
sanitize { "Address" }
@@ -213,10 +217,10 @@ project "ImGui"
kind "StaticLib"
language "C++"
staticruntime "on"
-
+
targetdir ("bin/" .. outputdir)
objdir ("bin-int/" .. outputdir)
-
+
files
{
"Vendor/imgui/imgui.cpp",
@@ -225,24 +229,24 @@ project "ImGui"
"Vendor/imgui/imgui_widgets.cpp",
"Vendor/imgui/imgui_demo.cpp"
}
-
+
filter "system:windows"
systemversion "latest"
cppdialect "C++17"
-
+
filter "system:linux"
pic "On"
systemversion "latest"
cppdialect "C++17"
-
+
filter "configurations:Debug"
runtime "Debug"
symbols "on"
-
+
filter "configurations:Release"
runtime "Release"
optimize "on"
-
+
filter "configurations:Dist"
runtime "Release"
optimize "on"
@@ -252,39 +256,39 @@ project "ImGuizmo"
kind "StaticLib"
language "C++"
staticruntime "on"
-
+
targetdir ("bin/" .. outputdir)
objdir ("bin-int/" .. outputdir)
-
+
files
{
"Vendor/ImGuizmo/ImGuizmo.cpp",
"Vendor/ImGuizmo/ImGuizmo.h"
}
-
+
includedirs
{
"%{IncludeDir.imgui}",
"%{IncludeDir.imguizmo}"
}
-
+
filter "system:windows"
systemversion "latest"
cppdialect "C++17"
-
+
filter "system:linux"
pic "On"
systemversion "latest"
cppdialect "C++17"
-
+
filter "configurations:Debug"
runtime "Debug"
symbols "on"
-
+
filter "configurations:Release"
runtime "Release"
optimize "on"
-
+
filter "configurations:Dist"
runtime "Release"
optimize "on"
@@ -318,10 +322,10 @@ project "Donut"
"src/Rendering/Framebuffer.cpp",
"src/Platform/OpenGL/OpenGLFramebuffer.h",
"src/Platform/OpenGL/OpenGLFramebuffer.cpp",
-
+
"Vendor/imgui/backends/imgui_impl_glfw.cpp",
"Vendor/imgui/backends/imgui_impl_opengl3.cpp",
-
+
"Assets/Fonts/Inter/static/Inter_18pt-Regular.ttf",
"Assets/Fonts/Inter/static/Inter_18pt-Bold.ttf",
"Assets/Fonts/Inter/static/Inter_18pt-Light.ttf"
@@ -330,7 +334,7 @@ project "Donut"
includedirs
{
"src",
-
+
"%{IncludeDir.glm}",
"%{IncludeDir.glfw}",
"%{IncludeDir.glad}",
@@ -359,12 +363,20 @@ project "Donut"
}
filter "system:macosx"
+ -- Objective-C++ (Metal) sources compile only on macOS.
+ files
+ {
+ "src/**.mm"
+ }
+
links
{
"Cocoa.framework",
"IOKit.framework",
"CoreFoundation.framework",
- "QuartzCore.framework"
+ "QuartzCore.framework",
+ "Metal.framework",
+ "Foundation.framework"
}
filter "configurations:Debug"
@@ -380,4 +392,4 @@ project "Donut"
filter "configurations:Dist"
defines "DONUT_DIST"
runtime "Release"
- optimize "on" \ No newline at end of file
+ optimize "on"
diff --git a/src/Core/Application.cpp b/src/Core/Application.cpp
index eefa482..df42877 100644
--- a/src/Core/Application.cpp
+++ b/src/Core/Application.cpp
@@ -10,6 +10,10 @@
#include <GLFW/glfw3.h>
#include <ImGuizmo.h>
+#ifdef __APPLE__
+#include "Platform/Metal/MetalContext.h"
+#endif
+
namespace Donut
{
Application* Application::s_Instance = nullptr;
@@ -103,9 +107,14 @@ namespace Donut
m_StateManager->OnEvent(event);
}
- void Application::OnInit()
- {
+ void Application::OnInit()
+ {
Logger::Init();
+
+#ifdef __APPLE__
+ MetalProbe(); // Phase 1: native Metal device available?
+ MetalComputeSelfTest(); // Phase 1: full compute dispatch + read-back works?
+#endif
SettingsManager::Initialize();
diff --git a/src/Engine/Engine.h b/src/Engine/Engine.h
index f8ef628..a4cbdc7 100644
--- a/src/Engine/Engine.h
+++ b/src/Engine/Engine.h
@@ -35,7 +35,7 @@ namespace Donut
double m_Radius;
double m_Rs;
- BlackHole(glm::vec3 pos, float mass)
+ BlackHole(glm::vec3 pos, float mass)
: m_Position(pos), m_Mass(mass)
{
m_Rs = 2.0 * G * m_Mass / (c * c);
@@ -102,33 +102,33 @@ namespace Donut
int GetComputeHeight() const { return m_ComputeHeight; }
int GetComputeWidth() const { return (m_Width * m_ComputeHeight) / m_Height; }
void UpdateComputeDimensions();
-
+
int GetMaxStepsMoving() const { return m_MaxStepsMoving; }
int GetMaxStepsStatic() const { return m_MaxStepsStatic; }
float GetEarlyExitDistance() const { return m_EarlyExitDistance; }
void SetMaxStepsMoving(int steps) { m_MaxStepsMoving = steps; }
void SetMaxStepsStatic(int steps) { m_MaxStepsStatic = steps; }
void SetEarlyExitDistance(float distance) { m_EarlyExitDistance = distance; }
-
+
float GetDiskThickness() const { return m_DiskThickness; }
void SetDiskThickness(float thickness) { m_DiskThickness = thickness; }
-
+
float GetDiskDensity() const { return m_DiskDensity; }
void SetDiskDensity(float density) { m_DiskDensity = density; }
-
+
float GetRotationSpeed() const { return m_RotationSpeed; }
void SetRotationSpeed(float speed) { m_RotationSpeed = speed; }
-
+
float GetBlurStrength() const { return m_BlurStrength; }
void SetBlurStrength(float strength) { m_BlurStrength = strength; }
-
+
float GetGlowIntensity() const { return m_GlowIntensity; }
void SetGlowIntensity(float intensity) { m_GlowIntensity = intensity; }
-
+
void LoadObjectsFromScene(const std::vector<Donut::Object>& objects);
void ExportHighResFrame(const std::string& filename, int width = 4096, int height = 3072);
void PrintObjectInfo() const;
-
+
void SetHDRIEnvironment(Ref<CubemapTexture> hdri) { m_HDRIEnvironment = hdri; }
Ref<CubemapTexture> GetHDRIEnvironment() const { return m_HDRIEnvironment; }
private:
@@ -170,12 +170,12 @@ namespace Donut
BlackHole m_SagA;
Camera m_Camera;
bool m_Gravity = false;
-
+
int m_MaxStepsMoving = 60000;
int m_MaxStepsStatic = 30000;
float m_EarlyExitDistance = 5.0e11f;
-
- float m_DiskThickness = 0.1f;
+
+ float m_DiskThickness = 0.1f;
float m_DiskDensity = 0.1f;
float m_RotationSpeed = 1.0f;
float m_BlurStrength = 2.0f;
diff --git a/src/Platform/Metal/MetalContext.h b/src/Platform/Metal/MetalContext.h
new file mode 100644
index 0000000..5d9eb08
--- /dev/null
+++ b/src/Platform/Metal/MetalContext.h
@@ -0,0 +1,16 @@
+#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
new file mode 100644
index 0000000..1bb9e70
--- /dev/null
+++ b/src/Platform/Metal/MetalContext.mm
@@ -0,0 +1,124 @@
+#import <Metal/Metal.h>
+#import <Foundation/Foundation.h>
+
+#include "MetalContext.h"
+#include "Core/Log.h"
+
+#include <vector>
+
+namespace Donut
+{
+ bool MetalProbe()
+ {
+ @autoreleasepool
+ {
+ id<MTLDevice> device = MTLCreateSystemDefaultDevice();
+ if (device == nil)
+ {
+ DONUT_ERROR("Metal: no default device available");
+ return false;
+ }
+
+ id<MTLCommandQueue> queue = [device newCommandQueue];
+ const char* name = [[device name] UTF8String];
+
+ DONUT_INFO("Metal device: {}", name ? name : "(unknown)");
+ DONUT_INFO("Metal: unified memory = {}, max threads/threadgroup = {}",
+ device.hasUnifiedMemory ? "yes" : "no",
+ (unsigned long)device.maxThreadsPerThreadgroup.width);
+
+ if (queue == nil)
+ {
+ DONUT_WARN("Metal: failed to create command queue");
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ // Compiles a trivial compute kernel, dispatches it over a small texture, and
+ // reads the result back to confirm the full Metal compute path works: source
+ // compilation, pipeline state, command encoding, dispatch, and shared-memory
+ // read-back. This is the mechanism the geodesic ray tracer will run on.
+ bool MetalComputeSelfTest()
+ {
+ @autoreleasepool
+ {
+ id<MTLDevice> device = MTLCreateSystemDefaultDevice();
+ id<MTLCommandQueue> queue = [device newCommandQueue];
+ if (device == nil || queue == nil)
+ return false;
+
+ NSString* src =
+ @"#include <metal_stdlib>\n"
+ "using namespace metal;\n"
+ "kernel void selfTest(texture2d<float, access::write> outTex [[texture(0)]],\n"
+ " uint2 gid [[thread_position_in_grid]])\n"
+ "{\n"
+ " uint w = outTex.get_width();\n"
+ " uint h = outTex.get_height();\n"
+ " if (gid.x >= w || gid.y >= h) return;\n"
+ " outTex.write(float4(float(gid.x) / float(w - 1),\n"
+ " float(gid.y) / float(h - 1), 0.5, 1.0), gid);\n"
+ "}\n";
+
+ NSError* err = nil;
+ id<MTLLibrary> lib = [device newLibraryWithSource:src options:nil error:&err];
+ if (lib == nil)
+ {
+ DONUT_ERROR("Metal self-test: kernel compile failed: {}",
+ err ? [[err localizedDescription] UTF8String] : "unknown");
+ return false;
+ }
+
+ id<MTLFunction> fn = [lib newFunctionWithName:@"selfTest"];
+ id<MTLComputePipelineState> pipeline =
+ [device newComputePipelineStateWithFunction:fn error:&err];
+ if (pipeline == nil)
+ {
+ DONUT_ERROR("Metal self-test: pipeline creation failed");
+ return false;
+ }
+
+ const uint32_t W = 64, H = 64;
+ MTLTextureDescriptor* desc =
+ [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
+ width:W
+ height:H
+ mipmapped:NO];
+ desc.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
+ desc.storageMode = MTLStorageModeShared;
+ id<MTLTexture> tex = [device newTextureWithDescriptor:desc];
+
+ id<MTLCommandBuffer> cb = [queue commandBuffer];
+ id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
+ [enc setComputePipelineState:pipeline];
+ [enc setTexture:tex atIndex:0];
+
+ MTLSize tg = MTLSizeMake(16, 16, 1);
+ MTLSize grid = MTLSizeMake(W, H, 1);
+ [enc dispatchThreads:grid threadsPerThreadgroup:tg];
+ [enc endEncoding];
+ [cb commit];
+ [cb waitUntilCompleted];
+
+ // Read back the far corner; the kernel writes (~1, ~1, 0.5, 1) there.
+ std::vector<uint8_t> px(W * H * 4);
+ [tex getBytes:px.data()
+ bytesPerRow:W * 4
+ fromRegion:MTLRegionMake2D(0, 0, W, H)
+ mipmapLevel:0];
+
+ size_t corner = ((size_t)(H - 1) * W + (W - 1)) * 4;
+ DONUT_INFO("Metal compute self-test: corner pixel RGBA = ({}, {}, {}, {})",
+ (int)px[corner + 0], (int)px[corner + 1],
+ (int)px[corner + 2], (int)px[corner + 3]);
+
+ bool ok = px[corner + 0] > 250 && px[corner + 1] > 250 &&
+ px[corner + 3] == 255;
+ DONUT_INFO("Metal compute self-test: {}", ok ? "PASS" : "FAIL");
+ return ok;
+ }
+ }
+}