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