diff options
| author | hachem <im@hachem.wtf> | 2026-08-19 01:00:48 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-19 01:00:48 +0200 |
| commit | 5c5fc10d8938fd2da1922f01c638d42e70eb1ab1 (patch) | |
| tree | 57ef5be020247c817079d010faae9a4635c8416a /src | |
| parent | d3c54837fc8b4ef0027aa6757c303aa673944e6f (diff) | |
[feat]: support mac arm
Diffstat (limited to 'src')
| -rw-r--r-- | src/Core/Application.cpp | 13 | ||||
| -rw-r--r-- | src/Engine/Engine.h | 24 | ||||
| -rw-r--r-- | src/Platform/Metal/MetalContext.h | 16 | ||||
| -rw-r--r-- | src/Platform/Metal/MetalContext.mm | 124 |
4 files changed, 163 insertions, 14 deletions
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; + } + } +} |
