1 /*
2  * Copyright 2020 Google LLC
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/d3d/GrD3DTexture.h"
9 
10 #include "src/gpu/GrTexturePriv.h"
11 #include "src/gpu/d3d/GrD3DGpu.h"
12 #include "src/gpu/d3d/GrD3DUtil.h"
13 
14 #include "include/gpu/d3d/GrD3DTypes.h"
15 
16 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrD3DTexture(GrD3DGpu * gpu,SkBudgeted budgeted,SkISize dimensions,const GrD3DTextureResourceInfo & info,sk_sp<GrD3DResourceState> state,GrMipMapsStatus mipMapsStatus)17 GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu,
18                            SkBudgeted budgeted,
19                            SkISize dimensions,
20                            const GrD3DTextureResourceInfo& info,
21                            sk_sp<GrD3DResourceState> state,
22                            GrMipMapsStatus mipMapsStatus)
23         : GrSurface(gpu, dimensions, info.fProtected)
24         , GrD3DTextureResource(info, std::move(state))
25         , INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipMapsStatus) {
26     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
27     this->registerWithCache(budgeted);
28     if (GrDxgiFormatIsCompressed(info.fFormat)) {
29         this->setReadOnly();
30     }
31 }
32 
GrD3DTexture(GrD3DGpu * gpu,SkISize dimensions,const GrD3DTextureResourceInfo & info,sk_sp<GrD3DResourceState> state,GrMipMapsStatus mipMapsStatus,GrWrapCacheable cacheable,GrIOType ioType)33 GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu, SkISize dimensions, const GrD3DTextureResourceInfo& info,
34                            sk_sp<GrD3DResourceState> state, GrMipMapsStatus mipMapsStatus,
35                            GrWrapCacheable cacheable, GrIOType ioType)
36         : GrSurface(gpu, dimensions, info.fProtected)
37         , GrD3DTextureResource(info, std::move(state))
38         , INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipMapsStatus) {
39     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
40     if (ioType == kRead_GrIOType) {
41         this->setReadOnly();
42     }
43     this->registerWithCacheWrapped(cacheable);
44 }
45 
46 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrD3DTexture(GrD3DGpu * gpu,SkISize dimensions,const GrD3DTextureResourceInfo & info,sk_sp<GrD3DResourceState> state,GrMipMapsStatus mipMapsStatus)47 GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu,
48                            SkISize dimensions,
49                            const GrD3DTextureResourceInfo& info,
50                            sk_sp<GrD3DResourceState> state,
51                            GrMipMapsStatus mipMapsStatus)
52         : GrSurface(gpu, dimensions, info.fProtected)
53         , GrD3DTextureResource(info, state)
54         , INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipMapsStatus) {
55     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
56 }
57 
MakeNewTexture(GrD3DGpu * gpu,SkBudgeted budgeted,SkISize dimensions,const D3D12_RESOURCE_DESC & desc,GrProtected isProtected,GrMipMapsStatus mipMapsStatus)58 sk_sp<GrD3DTexture> GrD3DTexture::MakeNewTexture(GrD3DGpu* gpu, SkBudgeted budgeted,
59                                                  SkISize dimensions,
60                                                  const D3D12_RESOURCE_DESC& desc,
61                                                  GrProtected isProtected,
62                                                  GrMipMapsStatus mipMapsStatus) {
63     GrD3DTextureResourceInfo info;
64     if (!GrD3DTextureResource::InitTextureResourceInfo(gpu, desc, isProtected, &info)) {
65         return nullptr;
66     }
67 
68     sk_sp<GrD3DResourceState> state(
69             new GrD3DResourceState(static_cast<D3D12_RESOURCE_STATES>(info.fResourceState)));
70 
71     GrD3DTexture* tex = new GrD3DTexture(gpu, budgeted, dimensions, info, std::move(state),
72                                          mipMapsStatus);
73 
74     // The GrD3DTexture takes a ref on the texture so we need to release ours
75     GrD3DTextureResource::ReleaseTextureResourceInfo(&info);
76 
77     return sk_sp<GrD3DTexture>(tex);
78 }
79 
MakeWrappedTexture(GrD3DGpu * gpu,SkISize dimensions,GrWrapCacheable cacheable,GrIOType ioType,const GrD3DTextureResourceInfo & info,sk_sp<GrD3DResourceState> state)80 sk_sp<GrD3DTexture> GrD3DTexture::MakeWrappedTexture(GrD3DGpu* gpu,
81                                                      SkISize dimensions,
82                                                      GrWrapCacheable cacheable,
83                                                      GrIOType ioType,
84                                                      const GrD3DTextureResourceInfo& info,
85                                                      sk_sp<GrD3DResourceState> state) {
86     // TODO: If a client uses their own heap to allocate, how do we manage that?
87     // Adopted textures require both image and allocation because we're responsible for freeing
88     //SkASSERT(info.fTexture &&
89     //         (kBorrow_GrWrapOwnership == wrapOwnership || VK_NULL_HANDLE != info.fAlloc.fMemory));
90 
91     GrMipMapsStatus mipMapsStatus = info.fLevelCount > 1 ? GrMipMapsStatus::kValid
92                                                          : GrMipMapsStatus::kNotAllocated;
93 
94     return sk_sp<GrD3DTexture>(new GrD3DTexture(gpu, dimensions, info, std::move(state),
95                                                 mipMapsStatus, cacheable, ioType));
96 }
97 
onRelease()98 void GrD3DTexture::onRelease() {
99     // We're about to be severed from our GrManagedResource. If there are "finish" idle procs we
100     // have to decide who will handle them. If the resource is still tied to a command buffer we let
101     // it handle them. Otherwise, we handle them.
102     SkASSERT(this->resource());
103     if (this->resource()->isQueuedForWorkOnGpu()) {
104         this->removeFinishIdleProcs();
105     }
106 
107     this->releaseResource(this->getD3DGpu());
108 
109     INHERITED::onRelease();
110 }
111 
onAbandon()112 void GrD3DTexture::onAbandon() {
113     // We're about to be severed from our GrManagedResource. If there are "finish" idle procs we
114     // have to decide who will handle them. If the resource is still tied to a command buffer we let
115     // it handle them. Otherwise, we handle them.
116     SkASSERT(this->resource());
117     if (this->resource()->isQueuedForWorkOnGpu()) {
118         this->removeFinishIdleProcs();
119     }
120 
121     this->releaseResource(this->getD3DGpu());
122     INHERITED::onAbandon();
123 }
124 
getBackendTexture() const125 GrBackendTexture GrD3DTexture::getBackendTexture() const {
126     return GrBackendTexture(this->width(), this->height(), fInfo, this->grD3DResourceState());
127 }
128 
getD3DGpu() const129 GrD3DGpu* GrD3DTexture::getD3DGpu() const {
130     SkASSERT(!this->wasDestroyed());
131     return static_cast<GrD3DGpu*>(this->getGpu());
132 }
133 
addIdleProc(sk_sp<GrRefCntedCallback> idleProc,IdleState type)134 void GrD3DTexture::addIdleProc(sk_sp<GrRefCntedCallback> idleProc, IdleState type) {
135     INHERITED::addIdleProc(idleProc, type);
136     if (type == IdleState::kFinished) {
137         if (auto* resource = this->resource()) {
138             resource->addIdleProc(this, std::move(idleProc));
139         }
140     }
141 }
142 
callIdleProcsOnBehalfOfResource()143 void GrD3DTexture::callIdleProcsOnBehalfOfResource() {
144     // If we got here then the resource is being removed from its last command buffer and the
145     // texture is idle in the cache. Any kFlush idle procs should already have been called. So
146     // the texture and resource should have the same set of procs.
147     SkASSERT(this->resource());
148     SkASSERT(this->resource()->idleProcCnt() == fIdleProcs.count());
149 #ifdef SK_DEBUG
150     for (int i = 0; i < fIdleProcs.count(); ++i) {
151         SkASSERT(fIdleProcs[i] == this->resource()->idleProc(i));
152     }
153 #endif
154     fIdleProcs.reset();
155     this->resource()->resetIdleProcs();
156 }
157 
willRemoveLastRef()158 void GrD3DTexture::willRemoveLastRef() {
159     if (!fIdleProcs.count()) {
160         return;
161     }
162     // This is called when the GrTexture is purgeable. However, we need to check whether the
163     // Resource is still owned by any command buffers. If it is then it will call the proc.
164     auto* resource = this->resource();
165     SkASSERT(resource);
166     if (!resource->isQueuedForWorkOnGpu()) {
167         // Everything must go!
168         fIdleProcs.reset();
169         resource->resetIdleProcs();
170     } else {
171         // The procs that should be called on flush but not finish are those that are owned
172         // by the GrD3DTexture and not the Resource. We do this by copying the resource's array
173         // and thereby dropping refs to procs we own but the resource does not.
174         fIdleProcs.reset(resource->idleProcCnt());
175         for (int i = 0; i < fIdleProcs.count(); ++i) {
176             fIdleProcs[i] = resource->idleProc(i);
177         }
178     }
179 }
180 
removeFinishIdleProcs()181 void GrD3DTexture::removeFinishIdleProcs() {
182     // This should only be called by onRelease/onAbandon when we have already checked for a
183     // resource.
184     const auto* resource = this->resource();
185     SkASSERT(resource);
186     SkSTArray<4, sk_sp<GrRefCntedCallback>> procsToKeep;
187     int resourceIdx = 0;
188     // The idle procs that are common between the GrD3DTexture and its Resource should be found in
189     // the same order.
190     for (int i = 0; i < fIdleProcs.count(); ++i) {
191         if (fIdleProcs[i] == resource->idleProc(resourceIdx)) {
192             ++resourceIdx;
193         } else {
194             procsToKeep.push_back(fIdleProcs[i]);
195         }
196     }
197     SkASSERT(resourceIdx == resource->idleProcCnt());
198     fIdleProcs = procsToKeep;
199 }
200