diff options
| -rw-r--r-- | premake5.lua | 74 | ||||
| -rw-r--r-- | src/core/application.cpp | 13 | ||||
| -rw-r--r-- | src/engine/engine.h | 24 | ||||
| -rw-r--r-- | src/platform/metal/metal_context.h | 8 | ||||
| -rw-r--r-- | src/platform/metal/metal_context.mm | 119 |
5 files changed, 193 insertions, 45 deletions
diff --git a/premake5.lua b/premake5.lua index 98e5757..236347c 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" { "ext/glad/include" } - + filter "system:windows" systemversion "latest" @@ -101,7 +105,7 @@ project "GLFW" filter "system:linux" pic "On" systemversion "latest" - + files { "ext/glfw/src/x11_init.c", @@ -110,22 +114,22 @@ project "GLFW" "ext/glfw/src/x11_window.c", "ext/glfw/src/xkb_unicode.c", "ext/glfw/src/xkb_unicode.h", - + "ext/glfw/src/wl_init.c", "ext/glfw/src/wl_monitor.c", "ext/glfw/src/wl_platform.h", "ext/glfw/src/wl_window.c", - + "ext/glfw/src/posix_module.c", "ext/glfw/src/posix_time.c", "ext/glfw/src/posix_time.h", "ext/glfw/src/posix_thread.c", "ext/glfw/src/posix_thread.h", - + "ext/glfw/src/glx_context.c", "ext/glfw/src/egl_context.c", "ext/glfw/src/osmesa_context.c", - + "ext/glfw/src/linux_joystick.c", "ext/glfw/src/linux_joystick.h" } @@ -148,11 +152,11 @@ project "GLFW" "ext/glfw/src/cocoa_time.c", "ext/glfw/src/cocoa_time.h", "ext/glfw/src/cocoa_window.m", - + "ext/glfw/src/nsgl_context.m", "ext/glfw/src/egl_context.c", "ext/glfw/src/osmesa_context.c", - + "ext/glfw/src/posix_module.c", "ext/glfw/src/posix_thread.c", "ext/glfw/src/posix_thread.h" @@ -184,8 +188,8 @@ project "GLFW" "ext/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 { "ext/imgui/imgui.cpp", @@ -225,24 +229,24 @@ project "ImGui" "ext/imgui/imgui_widgets.cpp", "ext/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 { "ext/ImGuizmo/ImGuizmo.cpp", "ext/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/opengl_framebuffer.h", "src/platform/opengl/opengl_framebuffer.cpp", - + "ext/imgui/backends/imgui_impl_glfw.cpp", "ext/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" diff --git a/src/core/application.cpp b/src/core/application.cpp index 37d7fa0..fa5fd6e 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/metal_context.h" +#endif + namespace Donut { Application* Application::s_instance = nullptr; @@ -103,9 +107,14 @@ namespace Donut m_state_manager->on_event(event); } - void Application::on_init() - { + void Application::on_init() + { Logger::init(); + +#ifdef __APPLE__ + metal_probe(); + metal_compute_self_test(); +#endif SettingsManager::initialize(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 0a50612..1c25466 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); @@ -99,33 +99,33 @@ namespace Donut int get_compute_height() const { return m_compute_height; } int get_compute_width() const { return (m_width * m_compute_height) / m_height; } void update_compute_dimensions(); - + int get_max_steps_moving() const { return m_max_steps_moving; } int get_max_steps_static() const { return m_max_steps_static; } float get_early_exit_distance() const { return m_early_exit_distance; } void set_max_steps_moving(int steps) { m_max_steps_moving = steps; } void set_max_steps_static(int steps) { m_max_steps_static = steps; } void set_early_exit_distance(float distance) { m_early_exit_distance = distance; } - + float get_disk_thickness() const { return m_disk_thickness; } void set_disk_thickness(float thickness) { m_disk_thickness = thickness; } - + float get_disk_density() const { return m_disk_density; } void set_disk_density(float density) { m_disk_density = density; } - + float get_rotation_speed() const { return m_rotation_speed; } void set_rotation_speed(float speed) { m_rotation_speed = speed; } - + float get_blur_strength() const { return m_blur_strength; } void set_blur_strength(float strength) { m_blur_strength = strength; } - + float get_glow_intensity() const { return m_glow_intensity; } void set_glow_intensity(float intensity) { m_glow_intensity = intensity; } - + void load_objects_from_scene(const std::vector<Donut::Object>& objects); void export_high_res_frame(const std::string& filename, int width = 4096, int height = 3072); void print_object_info() const; - + void set_hdri_environment(Ref<CubemapTexture> hdri) { m_hdri_environment = hdri; } Ref<CubemapTexture> get_hdri_environment() const { return m_hdri_environment; } private: @@ -161,12 +161,12 @@ namespace Donut BlackHole m_sag_a; Camera m_camera; bool m_gravity = false; - + int m_max_steps_moving = 60000; int m_max_steps_static = 30000; float m_early_exit_distance = 5.0e11f; - - float m_disk_thickness = 0.1f; + + float m_disk_thickness = 0.1f; float m_disk_density = 0.1f; float m_rotation_speed = 1.0f; float m_blur_strength = 2.0f; diff --git a/src/platform/metal/metal_context.h b/src/platform/metal/metal_context.h new file mode 100644 index 0000000..66e8ae2 --- /dev/null +++ b/src/platform/metal/metal_context.h @@ -0,0 +1,8 @@ +#pragma once + +namespace Donut +{ + bool metal_probe(); + + bool metal_compute_self_test(); +} diff --git a/src/platform/metal/metal_context.mm b/src/platform/metal/metal_context.mm new file mode 100644 index 0000000..a616865 --- /dev/null +++ b/src/platform/metal/metal_context.mm @@ -0,0 +1,119 @@ +#import <Metal/Metal.h> +#import <Foundation/Foundation.h> + +#include "metal_context.h" +#include "core/log.h" + +#include <vector> + +namespace Donut +{ + bool metal_probe() + { + @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; + } + } + + bool metal_compute_self_test() + { + @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]; + + 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; + } + } +} |
