Skip to content

Commit c546d12

Browse files
Texture Loader: added option to compress textures into BC formats
1 parent ee53198 commit c546d12

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

TextureLoader/include/TextureLoaderImpl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class TextureLoaderImpl final : public ObjectBase<ITextureLoader>
7676
void LoadFromImage(Image* pImage, const TextureLoadInfo& TexLoadInfo);
7777
void LoadFromKTX(const TextureLoadInfo& TexLoadInfo, const Uint8* pData, size_t DataSize);
7878
void LoadFromDDS(const TextureLoadInfo& TexLoadInfo, const Uint8* pData, size_t DataSize);
79+
void CompressSubresources(Uint32 NumComponents, Uint32 NumSrcComponents, const TextureLoadInfo& TexLoadInfo);
7980

8081
private:
8182
RefCntAutoPtr<IDataBlob> m_pDataBlob;

TextureLoader/interface/TextureLoader.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,29 @@ DILIGENT_TYPED_ENUM(TEXTURE_LOAD_MIP_FILTER, Uint8)
5454
TEXTURE_LOAD_MIP_FILTER_MOST_FREQUENT
5555
};
5656

57+
/// Texture compression mode
58+
DILIGENT_TYPED_ENUM(TEXTURE_LOAD_COMPRESS_MODE, Uint8)
59+
{
60+
/// Do not compress the texture.
61+
TEXTURE_LOAD_COMPRESS_MODE_NONE = 0,
62+
63+
/// Compress the texture using BC compression.
64+
///
65+
/// \remarks The BC texture format is selected based on the number of channels in the
66+
/// source image:
67+
/// * R8 -> BC4_UNORM
68+
/// * RG8 -> BC5_UNORM
69+
/// * RGB8 -> BC1_UNORM / BC1_UNORM_SRGB
70+
/// * RGBA8 -> BC3_UNORM / BC3_UNORM_SRGB
71+
TEXTURE_LOAD_COMPRESS_MODE_BC,
72+
73+
/// Compress the texture using high-quality BC compression.
74+
///
75+
/// \remarks This mode is similar to TEXTURE_LOAD_COMPRESS_MODE_BC, but uses higher
76+
/// quality settings that result in better image quality at the cost of
77+
/// 30%-40% longer compression time.
78+
TEXTURE_LOAD_COMPRESS_MODE_BC_HIGH_QUAL,
79+
};
5780

5881
/// Texture loading information
5982
struct TextureLoadInfo
@@ -100,6 +123,9 @@ struct TextureLoadInfo
100123
/// Coarse mip filter type, see Diligent::TEXTURE_LOAD_MIP_FILTER.
101124
TEXTURE_LOAD_MIP_FILTER MipFilter DEFAULT_VALUE(TEXTURE_LOAD_MIP_FILTER_DEFAULT);
102125

126+
/// Texture compression mode, see Diligent::TEXTURE_LOAD_COMPRESS_MODE.
127+
TEXTURE_LOAD_COMPRESS_MODE CompressMode DEFAULT_VALUE(TEXTURE_LOAD_COMPRESS_MODE_NONE);
128+
103129
/// Texture component swizzle.
104130
///
105131
/// \remarks When the number of channels in the source image is less than

TextureLoader/src/TextureLoaderImpl.cpp

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <limits>
3131
#include <math.h>
3232
#include <vector>
33+
#include <array>
3334

3435
#include "TextureLoaderImpl.hpp"
3536
#include "GraphicsAccessories.hpp"
@@ -43,6 +44,10 @@
4344
#include "DataBlobImpl.hpp"
4445
#include "Align.hpp"
4546

