Add ability for an SkDevice to handle the drawing of an SkDrawable.

If supported, an Sk*Device can take charge of handling of an SkDrawable.
The specific use case right now will be to use this to execute Vulkan
specific SkDrawable's that need to know information about our Vulkan state
and objects at the time the SkDrawable is executed. If a device does not
support the SkDrawable we fall back to the cavans version like we did
previously.

BUG=skia:

Change-Id: I821fa600a80ff645412f296be36990ef390ae0a9
Reviewed-on: https://skia-review.googlesource.com/c/7740
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Greg Daniel 2018-10-18 12:43:25 -04:00 committed by Skia Commit-Bot
parent eb772c0869
commit 9ed1a2cd71
9 changed files with 158 additions and 4 deletions

View File

@ -8,6 +8,7 @@ _src = get_path_info("../src", "abspath")
_include = get_path_info("../include", "abspath")
skia_gpu_sources = [
"$_include/gpu/GrBackendDrawableInfo.h",
"$_include/gpu/GrBackendSemaphore.h",
"$_include/gpu/GrBackendSurface.h",
"$_include/gpu/GrBlend.h",

View File

@ -11,9 +11,11 @@
#include "SkFlattenable.h"
#include "SkScalar.h"
class GrBackendDrawableInfo;
class SkCanvas;
class SkMatrix;
class SkPicture;
enum class GrBackendApi : unsigned;
struct SkRect;
/**
@ -25,8 +27,6 @@ struct SkRect;
*/
class SK_API SkDrawable : public SkFlattenable {
public:
SkDrawable();
/**
* Draws into the specified content. The drawing sequence will be balanced upon return
* (i.e. the saveLevel() on the canvas will match what it was when draw() was called,
@ -35,6 +35,53 @@ public:
void draw(SkCanvas*, const SkMatrix* = nullptr);
void draw(SkCanvas*, SkScalar x, SkScalar y);
/**
* When using the GPU backend it is possible for a drawable to execute using the underlying 3D
* API rather than the SkCanvas API. It does so by creating a GpuDrawHandler. The GPU backend
* is deferred so the handler will be given access to the 3D API at the correct point in the
* drawing stream as the GPU backend flushes. Since the drawable may mutate, each time it is
* drawn to a GPU-backed canvas a new handler is snapped, representing the drawable's state at
* the time of the snap.
*
* When the GPU backend flushes to the 3D API it will call the draw method on the
* GpuDrawHandler. At this time the drawable may add commands to the stream of GPU commands for
* the unerlying 3D API. The draw function takes a GrBackendDrawableInfo which contains
* information about the current state of 3D API which the caller must respect. See
* GrBackendDrawableInfo for more specific details on what information is sent and the
* requirements for different 3D APIs.
*
* Additionaly there may be a slight delay from when the drawable adds its commands to when
* those commands are actually submitted to the GPU. Thus the drawable or GpuDrawHandler is
* required to keep any resources that are used by its added commands alive and valid until
* those commands are submitted to the GPU. The GpuDrawHandler will be kept alive and then
* deleted once the commands are submitted to the GPU. The dtor of the GpuDrawHandler is the
* signal to the drawable that the commands have all been submitted. Different 3D APIs may have
* additional requirements for certain resources which require waiting for the GPU to finish
* all work on those resources before reusing or deleting them. In this case, the drawable can
* use the dtor call of the GpuDrawHandler to add a fence to the GPU to track when the GPU work
* has completed.
*
* Currently this is only supported for the GPU Vulkan backend.
*/
class GpuDrawHandler {
public:
virtual ~GpuDrawHandler() {}
virtual void draw(const GrBackendDrawableInfo&) {}
};
/**
* Snaps off a GpuDrawHandler to represent the state of the SkDrawable at the time the snap is
* called. This is used for executing GPU backend specific draws intermixed with normal Skia GPU
* draws. The GPU API, which will be used for the draw, as well as the full matrix are passed in
* as inputs.
*/
std::unique_ptr<GpuDrawHandler> snapGpuDrawHandler(GrBackendApi backendApi,
const SkMatrix& matrix) {
return this->onSnapGpuDrawHandler(backendApi, matrix);
}
SkPicture* newPictureSnapshot();
/**
@ -78,9 +125,15 @@ public:
Factory getFactory() const override { return nullptr; }
protected:
SkDrawable();
virtual SkRect onGetBounds() = 0;
virtual void onDraw(SkCanvas*) = 0;
virtual std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi, const SkMatrix&) {
return nullptr;
}
/**
* Default implementation calls onDraw() with a canvas that records into a picture. Subclasses
* may override if they have a more efficient way to return a picture for the current state

View File

@ -0,0 +1,44 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrBackendDrawableInfo_DEFINED
#define GrBackendDrawableInfo_DEFINED
#include "GrTypes.h"
#include "vk/GrVkTypes.h"
class SK_API GrBackendDrawableInfo {
public:
// Creates an invalid backend drawable info.
GrBackendDrawableInfo() : fIsValid(false) {}
GrBackendDrawableInfo(const GrVkDrawableInfo& info)
: fIsValid(true)
, fBackend(GrBackendApi::kVulkan)
, fVkInfo(info) {}
// Returns true if the backend texture has been initialized.
bool isValid() const { return fIsValid; }
GrBackendApi backend() const { return fBackend; }
bool getVkDrawableInfo(GrVkDrawableInfo* outInfo) const {
if (this->isValid() && GrBackendApi::kVulkan == fBackend) {
*outInfo = fVkInfo;
return true;
}
return false;
}
private:
bool fIsValid;
GrBackendApi fBackend;
GrVkDrawableInfo fVkInfo;
};
#endif

View File

@ -112,5 +112,34 @@ using GrVkGetProc = std::function<PFN_vkVoidFunction(
VkDevice // device or VK_NULL_HANDLE
)>;
/**
* This object is wrapped in a GrBackendDrawableInfo and passed in as an argument to
* drawBackendGpu() calls on an SkDrawable. The drawable will use this info to inject direct
* Vulkan calls into our stream of GPU draws.
*
* The SkDrawable is given a secondary VkCommandBuffer in which to record draws. The GPU backend
* will then execute that command buffer within a render pass it is using for its own draws. The
* drawable is also given the attachment of the color index, a compatible VkRenderPass, and the
* VkFormat of the color attachment so that it can make VkPipeline objects for the draws. The
* SkDrawable must not alter the state of the VkRenderpass or sub pass.
*
* Additionally, the SkDrawable may fill in the passed in fDrawBounds with the bounds of the draws
* that it submits to the command buffer. This will be used by the GPU backend for setting the
* bounds in vkCmdBeginRenderPass. If fDrawBounds is not updated, we will assume that the entire
* attachment may have been written to.
*
* The SkDrawable is always allowed to create its own command buffers and submit them to the queue
* to render offscreen textures which will be sampled in draws added to the passed in
* VkCommandBuffer. If this is done the SkDrawable is in charge of adding the required memory
* barriers to the queue for the sampled images since the Skia backend will not do this.
*/
struct GrVkDrawableInfo {
VkCommandBuffer fSecondaryCommandBuffer;
uint32_t fColorAttachmentIndex;
VkRenderPass fCompatibleRenderPass;
uint32_t fImageAttachmentIndex;
VkFormat fFormat;
VkRect2D* fDrawBounds;
};
#endif

View File

@ -15,7 +15,6 @@
#include "SkColorFilter.h"
#include "SkDraw.h"
#include "SkDrawLooper.h"
#include "SkDrawable.h"
#include "SkGlyphCache.h"
#include "SkGlyphRun.h"
#include "SkImage.h"
@ -2643,7 +2642,7 @@ void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
// drawable bounds are no longer reliable (e.g. android displaylist)
// so don't use them for quick-reject
dr->draw(this, matrix);
this->getDevice()->drawDrawable(dr, matrix, this);
}
void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],

