From afe3005be3392e43bc51eb7eb2017eefaed85ad1 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Fri, 16 Jan 2015 07:32:33 -0800 Subject: [PATCH] Require budget decision when creating a RenderTarget SkSurface. Restructure SkGpuDevice creation: *SkSurfaceProps are optional. *Use SkSurfaceProps to communicate DF text rather than a flag. *Tell SkGpuDevice::Create whether RT comes from cache or not. Review URL: https://codereview.chromium.org/848903004 --- bench/nanobench.cpp | 3 +- dm/DMGpuSupport.h | 3 +- expectations/gm/ignored-tests.txt | 3 + gm/dftext.cpp | 3 +- gm/discard.cpp | 2 +- gm/image.cpp | 3 +- gm/surface.cpp | 2 +- gm/xfermodes3.cpp | 19 ++---- include/core/SkSurface.h | 43 +++++++----- include/gpu/GrGpuResource.h | 1 + src/gpu/GrGpuResource.cpp | 8 ++- src/gpu/GrGpuResourceCacheAccess.h | 6 ++ src/gpu/GrResourceCache2.cpp | 4 ++ src/gpu/SkGpuDevice.cpp | 98 ++++++++++++++++------------ src/gpu/SkGpuDevice.h | 39 +++++------ src/image/SkImage_Gpu.cpp | 4 +- src/image/SkSurface.cpp | 7 +- src/image/SkSurface_Gpu.cpp | 101 +++++++++++------------------ src/image/SkSurface_Gpu.h | 2 +- tests/DeferredCanvasTest.cpp | 9 ++- tests/GpuDrawPathTest.cpp | 5 +- tests/ImageFilterTest.cpp | 20 +++--- tests/ImageIsOpaqueTest.cpp | 6 +- tests/ImageNewShaderTest.cpp | 12 ++-- tests/PremulAlphaRoundTripTest.cpp | 9 ++- tests/ReadWriteAlphaTest.cpp | 4 +- tests/ResourceCacheTest.cpp | 3 +- tests/SurfaceTest.cpp | 17 ++--- tools/PictureRenderer.cpp | 14 ++-- 29 files changed, 233 insertions(+), 217 deletions(-) diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index 12686e3a84..e2fcaaae91 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -388,7 +388,8 @@ static Target* is_enabled(Benchmark* bench, const Config& config) { else if (Benchmark::kGPU_Backend == config.backend) { uint32_t flags = config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); - target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType), info, + target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType), + SkSurface::kNo_Budgeted, info, config.samples, &props)); target->gl = gGrFactory->getGLContext(config.ctxType); } diff --git a/dm/DMGpuSupport.h b/dm/DMGpuSupport.h index a513d64bfd..335da08619 100644 --- a/dm/DMGpuSupport.h +++ b/dm/DMGpuSupport.h @@ -25,7 +25,8 @@ static inline SkSurface* NewGpuSurface(GrContextFactory* grFactory, bool useDFText) { uint32_t flags = useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); - return SkSurface::NewRenderTarget(grFactory->get(type, gpuAPI), info, samples, &props); + return SkSurface::NewRenderTarget(grFactory->get(type, gpuAPI), SkSurface::kNo_Budgeted, + info, samples, &props); } } // namespace DM diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 7bc03a30a5..d806aa4114 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -46,3 +46,6 @@ dropshadowimagefilter # fmalita - rebaseline for http://crbug.com/447707 pictureshader + +# bsalomon slight change to image-surface GM on gpu +image-surface diff --git a/gm/dftext.cpp b/gm/dftext.cpp index 36b8d5501c..8ac36cce4c 100755 --- a/gm/dftext.cpp +++ b/gm/dftext.cpp @@ -68,7 +68,8 @@ protected: SkImageInfo info = SkImageInfo::MakeN32Premul(onISize()); SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag, SkSurfaceProps::kLegacyFontHost_InitType); - SkAutoTUnref surface(SkSurface::NewRenderTarget(ctx, info, 0, &props)); + SkAutoTUnref surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, + info, 0, &props)); SkCanvas* canvas = surface.get() ? surface->getCanvas() : inputCanvas; // init our new canvas with the old canvas's matrix canvas->setMatrix(inputCanvas->getTotalMatrix()); diff --git a/gm/discard.cpp b/gm/discard.cpp index 58a08499c5..19a332fa15 100644 --- a/gm/discard.cpp +++ b/gm/discard.cpp @@ -46,7 +46,7 @@ protected: size.fWidth /= 10; size.fHeight /= 10; SkImageInfo info = SkImageInfo::MakeN32Premul(size); - SkSurface* surface = SkSurface::NewRenderTarget(context, info); + SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info); if (NULL == surface) { return; diff --git a/gm/image.cpp b/gm/image.cpp index d10152987f..2eaa15dfd3 100644 --- a/gm/image.cpp +++ b/gm/image.cpp @@ -177,7 +177,8 @@ protected: #if SK_SUPPORT_GPU GrContext* ctx = canvas->getGrContext(); - SkAutoTUnref surf4(SkSurface::NewRenderTarget(ctx, info)); + SkAutoTUnref surf4(SkSurface::NewRenderTarget( + ctx, SkSurface::kNo_Budgeted, info)); #endif test_surface(canvas, surf0, true); diff --git a/gm/surface.cpp b/gm/surface.cpp index 5961a336b8..5a4c09a54d 100644 --- a/gm/surface.cpp +++ b/gm/surface.cpp @@ -33,7 +33,7 @@ static SkSurface* make_surface(GrContext* ctx, const SkImageInfo& info, SkPixelG SkSurfaceProps props(flags, geo); if (ctx) { - return SkSurface::NewRenderTarget(ctx, info, 0, &props); + return SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info, 0, &props); } else { return SkSurface::NewRaster(info, &props); } diff --git a/gm/xfermodes3.cpp b/gm/xfermodes3.cpp index c385e41d3a..ffbee1c042 100644 --- a/gm/xfermodes3.cpp +++ b/gm/xfermodes3.cpp @@ -125,18 +125,13 @@ private: SkCanvas* tempCanvas = NULL; #if SK_SUPPORT_GPU GrContext* context = baseCanvas->getGrContext(); - if (context) { - GrSurfaceDesc desc; - desc.fWidth = w; - desc.fHeight = h; - desc.fConfig = SkImageInfo2GrPixelConfig(baseCanvas->imageInfo()); - desc.fFlags = kRenderTarget_GrSurfaceFlag; - SkAutoTUnref surface(context->createUncachedTexture(desc, NULL, 0)); - SkAutoTUnref device(SkGpuDevice::Create(surface.get(), - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))); - if (device.get()) { - tempCanvas = SkNEW_ARGS(SkCanvas, (device.get())); - } + SkImageInfo baseInfo = baseCanvas->imageInfo(); + SkImageInfo info = SkImageInfo::Make(w, h, baseInfo.colorType(), baseInfo.alphaType(), + baseInfo.profileType()); + SkAutoTUnref surface(SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, + info, 0, NULL)); + if (surface) { + tempCanvas = SkRef(surface->getCanvas()); } #endif return tempCanvas; diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index aa1e2cc2bb..1fd345a2fa 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -32,6 +32,18 @@ class SK_API SkSurface : public SkRefCnt { public: SK_DECLARE_INST_COUNT(SkSurface) + /** + * Indicates whether a new surface or image should count against a cache budget. Currently this + * is only used by the GPU backend (sw-raster surfaces and images are never counted against the + * resource cache budget.) + */ + enum Budgeted { + /** The surface or image does not count against the cache budget. */ + kNo_Budgeted, + /** The surface or image counts against the cache budget. */ + kYes_Budgeted + }; + /** * Create a new surface, using the specified pixels/rowbytes as its * backend. @@ -86,30 +98,27 @@ public: * Return a new surface whose contents will be drawn to an offscreen * render target, allocated by the surface. */ - static SkSurface* NewRenderTarget(GrContext*, const SkImageInfo&, int sampleCount, + static SkSurface* NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&, int sampleCount, const SkSurfaceProps* = NULL); - static SkSurface* NewRenderTarget(GrContext* gr, const SkImageInfo& info) { - return NewRenderTarget(gr, info, 0, NULL); + static SkSurface* NewRenderTarget(GrContext* gr, Budgeted b, const SkImageInfo& info) { + return NewRenderTarget(gr, b, info, 0, NULL); } /** - * Return a new surface whose contents will be drawn to an offscreen - * render target, allocated by the surface from the scratch texture pool - * managed by the GrContext. The scratch texture pool serves the purpose - * of retaining textures after they are no longer in use in order to - * re-use them later without having to re-allocate. Scratch textures - * should be used in cases where high turnover is expected. This allows, - * for example, the copy on write to recycle a texture from a recently - * released SkImage snapshot of the surface. - * Note: Scratch textures count against the GrContext's cached resource - * budget. + * Deprecated - use the Budgeted param on NewRenderTarget. */ - static SkSurface* NewScratchRenderTarget(GrContext*, const SkImageInfo&, int sampleCount, - const SkSurfaceProps* = NULL); + static SkSurface* NewScratchRenderTarget(GrContext* gr, const SkImageInfo& info, + int sampleCount, const SkSurfaceProps* props) { + return NewRenderTarget(gr, kYes_Budgeted, info, sampleCount, props); + } - static SkSurface* NewScratchRenderTarget(GrContext* gr, const SkImageInfo& info) { - return NewScratchRenderTarget(gr, info, 0, NULL); + /** + * Deprecated - use the version that takes a Budgeted param. + */ + static SkSurface* NewRenderTarget(GrContext* gr, const SkImageInfo& info, int sampleCount, + const SkSurfaceProps* props) { + return NewRenderTarget(gr, kNo_Budgeted, info, sampleCount, props); } int width() const { return fWidth; } diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index 44c4ba0082..750ec6a440 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -262,6 +262,7 @@ private: bool setContentKey(const GrResourceKey& contentKey); void notifyIsPurgable() const; void removeScratchKey(); + void makeBudgeted(); #ifdef SK_DEBUG friend class GrGpu; // for assert in GrGpu to access getGpu diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp index a2df7e13e8..2f267a4197 100644 --- a/src/gpu/GrGpuResource.cpp +++ b/src/gpu/GrGpuResource.cpp @@ -6,7 +6,6 @@ * found in the LICENSE file. */ - #include "GrGpuResource.h" #include "GrResourceCache2.h" #include "GrGpu.h" @@ -134,6 +133,13 @@ void GrGpuResource::removeScratchKey() { } } +void GrGpuResource::makeBudgeted() { + if (GrGpuResource::kUncached_LifeCycle == fLifeCycle) { + fLifeCycle = kCached_LifeCycle; + get_resource_cache2(fGpu)->resourceAccess().didChangeBudgetStatus(this); + } +} + uint32_t GrGpuResource::CreateUniqueID() { static int32_t gUniqueID = SK_InvalidUniqueID; uint32_t id; diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h index 0aadb89d97..d7c5028b20 100644 --- a/src/gpu/GrGpuResourceCacheAccess.h +++ b/src/gpu/GrGpuResourceCacheAccess.h @@ -69,6 +69,12 @@ public: */ bool isBudgeted() const { return GrGpuResource::kCached_LifeCycle == fResource->fLifeCycle; } + /** + * If the resource is uncached make it cached. Has no effect on resources that are wrapped or + * already cached. + */ + void makeBudgeted() { fResource->makeBudgeted(); } + /** * Called by the cache to delete the resource under normal circumstances. */ diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp index e23f9688ac..ba9d4d1016 100644 --- a/src/gpu/GrResourceCache2.cpp +++ b/src/gpu/GrResourceCache2.cpp @@ -306,6 +306,10 @@ void GrResourceCache2::didChangeBudgetStatus(GrGpuResource* resource) { if (resource->cacheAccess().isBudgeted()) { ++fBudgetedCount; fBudgetedBytes += size; +#if GR_CACHE_STATS + fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes); + fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount); +#endif this->purgeAsNeeded(); } else { --fBudgetedCount; diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 5f4713245e..e25cba5202 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -48,8 +48,6 @@ enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 }; -#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 - #if 0 extern bool (*gShouldDrawProc)(); #define CHECK_SHOULD_DRAW(draw) \ @@ -68,8 +66,8 @@ enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 }; #define DO_DEFERRED_CLEAR() \ do { \ - if (fFlags & kNeedClear_Flag) { \ - this->clearAll(); \ + if (fNeedClear) { \ + this->clearAll(); \ } \ } while (false) \ @@ -124,56 +122,71 @@ public: /////////////////////////////////////////////////////////////////////////////// -SkGpuDevice* SkGpuDevice::Create(GrSurface* surface, const SkSurfaceProps& props, unsigned flags) { - SkASSERT(surface); - if (NULL == surface->asRenderTarget() || surface->wasDestroyed()) { +SkGpuDevice* SkGpuDevice::Create(GrRenderTarget* rt, const SkSurfaceProps* props, unsigned flags) { + if (!rt || rt->wasDestroyed()) { return NULL; } - return SkNEW_ARGS(SkGpuDevice, (surface, props, flags)); + return SkNEW_ARGS(SkGpuDevice, (rt, props, flags)); } -static SkDeviceProperties surfaceprops_to_deviceprops(const SkSurfaceProps& props) { - return SkDeviceProperties(props.pixelGeometry()); +static SkDeviceProperties surfaceprops_to_deviceprops(const SkSurfaceProps* props) { + if (props) { + return SkDeviceProperties(props->pixelGeometry()); + } else { + return SkDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType); + } } -SkGpuDevice::SkGpuDevice(GrSurface* surface, const SkSurfaceProps& props, unsigned flags) +static SkSurfaceProps copy_or_default_props(const SkSurfaceProps* props) { + if (props) { + return SkSurfaceProps(*props); + } else { + return SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType); + } +} + +SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, const SkSurfaceProps* props, unsigned flags) : INHERITED(surfaceprops_to_deviceprops(props)) + , fSurfaceProps(copy_or_default_props(props)) { fDrawProcs = NULL; - fContext = SkRef(surface->getContext()); + fContext = SkRef(rt->getContext()); + fNeedClear = flags & kNeedClear_Flag; - fFlags = flags; + fRenderTarget = SkRef(rt); - fRenderTarget = SkRef(surface->asRenderTarget()); - - SkImageInfo info = surface->surfacePriv().info(); - SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface)); + SkImageInfo info = rt->surfacePriv().info(); + SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, rt)); fLegacyBitmap.setInfo(info); fLegacyBitmap.setPixelRef(pr)->unref(); - bool useDFT = SkToBool(flags & kDFText_Flag); + bool useDFT = fSurfaceProps.isUseDistanceFieldFonts(); fTextContext = fContext->createTextContext(fRenderTarget, this->getLeakyProperties(), useDFT); } -SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo, - const SkSurfaceProps& props, int sampleCount) { +SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgeted, + const SkImageInfo& origInfo, int sampleCount, + const SkSurfaceProps* props, unsigned flags) { if (kUnknown_SkColorType == origInfo.colorType() || origInfo.width() < 0 || origInfo.height() < 0) { return NULL; } + if (!context) { + return NULL; + } + SkColorType ct = origInfo.colorType(); SkAlphaType at = origInfo.alphaType(); - // TODO: perhaps we can loosen this check now that colortype is more detailed - // e.g. can we support both RGBA and BGRA here? if (kRGB_565_SkColorType == ct) { at = kOpaque_SkAlphaType; // force this setting - } else { + } else if (ct != kBGRA_8888_SkColorType && ct != kRGBA_8888_SkColorType) { + // Fall back from whatever ct was to default of kRGBA or kBGRA which is aliased as kN32 ct = kN32_SkColorType; - if (kOpaque_SkAlphaType != at) { - at = kPremul_SkAlphaType; // force this setting - } + } + if (kOpaque_SkAlphaType != at) { + at = kPremul_SkAlphaType; // force this setting } const SkImageInfo info = SkImageInfo::Make(origInfo.width(), origInfo.height(), ct, at); @@ -184,12 +197,18 @@ SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo desc.fConfig = SkImageInfo2GrPixelConfig(info); desc.fSampleCnt = sampleCount; - SkAutoTUnref texture(context->createUncachedTexture(desc, NULL, 0)); - if (!texture.get()) { + SkAutoTUnref texture; + if (SkSurface::kYes_Budgeted == budgeted) { + texture.reset(context->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch)); + } else { + texture.reset(context->createUncachedTexture(desc, NULL, 0)); + } + + if (!texture) { return NULL; } - return SkNEW_ARGS(SkGpuDevice, (texture.get(), props)); + return SkNEW_ARGS(SkGpuDevice, (texture->asRenderTarget(), props, flags)); } SkGpuDevice::~SkGpuDevice() { @@ -295,7 +314,7 @@ void SkGpuDevice::clearAll() { GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::clearAll", fContext); SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); fContext->clear(&rect, color, true, fRenderTarget); - fFlags &= ~kNeedClear_Flag; + fNeedClear = false; } /////////////////////////////////////////////////////////////////////////////// @@ -1517,7 +1536,7 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, // clear of the source device must occur before CHECK_SHOULD_DRAW GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawDevice", fContext); SkGpuDevice* dev = static_cast(device); - if (dev->fFlags & kNeedClear_Flag) { + if (fNeedClear) { // TODO: could check here whether we really need to draw at all dev->clearAll(); } @@ -1800,21 +1819,17 @@ SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) { SkAutoTUnref texture; // Skia's convention is to only clear a device if it is non-opaque. unsigned flags = cinfo.fInfo.isOpaque() ? 0 : kNeedClear_Flag; - // If we're using distance field text, enable in the new device - flags |= (fFlags & kDFText_Flag) ? kDFText_Flag : 0; -#if CACHE_COMPATIBLE_DEVICE_TEXTURES // layers are never draw in repeat modes, so we can request an approx // match and ignore any padding. const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == cinfo.fUsage) ? GrContext::kApprox_ScratchTexMatch : GrContext::kExact_ScratchTexMatch; texture.reset(fContext->refScratchTexture(desc, match)); -#else - texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); -#endif - if (texture.get()) { - return SkGpuDevice::Create(texture, SkSurfaceProps(0, cinfo.fPixelGeometry), flags); + + if (texture) { + SkSurfaceProps props(fSurfaceProps.flags(), cinfo.fPixelGeometry); + return SkGpuDevice::Create(texture->asRenderTarget(), &props, flags); } else { SkDebugf("---- failed to create compatible device texture [%d %d]\n", cinfo.fInfo.width(), cinfo.fInfo.height()); @@ -1823,7 +1838,10 @@ SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) { } SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) { - return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples(), &props); + // TODO: Change the signature of newSurface to take a budgeted parameter. + static const SkSurface::Budgeted kBudgeted = SkSurface::kNo_Budgeted; + return SkSurface::NewRenderTarget(fContext, kBudgeted, info, fRenderTarget->numSamples(), + &props); } bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture* mainPicture, diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 56dad1b475..6cbf628e58 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -6,8 +6,6 @@ * found in the LICENSE file. */ - - #ifndef SkGpuDevice_DEFINED #define SkGpuDevice_DEFINED @@ -16,6 +14,7 @@ #include "SkDevice.h" #include "SkPicture.h" #include "SkRegion.h" +#include "SkSurface.h" #include "GrContext.h" #include "GrSurfacePriv.h" @@ -34,25 +33,20 @@ class SK_API SkGpuDevice : public SkBaseDevice { public: enum Flags { kNeedClear_Flag = 1 << 0, //!< Surface requires an initial clear - kDFText_Flag = 1 << 1, //!< Surface should render text using signed distance fields }; /** - * Creates an SkGpuDevice from a GrSurface. This will fail if the surface is not a render - * target. The caller owns a ref on the returned device. If the surface is cached, - * the kCached_Flag should be specified to make the device responsible for unlocking - * the surface when it is released. + * Creates an SkGpuDevice from a GrRenderTarget. */ - static SkGpuDevice* Create(GrSurface* surface, const SkSurfaceProps&, unsigned flags = 0); + static SkGpuDevice* Create(GrRenderTarget* target, const SkSurfaceProps*, unsigned flags = 0); /** - * New device that will create an offscreen renderTarget based on the - * ImageInfo and sampleCount. The device's storage will not - * count against the GrContext's texture cache budget. The device's pixels - * will be uninitialized. On failure, returns NULL. + * New device that will create an offscreen renderTarget based on the ImageInfo and + * sampleCount. The Budgeted param controls whether the device's backing store counts against + * the resource cache budget. On failure, returns NULL. */ - static SkGpuDevice* Create(GrContext*, const SkImageInfo&, const SkSurfaceProps&, - int sampleCount); + static SkGpuDevice* Create(GrContext*, SkSurface::Budgeted, const SkImageInfo&, + int sampleCount, const SkSurfaceProps*, unsigned flags = 0); virtual ~SkGpuDevice(); @@ -63,7 +57,7 @@ public: return static_cast(dev); } - GrContext* context() const { return fContext; } + GrContext* context() const { return fRenderTarget->getContext(); } // set all pixels to 0 void clearAll(); @@ -74,6 +68,8 @@ public: return fRenderTarget ? fRenderTarget->surfacePriv().info() : SkImageInfo::MakeUnknown(); } + const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; } + void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE; virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint& paint) SK_OVERRIDE; @@ -139,21 +135,16 @@ protected: private: GrContext* fContext; - GrSkDrawProcs* fDrawProcs; - GrClipData fClipData; - GrTextContext* fTextContext; - - // state for our render-target + SkSurfaceProps fSurfaceProps; GrRenderTarget* fRenderTarget; - uint32_t fFlags; - // remove when our clients don't rely on accessBitmap() - SkBitmap fLegacyBitmap; + SkBitmap fLegacyBitmap; + bool fNeedClear; - SkGpuDevice(GrSurface*, const SkSurfaceProps&, unsigned flags = 0); + SkGpuDevice(GrRenderTarget*, const SkSurfaceProps*, unsigned flags); SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE; diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 6a42ece802..350a1e88de 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -69,7 +69,9 @@ void SkImage_Gpu::onDrawRect(SkCanvas* canvas, const SkRect* src, const SkRect& SkSurface* SkImage_Gpu::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const { GrContext* ctx = this->getTexture()->getContext(); - return SkSurface::NewRenderTarget(ctx, info, fSampleCountForNewSurfaces, &props); + // TODO: Change signature of onNewSurface to take a budgeted param. + static const SkSurface::Budgeted kBudgeted = SkSurface::kNo_Budgeted; + return SkSurface::NewRenderTarget(ctx, kBudgeted, info, fSampleCountForNewSurfaces, &props); } GrTexture* SkImage_Gpu::onGetTexture() const { diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index 6fd40c10af..81b348fda0 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -183,13 +183,10 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget*, const SkSurfaceProp return NULL; } -SkSurface* SkSurface::NewRenderTarget(GrContext*, const SkImageInfo&, int, const SkSurfaceProps*) { +SkSurface* SkSurface::NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&, int, + const SkSurfaceProps*) { return NULL; } -SkSurface* SkSurface::NewScratchRenderTarget(GrContext*, const SkImageInfo&, int sampleCount, - const SkSurfaceProps*) { - return NULL; -} #endif diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp index e58375b53b..6cd741d149 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -5,27 +5,22 @@ * found in the LICENSE file. */ -#include "SkSurface_Base.h" -#include "SkImagePriv.h" -#include "SkImage_Base.h" +#include "SkSurface_Gpu.h" + +#include "GrGpuResourceCacheAccess.h" #include "SkCanvas.h" #include "SkGpuDevice.h" -#include "SkSurface_Gpu.h" +#include "SkImage_Base.h" +#include "SkImagePriv.h" +#include "SkSurface_Base.h" #if SK_SUPPORT_GPU /////////////////////////////////////////////////////////////////////////////// -SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, const SkSurfaceProps* props, - bool doClear) - : INHERITED(renderTarget->width(), renderTarget->height(), props) { - int deviceFlags = 0; - deviceFlags |= this->props().isUseDistanceFieldFonts() ? SkGpuDevice::kDFText_Flag : 0; - fDevice = SkGpuDevice::Create(renderTarget, this->props(), deviceFlags); - - if (kRGB_565_GrPixelConfig != renderTarget->config() && doClear) { - fDevice->clearAll(); - } +SkSurface_Gpu::SkSurface_Gpu(SkGpuDevice* device) + : INHERITED(device->width(), device->height(), &device->surfaceProps()) + , fDevice(SkRef(device)) { } SkSurface_Gpu::~SkSurface_Gpu() { @@ -43,7 +38,10 @@ SkCanvas* SkSurface_Gpu::onNewCanvas() { SkSurface* SkSurface_Gpu::onNewSurface(const SkImageInfo& info) { GrRenderTarget* rt = fDevice->accessRenderTarget(); int sampleCount = rt->numSamples(); - return SkSurface::NewRenderTarget(fDevice->context(), info, sampleCount, &this->props()); + // TODO: Make caller specify this (change virtual signature of onNewSurface). + static const Budgeted kBudgeted = kNo_Budgeted; + return SkSurface::NewRenderTarget(fDevice->context(), kBudgeted, info, sampleCount, + &this->props()); } SkImage* SkSurface_Gpu::onNewImageSnapshot() { @@ -68,19 +66,26 @@ void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) { // are we sharing our render target with the image? SkASSERT(this->getCachedImage()); if (rt->asTexture() == SkTextureImageGetTexture(this->getCachedImage())) { - // We call createCompatibleDevice because it uses the texture cache. This isn't - // necessarily correct (http://skbug.com/2252), but never using the cache causes - // a Chromium regression. (http://crbug.com/344020) - SkGpuDevice* newDevice = fDevice->cloneDevice(this->props()); - SkAutoTUnref aurd(newDevice); - if (kRetain_ContentChangeMode == mode) { - fDevice->context()->copySurface(newDevice->accessRenderTarget(), rt->asTexture()); + GrRenderTarget* oldRT = this->fDevice->accessRenderTarget(); + SkSurface::Budgeted budgeted = oldRT->cacheAccess().isBudgeted() ? kYes_Budgeted : + kNo_Budgeted; + SkAutoTUnref newDevice( + SkGpuDevice::Create(oldRT->getContext(), budgeted, fDevice->imageInfo(), + oldRT->numSamples(), &this->props(), 0)); + if (kRetain_ContentChangeMode == mode && !oldRT->wasDestroyed() && newDevice) { + oldRT->getContext()->copySurface(newDevice->accessRenderTarget(), oldRT); } + SkASSERT(this->getCachedCanvas()); SkASSERT(this->getCachedCanvas()->getDevice() == fDevice); this->getCachedCanvas()->setRootDevice(newDevice); - SkRefCnt_SafeAssign(fDevice, newDevice); + SkRefCnt_SafeAssign(fDevice, newDevice.get()); + + // For now we always treat the image snapshots as budgeted. We could make newImageSnapshot + // take a Budgeted param. + oldRT->cacheAccess().makeBudgeted(); + } else if (kDiscard_ContentChangeMode == mode) { this->SkSurface_Gpu::onDiscard(); } @@ -93,53 +98,21 @@ void SkSurface_Gpu::onDiscard() { /////////////////////////////////////////////////////////////////////////////// SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurfaceProps* props) { - if (NULL == target) { + SkAutoTUnref device(SkGpuDevice::Create(target, props)); + if (!device) { return NULL; } - return SkNEW_ARGS(SkSurface_Gpu, (target, props, false)); + return SkNEW_ARGS(SkSurface_Gpu, (device)); } -SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount, - const SkSurfaceProps* props) { - if (NULL == ctx) { +SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, Budgeted budgeted, const SkImageInfo& info, + int sampleCount, const SkSurfaceProps* props) { + SkAutoTUnref device(SkGpuDevice::Create(ctx, budgeted, info, sampleCount, props, + SkGpuDevice::kNeedClear_Flag)); + if (!device) { return NULL; } - - GrSurfaceDesc desc; - desc.fFlags = kRenderTarget_GrSurfaceFlag | kCheckAllocation_GrSurfaceFlag; - desc.fWidth = info.width(); - desc.fHeight = info.height(); - desc.fConfig = SkImageInfo2GrPixelConfig(info); - desc.fSampleCnt = sampleCount; - - SkAutoTUnref tex(ctx->createUncachedTexture(desc, NULL, 0)); - if (NULL == tex) { - return NULL; - } - - return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), props, true)); -} - -SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo& info, - int sampleCount, const SkSurfaceProps* props) { - if (NULL == ctx) { - return NULL; - } - - GrSurfaceDesc desc; - desc.fFlags = kRenderTarget_GrSurfaceFlag | kCheckAllocation_GrSurfaceFlag; - desc.fWidth = info.width(); - desc.fHeight = info.height(); - desc.fConfig = SkImageInfo2GrPixelConfig(info); - desc.fSampleCnt = sampleCount; - - SkAutoTUnref tex(ctx->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch)); - - if (NULL == tex) { - return NULL; - } - - return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), props, true)); + return SkNEW_ARGS(SkSurface_Gpu, (device)); } #endif diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h index 4a49272d3a..f87c5d7842 100644 --- a/src/image/SkSurface_Gpu.h +++ b/src/image/SkSurface_Gpu.h @@ -18,7 +18,7 @@ class SkSurface_Gpu : public SkSurface_Base { public: SK_DECLARE_INST_COUNT(SkSurface_Gpu) - SkSurface_Gpu(GrRenderTarget*, const SkSurfaceProps*, bool doClear); + SkSurface_Gpu(SkGpuDevice*); virtual ~SkSurface_Gpu(); SkCanvas* onNewCanvas() SK_OVERRIDE; diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp index 4ec67ae1ba..3089dc0aad 100644 --- a/tests/DeferredCanvasTest.cpp +++ b/tests/DeferredCanvasTest.cpp @@ -698,7 +698,8 @@ static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFac return; } - surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); + surface = + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); } else #endif { @@ -780,8 +781,10 @@ static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContext if (NULL == context) { continue; } - surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); - alternateSurface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL); + surface = + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); + alternateSurface = + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, imageSpec, 0, NULL); } else #endif { diff --git a/tests/GpuDrawPathTest.cpp b/tests/GpuDrawPathTest.cpp index 3e47a05ba3..b86689fd4e 100644 --- a/tests/GpuDrawPathTest.cpp +++ b/tests/GpuDrawPathTest.cpp @@ -56,8 +56,9 @@ DEF_GPUTEST(GpuDrawPath, reporter, factory) { for (size_t i = 0; i < SK_ARRAY_COUNT(sampleCounts); ++i) { SkImageInfo info = SkImageInfo::MakeN32Premul(255, 255); - SkAutoTUnref surface(SkSurface::NewRenderTarget(grContext, info, - sampleCounts[i], NULL)); + SkAutoTUnref surface( + SkSurface::NewRenderTarget(grContext, SkSurface::kNo_Budgeted, info, + sampleCounts[i], NULL)); test_drawPathEmpty(reporter, surface->getCanvas()); } } diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index 4ffcc11209..55499d74c3 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -1074,36 +1074,40 @@ const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_Ini DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) { GrContext* context = factory->get(static_cast(0)); SkAutoTUnref device(SkGpuDevice::Create(context, + SkSurface::kNo_Budgeted, SkImageInfo::MakeN32Premul(100, 100), - gProps, - 0)); + 0, + &gProps)); test_crop_rects(device, reporter); } DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) { GrContext* context = factory->get(static_cast(0)); SkAutoTUnref device(SkGpuDevice::Create(context, + SkSurface::kNo_Budgeted, SkImageInfo::MakeN32Premul(100, 100), - gProps, - 0)); + 0, + &gProps)); test_huge_blur(device, reporter); } DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) { GrContext* context = factory->get(static_cast(0)); SkAutoTUnref device(SkGpuDevice::Create(context, + SkSurface::kNo_Budgeted, SkImageInfo::MakeN32Premul(1, 1), - gProps, - 0)); + 0, + &gProps)); test_xfermode_cropped_input(device, reporter); } DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) { GrContext* context = factory->get(static_cast(0)); SkAutoTUnref device(SkGpuDevice::Create(context, + SkSurface::kNo_Budgeted, SkImageInfo::MakeN32Premul(1, 1), - gProps, - 0)); + 0, + &gProps)); test_negative_blur_sigma(device, reporter); } #endif diff --git a/tests/ImageIsOpaqueTest.cpp b/tests/ImageIsOpaqueTest.cpp index 6d886d48bf..522bd9447f 100644 --- a/tests/ImageIsOpaqueTest.cpp +++ b/tests/ImageIsOpaqueTest.cpp @@ -80,11 +80,13 @@ DEF_GPUTEST(ImageIsOpaqueTest_GPU, reporter, factory) { } SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref surfaceTransparent(SkSurface::NewRenderTarget(context, infoTransparent)); + SkAutoTUnref surfaceTransparent( + SkSurface::NewRenderTarget(context,SkSurface::kNo_Budgeted, infoTransparent)); check_isopaque(reporter, surfaceTransparent, false); SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType); - SkAutoTUnref surfaceOpaque(SkSurface::NewRenderTarget(context, infoOpaque)); + SkAutoTUnref surfaceOpaque( + SkSurface::NewRenderTarget(context,SkSurface::kNo_Budgeted, infoOpaque)); #if 0 // this is failing right now : TODO fix me check_isopaque(reporter, surfaceOpaque, true); diff --git a/tests/ImageNewShaderTest.cpp b/tests/ImageNewShaderTest.cpp index 1bc77fba66..c78b7157a2 100644 --- a/tests/ImageNewShaderTest.cpp +++ b/tests/ImageNewShaderTest.cpp @@ -115,8 +115,10 @@ DEF_TEST(ImageNewShader, reporter) { void gpuToGpu(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref sourceSurface(SkSurface::NewRenderTarget(context, info)); - SkAutoTUnref destinationSurface(SkSurface::NewRenderTarget(context, info)); + SkAutoTUnref sourceSurface( + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info)); + SkAutoTUnref destinationSurface( + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info)); runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); } @@ -124,7 +126,8 @@ void gpuToGpu(skiatest::Reporter* reporter, GrContext* context) { void gpuToRaster(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); - SkAutoTUnref sourceSurface(SkSurface::NewRenderTarget(context, info)); + SkAutoTUnref sourceSurface(SkSurface::NewRenderTarget(context, + SkSurface::kNo_Budgeted, info)); SkAutoTUnref destinationSurface(SkSurface::NewRaster(info)); runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); @@ -134,7 +137,8 @@ void rasterToGpu(skiatest::Reporter* reporter, GrContext* context) { SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5); SkAutoTUnref sourceSurface(SkSurface::NewRaster(info)); - SkAutoTUnref destinationSurface(SkSurface::NewRenderTarget(context, info)); + SkAutoTUnref destinationSurface(SkSurface::NewRenderTarget(context, + SkSurface::kNo_Budgeted, info)); runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info); } diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp index ce45f16258..9f5d6ff338 100644 --- a/tests/PremulAlphaRoundTripTest.cpp +++ b/tests/PremulAlphaRoundTripTest.cpp @@ -85,13 +85,12 @@ DEF_GPUTEST(PremulAlphaRoundTrip, reporter, factory) { if (!GrContextFactory::IsRenderingGLContext(type)) { continue; } - GrContext* context = factory->get(type); - if (NULL == context) { + GrContext* ctx = factory->get(type); + if (NULL == ctx) { continue; } - - device.reset(SkGpuDevice::Create(context, info, - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), 0)); + SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + device.reset(SkGpuDevice::Create(ctx, SkSurface::kNo_Budgeted, info, 0, &props)); #else continue; #endif diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp index 6bcc51faf2..966bc7f901 100644 --- a/tests/ReadWriteAlphaTest.cpp +++ b/tests/ReadWriteAlphaTest.cpp @@ -82,8 +82,8 @@ DEF_GPUTEST(ReadWriteAlpha, reporter, factory) { REPORTER_ASSERT(reporter, match); // Now try writing on the single channel texture - SkAutoTUnref device(SkGpuDevice::Create(texture->asRenderTarget(), - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))); + SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkAutoTUnref device(SkGpuDevice::Create(texture->asRenderTarget(), &props)); SkCanvas canvas(device); SkPaint paint; diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index 8639626cf2..e51b3e0997 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -797,7 +797,8 @@ DEF_GPUTEST(ResourceCache, reporter, factory) { desc.fWidth = gWidth; desc.fHeight = gHeight; SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); - SkAutoTUnref surface(SkSurface::NewRenderTarget(context, info)); + SkAutoTUnref surface(SkSurface::NewRenderTarget(context, + SkSurface::kNo_Budgeted, info)); test_cache(reporter, context, surface->getCanvas()); } diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index c09d97cabe..4816ffc1ac 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -50,15 +50,9 @@ static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context, release_storage, storage); } case kGpu_SurfaceType: -#if SK_SUPPORT_GPU - return context ? SkSurface::NewRenderTarget(context, info, 0, NULL) : NULL; -#endif - break; + return SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0, NULL); case kGpuScratch_SurfaceType: -#if SK_SUPPORT_GPU - return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL; -#endif - break; + return SkSurface::NewRenderTarget(context, SkSurface::kYes_Budgeted, info, 0, NULL); } return NULL; } @@ -94,8 +88,8 @@ static void test_empty_surface(skiatest::Reporter* reporter, GrContext* ctx) { REPORTER_ASSERT(reporter, NULL == SkSurface::NewRaster(info)); REPORTER_ASSERT(reporter, NULL == SkSurface::NewRasterDirect(info, NULL, 0)); if (ctx) { - REPORTER_ASSERT(reporter, NULL == SkSurface::NewRenderTarget(ctx, info, 0, NULL)); - REPORTER_ASSERT(reporter, NULL == SkSurface::NewScratchRenderTarget(ctx, info, 0, NULL)); + REPORTER_ASSERT(reporter, NULL == + SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info, 0, NULL)); } } @@ -129,7 +123,8 @@ static SkImage* createImage(ImageType imageType, GrContext* context, SkColor col case kRasterData_ImageType: return SkImage::NewRasterData(info, data, rowBytes); case kGpu_ImageType: { - SkAutoTUnref surf(SkSurface::NewRenderTarget(context, info, 0)); + SkAutoTUnref surf( + SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0)); surf->getCanvas()->clear(color); return surf->newImageSnapshot(); } diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index 0d7e14c794..dc672f013b 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -157,16 +157,14 @@ SkCanvas* PictureRenderer::setupCanvas(int width, int height) { desc.fSampleCnt = fSampleCount; target.reset(fGrContext->createUncachedTexture(desc, NULL, 0)); } - if (NULL == target.get()) { - SkASSERT(0); + + uint32_t flags = fUseDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; + SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); + SkAutoTUnref device(SkGpuDevice::Create(target->asRenderTarget(), &props)); + if (!device) { return NULL; } - - uint32_t flags = fUseDFText ? SkGpuDevice::kDFText_Flag : 0; - SkAutoTUnref device(SkGpuDevice::Create(target, - SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), - flags)); - canvas = SkNEW_ARGS(SkCanvas, (device.get())); + canvas = SkNEW_ARGS(SkCanvas, (device)); break; } #endif