Cogs.Core
GliTextureLoader.cpp
1#ifdef __clang__
2#pragma clang diagnostic ignored "-Wformat"
3#endif
4#include "GliTextureLoader.h"
5#include "GliFormats.h"
6
7#include "Context.h"
8#include "Resources/TextureManager.h"
9#include "Resources/ResourceStore.h"
10
11#include "Foundation/Logging/Logger.h"
12#include "Foundation/Platform/IO.h"
13
14#include "gli/gli.hpp"
15#include "gli/convert.hpp"
16
17#include "zstd.h"
18
19
20namespace
21{
22 using namespace Cogs::Core;
23
24 Cogs::Logging::Log logger = Cogs::Logging::getLogger("GliTextureLoader");
25
26 const std::string dds_zstd_suffix = ".dds.zst";
27
28 const size_t dds_zstd_suffix_len = dds_zstd_suffix.length();
29
30 bool decodeFromMemory(Context* context, const TextureLoadInfo& loadInfo, const void* dataPtr, const size_t dataSize)
31 {
32 if (dataPtr == nullptr || dataSize == 0) {
33 return false;
34 }
35
36 Cogs::Memory::MemoryBuffer decompressed;
37 const void* data_ptr = nullptr;
38 size_t data_siz = 0u;
39
40 auto N = loadInfo.resourcePath.size();
41 if ((dds_zstd_suffix_len <= N) && (loadInfo.resourcePath.substr(N - dds_zstd_suffix_len) == dds_zstd_suffix)) {
42 ZSTD_DCtx* zstd_dctx = ZSTD_createDCtx();
43 assert(zstd_dctx);
44
45 auto size = ZSTD_getFrameContentSize(dataPtr, dataSize);
46 if (size == ZSTD_CONTENTSIZE_UNKNOWN) {
47 LOG_ERROR(logger, "ZSTD_CONTENTSIZE_UNKNOWN");
48 return false;
49 }
50 else if (size == ZSTD_CONTENTSIZE_ERROR) {
51 LOG_ERROR(logger, "ZSTD_CONTENTSIZE_ERROR");
52 return false;
53 }
54 else if (std::numeric_limits<size_t>::max() < size) {
55 LOG_ERROR(logger, "File too large: %ull", size);
56 return false;
57 }
58 data_siz = static_cast<size_t>(size);
59
60 decompressed.resize(data_siz, false);
61 auto result = ZSTD_decompressDCtx(zstd_dctx, decompressed.data(), decompressed.size(), dataPtr, dataSize);
62 if (ZSTD_isError(result)) {
63 LOG_ERROR(logger, "ZSTD decompression failed: %s", ZSTD_getErrorName(result));
64 return false;
65 }
66 LOG_DEBUG(logger, "ZSTD decompressed dds image %s", loadInfo.resourcePath.c_str());
67 data_ptr = decompressed.data();
68 data_siz = result;
69 ZSTD_freeDCtx(zstd_dctx);
70 }
71 else {
72 data_ptr = dataPtr;
73 data_siz = dataSize;
74 }
75
76 gli::texture textureData = gli::load(reinterpret_cast<const char*>(data_ptr), data_siz);
77
78 if (textureData.empty()) {
79 return false;
80 }
81
82
83 auto target = Cogs::ResourceDimensions::Unknown;
84 switch (textureData.target()) {
85 case gli::TARGET_1D:
86 target = Cogs::ResourceDimensions::Texture1D;
87 break;
88 case gli::TARGET_1D_ARRAY:
89 target = Cogs::ResourceDimensions::Texture1DArray;
90 break;
91 case gli::TARGET_2D:
92 target = Cogs::ResourceDimensions::Texture2D;
93 break;
94 case gli::TARGET_2D_ARRAY:
95 target = Cogs::ResourceDimensions::Texture2DArray;
96 break;
97 case gli::TARGET_3D:
98 target = Cogs::ResourceDimensions::Texture3D;
99 break;
100 case gli::TARGET_RECT:
101 target = Cogs::ResourceDimensions::Texture2D;
102 break;
103 case gli::TARGET_RECT_ARRAY:
104 target = Cogs::ResourceDimensions::Texture2DArray;
105 break;
106 case gli::TARGET_CUBE:
107 target = Cogs::ResourceDimensions::TextureCube;
108 break;
109 case gli::TARGET_CUBE_ARRAY:
110 target = Cogs::ResourceDimensions::TextureCubeArray;
111 break;
112 default:
113 assert(false && "Unhandled gli texture target");
114 break;
115 }
116
117 Cogs::TextureFormat format = Cogs::formatMap[textureData.format()];
118 if (format == Cogs::TextureFormat::Unknown) {
119 LOG_ERROR(logger, "Unsupported texture format %d in %s.", format, loadInfo.resourcePath.c_str());
120 return false;
121 }
122
123 if (loadInfo.flip) {
124 textureData = gli::flip(textureData);
125 }
126
127 auto texture = context->textureManager->lock(loadInfo.handle);
128 texture->description.target = target;
129
130 bool generateMipMaps = (textureData.levels() <= 1) && (TextureLoadFlags(loadInfo.loadFlags) & TextureLoadFlags::NoMipMaps) != TextureLoadFlags::NoMipMaps;
131 texture->setData(target,
132 textureData.data(),
133 textureData.size(),
134 textureData.extent(0).s,
135 textureData.extent(0).t,
136 textureData.extent(0).p,
137 static_cast<uint32_t>(textureData.layers()),
138 static_cast<uint32_t>(textureData.faces()),
139 static_cast<uint32_t>(textureData.levels()),
140 format,
141 generateMipMaps);
142
143 return true;
144 }
145
146}
147
148bool Cogs::Core::GliTextureLoader::canLoad(Context * /*context*/, const TextureLoadInfo & loadInfo)
149{
150 auto N = loadInfo.resourcePath.size();
151 if ((dds_zstd_suffix_len <= N) && (loadInfo.resourcePath.substr(N - dds_zstd_suffix_len) == dds_zstd_suffix)) {
152 return true;
153 }
154
155 auto extension = Cogs::IO::extension(loadInfo.resourcePath);
156 for (auto& c : extension) {
157 c = static_cast<decltype(extension)::value_type>(std::tolower(c));
158 }
159 return extension == ".dds" || extension == ".ktx";
160}
161
162bool Cogs::Core::GliTextureLoader::load(Context * context, const TextureLoadInfo & loadInfo)
163{
164 const auto & data = context->resourceStore->getResourceContents(loadInfo.resourcePath);
165 if (data.empty()) {
166 return false;
167 }
168 return decodeFromMemory(context, loadInfo, data.data(), data.size());
169}
170
171bool Cogs::Core::GliTextureLoader::load(Context* context, const TextureLoadInfo& loadInfo, const void* dataPtr, size_t dataSize)
172{
173 return decodeFromMemory(context, loadInfo, dataPtr, dataSize);
174}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Definition: Context.h:210
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
TextureLoadFlags
Texture loading flags. May be combined with resource load flags.
Definition: ResourceFlags.h:47
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
std::string resourcePath
Resource path. Used to locate resource.
ResourceHandleBase handle
Handle to resource structure for holding actual resource data.
ResourceLoadFlags loadFlags
Desired loading flags. Used to specify how the resource will be loaded.