View File

@ -9,6 +9,7 @@
#include "SkColorFilter.h"
#include "SkDraw.h"
#include "SkDrawable.h"
#include "SkGlyphRun.h"
#include "SkImageFilter.h"
#include "SkImageFilterCache.h"
@ -275,6 +276,12 @@ void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[],
///////////////////////////////////////////////////////////////////////////////////////////////////
void SkBaseDevice::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix, SkCanvas* canvas) {
drawable->draw(canvas, matrix);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage*, const SkMatrix&) {}
sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkBitmap&) { return nullptr; }

View File

@ -237,6 +237,8 @@ protected:
void drawGlyphRunRSXform(SkGlyphRun* run, const SkRSXform* xform);
virtual void drawDrawable(SkDrawable*, const SkMatrix*, SkCanvas*);
virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage* clipImage, const SkMatrix& clipMatrix);
virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&);

View File

@ -1693,6 +1693,23 @@ void SkGpuDevice::drawGlyphRunList(const SkGlyphRunList& glyphRunList) {
///////////////////////////////////////////////////////////////////////////////
void SkGpuDevice::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix, SkCanvas* canvas) {
GrBackendApi api = this->context()->contextPriv().getBackend();
if (GrBackendApi::kVulkan == api) {
const SkMatrix& ctm = canvas->getTotalMatrix();
const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm;
std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw =
drawable->snapGpuDrawHandler(api, combinedMatrix);
if (gpuDraw) {
// TODO: send the gpuDraw to the renderTargetContext and make an Op out of it
// return;
}
}
this->INHERITED::drawDrawable(drawable, matrix, canvas);
}
///////////////////////////////////////////////////////////////////////////////
void SkGpuDevice::flush() {
this->flushAndSignalSemaphores(0, nullptr);
}

View File

@ -108,6 +108,8 @@ public:
void drawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
SkBlendMode) override;
void drawDrawable(SkDrawable*, const SkMatrix*, SkCanvas* canvas) override;
void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint,
SkImage*, const SkMatrix&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;