[graphite] Add command buffer submission and tracking

Bug: skia:12466
Change-Id: I965417fc1de471af33a31155abf2760d5d1b4f62
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457317
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Jim Van Verth 2021-10-12 15:22:34 -04:00 committed by SkCQ
parent 73065f325f
commit dc6a9e3e12
13 changed files with 182 additions and 17 deletions

View File

@ -8,15 +8,22 @@
#ifndef skgpu_CommandBuffer_DEFINED
#define skgpu_CommandBuffer_DEFINED
namespace skgpu {
#include "include/core/SkRefCnt.h"
class CommandBuffer {
namespace skgpu {
class Gpu;
class CommandBuffer : public SkRefCnt {
public:
virtual ~CommandBuffer() {}
~CommandBuffer() override {}
bool hasWork() { return fHasWork; }
protected:
CommandBuffer();
bool fHasWork = false;
private:
};

View File

@ -8,16 +8,70 @@
#include "experimental/graphite/src/Gpu.h"
#include "experimental/graphite/src/Caps.h"
#include "experimental/graphite/src/CommandBuffer.h"
#include "experimental/graphite/src/GpuWorkSubmission.h"
#include "experimental/graphite/src/ResourceProvider.h"
namespace skgpu {
// This constant determines how many OutstandingSubmissions are allocated together as a block in
// the deque. As such it needs to balance allocating too much memory vs. incurring
// allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding
// submissions we expect to see.
static constexpr int kDefaultOutstandingAllocCnt = 8;
Gpu::Gpu(sk_sp<const Caps> caps)
: fCaps(std::move(caps)) {
: fOutstandingSubmissions(sizeof(OutstandingSubmission), kDefaultOutstandingAllocCnt)
, fCaps(std::move(caps)) {
// subclasses create their own subclassed resource provider
}
Gpu::~Gpu() {
// TODO: add disconnect?
// TODO: destroyResources instead?
// TODO: how do we handle command buffers that haven't been submitted yet?
this->checkForFinishedWork(SyncToCpu::kYes);
fResourceProvider.reset();
}
bool Gpu::submit(sk_sp<CommandBuffer> commandBuffer) {
if (!commandBuffer) {
return false;
}
if (!commandBuffer->hasWork()) {
return true;
}
return this->onSubmit(std::move(commandBuffer));
}
void Gpu::checkForFinishedWork(SyncToCpu sync) {
if (sync == SyncToCpu::kYes) {
// wait for the last submission to finish
OutstandingSubmission* back = (OutstandingSubmission*)fOutstandingSubmissions.back();
if (back) {
(*back)->waitUntilFinished(this);
}
}
// Iterate over all the outstanding submissions to see if any have finished. The work
// submissions are in order from oldest to newest, so we start at the front to check if they
// have finished. If so we pop it off and move onto the next.
// Repeat till we find a submission that has not finished yet (and all others afterwards are
// also guaranteed to not have finished).
OutstandingSubmission* front = (OutstandingSubmission*)fOutstandingSubmissions.front();
while (front && (*front)->isFinished()) {
// Make sure we remove before deleting as deletion might try to kick off another submit
// (though hopefully *not* in Graphite).
fOutstandingSubmissions.pop_front();
// Since we used placement new we are responsible for calling the destructor manually.
front->~OutstandingSubmission();
front = (OutstandingSubmission*)fOutstandingSubmissions.front();
}
SkASSERT(sync == SyncToCpu::kNo || fOutstandingSubmissions.empty());
}
} // namespace skgpu

View File

@ -9,11 +9,14 @@
#define skgpu_Gpu_DEFINED
#include "include/core/SkRefCnt.h"
#include "include/private/SkDeque.h"
namespace skgpu {
class Caps;
class ResourceProvider;
class CommandBuffer;
class GpuWorkSubmission;
class Gpu : public SkRefCnt {
public:
@ -27,12 +30,27 @@ public:
ResourceProvider* resourceProvider() const { return fResourceProvider.get(); }
/**
* Submit command buffer to GPU and track completion
*/
enum class SyncToCpu : bool {
kYes = true,
kNo = false
};
bool submit(sk_sp<CommandBuffer>);
void checkForFinishedWork(SyncToCpu);
protected:
Gpu(sk_sp<const Caps>);
std::unique_ptr<ResourceProvider> fResourceProvider;
using OutstandingSubmission = std::unique_ptr<GpuWorkSubmission>;
SkDeque fOutstandingSubmissions;
private:
virtual bool onSubmit(sk_sp<CommandBuffer>) = 0;
sk_sp<const Caps> fCaps;
};

View File

@ -0,0 +1,29 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_GpuWorkSubmission_DEFINED
#define skgpu_GpuWorkSubmission_DEFINED
namespace skgpu {
class Gpu;
class GpuWorkSubmission {
public:
virtual ~GpuWorkSubmission() = default;
virtual bool isFinished() = 0;
virtual void waitUntilFinished(const Gpu*) = 0;
protected:
GpuWorkSubmission() = default;
private:
};
} // namespace skgpu
#endif // skgpu_GpuWorkSubmission_DEFINED

View File

@ -21,12 +21,6 @@ ResourceProvider::~ResourceProvider() {
fRenderPipelineCache.release();
}
std::unique_ptr<CommandBuffer> ResourceProvider::createCommandBuffer() {
// TODO: cache the commandbuffer in an active list and return raw pointer instead
return this->onCreateCommandBuffer();
}
RenderPipeline* ResourceProvider::findOrCreateRenderPipeline(const RenderPipelineDesc& desc) {
return fRenderPipelineCache->refPipeline(desc);
}

View File

@ -26,7 +26,8 @@ class ResourceProvider {
public:
virtual ~ResourceProvider();
std::unique_ptr<CommandBuffer> createCommandBuffer();
virtual sk_sp<CommandBuffer> createCommandBuffer() = 0;
RenderPipeline* findOrCreateRenderPipeline(const RenderPipelineDesc&);
sk_sp<Texture> findOrCreateTexture(SkISize, const TextureInfo&);
@ -37,7 +38,6 @@ protected:
const Gpu* fGpu;
private:
virtual std::unique_ptr<CommandBuffer> onCreateCommandBuffer() = 0;
virtual std::unique_ptr<RenderPipeline> onCreateRenderPipeline(const RenderPipelineDesc&) = 0;
virtual sk_sp<Texture> createTexture(SkISize, const TextureInfo&) = 0;

View File

@ -9,6 +9,7 @@
#define skgpu_MtlCommandBuffer_DEFINED
#include "experimental/graphite/src/CommandBuffer.h"
#include "experimental/graphite/src/GpuWorkSubmission.h"
#include <memory>
@ -22,9 +23,22 @@ class Gpu;
class CommandBuffer final : public skgpu::CommandBuffer {
public:
static std::unique_ptr<CommandBuffer> Make(const Gpu*);
static sk_sp<CommandBuffer> Make(const Gpu*);
~CommandBuffer() override {}
bool isFinished() {
return (*fCommandBuffer).status == MTLCommandBufferStatusCompleted ||
(*fCommandBuffer).status == MTLCommandBufferStatusError;
}
void waitUntilFinished() {
// TODO: it's not clear what do to if status is Enqueued. Commit and then wait?
if ((*fCommandBuffer).status == MTLCommandBufferStatusCommitted) {
[(*fCommandBuffer) waitUntilCompleted];
}
}
bool commit();
private:
CommandBuffer(sk_cfp<id<MTLCommandBuffer>> cmdBuffer)
: fCommandBuffer(std::move(cmdBuffer)) {}

View File

@ -11,7 +11,7 @@
namespace skgpu::mtl {
std::unique_ptr<CommandBuffer> CommandBuffer::Make(const Gpu* gpu) {
sk_sp<CommandBuffer> CommandBuffer::Make(const Gpu* gpu) {
sk_cfp<id<MTLCommandBuffer>> cmdBuffer;
id<MTLCommandQueue> queue = gpu->queue();
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
@ -34,7 +34,22 @@ std::unique_ptr<CommandBuffer> CommandBuffer::Make(const Gpu* gpu) {
(*cmdBuffer).label = @"CommandBuffer::Make";
#endif
return std::unique_ptr<CommandBuffer>(new CommandBuffer(std::move(cmdBuffer)));
return sk_sp<CommandBuffer>(new CommandBuffer(std::move(cmdBuffer)));
}
bool CommandBuffer::commit() {
// TODO: end any encoding
[(*fCommandBuffer) commit];
// TODO: better error reporting
if ((*fCommandBuffer).status == MTLCommandBufferStatusError) {
NSString* description = (*fCommandBuffer).error.localizedDescription;
const char* errorString = [description UTF8String];
SkDebugf("Error submitting command buffer: %s\n", errorString);
}
return ((*fCommandBuffer).status != MTLCommandBufferStatusError);
}
} // namespace skgpu::mtl

View File

@ -32,6 +32,8 @@ public:
private:
Gpu(sk_cfp<id<MTLDevice>>, sk_cfp<id<MTLCommandQueue>>, sk_sp<const Caps>);
bool onSubmit(sk_sp<skgpu::CommandBuffer>) override;
sk_cfp<id<MTLDevice>> fDevice;
sk_cfp<id<MTLCommandQueue>> fQueue;
};

View File

@ -8,6 +8,7 @@
#include "experimental/graphite/src/mtl/MtlGpu.h"
#include "experimental/graphite/src/Caps.h"
#include "experimental/graphite/src/mtl/MtlCommandBuffer.h"
#include "experimental/graphite/src/mtl/MtlResourceProvider.h"
namespace skgpu::mtl {
@ -31,4 +32,34 @@ Gpu::Gpu(sk_cfp<id<MTLDevice>> device, sk_cfp<id<MTLCommandQueue>> queue, sk_sp<
Gpu::~Gpu() {
}
class WorkSubmission final : public skgpu::GpuWorkSubmission {
public:
WorkSubmission(sk_sp<CommandBuffer> cmdBuffer)
: fCommandBuffer(std::move(cmdBuffer)) {}
~WorkSubmission() override {}
bool isFinished() override {
return fCommandBuffer->isFinished();
}
void waitUntilFinished(const skgpu::Gpu*) override {
return fCommandBuffer->waitUntilFinished();
}
private:
sk_sp<CommandBuffer> fCommandBuffer;
};
bool Gpu::onSubmit(sk_sp<skgpu::CommandBuffer> commandBuffer) {
SkASSERT(commandBuffer);
sk_sp<CommandBuffer>& mtlCmdBuffer = (sk_sp<CommandBuffer>&)(commandBuffer);
if (!mtlCmdBuffer->commit()) {
return false;
}
std::unique_ptr<WorkSubmission> submission(new WorkSubmission(mtlCmdBuffer));
new (fOutstandingSubmissions.push_back()) OutstandingSubmission(std::move(submission));
return true;
}
} // namespace skgpu::mtl

View File

@ -28,7 +28,7 @@ public:
private:
const Gpu* mtlGpu();
std::unique_ptr<skgpu::CommandBuffer> onCreateCommandBuffer() override;
sk_sp<skgpu::CommandBuffer> createCommandBuffer() override;
std::unique_ptr<skgpu::RenderPipeline> onCreateRenderPipeline(
const RenderPipelineDesc&) override;
sk_sp<skgpu::Texture> createTexture(SkISize, const skgpu::TextureInfo&) override;

View File

@ -24,7 +24,7 @@ const Gpu* ResourceProvider::mtlGpu() {
return static_cast<const Gpu*>(fGpu);
}
std::unique_ptr<skgpu::CommandBuffer> ResourceProvider::onCreateCommandBuffer() {
sk_sp<skgpu::CommandBuffer> ResourceProvider::createCommandBuffer() {
return CommandBuffer::Make(this->mtlGpu());
}

View File

@ -33,6 +33,7 @@ skia_graphite_sources = [
"$_src/DrawPass.h",
"$_src/Gpu.cpp",
"$_src/Gpu.h",
"$_src/GpuWorkSubmission.h",
"$_src/Image_Graphite.cpp",
"$_src/Image_Graphite.h",
"$_src/Recorder.cpp",