Remove separate cache for clip mask textures

Review URL: https://codereview.chromium.org/1377943003
This commit is contained in:
bsalomon 2015-10-02 07:49:05 -07:00 committed by Commit bot
parent 399dd05962
commit 473addf176
13 changed files with 149 additions and 592 deletions

View File

@ -78,8 +78,6 @@
'<(skia_src_path)/gpu/GrBufferAllocPool.h',
'<(skia_src_path)/gpu/GrCaps.cpp',
'<(skia_src_path)/gpu/GrClip.cpp',
'<(skia_src_path)/gpu/GrClipMaskCache.h',
'<(skia_src_path)/gpu/GrClipMaskCache.cpp',
'<(skia_src_path)/gpu/GrClipMaskManager.h',
'<(skia_src_path)/gpu/GrClipMaskManager.cpp',
'<(skia_src_path)/gpu/GrContext.cpp',

View File

@ -430,7 +430,6 @@ private:
void abandon();
bool abandoned() const { return NULL == fDrawTarget; }
void purgeResources();
void reset();
void flush();

View File

@ -1,19 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrClipMaskCache.h"
GrClipMaskCache::GrClipMaskCache(GrResourceProvider* resourceProvider)
: fStack(sizeof(GrClipStackFrame))
, fResourceProvider(resourceProvider) {
// We need an initial frame to capture the clip state prior to
// any pushes
new (fStack.push_back()) GrClipStackFrame;
}
void GrClipMaskCache::push() { new (fStack.push_back()) GrClipStackFrame; }

View File

