Update command buffer and finishProc processing in Metal
* Restructure GrMtlGpu::submitCommandBuffer() to handle empty command buffers better and ensure that finishedCallbacks are invoked. * Move finishedCallbacks from GrMtlGpu to GrMtlCommandBuffer to ensure they're really invoked on completion/deletion. Bug: skia:10530 Change-Id: I9f642342f03f540e46fad62a311c4195be87eab8 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/306936 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
ebae17d815
commit
f02c0489c9
@ -11,6 +11,7 @@
|
|||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
|
|
||||||
#include "include/core/SkRefCnt.h"
|
#include "include/core/SkRefCnt.h"
|
||||||
|
#include "include/gpu/GrTypes.h"
|
||||||
#include "src/gpu/GrBuffer.h"
|
#include "src/gpu/GrBuffer.h"
|
||||||
#include "src/gpu/mtl/GrMtlUtil.h"
|
#include "src/gpu/mtl/GrMtlUtil.h"
|
||||||
|
|
||||||
@ -23,7 +24,12 @@ public:
|
|||||||
static sk_sp<GrMtlCommandBuffer> Make(id<MTLCommandQueue> queue);
|
static sk_sp<GrMtlCommandBuffer> Make(id<MTLCommandQueue> queue);
|
||||||
~GrMtlCommandBuffer();
|
~GrMtlCommandBuffer();
|
||||||
|
|
||||||
void commit(bool waitUntilCompleted);
|
bool commit(bool waitUntilCompleted);
|
||||||
|
bool hasWork() { return fHasWork; }
|
||||||
|
|
||||||
|
void addFinishedCallback(sk_sp<GrRefCntedCallback> callback) {
|
||||||
|
fFinishedCallbacks.push_back(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
id<MTLBlitCommandEncoder> getBlitCommandEncoder();
|
id<MTLBlitCommandEncoder> getBlitCommandEncoder();
|
||||||
id<MTLRenderCommandEncoder> getRenderCommandEncoder(MTLRenderPassDescriptor*,
|
id<MTLRenderCommandEncoder> getRenderCommandEncoder(MTLRenderPassDescriptor*,
|
||||||
@ -41,12 +47,18 @@ public:
|
|||||||
void encodeSignalEvent(id<MTLEvent>, uint64_t value) SK_API_AVAILABLE(macos(10.14), ios(12.0));
|
void encodeSignalEvent(id<MTLEvent>, uint64_t value) SK_API_AVAILABLE(macos(10.14), ios(12.0));
|
||||||
void encodeWaitForEvent(id<MTLEvent>, uint64_t value) SK_API_AVAILABLE(macos(10.14), ios(12.0));
|
void encodeWaitForEvent(id<MTLEvent>, uint64_t value) SK_API_AVAILABLE(macos(10.14), ios(12.0));
|
||||||
|
|
||||||
|
void waitUntilCompleted() {
|
||||||
|
[fCmdBuffer waitUntilCompleted];
|
||||||
|
}
|
||||||
|
void callFinishedCallbacks() { fFinishedCallbacks.reset(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kInitialTrackedResourcesCount = 32;
|
static const int kInitialTrackedResourcesCount = 32;
|
||||||
|
|
||||||
GrMtlCommandBuffer(id<MTLCommandBuffer> cmdBuffer)
|
GrMtlCommandBuffer(id<MTLCommandBuffer> cmdBuffer)
|
||||||
: fCmdBuffer(cmdBuffer)
|
: fCmdBuffer(cmdBuffer)
|
||||||
, fPreviousRenderPassDescriptor(nil) {}
|
, fPreviousRenderPassDescriptor(nil)
|
||||||
|
, fHasWork(false) {}
|
||||||
|
|
||||||
void endAllEncoding();
|
void endAllEncoding();
|
||||||
|
|
||||||
@ -54,6 +66,9 @@ private:
|
|||||||
id<MTLBlitCommandEncoder> fActiveBlitCommandEncoder;
|
id<MTLBlitCommandEncoder> fActiveBlitCommandEncoder;
|
||||||
id<MTLRenderCommandEncoder> fActiveRenderCommandEncoder;
|
id<MTLRenderCommandEncoder> fActiveRenderCommandEncoder;
|
||||||
MTLRenderPassDescriptor* fPreviousRenderPassDescriptor;
|
MTLRenderPassDescriptor* fPreviousRenderPassDescriptor;
|
||||||
|
bool fHasWork;
|
||||||
|
|
||||||
|
SkTArray<sk_sp<GrRefCntedCallback>> fFinishedCallbacks;
|
||||||
|
|
||||||
SkSTArray<kInitialTrackedResourcesCount, sk_sp<const GrBuffer>> fTrackedGrBuffers;
|
SkSTArray<kInitialTrackedResourcesCount, sk_sp<const GrBuffer>> fTrackedGrBuffers;
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,8 @@ sk_sp<GrMtlCommandBuffer> GrMtlCommandBuffer::Make(id<MTLCommandQueue> queue) {
|
|||||||
GrMtlCommandBuffer::~GrMtlCommandBuffer() {
|
GrMtlCommandBuffer::~GrMtlCommandBuffer() {
|
||||||
this->endAllEncoding();
|
this->endAllEncoding();
|
||||||
fTrackedGrBuffers.reset();
|
fTrackedGrBuffers.reset();
|
||||||
|
this->callFinishedCallbacks();
|
||||||
|
|
||||||
fCmdBuffer = nil;
|
fCmdBuffer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ id<MTLBlitCommandEncoder> GrMtlCommandBuffer::getBlitCommandEncoder() {
|
|||||||
fActiveBlitCommandEncoder = [fCmdBuffer blitCommandEncoder];
|
fActiveBlitCommandEncoder = [fCmdBuffer blitCommandEncoder];
|
||||||
}
|
}
|
||||||
fPreviousRenderPassDescriptor = nil;
|
fPreviousRenderPassDescriptor = nil;
|
||||||
|
fHasWork = true;
|
||||||
|
|
||||||
return fActiveBlitCommandEncoder;
|
return fActiveBlitCommandEncoder;
|
||||||
}
|
}
|
||||||
@ -88,33 +91,34 @@ id<MTLRenderCommandEncoder> GrMtlCommandBuffer::getRenderCommandEncoder(
|
|||||||
opsRenderPass->initRenderState(fActiveRenderCommandEncoder);
|
opsRenderPass->initRenderState(fActiveRenderCommandEncoder);
|
||||||
}
|
}
|
||||||
fPreviousRenderPassDescriptor = descriptor;
|
fPreviousRenderPassDescriptor = descriptor;
|
||||||
|
fHasWork = true;
|
||||||
|
|
||||||
return fActiveRenderCommandEncoder;
|
return fActiveRenderCommandEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrMtlCommandBuffer::commit(bool waitUntilCompleted) {
|
bool GrMtlCommandBuffer::commit(bool waitUntilCompleted) {
|
||||||
this->endAllEncoding();
|
this->endAllEncoding();
|
||||||
[fCmdBuffer commit];
|
[fCmdBuffer commit];
|
||||||
if (waitUntilCompleted) {
|
if (waitUntilCompleted) {
|
||||||
[fCmdBuffer waitUntilCompleted];
|
this->waitUntilCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MTLCommandBufferStatusError == fCmdBuffer.status) {
|
if (fCmdBuffer.status == MTLCommandBufferStatusError) {
|
||||||
NSString* description = fCmdBuffer.error.localizedDescription;
|
NSString* description = fCmdBuffer.error.localizedDescription;
|
||||||
const char* errorString = [description UTF8String];
|
const char* errorString = [description UTF8String];
|
||||||
SkDebugf("Error submitting command buffer: %s\n", errorString);
|
SkDebugf("Error submitting command buffer: %s\n", errorString);
|
||||||
}
|
}
|
||||||
|
|
||||||
fCmdBuffer = nil;
|
return (fCmdBuffer.status != MTLCommandBufferStatusError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrMtlCommandBuffer::endAllEncoding() {
|
void GrMtlCommandBuffer::endAllEncoding() {
|
||||||
if (nil != fActiveRenderCommandEncoder) {
|
if (fActiveRenderCommandEncoder) {
|
||||||
[fActiveRenderCommandEncoder endEncoding];
|
[fActiveRenderCommandEncoder endEncoding];
|
||||||
fActiveRenderCommandEncoder = nil;
|
fActiveRenderCommandEncoder = nil;
|
||||||
fPreviousRenderPassDescriptor = nil;
|
fPreviousRenderPassDescriptor = nil;
|
||||||
}
|
}
|
||||||
if (nil != fActiveBlitCommandEncoder) {
|
if (fActiveBlitCommandEncoder) {
|
||||||
[fActiveBlitCommandEncoder endEncoding];
|
[fActiveBlitCommandEncoder endEncoding];
|
||||||
fActiveBlitCommandEncoder = nil;
|
fActiveBlitCommandEncoder = nil;
|
||||||
}
|
}
|
||||||
@ -126,6 +130,7 @@ void GrMtlCommandBuffer::encodeSignalEvent(id<MTLEvent> event, uint64_t eventVal
|
|||||||
if (@available(macOS 10.14, iOS 12.0, *)) {
|
if (@available(macOS 10.14, iOS 12.0, *)) {
|
||||||
[fCmdBuffer encodeSignalEvent:event value:eventValue];
|
[fCmdBuffer encodeSignalEvent:event value:eventValue];
|
||||||
}
|
}
|
||||||
|
fHasWork = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrMtlCommandBuffer::encodeWaitForEvent(id<MTLEvent> event, uint64_t eventValue) {
|
void GrMtlCommandBuffer::encodeWaitForEvent(id<MTLEvent> event, uint64_t eventValue) {
|
||||||
@ -135,5 +140,6 @@ void GrMtlCommandBuffer::encodeWaitForEvent(id<MTLEvent> event, uint64_t eventVa
|
|||||||
if (@available(macOS 10.14, iOS 12.0, *)) {
|
if (@available(macOS 10.14, iOS 12.0, *)) {
|
||||||
[fCmdBuffer encodeWaitForEvent:event value:eventValue];
|
[fCmdBuffer encodeWaitForEvent:event value:eventValue];
|
||||||
}
|
}
|
||||||
|
fHasWork = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,10 @@ public:
|
|||||||
|
|
||||||
GrMtlResourceProvider& resourceProvider() { return fResourceProvider; }
|
GrMtlResourceProvider& resourceProvider() { return fResourceProvider; }
|
||||||
|
|
||||||
GrMtlCommandBuffer* commandBuffer();
|
GrMtlCommandBuffer* commandBuffer() {
|
||||||
|
SkASSERT(fCurrentCmdBuffer);
|
||||||
|
return fCurrentCmdBuffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
enum SyncQueue {
|
enum SyncQueue {
|
||||||
kForce_SyncQueue,
|
kForce_SyncQueue,
|
||||||
@ -101,7 +104,7 @@ public:
|
|||||||
GrWrapOwnership ownership) override;
|
GrWrapOwnership ownership) override;
|
||||||
void insertSemaphore(GrSemaphore* semaphore) override;
|
void insertSemaphore(GrSemaphore* semaphore) override;
|
||||||
void waitSemaphore(GrSemaphore* semaphore) override;
|
void waitSemaphore(GrSemaphore* semaphore) override;
|
||||||
void checkFinishProcs() override;
|
void checkFinishProcs() override { this->checkForFinishedCommandBuffers(); }
|
||||||
std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
|
std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
|
||||||
|
|
||||||
// When the Metal backend actually uses indirect command buffers, this function will actually do
|
// When the Metal backend actually uses indirect command buffers, this function will actually do
|
||||||
@ -208,13 +211,14 @@ private:
|
|||||||
|
|
||||||
void addFinishedProc(GrGpuFinishedProc finishedProc,
|
void addFinishedProc(GrGpuFinishedProc finishedProc,
|
||||||
GrGpuFinishedContext finishedContext) override;
|
GrGpuFinishedContext finishedContext) override;
|
||||||
|
void addFinishedCallback(sk_sp<GrRefCntedCallback> finishedCallback);
|
||||||
|
|
||||||
bool onSubmitToGpu(bool syncCpu) override;
|
bool onSubmitToGpu(bool syncCpu) override;
|
||||||
|
|
||||||
// Commits the current command buffer to the queue and then creates a new command buffer. If
|
// Commits the current command buffer to the queue and then creates a new command buffer. If
|
||||||
// sync is set to kForce_SyncQueue, the function will wait for all work in the committed
|
// sync is set to kForce_SyncQueue, the function will wait for all work in the committed
|
||||||
// command buffer to finish before returning.
|
// command buffer to finish before returning.
|
||||||
void submitCommandBuffer(SyncQueue sync);
|
bool submitCommandBuffer(SyncQueue sync);
|
||||||
|
|
||||||
void checkForFinishedCommandBuffers();
|
void checkForFinishedCommandBuffers();
|
||||||
|
|
||||||
@ -268,8 +272,6 @@ private:
|
|||||||
|
|
||||||
bool fDisconnected;
|
bool fDisconnected;
|
||||||
|
|
||||||
GrFinishCallbacks fFinishCallbacks;
|
|
||||||
|
|
||||||
typedef GrGpu INHERITED;
|
typedef GrGpu INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,10 +126,10 @@ GrMtlGpu::GrMtlGpu(GrDirectContext* direct, const GrContextOptions& options,
|
|||||||
, fOutstandingCommandBuffers(sizeof(OutstandingCommandBuffer), kDefaultOutstandingAllocCnt)
|
, fOutstandingCommandBuffers(sizeof(OutstandingCommandBuffer), kDefaultOutstandingAllocCnt)
|
||||||
, fCompiler(new SkSL::Compiler())
|
, fCompiler(new SkSL::Compiler())
|
||||||
, fResourceProvider(this)
|
, fResourceProvider(this)
|
||||||
, fDisconnected(false)
|
, fDisconnected(false) {
|
||||||
, fFinishCallbacks(this) {
|
|
||||||
fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
|
fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
|
||||||
fCaps = fMtlCaps;
|
fCaps = fMtlCaps;
|
||||||
|
fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
GrMtlGpu::~GrMtlGpu() {
|
GrMtlGpu::~GrMtlGpu() {
|
||||||
@ -181,30 +181,44 @@ void GrMtlGpu::submit(GrOpsRenderPass* renderPass) {
|
|||||||
delete renderPass;
|
delete renderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrMtlCommandBuffer* GrMtlGpu::commandBuffer() {
|
bool GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
|
||||||
if (!fCurrentCmdBuffer) {
|
SkASSERT(fCurrentCmdBuffer);
|
||||||
fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue);
|
if (!fCurrentCmdBuffer->hasWork()) {
|
||||||
|
if (sync == SyncQueue::kForce_SyncQueue) {
|
||||||
// This should be done after we have a new command buffer in case the freeing of any
|
// wait for the last command buffer we've submitted to finish
|
||||||
// resources held by a finished command buffer causes us to send a new command to the gpu
|
OutstandingCommandBuffer* back =
|
||||||
// (like changing the resource state).
|
(OutstandingCommandBuffer*)fOutstandingCommandBuffers.back();
|
||||||
this->checkForFinishedCommandBuffers();
|
if (back) {
|
||||||
|
back->fCommandBuffer->waitUntilCompleted();
|
||||||
|
}
|
||||||
|
this->checkForFinishedCommandBuffers();
|
||||||
|
}
|
||||||
|
// We need to manually call the finishedCallbacks since we don't add this
|
||||||
|
// to the OutstandingCommandBuffer list
|
||||||
|
fCurrentCmdBuffer->callFinishedCallbacks();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return fCurrentCmdBuffer.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
|
fResourceProvider.addBufferCompletionHandler(fCurrentCmdBuffer.get());
|
||||||
// TODO: handle sync with empty command buffer
|
|
||||||
if (fCurrentCmdBuffer) {
|
|
||||||
fResourceProvider.addBufferCompletionHandler(fCurrentCmdBuffer.get());
|
|
||||||
|
|
||||||
GrFence fence = this->insertFence();
|
GrFence fence = this->insertFence();
|
||||||
new (fOutstandingCommandBuffers.push_back()) OutstandingCommandBuffer(
|
new (fOutstandingCommandBuffers.push_back()) OutstandingCommandBuffer(
|
||||||
fCurrentCmdBuffer, fence);
|
fCurrentCmdBuffer, fence);
|
||||||
|
|
||||||
fCurrentCmdBuffer->commit(SyncQueue::kForce_SyncQueue == sync);
|
if (!fCurrentCmdBuffer->commit(sync == SyncQueue::kForce_SyncQueue)) {
|
||||||
fCurrentCmdBuffer.reset();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new command buffer for the next submit
|
||||||
|
fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue);
|
||||||
|
|
||||||
|
// This should be done after we have a new command buffer in case the freeing of any
|
||||||
|
// resources held by a finished command buffer causes us to send a new command to the gpu
|
||||||
|
// (like changing the resource state).
|
||||||
|
this->checkForFinishedCommandBuffers();
|
||||||
|
|
||||||
|
SkASSERT(fCurrentCmdBuffer);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrMtlGpu::checkForFinishedCommandBuffers() {
|
void GrMtlGpu::checkForFinishedCommandBuffers() {
|
||||||
@ -225,21 +239,33 @@ void GrMtlGpu::checkForFinishedCommandBuffers() {
|
|||||||
|
|
||||||
void GrMtlGpu::addFinishedProc(GrGpuFinishedProc finishedProc,
|
void GrMtlGpu::addFinishedProc(GrGpuFinishedProc finishedProc,
|
||||||
GrGpuFinishedContext finishedContext) {
|
GrGpuFinishedContext finishedContext) {
|
||||||
fFinishCallbacks.add(finishedProc, finishedContext);
|
SkASSERT(finishedProc);
|
||||||
|
sk_sp<GrRefCntedCallback> finishedCallback(
|
||||||
|
new GrRefCntedCallback(finishedProc, finishedContext));
|
||||||
|
this->addFinishedCallback(std::move(finishedCallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrMtlGpu::addFinishedCallback(sk_sp<GrRefCntedCallback> finishedCallback) {
|
||||||
|
SkASSERT(finishedCallback);
|
||||||
|
// Besides the current commandbuffer, we also add the finishedCallback to the newest outstanding
|
||||||
|
// commandbuffer. Our contract for calling the proc is that all previous submitted cmdbuffers
|
||||||
|
// have finished when we call it. However, if our current command buffer has no work when it is
|
||||||
|
// flushed it will drop its ref to the callback immediately. But the previous work may not have
|
||||||
|
// finished. It is safe to only add the proc to the newest outstanding commandbuffer cause that
|
||||||
|
// must finish after all previously submitted command buffers.
|
||||||
|
OutstandingCommandBuffer* back = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.back();
|
||||||
|
if (back) {
|
||||||
|
back->fCommandBuffer->addFinishedCallback(finishedCallback);
|
||||||
|
}
|
||||||
|
fCurrentCmdBuffer->addFinishedCallback(std::move(finishedCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GrMtlGpu::onSubmitToGpu(bool syncCpu) {
|
bool GrMtlGpu::onSubmitToGpu(bool syncCpu) {
|
||||||
if (syncCpu) {
|
if (syncCpu) {
|
||||||
this->submitCommandBuffer(kForce_SyncQueue);
|
return this->submitCommandBuffer(kForce_SyncQueue);
|
||||||
fFinishCallbacks.callAll(true);
|
|
||||||
} else {
|
} else {
|
||||||
this->submitCommandBuffer(kSkip_SyncQueue);
|
return this->submitCommandBuffer(kSkip_SyncQueue);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrMtlGpu::checkFinishProcs() {
|
|
||||||
fFinishCallbacks.check();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GrSemaphore> GrMtlGpu::prepareTextureForCrossContextUsage(GrTexture*) {
|
std::unique_ptr<GrSemaphore> GrMtlGpu::prepareTextureForCrossContextUsage(GrTexture*) {
|
||||||
@ -1002,6 +1028,11 @@ bool GrMtlGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
|
|||||||
[transferBuffer didModifyRange: NSMakeRange(0, transferBufferSize)];
|
[transferBuffer didModifyRange: NSMakeRange(0, transferBufferSize)];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO: Add this when we switch over to using the main cmdbuffer
|
||||||
|
// if (finishedCallback) {
|
||||||
|
// this->addFinishedCallback(std::move(finishedCallback));
|
||||||
|
// }
|
||||||
|
|
||||||
[blitCmdEncoder endEncoding];
|
[blitCmdEncoder endEncoding];
|
||||||
[cmdBuffer commit];
|
[cmdBuffer commit];
|
||||||
transferBuffer = nil;
|
transferBuffer = nil;
|
||||||
|
Loading…
Reference in New Issue
Block a user