Add initialize method to PlatformMetal (#8708)

This commit is contained in:
Ben Doherty
2025-05-13 14:30:02 -07:00
committed by GitHub
parent cc7361dba5
commit 78419cd992
2 changed files with 95 additions and 19 deletions

View File

@@ -39,18 +39,40 @@ public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
*
* This method attempts to acquire a Metal device and command queue, returning true if both are
* successfully obtained, or false otherwise. Typically, these objects are acquired when
* the Metal backend is initialized. This method allows clients to check for their availability
* earlier.
*
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
* true, subsequent calls will continue to return true but have no effect.
*
* initialize() must be called from the main thread.
*
* @returns true if the device and command queue have been successfully obtained; false
* otherwise.
*/
bool initialize() noexcept;
/**
* Obtain the preferred Metal device object for the backend to use.
*
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
* free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice();
*
* createDevice is called by the Metal backend from the backend thread.
*/
virtual void createDevice(MetalDevice& outDevice) noexcept;
/**
* Create a command submission queue on the Metal device object.
*
* createCommandQueue is called by the Metal backend from the backend thread.
*
* @param device The device which was returned from createDevice()
*/
virtual void createCommandQueue(
@@ -60,6 +82,8 @@ public:
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
* guaranteed to execute before all subsequent command buffers created either by Filament, or
* further calls to this method.
*
* createAndEnqueueCommandBuffer must be called from the main thread.
*/
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
@@ -68,6 +92,8 @@ public:
*
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
* completion of each frame. These are limited and provided round-robin style by the system.
*
* setDrawableFailureBehavior must be called from the main thread.
*/
enum class DrawableFailureBehavior : uint8_t {
/**

View File

@@ -24,14 +24,22 @@
#import <Foundation/Foundation.h>
#include <atomic>
#include <mutex>
namespace filament::backend {
struct PlatformMetalImpl {
std::mutex mLock; // locks mDevice and mCommandQueue
id<MTLDevice> mDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
// read form driver thread, read/written to from client thread
std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior =
PlatformMetal::DrawableFailureBehavior::PANIC;
// These methods must be called with mLock held
void createDeviceImpl(MetalDevice& outDevice);
void createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue);
};
Platform* createDefaultMetalPlatform() {
@@ -48,7 +56,59 @@ Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::Dri
return MetalDriverFactory::create(this, driverConfig);
}
bool PlatformMetal::initialize() noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
MetalDevice device{};
pImpl->createDeviceImpl(device);
if (device.device == nil) {
return false;
}
MetalCommandQueue commandQueue{};
pImpl->createCommandQueueImpl(device, commandQueue);
if (commandQueue.commandQueue == nil) {
return false;
}
return true;
}
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createDeviceImpl(outDevice);
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createCommandQueueImpl(device, outCommandQueue);
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
}
// -------------------------------------------------------------------------------------------------
void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
if (mDevice) {
outDevice.device = mDevice;
return;
}
id<MTLDevice> result;
#if !defined(FILAMENT_IOS)
@@ -74,27 +134,17 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
<< utils::io::endl;
outDevice.device = result;
mDevice = result;
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
pImpl->mCommandQueue = [device.device newCommandQueue];
pImpl->mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = pImpl->mCommandQueue;
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
if (mCommandQueue) {
outCommandQueue.commandQueue = mCommandQueue;
return;
}
mCommandQueue = [device.device newCommandQueue];
mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = mCommandQueue;
}
} // namespace filament