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
30    if (this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit) &&
31        NULL != this->INHERITED::getContext()) {
32        GrTexture* nonConstThis = const_cast<GrTexture *>(this);
33        this->fRefCnt = 1;      // restore ref count to initial setting
34
35        nonConstThis->resetFlag((GrTextureFlags) kReturnToCache_FlagBit);
36        nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis);
37
38        // Note: "this" texture might be freed inside addExistingTextureToCache
39        // if it is purged.
40        return;
41    }
42
43    SkASSERT(0 == this->getDeferredRefCount());
44    this->INHERITED::internal_dispose();
45}
46
47bool GrTexture::readPixels(int left, int top, int width, int height,
48                           GrPixelConfig config, void* buffer,
49                           size_t rowBytes, uint32_t pixelOpsFlags) {
50    // go through context so that all necessary flushing occurs
51    GrContext* context = this->getContext();
52    if (NULL == context) {
53        return false;
54    }
55    return context->readTexturePixels(this,
56                                      left, top, width, height,
57                                      config, buffer, rowBytes,
58                                      pixelOpsFlags);
59}
60
61void GrTexture::writePixels(int left, int top, int width, int height,
62                            GrPixelConfig config, const void* buffer,
63                            size_t rowBytes, uint32_t pixelOpsFlags) {
64    // go through context so that all necessary flushing occurs
65    GrContext* context = this->getContext();
66    if (NULL == context) {
67        return;
68    }
69    context->writeTexturePixels(this,
70                                left, top, width, height,
71                                config, buffer, rowBytes,
72                                pixelOpsFlags);
73}
74
75void GrTexture::onRelease() {
76    SkASSERT(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit));
77    INHERITED::onRelease();
78}
79
80void GrTexture::onAbandon() {
81    if (NULL != fRenderTarget.get()) {
82        fRenderTarget->abandon();
83    }
84    INHERITED::onAbandon();
85}
86
87void GrTexture::validateDesc() const {
88    if (NULL != this->asRenderTarget()) {
89        // This texture has a render target
90        SkASSERT(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit));
91
92        if (NULL != this->asRenderTarget()->getStencilBuffer()) {
93            SkASSERT(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
94        } else {
95            SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
96        }
97
98        SkASSERT(fDesc.fSampleCnt == this->asRenderTarget()->numSamples());
99    } else {
100        SkASSERT(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit));
101        SkASSERT(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
102        SkASSERT(0 == fDesc.fSampleCnt);
103    }
104}
105
106// These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture
107// key
108enum TextureFlags {
109    /**
110     * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the
111     * hardware doesn't support that feature.
112     */
113    kStretchToPOT_TextureFlag = 0x1,
114    /**
115     * The kBilerp bit can only be set when the kStretchToPOT flag is set and indicates whether the
116     * stretched texture should be bilerped.
117     */
118     kBilerp_TextureFlag       = 0x2,
119};
120
121namespace {
122GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu,
123                                               const GrTextureParams* params,
124                                               const GrTextureDesc& desc) {
125    GrResourceKey::ResourceFlags flags = 0;
126    bool tiled = NULL != params && params->isTiled();
127    if (tiled && !gpu->caps()->npotTextureTileSupport()) {
128        if (!GrIsPow2(desc.fWidth) || !GrIsPow2(desc.fHeight)) {
129            flags |= kStretchToPOT_TextureFlag;
130            switch(params->filterMode()) {
131                case GrTextureParams::kNone_FilterMode:
132                    break;
133                case GrTextureParams::kBilerp_FilterMode:
134                case GrTextureParams::kMipMap_FilterMode:
135                    flags |= kBilerp_TextureFlag;
136                    break;
137            }
138        }
139    }
140    return flags;
141}
142
143GrResourceKey::ResourceType texture_resource_type() {
144    static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType();
145    return gType;
146}
147
148// FIXME:  This should be refactored with the code in gl/GrGpuGL.cpp.
149GrSurfaceOrigin resolve_origin(const GrTextureDesc& desc) {
150    // By default, GrRenderTargets are GL's normal orientation so that they
151    // can be drawn to by the outside world without the client having
152    // to render upside down.
153    bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
154    if (kDefault_GrSurfaceOrigin == desc.fOrigin) {
155        return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
156    } else {
157        return desc.fOrigin;
158    }
159}
160}
161
162GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu,
163                                    const GrTextureParams* params,
164                                    const GrTextureDesc& desc,
165                                    const GrCacheID& cacheID) {
166    GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc);
167    return GrResourceKey(cacheID, texture_resource_type(), flags);
168}
169
170GrResourceKey GrTexture::ComputeScratchKey(const GrTextureDesc& desc) {
171    GrCacheID::Key idKey;
172    // Instead of a client-provided key of the texture contents we create a key from the
173    // descriptor.
174    GR_STATIC_ASSERT(sizeof(idKey) >= 16);
175    SkASSERT(desc.fHeight < (1 << 16));
176    SkASSERT(desc.fWidth < (1 << 16));
177    idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16);
178    idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16;
179    idKey.fData32[2] = desc.fFlags;
180    idKey.fData32[3] = resolve_origin(desc);    // Only needs 2 bits actually
181    static const int kPadSize = sizeof(idKey) - 16;
182    GR_STATIC_ASSERT(kPadSize >= 0);
183    memset(idKey.fData8 + 16, 0, kPadSize);
184
185    GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey);
186    return GrResourceKey(cacheID, texture_resource_type(), 0);
187}
188
189bool GrTexture::NeedsResizing(const GrResourceKey& key) {
190    return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag);
191}
192
193bool GrTexture::NeedsBilerp(const GrResourceKey& key) {
194    return SkToBool(key.getResourceFlags() & kBilerp_TextureFlag);
195}
196