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