• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
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 "GrVkBuffer.h"
9 #include "GrVkGpu.h"
10 #include "GrVkMemory.h"
11 #include "GrVkTransferBuffer.h"
12 #include "GrVkUtil.h"
13 
14 #define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
15 
16 #ifdef SK_DEBUG
17 #define VALIDATE() this->validate()
18 #else
19 #define VALIDATE() do {} while(false)
20 #endif
21 
Create(const GrVkGpu * gpu,const Desc & desc)22 const GrVkBuffer::Resource* GrVkBuffer::Create(const GrVkGpu* gpu, const Desc& desc) {
23     VkBuffer       buffer;
24     GrVkAlloc      alloc;
25 
26     // create the buffer object
27     VkBufferCreateInfo bufInfo;
28     memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
29     bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
30     bufInfo.flags = 0;
31     bufInfo.size = desc.fSizeInBytes;
32     switch (desc.fType) {
33         case kVertex_Type:
34             bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
35             break;
36         case kIndex_Type:
37             bufInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
38             break;
39         case kUniform_Type:
40             bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
41             break;
42         case kCopyRead_Type:
43             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
44             break;
45         case kCopyWrite_Type:
46             bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
47             break;
48         case kTexel_Type:
49             bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
50     }
51     if (!desc.fDynamic) {
52         bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
53     }
54 
55     bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
56     bufInfo.queueFamilyIndexCount = 0;
57     bufInfo.pQueueFamilyIndices = nullptr;
58 
59     VkResult err;
60     err = VK_CALL(gpu, CreateBuffer(gpu->device(), &bufInfo, nullptr, &buffer));
61     if (err) {
62         return nullptr;
63     }
64 
65     if (!GrVkMemory::AllocAndBindBufferMemory(gpu,
66                                               buffer,
67                                               desc.fType,
68                                               desc.fDynamic,
69                                               &alloc)) {
70         return nullptr;
71     }
72 
73     const GrVkBuffer::Resource* resource = new GrVkBuffer::Resource(buffer, alloc, desc.fType);
74     if (!resource) {
75         VK_CALL(gpu, DestroyBuffer(gpu->device(), buffer, nullptr));
76         GrVkMemory::FreeBufferMemory(gpu, desc.fType, alloc);
77         return nullptr;
78     }
79 
80     return resource;
81 }
82 
addMemoryBarrier(const GrVkGpu * gpu,VkAccessFlags srcAccessMask,VkAccessFlags dstAccesMask,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,bool byRegion) const83 void GrVkBuffer::addMemoryBarrier(const GrVkGpu* gpu,
84                                   VkAccessFlags srcAccessMask,
85                                   VkAccessFlags dstAccesMask,
86                                   VkPipelineStageFlags srcStageMask,
87                                   VkPipelineStageFlags dstStageMask,
88                                   bool byRegion) const {
89     VkBufferMemoryBarrier bufferMemoryBarrier = {
90         VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
91         nullptr,                                 // pNext
92         srcAccessMask,                           // srcAccessMask
93         dstAccesMask,                            // dstAccessMask
94         VK_QUEUE_FAMILY_IGNORED,                 // srcQueueFamilyIndex
95         VK_QUEUE_FAMILY_IGNORED,                 // dstQueueFamilyIndex
96         this->buffer(),                          // buffer
97         0,                                       // offset
98         fDesc.fSizeInBytes,                      // size
99     };
100 
101     // TODO: restrict to area of buffer we're interested in
102     gpu->addBufferMemoryBarrier(srcStageMask, dstStageMask, byRegion, &bufferMemoryBarrier);
103 }
104 
freeGPUData(const GrVkGpu * gpu) const105 void GrVkBuffer::Resource::freeGPUData(const GrVkGpu* gpu) const {
106     SkASSERT(fBuffer);
107     SkASSERT(fAlloc.fMemory);
108     VK_CALL(gpu, DestroyBuffer(gpu->device(), fBuffer, nullptr));
109     GrVkMemory::FreeBufferMemory(gpu, fType, fAlloc);
110 }
111 
vkRelease(const GrVkGpu * gpu)112 void GrVkBuffer::vkRelease(const GrVkGpu* gpu) {
113     VALIDATE();
114     fResource->recycle(const_cast<GrVkGpu*>(gpu));
115     fResource = nullptr;
116     if (!fDesc.fDynamic) {
117         delete[] (unsigned char*)fMapPtr;
118     }
119     fMapPtr = nullptr;
120     VALIDATE();
121 }
122 
vkAbandon()123 void GrVkBuffer::vkAbandon() {
124     fResource->unrefAndAbandon();
125     fResource = nullptr;
126     if (!fDesc.fDynamic) {
127         delete[] (unsigned char*)fMapPtr;
128     }
129     fMapPtr = nullptr;
130     VALIDATE();
131 }
132 
buffer_type_to_access_flags(GrVkBuffer::Type type)133 VkAccessFlags buffer_type_to_access_flags(GrVkBuffer::Type type) {
134     switch (type) {
135         case GrVkBuffer::kIndex_Type:
136             return VK_ACCESS_INDEX_READ_BIT;
137         case GrVkBuffer::kVertex_Type:
138             return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
139         default:
140             // This helper is only called for static buffers so we should only ever see index or
141             // vertex buffers types
142             SkASSERT(false);
143             return 0;
144     }
145 }
146 
internalMap(GrVkGpu * gpu,size_t size,bool * createdNewBuffer)147 void GrVkBuffer::internalMap(GrVkGpu* gpu, size_t size, bool* createdNewBuffer) {
148     VALIDATE();
149     SkASSERT(!this->vkIsMapped());
150 
151     if (!fResource->unique()) {
152         if (fDesc.fDynamic) {
153             // in use by the command buffer, so we need to create a new one
154             fResource->recycle(gpu);
155             fResource = this->createResource(gpu, fDesc);
156             if (createdNewBuffer) {
157                 *createdNewBuffer = true;
158             }
159         } else {
160             SkASSERT(fMapPtr);
161             this->addMemoryBarrier(gpu,
162                                    buffer_type_to_access_flags(fDesc.fType),
163                                    VK_ACCESS_TRANSFER_WRITE_BIT,
164                                    VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
165                                    VK_PIPELINE_STAGE_TRANSFER_BIT,
166                                    false);
167         }
168     }
169 
170     if (fDesc.fDynamic) {
171         const GrVkAlloc& alloc = this->alloc();
172         SkASSERT(alloc.fSize > 0);
173 
174         // For Noncoherent buffers we want to make sure the range that we map, both offset and size,
175         // are aligned to the nonCoherentAtomSize limit. The offset should have been correctly
176         // aligned by our memory allocator. For size we pad out to make the range also aligned.
177         if (SkToBool(alloc.fFlags & GrVkAlloc::kNoncoherent_Flag)) {
178             // Currently we always have the internal offset as 0.
179             SkASSERT(0 == fOffset);
180             VkDeviceSize alignment = gpu->physicalDeviceProperties().limits.nonCoherentAtomSize;
181             SkASSERT(0 == (alloc.fOffset & (alignment - 1)));
182 
183             // Make size of the map aligned to nonCoherentAtomSize
184             size = (size + alignment - 1) & ~(alignment - 1);
185             fMappedSize = size;
186         }
187         SkASSERT(size + fOffset <= alloc.fSize);
188         VkResult err = VK_CALL(gpu, MapMemory(gpu->device(), alloc.fMemory,
189                                               alloc.fOffset + fOffset,
190                                               size, 0, &fMapPtr));
191         if (err) {
192             fMapPtr = nullptr;
193             fMappedSize = 0;
194         }
195     } else {
196         if (!fMapPtr) {
197             fMapPtr = new unsigned char[this->size()];
198         }
199     }
200 
201     VALIDATE();
202 }
203 
internalUnmap(GrVkGpu * gpu,size_t size)204 void GrVkBuffer::internalUnmap(GrVkGpu* gpu, size_t size) {
205     VALIDATE();
206     SkASSERT(this->vkIsMapped());
207 
208     if (fDesc.fDynamic) {
209         // We currently don't use fOffset
210         SkASSERT(0 == fOffset);
211         VkDeviceSize flushOffset = this->alloc().fOffset + fOffset;
212         VkDeviceSize flushSize = gpu->vkCaps().canUseWholeSizeOnFlushMappedMemory() ? VK_WHOLE_SIZE
213                                                                                     : fMappedSize;
214 
215         GrVkMemory::FlushMappedAlloc(gpu, this->alloc(), flushOffset, flushSize);
216         VK_CALL(gpu, UnmapMemory(gpu->device(), this->alloc().fMemory));
217         fMapPtr = nullptr;
218         fMappedSize = 0;
219     } else {
220         // vkCmdUpdateBuffer requires size < 64k and 4-byte alignment.
221         // https://bugs.chromium.org/p/skia/issues/detail?id=7488
222         if (size <= 65536 && 0 == (size & 0x3)) {
223             gpu->updateBuffer(this, fMapPtr, this->offset(), size);
224         } else {
225             GrVkTransferBuffer* transferBuffer =
226                     GrVkTransferBuffer::Create(gpu, size, GrVkBuffer::kCopyRead_Type);
227             if(!transferBuffer) {
228                 return;
229             }
230 
231             char* buffer = (char*) transferBuffer->map();
232             memcpy (buffer, fMapPtr, size);
233             transferBuffer->unmap();
234 
235             gpu->copyBuffer(transferBuffer, this, 0, this->offset(), size);
236             transferBuffer->unref();
237         }
238         this->addMemoryBarrier(gpu,
239                                VK_ACCESS_TRANSFER_WRITE_BIT,
240                                buffer_type_to_access_flags(fDesc.fType),
241                                VK_PIPELINE_STAGE_TRANSFER_BIT,
242                                VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
243                                false);
244     }
245 }
246 
vkIsMapped() const247 bool GrVkBuffer::vkIsMapped() const {
248     VALIDATE();
249     return SkToBool(fMapPtr);
250 }
251 
vkUpdateData(GrVkGpu * gpu,const void * src,size_t srcSizeInBytes,bool * createdNewBuffer)252 bool GrVkBuffer::vkUpdateData(GrVkGpu* gpu, const void* src, size_t srcSizeInBytes,
253                               bool* createdNewBuffer) {
254     if (srcSizeInBytes > fDesc.fSizeInBytes) {
255         return false;
256     }
257 
258     this->internalMap(gpu, srcSizeInBytes, createdNewBuffer);
259     if (!fMapPtr) {
260         return false;
261     }
262 
263     memcpy(fMapPtr, src, srcSizeInBytes);
264 
265     this->internalUnmap(gpu, srcSizeInBytes);
266 
267     return true;
268 }
269 
validate() const270 void GrVkBuffer::validate() const {
271     SkASSERT(!fResource || kVertex_Type == fDesc.fType || kIndex_Type == fDesc.fType
272              || kTexel_Type == fDesc.fType || kCopyRead_Type == fDesc.fType
273              || kCopyWrite_Type == fDesc.fType || kUniform_Type == fDesc.fType);
274 }
275