Create FenceSync implementation that works for all Metal configs
Bug: skia:8243 Change-Id: I609db0bf4183b3508c002f9f2d0a8a70a276edca Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217942 Commit-Queue: Jim Van Verth <jvanverth@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
parent
baf64786ba
commit
81b7e3df38
@ -17,79 +17,55 @@
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
namespace {
|
||||
#if GR_METAL_SDK_VERSION >= 200
|
||||
/**
|
||||
* Implements sk_gpu_test::FenceSync for Metal.
|
||||
*
|
||||
* Uses a single MTLSharedEvent, and inserts a GPU command to increment the value
|
||||
* each time we call insertFence(). On the CPU side we use a MTLSharedEventListener to
|
||||
* wait for the new value to be signaled on the GPU. Since the event listener is handled
|
||||
* on a separate thread, we communicate completion to the main thread via a semaphore.
|
||||
* Fences as MTLSharedEvents are not supported across all Metal platforms, so we do
|
||||
* the next best thing and submit an empty MTLCommandBuffer and track when it's complete.
|
||||
*/
|
||||
class MtlFenceSync : public sk_gpu_test::FenceSync {
|
||||
public:
|
||||
MtlFenceSync(id<MTLDevice> device, id<MTLCommandQueue> queue)
|
||||
: fDevice(device)
|
||||
, fQueue(queue)
|
||||
, fLatestEvent(0) {
|
||||
MtlFenceSync(id<MTLCommandQueue> queue)
|
||||
: fQueue(queue) {
|
||||
SkDEBUGCODE(fUnfinishedSyncs = 0;)
|
||||
fSharedEvent = [fDevice newSharedEvent];
|
||||
dispatch_queue_t dispatchQueue = dispatch_queue_create("MTLFenceSync", NULL);
|
||||
fSharedEventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:dispatchQueue];
|
||||
}
|
||||
|
||||
~MtlFenceSync() override {
|
||||
SkASSERT(!fUnfinishedSyncs);
|
||||
// If the above assertion is true then the command buffer should not be in flight.
|
||||
// ARC should take care of these:
|
||||
fSharedEventListener = nil;
|
||||
fSharedEvent = nil;
|
||||
}
|
||||
|
||||
sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override {
|
||||
id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
|
||||
++fLatestEvent;
|
||||
[cmdBuffer encodeSignalEvent:fSharedEvent value:fLatestEvent];
|
||||
cmdBuffer.label = @"Fence";
|
||||
[cmdBuffer commit];
|
||||
|
||||
SkDEBUGCODE(++fUnfinishedSyncs;)
|
||||
return (sk_gpu_test::PlatformFence)fLatestEvent;
|
||||
|
||||
void* cfCmdBuffer = (__bridge_retained void*)cmdBuffer;
|
||||
return (sk_gpu_test::PlatformFence)cfCmdBuffer;
|
||||
}
|
||||
|
||||
bool waitFence(sk_gpu_test::PlatformFence opaqueFence) const override {
|
||||
uint64_t value = (uint64_t)opaqueFence;
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
void* cfCmdBuffer = (void*) opaqueFence;
|
||||
id<MTLCommandBuffer> cmdBuffer = (__bridge id<MTLCommandBuffer>) cfCmdBuffer;
|
||||
|
||||
// Add listener for this particular value or greater
|
||||
__block dispatch_semaphore_t block_sema = semaphore;
|
||||
[fSharedEvent notifyListener: fSharedEventListener
|
||||
atValue: value
|
||||
block: ^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
|
||||
dispatch_semaphore_signal(block_sema);
|
||||
}];
|
||||
[cmdBuffer waitUntilCompleted];
|
||||
|
||||
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
return !result;
|
||||
return (MTLCommandBufferStatusError != cmdBuffer.status);
|
||||
}
|
||||
|
||||
void deleteFence(sk_gpu_test::PlatformFence opaqueFence) const override {
|
||||
// Nothing to delete
|
||||
CFRelease((void*) opaqueFence);
|
||||
SkDEBUGCODE(--fUnfinishedSyncs;)
|
||||
}
|
||||
|
||||
private:
|
||||
id<MTLDevice> fDevice;
|
||||
id<MTLCommandQueue> fQueue;
|
||||
id<MTLSharedEvent> fSharedEvent;
|
||||
MTLSharedEventListener* fSharedEventListener;
|
||||
mutable uint64_t fLatestEvent;
|
||||
SkDEBUGCODE(mutable int fUnfinishedSyncs;)
|
||||
typedef sk_gpu_test::FenceSync INHERITED;
|
||||
};
|
||||
|
||||
GR_STATIC_ASSERT(sizeof(uint64_t) <= sizeof(sk_gpu_test::PlatformFence));
|
||||
#endif
|
||||
|
||||
class MtlTestContextImpl : public sk_gpu_test::MtlTestContext {
|
||||
public:
|
||||
@ -112,7 +88,7 @@ public:
|
||||
|
||||
void testAbandon() override {}
|
||||
|
||||
// There is really nothing to here since we don't own any unqueued command buffers here.
|
||||
// There is really nothing to do here since we don't own any unqueued command buffers here.
|
||||
void submit() override {}
|
||||
|
||||
void finish() override {}
|
||||
@ -129,25 +105,8 @@ public:
|
||||
private:
|
||||
MtlTestContextImpl(id<MTLDevice> device, id<MTLCommandQueue> queue)
|
||||
: INHERITED(), fDevice(device), fQueue(queue) {
|
||||
#if GR_METAL_SDK_VERSION >= 200
|
||||
// TODO: I believe we can just check whether creating a MTLSharedEvent returns nil,
|
||||
// but this needs to be tested on an old OS.
|
||||
NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
bool supportsFenceSync = (osVersion.majorVersion > 10 ||
|
||||
(osVersion.majorVersion == 10 && osVersion.minorVersion >= 14));
|
||||
#else
|
||||
bool supportsFenceSync = (osVersion.majorVersion >= 12);
|
||||
#endif
|
||||
if (supportsFenceSync ) {
|
||||
fFenceSync.reset(new MtlFenceSync(device, queue));
|
||||
} else {
|
||||
fFenceSync.reset(nullptr);
|
||||
}
|
||||
#else
|
||||
fFenceSync.reset(nullptr);
|
||||
#endif
|
||||
}
|
||||
fFenceSync.reset(new MtlFenceSync(queue));
|
||||
}
|
||||
|
||||
void onPlatformMakeCurrent() const override {}
|
||||
std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; }
|
||||
|
Loading…
Reference in New Issue
Block a user