|
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 |
|
18 GrTexture::~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 */ |
|
28 void 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 |
|
47 bool 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 |
|
61 void 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 |
|
75 void GrTexture::onRelease() { |
|
76 SkASSERT(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit)); |
|
77 INHERITED::onRelease(); |
|
78 } |
|
79 |
|
80 void GrTexture::onAbandon() { |
|
81 if (NULL != fRenderTarget.get()) { |
|
82 fRenderTarget->abandon(); |
|
83 } |
|
84 INHERITED::onAbandon(); |
|
85 } |
|
86 |
|
87 void 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 |
|
108 enum 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 |
|
121 namespace { |
|
122 GrResourceKey::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 |
|
143 GrResourceKey::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. |
|
149 GrSurfaceOrigin 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 |
|
162 GrResourceKey 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 |
|
170 GrResourceKey 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 |
|
189 bool GrTexture::NeedsResizing(const GrResourceKey& key) { |
|
190 return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag); |
|
191 } |
|
192 |
|
193 bool GrTexture::NeedsBilerp(const GrResourceKey& key) { |
|
194 return SkToBool(key.getResourceFlags() & kBilerp_TextureFlag); |
|
195 } |