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