Create exportable Syncs (fences) (#9110)

* Create exportable fences on Android

Also - create a conversion method for the driver api so that a fence can
be converted to an external handle, where available.

* Create a frontend API for external fences

Fences will be called 'Sync' on the frontend, and come with a method for
conversion to an external sync. This will typically be a file
descriptor, for use in posix systems.

* Refactor Sync to rely on opaque handles

Also - push the callback invocations into the backend, such that they
are scheduled on a worker thread, and not run on the Filament backend
thread.

* Address PR comment - minor refactor

Clear vectors after they've been consumed, and update null checks by
removing explicit reference to nullptr.

* Remove convertSyncToFd from non-android

Other platforms might have other handle types.

Also - remove unused header include

* Add logging for convert to fd

Also, remove duplicate includes

* Address PR - change fd type

As per request by rafadavi@, changed to match the Android spec for file
descriptor type, which is `int`.

* Address PR - remove vtable from sync

Also - rename derived sync classes

* Address comment - make SyncCallbackData private

It's technically implementation-specific, even though it currently
happens to be the same. Separate this into the different backends.

* Add stub for metal backend for sync

* Add stub impl for Sync in WebGPU
This commit is contained in:
Anish Goyal
2025-09-11 09:02:56 -07:00
committed by GitHub
parent 17b8b9ff65
commit e218eca6b0
37 changed files with 737 additions and 8 deletions
+3
View File
@@ -40,6 +40,7 @@ set(PUBLIC_HDRS
include/filament/Skybox.h
include/filament/Stream.h
include/filament/SwapChain.h
include/filament/Sync.h
include/filament/Texture.h
include/filament/TextureSampler.h
include/filament/ToneMapper.h
@@ -103,6 +104,7 @@ set(SRCS
src/Skybox.cpp
src/Stream.cpp
src/SwapChain.cpp
src/Sync.cpp
src/Texture.cpp
src/ToneMapper.cpp
src/TransformManager.cpp
@@ -132,6 +134,7 @@ set(SRCS
src/details/Skybox.cpp
src/details/Stream.cpp
src/details/SwapChain.cpp
src/details/Sync.cpp
src/details/Texture.cpp
src/details/VertexBuffer.cpp
src/details/View.cpp
+1
View File
@@ -295,6 +295,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUStrings.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUSync.h
src/webgpu/WebGPUTexture.cpp
src/webgpu/WebGPUTexture.h
src/webgpu/WebGPUTextureHelpers.h
@@ -38,6 +38,7 @@ struct HwRenderPrimitive;
struct HwRenderTarget;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
@@ -151,6 +152,7 @@ using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
@@ -29,6 +29,7 @@
namespace filament::backend {
class CallbackHandler;
class Driver;
/**
@@ -42,6 +43,9 @@ public:
struct SwapChain {};
struct Fence {};
struct Stream {};
struct Sync {};
using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData);
class ExternalImageHandle;
@@ -276,6 +276,26 @@ public:
*/
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
// Sync support
/**
* Creates a Sync. These can be used for frame synchronization externally
* (certain platform implementations can be exported to handles that can
* be used in other processes).
*
* @return A Sync object.
*/
virtual Platform::Sync* UTILS_NONNULL createSync() noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, that was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept;
// --------------------------------------------------------------------------------------------
// Streaming support
@@ -58,6 +58,17 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -74,6 +85,10 @@ protected:
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Platform::Sync {
EGLSyncKHR sync;
};
void terminate() noexcept override;
void beginFrame(
@@ -89,9 +104,10 @@ protected:
*/
void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override;
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Platform::Sync* createSync() noexcept override;
void destroySync(Platform::Sync* sync) noexcept override;
void attach(Stream* stream, intptr_t tname) noexcept override;
void detach(Stream* stream) noexcept override;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override;
@@ -17,8 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
@@ -29,9 +30,9 @@
#include <cstddef>
#include <functional>
#include <string>
#include <tuple>
#include <unordered_set>
#include <string>
#include <stddef.h>
#include <stdint.h>
@@ -45,6 +46,10 @@ using SwapChain = Platform::SwapChain;
*/
struct VulkanPlatformPrivate;
// Forward declare the fence status that will be maintained by the command
// buffer manager.
struct VulkanCmdFence;
/**
* A Platform interface that creates a Vulkan backend.
*/
@@ -229,6 +234,25 @@ public:
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Creates a Platform::Sync object, which tracks a fence and its status,
* and allows conversion to an external sync.
* @param fence The underlying VkFence to use for synchronization.
* @param fenceStatus An object tracking the fence's state
* @return A Platform::Sync object tracking the provided fence.
*/
virtual Platform::Sync* createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, which was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* sync) noexcept;
/**
* Allows implementers to provide instance extensions that they'd like to include in the
* instance creation.
@@ -408,12 +432,19 @@ public:
}
protected:
struct VulkanSync : public Platform::Sync {
VkFence fence;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
@@ -44,6 +44,17 @@ public:
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
@@ -51,6 +62,8 @@ protected:
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
@@ -272,6 +272,8 @@ DECL_DRIVER_API_TAGGED_R_N(backend::RenderTargetHandle, createRenderTarget,
DECL_DRIVER_API_TAGGED_R_N(backend::FenceHandle, createFence)
DECL_DRIVER_API_TAGGED_R_N(backend::SyncHandle, createSync)
DECL_DRIVER_API_TAGGED_R_N(backend::SwapChainHandle, createSwapChain,
void*, nativeWindow,
uint64_t, flags)
@@ -328,6 +330,7 @@ DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch)
DECL_DRIVER_API_N(destroyStream, backend::StreamHandle, sh)
DECL_DRIVER_API_N(destroyTimerQuery, backend::TimerQueryHandle, sh)
DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh)
DECL_DRIVER_API_N(destroySync, backend::SyncHandle, sh)
DECL_DRIVER_API_N(destroyDescriptorSetLayout, backend::DescriptorSetLayoutHandle, dslh)
DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, dsh)
@@ -344,6 +347,8 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, setStreamDimensions, backend::StreamHandle,
DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle, stream)
DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, getFenceStatus, backend::FenceHandle, fh)
DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
backend::CallbackHandler*, handler, backend::Platform::SyncCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isTextureSwizzleSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatMipmappable, backend::TextureFormat, format)
+4
View File
@@ -140,6 +140,10 @@ struct HwFence : public HwBase {
Platform::Fence* fence = nullptr;
};
struct HwSync : public HwBase {
Platform::Sync* sync = nullptr;
};
struct HwSwapChain : public HwBase {
Platform::SwapChain* swapChain = nullptr;
};
+23
View File
@@ -671,6 +671,12 @@ void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
@@ -816,6 +822,10 @@ Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<MetalSwapChain>();
}
Handle<HwSync> MetalDriver::createSyncS() noexcept {
return alloc_handle<MetalSync>();
}
Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
@@ -1017,6 +1027,19 @@ FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fence->wait(0);
}
void MetalDriver::destroySync(Handle<HwSync> sh) {
if (sh) {
destruct_handle<MetalSync>(sh);
}
}
void MetalDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
// TODO: If the sync has been inserted into the command stream, execute
// the callback. Otherwise, enqueue it.
}
bool MetalDriver::isTextureFormatSupported(TextureFormat format) {
return MetalTexture::decidePixelFormat(mContext, format) != MTLPixelFormatInvalid;
}
@@ -437,6 +437,10 @@ private:
uint64_t value;
};
// TODO: Provide implementation for MetalSync.
class MetalSync : public HwSync {
};
struct MetalTimerQuery : public HwTimerQuery {
MetalTimerQuery() : status(std::make_shared<Status>()) {}
+8 -2
View File
@@ -16,12 +16,11 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
#include <backend/Handle.h>
#include <stdint.h>
namespace filament::backend {
@@ -116,6 +115,9 @@ void NoopDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void NoopDriver::destroyStream(Handle<HwStream> sh) {
}
void NoopDriver::destroySync(Handle<HwSync> sh) {
}
void NoopDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
@@ -147,6 +149,10 @@ int64_t NoopDriver::getStreamTimestamp(Handle<HwStream> sh) {
void NoopDriver::updateStreams(CommandStream* driver) {
}
void NoopDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
}
void NoopDriver::destroyFence(Handle<HwFence> fh) {
}
@@ -149,6 +149,18 @@ using namespace utils;
namespace filament::backend {
namespace {
static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
std::unique_ptr<OpenGLDriver::GLSyncFence::CallbackData> cbData(
static_cast<OpenGLDriver::GLSyncFence::CallbackData*>(userData));
// This assumes the sync has not yet been destroyed. If it has, this will be
// undefined behavior.
cbData->cb(cbData->sync, cbData->userData);
};
} // namespace
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* platform,
void* sharedGLContext,
@@ -615,6 +627,10 @@ Handle<HwFence> OpenGLDriver::createFenceS() noexcept {
return initHandle<GLFence>();
}
Handle<HwSync> OpenGLDriver::createSyncS() noexcept {
return initHandle<GLSyncFence>();
}
Handle<HwSwapChain> OpenGLDriver::createSwapChainS() noexcept {
return initHandle<GLSwapChain>();
}
@@ -1807,6 +1823,24 @@ void OpenGLDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void OpenGLDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
DEBUG_MARKER()
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
{
std::lock_guard<std::mutex> guard(s->lock);
s->sync = mPlatform.createSync();
}
for (auto& cbData : s->conversionCallbacks) {
cbData->sync = s->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
s->conversionCallbacks.clear();
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString tag) {
DEBUG_MARKER()
@@ -2080,6 +2114,16 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
}
}
void OpenGLDriver::destroySync(Handle<HwSync> sh) {
DEBUG_MARKER()
if (sh) {
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
mPlatform.destroySync(s->sync);
destruct(sh, s);
}
}
void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
DEBUG_MARKER()
@@ -2268,6 +2312,33 @@ FenceStatus OpenGLDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::ERROR;
}
void OpenGLDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
if (!sh) {
return;
}
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
auto cbData = std::make_unique<GLSyncFence::CallbackData>();
cbData->handler = handler;
cbData->cb = cb;
cbData->userData = userData;
// If we haven't already set the handle, toss the conversion callback in the
// back of the list.
{
std::lock_guard guard(s->lock);
if (s->sync == nullptr) {
s->conversionCallbacks.push_back(std::move(cbData));
return;
}
}
// Otherwise, go ahead and schedule the callback now.
cbData->sync = s->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
bool OpenGLDriver::isTextureFormatSupported(TextureFormat format) {
const auto& ext = mContext.ext;
if (isETC2Compression(format)) {
@@ -198,6 +198,19 @@ public:
std::shared_ptr<State> state{ std::make_shared<State>() };
};
// Note: named "GLSyncFence" to avoid confusion with the GL handle,
// "GLsync" (lowercase S)
struct GLSyncFence : public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
OpenGLDriver(OpenGLDriver const&) = delete;
OpenGLDriver& operator=(OpenGLDriver const&) = delete;
@@ -117,6 +117,15 @@ FenceStatus OpenGLPlatform::waitFence(
return FenceStatus::ERROR;
}
Platform::Sync* OpenGLPlatform::createSync() noexcept {
return new Platform::Sync();
}
void OpenGLPlatform::destroySync(Platform::Sync* sync) noexcept {
// sync must be a Platform::Sync, since it was created by this platform
// object.
delete sync;
}
Platform::Stream* OpenGLPlatform::createStream(
UTILS_UNUSED void* nativeStream) noexcept {
@@ -83,6 +83,7 @@ UTILS_PRIVATE PFNEGLGETCOMPOSITORTIMINGANDROIDPROC eglGetCompositorTimingANDROID
UTILS_PRIVATE PFNEGLGETNEXTFRAMEIDANDROIDPROC eglGetNextFrameIdANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC eglGetFrameTimestampSupportedANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSANDROIDPROC eglGetFrameTimestampsANDROID = {};
UTILS_PRIVATE PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = {};
}
using namespace glext;
@@ -226,6 +227,8 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
"eglGetFrameTimestampSupportedANDROID");
eglGetFrameTimestampsANDROID = (PFNEGLGETFRAMETIMESTAMPSANDROIDPROC)eglGetProcAddress(
"eglGetFrameTimestampsANDROID");
eglDupNativeFenceFDANDROID =
(PFNEGLDUPNATIVEFENCEFDANDROIDPROC) eglGetProcAddress("eglDupNativeFenceFDANDROID");
}
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
@@ -395,6 +398,31 @@ void PlatformEGLAndroid::destroyStream(Platform::Stream* stream) noexcept {
mExternalStreamManager.release(stream);
}
Platform::Sync* PlatformEGLAndroid::createSync() noexcept {
auto sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
return new SyncEGLAndroid{.sync = sync};
}
bool PlatformEGLAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) noexcept {
assert_invariant(sync && fd);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
*fd = eglDupNativeFenceFDANDROID(mEGLDisplay, eglSync.sync);
// In the case where there was no native FD, -1 is returned. Return false
// to indicate there was an error in this case.
if (*fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
LOG(ERROR) << "Failed to convert sync to fd: " << eglGetError();
return false;
}
return true;
}
void PlatformEGLAndroid::destroySync(Platform::Sync* sync) noexcept {
assert_invariant(sync);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
eglDestroySyncKHR(mEGLDisplay, eglSync.sync);
delete sync;
}
void PlatformEGLAndroid::attach(Stream* stream, intptr_t tname) noexcept {
mExternalStreamManager.attach(stream, tname);
}
@@ -20,6 +20,7 @@
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "backend/Platform.h"
#include "vulkan/memory/Resource.h"
@@ -52,6 +53,19 @@ struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
VulkanSync() {}
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
: mStartingQueryIndex(startingIndex),
@@ -103,6 +103,12 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkExportFenceCreateInfo exportFenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
.handleTypes = context.getFenceExportFlags()
};
fenceCreateInfo.pNext = &exportFenceCreateInfo;
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
}
@@ -425,6 +431,9 @@ bool VulkanCommands::flush() {
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
VkFence flushedFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
// Note that we've ordered it so that the non-protected commands are followed by the protected
// commands. This assumes that the protected commands will be that one doing the rendering into
// the protected memory (i.e. protected render target).
@@ -446,6 +455,8 @@ bool VulkanCommands::flush() {
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
lastSubmit = VK_NULL_HANDLE;
}
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
dependency = pool->flush();
hasFlushed = true;
}
@@ -453,6 +464,8 @@ bool VulkanCommands::flush() {
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
mLastFence = flushedFence;
mLastFenceStatus = flushedFenceStatus;
}
return true;
+12 -1
View File
@@ -223,11 +223,19 @@ public:
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal() {
VkSemaphore ret= mLastSubmit;
VkSemaphore ret = mLastSubmit;
mLastSubmit = VK_NULL_HANDLE;
return ret;
}
VkFence getMostRecentFence() {
return mLastFence;
}
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
return mLastFenceStatus;
}
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
// waitStage
@@ -265,6 +273,9 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
};
@@ -106,6 +106,10 @@ public:
return mPhysicalDeviceProperties.properties.vendorID;
}
inline VkExternalFenceHandleTypeFlags getFenceExportFlags() const noexcept {
return mFenceExportFlags;
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
}
@@ -168,6 +172,9 @@ private:
// non-conformant vulkan implementation).
.imageView2DOn3DImage = VK_TRUE,
};
VkExternalFenceHandleTypeFlags mFenceExportFlags = {};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false;
@@ -136,6 +136,13 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
std::unique_ptr<VulkanSync::CallbackData> cbData(
static_cast<VulkanSync::CallbackData*>(userData));
// This assumes the sync has not yet been destroyed. If it has, this will be
// undefined behavior.
cbData->cb(cbData->sync, cbData->userData);
};
}// anonymous namespace
@@ -822,6 +829,36 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
mResourceManager.associateHandle(fh.getId(), std::move(tag));
}
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
VkFence fence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fenceStatus;
if (mCurrentRenderPass.commandBuffer) {
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
fence = cmdBuff->getVkFence();
fenceStatus = cmdBuff->getFenceStatus();
// If we're currently recording, flush so that the fence only applies
// to commands already issued.
mCommands.flush();
} else {
fence = mCommands.getMostRecentFence();
fenceStatus = mCommands.getMostRecentFenceStatus();
}
{
std::lock_guard<std::mutex> guard(sync->lock);
sync->sync = mPlatform->createSync(fence, fenceStatus);
}
for (auto& cbData : sync->conversionCallbacks) {
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
sync->conversionCallbacks.clear();
mResourceManager.associateHandle(sh.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString tag) {
FVK_SYSTRACE_SCOPE();
@@ -954,6 +991,13 @@ Handle<HwFence> VulkanDriver::createFenceS() noexcept {
return handle;
}
Handle<HwSync> VulkanDriver::createSyncS() noexcept {
auto handle = mResourceManager.allocHandle<VulkanSync>();
auto sync = resource_ptr<VulkanSync>::make(&mResourceManager, handle);
sync.inc();
return handle;
}
Handle<HwSwapChain> VulkanDriver::createSwapChainS() noexcept {
return mResourceManager.allocHandle<VulkanSwapChain>();
}
@@ -992,6 +1036,17 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void VulkanDriver::destroyStream(Handle<HwStream> sh) {
}
void VulkanDriver::destroySync(Handle<HwSync> sh) {
if (!sh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
mPlatform->destroySync(sync->sync);
sync.dec();
}
void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (!tqh) {
return;
@@ -1067,6 +1122,29 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::TIMEOUT_EXPIRED;
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
auto cbData = std::make_unique<VulkanSync::CallbackData>();
cbData->handler = handler;
cbData->cb = cb;
cbData->userData = userData;
// If we haven't already set the handle, toss the conversion callback in the
// back of the list.
{
std::lock_guard guard(sync->lock);
if (sync->sync == nullptr) {
sync->conversionCallbacks.push_back(std::move(cbData));
return;
}
}
// Otherwise, go ahead and schedule the callback now.
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool VulkanDriver::isTextureFormatSupported(TextureFormat format) {
@@ -38,6 +38,7 @@ template ResourceType getTypeEnum<VulkanDescriptorSetLayout>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
template ResourceType getTypeEnum<VulkanFence>() noexcept;
template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSync>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
@@ -92,6 +93,9 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanBuffer>) {
return ResourceType::VULKAN_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanSync>) {
return ResourceType::SYNC;
}
return ResourceType::UNDEFINED_TYPE;
}
@@ -131,6 +135,8 @@ std::string_view getTypeStr(ResourceType type) {
return "Fence";
case ResourceType::VULKAN_BUFFER:
return "VulkanBuffer";
case ResourceType::SYNC:
return "Sync";
case ResourceType::UNDEFINED_TYPE:
return "";
}
@@ -52,7 +52,8 @@ enum class ResourceType : uint8_t {
VULKAN_BUFFER = 14,
STAGE_SEGMENT = 15,
STAGE_IMAGE = 16,
UNDEFINED_TYPE = 17, // Must be the last enum because we use it for iterating over the enums.
SYNC = 17,
UNDEFINED_TYPE = 18, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>
@@ -114,6 +114,9 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::VULKAN_BUFFER:
destruct<VulkanBuffer>(Handle<VulkanBuffer>(id));
break;
case ResourceType::SYNC:
destruct<VulkanSync>(Handle<VulkanSync>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}
@@ -920,6 +920,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
context.mBlittableDepthStencilFormats =
findBlittableDepthStencilFormats(mImpl->mPhysicalDevice);
context.mFenceExportFlags = getFenceExportFlags();
assert_invariant(context.mDepthStencilFormats.size() > 0);
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -1002,6 +1004,17 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
return swapchain;
}
Platform::Sync* VulkanPlatform::createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept {
return new VulkanSync{.fence = fence, .fenceStatus = fenceStatus};
}
void VulkanPlatform::destroySync(Platform::Sync* sync) noexcept {
// Sync must be a VulkanSync*, since it was created by VulkanPlatform's
// createSync object.
delete sync;
}
VkInstance VulkanPlatform::getInstance() const noexcept {
return mImpl->mInstance;
}
@@ -1038,6 +1051,11 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const noexcept {
// By default, fences should not be exportable.
return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
@@ -389,6 +389,37 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
};
}
bool VulkanPlatformAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept {
assert_invariant(sync && fd);
VulkanSync& vulkanSync = static_cast<VulkanSync&>(*sync);
assert_invariant(vulkanSync.fenceStatus);
if (vulkanSync.fenceStatus->getStatus() == VK_SUCCESS) {
// We've already signaled; return -1 so that operations will proceed
// immediately. Also, signal that fence conversion was successful.
*fd = -1;
return true;
}
VkFenceGetFdInfoKHR getFdInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR,
.fence = vulkanSync.fence,
.handleType = getFenceExportFlags(),
};
VkResult res = vkGetFenceFdKHR(getDevice(), &getFdInfo, fd);
if (res != VK_SUCCESS) {
LOG(ERROR) << "Failed to convert sync to fd: " << res;
return false;
}
return true;
}
VkExternalFenceHandleTypeFlagBits VulkanPlatformAndroid::getFenceExportFlags() const noexcept {
// On android, make fences that can be exported to fd's.
return VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
@@ -28,6 +28,7 @@
#include "WebGPURenderTarget.h"
#include "WebGPUStrings.h"
#include "WebGPUSwapChain.h"
#include "WebGPUSync.h"
#include "WebGPUTexture.h"
#include "WebGPUTextureHelpers.h"
#include "WebGPUVertexBuffer.h"
@@ -377,6 +378,10 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
return allocAndConstructHandle<WebGPUFence, HwFence>();
}
Handle<HwSync> WebGPUDriver::createSyncS() noexcept {
return allocHandle<WebGPUSync>();
}
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
}
@@ -650,6 +655,12 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString tag)
setDebugTag(fenceHandle.getId(), std::move(tag));
}
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::CString tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
setDebugTag(syncHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {}
void WebGPUDriver::createDescriptorSetLayoutR(
@@ -718,6 +729,18 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fenceHandle) {
return fence->getStatus();
}
void WebGPUDriver::destroySync(Handle<HwSync> syncHandle) {
if (syncHandle) {
destructHandle<WebGPUSync>(syncHandle);
}
}
void WebGPUDriver::getPlatformSync(Handle<HwSync> syncHandle, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
// TODO: If the sync has been inserted into the command stream, execute
// the callback. Otherwise, enqueue it.
}
bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
return toWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2025 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_BACKEND_WEBGPUSYNC_H
#define TNT_FILAMENT_BACKEND_WEBGPUSYNC_H
#include "DriverBase.h"
namespace filament::backend {
// TODO: provide a more complete implementation of Sync.
class WebGPUSync final : public HwSync {
};
}
#endif // TNT_FILAMENT_BACKEND_WEBGPUSYNC_H
+13
View File
@@ -63,6 +63,7 @@ class Scene;
class Skybox;
class Stream;
class SwapChain;
class Sync;
class Texture;
class VertexBuffer;
class View;
@@ -886,9 +887,19 @@ public:
*/
Fence* UTILS_NONNULL createFence() noexcept;
/**
* Creates a Sync.
* @param callback A callback that will be invoked when the handle for
* the created sync is set
*
* @return A pointer to the newly created Sync.
*/
Sync* UTILS_NONNULL createSync() noexcept;
bool destroy(const BufferObject* UTILS_NULLABLE p); //!< Destroys a BufferObject object.
bool destroy(const VertexBuffer* UTILS_NULLABLE p); //!< Destroys an VertexBuffer object.
bool destroy(const Fence* UTILS_NULLABLE p); //!< Destroys a Fence object.
bool destroy(const Sync* UTILS_NULLABLE p); //!< Destroys a Sync object.
bool destroy(const IndexBuffer* UTILS_NULLABLE p); //!< Destroys an IndexBuffer object.
bool destroy(const SkinningBuffer* UTILS_NULLABLE p); //!< Destroys a SkinningBuffer object.
bool destroy(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Destroys a MorphTargetBuffer object.
@@ -922,6 +933,8 @@ public:
bool isValid(const VertexBuffer* UTILS_NULLABLE p) const;
/** Tells whether a Fence object is valid */
bool isValid(const Fence* UTILS_NULLABLE p) const;
/** Tells whether a Sync object is valid */
bool isValid(const Sync* UTILS_NULLABLE p) const;
/** Tells whether an IndexBuffer object is valid */
bool isValid(const IndexBuffer* UTILS_NULLABLE p) const;
/** Tells whether a SkinningBuffer object is valid */
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2025 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_SYNC_H
#define TNT_FILAMENT_SYNC_H
#include <filament/FilamentAPI.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
namespace filament {
class UTILS_PUBLIC Sync : public FilamentAPI {
public:
using CallbackHandler = backend::CallbackHandler;
using Callback = backend::Platform::SyncCallback;
/**
* Fetches a handle to the external, platform-specific representation of
* this sync object.
*
* @param handler A handler for the callback that will receive the handle
* @param callback A callback that will receive the handle when ready
* @param userData Data to be passed to the callback so that the application
* can identify what frame the sync is relevant to.
* @return The external handle for the Sync. This is valid destroy() is
* called on this Sync object.
*/
void getExternalHandle(CallbackHandler* handler, Callback callback, void* userData) noexcept;
protected:
// prevent heap allocation
~Sync() = default;
};
} // namespace filament
#endif // TNT_FILAMENT_SYNC_H
+12
View File
@@ -30,6 +30,7 @@
#include "details/Skybox.h"
#include "details/Stream.h"
#include "details/SwapChain.h"
#include "details/Sync.h"
#include "details/Texture.h"
#include "details/VertexBuffer.h"
#include "details/View.h"
@@ -132,6 +133,10 @@ SwapChain* Engine::createSwapChain(uint32_t const width, uint32_t const height,
return downcast(this)->createSwapChain(width, height, flags);
}
Sync* Engine::createSync() noexcept {
return downcast(this)->createSync();
}
bool Engine::destroy(const BufferObject* p) {
return downcast(this)->destroy(downcast(p));
}
@@ -204,6 +209,10 @@ bool Engine::destroy(const SwapChain* p) {
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Sync* p) {
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const InstanceBuffer* p) {
return downcast(this)->destroy(downcast(p));
}
@@ -221,6 +230,9 @@ bool Engine::isValid(const VertexBuffer* p) const {
bool Engine::isValid(const Fence* p) const {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const Sync* p) const {
return downcast(this)->isValid(downcast(p));
}
bool Engine::isValid(const IndexBuffer* p) const {
return downcast(this)->isValid(downcast(p));
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2025 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 "details/Sync.h"
#include <backend/Platform.h>
namespace filament {
void Sync::getExternalHandle(Sync::CallbackHandler* handler, Sync::Callback callback,
void* userData) noexcept {
downcast(this)->getExternalHandle(handler, callback, userData);
}
} // namespace filament
+19
View File
@@ -34,6 +34,7 @@
#include "details/Skybox.h"
#include "details/Stream.h"
#include "details/SwapChain.h"
#include "details/Sync.h"
#include "details/Texture.h"
#include "details/VertexBuffer.h"
#include "details/View.h"
@@ -1011,6 +1012,15 @@ FSwapChain* FEngine::createSwapChain(uint32_t width, uint32_t height, uint64_t f
return p;
}
FSync* FEngine::createSync() noexcept {
FSync* p = mHeapAllocator.make<FSync>(*this);
if (UTILS_LIKELY(p)) {
std::lock_guard const guard(mSyncListLock);
mSyncs.insert(p);
}
return p;
}
/*
* Objects created with a component manager
*/
@@ -1200,6 +1210,11 @@ bool FEngine::destroy(const FSwapChain* p) {
return terminateAndDestroy(p, mSwapChains);
}
UTILS_NOINLINE
bool FEngine::destroy(const FSync* p) {
return terminateAndDestroyLocked(mSyncListLock, p, mSyncs);
}
UTILS_NOINLINE
bool FEngine::destroy(const FStream* p) {
return terminateAndDestroy(p, mStreams);
@@ -1281,6 +1296,10 @@ bool FEngine::isValid(const FFence* p) const {
return isValid(p, mFences);
}
bool FEngine::isValid(const FSync* p) const {
return isValid(p, mSyncs);
}
bool FEngine::isValid(const FIndexBuffer* p) const {
return isValid(p, mIndexBuffers);
}
+11 -1
View File
@@ -40,10 +40,11 @@
#include "details/Fence.h"
#include "details/IndexBuffer.h"
#include "details/InstanceBuffer.h"
#include "details/MorphTargetBuffer.h"
#include "details/RenderTarget.h"
#include "details/SkinningBuffer.h"
#include "details/MorphTargetBuffer.h"
#include "details/Skybox.h"
#include "details/Sync.h"
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/CommandStream.h"
@@ -122,6 +123,7 @@ class FMaterialInstance;
class FRenderer;
class FScene;
class FSwapChain;
class FSync;
class FView;
class ResourceAllocator;
@@ -331,6 +333,7 @@ public:
FScene* createScene() noexcept;
FView* createView() noexcept;
FFence* createFence() noexcept;
FSync* createSync() noexcept;
FSwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept;
FSwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept;
@@ -342,6 +345,7 @@ public:
bool destroy(const FBufferObject* p);
bool destroy(const FVertexBuffer* p);
bool destroy(const FFence* p);
bool destroy(const FSync* p);
bool destroy(const FIndexBuffer* p);
bool destroy(const FSkinningBuffer* p);
bool destroy(const FMorphTargetBuffer* p);
@@ -362,6 +366,7 @@ public:
bool isValid(const FBufferObject* p) const;
bool isValid(const FVertexBuffer* p) const;
bool isValid(const FFence* p) const;
bool isValid(const FSync* p) const;
bool isValid(const FIndexBuffer* p) const;
bool isValid(const FSkinningBuffer* p) const;
bool isValid(const FMorphTargetBuffer* p) const;
@@ -613,6 +618,11 @@ private:
utils::Mutex mFenceListLock;
ResourceList<FFence> mFences{"Fence"};
// the sync list is accessed from multiple threads, because they are
// synchronization objects.
utils::Mutex mSyncListLock;
ResourceList<FSync> mSyncs{ "Sync" };
mutable uint32_t mMaterialId = 0;
// FMaterialInstance are handled directly by FMaterial
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2025 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 "details/Sync.h"
#include "details/Engine.h"
#include <backend/Platform.h>
#include <filament/Sync.h>
namespace filament {
using DriverApi = backend::DriverApi;
FSync::FSync(FEngine& engine)
: mEngine(engine) {
DriverApi& driverApi = engine.getDriverApi();
mHwSync = driverApi.createSync();
}
void FSync::terminate(FEngine& engine) noexcept {
engine.getDriverApi().destroySync(mHwSync);
}
void FSync::getExternalHandle(Sync::CallbackHandler* handler, Sync::Callback callback,
void* userData) noexcept {
mEngine.getDriverApi().getPlatformSync(mHwSync, handler, callback, userData);
}
} // namespace filament
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2025 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_DETAILS_SYNC_H
#define TNT_FILAMENT_DETAILS_SYNC_H
#include "downcast.h"
#include <filament/Sync.h>
#include <backend/CallbackHandler.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
namespace filament {
class FEngine;
class FSync : public Sync {
public:
FSync(FEngine& engine);
void terminate(FEngine& engine) noexcept;
backend::SyncHandle getHwHandle() const noexcept { return mHwSync; }
/**
* Fetches a handle to the external, platform-specific representation of
* this sync object.
*
* @param handler A handler for the callback that will receive the handle
* @param callback A callback that will receive the handle when ready
* @param userData Data to be passed to the callback so that the application
* can identify what frame the sync is relevant to.
* @return The external handle for the Sync. This is valid destroy() is
* called on this Sync object.
*/
void getExternalHandle(Sync::CallbackHandler* handler, Sync::Callback callback,
void* userData) noexcept;
private:
FEngine& mEngine;
backend::SyncHandle mHwSync;
};
FILAMENT_DOWNCAST(Sync)
} // namespace filament
#endif // TNT_FILAMENT_DETAILS_SYNC_H