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