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