@ -1,231 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrClipMaskCache_DEFINED
#define GrClipMaskCache_DEFINED
#include "GrResourceProvider.h"
#include "SkClipStack.h"
#include "SkTypes.h"
class GrTexture;
/**
* The stencil buffer stores the last clip path - providing a single entry
* "cache". This class provides similar functionality for AA clip paths
*/
class GrClipMaskCache : SkNoncopyable {
public:
GrClipMaskCache(GrResourceProvider*);
~GrClipMaskCache() {
while (!fStack.empty()) {
GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
temp->~GrClipStackFrame();
fStack.pop_back();
}
}
bool canReuse(int32_t clipGenID, const SkIRect& bounds) {
SkASSERT(clipGenID != SkClipStack::kWideOpenGenID);
SkASSERT(clipGenID != SkClipStack::kEmptyGenID);
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
// We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate
// an offset to the caller.
if (back->fLastMask &&
!back->fLastMask->wasDestroyed() &&
back->fLastBound == bounds &&
back->fLastClipGenID == clipGenID) {
return true;
}
return false;
}
void reset() {
if (fStack.empty()) {
// SkASSERT(false);
return;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
back->reset();
}
/**
* After a "push" the clip state is entirely open. Currently, the
* entire clip stack will be re-rendered into a new clip mask.
* TODO: can we take advantage of the nested nature of the clips to
* reduce the mask creation cost?
*/
void push();
void pop() {
//SkASSERT(!fStack.empty());
if (!fStack.empty()) {
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
back->~GrClipStackFrame();
fStack.pop_back();
}
}
int32_t getLastClipGenID() const {
if (fStack.empty()) {
return SkClipStack::kInvalidGenID;
}
return ((GrClipStackFrame*) fStack.back())->fLastClipGenID;
}
GrTexture* getLastMask() {
if (fStack.empty()) {
SkASSERT(false);
return nullptr;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
return back->fLastMask;
}
const GrTexture* getLastMask() const {
if (fStack.empty()) {
SkASSERT(false);
return nullptr;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
return back->fLastMask;
}
void acquireMask(int32_t clipGenID,
const GrSurfaceDesc& desc,
const SkIRect& bound) {
if (fStack.empty()) {
SkASSERT(false);
return;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
back->acquireMask(fResourceProvider, clipGenID, desc, bound);
}
int getLastMaskWidth() const {
if (fStack.empty()) {
SkASSERT(false);
return -1;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
if (nullptr == back->fLastMask) {
return -1;
}
return back->fLastMask->width();
}
int getLastMaskHeight() const {
if (fStack.empty()) {
SkASSERT(false);
return -1;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
if (nullptr == back->fLastMask) {
return -1;
}
return back->fLastMask->height();
}
void getLastBound(SkIRect* bound) const {
if (fStack.empty()) {
SkASSERT(false);
bound->setEmpty();
return;
}
GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
*bound = back->fLastBound;
}
// TODO: Remove this when we hold cache keys instead of refs to textures.
void purgeResources() {
SkDeque::F2BIter iter(fStack);
for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next();
frame != nullptr;
frame = (GrClipStackFrame*) iter.next()) {
frame->reset();
}
}
private:
struct GrClipStackFrame {
GrClipStackFrame() {
this->reset();
}
void acquireMask(GrResourceProvider* resourceProvider,
int32_t clipGenID,
const GrSurfaceDesc& desc,
const SkIRect& bound) {
fLastClipGenID = clipGenID;
// TODO: Determine if we really need the NoPendingIO flag anymore.
// (http://skbug.com/4156)
static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag;
fLastMask.reset(resourceProvider->createApproxTexture(desc, kFlags));
fLastBound = bound;
}
void reset () {
fLastClipGenID = SkClipStack::kInvalidGenID;
GrSurfaceDesc desc;
fLastMask.reset(nullptr);
fLastBound.setEmpty();
}
int32_t fLastClipGenID;
// The mask's width & height values are used by GrClipMaskManager to correctly scale the
// texture coords for the geometry drawn with this mask. TODO: This should be a cache key
// and not a hard ref to a texture.
SkAutoTUnref<GrTexture> fLastMask;
// fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is
// used by GrClipMaskManager to position a rect and compute texture coords for the mask.
SkIRect fLastBound;
};
SkDeque fStack;
GrResourceProvider* fResourceProvider;
typedef SkNoncopyable INHERITED;
};
#endif // GrClipMaskCache_DEFINED

View File

@ -9,10 +9,12 @@
#include "GrCaps.h"
#include "GrDrawContext.h"
#include "GrDrawTarget.h"
#include "GrGpuResourcePriv.h"
#include "GrPaint.h"
#include "GrPathRenderer.h"
#include "GrRenderTarget.h"
#include "GrRenderTargetPriv.h"
#include "GrResourceProvider.h"
#include "GrStencilAttachment.h"
#include "GrSWMaskHelper.h"
#include "SkRasterClip.h"
@ -78,7 +80,6 @@ bool path_needs_SW_renderer(GrContext* context,
GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget)
: fCurrClipMaskType(kNone_ClipMaskType)
, fAACache(drawTarget->cmmAccess().resourceProvider())
, fDrawTarget(drawTarget)
, fClipMode(kIgnoreClip_StencilClipMode) {
}
@ -344,13 +345,6 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
// if alpha clip mask creation fails fall through to the non-AA code paths
}
// Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
// be created. In either case, free up the texture in the anti-aliased mask cache.
// TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
// clears, GrBufferedDrawTarget playbacks) that hit the stencil buffer path. These may be
// "incorrectly" clearing the AA cache.
fAACache.reset();
// use the stencil clip if we can't represent the clip as a rectangle.
SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin();
this->createStencilClipMask(rt,
@ -516,62 +510,55 @@ GrTexture* GrClipMaskManager::createTempMask(int width, int height) {
}
////////////////////////////////////////////////////////////////////////////////
// Return the texture currently in the cache if it exists. Otherwise, return nullptr
GrTexture* GrClipMaskManager::getCachedMaskTexture(int32_t elementsGenID,
const SkIRect& clipSpaceIBounds) {
bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds);
if (!cached) {
// Create a 8-bit clip mask in alpha
static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(key, kDomain, 3);
builder[0] = clipGenID;
builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16);
builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16);
}
GrTexture* GrClipMaskManager::createCachedMask(int width, int height, const GrUniqueKey& key,
bool renderTarget) {
GrSurfaceDesc desc;
desc.fWidth = width;
desc.fHeight = height;
desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
if (!renderTarget || fDrawTarget->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
desc.fConfig = kAlpha_8_GrPixelConfig;
} else {
desc.fConfig = kRGBA_8888_GrPixelConfig;
}
GrTexture* texture = fDrawTarget->cmmAccess().resourceProvider()->createApproxTexture(desc, 0);
if (!texture) {
return nullptr;
}
return fAACache.getLastMask();
texture->resourcePriv().setUniqueKey(key);
return texture;
}
////////////////////////////////////////////////////////////////////////////////
// Allocate a texture in the texture cache. This function returns the texture
// allocated (or nullptr on error).
GrTexture* GrClipMaskManager::allocMaskTexture(int32_t elementsGenID,
const SkIRect& clipSpaceIBounds,
bool willUpload) {
// Since we are setting up the cache we should free up the
// currently cached mask so it can be reused.
fAACache.reset();
GrSurfaceDesc desc;
desc.fFlags = willUpload ? kNone_GrSurfaceFlags : kRenderTarget_GrSurfaceFlag;
desc.fWidth = clipSpaceIBounds.width();
desc.fHeight = clipSpaceIBounds.height();
desc.fConfig = kRGBA_8888_GrPixelConfig;
if (willUpload ||
this->getContext()->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
// We would always like A8 but it isn't supported on all platforms
desc.fConfig = kAlpha_8_GrPixelConfig;
}
fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds);
return fAACache.getLastMask();
}
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
GrReducedClip::InitialState initialState,
const GrReducedClip::ElementList& elements,
const SkVector& clipToMaskOffset,
const SkIRect& clipSpaceIBounds) {
SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
// First, check for cached texture
GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBounds);
if (result) {
GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider();
GrUniqueKey key;
GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
fCurrClipMaskType = kAlpha_ClipMaskType;
return result;
return texture;
}
SkAutoTUnref<GrTexture> texture(this->createCachedMask(
clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true));
// There's no texture in the cache. Let's try to allocate it then.
result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, false);
if (nullptr == result) {
fAACache.reset();
if (!texture) {
return nullptr;
}
@ -589,7 +576,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
fDrawTarget->clear(&maskSpaceIBounds,
GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
true,
result->asRenderTarget());
texture->asRenderTarget());
// When we use the stencil in the below loop it is important to have this clip installed.
// The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
@ -608,7 +595,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
pipelineBuilder.setClip(clip);
GrPathRenderer* pr = nullptr;
bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, result, &pr, element);
bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element);
GrTexture* dst;
// This is the bounds of the clip element in the space of the alpha-mask. The temporary
// mask buffer can be substantially larger than the actually clip stack element. We
@ -629,7 +616,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
temp.reset(this->createTempMask(maskSpaceIBounds.fRight,
maskSpaceIBounds.fBottom));
if (!temp) {
fAACache.reset();
texture->resourcePriv().removeUniqueKey();
return nullptr;
}
}
@ -643,7 +630,7 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
} else {
// draw directly into the result with the stencil set to make the pixels affected
// by the clip shape be non-zero.
dst = result;
dst = texture;
GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
kReplace_StencilOp,
kReplace_StencilOp,
@ -656,25 +643,25 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
}
if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) {
fAACache.reset();
texture->resourcePriv().removeUniqueKey();
return nullptr;
}
if (useTemp) {
GrPipelineBuilder backgroundPipelineBuilder;
backgroundPipelineBuilder.setRenderTarget(result->asRenderTarget());
backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());
// Now draw into the accumulator using the real operation and the temp buffer as a
// texture
this->mergeMask(&backgroundPipelineBuilder,
result,
texture,
temp,
op,
maskSpaceIBounds,
maskSpaceElementIBounds);
} else {
GrPipelineBuilder backgroundPipelineBuilder;
backgroundPipelineBuilder.setRenderTarget(result->asRenderTarget());
backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());
set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder);
// Draw to the exterior pixels (those with a zero stencil value).
@ -697,12 +684,12 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
// all the remaining ops can just be directly draw into the accumulation buffer
set_coverage_drawing_xpf(op, false, &pipelineBuilder);
// The color passed in here does not matter since the coverageSetOpXP won't read it.
this->drawElement(&pipelineBuilder, translate, result, element);
this->drawElement(&pipelineBuilder, translate, texture, element);
}
}
fCurrClipMaskType = kAlpha_ClipMaskType;
return result;
return texture.detach();
}
////////////////////////////////////////////////////////////////////////////////
@ -1079,10 +1066,11 @@ GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
const SkVector& clipToMaskOffset,
const SkIRect& clipSpaceIBounds) {
SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBounds);
if (result) {
return result;
GrUniqueKey key;
GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider();
if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
return texture;
}
// The mask texture may be larger than necessary. We round out the clip space bounds and pin
@ -1133,9 +1121,9 @@ GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
}
// Allocate clip mask texture
result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, true);
GrTexture* result = this->createCachedMask(clipSpaceIBounds.width(), clipSpaceIBounds.height(),
key, false);
if (nullptr == result) {
fAACache.reset();
return nullptr;
}
helper.toTexture(result);
@ -1145,9 +1133,6 @@ GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
}
////////////////////////////////////////////////////////////////////////////////
void GrClipMaskManager::purgeResources() {
fAACache.purgeResources();
}
void GrClipMaskManager::adjustPathStencilParams(const GrStencilAttachment* stencilAttachment,
GrStencilSettings* settings) {

View File

@ -7,7 +7,6 @@
#ifndef GrClipMaskManager_DEFINED
#define GrClipMaskManager_DEFINED
#include "GrClipMaskCache.h"
#include "GrPipelineBuilder.h"
#include "GrReducedClip.h"
#include "GrStencil.h"
@ -49,12 +48,6 @@ public:
GrScissorState*,
const SkRect* devBounds);
/**
* Purge resources to free up memory. TODO: This class shouldn't hold any long lived refs
* which will allow GrResourceCache to automatically purge anything this class has created.
*/
void purgeResources();
bool isClipInStencil() const {
return kStencil_ClipMaskType == fCurrClipMaskType;
}
@ -113,17 +106,7 @@ private:
const SkVector& clipToMaskOffset,
const SkIRect& clipSpaceIBounds);
// Returns the cached mask texture if it matches the elementsGenID and the clipSpaceIBounds.
// Returns nullptr if not found.
GrTexture* getCachedMaskTexture(int32_t elementsGenID, const SkIRect& clipSpaceIBounds);
// Handles allocation (if needed) of a clip alpha-mask texture for both the sw-upload
// or gpu-rendered cases.
GrTexture* allocMaskTexture(int32_t elementsGenID,
const SkIRect& clipSpaceIBounds,
bool willUpload);
bool useSWOnlyPath(const GrPipelineBuilder&,
bool useSWOnlyPath(const GrPipelineBuilder&,
const SkVector& clipToMaskOffset,
const GrReducedClip::ElementList& elements);
@ -153,8 +136,6 @@ private:
GrTexture* createTempMask(int width, int height);
void setupCache(const SkClipStack& clip,
const SkIRect& bounds);
/**
* Called prior to return control back the GrGpu in setupClipping. It updates the
* GrPipelineBuilder with stencil settings that account for stencil-based clipping.
@ -170,6 +151,8 @@ private:
StencilClipMode mode,
int stencilBitCnt);
GrTexture* createCachedMask(int width, int height, const GrUniqueKey& key, bool renderTarget);
/**
* We may represent the clip as a mask in the stencil buffer or as an alpha
* texture. It may be neither because the scissor rect suffices or we
@ -181,7 +164,6 @@ private:
kAlpha_ClipMaskType,
} fCurrClipMaskType;
GrClipMaskCache fAACache; // cache for the AA path
GrDrawTarget* fDrawTarget; // This is our owning draw target.
StencilClipMode fClipMode;

View File

@ -87,12 +87,6 @@ void GrContext::DrawingMgr::abandon() {
}
}
void GrContext::DrawingMgr::purgeResources() {
if (fDrawTarget) {
fDrawTarget->purgeResources();
}
}
void GrContext::DrawingMgr::reset() {
if (fDrawTarget) {
fDrawTarget->reset();
@ -243,8 +237,6 @@ void GrContext::resetContext(uint32_t state) {
void GrContext::freeGpuResources() {
this->flush();
fDrawingMgr.purgeResources();
fBatchFontCache->freeAll();
fLayerCache->freeAll();
// a path renderer may be holding onto resources

View File

@ -14,6 +14,7 @@
#include "GrPathRenderer.h"
#include "GrRenderTarget.h"
#include "GrRenderTargetPriv.h"
#include "GrResourceProvider.h"
#include "GrStencilAndCoverTextContext.h"
#include "batches/GrBatch.h"

View File

@ -20,6 +20,8 @@
#include "GrVertexBuffer.h"
#include "GrXferProcessor.h"
#include "batches/GrDrawBatch.h"
#include "SkClipStack.h"
#include "SkMatrix.h"
#include "SkPath.h"
@ -34,7 +36,6 @@ class GrBatch;
class GrClip;
class GrCaps;
class GrPath;
class GrDrawBatch;
class GrDrawPathBatchBase;
class GrPathRangeDraw;
@ -170,15 +171,6 @@ public:
GrSurface* src,
const SkIRect& srcRect,
const SkIPoint& dstPoint);
/**
* Release any resources that are cached but not currently in use. This
* is intended to give an application some recourse when resources are low.
* TODO: Stop holding on to resources.
*/
virtual void purgeResources() {
// The clip mask manager can rebuild all its clip masks so just get rid of them all.
fClipMaskManager->purgeResources();
};
bool programUnitTest(GrContext* owner, int maxStages);

View File

@ -90,6 +90,7 @@ public:
using GrTextureProvider::assignUniqueKeyToResource;
using GrTextureProvider::findAndRefResourceByUniqueKey;
using GrTextureProvider::findAndRefTextureByUniqueKey;
using GrTextureProvider::abandon;
enum Flags {
@ -142,6 +143,8 @@ public:
*/
GrStencilAttachment* attachStencilAttachment(GrRenderTarget* rt);
const GrCaps* caps() { return this->gpu()->caps(); }
private:
const GrIndexBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
int patternSize,

85
tests/ClipBoundsTest.cpp Normal file
View File

@ -0,0 +1,85 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
// This is a GR test
#if SK_SUPPORT_GPU
#include "GrClipMaskManager.h"
#include "GrContextFactory.h"
#include "SkGpuDevice.h"
// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
// to the render target
static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
static const int kXSize = 100;
static const int kYSize = 100;
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fConfig = kAlpha_8_GrPixelConfig;
desc.fWidth = kXSize;
desc.fHeight = kYSize;
SkAutoTUnref<GrTexture> texture(
context->textureProvider()->createTexture(desc, false, nullptr, 0));
if (!texture) {
return;
}
SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
SkRect screen = SkRect::Make(intScreen);
SkRect clipRect(screen);
clipRect.outset(10, 10);
// create a clip stack that will (trivially) reduce to a single rect that
// is larger than the screen
SkClipStack stack;
stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
bool isIntersectionOfRects = true;
SkRect devStackBounds;
stack.getConservativeBounds(0, 0, kXSize, kYSize,
&devStackBounds,
&isIntersectionOfRects);
// make sure that the SkClipStack is behaving itself
REPORTER_ASSERT(reporter, screen == devStackBounds);
REPORTER_ASSERT(reporter, isIntersectionOfRects);
// wrap the SkClipStack in a GrClip
GrClip clipData;
clipData.setClipStack(&stack);
SkIRect devGrClipBound;
clipData.getConservativeBounds(texture,
&devGrClipBound,
&isIntersectionOfRects);
// make sure that GrClip is behaving itself
REPORTER_ASSERT(reporter, intScreen == devGrClipBound);
REPORTER_ASSERT(reporter, isIntersectionOfRects);
}
DEF_GPUTEST(GrClipBounds, reporter, factory) {
for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
if (!GrContextFactory::IsRenderingGLContext(glType)) {
continue;
}
GrContext* context = factory->get(glType);
if (nullptr == context) {
continue;
}
test_clip_bounds(reporter, context);
}
}
#endif

