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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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>()) {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user