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