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