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
This commit is contained in:
Brecht Van Lommel
2025-04-14 14:06:58 +02:00
parent b174e5f0d1
commit ecd54ba4e4
12 changed files with 118 additions and 7 deletions

View File

@@ -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

View File

@@ -1122,6 +1122,7 @@ bool CUDADevice::should_use_graphics_interop(const GraphicsInteropDevice &intero
return found;
}
case GraphicsInteropDevice::METAL:
case GraphicsInteropDevice::NONE: {
return false;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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<DeviceQueue> MetalDevice::gpu_queue_create()
return make_unique<MetalDeviceQueue>(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)

View File

@@ -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

View File

@@ -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<MetalDevice *>(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<void *>(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

View File

@@ -44,6 +44,8 @@ class MetalDeviceQueue : public DeviceQueue {
void *native_queue() override;
unique_ptr<DeviceGraphicsInterop> graphics_interop_create() override;
protected:
void setup_capture();
void update_capture(DeviceKernel kernel);

View File

@@ -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<DeviceGraphicsInterop> MetalDeviceQueue::graphics_interop_create()
{
return make_unique<MetalDeviceGraphicsInterop>(this);
}
CCL_NAMESPACE_END
#endif /* WITH_METAL */

View File

@@ -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;

View File

@@ -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<int64_t>(buffer_);
/* Just get pointer to unified memory. No need to unmap. */
native_handle.handle = reinterpret_cast<int64_t>(map());
native_handle.size = size_;
return native_handle;