michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "GrTexture.h" michael@0: michael@0: #include "GrContext.h" michael@0: #include "GrDrawTargetCaps.h" michael@0: #include "GrGpu.h" michael@0: #include "GrRenderTarget.h" michael@0: #include "GrResourceCache.h" michael@0: michael@0: GrTexture::~GrTexture() { michael@0: if (NULL != fRenderTarget.get()) { michael@0: fRenderTarget.get()->owningTextureDestroyed(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * This method allows us to interrupt the normal deletion process and place michael@0: * textures back in the texture cache when their ref count goes to zero. michael@0: */ michael@0: void GrTexture::internal_dispose() const { michael@0: michael@0: if (this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit) && michael@0: NULL != this->INHERITED::getContext()) { michael@0: GrTexture* nonConstThis = const_cast(this); michael@0: this->fRefCnt = 1; // restore ref count to initial setting michael@0: michael@0: nonConstThis->resetFlag((GrTextureFlags) kReturnToCache_FlagBit); michael@0: nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis); michael@0: michael@0: // Note: "this" texture might be freed inside addExistingTextureToCache michael@0: // if it is purged. michael@0: return; michael@0: } michael@0: michael@0: SkASSERT(0 == this->getDeferredRefCount()); michael@0: this->INHERITED::internal_dispose(); michael@0: } michael@0: michael@0: bool GrTexture::readPixels(int left, int top, int width, int height, michael@0: GrPixelConfig config, void* buffer, michael@0: size_t rowBytes, uint32_t pixelOpsFlags) { michael@0: // go through context so that all necessary flushing occurs michael@0: GrContext* context = this->getContext(); michael@0: if (NULL == context) { michael@0: return false; michael@0: } michael@0: return context->readTexturePixels(this, michael@0: left, top, width, height, michael@0: config, buffer, rowBytes, michael@0: pixelOpsFlags); michael@0: } michael@0: michael@0: void GrTexture::writePixels(int left, int top, int width, int height, michael@0: GrPixelConfig config, const void* buffer, michael@0: size_t rowBytes, uint32_t pixelOpsFlags) { michael@0: // go through context so that all necessary flushing occurs michael@0: GrContext* context = this->getContext(); michael@0: if (NULL == context) { michael@0: return; michael@0: } michael@0: context->writeTexturePixels(this, michael@0: left, top, width, height, michael@0: config, buffer, rowBytes, michael@0: pixelOpsFlags); michael@0: } michael@0: michael@0: void GrTexture::onRelease() { michael@0: SkASSERT(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit)); michael@0: INHERITED::onRelease(); michael@0: } michael@0: michael@0: void GrTexture::onAbandon() { michael@0: if (NULL != fRenderTarget.get()) { michael@0: fRenderTarget->abandon(); michael@0: } michael@0: INHERITED::onAbandon(); michael@0: } michael@0: michael@0: void GrTexture::validateDesc() const { michael@0: if (NULL != this->asRenderTarget()) { michael@0: // This texture has a render target michael@0: SkASSERT(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); michael@0: michael@0: if (NULL != this->asRenderTarget()->getStencilBuffer()) { michael@0: SkASSERT(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); michael@0: } else { michael@0: SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); michael@0: } michael@0: michael@0: SkASSERT(fDesc.fSampleCnt == this->asRenderTarget()->numSamples()); michael@0: } else { michael@0: SkASSERT(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); michael@0: SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); michael@0: SkASSERT(0 == fDesc.fSampleCnt); michael@0: } michael@0: } michael@0: michael@0: // These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture michael@0: // key michael@0: enum TextureFlags { michael@0: /** michael@0: * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the michael@0: * hardware doesn't support that feature. michael@0: */ michael@0: kStretchToPOT_TextureFlag = 0x1, michael@0: /** michael@0: * The kBilerp bit can only be set when the kStretchToPOT flag is set and indicates whether the michael@0: * stretched texture should be bilerped. michael@0: */ michael@0: kBilerp_TextureFlag = 0x2, michael@0: }; michael@0: michael@0: namespace { michael@0: GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu, michael@0: const GrTextureParams* params, michael@0: const GrTextureDesc& desc) { michael@0: GrResourceKey::ResourceFlags flags = 0; michael@0: bool tiled = NULL != params && params->isTiled(); michael@0: if (tiled && !gpu->caps()->npotTextureTileSupport()) { michael@0: if (!GrIsPow2(desc.fWidth) || !GrIsPow2(desc.fHeight)) { michael@0: flags |= kStretchToPOT_TextureFlag; michael@0: switch(params->filterMode()) { michael@0: case GrTextureParams::kNone_FilterMode: michael@0: break; michael@0: case GrTextureParams::kBilerp_FilterMode: michael@0: case GrTextureParams::kMipMap_FilterMode: michael@0: flags |= kBilerp_TextureFlag; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return flags; michael@0: } michael@0: michael@0: GrResourceKey::ResourceType texture_resource_type() { michael@0: static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType(); michael@0: return gType; michael@0: } michael@0: michael@0: // FIXME: This should be refactored with the code in gl/GrGpuGL.cpp. michael@0: GrSurfaceOrigin resolve_origin(const GrTextureDesc& desc) { michael@0: // By default, GrRenderTargets are GL's normal orientation so that they michael@0: // can be drawn to by the outside world without the client having michael@0: // to render upside down. michael@0: bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit); michael@0: if (kDefault_GrSurfaceOrigin == desc.fOrigin) { michael@0: return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; michael@0: } else { michael@0: return desc.fOrigin; michael@0: } michael@0: } michael@0: } michael@0: michael@0: GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu, michael@0: const GrTextureParams* params, michael@0: const GrTextureDesc& desc, michael@0: const GrCacheID& cacheID) { michael@0: GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc); michael@0: return GrResourceKey(cacheID, texture_resource_type(), flags); michael@0: } michael@0: michael@0: GrResourceKey GrTexture::ComputeScratchKey(const GrTextureDesc& desc) { michael@0: GrCacheID::Key idKey; michael@0: // Instead of a client-provided key of the texture contents we create a key from the michael@0: // descriptor. michael@0: GR_STATIC_ASSERT(sizeof(idKey) >= 16); michael@0: SkASSERT(desc.fHeight < (1 << 16)); michael@0: SkASSERT(desc.fWidth < (1 << 16)); michael@0: idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16); michael@0: idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16; michael@0: idKey.fData32[2] = desc.fFlags; michael@0: idKey.fData32[3] = resolve_origin(desc); // Only needs 2 bits actually michael@0: static const int kPadSize = sizeof(idKey) - 16; michael@0: GR_STATIC_ASSERT(kPadSize >= 0); michael@0: memset(idKey.fData8 + 16, 0, kPadSize); michael@0: michael@0: GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey); michael@0: return GrResourceKey(cacheID, texture_resource_type(), 0); michael@0: } michael@0: michael@0: bool GrTexture::NeedsResizing(const GrResourceKey& key) { michael@0: return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag); michael@0: } michael@0: michael@0: bool GrTexture::NeedsBilerp(const GrResourceKey& key) { michael@0: return SkToBool(key.getResourceFlags() & kBilerp_TextureFlag); michael@0: }