From c82700482122c3c193957a04474fe4cbd68bf683 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Mon, 24 May 2021 10:36:56 -0700 Subject: [PATCH] Add getPlatform API to Engine, move functionality into MetalPlatform (#4007) This change allows access to the Engine's underlying Platform object. This is useful for Metal clients to access a new method on MetalPlatform, createAndEnqueueCommandBuffer. --- filament/backend/CMakeLists.txt | 2 +- .../include/private/backend/MetalPlatform.h | 66 ++++++++++++++++ filament/backend/src/Platform.cpp | 6 +- filament/backend/src/metal/MetalDriver.mm | 28 ++----- filament/backend/src/metal/MetalPlatform.mm | 79 +++++++++++++++++++ filament/backend/src/metal/PlatformMetal.h | 44 ----------- filament/backend/src/metal/PlatformMetal.mm | 36 --------- filament/include/filament/Engine.h | 26 ++++++ filament/src/Engine.cpp | 4 + filament/src/details/Engine.h | 4 + 10 files changed, 189 insertions(+), 106 deletions(-) create mode 100644 filament/backend/include/private/backend/MetalPlatform.h create mode 100644 filament/backend/src/metal/MetalPlatform.mm delete mode 100644 filament/backend/src/metal/PlatformMetal.h delete mode 100644 filament/backend/src/metal/PlatformMetal.mm diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 3827d6f618..e28e1100a7 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -115,7 +115,7 @@ if (FILAMENT_SUPPORTS_METAL) src/metal/MetalResourceTracker.cpp src/metal/MetalState.mm src/metal/MetalTimerQuery.mm - src/metal/PlatformMetal.mm + src/metal/MetalPlatform.mm ) list(APPEND SRCS ${METAL_SRCS}) diff --git a/filament/backend/include/private/backend/MetalPlatform.h b/filament/backend/include/private/backend/MetalPlatform.h new file mode 100644 index 0000000000..1f83b85c14 --- /dev/null +++ b/filament/backend/include/private/backend/MetalPlatform.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DRIVER_PLATFORM_METAL_H +#define TNT_FILAMENT_DRIVER_PLATFORM_METAL_H + +#include +#include + +#import + +namespace filament { +namespace backend { + +class MetalPlatform : public DefaultPlatform { +public: + ~MetalPlatform() override; + + Driver* createDriver(void* sharedContext) noexcept override; + int getOSVersion() const noexcept override { return 0; } + + /** + * 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(); + */ + virtual id createDevice() noexcept; + + /** + * Create a command submission queue on the Metal device object. + * + * @param device The device which was returned from createDevice() + */ + virtual id createCommandQueue(id device) noexcept; + + /** + * 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. + */ + id createAndEnqueueCommandBuffer() noexcept; + +private: + id mCommandQueue = nil; + +}; + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_PLATFORM_METAL_H diff --git a/filament/backend/src/Platform.cpp b/filament/backend/src/Platform.cpp index 16ed73dbad..6e96cbb0f8 100644 --- a/filament/backend/src/Platform.cpp +++ b/filament/backend/src/Platform.cpp @@ -63,7 +63,9 @@ #endif #if defined (FILAMENT_SUPPORTS_METAL) - #include "metal/PlatformMetal.h" +namespace filament::backend { +filament::backend::DefaultPlatform* createDefaultMetalPlatform(); +} #endif #include "noop/PlatformNoop.h" @@ -107,7 +109,7 @@ DefaultPlatform* DefaultPlatform::create(Backend* backend) noexcept { } if (*backend == Backend::METAL) { #if defined(FILAMENT_SUPPORTS_METAL) - return new PlatformMetal(); + return createDefaultMetalPlatform(); #else return nullptr; #endif diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 865f4a82d1..676869282a 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -27,6 +27,8 @@ #include "MetalState.h" #include "MetalTimerQuery.h" +#include "private/backend/MetalPlatform.h" + #include #include #include @@ -56,27 +58,8 @@ MetalDriver::MetalDriver(backend::MetalPlatform* platform) noexcept mContext(new MetalContext) { mContext->driver = this; -#if !defined(IOS) - const bool forceIntegrated = - NSProcessInfo.processInfo.environment[@"FILAMENT_FORCE_INTEGRATED_GPU"] != nil; - if (forceIntegrated) { - // Find the first low power device, which is likely the integrated GPU. - NSArray>* const devices = MTLCopyAllDevices(); - for (id device in devices) { - if (device.isLowPower) { - mContext->device = device; - break; - } - } - } else -#endif - { - mContext->device = MTLCreateSystemDefaultDevice(); - } - - utils::slog.i << "Selected physical device '" - << [mContext->device.name cStringUsingEncoding:NSUTF8StringEncoding] << "'" - << utils::io::endl; + mContext->device = mPlatform.createDevice(); + assert_invariant(mContext->device); // In order to support texture swizzling, the GPU needs to support it and the system be running // macOS 10.15+ / iOS 13+. @@ -102,8 +85,7 @@ MetalDriver::MetalDriver(backend::MetalPlatform* platform) noexcept } #endif - mContext->commandQueue = [mContext->device newCommandQueue]; - mContext->commandQueue.label = @"Filament"; + mContext->commandQueue = mPlatform.createCommandQueue(mContext->device); mContext->pipelineStateCache.setDevice(mContext->device); mContext->depthStencilStateCache.setDevice(mContext->device); mContext->samplerStateCache.setDevice(mContext->device); diff --git a/filament/backend/src/metal/MetalPlatform.mm b/filament/backend/src/metal/MetalPlatform.mm new file mode 100644 index 0000000000..11d2e56ef6 --- /dev/null +++ b/filament/backend/src/metal/MetalPlatform.mm @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "private/backend/MetalPlatform.h" + +#include "MetalDriverFactory.h" + +#include + +#import + +namespace filament { +namespace backend { + +DefaultPlatform* createDefaultMetalPlatform() { + return new MetalPlatform(); +} + +MetalPlatform::~MetalPlatform() = default; + +Driver* MetalPlatform::createDriver(void* sharedContext) noexcept { + return MetalDriverFactory::create(this); +} + +id MetalPlatform::createDevice() noexcept { + id result; + +#if !defined(IOS) + const bool forceIntegrated = + NSProcessInfo.processInfo.environment[@"FILAMENT_FORCE_INTEGRATED_GPU"] != nil; + if (forceIntegrated) { + // Find the first low power device, which is likely the integrated GPU. + NSArray>* const devices = MTLCopyAllDevices(); + for (id device in devices) { + if (device.isLowPower) { + result = device; + break; + } + } + } else +#endif + { + result = MTLCreateSystemDefaultDevice(); + } + + utils::slog.i << "Selected physical device '" + << [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'" + << utils::io::endl; + + return result; +} + +id MetalPlatform::createCommandQueue(id device) noexcept { + mCommandQueue = [device newCommandQueue]; + mCommandQueue.label = @"Filament"; + return mCommandQueue; +} + +id MetalPlatform::createAndEnqueueCommandBuffer() noexcept { + id commandBuffer = [mCommandQueue commandBuffer]; + [commandBuffer enqueue]; + return commandBuffer; +} + +} // namespace backend +} // namespace filament diff --git a/filament/backend/src/metal/PlatformMetal.h b/filament/backend/src/metal/PlatformMetal.h deleted file mode 100644 index a65d52182d..0000000000 --- a/filament/backend/src/metal/PlatformMetal.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_FILAMENT_DRIVER_PLATFORM_METAL_H -#define TNT_FILAMENT_DRIVER_PLATFORM_METAL_H - -#include - -#include -#include - -namespace filament { -namespace backend { - -class MetalPlatform : public DefaultPlatform { -public: - ~MetalPlatform() override; -}; - -} // namespace backend - -class PlatformMetal final : public backend::MetalPlatform { -public: - backend::Driver* createDriver(void* sharedContext) noexcept override; - int getOSVersion() const noexcept override { return 0; } - ~PlatformMetal() override; -}; - -} // namespace filament - -#endif // TNT_FILAMENT_DRIVER_PLATFORM_METAL_H diff --git a/filament/backend/src/metal/PlatformMetal.mm b/filament/backend/src/metal/PlatformMetal.mm deleted file mode 100644 index af15d837f8..0000000000 --- a/filament/backend/src/metal/PlatformMetal.mm +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PlatformMetal.h" -#include "MetalDriverFactory.h" - -namespace filament { - -namespace backend { -MetalPlatform::~MetalPlatform() = default; -} // namespace backend - - -using namespace backend; - -Driver* PlatformMetal::createDriver(void* sharedContext) noexcept { - return MetalDriverFactory::create(this); -} - -PlatformMetal::~PlatformMetal() = default; - - -} // namespace filament diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 71c9709262..95312f3e41 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -459,6 +459,32 @@ public: */ Backend getBackend() const noexcept; + /** + * Returns the Platform object that belongs to this Engine. + * + * When Engine::create is called with no platform argument, Filament creates an appropriate + * Platform subclass automatically. The specific subclass created depends on the backend and + * OS. For example, when the OpenGL backend is used, the Platform object will be a descendant of + * OpenGLPlatform. + * + * dynamic_cast should be used to cast the returned Platform object into a specific subclass. + * Note that RTTI must be available to use dynamic_cast. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Platform* platform = engine->getPlatform(); + * // static_cast also works, but more dangerous. + * SpecificPlatform* specificPlatform = dynamic_cast(platform); + * specificPlatform->platformSpecificMethod(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * When a custom Platform is passed to Engine::create, Filament will use it instead, and this + * method will return it. + * + * @return A pointer to the Platform object that was provided to Engine::create, or the + * Filament-created one. + */ + Platform* getPlatform() const noexcept; + /** * Allocate a small amount of memory directly in the command stream. The allocated memory is * guaranteed to be preserved until the current command buffer is executed diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index ef9048faae..cf35103fee 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -899,6 +899,10 @@ Backend Engine::getBackend() const noexcept { return upcast(this)->getBackend(); } +Platform* Engine::getPlatform() const noexcept { + return upcast(this)->getPlatform(); +} + Renderer* Engine::createRenderer() noexcept { return upcast(this)->createRenderer(); } diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index b27704ebd9..190205be93 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -210,6 +210,10 @@ public: return mBackend; } + Platform* getPlatform() const noexcept { + return mPlatform; + } + ResourceAllocator& getResourceAllocator() noexcept { assert_invariant(mResourceAllocator); return *mResourceAllocator;