Proposed SkCanvas API for preLoading textures to VRAM v2.0

This is an update to (Proposed SkCanvas API for preLoading textures to VRAM - https://codereview.chromium.org/192853002/). It takes into account in-person feedback on the initial proposal. The main feedback was to land this closer to where we will ultimately wind up with the reordered rendering capability (and don't have an SkCanvas entry point (yet)).

R=reed@google.com, bsalomon@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/197123003

git-svn-id: http://skia.googlecode.com/svn/trunk@13810 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-03-14 18:23:12 +00:00
parent 118252962f
commit 1ab85c8719
11 changed files with 181 additions and 15 deletions

View File

@ -962,6 +962,14 @@ public:
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint);
/** PRIVATE / EXPERIMENTAL -- do not call
Perform back-end analysis/optimization of a picture. This may attach
optimization data to the picture which can be used by a later
drawPicture call.
@param picture The recorded drawing commands to analyze/optimize
*/
void EXPERIMENTAL_optimize(SkPicture* picture);
/** Draw the picture into this canvas. This method effective brackets the
playback of the picture's draw calls with save/restore, so the state
of this canvas will be unchanged after this call.

View File

@ -423,6 +423,24 @@ protected:
*/
SkDeviceProperties fLeakyProperties;
/**
* PRIVATE / EXPERIMENTAL -- do not call
* Construct an acceleration object and attach it to 'picture'
*/
virtual void EXPERIMENTAL_optimize(SkPicture* picture);
/**
* PRIVATE / EXPERIMENTAL -- do not call
* This entry point gives the backend an opportunity to take over the rendering
* of 'picture'. If optimization data is available (due to an earlier
* 'optimize' call) this entry point should make use of it and return true
* if all rendering has been done. If false is returned, SkCanvas will
* perform its own rendering pass. It is acceptable for the backend
* to perform some device-specific warm up tasks and then let SkCanvas
* perform the main rendering loop (by return false from here).
*/
virtual bool EXPERIMENTAL_drawPicture(const SkPicture& picture);
private:
friend class SkCanvas;
friend struct DeviceCM; //for setMatrixClip

View File

@ -34,6 +34,27 @@ class SK_API SkPicture : public SkRefCnt {
public:
SK_DECLARE_INST_COUNT(SkPicture)
// AccelData provides a base class for device-specific acceleration
// data. It is added to the picture via a call to a device's optimize
// method.
class AccelData : public SkRefCnt {
public:
typedef uint8_t Domain;
typedef uint32_t Key;
AccelData(Key key) : fKey(key) { }
const Key& getKey() const { return fKey; }
// This entry point allows user's to get a unique domain prefix
// for their keys
static Domain GenerateDomain();
private:
Key fKey;
typedef SkRefCnt INHERITED;
};
/** The constructor prepares the picture to record.
@param width the width of the virtual device the picture records.
@param height the height of the virtual device the picture records.
@ -44,6 +65,18 @@ public:
*/
SkPicture(const SkPicture& src);
/** PRIVATE / EXPERIMENTAL -- do not call */
void EXPERIMENTAL_addAccelData(const AccelData* data) {
SkRefCnt_SafeAssign(fAccelData, data);
}
/** PRIVATE / EXPERIMENTAL -- do not call */
const AccelData* EXPERIMENTAL_getAccelData(AccelData::Key key) const {
if (NULL != fAccelData && fAccelData->getKey() == key) {
return fAccelData;
}
return NULL;
}
/**
* Function signature defining a function that sets up an SkBitmap from encoded data. On
* success, the SkBitmap should have its Config, width, height, rowBytes and pixelref set.
@ -262,9 +295,10 @@ protected:
// fPlayback, fRecord, fWidth & fHeight are protected to allow derived classes to
// install their own SkPicturePlayback-derived players,SkPictureRecord-derived
// recorders and set the picture size
SkPicturePlayback* fPlayback;
SkPictureRecord* fRecord;
int fWidth, fHeight;
SkPicturePlayback* fPlayback;
SkPictureRecord* fRecord;
int fWidth, fHeight;
const AccelData* fAccelData;
// Create a new SkPicture from an existing SkPicturePlayback. Ref count of
// playback is unchanged.

View File

@ -14,6 +14,7 @@
#include "SkGr.h"
#include "SkBitmap.h"
#include "SkBitmapDevice.h"
#include "SkPicture.h"
#include "SkRegion.h"
#include "GrContext.h"
@ -146,10 +147,16 @@ public:
class SkAutoCachedTexture; // used internally
protected:
virtual bool onReadPixels(const SkBitmap&, int x, int y, SkCanvas::Config8888) SK_OVERRIDE;
virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
/** PRIVATE / EXPERIMENTAL -- do not call */
virtual void EXPERIMENTAL_optimize(SkPicture* picture) SK_OVERRIDE;
/** PRIVATE / EXPERIMENTAL -- do not call */
virtual bool EXPERIMENTAL_drawPicture(const SkPicture& picture) SK_OVERRIDE;
private:
GrContext* fContext;
@ -215,6 +222,8 @@ private:
int tileSize,
bool bicubic);
static SkPicture::AccelData::Key ComputeAccelDataKey();
typedef SkBitmapDevice INHERITED;
};