47+
#define STB_DXT_STATIC
48+
#define STB_DXT_IMPLEMENTATION
49+
#include "../../ThirdParty/stb/stb_dxt.h"
50+
4651
extern "C"
4752
{
4853
Diligent::DECODE_PNG_RESULT Diligent_DecodePng(Diligent::IDataBlob* pSrcPngBits,
@@ -194,8 +199,8 @@ void TextureLoaderImpl::LoadFromImage(Image* pImage, const TextureLoadInfo& TexL
194199
{
195200
VERIFY_EXPR(pImage != nullptr);
196201

197-
const auto& ImgDesc = pImage->GetDesc();
198-
const auto CompSize = GetValueSize(ImgDesc.ComponentType);
202+
const ImageDesc& ImgDesc = pImage->GetDesc();
203+
const Uint32 CompSize = GetValueSize(ImgDesc.ComponentType);
199204

200205
m_TexDesc.Type = RESOURCE_DIM_TEX_2D;
201206
m_TexDesc.Width = ImgDesc.Width;
@@ -335,8 +340,126 @@ void TextureLoaderImpl::LoadFromImage(Image* pImage, const TextureLoadInfo& TexL
335340
}
336341
}
337342
}
343+
344+
if (TexLoadInfo.CompressMode != TEXTURE_LOAD_COMPRESS_MODE_NONE)
345+
{
346+
CompressSubresources(NumComponents, ImgDesc.NumComponents, TexLoadInfo);
347+
m_pImage.Release();
348+
}
338349
}
339350

351+
void TextureLoaderImpl::CompressSubresources(Uint32 NumComponents, Uint32 NumSrcComponents, const TextureLoadInfo& TexLoadInfo)
352+
{
353+
TEXTURE_FORMAT CompressedFormat = TEX_FORMAT_UNKNOWN;
354+
switch (NumComponents)
355+
{
356+
case 1:
357+
CompressedFormat = TEX_FORMAT_BC4_UNORM;
358+
break;
359+
360+
case 2:
361+
CompressedFormat = TEX_FORMAT_BC5_UNORM;
362+
break;
363+
364+
case 4:
365+
if (NumSrcComponents == 3)
366+
CompressedFormat = TexLoadInfo.IsSRGB ? TEX_FORMAT_BC1_UNORM_SRGB : TEX_FORMAT_BC1_UNORM;
367+
else if (NumSrcComponents == 4)
368+
CompressedFormat = TexLoadInfo.IsSRGB ? TEX_FORMAT_BC3_UNORM_SRGB : TEX_FORMAT_BC3_UNORM;
369+
else
370+
UNEXPECTED("Unexpected number of source components ", NumSrcComponents);
371+
break;
372+
373+
default:
374+
UNEXPECTED("Unexpected number of components ", NumComponents);
375+
}
376+
377+
if (CompressedFormat == TEX_FORMAT_UNKNOWN)
378+
return;
379+
380+
m_TexDesc.Format = CompressedFormat;
381+
const TextureFormatAttribs& FmtAttribs = GetTextureFormatAttribs(CompressedFormat);
382+
383+
std::vector<std::vector<Uint8>> CompressedMips(m_SubResources.size());
384+
for (Uint32 slice = 0; slice < m_TexDesc.GetArraySize(); ++slice)
385+
{
386+
for (Uint32 mip = 0; mip < m_TexDesc.MipLevels; ++mip)
387+
{
388+
const Uint32 SubResIndex = slice * m_TexDesc.MipLevels + mip;
389+
TextureSubResData& SubResData = m_SubResources[SubResIndex];
390+
std::vector<Uint8>& CompressedMip = CompressedMips[SubResIndex];
391+
392+
const MipLevelProperties CompressedMipProps = GetMipLevelProperties(m_TexDesc, mip);
393+
const Uint32 MaxCol = CompressedMipProps.LogicalWidth - 1;
394+
const Uint32 MaxRow = CompressedMipProps.LogicalHeight - 1;
395+
const size_t CompressedStride = static_cast<size_t>(CompressedMipProps.RowSize);
396+
CompressedMip.resize(CompressedStride * CompressedMipProps.StorageHeight);
397+
398+
for (Uint32 row = 0; row < CompressedMipProps.StorageHeight; row += FmtAttribs.BlockHeight)
399+
{
400+
const Uint32 row0 = row;
401+
const Uint32 row1 = std::min(row + 1, MaxRow);
402+
const Uint32 row2 = std::min(row + 2, MaxRow);
403+
const Uint32 row3 = std::min(row + 3, MaxRow);
404+
405+
for (Uint32 col = 0; col < CompressedMipProps.StorageWidth; col += FmtAttribs.BlockWidth)
406+
{
407+
const Uint32 col0 = col;
408+
const Uint32 col1 = std::min(col + 1, MaxCol);
409+
const Uint32 col2 = std::min(col + 2, MaxCol);
410+
const Uint32 col3 = std::min(col + 3, MaxCol);
411+
412+
auto ReadBlockData = [&](auto& BlockData) {
413+
using T = typename std::decay_t<decltype(BlockData)>::value_type;
414+
415+
const T* SrcPtr = static_cast<const T*>(SubResData.pData);
416+
const size_t SrcStride = static_cast<size_t>(SubResData.Stride) / sizeof(T);
417+
// clang-format off
418+
BlockData =
419+
{
420+
SrcPtr[col0 + SrcStride * row0], SrcPtr[col1 + SrcStride * row0], SrcPtr[col2 + SrcStride * row0], SrcPtr[col3 + SrcStride * row0],
421+
SrcPtr[col0 + SrcStride * row1], SrcPtr[col1 + SrcStride * row1], SrcPtr[col2 + SrcStride * row1], SrcPtr[col3 + SrcStride * row1],
422+
SrcPtr[col0 + SrcStride * row2], SrcPtr[col1 + SrcStride * row2], SrcPtr[col2 + SrcStride * row2], SrcPtr[col3 + SrcStride * row2],
423+
SrcPtr[col0 + SrcStride * row3], SrcPtr[col1 + SrcStride * row3], SrcPtr[col2 + SrcStride * row3], SrcPtr[col3 + SrcStride * row3]
424+
};
425+
// clang-format on
426+
return reinterpret_cast<const unsigned char*>(BlockData.data());
427+
};
428+
429+
Uint8* pDst = &CompressedMip[(col / FmtAttribs.BlockWidth) * FmtAttribs.ComponentSize + CompressedStride * (row / FmtAttribs.BlockHeight)];
430+
if (NumComponents == 1)
431+
{
432+
std::array<Uint8, 16> BlockData8;
433+
stb_compress_bc4_block(pDst, ReadBlockData(BlockData8));
434+
}
435+
else if (NumComponents == 2)
436+
{
437+
std::array<Uint16, 16> BlockData16;
438+
stb_compress_bc5_block(pDst, ReadBlockData(BlockData16));
439+
}
440+
else if (NumComponents == 4)
441+
{
442+
std::array<Uint32, 16> BlockData32;
443+
const int StbDxtMode = (TexLoadInfo.CompressMode == TEXTURE_LOAD_COMPRESS_MODE_BC_HIGH_QUAL) ? STB_DXT_HIGHQUAL : STB_DXT_NORMAL;
444+
const int StoreAlpha = NumSrcComponents == 4 ? 1 : 0;
445+
stb_compress_dxt_block(pDst, ReadBlockData(BlockData32), StoreAlpha, StbDxtMode);
446+
}
447+
else
448+
{
449+
UNEXPECTED("Unexpected number of components");
450+
}
451+
}
452+
}
453+
454+
SubResData.pData = CompressedMip.data();
455+
SubResData.Stride = CompressedStride;
456+
}
457+
}
458+
459+
m_TexDesc.Width = AlignUp(m_TexDesc.Width, FmtAttribs.BlockWidth);
460+
m_TexDesc.Height = AlignUp(m_TexDesc.Height, FmtAttribs.BlockHeight);
461+
m_Mips.swap(CompressedMips);
462+
}
340463

341464
void CreateTextureLoaderFromFile(const char* FilePath,
342465
IMAGE_FILE_FORMAT FileFormat,

0 commit comments

Comments
 (0)