Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
VulkanTexture.cpp
1/*
2Copyright (C) 2017-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
17#include <sstream>
18#include <limits>
19#include <cstring>
20#include <utility>
21#include "VulkanTexture.hpp"
22#include "VulkanRenderer.hpp"
23#include "VulkanUtilities.hpp"
24#include "aeongames/AeonEngine.hpp"
25#include "aeongames/CRC.hpp"
26#include "aeongames/Texture.hpp"
27#include "aeongames/Utilities.hpp"
29
30namespace AeonGames
31{
32 VulkanTexture::VulkanTexture ( const VulkanRenderer& aVulkanRenderer, const Texture& aTexture ) :
33 mVulkanRenderer ( aVulkanRenderer ), mTexture{&aTexture}
34 {
35 VkImageCreateInfo image_create_info{};
36 image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
37 image_create_info.pNext = nullptr;
38 image_create_info.flags = 0;
39 image_create_info.imageType = VK_IMAGE_TYPE_2D;
40 image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; // This field contains both format and type, calculate rather than hardcode
41 VkFormatProperties format_properties{};
42 vkGetPhysicalDeviceFormatProperties ( mVulkanRenderer.GetPhysicalDevice(), image_create_info.format, &format_properties );
43 image_create_info.extent.width = aTexture.GetWidth();
44 image_create_info.extent.height = aTexture.GetHeight();
45 image_create_info.extent.depth = 1;
46 image_create_info.mipLevels = 1;
47 image_create_info.arrayLayers = 1;
48 image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
49 image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
50 image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
51 image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
52 image_create_info.queueFamilyIndexCount = 0;
53 image_create_info.pQueueFamilyIndices = nullptr;
54 image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
55 if ( VkResult result = vkCreateImage ( mVulkanRenderer.GetDevice(), &image_create_info, nullptr, &mVkImage ) )
56 {
57 std::ostringstream stream;
58 stream << "Image creation failed: ( " << GetVulkanResultString ( result ) << " )";
59 std::cout << LogLevel::Error << stream.str() << std::endl;
60 throw std::runtime_error ( stream.str().c_str() );
61 }
62
63 VkMemoryRequirements memory_requirements{};
64 vkGetImageMemoryRequirements ( mVulkanRenderer.GetDevice(), mVkImage, &memory_requirements );
65 VkMemoryAllocateInfo memory_allocate_info{};
66 memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
67 memory_allocate_info.pNext = nullptr;
68 memory_allocate_info.allocationSize = memory_requirements.size;
69 memory_allocate_info.memoryTypeIndex = mVulkanRenderer.FindMemoryTypeIndex ( memory_requirements.memoryTypeBits,
70 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT );
71 if ( memory_allocate_info.memoryTypeIndex == std::numeric_limits<uint32_t>::max() )
72 {
73 std::cout << LogLevel::Error << "Unable to find a suitable memory type index" << std::endl;
74 throw std::runtime_error ( "Unable to find a suitable memory type index" );
75 }
76 if ( VkResult result = vkAllocateMemory ( mVulkanRenderer.GetDevice(), &memory_allocate_info, nullptr, &mVkDeviceMemory ) )
77 {
78 std::ostringstream stream;
79 stream << "Image Memory Allocation failed: ( " << GetVulkanResultString ( result ) << " )";
80 std::cout << LogLevel::Error << stream.str() << std::endl;
81 throw std::runtime_error ( stream.str().c_str() );
82 }
83 if ( VkResult result = vkBindImageMemory ( mVulkanRenderer.GetDevice(), mVkImage, mVkDeviceMemory, 0 ) )
84 {
85 std::ostringstream stream;
86 stream << "Bind Image Memory failed: ( " << GetVulkanResultString ( result ) << " )";
87 std::cout << LogLevel::Error << stream.str() << std::endl;
88 throw std::runtime_error ( stream.str().c_str() );
89 }
90
92
93 mVkDescriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
94 VkImageViewCreateInfo image_view_create_info = {};
95 image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
96 image_view_create_info.image = mVkImage;
97 image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
98 image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
99 image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
100 image_view_create_info.subresourceRange.baseMipLevel = 0;
101 image_view_create_info.subresourceRange.levelCount = 1;
102 image_view_create_info.subresourceRange.baseArrayLayer = 0;
103 image_view_create_info.subresourceRange.layerCount = 1;
104 if ( VkResult result = vkCreateImageView ( mVulkanRenderer.GetDevice(), &image_view_create_info, nullptr, &mVkDescriptorImageInfo.imageView ) )
105 {
106 std::ostringstream stream;
107 stream << "Create Image View failed: ( " << GetVulkanResultString ( result ) << " )";
108 std::cout << LogLevel::Error << stream.str() << std::endl;
109 throw std::runtime_error ( stream.str().c_str() );
110 }
111 /*-----------------------------------------------------------------*/
112 VkSamplerCreateInfo sampler_create_info{};
113 sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
114 sampler_create_info.pNext = nullptr;
115 sampler_create_info.flags = 0;
116 sampler_create_info.magFilter = VK_FILTER_NEAREST;
117 sampler_create_info.minFilter = VK_FILTER_NEAREST;
118 sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
119 sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
120 sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
121 sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
122 sampler_create_info.mipLodBias = 0.0f;
123 sampler_create_info.anisotropyEnable = VK_FALSE;
124 sampler_create_info.maxAnisotropy = 1.0f;
125 sampler_create_info.compareEnable = VK_FALSE;
126 sampler_create_info.compareOp = VK_COMPARE_OP_NEVER;
127 sampler_create_info.minLod = 0.0f;
128 sampler_create_info.maxLod = 1.0f;
129 sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
130 sampler_create_info.unnormalizedCoordinates = VK_FALSE;
131 if ( VkResult result = vkCreateSampler ( mVulkanRenderer.GetDevice(), &sampler_create_info, nullptr, &mVkDescriptorImageInfo.sampler ) )
132 {
133 std::ostringstream stream;
134 stream << "Sampler creation failed: ( " << GetVulkanResultString ( result ) << " )";
135 std::cout << LogLevel::Error << stream.str() << std::endl;
136 throw std::runtime_error ( stream.str().c_str() );
137 }
138
139 // -----------------------------
140 // Write Image
141 VkBuffer image_buffer{};
142 VkDeviceMemory image_buffer_memory{};
143
144 VkBufferCreateInfo buffer_create_info = {};
145 buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
146 buffer_create_info.size = static_cast<size_t> ( aTexture.GetWidth() ) * static_cast<size_t> ( aTexture.GetHeight() ) * 4;
147 buffer_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
148 buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
149
150 if ( VkResult result = vkCreateBuffer ( mVulkanRenderer.GetDevice(), &buffer_create_info, nullptr, &image_buffer ) )
151 {
152 std::ostringstream stream;
153 stream << "Create Buffer failed: ( " << GetVulkanResultString ( result ) << " )";
154 std::cout << LogLevel::Error << stream.str() << std::endl;
155 throw std::runtime_error ( stream.str().c_str() );
156 }
157
158 memset ( &memory_requirements, 0, sizeof ( VkMemoryRequirements ) );
159 vkGetBufferMemoryRequirements ( mVulkanRenderer.GetDevice(), image_buffer, &memory_requirements );
160 memset ( &memory_allocate_info, 0, sizeof ( VkMemoryAllocateInfo ) );
161 memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
162 memory_allocate_info.allocationSize = memory_requirements.size;
163 memory_allocate_info.memoryTypeIndex = mVulkanRenderer.FindMemoryTypeIndex ( memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT );
164
165 if ( VkResult result = vkAllocateMemory ( mVulkanRenderer.GetDevice(), &memory_allocate_info, nullptr, &image_buffer_memory ) )
166 {
167 std::ostringstream stream;
168 stream << "Allocate Memory failed: ( " << GetVulkanResultString ( result ) << " )";
169 std::cout << LogLevel::Error << stream.str() << std::endl;
170 throw std::runtime_error ( stream.str().c_str() );
171 }
172
173 if ( VkResult result = vkBindBufferMemory ( mVulkanRenderer.GetDevice(), image_buffer, image_buffer_memory, 0 ) )
174 {
175 std::ostringstream stream;
176 stream << "Bind Buffer Memory failed: ( " << GetVulkanResultString ( result ) << " )";
177 std::cout << LogLevel::Error << stream.str() << std::endl;
178 throw std::runtime_error ( stream.str().c_str() );
179 }
180
181 void* image_memory = nullptr;
182 if ( VkResult result = vkMapMemory ( mVulkanRenderer.GetDevice(), image_buffer_memory, 0, VK_WHOLE_SIZE, 0, &image_memory ) )
183 {
184 std::ostringstream stream;
185 stream << "Map Memory failed: ( " << GetVulkanResultString ( result ) << " )";
186 std::cout << LogLevel::Error << stream.str() << std::endl;
187 throw std::runtime_error ( stream.str().c_str() );
188 }
189 if ( aTexture.GetFormat() == Texture::Format::RGBA )
190 {
191 if ( aTexture.GetType() == Texture::Type::UNSIGNED_BYTE )
192 {
193 memcpy ( image_memory, aTexture.GetPixels().data(), aTexture.GetWidth() * aTexture.GetHeight() * GetPixelSize ( aTexture.GetFormat(), aTexture.GetType() ) );
194 }
195 else
196 {
197 // Is this a temporary fix?
206 const auto* read_pointer = reinterpret_cast<const uint16_t*> ( aTexture.GetPixels().data() );
207 auto* write_pointer = static_cast<uint8_t*> ( image_memory );
208 auto data_size = aTexture.GetWidth() * aTexture.GetHeight() * GetPixelSize ( aTexture.GetFormat(), aTexture.GetType() ) / 2;
209 for ( uint32_t i = 0; i < data_size; i += 4 )
210 {
211 write_pointer[i] = read_pointer[i] / 256;
212 write_pointer[i + 1] = read_pointer[i + 1] / 256;
213 write_pointer[i + 2] = read_pointer[i + 2] / 256;
214 write_pointer[i + 3] = read_pointer[i + 3] / 256;
215 }
216 }
217 }
218 else
219 {
220 if ( aTexture.GetType() == Texture::Type::UNSIGNED_BYTE )
221 {
222 const uint8_t* read_pointer = aTexture.GetPixels().data();
223 auto* write_pointer = static_cast<uint8_t*> ( image_memory );
224 auto data_size = aTexture.GetWidth() * aTexture.GetHeight() * GetPixelSize ( aTexture.GetFormat(), aTexture.GetType() );
225 for ( uint32_t i = 0; i < data_size; i += 3 )
226 {
227 write_pointer[0] = read_pointer[i];
228 write_pointer[1] = read_pointer[i + 1];
229 write_pointer[2] = read_pointer[i + 2];
230 write_pointer[3] = 255;
231 write_pointer += 4;
232 }
233 }
234 else
235 {
236 // Is this a temporary fix?
237 const auto* read_pointer = reinterpret_cast<const uint16_t*> ( aTexture.GetPixels().data() );
238 auto* write_pointer = static_cast<uint8_t*> ( image_memory );
239 auto data_size = aTexture.GetWidth() * aTexture.GetHeight() * GetPixelSize ( aTexture.GetFormat(), aTexture.GetType() ) / 2;
240 for ( uint32_t i = 0; i < data_size; i += 3 )
241 {
242 write_pointer[0] = read_pointer[i] / 256;
243 write_pointer[1] = read_pointer[i + 1] / 256;
244 write_pointer[2] = read_pointer[i + 2] / 256;
245 write_pointer[3] = 255;
246 write_pointer += 4;
247 }
248 }
249 }
250 vkUnmapMemory ( mVulkanRenderer.GetDevice(), image_buffer_memory );
251
252 //--------------------------------------------------------
253 // Transition Image Layout
254 VkCommandBuffer command_buffer = mVulkanRenderer.BeginSingleTimeCommands();
255 VkImageMemoryBarrier image_memory_barrier{};
256 image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
257 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
258 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
259 image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
260 image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
261 image_memory_barrier.image = mVkImage;
262 image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
263 image_memory_barrier.subresourceRange.baseMipLevel = 0;
264 image_memory_barrier.subresourceRange.levelCount = 1;
265 image_memory_barrier.subresourceRange.baseArrayLayer = 0;
266 image_memory_barrier.subresourceRange.layerCount = 1;
267 image_memory_barrier.srcAccessMask = 0; //VK_ACCESS_HOST_WRITE_BIT;
268 image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
269 vkCmdPipelineBarrier (
270 command_buffer,
271 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
272 0,
273 0, nullptr,
274 0, nullptr,
275 1, &image_memory_barrier
276 );
277 VkBufferImageCopy buffer_image_copy{};
278 buffer_image_copy.bufferOffset = 0;
279 buffer_image_copy.bufferRowLength = 0;
280 buffer_image_copy.bufferImageHeight = 0;
281 buffer_image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
282 buffer_image_copy.imageSubresource.mipLevel = 0;
283 buffer_image_copy.imageSubresource.baseArrayLayer = 0;
284 buffer_image_copy.imageSubresource.layerCount = 1;
285 buffer_image_copy.imageOffset = { 0, 0, 0 };
286 buffer_image_copy.imageExtent = { aTexture.GetWidth(), aTexture.GetHeight(), 1 };
287 vkCmdCopyBufferToImage ( command_buffer, image_buffer, mVkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy );
288
289 image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
290 image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
291 image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
292 image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
293 vkCmdPipelineBarrier (
294 command_buffer,
295 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
296 0,
297 0, nullptr,
298 0, nullptr,
299 1, &image_memory_barrier
300 );
301 mVulkanRenderer.EndSingleTimeCommands ( command_buffer );
302 vkDestroyBuffer ( mVulkanRenderer.GetDevice(), image_buffer, nullptr );
303 vkFreeMemory ( mVulkanRenderer.GetDevice(), image_buffer_memory, nullptr );
304 }
305
307 mVulkanRenderer ( aVulkanTexture.mVulkanRenderer ), mTexture{aVulkanTexture.mTexture}
308 {
309 std::swap ( mVkImage, aVulkanTexture.mVkImage );
310 std::swap ( mVkDeviceMemory, aVulkanTexture.mVkDeviceMemory );
311 std::swap ( mVkDescriptorImageInfo, aVulkanTexture.mVkDescriptorImageInfo );
312 }
313
314 VulkanTexture::~VulkanTexture()
315 {
316 if ( mVkDescriptorImageInfo.sampler != VK_NULL_HANDLE )
317 {
318 vkDestroySampler ( mVulkanRenderer.GetDevice(), mVkDescriptorImageInfo.sampler, nullptr );
319 }
320 if ( mVkDescriptorImageInfo.imageView != VK_NULL_HANDLE )
321 {
322 vkDestroyImageView ( mVulkanRenderer.GetDevice(), mVkDescriptorImageInfo.imageView, nullptr );
323 }
324 if ( mVkImage != VK_NULL_HANDLE )
325 {
326 vkDestroyImage ( mVulkanRenderer.GetDevice(), mVkImage, nullptr );
327 }
328 if ( mVkDeviceMemory != VK_NULL_HANDLE )
329 {
330 vkFreeMemory ( mVulkanRenderer.GetDevice(), mVkDeviceMemory, nullptr );
331 }
332 }
333
334 const VkDescriptorImageInfo& VulkanTexture::GetDescriptorImageInfo() const
335 {
336 return mVkDescriptorImageInfo;
337 }
338}
Defines log severity levels and stream output for the AeonGames engine.
Represents a 2D texture image resource.
Definition Texture.hpp:33
DLL uint32_t GetWidth() const
Returns the texture width in pixels.
Definition Texture.cpp:75
DLL Format GetFormat() const
Returns the pixel format.
Definition Texture.cpp:83
DLL const std::vector< uint8_t > & GetPixels() const
Returns a reference to the raw pixel data.
Definition Texture.cpp:92
@ UNSIGNED_BYTE
8-bit unsigned integer per channel.
Definition Texture.hpp:53
@ RGBA
4-channel red, green, blue, alpha.
Definition Texture.hpp:43
DLL Type GetType() const
Returns the pixel component type.
Definition Texture.cpp:87
DLL uint32_t GetHeight() const
Returns the texture height in pixels.
Definition Texture.cpp:79
Vulkan rendering backend implementing the Renderer interface.
const VkDevice & GetDevice() const
Get the logical device handle.
VulkanTexture(const VulkanRenderer &aVulkanRenderer, const Texture &aTexture)
const VkDescriptorImageInfo & GetDescriptorImageInfo() const
Get the Vulkan descriptor image info for binding.
<- 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.
@ Error
Error conditions.
Definition LogLevel.hpp:33