From ecd54ba4e4a3d6402fba312139fffa1e9dd2917e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 14 Apr 2025 14:06:58 +0200 Subject: [PATCH] Cycles: Metal graphics interop This is trivial with unified memory, and avoids one memory copy. Pull Request: https://projects.blender.org/blender/blender/pulls/137363 --- intern/cycles/device/CMakeLists.txt | 2 + intern/cycles/device/cuda/device_impl.cpp | 1 + .../cycles/device/cuda/graphics_interop.cpp | 1 + intern/cycles/device/hip/device_impl.cpp | 1 + intern/cycles/device/hip/graphics_interop.cpp | 3 +- intern/cycles/device/metal/device_impl.mm | 8 ++-- intern/cycles/device/metal/graphics_interop.h | 46 +++++++++++++++++++ .../cycles/device/metal/graphics_interop.mm | 43 +++++++++++++++++ intern/cycles/device/metal/queue.h | 2 + intern/cycles/device/metal/queue.mm | 6 +++ intern/cycles/session/display_driver.h | 4 +- source/blender/gpu/metal/mtl_texture.mm | 8 +++- 12 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 intern/cycles/device/metal/graphics_interop.h create mode 100644 intern/cycles/device/metal/graphics_interop.mm diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 431ae7a80d4..70461e029c1 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -98,6 +98,8 @@ set(SRC_METAL metal/device.h metal/device_impl.mm metal/device_impl.h + metal/graphics_interop.mm + metal/graphics_interop.h metal/kernel.mm metal/kernel.h metal/queue.mm diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp index e55e890eb9f..e43b48c1112 100644 --- a/intern/cycles/device/cuda/device_impl.cpp +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -1122,6 +1122,7 @@ bool CUDADevice::should_use_graphics_interop(const GraphicsInteropDevice &intero return found; } + case GraphicsInteropDevice::METAL: case GraphicsInteropDevice::NONE: { return false; } diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp index 926681033d4..60ea4951f88 100644 --- a/intern/cycles/device/cuda/graphics_interop.cpp +++ b/intern/cycles/device/cuda/graphics_interop.cpp @@ -86,6 +86,7 @@ void CUDADeviceGraphicsInterop::set_buffer(const GraphicsInteropBuffer &interop_ cu_external_memory_ptr_ = external_memory_device_ptr; break; } + case GraphicsInteropDevice::METAL: case GraphicsInteropDevice::NONE: break; } diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp index e1103da3250..ef029f2bcf0 100644 --- a/intern/cycles/device/hip/device_impl.cpp +++ b/intern/cycles/device/hip/device_impl.cpp @@ -1069,6 +1069,7 @@ bool HIPDevice::should_use_graphics_interop(const GraphicsInteropDevice &interop return found; } case GraphicsInteropDevice::VULKAN: + case GraphicsInteropDevice::METAL: case GraphicsInteropDevice::NONE: /* TODO: Implement Vulkan support. */ return false; diff --git a/intern/cycles/device/hip/graphics_interop.cpp b/intern/cycles/device/hip/graphics_interop.cpp index e497683681d..6aae4caa6f2 100644 --- a/intern/cycles/device/hip/graphics_interop.cpp +++ b/intern/cycles/device/hip/graphics_interop.cpp @@ -56,9 +56,10 @@ void HIPDeviceGraphicsInterop::set_buffer(const GraphicsInteropBuffer &interop_b } break; } - /* TODO: implement vulkan support. */ case GraphicsInteropDevice::VULKAN: + case GraphicsInteropDevice::METAL: case GraphicsInteropDevice::NONE: + /* TODO: implement vulkan support. */ break; } } diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index aae0fd1457a..9bbc03cf87d 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -12,6 +12,8 @@ # include "scene/scene.h" +# include "session/display_driver.h" + # include "util/debug.h" # include "util/md5.h" # include "util/path.h" @@ -1360,11 +1362,11 @@ unique_ptr MetalDevice::gpu_queue_create() return make_unique(this); } -bool MetalDevice::should_use_graphics_interop(const GraphicsInteropDevice & /*interop_device*/, +bool MetalDevice::should_use_graphics_interop(const GraphicsInteropDevice &interop_device, const bool /*log*/) { - /* METAL_WIP - provide fast interop */ - return false; + /* Always supported with unified memory. */ + return interop_device.type == GraphicsInteropDevice::METAL; } void *MetalDevice::get_native_buffer(device_ptr ptr) diff --git a/intern/cycles/device/metal/graphics_interop.h b/intern/cycles/device/metal/graphics_interop.h new file mode 100644 index 00000000000..6bfd1d953d6 --- /dev/null +++ b/intern/cycles/device/metal/graphics_interop.h @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2025 Blender Foundation + * + * SPDX-License-Identifier: Apache-2.0 */ + +#ifdef WITH_METAL + +# include "device/graphics_interop.h" +# include "session/display_driver.h" + +CCL_NAMESPACE_BEGIN + +class MetalDevice; +class MetalDeviceQueue; + +class MetalDeviceGraphicsInterop : public DeviceGraphicsInterop { + public: + explicit MetalDeviceGraphicsInterop(MetalDeviceQueue *queue); + + MetalDeviceGraphicsInterop(const MetalDeviceGraphicsInterop &other) = delete; + MetalDeviceGraphicsInterop(MetalDeviceGraphicsInterop &&other) noexcept = delete; + + ~MetalDeviceGraphicsInterop() override; + + MetalDeviceGraphicsInterop &operator=(const MetalDeviceGraphicsInterop &other) = delete; + MetalDeviceGraphicsInterop &operator=(MetalDeviceGraphicsInterop &&other) = delete; + + void set_buffer(const GraphicsInteropBuffer &interop_buffer) override; + + device_ptr map() override; + void unmap() override; + + protected: + MetalDeviceQueue *queue_ = nullptr; + MetalDevice *device_ = nullptr; + + /* Native handle. */ + void *buffer_ = nullptr; + size_t size_ = 0; + + /* The destination was requested to be cleared. */ + bool need_clear_ = false; +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/metal/graphics_interop.mm b/intern/cycles/device/metal/graphics_interop.mm new file mode 100644 index 00000000000..ca9345b88d9 --- /dev/null +++ b/intern/cycles/device/metal/graphics_interop.mm @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2025 Blender Foundation + * + * SPDX-License-Identifier: Apache-2.0 */ + +#ifdef WITH_METAL + +# include "device/metal/graphics_interop.h" + +# include "device/metal/device_impl.h" + +CCL_NAMESPACE_BEGIN + +MetalDeviceGraphicsInterop::MetalDeviceGraphicsInterop(MetalDeviceQueue *queue) + : queue_(queue), device_(static_cast(queue->device)) +{ +} + +MetalDeviceGraphicsInterop::~MetalDeviceGraphicsInterop() = default; + +void MetalDeviceGraphicsInterop::set_buffer(const GraphicsInteropBuffer &interop_buffer) +{ + /* Trivial implementation due to unified memory. */ + if (interop_buffer.type == GraphicsInteropDevice::METAL) { + need_clear_ |= interop_buffer.need_clear; + buffer_ = reinterpret_cast(interop_buffer.handle); + size_ = interop_buffer.width * interop_buffer.height * sizeof(half4); + } +} + +device_ptr MetalDeviceGraphicsInterop::map() +{ + if (buffer_ && need_clear_) { + memset(buffer_, 0, size_); + } + + return device_ptr(buffer_); +} + +void MetalDeviceGraphicsInterop::unmap() {} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index 6a310b065fc..c4251d42b47 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -44,6 +44,8 @@ class MetalDeviceQueue : public DeviceQueue { void *native_queue() override; + unique_ptr graphics_interop_create() override; + protected: void setup_capture(); void update_capture(DeviceKernel kernel); diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 69d058a4b9c..8a7bbb54336 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -10,6 +10,7 @@ # include "device/metal/queue.h" # include "device/metal/device_impl.h" +# include "device/metal/graphics_interop.h" # include "device/metal/kernel.h" # include "util/path.h" @@ -857,6 +858,11 @@ void *MetalDeviceQueue::native_queue() return mtlCommandQueue_; } +unique_ptr MetalDeviceQueue::graphics_interop_create() +{ + return make_unique(this); +} + CCL_NAMESPACE_END #endif /* WITH_METAL */ diff --git a/intern/cycles/session/display_driver.h b/intern/cycles/session/display_driver.h index f5919e7a8d7..8f409f1726f 100644 --- a/intern/cycles/session/display_driver.h +++ b/intern/cycles/session/display_driver.h @@ -18,6 +18,7 @@ class GraphicsInteropDevice { NONE, OPENGL, VULKAN, + METAL, }; Type type = NONE; @@ -39,7 +40,8 @@ class GraphicsInteropBuffer { /* The handle is expected to be: * - OpenGL: pixel buffer object ID. * - Vulkan on Windows: opaque handle for VkBuffer. - * - Vulkan on Unix: opaque file descriptor for VkBuffer. */ + * - Vulkan on Unix: opaque file descriptor for VkBuffer. + * - Metal: pixel buffer unified memory pointer. */ GraphicsInteropDevice::Type type = GraphicsInteropDevice::NONE; int64_t handle = 0; diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index 1b2c4893a95..f4b3e9fb8cf 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -2649,11 +2649,15 @@ GPUPixelBufferNativeHandle MTLPixelBuffer::get_native_handle() { GPUPixelBufferNativeHandle native_handle; - if (buffer_ == nil) { + /* Only spported with unified memory currently. */ + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + if (![ctx->device hasUnifiedMemory]) { return native_handle; } - native_handle.handle = reinterpret_cast(buffer_); + /* Just get pointer to unified memory. No need to unmap. */ + native_handle.handle = reinterpret_cast(map()); native_handle.size = size_; return native_handle;