View File

@ -2461,8 +2461,23 @@ void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
}
///////////////////////////////////////////////////////////////////////////////
void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
SkBaseDevice* device = this->getDevice();
if (NULL != device) {
device->EXPERIMENTAL_optimize(picture);
}
}
void SkCanvas::drawPicture(SkPicture& picture) {
SkBaseDevice* device = this->getTopDevice();
if (NULL != device) {
// Canvas has to first give the device the opportunity to render
// the picture itself.
if (device->EXPERIMENTAL_drawPicture(picture)) {
return; // the device has rendered the entire picture
}
}
picture.draw(this);
}

View File

@ -213,3 +213,12 @@ void* SkBaseDevice::onAccessPixels(SkImageInfo* info, size_t* rowBytes) {
#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
void SkBaseDevice::writePixels(const SkBitmap&, int x, int y, SkCanvas::Config8888) {}
#endif
void SkBaseDevice::EXPERIMENTAL_optimize(SkPicture* picture) {
// The base class doesn't perform any analysis but derived classes may
}
bool SkBaseDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) {
// The base class doesn't perform any accelerated picture rendering
return false;
}

View File

@ -117,9 +117,12 @@ SkPicture::SkPicture() {
fRecord = NULL;
fPlayback = NULL;
fWidth = fHeight = 0;
fAccelData = NULL;
}
SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
SkPicture::SkPicture(const SkPicture& src)
: INHERITED()
, fAccelData(NULL) {
fWidth = src.fWidth;
fHeight = src.fHeight;
fRecord = NULL;
@ -141,6 +144,7 @@ SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
SkPicture::~SkPicture() {
SkSafeUnref(fRecord);
SkDELETE(fPlayback);
SkSafeUnref(fAccelData);
}
void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
@ -152,6 +156,7 @@ void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
void SkPicture::swap(SkPicture& other) {
SkTSwap(fRecord, other.fRecord);
SkTSwap(fPlayback, other.fPlayback);
SkTSwap(fAccelData, other.fAccelData);
SkTSwap(fWidth, other.fWidth);
SkTSwap(fHeight, other.fHeight);
}
@ -188,6 +193,17 @@ void SkPicture::clone(SkPicture* pictures, int count) const {
}
}
SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
static int32_t gNextID = 0;
int32_t id = sk_atomic_inc(&gNextID);
if (id >= 1 << (8 * sizeof(Domain))) {
SK_CRASH();
}
return static_cast<Domain>(id);
}
///////////////////////////////////////////////////////////////////////////////
SkCanvas* SkPicture::beginRecording(int width, int height,
@ -196,7 +212,7 @@ SkCanvas* SkPicture::beginRecording(int width, int height,
SkDELETE(fPlayback);
fPlayback = NULL;
}
SkSafeUnref(fAccelData);
SkSafeSetNull(fRecord);
// Must be set before calling createBBoxHierarchy
@ -250,7 +266,7 @@ void SkPicture::endRecording() {
void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
this->endRecording();
if (fPlayback) {
if (NULL != fPlayback) {
fPlayback->draw(*surface, callback);
}
}
@ -310,7 +326,8 @@ SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
: fPlayback(playback)
, fRecord(NULL)
, fWidth(width)
, fHeight(height) {}
, fHeight(height)
, fAccelData(NULL) {}
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
SkPictInfo info;
@ -418,7 +435,9 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const {
}
bool SkPicture::willPlayBackBitmaps() const {
if (!fPlayback) return false;
if (!fPlayback) {
return false;
}
return fPlayback->containsBitmaps();
}

View File

@ -25,6 +25,7 @@
#include "SkImageFilter.h"
#include "SkMaskFilter.h"
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkRRect.h"
#include "SkStroke.h"
#include "SkSurface.h"
@ -2003,3 +2004,44 @@ SkGpuDevice::SkGpuDevice(GrContext* context,
this->initFromRenderTarget(context, texture->asRenderTarget(), true);
fNeedClear = needClear;
}
class GPUAccelData : public SkPicture::AccelData {
public:
GPUAccelData(Key key) : INHERITED(key) { }
protected:
private:
typedef SkPicture::AccelData INHERITED;
};
// In the future this may not be a static method if we need to incorporate the
// clip and matrix state into the key
SkPicture::AccelData::Key SkGpuDevice::ComputeAccelDataKey() {
static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
return gGPUID;
}
void SkGpuDevice::EXPERIMENTAL_optimize(SkPicture* picture) {
SkPicture::AccelData::Key key = ComputeAccelDataKey();
GPUAccelData* data = SkNEW_ARGS(GPUAccelData, (key));
picture->EXPERIMENTAL_addAccelData(data);
}
bool SkGpuDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) {
SkPicture::AccelData::Key key = ComputeAccelDataKey();
const SkPicture::AccelData* data = picture.EXPERIMENTAL_getAccelData(key);
if (NULL == data) {
return false;
}
#if 0
const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
#endif
return false;
}

View File

@ -570,20 +570,19 @@ void TiledPictureRenderer::setupPowerOf2Tiles() {
}
/**
* Draw the specified playback to the canvas translated to rectangle provided, so that this mini
* Draw the specified picture to the canvas translated to rectangle provided, so that this mini
* canvas represents the rectangle's portion of the overall picture.
* Saves and restores so that the initial clip and matrix return to their state before this function
* is called.
*/
template<class T>
static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
int saveCount = canvas->save();
// Translate so that we draw the correct portion of the picture.
// Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
SkMatrix mat(canvas->getTotalMatrix());
mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
canvas->setMatrix(mat);
playback->draw(canvas);
canvas->drawPicture(*picture);
canvas->restoreToCount(saveCount);
canvas->flush();
}
@ -621,7 +620,7 @@ bool TiledPictureRenderer::nextTile(int &i, int &j) {
void TiledPictureRenderer::drawCurrentTile() {
SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
}
bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
@ -638,7 +637,7 @@ bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
}
bool success = true;
for (int i = 0; i < fTileRects.count(); ++i) {
DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
if (NULL != path) {
success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
}
@ -722,7 +721,7 @@ public:
}
for (int i = fStart; i < fEnd; i++) {
DrawTileToCanvas(fCanvas, fRects[i], fClone);
draw_tile_to_canvas(fCanvas, fRects[i], fClone);
if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
&& fSuccess != NULL) {
*fSuccess = false;

View File

@ -309,6 +309,10 @@ public:
}
}
SkCanvas* getCanvas() {
return fCanvas;
}
SkGLContextHelper* getGLContext() {
GrContextFactory::GLContextType glContextType
= GrContextFactory::kNull_GLContextType;

View File

@ -44,6 +44,8 @@ DEFINE_bool(validate, false, "Verify that the rendered image contains the same p
DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before rendering.");
static void make_output_filepath(SkString* path, const SkString& dir,
const SkString& name) {
sk_tools::make_filepath(path, dir, name);
@ -188,6 +190,13 @@ static bool render_picture_internal(const SkString& inputPath, const SkString* o
inputPath.c_str());
renderer.init(picture);
if (FLAGS_preprocess) {
if (NULL != renderer.getCanvas()) {
renderer.getCanvas()->EXPERIMENTAL_optimize(picture);
}
}
renderer.setup();
SkString* outputPath = NULL;