1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "GrTexture.h" 11 12#include "GrContext.h" 13#include "GrDrawTargetCaps.h" 14#include "GrGpu.h" 15#include "GrRenderTarget.h" 16#include "GrResourceCache.h" 17 18GrTexture::~GrTexture() { 19 if (NULL != fRenderTarget.get()) { 20 fRenderTarget.get()->owningTextureDestroyed(); 21 } 22} 23 24/** 25 * This method allows us to interrupt the normal deletion process and place 26 * textures back in the texture cache when their ref count goes to zero. 27 */ 28void GrTexture::internal_dispose() const { 29 if (this->impl()->isSetFlag((GrTextureFlags) GrTextureImpl::kReturnToCache_FlagBit) && 30 NULL != this->INHERITED::getContext()) { 31 GrTexture* nonConstThis = const_cast<GrTexture *>(this); 32 this->fRefCnt = 1; // restore ref count to initial setting 33 34 nonConstThis->impl()->resetFlag((GrTextureFlags) GrTextureImpl::kReturnToCache_FlagBit); 35 nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis); 36 37 // Note: "this" texture might be freed inside addExistingTextureToCache 38 // if it is purged. 39 return; 40 } 41 42 SkASSERT(0 == this->getDeferredRefCount()); 43 this->INHERITED::internal_dispose(); 44} 45 46void GrTextureImpl::dirtyMipMaps(bool mipMapsDirty) { 47 if (mipMapsDirty) { 48 if (kValid_MipMapsStatus == fMipMapsStatus) { 49 fMipMapsStatus = kAllocated_MipMapsStatus; 50 } 51 } else { 52 const bool sizeChanged = kNotAllocated_MipMapsStatus == fMipMapsStatus; 53 fMipMapsStatus = kValid_MipMapsStatus; 54 if (sizeChanged) { 55 // This must not be called until after changing fMipMapsStatus. 56 this->didChangeGpuMemorySize(); 57 } 58 } 59} 60 61size_t GrTexture::gpuMemorySize() const { 62 size_t textureSize = (size_t) fDesc.fWidth * 63 fDesc.fHeight * 64 GrBytesPerPixel(fDesc.fConfig); 65 66 if (GrPixelConfigIsCompressed(fDesc.fConfig)) { 67 // Figure out the width and height corresponding to the data... 68 69 // Both of the available formats (ETC1 and LATC) have 4x4 70 // blocks that compress down to 8 bytes. 71 switch(fDesc.fConfig) { 72 case kETC1_GrPixelConfig: 73 case kLATC_GrPixelConfig: 74 SkASSERT((fDesc.fWidth & 3) == 0); 75 SkASSERT((fDesc.fHeight & 3) == 0); 76 textureSize = (fDesc.fWidth >> 2) * (fDesc.fHeight >> 2) * 8; 77 break; 78 79 default: 80 SkFAIL("Unknown compressed config"); 81 } 82 } 83 84 if (this->impl()->hasMipMaps()) { 85 // We don't have to worry about the mipmaps being a different size than 86 // we'd expect because we never change fDesc.fWidth/fHeight. 87 textureSize *= 2; 88 } 89 return textureSize; 90} 91 92bool GrTexture::readPixels(int left, int top, int width, int height, 93 GrPixelConfig config, void* buffer, 94 size_t rowBytes, uint32_t pixelOpsFlags) { 95 // go through context so that all necessary flushing occurs 96 GrContext* context = this->getContext(); 97 if (NULL == context) { 98 return false; 99 } 100 return context->readTexturePixels(this, 101 left, top, width, height, 102 config, buffer, rowBytes, 103 pixelOpsFlags); 104} 105 106void GrTexture::writePixels(int left, int top, int width, int height, 107 GrPixelConfig config, const void* buffer, 108 size_t rowBytes, uint32_t pixelOpsFlags) { 109 // go through context so that all necessary flushing occurs 110 GrContext* context = this->getContext(); 111 if (NULL == context) { 112 return; 113 } 114 context->writeTexturePixels(this, 115 left, top, width, height, 116 config, buffer, rowBytes, 117 pixelOpsFlags); 118} 119 120void GrTexture::onRelease() { 121 SkASSERT(!this->impl()->isSetFlag((GrTextureFlags) GrTextureImpl::kReturnToCache_FlagBit)); 122 INHERITED::onRelease(); 123} 124 125void GrTexture::onAbandon() { 126 if (NULL != fRenderTarget.get()) { 127 fRenderTarget->abandon(); 128 } 129 INHERITED::onAbandon(); 130} 131 132void GrTexture::validateDesc() const { 133 if (NULL != this->asRenderTarget()) { 134 // This texture has a render target 135 SkASSERT(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); 136 137 if (NULL != this->asRenderTarget()->getStencilBuffer()) { 138 SkASSERT(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 139 } else { 140 SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 141 } 142 143 SkASSERT(fDesc.fSampleCnt == this->asRenderTarget()->numSamples()); 144 } else { 145 SkASSERT(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); 146 SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 147 SkASSERT(0 == fDesc.fSampleCnt); 148 } 149} 150 151////////////////////////////////////////////////////////////////////////////// 152 153// These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture 154// key 155enum TextureFlags { 156 /** 157 * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the 158 * hardware doesn't support that feature. 159 */ 160 kStretchToPOT_TextureFlag = 0x1, 161 /** 162 * The kBilerp bit can only be set when the kStretchToPOT flag is set and indicates whether the 163 * stretched texture should be bilerped. 164 */ 165 kBilerp_TextureFlag = 0x2, 166}; 167 168namespace { 169GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu, 170 const GrTextureParams* params, 171 const GrTextureDesc& desc) { 172 GrResourceKey::ResourceFlags flags = 0; 173 bool tiled = NULL != params && params->isTiled(); 174 if (tiled && !gpu->caps()->npotTextureTileSupport()) { 175 if (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight)) { 176 flags |= kStretchToPOT_TextureFlag; 177 switch(params->filterMode()) { 178 case GrTextureParams::kNone_FilterMode: 179 break; 180 case GrTextureParams::kBilerp_FilterMode: 181 case GrTextureParams::kMipMap_FilterMode: 182 flags |= kBilerp_TextureFlag; 183 break; 184 } 185 } 186 } 187 return flags; 188} 189 190GrResourceKey::ResourceType texture_resource_type() { 191 static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType(); 192 return gType; 193} 194 195// FIXME: This should be refactored with the code in gl/GrGpuGL.cpp. 196GrSurfaceOrigin resolve_origin(const GrTextureDesc& desc) { 197 // By default, GrRenderTargets are GL's normal orientation so that they 198 // can be drawn to by the outside world without the client having 199 // to render upside down. 200 bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit); 201 if (kDefault_GrSurfaceOrigin == desc.fOrigin) { 202 return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; 203 } else { 204 return desc.fOrigin; 205 } 206} 207} 208 209////////////////////////////////////////////////////////////////////////////// 210 211GrResourceKey GrTextureImpl::ComputeKey(const GrGpu* gpu, 212 const GrTextureParams* params, 213 const GrTextureDesc& desc, 214 const GrCacheID& cacheID) { 215 GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc); 216 return GrResourceKey(cacheID, texture_resource_type(), flags); 217} 218 219GrResourceKey GrTextureImpl::ComputeScratchKey(const GrTextureDesc& desc) { 220 GrCacheID::Key idKey; 221 // Instead of a client-provided key of the texture contents we create a key from the 222 // descriptor. 223 GR_STATIC_ASSERT(sizeof(idKey) >= 16); 224 SkASSERT(desc.fHeight < (1 << 16)); 225 SkASSERT(desc.fWidth < (1 << 16)); 226 idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16); 227 idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16; 228 idKey.fData32[2] = desc.fFlags; 229 idKey.fData32[3] = resolve_origin(desc); // Only needs 2 bits actually 230 static const int kPadSize = sizeof(idKey) - 16; 231 GR_STATIC_ASSERT(kPadSize >= 0); 232 memset(idKey.fData8 + 16, 0, kPadSize); 233 234 GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey); 235 return GrResourceKey(cacheID, texture_resource_type(), 0); 236} 237 238bool GrTextureImpl::NeedsResizing(const GrResourceKey& key) { 239 return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag); 240} 241 242bool GrTextureImpl::NeedsBilerp(const GrResourceKey& key) { 243 return SkToBool(key.getResourceFlags() & kBilerp_TextureFlag); 244} 245