SpiecsEngine
 
Loading...
Searching...
No Matches
TextureLoader.cpp
Go to the documentation of this file.
1/**
2* @file TextureLoader.cpp.
3* @brief The TextureLoader Class Implementation.
4* @author Spices.
5*/
6
7#include "Pchheader.h"
9#include "Render/Vulkan/VulkanImage.h"
10#include "Render/Vulkan/VulkanRenderBackend.h"
11#include "Core/Library/FileLibrary.h"
12#include "Systems/ResourceSystem.h"
13#include "Resources/Texture/Transcoder.h"
14
15#include <stb_image.h>
16
17#include "Resources/Texture/Texture2D.h"
18#include "Resources/Texture/Texture2DCube.h"
19
20namespace Spices {
21
22 /**
23 * @brief Const variable: Original Image File Path.
24 */
25 const std::string defaultTexturePath = "Textures/src/";
26 const std::string binTexturePath = "Textures/bin/";
27
28 void TextureLoader::Load(const std::string& fileName, Texture2D* outTexture)
29 {
31
32 std::filesystem::path path(fileName);
33 if (path.is_absolute())
34 {
35 if (path.extension().string() == "ktx")
36 {
37 LoadBin(fileName, "", outTexture);
38 }
39 else
40 {
41 LoadSrc(fileName, "", outTexture, false);
42 }
43
44 return;
45 }
46
47 SearchFile(
48 fileName,
49 [&](const std::string& it) {
50 LoadBin(fileName, it, outTexture);
51 },
52 [&](const std::string& it) {
53
54 /**
55 * @brief Temp texture for create ktxtexture.
56 */
57 std::unique_ptr<Texture2D> texture = std::make_unique<Texture2D>();
58 LoadSrc(fileName, it, texture.get());
59 LoadBin(fileName, it, outTexture);
60 }
61 );
62 }
63
64 void TextureLoader::Load(const std::string& fileName, Texture2DCube* outTexture)
65 {
66
67 }
68
69 bool TextureLoader::SearchFile(
70 const std::string& fileName,
71 std::function<void(const std::string&)> binF,
72 std::function<void(const std::string&)> srcF
73 )
74 {
76
77 const std::vector<std::string> splitString = StringLibrary::SplitString(fileName, '.');
78
79 for (auto& it : ResourceSystem::GetSearchFolder())
80 {
81 std::string filePath = it + binTexturePath + splitString[0] + ".ktx";
82 if (FileLibrary::FileLibrary_Exists(filePath.c_str()))
83 {
84 binF(it);
85 return true;
86 }
87 }
88 for (auto& it : ResourceSystem::GetSearchFolder())
89 {
90 std::string filePath = it + defaultTexturePath + fileName;
91 if (FileLibrary::FileLibrary_Exists(filePath.c_str()))
92 {
93 srcF(it);
94 return true;
95 }
96 }
97
98 std::stringstream ss;
99 ss << "File: " << fileName << " Not Find";
100
101 SPICES_CORE_ERROR(ss.str())
102 return false;
103 }
104
105 bool TextureLoader::LoadBin(const std::string& fileName, const std::string& it, Texture2D* outTexture)
106 {
108
109 std::string binPath;
110 if (it == "")
111 {
112 binPath = fileName;
113 }
114 else
115 {
116 std::vector<std::string> splitString = StringLibrary::SplitString(fileName, '.');
117 binPath = it + binTexturePath + splitString[0] + ".ktx";
118 }
119
120 ktxTexture2* texture = nullptr;
121 Transcoder::LoadFromKTX(binPath, texture);
122
123 /**
124 * @brief Instance the VulkanImage as Texture2D Resource.
125 */
126 outTexture->m_Resource = std::make_shared<VulkanImage>(VulkanRenderBackend::GetState());
127 auto resourceptr = outTexture->GetResource<VulkanImage>();
128
129 VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT; // Can be Used for Sample
130 bool hostCopy = VulkanImage::IsHostCopyable(resourceptr->m_VulkanState, static_cast<VkFormat>(texture->vkFormat));
131 usage |= hostCopy ? VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT : VK_IMAGE_USAGE_TRANSFER_DST_BIT;
132
133 /**
134 * @brief Instance the Resource.
135 */
136 resourceptr->CreateImage(
137 resourceptr->m_VulkanState ,
138 fileName ,
139 static_cast<VkImageType>(texture->numDimensions - 1),
140 texture->baseWidth ,
141 texture->baseHeight ,
142 texture->baseDepth , // 1 layers.
143 VK_SAMPLE_COUNT_1_BIT , // No MASS.
144 static_cast<VkFormat>(texture->vkFormat) , // Compress Format.
145 VK_IMAGE_TILING_OPTIMAL , // Tiling Optimal.
146 usage ,
147 0 ,
148 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
149 texture->numLevels
150 );
151
152 if (hostCopy)
153 {
154 /**
155 * @brief Transform Image Layout from undefined to transfer_dst.
156 */
157 resourceptr->TransitionImageLayout(
158 resourceptr->m_Format,
159 VK_IMAGE_LAYOUT_UNDEFINED,
160 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
161 );
162
163 std::vector<VkMemoryToImageCopyEXT> copies;
164 for (uint32_t mip_level = 0; mip_level < texture->numLevels; mip_level++)
165 {
166 VkMemoryToImageCopyEXT memoryCopy{};
167 memoryCopy.sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT;
168 memoryCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
169 memoryCopy.imageSubresource.mipLevel = mip_level;
170 memoryCopy.imageSubresource.baseArrayLayer = 0;
171 memoryCopy.imageSubresource.layerCount = 1;
172 memoryCopy.imageExtent.width = std::max((uint32_t)1, texture->baseWidth >> mip_level);
173 memoryCopy.imageExtent.height = std::max((uint32_t)1, texture->baseHeight >> mip_level);
174 memoryCopy.imageExtent.depth = 1;
175 memoryCopy.pHostPointer = texture->pData + Transcoder::GetMipmapOffset(texture, mip_level);
176
177 copies.push_back(memoryCopy);
178 }
179
180 /**
181 * @brief Copy Memory to Image.
182 */
183 resourceptr->CopyMemoryToImageHost(copies);
184 }
185 else
186 {
187 /**
188 * @brief Instance a staginBuffer.
189 */
190 VulkanBuffer stagingBuffer(
191 resourceptr->m_VulkanState,
192 "StagingBuffer" ,
193 texture->dataSize ,
194 VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
195 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
196 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
197 );
198
199 /**
200 * @brief Copy the data from texture bytes to staginBuffer.
201 */
202 stagingBuffer.WriteToBuffer(texture->pData);
203
204 /**
205 * @brief Transform Image Layout from undefined to transfer_dst.
206 */
207 resourceptr->TransitionImageLayout(
208 resourceptr->m_Format ,
209 VK_IMAGE_LAYOUT_UNDEFINED ,
210 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
211 );
212
213 std::vector<VkBufferImageCopy> regions;
214 for (uint32_t mip_level = 0; mip_level < texture->numLevels; mip_level++)
215 {
216 VkBufferImageCopy region {};
217 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
218 region.imageSubresource.mipLevel = mip_level;
219 region.imageSubresource.baseArrayLayer = 0;
220 region.imageSubresource.layerCount = 1;
221 region.imageExtent.width = std::max((uint32_t)1, texture->baseWidth >> mip_level);
222 region.imageExtent.height = std::max((uint32_t)1, texture->baseHeight >> mip_level);
223 region.imageExtent.depth = 1;
224 region.bufferOffset = Transcoder::GetMipmapOffset(texture, mip_level);
225
226 regions.push_back(region);
227 }
228
229 /**
230 * @brief Copy the data from staginBuffer to Image.
231 */
232 resourceptr->CopyBufferToImage(
233 stagingBuffer.Get() ,
234 resourceptr->m_Image ,
235 static_cast<uint32_t>(resourceptr->m_Width) ,
236 static_cast<uint32_t>(resourceptr->m_Height),
237 regions
238 );
239
240 /**
241 * @brief Transform Image Layout from transfer_dst to shaderread.
242 */
243 resourceptr->TransitionImageLayout(
244 resourceptr->m_Format ,
245 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
246 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
247 );
248 }
249
250 /**
251 * @brief Create Image View.
252 */
253 resourceptr->CreateImageView(
254 resourceptr->m_Format ,
255 VK_IMAGE_VIEW_TYPE_2D ,
256 VK_IMAGE_ASPECT_COLOR_BIT
257 );
258
259 /**
260 * @brief Create Image Sampler.
261 */
262 resourceptr->CreateSampler();
263
264 /**
265 * @brief Release ktx image data.
266 */
267 Transcoder::DestroyktxTexture2(texture);
268
269 return true;
270 }
271
272 bool TextureLoader::LoadSrc(const std::string& fileName, const std::string& it, Texture2D* outTexture, bool isCreateCompressTexture)
273 {
275
276 std::string filePath;
277 std::string binPath;
278 if (it == "")
279 {
280 assert(!isCreateCompressTexture);
281
282 filePath = fileName;
283 }
284 else
285 {
286 const std::vector<std::string> splitString = StringLibrary::SplitString(fileName, '.');
287 filePath = it + defaultTexturePath + fileName;
288 binPath = it + binTexturePath + splitString[0] + ".ktx";
289 }
290
291 /**
292 * @brief Load Texture data.
293 */
294 int width;
295 int height;
296 int texChannels;
297 stbi_uc* pixels = stbi_load(filePath.c_str(), &width, &height, &texChannels, STBI_rgb_alpha);
298 if (!pixels)
299 {
300 SPICES_CORE_ERROR("Failed to load texture image!")
301 }
302
303 /**
304 * @brief Instance the VulkanImage as Texture2D Resource.
305 */
306 outTexture->m_Resource = std::make_shared<VulkanImage>(VulkanRenderBackend::GetState());
307 auto resourceptr = outTexture->GetResource<VulkanImage>();
308
309 /**
310 * @brief Set resource values.
311 */
312 resourceptr->m_Width = width;
313 resourceptr->m_Height = height;
314 resourceptr->m_MipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(resourceptr->m_Width, resourceptr->m_Height)))) + 1;
315
316 VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT; // Can be Used for Sample
317 const bool hostCopy = VulkanImage::IsHostCopyable(resourceptr->m_VulkanState, VK_FORMAT_R8G8B8A8_UNORM);
318 usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
319 if (hostCopy) usage |= VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
320
321 /**
322 * @brief Instance the Resource.
323 */
324 resourceptr->CreateImage(
325 resourceptr->m_VulkanState,
326 fileName ,
327 VK_IMAGE_TYPE_2D ,
328 resourceptr->m_Width ,
329 resourceptr->m_Height ,
330 1 , // 1 layers.
331 VK_SAMPLE_COUNT_1_BIT , // No MASS.
332 VK_FORMAT_R8G8B8A8_UNORM , // RGBA8 Format.
333 VK_IMAGE_TILING_OPTIMAL , // Tiling Optimal.
334 usage ,
335 0 ,
336 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
337 resourceptr->m_MipLevels
338 );
339
340 /**
341 * @brief Transform Image Layout from undefined to transfer_dst.
342 */
343 resourceptr->TransitionImageLayout(
344 resourceptr->m_Format,
345 VK_IMAGE_LAYOUT_UNDEFINED,
346 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
347 );
348
349 /**
350 * @brief Get Texture bytes.
351 * @note 4 means 4 channels per texel, 1 means 1 bytes per texel channel.(RGBA8 Format support only)
352 */
353 const VkDeviceSize imageSize = static_cast<uint64_t>(resourceptr->m_Width * resourceptr->m_Height * 4 * 1);
354
355 /**
356 * @brief Instance a staginBuffer.
357 */
358 VulkanBuffer stagingBuffer(
359 resourceptr->m_VulkanState,
360 "StagingBuffer",
361 imageSize,
362 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
363 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
364 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
365 );
366
367 /**
368 * @brief Copy the data from texture bytes to staginBuffer.
369 */
370 stagingBuffer.WriteToBuffer(pixels);
371
372 /**
373 * @brief Copy the data from staginBuffer to Image.
374 */
375 resourceptr->CopyBufferToImage(
376 stagingBuffer.Get() ,
377 resourceptr->m_Image ,
378 static_cast<uint32_t>(resourceptr->m_Width) ,
379 static_cast<uint32_t>(resourceptr->m_Height)
380 );
381
382 /**
383 * @brief Release texture bytes.
384 */
385 stbi_image_free(pixels);
386
387 /**
388 * @brief Generate Image Mipmaps.
389 */
390 resourceptr->GenerateMipmaps(
391 resourceptr->m_Format,
392 resourceptr->m_Width,
393 resourceptr->m_Height
394 );
395
396 /**
397 * @brief Create Image View.
398 */
399 resourceptr->CreateImageView(
400 resourceptr->m_Format ,
401 VK_IMAGE_VIEW_TYPE_2D ,
402 VK_IMAGE_ASPECT_COLOR_BIT
403 );
404
405 /**
406 * @brief Create Image Sampler.
407 */
408 resourceptr->CreateSampler();
409
410 auto deviceCopyF = [&](uint32_t w, uint32_t h, int mip, std::vector<unsigned char>& data) {
411
412 /**
413 * @brief Instance a VkBufferImageCopy.
414 */
415 VkBufferImageCopy region{};
416 region.bufferOffset = 0;
417 region.bufferRowLength = 0;
418 region.bufferImageHeight = 0;
419
420 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
421 region.imageSubresource.mipLevel = resourceptr->m_MipLevels - 1 - mip;
422 region.imageSubresource.baseArrayLayer = 0;
423 region.imageSubresource.layerCount = resourceptr->m_Layers;
424
425 region.imageOffset.x = 0;
426 region.imageOffset.y = 0;
427 region.imageExtent = { w, h, 1 };
428
429 const uint32_t size = w * h * 4;
430
431 /**
432 * @brief The temp buffer image date copy to.
433 */
434 VulkanBuffer copyStagingBuffer(
435 resourceptr->m_VulkanState,
436 "StagingBuffer",
437 size,
438 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
439 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
440 );
441
442 resourceptr->CopyImageToBuffer(copyStagingBuffer.Get(), { region });
443
444 copyStagingBuffer.WriteFromBuffer(reinterpret_cast<void*>(data.data()));
445 };
446
447 auto hostCopyF = [&](uint32_t w, uint32_t h, int mip, std::vector<unsigned char>& data) {
448
449 /**
450 * @brief Instance a VkImageToMemoryCopyEXT.
451 */
452 VkImageToMemoryCopyEXT memoryCopy{};
453 memoryCopy.sType = VK_STRUCTURE_TYPE_IMAGE_TO_MEMORY_COPY_EXT;
454 memoryCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
455 memoryCopy.imageSubresource.mipLevel = resourceptr->m_MipLevels - 1 - mip;
456 memoryCopy.imageSubresource.baseArrayLayer = 0;
457 memoryCopy.imageSubresource.layerCount = resourceptr->m_Layers;
458 memoryCopy.imageOffset = { 0, 0, 0 };
459 memoryCopy.imageExtent = { w, h, 1 };
460 memoryCopy.pHostPointer = data.data();
461
462 resourceptr->CopyImageToMemoryHost({ memoryCopy });
463 };
464
465 if (!isCreateCompressTexture)
466 {
467 return true;
468 }
469
470 ktxTexture2* ktxTexture = Transcoder::CreateKTX2Texture(resourceptr->m_Width, resourceptr->m_Height);
471 for (uint32_t i = 0; i < resourceptr->m_MipLevels; i++)
472 {
473 const uint32_t w = std::max(1, resourceptr->m_Width >> resourceptr->m_MipLevels - 1 - i);
474 const uint32_t h = std::max(1, resourceptr->m_Height >> resourceptr->m_MipLevels - 1 - i);
475 const uint32_t size = w * h * 4;
476
477 std::vector<unsigned char> data;
478 data.resize(size);
479
480 if (hostCopy)
481 {
482 hostCopyF(w, h, i, data);
483 }
484 else
485 {
486 deviceCopyF(w, h, i, data);
487 }
488
489 /**
490 * @brief Write mipmap data to ktxTexture.
491 */
492 Transcoder::WriteData(ktxTexture, resourceptr->m_MipLevels - 1 - i, data.data(), size);
493 }
494
495 /**
496 * @brief Save to disk.
497 */
498 Transcoder::SaveToDisk(ktxTexture, binPath);
499
500 return true;
501 }
502}
#define SPICES_PROFILE_ZONE
Texture2DCube Class. This class defines the basic behaves of Texture2DCube.
Texture2D Class. This class defines the basic behaves of texture2D.
Definition Texture2D.h:20
static void Load(const std::string &fileName, Texture2DCube *outTexture)
Load image to a Texture2DCube object.
static void Load(const std::string &fileName, Texture2D *outTexture)
Load image to a Texture2D object.
static bool LoadSrc(const std::string &fileName, const std::string &it, Texture2D *outTexture, bool isCreateCompressTexture=true)
Function of load a src file.
static bool LoadBin(const std::string &fileName, const std::string &it, Texture2D *outTexture)
Function of load a ktx file.
TextureLoader Class. This class only defines static function for load data from image file.
This Class is a Wrapper of VulkanBuffer.
static VulkanState & GetState()
Get VulkanState in use.
This class defines the render backend behaves of Vulkan.
const std::string binTexturePath
const std::string defaultTexturePath
Const variable: Original Image File Path.