зеркало из https://github.com/AvaloniaUI/angle.git
Metal: Implement fence sync
Bug: angleproject:2634 Change-Id: If1f7bb12c0e661c8e4b5677798a92440995819e4 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2433325 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Родитель
0c046affc5
Коммит
6dfd855a10
|
@ -48,6 +48,9 @@ struct FeaturesMtl : FeatureSetBase
|
|||
"has_msaa_stencil_auto_resolve", FeatureCategory::MetalFeatures,
|
||||
"The renderer supports MSAA stencil auto resolve at the end of render pass", &members};
|
||||
|
||||
Feature hasEvents = {"has_mtl_events", FeatureCategory::MetalFeatures,
|
||||
"The renderer supports MTL(Shared)Event", &members};
|
||||
|
||||
// On macos, separate depth & stencil buffers are not supproted. However, on iOS devices,
|
||||
// they are supproted:
|
||||
Feature allowSeparatedDepthStencilBuffers = {
|
||||
|
|
|
@ -33,6 +33,8 @@ _metal_backend_sources = [
|
|||
"ShaderMtl.mm",
|
||||
"SurfaceMtl.h",
|
||||
"SurfaceMtl.mm",
|
||||
"SyncMtl.h",
|
||||
"SyncMtl.mm",
|
||||
"TextureMtl.h",
|
||||
"TextureMtl.mm",
|
||||
"TransformFeedbackMtl.h",
|
||||
|
|
|
@ -289,12 +289,15 @@ class ContextMtl : public ContextImpl, public mtl::Context
|
|||
// NOTE: the old query's result will be retained and combined with the new result.
|
||||
angle::Result restartActiveOcclusionQueryInRenderPass();
|
||||
|
||||
const MTLClearColor &getClearColorValue() const;
|
||||
|
||||
// Invoke by TransformFeedbackMtl
|
||||
void onTransformFeedbackActive(const gl::Context *context, TransformFeedbackMtl *xfb);
|
||||
void onTransformFeedbackInactive(const gl::Context *context, TransformFeedbackMtl *xfb);
|
||||
|
||||
// Invoke by mtl::Sync
|
||||
void queueEventSignal(const mtl::SharedEventRef &event, uint64_t value);
|
||||
void serverWaitEvent(const mtl::SharedEventRef &event, uint64_t value);
|
||||
|
||||
const MTLClearColor &getClearColorValue() const;
|
||||
MTLColorWriteMask getColorMask() const;
|
||||
float getClearDepthValue() const;
|
||||
uint32_t getClearStencilValue() const;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "libANGLE/renderer/metal/RenderBufferMtl.h"
|
||||
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
|
||||
#include "libANGLE/renderer/metal/ShaderMtl.h"
|
||||
#include "libANGLE/renderer/metal/SyncMtl.h"
|
||||
#include "libANGLE/renderer/metal/TextureMtl.h"
|
||||
#include "libANGLE/renderer/metal/TransformFeedbackMtl.h"
|
||||
#include "libANGLE/renderer/metal/VertexArrayMtl.h"
|
||||
|
@ -978,13 +979,11 @@ QueryImpl *ContextMtl::createQuery(gl::QueryType type)
|
|||
}
|
||||
FenceNVImpl *ContextMtl::createFenceNV()
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
return new FenceNVMtl();
|
||||
}
|
||||
SyncImpl *ContextMtl::createSync()
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
return new SyncMtl();
|
||||
}
|
||||
|
||||
// Transform Feedback creation
|
||||
|
@ -1609,6 +1608,22 @@ void ContextMtl::onTransformFeedbackInactive(const gl::Context *context, Transfo
|
|||
endEncoding(true);
|
||||
}
|
||||
|
||||
void ContextMtl::queueEventSignal(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
ensureCommandBufferReady();
|
||||
mCmdBuffer.queueEventSignal(event, value);
|
||||
}
|
||||
|
||||
void ContextMtl::serverWaitEvent(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
ensureCommandBufferReady();
|
||||
|
||||
// Event waiting cannot be encoded if there is active encoder.
|
||||
endEncoding(true);
|
||||
|
||||
mCmdBuffer.serverWaitEvent(event, value);
|
||||
}
|
||||
|
||||
void ContextMtl::updateProgramExecutable(const gl::Context *context)
|
||||
{
|
||||
// Need to rebind textures
|
||||
|
|
|
@ -149,7 +149,9 @@ class DisplayMtl : public DisplayImpl
|
|||
{
|
||||
return mFormatTable.getVertexFormat(angleFormatId, tightlyPacked);
|
||||
}
|
||||
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
mtl::AutoObjCObj<MTLSharedEventListener> getOrCreateSharedEventListener();
|
||||
#endif
|
||||
protected:
|
||||
void generateExtensions(egl::DisplayExtensions *outExtensions) const override;
|
||||
void generateCaps(egl::Caps *outCaps) const override;
|
||||
|
@ -174,6 +176,9 @@ class DisplayMtl : public DisplayImpl
|
|||
|
||||
// Built-in Shaders
|
||||
mtl::AutoObjCPtr<id<MTLLibrary>> mDefaultShaders = nil;
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
mtl::AutoObjCObj<MTLSharedEventListener> mSharedEventListener;
|
||||
#endif
|
||||
|
||||
mutable bool mCapsInitialized;
|
||||
mutable gl::TextureCapsMap mNativeTextureCaps;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "libANGLE/renderer/glslang_wrapper_utils.h"
|
||||
#include "libANGLE/renderer/metal/ContextMtl.h"
|
||||
#include "libANGLE/renderer/metal/SurfaceMtl.h"
|
||||
#include "libANGLE/renderer/metal/SyncMtl.h"
|
||||
#include "libANGLE/renderer/metal/mtl_common.h"
|
||||
#include "libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders_autogen.inc"
|
||||
#include "platform/Platform.h"
|
||||
|
@ -99,8 +100,11 @@ void DisplayMtl::terminate()
|
|||
{
|
||||
mUtils.onDestroy();
|
||||
mCmdQueue.reset();
|
||||
mDefaultShaders = nil;
|
||||
mMetalDevice = nil;
|
||||
mDefaultShaders = nil;
|
||||
mMetalDevice = nil;
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
mSharedEventListener = nil;
|
||||
#endif
|
||||
mCapsInitialized = false;
|
||||
|
||||
mMetalDeviceVendorId = 0;
|
||||
|
@ -158,7 +162,7 @@ egl::Error DisplayMtl::waitClient(const gl::Context *context)
|
|||
egl::Error DisplayMtl::waitNative(const gl::Context *context, EGLint engine)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return egl::EglBadAccess();
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
SurfaceImpl *DisplayMtl::createWindowSurface(const egl::SurfaceState &state,
|
||||
|
@ -241,8 +245,7 @@ gl::Version DisplayMtl::getMaxConformantESVersion() const
|
|||
|
||||
EGLSyncImpl *DisplayMtl::createSync(const egl::AttributeMap &attribs)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
return new EGLSyncMtl(attribs);
|
||||
}
|
||||
|
||||
egl::Error DisplayMtl::makeCurrent(egl::Display *display,
|
||||
|
@ -266,6 +269,13 @@ void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const
|
|||
outExtensions->displayTextureShareGroup = true;
|
||||
outExtensions->displaySemaphoreShareGroup = true;
|
||||
|
||||
if (mFeatures.hasEvents.enabled)
|
||||
{
|
||||
// MTLSharedEvent is only available since Metal 2.1
|
||||
outExtensions->fenceSync = true;
|
||||
outExtensions->waitSync = true;
|
||||
}
|
||||
|
||||
// Note that robust resource initialization is not yet implemented. We only expose
|
||||
// this extension so that ANGLE can be initialized in Chrome. WebGL will fail to use
|
||||
// this extension (anglebug.com/4929)
|
||||
|
@ -651,6 +661,17 @@ void DisplayMtl::initializeExtensions() const
|
|||
|
||||
// GL_NV_pixel_buffer_object
|
||||
mNativeExtensions.pixelBufferObjectNV = true;
|
||||
|
||||
if (mFeatures.hasEvents.enabled)
|
||||
{
|
||||
// MTLSharedEvent is only available since Metal 2.1
|
||||
|
||||
// GL_NV_fence
|
||||
mNativeExtensions.fenceNV = true;
|
||||
|
||||
// GL_OES_EGL_sync
|
||||
mNativeExtensions.eglSyncOES = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayMtl::initializeTextureCaps() const
|
||||
|
@ -707,6 +728,8 @@ void DisplayMtl::initializeFeatures()
|
|||
ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle,
|
||||
isMetal2_2 && supportsEitherGPUFamily(1, 2));
|
||||
|
||||
ANGLE_FEATURE_CONDITION((&mFeatures), hasEvents, isMetal2_1);
|
||||
|
||||
#if !TARGET_OS_MACCATALYST && (TARGET_OS_IOS || TARGET_OS_TV)
|
||||
// Base Vertex drawing is only supported since GPU family 3.
|
||||
ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw, supportsIOSGPUFamily(3));
|
||||
|
@ -932,4 +955,18 @@ bool DisplayMtl::isNVIDIA() const
|
|||
return angle::IsNVIDIA(mMetalDeviceVendorId);
|
||||
}
|
||||
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
mtl::AutoObjCObj<MTLSharedEventListener> DisplayMtl::getOrCreateSharedEventListener()
|
||||
{
|
||||
if (!mSharedEventListener)
|
||||
{
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
mSharedEventListener = [[[MTLSharedEventListener alloc] init] ANGLE_MTL_AUTORELEASE];
|
||||
}
|
||||
}
|
||||
return mSharedEventListener;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace rx
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// SyncMtl:
|
||||
// Defines the class interface for SyncMtl, implementing SyncImpl.
|
||||
//
|
||||
|
||||
#ifndef LIBANGLE_RENDERER_METAL_SYNCMTL_H_
|
||||
#define LIBANGLE_RENDERER_METAL_SYNCMTL_H_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "libANGLE/renderer/EGLSyncImpl.h"
|
||||
#include "libANGLE/renderer/FenceNVImpl.h"
|
||||
#include "libANGLE/renderer/SyncImpl.h"
|
||||
#include "libANGLE/renderer/metal/mtl_common.h"
|
||||
|
||||
namespace egl
|
||||
{
|
||||
class AttributeMap;
|
||||
}
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
||||
class ContextMtl;
|
||||
|
||||
namespace mtl
|
||||
{
|
||||
|
||||
// Common class to be used by both SyncImpl and EGLSyncImpl.
|
||||
// NOTE: SharedEvent is only declared on iOS 12.0+ or mac 10.14+
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
class Sync
|
||||
{
|
||||
public:
|
||||
Sync();
|
||||
~Sync();
|
||||
|
||||
void onDestroy();
|
||||
|
||||
angle::Result initialize(ContextMtl *contextMtl);
|
||||
|
||||
angle::Result set(ContextMtl *contextMtl, GLenum condition, GLbitfield flags);
|
||||
angle::Result clientWait(ContextMtl *contextMtl,
|
||||
bool flushCommands,
|
||||
uint64_t timeout,
|
||||
GLenum *outResult);
|
||||
void serverWait(ContextMtl *contextMtl);
|
||||
angle::Result getStatus(bool *signaled);
|
||||
|
||||
private:
|
||||
SharedEventRef mMetalSharedEvent;
|
||||
uint64_t mSetCounter = 0;
|
||||
|
||||
std::shared_ptr<std::condition_variable> mCv;
|
||||
std::shared_ptr<std::mutex> mLock;
|
||||
};
|
||||
#else // #if ANGLE_MTL_EVENT_AVAILABLE
|
||||
class Sync
|
||||
{
|
||||
public:
|
||||
void onDestroy() { UNREACHABLE(); }
|
||||
|
||||
angle::Result initialize(ContextMtl *context)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return angle::Result::Stop;
|
||||
}
|
||||
angle::Result set(ContextMtl *contextMtl, GLenum condition, GLbitfield flags)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return angle::Result::Stop;
|
||||
}
|
||||
angle::Result clientWait(ContextMtl *context,
|
||||
bool flushCommands,
|
||||
uint64_t timeout,
|
||||
GLenum *outResult)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return angle::Result::Stop;
|
||||
}
|
||||
void serverWait(ContextMtl *contextMtl) { UNREACHABLE(); }
|
||||
angle::Result getStatus(bool *signaled)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return angle::Result::Stop;
|
||||
}
|
||||
};
|
||||
#endif // #if ANGLE_MTL_EVENT_AVAILABLE
|
||||
} // namespace mtl
|
||||
|
||||
class FenceNVMtl : public FenceNVImpl
|
||||
{
|
||||
public:
|
||||
FenceNVMtl();
|
||||
~FenceNVMtl() override;
|
||||
|
||||
void onDestroy(const gl::Context *context) override;
|
||||
|
||||
angle::Result set(const gl::Context *context, GLenum condition) override;
|
||||
angle::Result test(const gl::Context *context, GLboolean *outFinished) override;
|
||||
angle::Result finish(const gl::Context *context) override;
|
||||
|
||||
private:
|
||||
mtl::Sync mSync;
|
||||
};
|
||||
|
||||
class SyncMtl : public SyncImpl
|
||||
{
|
||||
public:
|
||||
SyncMtl();
|
||||
~SyncMtl() override;
|
||||
|
||||
void onDestroy(const gl::Context *context) override;
|
||||
|
||||
angle::Result set(const gl::Context *context, GLenum condition, GLbitfield flags) override;
|
||||
angle::Result clientWait(const gl::Context *context,
|
||||
GLbitfield flags,
|
||||
GLuint64 timeout,
|
||||
GLenum *outResult) override;
|
||||
angle::Result serverWait(const gl::Context *context,
|
||||
GLbitfield flags,
|
||||
GLuint64 timeout) override;
|
||||
angle::Result getStatus(const gl::Context *context, GLint *outResult) override;
|
||||
|
||||
private:
|
||||
mtl::Sync mSync;
|
||||
};
|
||||
|
||||
class EGLSyncMtl final : public EGLSyncImpl
|
||||
{
|
||||
public:
|
||||
EGLSyncMtl(const egl::AttributeMap &attribs);
|
||||
~EGLSyncMtl() override;
|
||||
|
||||
void onDestroy(const egl::Display *display) override;
|
||||
|
||||
egl::Error initialize(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLenum type) override;
|
||||
egl::Error clientWait(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLint flags,
|
||||
EGLTime timeout,
|
||||
EGLint *outResult) override;
|
||||
egl::Error serverWait(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLint flags) override;
|
||||
egl::Error getStatus(const egl::Display *display, EGLint *outStatus) override;
|
||||
|
||||
egl::Error dupNativeFenceFD(const egl::Display *display, EGLint *result) const override;
|
||||
|
||||
private:
|
||||
mtl::Sync mSync;
|
||||
};
|
||||
|
||||
} // namespace rx
|
||||
|
||||
#endif
|
|
@ -0,0 +1,316 @@
|
|||
//
|
||||
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// SyncMtl:
|
||||
// Defines the class interface for SyncMtl, implementing SyncImpl.
|
||||
//
|
||||
|
||||
#include "libANGLE/renderer/metal/SyncMtl.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "libANGLE/Context.h"
|
||||
#include "libANGLE/Display.h"
|
||||
#include "libANGLE/renderer/metal/ContextMtl.h"
|
||||
#include "libANGLE/renderer/metal/DisplayMtl.h"
|
||||
|
||||
namespace rx
|
||||
{
|
||||
namespace mtl
|
||||
{
|
||||
// SharedEvent is only available on iOS 12.0+ or mac 10.14+
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
Sync::Sync() {}
|
||||
Sync::~Sync() {}
|
||||
|
||||
void Sync::onDestroy()
|
||||
{
|
||||
mMetalSharedEvent = nil;
|
||||
mCv = nullptr;
|
||||
mLock = nullptr;
|
||||
}
|
||||
|
||||
angle::Result Sync::initialize(ContextMtl *contextMtl)
|
||||
{
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
mMetalSharedEvent = [[contextMtl->getMetalDevice() newSharedEvent] ANGLE_MTL_AUTORELEASE];
|
||||
}
|
||||
|
||||
mSetCounter = mMetalSharedEvent.get().signaledValue;
|
||||
|
||||
mCv.reset(new std::condition_variable());
|
||||
mLock.reset(new std::mutex());
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result Sync::set(ContextMtl *contextMtl, GLenum condition, GLbitfield flags)
|
||||
{
|
||||
if (!mMetalSharedEvent)
|
||||
{
|
||||
ANGLE_TRY(initialize(contextMtl));
|
||||
}
|
||||
ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
|
||||
ASSERT(flags == 0);
|
||||
|
||||
mSetCounter++;
|
||||
contextMtl->queueEventSignal(mMetalSharedEvent, mSetCounter);
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
angle::Result Sync::clientWait(ContextMtl *contextMtl,
|
||||
bool flushCommands,
|
||||
uint64_t timeout,
|
||||
GLenum *outResult)
|
||||
{
|
||||
std::unique_lock<std::mutex> lg(*mLock);
|
||||
if (mMetalSharedEvent.get().signaledValue >= mSetCounter)
|
||||
{
|
||||
*outResult = GL_ALREADY_SIGNALED;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
if (flushCommands)
|
||||
{
|
||||
contextMtl->flushCommandBufer();
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
*outResult = GL_TIMEOUT_EXPIRED;
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
// Create references to mutex and condition variable since they might be released in
|
||||
// onDestroy(), but the callback might still not be fired yet.
|
||||
std::shared_ptr<std::condition_variable> cvRef = mCv;
|
||||
std::shared_ptr<std::mutex> lockRef = mLock;
|
||||
|
||||
AutoObjCObj<MTLSharedEventListener> eventListener =
|
||||
contextMtl->getDisplay()->getOrCreateSharedEventListener();
|
||||
[mMetalSharedEvent.get() notifyListener:eventListener
|
||||
atValue:mSetCounter
|
||||
block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
|
||||
std::unique_lock<std::mutex> lg(*lockRef);
|
||||
cvRef->notify_one();
|
||||
}];
|
||||
|
||||
if (!mCv->wait_for(lg, std::chrono::nanoseconds(timeout),
|
||||
[this] { return mMetalSharedEvent.get().signaledValue >= mSetCounter; }))
|
||||
{
|
||||
*outResult = GL_TIMEOUT_EXPIRED;
|
||||
return angle::Result::Incomplete;
|
||||
}
|
||||
|
||||
ASSERT(mMetalSharedEvent.get().signaledValue >= mSetCounter);
|
||||
*outResult = GL_CONDITION_SATISFIED;
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
void Sync::serverWait(ContextMtl *contextMtl)
|
||||
{
|
||||
contextMtl->serverWaitEvent(mMetalSharedEvent, mSetCounter);
|
||||
}
|
||||
angle::Result Sync::getStatus(bool *signaled)
|
||||
{
|
||||
*signaled = mMetalSharedEvent.get().signaledValue >= mSetCounter;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
#endif // #if ANGLE_MTL_EVENT_AVAILABLE
|
||||
} // namespace mtl
|
||||
|
||||
// FenceNVMtl implementation
|
||||
FenceNVMtl::FenceNVMtl() : FenceNVImpl() {}
|
||||
|
||||
FenceNVMtl::~FenceNVMtl() {}
|
||||
|
||||
void FenceNVMtl::onDestroy(const gl::Context *context)
|
||||
{
|
||||
mSync.onDestroy();
|
||||
}
|
||||
|
||||
angle::Result FenceNVMtl::set(const gl::Context *context, GLenum condition)
|
||||
{
|
||||
ASSERT(condition == GL_ALL_COMPLETED_NV);
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
return mSync.set(contextMtl, GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
angle::Result FenceNVMtl::test(const gl::Context *context, GLboolean *outFinished)
|
||||
{
|
||||
bool signaled = false;
|
||||
ANGLE_TRY(mSync.getStatus(&signaled));
|
||||
|
||||
*outFinished = signaled ? GL_TRUE : GL_FALSE;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result FenceNVMtl::finish(const gl::Context *context)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
uint64_t timeout = 1000000000ul;
|
||||
GLenum result;
|
||||
do
|
||||
{
|
||||
ANGLE_TRY(mSync.clientWait(contextMtl, true, timeout, &result));
|
||||
} while (result == GL_TIMEOUT_EXPIRED);
|
||||
|
||||
if (result == GL_WAIT_FAILED)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return angle::Result::Stop;
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
// SyncMtl implementation
|
||||
SyncMtl::SyncMtl() : SyncImpl() {}
|
||||
|
||||
SyncMtl::~SyncMtl() {}
|
||||
|
||||
void SyncMtl::onDestroy(const gl::Context *context)
|
||||
{
|
||||
mSync.onDestroy();
|
||||
}
|
||||
|
||||
angle::Result SyncMtl::set(const gl::Context *context, GLenum condition, GLbitfield flags)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
return mSync.set(contextMtl, condition, flags);
|
||||
}
|
||||
|
||||
angle::Result SyncMtl::clientWait(const gl::Context *context,
|
||||
GLbitfield flags,
|
||||
GLuint64 timeout,
|
||||
GLenum *outResult)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
|
||||
|
||||
bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
|
||||
|
||||
return mSync.clientWait(contextMtl, flush, timeout, outResult);
|
||||
}
|
||||
|
||||
angle::Result SyncMtl::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
|
||||
{
|
||||
ASSERT(flags == 0);
|
||||
ASSERT(timeout == GL_TIMEOUT_IGNORED);
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
mSync.serverWait(contextMtl);
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result SyncMtl::getStatus(const gl::Context *context, GLint *outResult)
|
||||
{
|
||||
bool signaled = false;
|
||||
ANGLE_TRY(mSync.getStatus(&signaled));
|
||||
|
||||
*outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
// EGLSyncMtl implementation
|
||||
EGLSyncMtl::EGLSyncMtl(const egl::AttributeMap &attribs) : EGLSyncImpl()
|
||||
{
|
||||
ASSERT(attribs.isEmpty());
|
||||
}
|
||||
|
||||
EGLSyncMtl::~EGLSyncMtl() {}
|
||||
|
||||
void EGLSyncMtl::onDestroy(const egl::Display *display)
|
||||
{
|
||||
mSync.onDestroy();
|
||||
}
|
||||
|
||||
egl::Error EGLSyncMtl::initialize(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLenum type)
|
||||
{
|
||||
ASSERT(type == EGL_SYNC_FENCE_KHR);
|
||||
ASSERT(context != nullptr);
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
if (IsError(mSync.set(contextMtl, GL_SYNC_GPU_COMMANDS_COMPLETE, 0)))
|
||||
{
|
||||
return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
|
||||
}
|
||||
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
egl::Error EGLSyncMtl::clientWait(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLint flags,
|
||||
EGLTime timeout,
|
||||
EGLint *outResult)
|
||||
{
|
||||
ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
|
||||
|
||||
bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
|
||||
GLenum result;
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
if (IsError(mSync.clientWait(contextMtl, flush, static_cast<uint64_t>(timeout), &result)))
|
||||
{
|
||||
return egl::Error(EGL_BAD_ALLOC);
|
||||
}
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
// fall through. EGL doesn't differentiate between event being already set, or set
|
||||
// before timeout.
|
||||
case GL_CONDITION_SATISFIED:
|
||||
*outResult = EGL_CONDITION_SATISFIED_KHR;
|
||||
return egl::NoError();
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
*outResult = EGL_TIMEOUT_EXPIRED_KHR;
|
||||
return egl::NoError();
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
*outResult = EGL_FALSE;
|
||||
return egl::Error(EGL_BAD_ALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
egl::Error EGLSyncMtl::serverWait(const egl::Display *display,
|
||||
const gl::Context *context,
|
||||
EGLint flags)
|
||||
{
|
||||
// Server wait requires a valid bound context.
|
||||
ASSERT(context);
|
||||
|
||||
// No flags are currently implemented.
|
||||
ASSERT(flags == 0);
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
mSync.serverWait(contextMtl);
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
egl::Error EGLSyncMtl::getStatus(const egl::Display *display, EGLint *outStatus)
|
||||
{
|
||||
bool signaled = false;
|
||||
if (IsError(mSync.getStatus(&signaled)))
|
||||
{
|
||||
return egl::Error(EGL_BAD_ALLOC);
|
||||
}
|
||||
|
||||
*outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
egl::Error EGLSyncMtl::dupNativeFenceFD(const egl::Display *display, EGLint *result) const
|
||||
{
|
||||
UNREACHABLE();
|
||||
return egl::EglBadDisplay();
|
||||
}
|
||||
|
||||
}
|
|
@ -113,6 +113,9 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
|
|||
void setReadDependency(const ResourceRef &resource);
|
||||
void setReadDependency(Resource *resourcePtr);
|
||||
|
||||
void queueEventSignal(const mtl::SharedEventRef &event, uint64_t value);
|
||||
void serverWaitEvent(const mtl::SharedEventRef &event, uint64_t value);
|
||||
|
||||
CommandQueue &cmdQueue() { return mCmdQueue; }
|
||||
|
||||
// Private use only
|
||||
|
@ -127,6 +130,10 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
|
|||
void commitImpl();
|
||||
void forceEndingCurrentEncoder();
|
||||
|
||||
void setPendingEvents();
|
||||
void setEventImpl(const mtl::SharedEventRef &event, uint64_t value);
|
||||
void waitEventImpl(const mtl::SharedEventRef &event, uint64_t value);
|
||||
|
||||
using ParentClass = WrappedObject<id<MTLCommandBuffer>>;
|
||||
|
||||
CommandQueue &mCmdQueue;
|
||||
|
@ -137,6 +144,8 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
|
|||
|
||||
mutable std::mutex mLock;
|
||||
|
||||
std::vector<std::pair<mtl::SharedEventRef, uint64_t>> mPendingSignalEvents;
|
||||
|
||||
bool mCommitted = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -596,6 +596,32 @@ void CommandBuffer::restart()
|
|||
ASSERT(metalCmdBuffer);
|
||||
}
|
||||
|
||||
void CommandBuffer::queueEventSignal(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lg(mLock);
|
||||
|
||||
ASSERT(readyImpl());
|
||||
|
||||
if (mActiveCommandEncoder && mActiveCommandEncoder->getType() == CommandEncoder::RENDER)
|
||||
{
|
||||
// We cannot set event when there is an active render pass, defer the setting until the
|
||||
// pass end.
|
||||
mPendingSignalEvents.push_back({event, value});
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventImpl(event, value);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandBuffer::serverWaitEvent(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lg(mLock);
|
||||
ASSERT(readyImpl());
|
||||
|
||||
waitEventImpl(event, value);
|
||||
}
|
||||
|
||||
/** private use only */
|
||||
void CommandBuffer::set(id<MTLCommandBuffer> metalBuffer)
|
||||
{
|
||||
|
@ -612,6 +638,9 @@ void CommandBuffer::invalidateActiveCommandEncoder(CommandEncoder *encoder)
|
|||
if (mActiveCommandEncoder == encoder)
|
||||
{
|
||||
mActiveCommandEncoder = nullptr;
|
||||
|
||||
// No active command encoder, we can safely encode event signalling now.
|
||||
setPendingEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,6 +671,9 @@ void CommandBuffer::commitImpl()
|
|||
// End the current encoder
|
||||
forceEndingCurrentEncoder();
|
||||
|
||||
// Encoding any pending event's signalling.
|
||||
setPendingEvents();
|
||||
|
||||
// Notify command queue
|
||||
mCmdQueue.onCommandBufferCommitted(get(), mQueueSerial);
|
||||
|
||||
|
@ -660,6 +692,47 @@ void CommandBuffer::forceEndingCurrentEncoder()
|
|||
}
|
||||
}
|
||||
|
||||
void CommandBuffer::setPendingEvents()
|
||||
{
|
||||
for (const std::pair<mtl::SharedEventRef, uint64_t> &eventEntry : mPendingSignalEvents)
|
||||
{
|
||||
setEventImpl(eventEntry.first, eventEntry.second);
|
||||
}
|
||||
mPendingSignalEvents.clear();
|
||||
}
|
||||
|
||||
void CommandBuffer::setEventImpl(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
ASSERT(!mActiveCommandEncoder || mActiveCommandEncoder->getType() != CommandEncoder::RENDER);
|
||||
// For non-render command encoder, we can safely end it, so that we can encode a signal
|
||||
// event.
|
||||
forceEndingCurrentEncoder();
|
||||
|
||||
[get() encodeSignalEvent:event value:value];
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
UNREACHABLE();
|
||||
#endif // #if ANGLE_MTL_EVENT_AVAILABLE
|
||||
}
|
||||
|
||||
void CommandBuffer::waitEventImpl(const mtl::SharedEventRef &event, uint64_t value)
|
||||
{
|
||||
#if ANGLE_MTL_EVENT_AVAILABLE
|
||||
ASSERT(!mActiveCommandEncoder || mActiveCommandEncoder->getType() != CommandEncoder::RENDER);
|
||||
|
||||
forceEndingCurrentEncoder();
|
||||
|
||||
// Encoding any pending event's signalling.
|
||||
setPendingEvents();
|
||||
|
||||
[get() encodeWaitForEvent:event value:value];
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
UNREACHABLE();
|
||||
#endif // #if ANGLE_MTL_EVENT_AVAILABLE
|
||||
}
|
||||
|
||||
// CommandEncoder implementation
|
||||
CommandEncoder::CommandEncoder(CommandBuffer *cmdBuffer, Type type)
|
||||
: mType(type), mCmdBuffer(*cmdBuffer)
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(TARGET_OS_MACCATALYST)
|
||||
# define TARGET_OS_MACCATALYST 0
|
||||
#endif
|
||||
|
||||
#define ANGLE_MTL_OBJC_SCOPE @autoreleasepool
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
|
@ -326,6 +330,15 @@ class AutoObjCPtr : public WrappedObject<T>
|
|||
template <typename T>
|
||||
using AutoObjCObj = AutoObjCPtr<T *>;
|
||||
|
||||
// NOTE: SharedEvent is only declared on iOS 12.0+ or mac 10.14+
|
||||
#if defined(__IPHONE_12_0) || defined(__MAC_10_14)
|
||||
# define ANGLE_MTL_EVENT_AVAILABLE 1
|
||||
using SharedEventRef = AutoObjCPtr<id<MTLSharedEvent>>;
|
||||
#else
|
||||
# define ANGLE_MTL_EVENT_AVAILABLE 0
|
||||
using SharedEventRef = AutoObjCObj<NSObject>;
|
||||
#endif
|
||||
|
||||
struct ClearOptions
|
||||
{
|
||||
Optional<MTLClearColor> clearColor;
|
||||
|
|
|
@ -482,6 +482,7 @@ TEST_P(EGLSyncTest, AndroidNativeFence_withFences)
|
|||
ANGLE_INSTANTIATE_TEST(EGLSyncTest,
|
||||
ES2_D3D9(),
|
||||
ES2_D3D11(),
|
||||
ES2_METAL(),
|
||||
ES3_D3D11(),
|
||||
ES2_OPENGL(),
|
||||
ES3_OPENGL(),
|
||||
|
|
|
@ -262,4 +262,4 @@ TEST_P(FenceSyncTest, BasicOperations)
|
|||
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
|
||||
// tests should be run against.
|
||||
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(FenceNVTest);
|
||||
ANGLE_INSTANTIATE_TEST_ES3(FenceSyncTest);
|
||||
ANGLE_INSTANTIATE_TEST_ES3_AND(FenceSyncTest, ES3_METAL());
|
||||
|
|
Загрузка…
Ссылка в новой задаче