• 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/ganesh/vk/GrVkBuffer.h"
9 
10 #include "include/gpu/GpuTypes.h"
11 #include "include/gpu/ganesh/GrDirectContext.h"
12 #include "include/gpu/vk/VulkanMemoryAllocator.h"
13 #include "include/private/base/SkAlign.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkDebug.h"
16 #include "include/private/base/SkTemplates.h"
17 #include "src/gpu/ganesh/GrDirectContextPriv.h"
18 #include "src/gpu/ganesh/GrResourceProvider.h"
19 #include "src/gpu/ganesh/vk/GrVkCaps.h"
20 #include "src/gpu/ganesh/vk/GrVkDescriptorSet.h"
21 #include "src/gpu/ganesh/vk/GrVkGpu.h"
22 #include "src/gpu/ganesh/vk/GrVkResourceProvider.h"
23 #include "src/gpu/ganesh/vk/GrVkUniformHandler.h"
24 #include "src/gpu/ganesh/vk/GrVkUtil.h"
25 #include "src/gpu/vk/VulkanMemory.h"
26 
27 #include <cstring>
28 #include <functional>
29 #include <utility>
30 
31 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
32 
GrVkBuffer(GrVkGpu * gpu,size_t sizeInBytes,GrGpuBufferType bufferType,GrAccessPattern accessPattern,VkBuffer buffer,const skgpu::VulkanAlloc & alloc,const GrVkDescriptorSet * uniformDescriptorSet,std::string_view label)33 GrVkBuffer::GrVkBuffer(GrVkGpu* gpu,
34                        size_t sizeInBytes,
35                        GrGpuBufferType bufferType,
36                        GrAccessPattern accessPattern,
37                        VkBuffer buffer,
38                        const skgpu::VulkanAlloc& alloc,
39                        const GrVkDescriptorSet* uniformDescriptorSet,
40                        std::string_view label)
41         : GrGpuBuffer(gpu, sizeInBytes, bufferType, accessPattern, label)
42         , fBuffer(buffer)
43         , fAlloc(alloc)
44         , fUniformDescriptorSet(uniformDescriptorSet) {
45     // We always require dynamic buffers to be mappable
46     SkASSERT(accessPattern != kDynamic_GrAccessPattern || this->isVkMappable());
47     SkASSERT(bufferType != GrGpuBufferType::kUniform || uniformDescriptorSet);
48     this->setRealAlloc(true); // OH ISSUE: set real alloc flag
49     this->registerWithCache(skgpu::Budgeted::kYes);
50 }
51 
make_uniform_desc_set(GrVkGpu * gpu,VkBuffer buffer,size_t size)52 static const GrVkDescriptorSet* make_uniform_desc_set(GrVkGpu* gpu, VkBuffer buffer, size_t size) {
53     const GrVkDescriptorSet* descriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
54     if (!descriptorSet) {
55         return nullptr;
56     }
57 
58     VkDescriptorBufferInfo bufferInfo;
59     memset(&bufferInfo, 0, sizeof(VkDescriptorBufferInfo));
60     bufferInfo.buffer = buffer;
61     bufferInfo.offset = 0;
62     bufferInfo.range = size;
63 
64     VkWriteDescriptorSet descriptorWrite;
65     memset(&descriptorWrite, 0, sizeof(VkWriteDescriptorSet));
66     descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
67     descriptorWrite.pNext = nullptr;
68     descriptorWrite.dstSet = *descriptorSet->descriptorSet();
69     descriptorWrite.dstBinding = GrVkUniformHandler::kUniformBinding;
70     descriptorWrite.dstArrayElement = 0;
71     descriptorWrite.descriptorCount = 1;
72     descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
73     descriptorWrite.pImageInfo = nullptr;
74     descriptorWrite.pBufferInfo = &bufferInfo;
75     descriptorWrite.pTexelBufferView = nullptr;
76 
77     GR_VK_CALL(gpu->vkInterface(),
78                UpdateDescriptorSets(gpu->device(), 1, &descriptorWrite, 0, nullptr));
79     return descriptorSet;
80 }
81 
Make(GrVkGpu * gpu,size_t size,GrGpuBufferType bufferType,GrAccessPattern accessPattern)82 sk_sp<GrVkBuffer> GrVkBuffer::Make(GrVkGpu* gpu,
83                                    size_t size,
84                                    GrGpuBufferType bufferType,
85                                    GrAccessPattern accessPattern) {
86     VkBuffer buffer;
87     skgpu::VulkanAlloc alloc;
88 
89     bool isProtected = gpu->protectedContext() &&
90                        accessPattern == kStatic_GrAccessPattern;
91 
92     // Protected memory _never_ uses mappable buffers.
93     // Otherwise, the only time we don't require mappable buffers is when we have a static
94     // access pattern and we're on a device where gpu only memory has faster reads on the gpu than
95     // memory that is also mappable on the cpu.
96     bool requiresMappable = !isProtected &&
97                             (accessPattern == kDynamic_GrAccessPattern ||
98                              accessPattern == kStream_GrAccessPattern ||
99                              !gpu->vkCaps().gpuOnlyBuffersMorePerformant());
100 
101     using BufferUsage = skgpu::VulkanMemoryAllocator::BufferUsage;
102     BufferUsage allocUsage;
103 
104     if (bufferType == GrGpuBufferType::kXferCpuToGpu) {
105         allocUsage = BufferUsage::kTransfersFromCpuToGpu;
106     } else if (bufferType == GrGpuBufferType::kXferGpuToCpu) {
107         allocUsage = BufferUsage::kTransfersFromGpuToCpu;
108     } else {
109         allocUsage = requiresMappable ? BufferUsage::kCpuWritesGpuReads : BufferUsage::kGpuOnly;
110     }
111 
112     // create the buffer object
113     VkBufferCreateInfo bufInfo;
114     memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
115     bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
116     bufInfo.flags = isProtected ? VK_BUFFER_CREATE_PROTECTED_BIT : 0;
117     bufInfo.size = size;
118     // To support SkMesh buffer updates we make Vertex and Index buffers capable of being transfer
119     // dsts.
120     switch (bufferType) {
121         case GrGpuBufferType::kVertex:
122             bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
123             break;
124         case GrGpuBufferType::kIndex:
125             bufInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
126             break;
127         case GrGpuBufferType::kDrawIndirect:
128             bufInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
129             break;
130         case GrGpuBufferType::kUniform:
131             bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
132             break;
133         case GrGpuBufferType::kXferCpuToGpu:
134             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
135             break;
136         case GrGpuBufferType::kXferGpuToCpu:
137             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
138             break;
139     }
140     // We may not always get a mappable buffer for non dynamic access buffers. Thus we set the
141     // transfer dst usage bit in case we need to do a copy to write data.
142     // TODO: It doesn't really hurt setting this extra usage flag, but maybe we can narrow the scope
143     // of buffers we set it on more than just not dynamic.
144     if (!requiresMappable) {
145         bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
146     }
147 
148     bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
149     bufInfo.queueFamilyIndexCount = 0;
150     bufInfo.pQueueFamilyIndices = nullptr;
151 
152     VkResult err;
153     err = VK_CALL(gpu, CreateBuffer(gpu->device(), &bufInfo, nullptr, &buffer));
154     if (err) {
155         return nullptr;
156     }
157 
158     bool shouldPersistentlyMapCpuToGpu = gpu->vkCaps().shouldPersistentlyMapCpuToGpuBuffers();
159     auto checkResult = [gpu, allocUsage, shouldPersistentlyMapCpuToGpu](VkResult result) {
160         GR_VK_LOG_IF_NOT_SUCCESS(gpu, result, "skgpu::VulkanMemory::AllocBufferMemory "
161                                  "(allocUsage:%d, shouldPersistentlyMapCpuToGpu:%d)",
162                                  (int)allocUsage, (int)shouldPersistentlyMapCpuToGpu);
163         return gpu->checkVkResult(result);
164     };
165     auto allocator = gpu->memoryAllocator();
166     if (!skgpu::VulkanMemory::AllocBufferMemory(allocator,
167                                                 buffer,
168                                                 skgpu::Protected(isProtected),
169                                                 allocUsage,
170                                                 shouldPersistentlyMapCpuToGpu,
171                                                 checkResult,
172 #ifdef SKIA_DFX_FOR_OHOS
173                                                 &alloc,
174                                                 size)) {
175 #else
176                                                 &alloc)) {
177 #endif
178         VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
179         return nullptr;
180     }
181 
182     // Bind buffer
183     GR_VK_CALL_RESULT(gpu, err, BindBufferMemory(gpu->device(),
184                                                  buffer,
185                                                  alloc.fMemory,
186                                                  alloc.fOffset));
187     if (err) {
188         if (alloc.fIsExternalMemory) {
189             skgpu::VulkanMemory::FreeBufferMemory(gpu, alloc);
190         } else {
191             skgpu::VulkanMemory::FreeBufferMemory(allocator, alloc);
192         }
193         VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
194         return nullptr;
195     }
196 
197     // If this is a uniform buffer we must setup a descriptor set
198     const GrVkDescriptorSet* uniformDescSet = nullptr;
199     if (bufferType == GrGpuBufferType::kUniform) {
200         uniformDescSet = make_uniform_desc_set(gpu, buffer, size);
201         if (!uniformDescSet) {
202             VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
203             if (alloc.fIsExternalMemory) {
204                 skgpu::VulkanMemory::FreeBufferMemory(gpu, alloc);
205             } else {
206                 skgpu::VulkanMemory::FreeBufferMemory(allocator, alloc);
207             }
208             return nullptr;
209         }
210     }
211 
212 #ifdef SKIA_DFX_FOR_OHOS
213     alloc.fBytes = size;
214     gpu->addAllocBufferBytes(size);
215 #endif
216 
217     return sk_sp<GrVkBuffer>(new GrVkBuffer(
218             gpu, size, bufferType, accessPattern, buffer, alloc, uniformDescSet,
219             /*label=*/"MakeVkBuffer"));
220 }
221 
222 sk_sp<GrVkBuffer> GrVkBuffer::MakeFromOHNativeBuffer(GrVkGpu* gpu,
223                                                      OH_NativeBuffer *nativeBuffer,
224                                                      size_t bufferSize,
225                                                      GrGpuBufferType bufferType,
226                                                      GrAccessPattern accessPattern) {
227     SkASSERT(gpu);
228     SkASSERT(nativeBuffer);
229 
230     VkBuffer buffer;
231     skgpu::VulkanAlloc alloc;
232 
233     // create the buffer object
234     VkBufferCreateInfo bufInfo{};
235     bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
236     bufInfo.flags = 0;
237     bufInfo.size = bufferSize;
238     switch (bufferType) {
239         case GrGpuBufferType::kVertex:
240             bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
241             break;
242         case GrGpuBufferType::kIndex:
243             bufInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
244             break;
245         case GrGpuBufferType::kDrawIndirect:
246             bufInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
247             break;
248         case GrGpuBufferType::kUniform:
249             bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
250             break;
251         case GrGpuBufferType::kXferCpuToGpu:
252             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
253             break;
254         case GrGpuBufferType::kXferGpuToCpu:
255             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
256             break;
257     }
258 
259     bool requiresMappable = gpu->protectedContext() ||
260                         accessPattern == kDynamic_GrAccessPattern ||
261                         accessPattern == kStream_GrAccessPattern ||
262                         !gpu->vkCaps().gpuOnlyBuffersMorePerformant();
263     if (!requiresMappable) {
264         bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
265     }
266 
267     bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
268     bufInfo.queueFamilyIndexCount = 0;
269     bufInfo.pQueueFamilyIndices = nullptr;
270 
271     VkResult err = VK_CALL(gpu, CreateBuffer(gpu->device(), &bufInfo, nullptr, &buffer));
272     if (err) {
273         return nullptr;
274     }
275 
276     if (!skgpu::VulkanMemory::ImportAndBindBufferMemory(gpu, nativeBuffer, buffer, &alloc)) {
277         VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
278         return nullptr;
279     }
280 
281     return sk_sp<GrVkBuffer>(new GrVkBuffer(
282             gpu, bufferSize, bufferType, accessPattern, buffer, alloc, nullptr,
283             /*label=*/"MakeVkBufferFromOHNativeBuffer"));
284 }
285 
286 void GrVkBuffer::vkMap(size_t readOffset, size_t readSize) {
287     SkASSERT(!fMapPtr);
288     if (this->isVkMappable()) {
289         // Not every buffer will use command buffer usage refs and instead the command buffer just
290         // holds normal refs. Systems higher up in Ganesh should be making sure not to reuse a
291         // buffer that currently has a ref held by something else. However, we do need to make sure
292         // there isn't a buffer with just a command buffer usage that is trying to be mapped.
293         SkASSERT(this->internalHasNoCommandBufferUsages());
294         SkASSERT(fAlloc.fSize > 0);
295         SkASSERT(fAlloc.fSize >= readOffset + readSize);
296 
297         GrVkGpu* gpu = this->getVkGpu();
298         auto checkResult_mapAlloc = [gpu](VkResult result) {
299             GR_VK_LOG_IF_NOT_SUCCESS(gpu, result, "skgpu::VulkanMemory::MapAlloc");
300             return gpu->checkVkResult(result);
301         };
302         auto allocator = gpu->memoryAllocator();
303         fMapPtr = skgpu::VulkanMemory::MapAlloc(allocator, fAlloc, checkResult_mapAlloc);
304         if (fMapPtr && readSize != 0) {
305             auto checkResult_invalidateMapAlloc = [gpu, readOffset, readSize](VkResult result) {
306                 GR_VK_LOG_IF_NOT_SUCCESS(gpu, result, "skgpu::VulkanMemory::InvalidateMappedAlloc "
307                                          "(readOffset:%zu, readSize:%zu)",
308                                          readOffset, readSize);
309                 return gpu->checkVkResult(result);
310             };
311             // "Invalidate" here means make device writes visible to the host. That is, it makes
312             // sure any GPU writes are finished in the range we might read from.
313             skgpu::VulkanMemory::InvalidateMappedAlloc(allocator,
314                                                        fAlloc,
315                                                        readOffset,
316                                                        readSize,
317                                                        checkResult_invalidateMapAlloc);
318         }
319     }
320 }
321 
322 void GrVkBuffer::vkUnmap(size_t flushOffset, size_t flushSize) {
323     SkASSERT(fMapPtr && this->isVkMappable());
324 
325     SkASSERT(fAlloc.fSize > 0);
326     SkASSERT(fAlloc.fSize >= flushOffset + flushSize);
327 
328     GrVkGpu* gpu = this->getVkGpu();
329     auto checkResult = [gpu, flushOffset, flushSize](VkResult result) {
330         GR_VK_LOG_IF_NOT_SUCCESS(gpu, result, "skgpu::VulkanMemory::FlushMappedAlloc "
331                                  "(flushOffset:%zu, flushSize:%zu)",
332                                  flushOffset, flushSize);
333         return gpu->checkVkResult(result);
334     };
335     auto allocator = this->getVkGpu()->memoryAllocator();
336     skgpu::VulkanMemory::FlushMappedAlloc(allocator, fAlloc, flushOffset, flushSize, checkResult);
337     skgpu::VulkanMemory::UnmapAlloc(allocator, fAlloc);
338 }
339 
340 void GrVkBuffer::copyCpuDataToGpuBuffer(const void* src, size_t offset, size_t size) {
341     SkASSERT(src);
342 
343     GrVkGpu* gpu = this->getVkGpu();
344 
345     // The vulkan api restricts the use of vkCmdUpdateBuffer to updates that are less than or equal
346     // to 65536 bytes and a size and offset that are both 4 byte aligned.
347     if ((size <= 65536) && SkIsAlign4(size) && SkIsAlign4(offset) &&
348         !gpu->vkCaps().avoidUpdateBuffers()) {
349         gpu->updateBuffer(sk_ref_sp(this), src, offset, size);
350     } else {
351         GrResourceProvider* resourceProvider = gpu->getContext()->priv().resourceProvider();
352         sk_sp<GrGpuBuffer> transferBuffer = resourceProvider->createBuffer(
353                 src,
354                 size,
355                 GrGpuBufferType::kXferCpuToGpu,
356                 kDynamic_GrAccessPattern);
357         if (!transferBuffer) {
358             return;
359         }
360 
361         gpu->transferFromBufferToBuffer(std::move(transferBuffer),
362                                         /*srcOffset=*/0,
363                                         sk_ref_sp(this),
364                                         offset,
365                                         size);
366     }
367 }
368 
369 void GrVkBuffer::addMemoryBarrier(VkAccessFlags srcAccessMask,
370                                   VkAccessFlags dstAccesMask,
371                                   VkPipelineStageFlags srcStageMask,
372                                   VkPipelineStageFlags dstStageMask,
373                                   bool byRegion) const {
374     VkBufferMemoryBarrier bufferMemoryBarrier = {
375             VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,  // sType
376             nullptr,                                  // pNext
377             srcAccessMask,                            // srcAccessMask
378             dstAccesMask,                             // dstAccessMask
379             VK_QUEUE_FAMILY_IGNORED,                  // srcQueueFamilyIndex
380             VK_QUEUE_FAMILY_IGNORED,                  // dstQueueFamilyIndex
381             fBuffer,                                  // buffer
382             0,                                        // offset
383             this->size(),                             // size
384     };
385 
386     // TODO: restrict to area of buffer we're interested in
387     this->getVkGpu()->addBufferMemoryBarrier(srcStageMask, dstStageMask, byRegion,
388                                              &bufferMemoryBarrier);
389 }
390 
391 void GrVkBuffer::vkRelease() {
392     if (this->wasDestroyed()) {
393         return;
394     }
395 
396     if (fMapPtr) {
397         this->vkUnmap(0, this->size());
398         fMapPtr = nullptr;
399     }
400 
401     if (fUniformDescriptorSet) {
402         fUniformDescriptorSet->recycle();
403         fUniformDescriptorSet = nullptr;
404     }
405 
406     SkASSERT(fBuffer);
407     SkASSERT(fAlloc.fMemory && fAlloc.fBackendMemory);
408     VK_CALL(this->getVkGpu(), DestroyBuffer(this->getVkGpu()->device(), fBuffer, nullptr));
409     fBuffer = VK_NULL_HANDLE;
410 
411 #ifdef SKIA_DFX_FOR_OHOS
412     this->getVkGpu()->removeAllocBufferBytes(fAlloc.fBytes);
413 #endif
414     if (fAlloc.fIsExternalMemory) {
415         skgpu::VulkanMemory::FreeBufferMemory(this->getVkGpu(), fAlloc);
416     } else {
417         skgpu::VulkanMemory::FreeBufferMemory(this->getVkGpu()->memoryAllocator(), fAlloc);
418     }
419     fAlloc.fMemory = VK_NULL_HANDLE;
420     fAlloc.fBackendMemory = 0;
421 }
422 
423 void GrVkBuffer::onRelease() {
424     this->vkRelease();
425     this->GrGpuBuffer::onRelease();
426 }
427 
428 void GrVkBuffer::onAbandon() {
429     this->vkRelease();
430     this->GrGpuBuffer::onAbandon();
431 }
432 
433 void GrVkBuffer::onMap(MapType type) {
434     this->vkMap(0, type == MapType::kRead ? this->size() : 0);
435 }
436 
437 void GrVkBuffer::onUnmap(MapType type) {
438     this->vkUnmap(0, type == MapType::kWriteDiscard ? this->size() : 0);
439 }
440 
441 bool GrVkBuffer::onClearToZero() { return this->getVkGpu()->zeroBuffer(sk_ref_sp(this)); }
442 
443 bool GrVkBuffer::onUpdateData(const void* src, size_t offset, size_t size, bool /*preserve*/) {
444     if (this->isVkMappable()) {
445         // We won't be reading the mapped memory so pass an empty range.
446         this->vkMap(0, 0);
447         if (!fMapPtr) {
448             return false;
449         }
450         memcpy(SkTAddOffset<void>(fMapPtr, offset), src, size);
451         // We only need to flush the updated portion so pass the true range here.
452         this->vkUnmap(offset, size);
453         fMapPtr = nullptr;
454     } else {
455         this->copyCpuDataToGpuBuffer(src, offset, size);
456     }
457     return true;
458 }
459 
460 GrVkGpu* GrVkBuffer::getVkGpu() const {
461     SkASSERT(!this->wasDestroyed());
462     return static_cast<GrVkGpu*>(this->getGpu());
463 }
464 
465 const VkDescriptorSet* GrVkBuffer::uniformDescriptorSet() const {
466     SkASSERT(fUniformDescriptorSet);
467     return fUniformDescriptorSet->descriptorSet();
468 }
469