GrTexture.cpp revision 7ef21622b2ed6b9c5fc4c149cb62944fc191f054
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 textureSize = GrCompressedFormatDataSize(fDesc.fConfig, fDesc.fWidth, fDesc.fHeight); 68 } 69 70 if (this->impl()->hasMipMaps()) { 71 // We don't have to worry about the mipmaps being a different size than 72 // we'd expect because we never change fDesc.fWidth/fHeight. 73 textureSize *= 2; 74 } 75 return textureSize; 76} 77 78bool GrTexture::readPixels(int left, int top, int width, int height, 79 GrPixelConfig config, void* buffer, 80 size_t rowBytes, uint32_t pixelOpsFlags) { 81 // go through context so that all necessary flushing occurs 82 GrContext* context = this->getContext(); 83 if (NULL == context) { 84 return false; 85 } 86 return context->readTexturePixels(this, 87 left, top, width, height, 88 config, buffer, rowBytes, 89 pixelOpsFlags); 90} 91 92void GrTexture::writePixels(int left, int top, int width, int height, 93 GrPixelConfig config, const 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; 99 } 100 context->writeTexturePixels(this, 101 left, top, width, height, 102 config, buffer, rowBytes, 103 pixelOpsFlags); 104} 105 106void GrTexture::onRelease() { 107 SkASSERT(!this->impl()->isSetFlag((GrTextureFlags) GrTextureImpl::kReturnToCache_FlagBit)); 108 INHERITED::onRelease(); 109} 110 111void GrTexture::onAbandon() { 112 if (NULL != fRenderTarget.get()) { 113 fRenderTarget->abandon(); 114 } 115 INHERITED::onAbandon(); 116} 117 118void GrTexture::validateDesc() const { 119 if (NULL != this->asRenderTarget()) { 120 // This texture has a render target 121 SkASSERT(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); 122 123 if (NULL != this->asRenderTarget()->getStencilBuffer()) { 124 SkASSERT(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 125 } else { 126 SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 127 } 128 129 SkASSERT(fDesc.fSampleCnt == this->asRenderTarget()->numSamples()); 130 } else { 131 SkASSERT(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit)); 132 SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit)); 133 SkASSERT(0 == fDesc.fSampleCnt); 134 } 135} 136 137////////////////////////////////////////////////////////////////////////////// 138 139// These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture 140// key 141enum TextureFlags { 142 /** 143 * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the 144 * hardware doesn't support that feature. 145 */ 146 kStretchToPOT_TextureFlag = 0x1, 147 /** 148 * The kBilerp bit can only be set when the kStretchToPOT flag is set and indicates whether the 149 * stretched texture should be bilerped. 150 */ 151 kBilerp_TextureFlag = 0x2, 152}; 153 154namespace { 155GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu, 156 const GrTextureParams* params, 157 const GrTextureDesc& desc) { 158 GrResourceKey::ResourceFlags flags = 0; 159 bool tiled = NULL != params && params->isTiled(); 160 if (tiled && !gpu->caps()->npotTextureTileSupport()) { 161 if (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight)) { 162 flags |= kStretchToPOT_TextureFlag; 163 switch(params->filterMode()) { 164 case GrTextureParams::kNone_FilterMode: 165 break; 166 case GrTextureParams::kBilerp_FilterMode: 167 case GrTextureParams::kMipMap_FilterMode: 168 flags |= kBilerp_TextureFlag; 169 break; 170 } 171 } 172 } 173 return flags; 174} 175 176GrResourceKey::ResourceType texture_resource_type() { 177 static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType(); 178 return gType; 179} 180 181// FIXME: This should be refactored with the code in gl/GrGpuGL.cpp. 182GrSurfaceOrigin resolve_origin(const GrTextureDesc& desc) { 183 // By default, GrRenderTargets are GL's normal orientation so that they 184 // can be drawn to by the outside world without the client having 185 // to render upside down. 186 bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit); 187 if (kDefault_GrSurfaceOrigin == desc.fOrigin) { 188 return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; 189 } else { 190 return desc.fOrigin; 191 } 192} 193} 194 195////////////////////////////////////////////////////////////////////////////// 196 197GrResourceKey GrTextureImpl::ComputeKey(const GrGpu* gpu, 198 const GrTextureParams* params, 199 const GrTextureDesc& desc, 200 const GrCacheID& cacheID) { 201 GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc); 202 return GrResourceKey(cacheID, texture_resource_type(), flags); 203} 204 205GrResourceKey GrTextureImpl::ComputeScratchKey(const GrTextureDesc& desc) { 206 GrCacheID::Key idKey; 207 // Instead of a client-provided key of the texture contents we create a key from the 208 // descriptor. 209 GR_STATIC_ASSERT(sizeof(idKey) >= 16); 210 SkASSERT(desc.fHeight < (1 << 16)); 211 SkASSERT(desc.fWidth < (1 << 16)); 212 idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16); 213 idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16; 214 idKey.fData32[2] = desc.fFlags; 215 idKey.fData32[3] = resolve_origin(desc); // Only needs 2 bits actually 216 static const int kPadSize = sizeof(idKey) - 16; 217 GR_STATIC_ASSERT(kPadSize >= 0); 218 memset(idKey.fData8 + 16, 0, kPadSize); 219 220 GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey); 221 return GrResourceKey(cacheID, texture_resource_type(), 0); 222} 223 224bool GrTextureImpl::NeedsResizing(const GrResourceKey& key) { 225 return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag); 226} 227 228bool GrTextureImpl::NeedsBilerp(const GrResourceKey& key) { 229 return SkToBool(key.getResourceFlags() & kBilerp_TextureFlag); 230} 231