View File

@ -1,231 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
// This is a GR test
#if SK_SUPPORT_GPU
#include "GrClipMaskManager.h"
#include "GrContextFactory.h"
#include "SkGpuDevice.h"
static const int X_SIZE = 12;
static const int Y_SIZE = 12;
////////////////////////////////////////////////////////////////////////////////
// note: this is unused
static GrTexture* create_texture(GrContext* context) {
unsigned char textureData[X_SIZE][Y_SIZE][4];
memset(textureData, 0, 4* X_SIZE * Y_SIZE);
GrSurfaceDesc desc;
// let Skia know we will be using this texture as a render target
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fConfig = kSkia8888_GrPixelConfig;
desc.fWidth = X_SIZE;
desc.fHeight = Y_SIZE;
// We are initializing the texture with zeros here
GrTexture* texture = context->textureProvider()->createTexture(desc, false, textureData, 0);
if (!texture) {
return nullptr;
}
return texture;
}
// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
// to the render target
static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
static const int kXSize = 100;
static const int kYSize = 100;
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fConfig = kAlpha_8_GrPixelConfig;
desc.fWidth = kXSize;
desc.fHeight = kYSize;
GrTexture* texture = context->textureProvider()->createTexture(desc, false, nullptr, 0);
if (!texture) {
return;
}
SkAutoTUnref<GrTexture> au(texture);
SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
SkRect screen;
screen = SkRect::MakeWH(SkIntToScalar(kXSize),
SkIntToScalar(kYSize));
SkRect clipRect(screen);
clipRect.outset(10, 10);
// create a clip stack that will (trivially) reduce to a single rect that
// is larger than the screen
SkClipStack stack;
stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
bool isIntersectionOfRects = true;
SkRect devStackBounds;
stack.getConservativeBounds(0, 0, kXSize, kYSize,
&devStackBounds,
&isIntersectionOfRects);
// make sure that the SkClipStack is behaving itself
REPORTER_ASSERT(reporter, screen == devStackBounds);
REPORTER_ASSERT(reporter, isIntersectionOfRects);
// wrap the SkClipStack in a GrClip
GrClip clipData;
clipData.setClipStack(&stack);
SkIRect devGrClipBound;
clipData.getConservativeBounds(texture,
&devGrClipBound,
&isIntersectionOfRects);
// make sure that GrClip is behaving itself
REPORTER_ASSERT(reporter, intScreen == devGrClipBound);
REPORTER_ASSERT(reporter, isIntersectionOfRects);
}
////////////////////////////////////////////////////////////////////////////////
// verify that the top state of the stack matches the passed in state
static void check_state(skiatest::Reporter* reporter,
const GrClipMaskCache& cache,
const SkClipStack& clip,
GrTexture* mask,
const SkIRect& bound) {
REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID());
REPORTER_ASSERT(reporter, mask == cache.getLastMask());
SkIRect cacheBound;
cache.getLastBound(&cacheBound);
REPORTER_ASSERT(reporter, bound == cacheBound);
}
static void check_empty_state(skiatest::Reporter* reporter,
const GrClipMaskCache& cache) {
REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID == cache.getLastClipGenID());
REPORTER_ASSERT(reporter, nullptr == cache.getLastMask());
SkIRect emptyBound;
emptyBound.setEmpty();
SkIRect cacheBound;
cache.getLastBound(&cacheBound);
REPORTER_ASSERT(reporter, emptyBound == cacheBound);
}
////////////////////////////////////////////////////////////////////////////////
// basic test of the cache's base functionality:
// push, pop, set, canReuse & getters
static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
if (false) { // avoid bit rot, suppress warning
create_texture(context);
}
GrClipMaskCache cache(context->resourceProvider());
// check initial state
check_empty_state(reporter, cache);
// set the current state
SkIRect bound1;
bound1.set(0, 0, 100, 100);
SkClipStack clip1(bound1);
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fWidth = X_SIZE;
desc.fHeight = Y_SIZE;
desc.fConfig = kSkia8888_GrPixelConfig;
cache.acquireMask(clip1.getTopmostGenID(), desc, bound1);
GrTexture* texture1 = cache.getLastMask();
REPORTER_ASSERT(reporter, texture1);
if (nullptr == texture1) {
return;
}
// check that the set took
check_state(reporter, cache, clip1, texture1, bound1);
// push the state
cache.push();
// verify that the pushed state is initially empty
check_empty_state(reporter, cache);
// modify the new state
SkIRect bound2;
bound2.set(-10, -10, 10, 10);
SkClipStack clip2(bound2);
cache.acquireMask(clip2.getTopmostGenID(), desc, bound2);
GrTexture* texture2 = cache.getLastMask();
REPORTER_ASSERT(reporter, texture2);
if (nullptr == texture2) {
return;
}
// check that the changes took
check_state(reporter, cache, clip2, texture2, bound2);
// check to make sure canReuse works
REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1));
// pop the state
cache.pop();
// verify that the old state is restored
check_state(reporter, cache, clip1, texture1, bound1);
// manually clear the state
cache.reset();
// verify it is now empty
check_empty_state(reporter, cache);
// pop again - so there is no state
cache.pop();
#if !defined(SK_DEBUG)
// verify that the getters don't crash
// only do in release since it generates asserts in debug
check_empty_state(reporter, cache);
#endif
}
DEF_GPUTEST(ClipCache, reporter, factory) {
for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
if (!GrContextFactory::IsRenderingGLContext(glType)) {
continue;
}
GrContext* context = factory->get(glType);
if (nullptr == context) {
continue;
}
test_cache(reporter, context);
test_clip_bounds(reporter, context);
}
}
#endif

View File

@ -18,6 +18,7 @@
#include "GrRenderTarget.h"
#include "GrRenderTargetPriv.h"
#include "GrResourceCache.h"
#include "GrResourceProvider.h"
#include "GrTest.h"
#include "SkCanvas.h"
#include "SkGr.h"