/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrTexture.h" #include "GrContext.h" #include "GrGpu.h" #include "GrRenderTarget.h" #include "GrResourceCache.h" SK_DEFINE_INST_COUNT(GrTexture) /** * This method allows us to interrupt the normal deletion process and place * textures back in the texture cache when their ref count goes to zero. */ void GrTexture::internal_dispose() const { if (this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit) && NULL != this->INHERITED::getContext()) { GrTexture* nonConstThis = const_cast(this); this->fRefCnt = 1; // restore ref count to initial setting nonConstThis->resetFlag((GrTextureFlags) kReturnToCache_FlagBit); nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis); // Note: "this" texture might be freed inside addExistingTextureToCache // if it is purged. return; } this->INHERITED::internal_dispose(); } bool GrTexture::readPixels(int left, int top, int width, int height, GrPixelConfig config, void* buffer, size_t rowBytes) { // go through context so that all necessary flushing occurs GrContext* context = this->getContext(); if (NULL == context) { return false; } return context->readTexturePixels(this, left, top, width, height, config, buffer, rowBytes); } void GrTexture::writePixels(int left, int top, int width, int height, GrPixelConfig config, const void* buffer, size_t rowBytes) { // go through context so that all necessary flushing occurs GrContext* context = this->getContext(); if (NULL == context) { return; } context->writeTexturePixels(this, left, top, width, height, config, buffer, rowBytes); } void GrTexture::releaseRenderTarget() { if (NULL != fRenderTarget) { GrAssert(fRenderTarget->asTexture() == this); GrAssert(fDesc.fFlags & kRenderTarget_GrTextureFlagBit); fRenderTarget->onTextureReleaseRenderTarget(); fRenderTarget->unref(); fRenderTarget = NULL; fDesc.fFlags = fDesc.fFlags & ~(kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit); fDesc.fSampleCnt = 0; } } void GrTexture::onRelease() { GrAssert(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit)); this->releaseRenderTarget(); } void GrTexture::onAbandon() { if (NULL != fRenderTarget) { fRenderTarget->abandon(); } } void GrTexture::validateDesc() const { if (NULL != this->asRenderTarget()) { // This texture has a render target GrAssert(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); if (NULL != this->asRenderTarget()->getStencilBuffer()) { GrAssert(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); } else { GrAssert(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); } GrAssert(fDesc.fSampleCnt == this->asRenderTarget()->numSamples()); } else { GrAssert(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); GrAssert(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); GrAssert(0 == fDesc.fSampleCnt); } } enum TextureBits { kFirst_TextureBit = (GrResourceKey::kLastPublic_TypeBit << 1), /* * The kNPOT bit is set when the texture is NPOT and is being repeated * but the hardware doesn't support that feature. */ kNPOT_TextureBit = kFirst_TextureBit, /* * The kFilter bit can only be set when the kNPOT flag is set and indicates * whether the resizing of the texture should use filtering. This is * to handle cases where the original texture is indexed to disable * filtering. */ kFilter_TextureBit = kNPOT_TextureBit << 1, /* * The kScratch bit is set if the texture is being used as a scratch * texture. */ kScratch_TextureBit = kFilter_TextureBit << 1, }; namespace { void gen_texture_key_values(const GrGpu* gpu, const GrSamplerState* sampler, const GrTextureDesc& desc, bool scratch, uint32_t v[4]) { uint64_t clientKey = desc.fClientCacheID; if (scratch) { // Instead of a client-provided key of the texture contents // we create a key of from the descriptor. GrAssert(kScratch_CacheID == clientKey); clientKey = (desc.fFlags << 8) | ((uint64_t) desc.fConfig << 32); } // we assume we only need 16 bits of width and height // assert that texture creation will fail anyway if this assumption // would cause key collisions. GrAssert(gpu->getCaps().fMaxTextureSize <= SK_MaxU16); v[0] = (uint32_t) (clientKey & 0xffffffffUL); v[1] = (uint32_t) ((clientKey >> 32) & 0xffffffffUL); v[2] = desc.fWidth | (desc.fHeight << 16); v[3] = (desc.fSampleCnt << 24); GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt < 256); if (!gpu->getCaps().fNPOTTextureTileSupport) { bool isPow2 = GrIsPow2(desc.fWidth) && GrIsPow2(desc.fHeight); bool tiled = NULL != sampler && ((sampler->getWrapX() != GrSamplerState::kClamp_WrapMode) || (sampler->getWrapY() != GrSamplerState::kClamp_WrapMode)); if (tiled && !isPow2) { v[3] |= kNPOT_TextureBit; if (GrSamplerState::kNearest_Filter != sampler->getFilter()) { v[3] |= kFilter_TextureBit; } } } if (scratch) { v[3] |= kScratch_TextureBit; } v[3] |= GrResourceKey::kTexture_TypeBit; } } GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu, const GrSamplerState* sampler, const GrTextureDesc& desc, bool scratch) { uint32_t v[4]; gen_texture_key_values(gpu, sampler, desc, scratch, v); return GrResourceKey(v); } bool GrTexture::NeedsResizing(const GrResourceKey& key) { return 0 != (key.getValue32(3) & kNPOT_TextureBit); } bool GrTexture::IsScratchTexture(const GrResourceKey& key) { return 0 != (key.getValue32(3) & kScratch_TextureBit); } bool GrTexture::NeedsFiltering(const GrResourceKey& key) { return 0 != (key.getValue32(3) & kFilter_TextureBit); }