Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
VulkanBuffer.cpp
1/*
2Copyright (C) 2017-2019,2021,2025,2026 Rodrigo Jose Hernandez Cordoba
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16#include <cstring>
17#include <sstream>
18#include <iostream>
19#include <limits>
20#include <cassert>
22#include "VulkanRenderer.hpp"
23#include "VulkanBuffer.hpp"
24#include "VulkanUtilities.hpp"
25
26namespace AeonGames
27{
28 VulkanBuffer::VulkanBuffer ( const VulkanRenderer & aVulkanRenderer ) : mVulkanRenderer { aVulkanRenderer }
29 {
30 std::cout << LogLevel::Info << __FUNCTION__ << std::endl;
31 }
32 VulkanBuffer::VulkanBuffer ( const VulkanRenderer& aVulkanRenderer, const VkDeviceSize aSize, const VkBufferUsageFlags aUsage, const VkMemoryPropertyFlags aProperties, const void *aData ) :
33 mVulkanRenderer { aVulkanRenderer },
34 mSize { aSize },
35 mUsage { aUsage },
36 mProperties { aProperties }
37 {
38 try
39 {
40 Initialize ( aData );
41 }
42 catch ( ... )
43 {
44 Finalize();
45 throw;
46 }
47 }
48
49 VulkanBuffer::~VulkanBuffer()
50 {
51 Finalize();
52 }
53
55 mVulkanRenderer { aBuffer.mVulkanRenderer }
56 {
57 std::swap ( mBuffer, aBuffer.mBuffer );
58 std::swap ( mSize, aBuffer.mSize );
59 std::swap ( mUsage, aBuffer.mUsage );
60 std::swap ( mProperties, aBuffer.mProperties );
61 std::swap ( mDeviceMemory, aBuffer.mDeviceMemory );
62 }
63
64 void VulkanBuffer::Initialize ( const VkDeviceSize aSize, const VkBufferUsageFlags aUsage, const VkMemoryPropertyFlags aProperties, const void * aData )
65 {
66 if ( mDeviceMemory != VK_NULL_HANDLE || mBuffer != VK_NULL_HANDLE )
67 {
68 std::cout << LogLevel::Error << "Buffer already initialized." << std::endl;
69 throw ( std::runtime_error ( "Buffer already initialized." ) );
70 }
71 mSize = aSize;
72 mUsage = aUsage;
73 mProperties = aProperties;
74 Initialize ( aData );
75 }
76
77 const VkBuffer& VulkanBuffer::GetBuffer() const
78 {
79 return mBuffer;
80 }
81
82 void VulkanBuffer::WriteMemory ( const size_t aOffset, const size_t aSize, const void * aData ) const
83 {
84 if ( aData )
85 {
86 if ( ( mProperties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ) )
87 {
88 void* data = Map ( aOffset, aSize );
89 memcpy ( data, aData, aSize );
90 Unmap();
91 }
92 else if ( ( mProperties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ) && ( mUsage & VK_BUFFER_USAGE_TRANSFER_DST_BIT ) )
93 {
94 VulkanBuffer source ( mVulkanRenderer, aSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, aData );
95 VkCommandBuffer command_buffer = mVulkanRenderer.BeginSingleTimeCommands();
96 VkBufferCopy copy_region = {};
97 copy_region.size = aSize;
98 copy_region.dstOffset = aOffset;
99 vkCmdCopyBuffer ( command_buffer, source.GetBuffer(), mBuffer, 1, &copy_region );
100 mVulkanRenderer.EndSingleTimeCommands ( command_buffer );
101 }
102 }
103 }
104
105 void* VulkanBuffer::Map ( size_t aOffset, size_t aSize ) const
106 {
107 void* data = nullptr;
108 if ( mProperties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT )
109 {
110 if ( VkResult result = vkMapMemory ( mVulkanRenderer.GetDevice(), mDeviceMemory, aOffset, aSize, 0, &data ) )
111 {
112 std::ostringstream stream;
113 stream << "vkMapMemory failed for buffer. error code: ( " << GetVulkanResultString ( result ) << " )";
114 std::cout << LogLevel::Error << stream.str() << std::endl;
115 throw std::runtime_error ( stream.str().c_str() );
116 }
117 }
118 else
119 {
120 std::cout << LogLevel::Error << "The VkBuffer VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT property must be set to be able to map buffer memory." << std::endl;
121 throw std::runtime_error ( "The VkBuffer VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT property must be set to be able to map buffer memory." );
122 }
123 return data;
124 }
125
127 {
128 vkUnmapMemory ( mVulkanRenderer.GetDevice(), mDeviceMemory );
129 }
130
132 {
133 return mSize;
134 }
135
136 void VulkanBuffer::Initialize ( const void* aData )
137 {
138 if ( !mSize )
139 {
140 return;
141 }
142 VkBufferCreateInfo buffer_create_info{};
143 buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
144 buffer_create_info.pNext = nullptr;
145 buffer_create_info.flags = 0;
146 buffer_create_info.size = mSize;
147 buffer_create_info.usage = mUsage;
148 buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
149 buffer_create_info.queueFamilyIndexCount = 0;
150 buffer_create_info.pQueueFamilyIndices = nullptr;
151
152 if ( VkResult result = vkCreateBuffer ( mVulkanRenderer.GetDevice(), &buffer_create_info, nullptr, &mBuffer ) )
153 {
154 std::ostringstream stream;
155 stream << "vkCreateBuffer failed for vertex buffer. error code: ( " << GetVulkanResultString ( result ) << " )";
156 std::string error_string = stream.str();
157 std::cout << LogLevel::Error << error_string << std::endl;
158 throw std::runtime_error ( error_string.c_str() );
159 }
160
161 VkMemoryRequirements memory_requirements;
162 vkGetBufferMemoryRequirements ( mVulkanRenderer.GetDevice(), mBuffer, &memory_requirements );
163
164 VkMemoryAllocateInfo memory_allocate_info{};
165 memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
166 memory_allocate_info.pNext = nullptr;
167 memory_allocate_info.allocationSize = memory_requirements.size;
168 memory_allocate_info.memoryTypeIndex = mVulkanRenderer.GetMemoryTypeIndex ( mProperties );
169
170 if ( memory_allocate_info.memoryTypeIndex == std::numeric_limits<uint32_t>::max() )
171 {
172 std::cout << LogLevel::Error << "No suitable memory type found for buffer." << std::endl;
173 throw std::runtime_error ( "No suitable memory type found for buffer." );
174 }
175
176 if ( VkResult result = vkAllocateMemory ( mVulkanRenderer.GetDevice(), &memory_allocate_info, nullptr, &mDeviceMemory ) )
177 {
178 std::ostringstream stream;
179 stream << "vkAllocateMemory failed for buffer. error code: ( " << GetVulkanResultString ( result ) << " )";
180 std::cout << LogLevel::Error << stream.str() << std::endl;
181 throw std::runtime_error ( stream.str().c_str() );
182 }
183 vkBindBufferMemory ( mVulkanRenderer.GetDevice(), mBuffer, mDeviceMemory, 0 );
184 if ( aData )
185 {
186 WriteMemory ( 0, mSize, aData );
187 }
188 }
189
191 {
192 if ( ( mDeviceMemory != VK_NULL_HANDLE ) || ( mBuffer != VK_NULL_HANDLE ) )
193 {
194 if ( VkResult result = vkQueueWaitIdle ( mVulkanRenderer.GetQueue() ) )
195 {
196 std::cout << GetVulkanResultString ( result ) << " " << __func__ << " " << __LINE__ << " " << std::endl;
197 }
198 }
199 if ( mBuffer != VK_NULL_HANDLE )
200 {
201 vkDestroyBuffer ( mVulkanRenderer.GetDevice(), mBuffer, nullptr );
202 mBuffer = VK_NULL_HANDLE;
203 }
204 if ( mDeviceMemory != VK_NULL_HANDLE )
205 {
206 vkFreeMemory ( mVulkanRenderer.GetDevice(), mDeviceMemory, nullptr );
207 mDeviceMemory = VK_NULL_HANDLE;
208 }
209 }
210}
Defines log severity levels and stream output for the AeonGames engine.
size_t GetSize() const final
Get the total size of the buffer in bytes.
VulkanBuffer(const VulkanRenderer &aVulkanRenderer)
Construct an uninitialized buffer.
void WriteMemory(const size_t aOffset, const size_t aSize, const void *aData=nullptr) const final
Write data into the buffer at a given offset.
void * Map(const size_t aOffset, size_t aSize) const final
Map a region of the buffer into CPU-accessible memory.
void Initialize(const VkDeviceSize aSize, const VkBufferUsageFlags aUsage, const VkMemoryPropertyFlags aProperties, const void *aData=nullptr)
Initialize the buffer with the given size, usage, memory properties, and optional data.
const VkBuffer & GetBuffer() const
Get the underlying Vulkan buffer handle.
void Finalize()
Release the Vulkan buffer and its device memory.
void Unmap() const final
Unmap a previously mapped buffer region.
Vulkan rendering backend implementing the Renderer interface.
<- This is here just for the literals
Definition AABB.hpp:31
const char * GetVulkanResultString(VkResult aResult)
Convert a VkResult code to a human-readable string.
@ Info
General informational messages.
Definition LogLevel.hpp:30
@ Error
Error conditions.
Definition LogLevel.hpp:33