1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/vk/GrVkTexture.h"
9
10 #include "src/gpu/GrTexturePriv.h"
11 #include "src/gpu/vk/GrVkDescriptorSet.h"
12 #include "src/gpu/vk/GrVkGpu.h"
13 #include "src/gpu/vk/GrVkImageView.h"
14 #include "src/gpu/vk/GrVkTextureRenderTarget.h"
15 #include "src/gpu/vk/GrVkUtil.h"
16
17 #include "include/gpu/vk/GrVkTypes.h"
18
19 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
20
21 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrVkTexture(GrVkGpu * gpu,SkBudgeted budgeted,SkISize dimensions,const GrVkImageInfo & info,sk_sp<GrVkImageLayout> layout,const GrVkImageView * view,GrMipMapsStatus mipMapsStatus)22 GrVkTexture::GrVkTexture(GrVkGpu* gpu,
23 SkBudgeted budgeted,
24 SkISize dimensions,
25 const GrVkImageInfo& info,
26 sk_sp<GrVkImageLayout> layout,
27 const GrVkImageView* view,
28 GrMipMapsStatus mipMapsStatus)
29 : GrSurface(gpu, dimensions, info.fProtected)
30 , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kOwned)
31 , INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipMapsStatus)
32 , fTextureView(view)
33 , fDescSetCache(kMaxCachedDescSets) {
34 SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
35 // We don't support creating external GrVkTextures
36 SkASSERT(!info.fYcbcrConversionInfo.isValid() || !info.fYcbcrConversionInfo.fExternalFormat);
37 this->registerWithCache(budgeted);
38 if (GrVkFormatIsCompressed(info.fFormat)) {
39 this->setReadOnly();
40 }
41 }
42
GrVkTexture(GrVkGpu * gpu,SkISize dimensions,const GrVkImageInfo & info,sk_sp<GrVkImageLayout> layout,const GrVkImageView * view,GrMipMapsStatus mipMapsStatus,GrBackendObjectOwnership ownership,GrWrapCacheable cacheable,GrIOType ioType,bool isExternal)43 GrVkTexture::GrVkTexture(GrVkGpu* gpu, SkISize dimensions, const GrVkImageInfo& info,
44 sk_sp<GrVkImageLayout> layout, const GrVkImageView* view,
45 GrMipMapsStatus mipMapsStatus, GrBackendObjectOwnership ownership,
46 GrWrapCacheable cacheable, GrIOType ioType, bool isExternal)
47 : GrSurface(gpu, dimensions, info.fProtected)
48 , GrVkImage(info, std::move(layout), ownership)
49 , INHERITED(gpu, dimensions, info.fProtected,
50 isExternal ? GrTextureType::kExternal : GrTextureType::k2D, mipMapsStatus)
51 , fTextureView(view)
52 , fDescSetCache(kMaxCachedDescSets) {
53 SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
54 if (ioType == kRead_GrIOType) {
55 this->setReadOnly();
56 }
57 this->registerWithCacheWrapped(cacheable);
58 }
59
60 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrVkTexture(GrVkGpu * gpu,SkISize dimensions,const GrVkImageInfo & info,sk_sp<GrVkImageLayout> layout,const GrVkImageView * view,GrMipMapsStatus mipMapsStatus,GrBackendObjectOwnership ownership)61 GrVkTexture::GrVkTexture(GrVkGpu* gpu,
62 SkISize dimensions,
63 const GrVkImageInfo& info,
64 sk_sp<GrVkImageLayout> layout,
65 const GrVkImageView* view,
66 GrMipMapsStatus mipMapsStatus,
67 GrBackendObjectOwnership ownership)
68 : GrSurface(gpu, dimensions, info.fProtected)
69 , GrVkImage(info, layout, ownership)
70 , INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipMapsStatus)
71 , fTextureView(view)
72 , fDescSetCache(kMaxCachedDescSets) {
73 SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
74 // Since this ctor is only called from GrVkTextureRenderTarget, we can't have a ycbcr conversion
75 // since we don't support that on render targets.
76 SkASSERT(!info.fYcbcrConversionInfo.isValid());
77 }
78
MakeNewTexture(GrVkGpu * gpu,SkBudgeted budgeted,SkISize dimensions,const GrVkImage::ImageDesc & imageDesc,GrMipMapsStatus mipMapsStatus)79 sk_sp<GrVkTexture> GrVkTexture::MakeNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
80 SkISize dimensions,
81 const GrVkImage::ImageDesc& imageDesc,
82 GrMipMapsStatus mipMapsStatus) {
83 SkASSERT(imageDesc.fUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT);
84
85 GrVkImageInfo info;
86 if (!GrVkImage::InitImageInfo(gpu, imageDesc, &info)) {
87 return nullptr;
88 }
89
90 const GrVkImageView* imageView = GrVkImageView::Create(
91 gpu, info.fImage, info.fFormat, GrVkImageView::kColor_Type, info.fLevelCount,
92 info.fYcbcrConversionInfo);
93 if (!imageView) {
94 GrVkImage::DestroyImageInfo(gpu, &info);
95 return nullptr;
96 }
97 sk_sp<GrVkImageLayout> layout(new GrVkImageLayout(info.fImageLayout));
98
99 return sk_sp<GrVkTexture>(new GrVkTexture(gpu, budgeted, dimensions, info, std::move(layout),
100 imageView, mipMapsStatus));
101 }
102
MakeWrappedTexture(GrVkGpu * gpu,SkISize dimensions,GrWrapOwnership wrapOwnership,GrWrapCacheable cacheable,GrIOType ioType,const GrVkImageInfo & info,sk_sp<GrVkImageLayout> layout)103 sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu,
104 SkISize dimensions,
105 GrWrapOwnership wrapOwnership,
106 GrWrapCacheable cacheable,
107 GrIOType ioType,
108 const GrVkImageInfo& info,
109 sk_sp<GrVkImageLayout> layout) {
110 // Adopted textures require both image and allocation because we're responsible for freeing
111 SkASSERT(VK_NULL_HANDLE != info.fImage &&
112 (kBorrow_GrWrapOwnership == wrapOwnership || VK_NULL_HANDLE != info.fAlloc.fMemory));
113
114 const GrVkImageView* imageView = GrVkImageView::Create(
115 gpu, info.fImage, info.fFormat, GrVkImageView::kColor_Type, info.fLevelCount,
116 info.fYcbcrConversionInfo);
117 if (!imageView) {
118 return nullptr;
119 }
120
121 GrMipMapsStatus mipMapsStatus = info.fLevelCount > 1 ? GrMipMapsStatus::kValid
122 : GrMipMapsStatus::kNotAllocated;
123
124 GrBackendObjectOwnership ownership = kBorrow_GrWrapOwnership == wrapOwnership
125 ? GrBackendObjectOwnership::kBorrowed : GrBackendObjectOwnership::kOwned;
126 bool isExternal = info.fYcbcrConversionInfo.isValid() &&
127 (info.fYcbcrConversionInfo.fExternalFormat != 0);
128 return sk_sp<GrVkTexture>(new GrVkTexture(gpu, dimensions, info, std::move(layout), imageView,
129 mipMapsStatus, ownership, cacheable, ioType,
130 isExternal));
131 }
132
~GrVkTexture()133 GrVkTexture::~GrVkTexture() {
134 // either release or abandon should have been called by the owner of this object.
135 SkASSERT(!fTextureView);
136 }
137
onRelease()138 void GrVkTexture::onRelease() {
139 // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to
140 // decide who will handle them. If the resource is still tied to a command buffer we let it
141 // handle them. Otherwise, we handle them.
142 if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
143 this->removeFinishIdleProcs();
144 }
145
146 // we create this and don't hand it off, so we should always destroy it
147 if (fTextureView) {
148 fTextureView->unref(this->getVkGpu());
149 fTextureView = nullptr;
150 }
151
152 fDescSetCache.reset();
153
154 this->releaseImage(this->getVkGpu());
155
156 INHERITED::onRelease();
157 }
158
159 struct GrVkTexture::DescriptorCacheEntry {
DescriptorCacheEntryGrVkTexture::DescriptorCacheEntry160 DescriptorCacheEntry(const GrVkDescriptorSet* fDescSet, GrVkGpu* gpu)
161 : fDescriptorSet(fDescSet), fGpu(gpu) {}
~DescriptorCacheEntryGrVkTexture::DescriptorCacheEntry162 ~DescriptorCacheEntry() {
163 if (fDescriptorSet) {
164 fDescriptorSet->recycle(fGpu);
165 }
166 }
167
168 const GrVkDescriptorSet* fDescriptorSet;
169 GrVkGpu* fGpu;
170 };
171
onAbandon()172 void GrVkTexture::onAbandon() {
173 // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to
174 // decide who will handle them. If the resource is still tied to a command buffer we let it
175 // handle them. Otherwise, we handle them.
176 if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
177 this->removeFinishIdleProcs();
178 }
179
180 // we create this and don't hand it off, so we should always destroy it
181 if (fTextureView) {
182 fTextureView->unref(this->getVkGpu());
183 fTextureView = nullptr;
184 }
185
186 fDescSetCache.reset();
187
188 this->releaseImage(this->getVkGpu());
189 INHERITED::onAbandon();
190 }
191
getBackendTexture() const192 GrBackendTexture GrVkTexture::getBackendTexture() const {
193 return GrBackendTexture(this->width(), this->height(), fInfo, this->grVkImageLayout());
194 }
195
getVkGpu() const196 GrVkGpu* GrVkTexture::getVkGpu() const {
197 SkASSERT(!this->wasDestroyed());
198 return static_cast<GrVkGpu*>(this->getGpu());
199 }
200
textureView()201 const GrVkImageView* GrVkTexture::textureView() {
202 return fTextureView;
203 }
204
addIdleProc(sk_sp<GrRefCntedCallback> idleProc,IdleState type)205 void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> idleProc, IdleState type) {
206 INHERITED::addIdleProc(idleProc, type);
207 if (type == IdleState::kFinished) {
208 if (auto* resource = this->resource()) {
209 resource->addIdleProc(this, std::move(idleProc));
210 }
211 }
212 }
213
callIdleProcsOnBehalfOfResource()214 void GrVkTexture::callIdleProcsOnBehalfOfResource() {
215 // If we got here then the resource is being removed from its last command buffer and the
216 // texture is idle in the cache. Any kFlush idle procs should already have been called. So
217 // the texture and resource should have the same set of procs.
218 SkASSERT(this->resource());
219 SkASSERT(this->resource()->idleProcCnt() == fIdleProcs.count());
220 #ifdef SK_DEBUG
221 for (int i = 0; i < fIdleProcs.count(); ++i) {
222 SkASSERT(fIdleProcs[i] == this->resource()->idleProc(i));
223 }
224 #endif
225 fIdleProcs.reset();
226 this->resource()->resetIdleProcs();
227 }
228
willRemoveLastRef()229 void GrVkTexture::willRemoveLastRef() {
230 if (!fIdleProcs.count()) {
231 return;
232 }
233 // This is called when the GrTexture is purgeable. However, we need to check whether the
234 // Resource is still owned by any command buffers. If it is then it will call the proc.
235 auto* resource = this->hasResource() ? this->resource() : nullptr;
236 bool callFinishProcs = !resource || !resource->isOwnedByCommandBuffer();
237 if (callFinishProcs) {
238 // Everything must go!
239 fIdleProcs.reset();
240 if (resource) {
241 resource->resetIdleProcs();
242 }
243 } else {
244 // The procs that should be called on flush but not finish are those that are owned
245 // by the GrVkTexture and not the Resource. We do this by copying the resource's array
246 // and thereby dropping refs to procs we own but the resource does not.
247 SkASSERT(resource);
248 fIdleProcs.reset(resource->idleProcCnt());
249 for (int i = 0; i < fIdleProcs.count(); ++i) {
250 fIdleProcs[i] = resource->idleProc(i);
251 }
252 }
253 }
254
removeFinishIdleProcs()255 void GrVkTexture::removeFinishIdleProcs() {
256 // This should only be called by onRelease/onAbandon when we have already checked for a
257 // resource.
258 const auto* resource = this->resource();
259 SkASSERT(resource);
260 SkSTArray<4, sk_sp<GrRefCntedCallback>> procsToKeep;
261 int resourceIdx = 0;
262 // The idle procs that are common between the GrVkTexture and its Resource should be found in
263 // the same order.
264 for (int i = 0; i < fIdleProcs.count(); ++i) {
265 if (fIdleProcs[i] == resource->idleProc(resourceIdx)) {
266 ++resourceIdx;
267 } else {
268 procsToKeep.push_back(fIdleProcs[i]);
269 }
270 }
271 SkASSERT(resourceIdx == resource->idleProcCnt());
272 fIdleProcs = procsToKeep;
273 }
274
cachedSingleDescSet(GrSamplerState state)275 const GrVkDescriptorSet* GrVkTexture::cachedSingleDescSet(GrSamplerState state) {
276 if (std::unique_ptr<DescriptorCacheEntry>* e = fDescSetCache.find(state)) {
277 return (*e)->fDescriptorSet;
278 }
279 return nullptr;
280 }
281
addDescriptorSetToCache(const GrVkDescriptorSet * descSet,GrSamplerState state)282 void GrVkTexture::addDescriptorSetToCache(const GrVkDescriptorSet* descSet, GrSamplerState state) {
283 SkASSERT(!fDescSetCache.find(state));
284 descSet->ref();
285 fDescSetCache.insert(state,
286 std::unique_ptr<DescriptorCacheEntry>(
287 new DescriptorCacheEntry(descSet, this->getVkGpu())));
288 }
289