• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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/vk/GrVkBuffer.h"
9 
10 #include "include/gpu/GrDirectContext.h"
11 #include "src/gpu/GrDirectContextPriv.h"
12 #include "src/gpu/GrResourceProvider.h"
13 #include "src/gpu/vk/GrVkDescriptorSet.h"
14 #include "src/gpu/vk/GrVkGpu.h"
15 #include "src/gpu/vk/GrVkMemory.h"
16 #include "src/gpu/vk/GrVkUtil.h"
17 
18 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
19 
GrVkBuffer(GrVkGpu * gpu,size_t sizeInBytes,GrGpuBufferType bufferType,GrAccessPattern accessPattern,VkBuffer buffer,const GrVkAlloc & alloc,const GrVkDescriptorSet * uniformDescriptorSet)20 GrVkBuffer::GrVkBuffer(GrVkGpu* gpu,
21                          size_t sizeInBytes,
22                          GrGpuBufferType bufferType,
23                          GrAccessPattern accessPattern,
24                          VkBuffer buffer,
25                          const GrVkAlloc& alloc,
26                          const GrVkDescriptorSet* uniformDescriptorSet)
27         : GrGpuBuffer(gpu, sizeInBytes, bufferType, accessPattern)
28         , fBuffer(buffer)
29         , fAlloc(alloc)
30         , fUniformDescriptorSet(uniformDescriptorSet) {
31     // We always require dynamic buffers to be mappable
32     SkASSERT(accessPattern != kDynamic_GrAccessPattern || this->isVkMappable());
33     SkASSERT(bufferType != GrGpuBufferType::kUniform || uniformDescriptorSet);
34     this->registerWithCache(SkBudgeted::kYes);
35 }
36 
make_uniform_desc_set(GrVkGpu * gpu,VkBuffer buffer,size_t size)37 static const GrVkDescriptorSet* make_uniform_desc_set(GrVkGpu* gpu, VkBuffer buffer, size_t size) {
38     const GrVkDescriptorSet* descriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
39     if (!descriptorSet) {
40         return nullptr;
41     }
42 
43     VkDescriptorBufferInfo bufferInfo;
44     memset(&bufferInfo, 0, sizeof(VkDescriptorBufferInfo));
45     bufferInfo.buffer = buffer;
46     bufferInfo.offset = 0;
47     bufferInfo.range = size;
48 
49     VkWriteDescriptorSet descriptorWrite;
50     memset(&descriptorWrite, 0, sizeof(VkWriteDescriptorSet));
51     descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
52     descriptorWrite.pNext = nullptr;
53     descriptorWrite.dstSet = *descriptorSet->descriptorSet();
54     descriptorWrite.dstBinding = GrVkUniformHandler::kUniformBinding;
55     descriptorWrite.dstArrayElement = 0;
56     descriptorWrite.descriptorCount = 1;
57     descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
58     descriptorWrite.pImageInfo = nullptr;
59     descriptorWrite.pBufferInfo = &bufferInfo;
60     descriptorWrite.pTexelBufferView = nullptr;
61 
62     GR_VK_CALL(gpu->vkInterface(),
63                UpdateDescriptorSets(gpu->device(), 1, &descriptorWrite, 0, nullptr));
64     return descriptorSet;
65 }
66 
Make(GrVkGpu * gpu,size_t size,GrGpuBufferType bufferType,GrAccessPattern accessPattern)67 sk_sp<GrVkBuffer> GrVkBuffer::Make(GrVkGpu* gpu,
68                                      size_t size,
69                                      GrGpuBufferType bufferType,
70                                      GrAccessPattern accessPattern) {
71     VkBuffer buffer;
72     GrVkAlloc alloc;
73 
74     // The only time we don't require mappable buffers is when we have a static access pattern and
75     // we're on a device where gpu only memory has faster reads on the gpu than memory that is also
76     // mappable on the cpu. Protected memory always uses mappable buffers.
77     bool requiresMappable = gpu->protectedContext() ||
78                             accessPattern == kDynamic_GrAccessPattern ||
79                             accessPattern == kStream_GrAccessPattern ||
80                             !gpu->vkCaps().gpuOnlyBuffersMorePerformant();
81 
82     using BufferUsage = GrVkMemoryAllocator::BufferUsage;
83     BufferUsage allocUsage;
84 
85     // create the buffer object
86     VkBufferCreateInfo bufInfo;
87     memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
88     bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
89     bufInfo.flags = 0;
90     bufInfo.size = size;
91     switch (bufferType) {
92         case GrGpuBufferType::kVertex:
93             bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
94             allocUsage = requiresMappable ? BufferUsage::kCpuWritesGpuReads : BufferUsage::kGpuOnly;
95             break;
96         case GrGpuBufferType::kIndex:
97             bufInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
98             allocUsage = requiresMappable ? BufferUsage::kCpuWritesGpuReads : BufferUsage::kGpuOnly;
99             break;
100         case GrGpuBufferType::kDrawIndirect:
101             bufInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
102             allocUsage = requiresMappable ? BufferUsage::kCpuWritesGpuReads : BufferUsage::kGpuOnly;
103             break;
104         case GrGpuBufferType::kUniform:
105             bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
106             allocUsage = BufferUsage::kCpuWritesGpuReads;
107             break;
108         case GrGpuBufferType::kXferCpuToGpu:
109             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
110             allocUsage = BufferUsage::kTransfersFromCpuToGpu;
111             break;
112         case GrGpuBufferType::kXferGpuToCpu:
113             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
114             allocUsage = BufferUsage::kTransfersFromGpuToCpu;
115             break;
116     }
117     // We may not always get a mappable buffer for non dynamic access buffers. Thus we set the
118     // transfer dst usage bit in case we need to do a copy to write data.
119     // TODO: It doesn't really hurt setting this extra usage flag, but maybe we can narrow the scope
120     // of buffers we set it on more than just not dynamic.
121     if (!requiresMappable) {
122         bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
123     }
124 
125     bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
126     bufInfo.queueFamilyIndexCount = 0;
127     bufInfo.pQueueFamilyIndices = nullptr;
128 
129     VkResult err;
130     err = VK_CALL(gpu, CreateBuffer(gpu->device(), &bufInfo, nullptr, &buffer));
131     if (err) {
132         return nullptr;
133     }
134 
135     if (!GrVkMemory::AllocAndBindBufferMemory(gpu, buffer, allocUsage, &alloc)) {
136         VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
137         return nullptr;
138     }
139 
140     // If this is a uniform buffer we must setup a descriptor set
141     const GrVkDescriptorSet* uniformDescSet = nullptr;
142     if (bufferType == GrGpuBufferType::kUniform) {
143         uniformDescSet = make_uniform_desc_set(gpu, buffer, size);
144         if (!uniformDescSet) {
145             VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
146             GrVkMemory::FreeBufferMemory(gpu, alloc);
147             return nullptr;
148         }
149     }
150 
151     return sk_sp<GrVkBuffer>(new GrVkBuffer(gpu, size, bufferType, accessPattern, buffer, alloc,
152                                               uniformDescSet));
153 }
154 
vkMap(size_t size)155 void GrVkBuffer::vkMap(size_t size) {
156     SkASSERT(!fMapPtr);
157     if (this->isVkMappable()) {
158         // Not every buffer will use command buffer usage refs and instead the command buffer just
159         // holds normal refs. Systems higher up in Ganesh should be making sure not to reuse a
160         // buffer that currently has a ref held by something else. However, we do need to make sure
161         // there isn't a buffer with just a command buffer usage that is trying to be mapped.
162         SkASSERT(this->internalHasNoCommandBufferUsages());
163         SkASSERT(fAlloc.fSize > 0);
164         SkASSERT(fAlloc.fSize >= size);
165         fMapPtr = GrVkMemory::MapAlloc(this->getVkGpu(), fAlloc);
166         if (fMapPtr && this->intendedType() == GrGpuBufferType::kXferGpuToCpu) {
167             GrVkMemory::InvalidateMappedAlloc(this->getVkGpu(), fAlloc, 0, size);
168         }
169     }
170 }
171 
vkUnmap(size_t size)172 void GrVkBuffer::vkUnmap(size_t size) {
173     SkASSERT(fMapPtr && this->isVkMappable());
174 
175     SkASSERT(fAlloc.fSize > 0);
176     SkASSERT(fAlloc.fSize >= size);
177 
178     GrVkGpu* gpu = this->getVkGpu();
179     GrVkMemory::FlushMappedAlloc(gpu, fAlloc, 0, size);
180     GrVkMemory::UnmapAlloc(gpu, fAlloc);
181 }
182 
buffer_type_to_access_flags(GrGpuBufferType type)183 static VkAccessFlags buffer_type_to_access_flags(GrGpuBufferType type) {
184     switch (type) {
185         case GrGpuBufferType::kIndex:
186             return VK_ACCESS_INDEX_READ_BIT;
187         case GrGpuBufferType::kVertex:
188             return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
189         default:
190             // This helper is only called for static buffers so we should only ever see index or
191             // vertex buffers types
192             SkUNREACHABLE;
193     }
194 }
195 
copyCpuDataToGpuBuffer(const void * src,size_t size)196 void GrVkBuffer::copyCpuDataToGpuBuffer(const void* src, size_t size) {
197     SkASSERT(src);
198 
199     GrVkGpu* gpu = this->getVkGpu();
200 
201     // We should never call this method in protected contexts.
202     SkASSERT(!gpu->protectedContext());
203 
204     // The vulkan api restricts the use of vkCmdUpdateBuffer to updates that are less than or equal
205     // to 65536 bytes and a size the is 4 byte aligned.
206     if ((size <= 65536) && (0 == (size & 0x3)) && !gpu->vkCaps().avoidUpdateBuffers()) {
207         gpu->updateBuffer(sk_ref_sp(this), src, /*offset=*/0, size);
208     } else {
209         GrResourceProvider* resourceProvider = gpu->getContext()->priv().resourceProvider();
210         sk_sp<GrGpuBuffer> transferBuffer = resourceProvider->createBuffer(
211                 size, GrGpuBufferType::kXferCpuToGpu, kDynamic_GrAccessPattern, src);
212         if (!transferBuffer) {
213             return;
214         }
215 
216         gpu->copyBuffer(std::move(transferBuffer), sk_ref_sp(this), /*srcOffset=*/0,
217                         /*dstOffset=*/0, size);
218     }
219 
220     this->addMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT,
221                            buffer_type_to_access_flags(this->intendedType()),
222                            VK_PIPELINE_STAGE_TRANSFER_BIT,
223                            VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
224                            /*byRegion=*/false);
225 }
226 
addMemoryBarrier(VkAccessFlags srcAccessMask,VkAccessFlags dstAccesMask,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,bool byRegion) const227 void GrVkBuffer::addMemoryBarrier(VkAccessFlags srcAccessMask,
228                                   VkAccessFlags dstAccesMask,
229                                   VkPipelineStageFlags srcStageMask,
230                                   VkPipelineStageFlags dstStageMask,
231                                   bool byRegion) const {
232     VkBufferMemoryBarrier bufferMemoryBarrier = {
233             VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,  // sType
234             nullptr,                                  // pNext
235             srcAccessMask,                            // srcAccessMask
236             dstAccesMask,                             // dstAccessMask
237             VK_QUEUE_FAMILY_IGNORED,                  // srcQueueFamilyIndex
238             VK_QUEUE_FAMILY_IGNORED,                  // dstQueueFamilyIndex
239             fBuffer,                                  // buffer
240             0,                                        // offset
241             this->size(),                             // size
242     };
243 
244     // TODO: restrict to area of buffer we're interested in
245     this->getVkGpu()->addBufferMemoryBarrier(srcStageMask, dstStageMask, byRegion,
246                                              &bufferMemoryBarrier);
247 }
248 
vkRelease()249 void GrVkBuffer::vkRelease() {
250     if (this->wasDestroyed()) {
251         return;
252     }
253 
254     if (fMapPtr) {
255         this->vkUnmap(this->size());
256         fMapPtr = nullptr;
257     }
258 
259     if (fUniformDescriptorSet) {
260         fUniformDescriptorSet->recycle();
261         fUniformDescriptorSet = nullptr;
262     }
263 
264     SkASSERT(fBuffer);
265     SkASSERT(fAlloc.fMemory && fAlloc.fBackendMemory);
266     VK_CALL(this->getVkGpu(), DestroyBuffer(this->getVkGpu()->device(), fBuffer, nullptr));
267     fBuffer = VK_NULL_HANDLE;
268 
269     GrVkMemory::FreeBufferMemory(this->getVkGpu(), fAlloc);
270     fAlloc.fMemory = VK_NULL_HANDLE;
271     fAlloc.fBackendMemory = 0;
272 }
273 
onRelease()274 void GrVkBuffer::onRelease() {
275     this->vkRelease();
276     this->GrGpuBuffer::onRelease();
277 }
278 
onAbandon()279 void GrVkBuffer::onAbandon() {
280     this->vkRelease();
281     this->GrGpuBuffer::onAbandon();
282 }
283 
onMap()284 void GrVkBuffer::onMap() {
285     if (!this->wasDestroyed()) {
286         this->vkMap(this->size());
287     }
288 }
289 
onUnmap()290 void GrVkBuffer::onUnmap() {
291     if (!this->wasDestroyed()) {
292         this->vkUnmap(this->size());
293     }
294 }
295 
onUpdateData(const void * src,size_t srcSizeInBytes)296 bool GrVkBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
297     if (this->wasDestroyed()) {
298         return false;
299     }
300 
301     if (srcSizeInBytes > this->size()) {
302         return false;
303     }
304 
305     if (this->isVkMappable()) {
306         this->vkMap(srcSizeInBytes);
307         if (!fMapPtr) {
308             return false;
309         }
310         memcpy(fMapPtr, src, srcSizeInBytes);
311         this->vkUnmap(srcSizeInBytes);
312         fMapPtr = nullptr;
313     } else {
314         this->copyCpuDataToGpuBuffer(src, srcSizeInBytes);
315     }
316     return true;
317 }
318 
getVkGpu() const319 GrVkGpu* GrVkBuffer::getVkGpu() const {
320     SkASSERT(!this->wasDestroyed());
321     return static_cast<GrVkGpu*>(this->getGpu());
322 }
323 
uniformDescriptorSet() const324 const VkDescriptorSet* GrVkBuffer::uniformDescriptorSet() const {
325     SkASSERT(fUniformDescriptorSet);
326     return fUniformDescriptorSet->descriptorSet();
327 }
328 
329