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