Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
vkBufferImpl.cpp
Go to the documentation of this file.
1
2
8
9namespace Divide
10{
12 {
13 VertexInputDescription description;
14
15 for ( const VertexBinding& vertBinding : vertexFormat._vertexBindings )
16 {
17 VkVertexInputBindingDescription mainBinding = {};
18 mainBinding.binding = vertBinding._bufferBindIndex;
19 mainBinding.stride = to_U32( vertBinding._strideInBytes );
20 mainBinding.inputRate = vertBinding._perVertexInputRate ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
21
22 description.bindings.push_back( mainBinding );
23 }
24
25 for ( U8 idx = 0u; idx < to_base( AttribLocation::COUNT ); ++idx )
26 {
27 const AttributeDescriptor& descriptor = vertexFormat._attributes[idx];
28 if ( descriptor._dataType == GFXDataFormat::COUNT )
29 {
30 continue;
31 }
32
33 VkVertexInputAttributeDescription attribute = {};
34 attribute.binding = descriptor._vertexBindingIndex;
35 attribute.location = idx;
36 attribute.format = VKUtil::InternalFormat( descriptor._dataType, descriptor._componentsPerElement, descriptor._normalized );
37 attribute.offset = to_U32( descriptor._strideInBytes );
38
39 description.attributes.push_back( attribute );
40 }
41
42 return description;
43 }
44
46 : _params(params)
47 {
48 }
49
51 {
52 if ( _buffer != VK_NULL_HANDLE )
53 {
54 VK_API::RegisterCustomAPIDelete( [buf = _buffer, alloc = _allocation]( [[maybe_unused]] VkDevice device )
55 {
56 LockGuard<Mutex> w_lock( VK_API::GetStateTracker()._allocatorInstance._allocatorLock );
57 vmaDestroyBuffer( *VK_API::GetStateTracker()._allocatorInstance._allocator, buf, alloc );
58 }, true );
59 }
60 }
61
63 const size_t alignedBufferSize,
64 const size_t ringQueueLength,
65 std::pair<bufferPtr, size_t> initialData,
66 const char* bufferName ) noexcept
67 : VMABuffer(params)
68 , _alignedBufferSize( alignedBufferSize )
69 , _totalBufferSize( _alignedBufferSize * ringQueueLength )
70 {
71 _lockManager = std::make_unique<LockManager>();
72
73 VkAccessFlags2 dstAccessMask = VK_ACCESS_2_NONE;
74 VkPipelineStageFlags2 dstStageMask = VK_PIPELINE_STAGE_2_NONE;
75
76 VkBufferUsageFlags usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
77
78 switch ( _params._flags._usageType )
79 {
81 {
82 usageFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
83 usageFlags &= ~VK_BUFFER_USAGE_TRANSFER_DST_BIT;
84 dstAccessMask = VK_ACCESS_2_HOST_READ_BIT;
85 dstStageMask = VK_PIPELINE_STAGE_2_NONE;
86 } break;
88 {
89 usageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
90 dstAccessMask = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT;
91 dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT;
92 } break;
94 {
95 usageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
96 dstAccessMask = VK_ACCESS_2_INDEX_READ_BIT;
97 dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT;
98 } break;
100 {
101 usageFlags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
102 dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
103 dstStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
104 } break;
106 {
107 usageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
108 dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
109 dstStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
110 } break;
112 {
113 usageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
114 usageFlags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
115 dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT;
116 dstStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT;
117 } break;
118 default: DIVIDE_UNEXPECTED_CALL(); break;
119 }
120
121 // Let the VMA library know that this buffer should be readable by the GPU only
122 VmaAllocationCreateInfo vmaallocInfo{};
123 vmaallocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
124 vmaallocInfo.flags = 0;
125 if ( _params._flags._updateFrequency != BufferUpdateFrequency::ONCE )
126 {
127 // If we write to this buffer often (e.g. GPU uniform blocks), we might as well persistently map it and use
128 // a lock manager to protect writes (same as GL_API's lockManager system)
129 // A staging buffer is just way too slow for multiple writes per frame.
130 vmaallocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
131 }
132 else if ( _params._hostVisible )
133 {
134 vmaallocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
135 }
136 // Allocate the vertex buffer (as a vb buffer and transfer destination)
137 VkBufferCreateInfo bufferInfo = vk::bufferCreateInfo( usageFlags, _totalBufferSize );
138 {
139 LockGuard<Mutex> w_lock( VK_API::GetStateTracker()._allocatorInstance._allocatorLock );
140 VK_CHECK( vmaCreateBuffer( *VK_API::GetStateTracker()._allocatorInstance._allocator,
141 &bufferInfo,
142 &vmaallocInfo,
143 &_buffer,
144 &_allocation,
145 &_allocInfo ) );
146
147 VkMemoryPropertyFlags memPropFlags;
148 vmaGetAllocationMemoryProperties( *VK_API::GetStateTracker()._allocatorInstance._allocator,
149 _allocation,
150 &memPropFlags );
151
152 vmaSetAllocationName( *VK_API::GetStateTracker()._allocatorInstance._allocator, _allocation, bufferName );
153
154 Debug::SetObjectName( VK_API::GetStateTracker()._device->getVKDevice(), (uint64_t)_buffer, VK_OBJECT_TYPE_BUFFER, bufferName );
155 _isMemoryMappable = memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
156 }
157
158 _isLockable = _isMemoryMappable;
159
160 Byte* mappedRange = nullptr;
161 if (!_isMemoryMappable)
162 {
163 _stagingBuffer = VKUtil::createStagingBuffer( _totalBufferSize, bufferName, false );
164 mappedRange = (Byte*)_stagingBuffer->_allocInfo.pMappedData;
165 }
166 else
167 {
168 mappedRange = (Byte*)_allocInfo.pMappedData;
169 }
170
171 const size_t localDataSize = initialData.second > 0 ? initialData.second : _alignedBufferSize;
172 for ( U32 i = 0u; i < ringQueueLength; ++i )
173 {
174 if ( initialData.first == nullptr )
175 {
176 memset( &mappedRange[i * _alignedBufferSize], 0, _alignedBufferSize );
177 }
178 else
179 {
180 memcpy( &mappedRange[i * _alignedBufferSize], initialData.first, localDataSize );
181 }
182
183 }
184
185 if ( !_isMemoryMappable )
186 {
187 const auto scopeName = Util::StringFormat( "Immediate Buffer Upload [ {} ]", bufferName );
188
189 // Queue a command to copy from the staging buffer to the vertex buffer
191 request.srcOffset = 0u;
192 request.dstOffset = 0u;
193 request.size = _totalBufferSize;
194 request.srcBuffer = _stagingBuffer->_buffer;
195 request.dstBuffer = _buffer;
196 request.dstAccessMask = dstAccessMask;
197 request.dstStageMask = dstStageMask;
198 VK_API::GetStateTracker().IMCmdContext(QueueType::GRAPHICS)->flushCommandBuffer([&request](VkCommandBuffer cmd, [[maybe_unused]] const QueueType queue, [[maybe_unused]] const bool isDedicatedQueue)
199 {
200 VK_API::SubmitTransferRequest( request, cmd );
201 }, scopeName.c_str() );
202 }
203
204 if ( _params._flags._updateFrequency == BufferUpdateFrequency::ONCE || _isMemoryMappable)
205 {
206 _stagingBuffer.reset();
207 }
208 else
209 {
210 // Try and recover some VRAM
211 _stagingBuffer = VKUtil::createStagingBuffer( alignedBufferSize, bufferName, false );
212 }
213 }
214
216 VkAccessFlags2 dstAccessMask,
217 VkPipelineStageFlags2 dstStageMask,
218 const bufferPtr data)
219 {
220
221 Byte* mappedRange = nullptr;
222 size_t mappedOffset = 0u;
223 if ( !_isMemoryMappable )
224 {
225 DIVIDE_ASSERT(_stagingBuffer != nullptr && _stagingBuffer->_params._elementSize >= range._length);
226 mappedRange = (Byte*)_stagingBuffer->_allocInfo.pMappedData;
227 }
228 else
229 {
230 mappedOffset = range._startOffset;
231 mappedRange = (Byte*)_allocInfo.pMappedData;
232 }
233
234 if ( data == nullptr )
235 {
236 memset( &mappedRange[mappedOffset], 0, range._length );
237 }
238 else
239 {
240 memcpy( &mappedRange[mappedOffset], data, range._length );
241 }
242
243 BufferLock ret = {
245 ._buffer = this
246 };
247
248 if ( !_isMemoryMappable )
249 {
250 // Queue a command to copy from the staging buffer to the vertex buffer
252 request.srcOffset = mappedOffset;
253 request.dstOffset = range._startOffset;
254 request.size = range._length;
255 request.srcBuffer = _stagingBuffer->_buffer;
256 request.dstBuffer = _buffer;
257 request.dstAccessMask = dstAccessMask;
258 request.dstStageMask = dstStageMask;
260 }
261 else
262 {
263 ret._range = range;
264 }
265
266 return ret;
267 }
268
269 void vkBufferImpl::readBytes( const BufferRange range, std::pair<bufferPtr, size_t> outData )
270 {
272 {
273 Byte* mappedRange = (Byte*)_allocInfo.pMappedData;
274 memcpy( outData.first, &mappedRange[range._startOffset], range._length );
275 }
276 else
277 {
278 void* mappedData;
279 LockGuard<Mutex> w_lock( VK_API::GetStateTracker()._allocatorInstance._allocatorLock );
280 vmaMapMemory( *VK_API::GetStateTracker()._allocatorInstance._allocator, _allocation, &mappedData );
281
282 Byte* mappedRange = (Byte*)mappedData;
283 memcpy( outData.first, &mappedRange[range._startOffset], range._length );
284
285 vmaUnmapMemory( *VK_API::GetStateTracker()._allocatorInstance._allocator, _allocation );
286 }
287 }
288
289 namespace VKUtil
290 {
291
292 VMABuffer_uptr createStagingBuffer( const size_t size, const std::string_view bufferName, const bool isCopySource )
293 {
294 BufferParams params{};
296 params._flags._updateFrequency = BufferUpdateFrequency::OFTEN;
297 params._flags._updateUsage = BufferUpdateUsage::CPU_TO_GPU;
298 params._elementCount = 1u;
299 params._elementSize = size;
300
301 VMABuffer_uptr ret = std::make_unique<VMABuffer>( params );
302
303 VmaAllocationCreateInfo vmaallocInfo = {};
304 // Let the VMA library know that this data should be writable by CPU only
305 vmaallocInfo.usage = VMA_MEMORY_USAGE_AUTO;
306 vmaallocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
307
308 // Allocate staging buffer
309 const VkBufferCreateInfo bufferInfo = vk::bufferCreateInfo( isCopySource ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : VK_BUFFER_USAGE_TRANSFER_SRC_BIT, size );
310 // Allocate the buffer
311 LockGuard<Mutex> w_lock( VK_API::GetStateTracker()._allocatorInstance._allocatorLock );
312 VK_CHECK( vmaCreateBuffer( *VK_API::GetStateTracker()._allocatorInstance._allocator,
313 &bufferInfo,
314 &vmaallocInfo,
315 &ret->_buffer,
316 &ret->_allocation,
317 &ret->_allocInfo ) );
318
319 Debug::SetObjectName( VK_API::GetStateTracker()._device->getVKDevice(), (uint64_t)ret->_buffer, VK_OBJECT_TYPE_BUFFER, Util::StringFormat( "{}_staging_buffer", bufferName.data() ).c_str() );
320
321 return ret;
322 }
323 } //namespace VKUtil
324}; //namespace Divide
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
static void RegisterTransferRequest(const VKTransferQueue::TransferRequest &request)
Definition: VKWrapper.cpp:549
static void RegisterCustomAPIDelete(DELEGATE< void, VkDevice > &&cbk, bool isResourceTransient)
Definition: VKWrapper.cpp:537
static VKStateTracker & GetStateTracker() noexcept
Definition: VKWrapper.cpp:3088
static void SubmitTransferRequest(const VKTransferQueue::TransferRequest &request, VkCommandBuffer cmd)
Definition: VKWrapper.cpp:2149
void SetObjectName(VkDevice device, uint64_t object, VkObjectType objectType, const char *name)
Definition: vkResources.cpp:35
Str StringFormat(const char *fmt, Args &&...args)
VMABuffer_uptr createStagingBuffer(size_t size, std::string_view bufferName, const bool isCopySource)
VkFormat InternalFormat(GFXImageFormat baseFormat, GFXDataFormat dataType, GFXImagePacking packing) noexcept
VkBufferCreateInfo bufferCreateInfo()
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
std::byte Byte
constexpr U32 to_U32(const T value)
uint8_t U8
VertexInputDescription getVertexDescription(const AttributeMap &vertexFormat)
uint32_t U32
void * bufferPtr
constexpr auto to_base(const Type value) -> Type
VertexBindings _vertexBindings
BufferUsageType _usageType
Definition: BufferParams.h:41
BufferRange _range
Definition: BufferLocks.h:57
BufferSyncUsage _type
Definition: BufferLocks.h:58
BufferFlags _flags
Definition: BufferParams.h:48
void flushCommandBuffer(FlushCallback &&function, const char *scopeName)
Definition: VKWrapper.cpp:459
VKImmediateCmdContext * IMCmdContext(QueueType type) const
Definition: VKWrapper.cpp:532
VmaAllocationInfo _allocInfo
Definition: vkBufferImpl.h:59
VMABuffer(BufferParams params)
VmaAllocation _allocation
Definition: vkBufferImpl.h:58
vector< VkVertexInputBindingDescription > bindings
Definition: vkBufferImpl.h:43
vector< VkVertexInputAttributeDescription > attributes
Definition: vkBufferImpl.h:44
BufferLock writeBytes(BufferRange range, VkAccessFlags2 dstAccessMask, VkPipelineStageFlags2 dstStageMask, const bufferPtr data)
VMABuffer_uptr _stagingBuffer
Definition: vkBufferImpl.h:82
vkBufferImpl(const BufferParams &params, size_t alignedBufferSize, size_t ringQueueLength, std::pair< bufferPtr, size_t > initialData, const char *bufferName) noexcept
void readBytes(BufferRange range, std::pair< bufferPtr, size_t > outData)
#define VK_CHECK(x)
Definition: vkResources.h:327