Add GECompressorS3TCBC3

This commit is contained in:
Benau 2022-08-06 09:48:11 +08:00
parent 4f32435ef2
commit b4b0ddc620
11 changed files with 186 additions and 69 deletions

View File

@ -242,6 +242,21 @@ else()
set(ENET_LIBRARIES "enet")
endif()
if(NOT SERVER_ONLY)
if(USE_SYSTEM_SQUISH)
find_library(SQUISH_LIBRARY NAMES squish libsquish)
find_path(SQUISH_INCLUDEDIR NAMES squish.h PATHS)
endif()
if (NOT SQUISH_LIBRARY OR NOT SQUISH_INCLUDEDIR)
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libsquish")
include_directories(BEFORE "${PROJECT_SOURCE_DIR}/lib/libsquish")
SET(SQUISH_LIBRARY squish)
else()
include_directories("${SQUISH_INCLUDEDIR}")
MESSAGE(STATUS "Use system libsquish: ${SQUISH_LIBRARY}")
endif()
endif()
if(NOT SERVER_ONLY)
find_library(SHADERC_LIBRARY NAMES shaderc_shared libshaderc_shared)
find_path(SHADERC_INCLUDEDIR NAMES shaderc/shaderc.hpp PATHS)
@ -322,21 +337,6 @@ else()
MESSAGE(STATUS "Use system libmcpp: ${MCPP_LIBRARY}")
endif()
if(NOT SERVER_ONLY)
if(USE_SYSTEM_SQUISH)
find_library(SQUISH_LIBRARY NAMES squish libsquish)
find_path(SQUISH_INCLUDEDIR NAMES squish.h PATHS)
endif()
if (NOT SQUISH_LIBRARY OR NOT SQUISH_INCLUDEDIR)
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libsquish")
include_directories(BEFORE "${PROJECT_SOURCE_DIR}/lib/libsquish")
SET(SQUISH_LIBRARY squish)
else()
include_directories("${SQUISH_INCLUDEDIR}")
MESSAGE(STATUS "Use system libsquish: ${SQUISH_LIBRARY}")
endif()
endif()
if (NOT SERVER_ONLY)
# SDL2
find_library(SDL2_LIBRARY NAMES SDL2 libSDL2)

View File

@ -24,6 +24,7 @@ endif()
set(GE_SOURCES
src/gl.c
src/ge_compressor_s3tc_bc3.cpp
src/ge_culling_tool.cpp
src/ge_dx9_texture.cpp
src/ge_main.cpp
@ -55,3 +56,5 @@ if(NOT APPLE OR DLOPEN_MOLTENVK)
endif()
add_library(graphics_engine STATIC ${GE_SOURCES})
target_link_libraries(graphics_engine ${SQUISH_LIBRARY})

View File

@ -13,6 +13,7 @@ struct GEConfig
{
bool m_disable_npot_texture;
bool m_convert_irrlicht_mesh;
bool m_texture_compression;
};
void setVideoDriver(irr::video::IVideoDriver* driver);
@ -34,6 +35,12 @@ inline size_t getPadding(size_t in, size_t alignment)
else
return alignment - mod;
}
inline int get4x4CompressedTextureSize(int width, int height)
{
int blockcount = ((width + 3) / 4) * ((height + 3) / 4);
int blocksize = 4 * 4;
return blockcount * blocksize;
}
}
#endif

View File

@ -0,0 +1,98 @@
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_main.hpp"
#include <algorithm>
#include <cassert>
#include <squish.h>
static_assert(squish::kColourClusterFit == (1 << 5), "Wrong header");
static_assert(squish::kColourRangeFit == (1 << 6), "Wrong header");
static_assert(squish::kColourIterativeClusterFit == (1 << 8), "Wrong header");
// ============================================================================
extern "C" void squishCompressImage(uint8_t* rgba, int width, int height,
int pitch, void* blocks, unsigned flags)
{
// This function is copied from CompressImage in libsquish to avoid omp
// if enabled by shared libsquish, because we are already using
// multiple thread
for (int y = 0; y < height; y += 4)
{
// initialise the block output
uint8_t* target_block = reinterpret_cast<uint8_t*>(blocks);
target_block += ((y >> 2) * ((width + 3) >> 2)) * 16;
for (int x = 0; x < width; x += 4)
{
// build the 4x4 block of pixels
uint8_t source_rgba[16 * 4];
uint8_t* target_pixel = source_rgba;
int mask = 0;
for (int py = 0; py < 4; py++)
{
for (int px = 0; px < 4; px++)
{
// get the source pixel in the image
int sx = x + px;
int sy = y + py;
// enable if we're in the image
if (sx < width && sy < height)
{
// copy the rgba value
uint8_t* source_pixel = rgba + pitch * sy + 4 * sx;
memcpy(target_pixel, source_pixel, 4);
// enable this pixel
mask |= (1 << (4 * py + px));
}
// advance to the next pixel
target_pixel += 4;
}
}
// compress it into the output
squish::CompressMasked(source_rgba, mask, target_block, flags);
// advance
target_block += 16;
}
}
} // squishCompressImage
namespace GE
{
// ----------------------------------------------------------------------------
GECompressorS3TCBC3::GECompressorS3TCBC3(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map)
: GEMipmapGenerator(texture, channels, size, normal_map)
{
assert(channels == 4);
size_t total_size = 0;
m_mipmap_sizes = 0;
for (unsigned i = 0; i < m_levels.size(); i++)
{
GEImageLevel& level = m_levels[i];
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
total_size += cur_size;
if (i > 0)
m_mipmap_sizes += cur_size;
}
std::vector<GEImageLevel> compressed_levels;
m_compressed_data = new uint8_t[total_size];
uint8_t* cur_offset = m_compressed_data;
const unsigned tc_flag = squish::kDxt5 | squish::kColourRangeFit;
for (GEImageLevel& level : m_levels)
{
squishCompressImage((uint8_t*)level.m_data, level.m_dim.Width,
level.m_dim.Height, level.m_dim.Width * channels,
cur_offset, tc_flag);
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
compressed_levels.push_back({ level.m_dim, cur_size, cur_offset });
cur_offset += cur_size;
}
freeMipmapCascade();
std::swap(compressed_levels, m_levels);
} // GECompressorS3TCBC3
}

View File

@ -0,0 +1,23 @@
#ifndef HEADER_GE_COMPRESSOR_S3TC_BC3_HPP
#define HEADER_GE_COMPRESSOR_S3TC_BC3_HPP
#include "ge_mipmap_generator.hpp"
namespace GE
{
class GECompressorS3TCBC3 : public GEMipmapGenerator
{
private:
uint8_t* m_compressed_data;
public:
// ------------------------------------------------------------------------
GECompressorS3TCBC3(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map);
// ------------------------------------------------------------------------
~GECompressorS3TCBC3() { delete [] m_compressed_data; }
}; // GECompressorS3TCBC3
}
#endif

View File

@ -26,6 +26,7 @@ class GEMipmapGenerator
private:
imMipmapCascade* m_cascade;
protected:
unsigned m_mipmap_sizes;
std::vector<GEImageLevel> m_levels;
@ -87,7 +88,7 @@ public:
m_levels[i].m_data = m_cascade->mipmap[i];
}
// ------------------------------------------------------------------------
~GEMipmapGenerator() { freeMipmapCascade(); }
virtual ~GEMipmapGenerator() { freeMipmapCascade(); }
// ------------------------------------------------------------------------
unsigned getMipmapSizes() const { return m_mipmap_sizes; }
// ------------------------------------------------------------------------

View File

@ -26,6 +26,7 @@ uint32_t g_max_sampler_supported = 0;
bool g_supports_multi_draw_indirect = false;
bool g_supports_base_vertex_rendering = true;
bool g_supports_compute_in_main_queue = false;
bool g_supports_s3tc_bc3 = false;
} // GEVulkanFeatures
// ============================================================================
@ -65,6 +66,10 @@ void GEVulkanFeatures::init(GEVulkanDriver* vk)
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_R8_UNORM, &format_properties);
g_supports_r8_blit = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
format_properties = {};
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_BC3_UNORM_BLOCK, &format_properties);
g_supports_s3tc_bc3 = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
uint32_t extension_count;
vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL,
@ -182,6 +187,9 @@ void GEVulkanFeatures::printStats()
os::Printer::log(
"Vulkan supports compute in main queue",
g_supports_compute_in_main_queue ? "true" : "false");
os::Printer::log(
"Vulkan supports s3 texture compression (bc3, dxt5)",
g_supports_s3tc_bc3 ? "true" : "false");
os::Printer::log(
"Vulkan descriptor indexes can be dynamically non-uniform",
g_supports_non_uniform_indexing ? "true" : "false");
@ -261,4 +269,10 @@ bool GEVulkanFeatures::supportsComputeInMainQueue()
return g_supports_compute_in_main_queue;
} // supportsComputeInMainQueue
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsS3TCBC3()
{
return g_supports_s3tc_bc3;
} // supportsS3TCBC3
}

View File

@ -34,6 +34,8 @@ bool supportsMultiDrawIndirect();
bool supportsBaseVertexRendering();
// ----------------------------------------------------------------------------
bool supportsComputeInMainQueue();
// ----------------------------------------------------------------------------
bool supportsS3TCBC3();
}; // GEVulkanFeatures
}

View File

@ -2,6 +2,7 @@
#include "ge_main.hpp"
#include "ge_mipmap_generator.hpp"
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_features.hpp"
@ -121,8 +122,20 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
{
const bool normal_map = (std::string(NamedPath.getPtr()).find(
"_Normal.") != std::string::npos);
mipmap_generator = new GEMipmapGenerator(texture_data, channels,
m_size, normal_map);
bool texture_compression = getGEConfig()->m_texture_compression;
if (texture_compression && GEVulkanFeatures::supportsS3TCBC3())
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_BC3_UNORM_BLOCK;
mipmap_generator = new GECompressorS3TCBC3(texture_data, channels,
m_size, normal_map);
}
else
{
mipmap_generator = new GEMipmapGenerator(texture_data, channels,
m_size, normal_map);
}
mipmap_data_size = mipmap_generator->getMipmapSizes();
}

View File

@ -712,54 +712,9 @@ void SPTexture::generateHQMipmap(void* in,
#endif
} // generateHQMipmap
// ----------------------------------------------------------------------------
void SPTexture::squishCompressImage(uint8_t* rgba, int width, int height,
int pitch, void* blocks, unsigned flags)
{
#if !(defined(SERVER_ONLY) || defined(MOBILE_STK))
// This function is copied from CompressImage in libsquish to avoid omp
// if enabled by shared libsquish, because we are already using
// multiple thread
for (int y = 0; y < height; y += 4)
{
// initialise the block output
uint8_t* target_block = reinterpret_cast<uint8_t*>(blocks);
target_block += ((y >> 2) * ((width + 3) >> 2)) * 16;
for (int x = 0; x < width; x += 4)
{
// build the 4x4 block of pixels
uint8_t source_rgba[16 * 4];
uint8_t* target_pixel = source_rgba;
int mask = 0;
for (int py = 0; py < 4; py++)
{
for (int px = 0; px < 4; px++)
{
// get the source pixel in the image
int sx = x + px;
int sy = y + py;
// enable if we're in the image
if (sx < width && sy < height)
{
// copy the rgba value
uint8_t* source_pixel = rgba + pitch * sy + 4 * sx;
memcpy(target_pixel, source_pixel, 4);
// enable this pixel
mask |= (1 << (4 * py + px));
}
// advance to the next pixel
target_pixel += 4;
}
}
// compress it into the output
squish::CompressMasked(source_rgba, mask, target_block, flags);
// advance
target_block += 16;
}
}
#endif
} // squishCompressImage
// ============================================================================
extern "C" void squishCompressImage(uint8_t* rgba, int width, int height,
int pitch, void* blocks, unsigned flags);
// ----------------------------------------------------------------------------
std::vector<std::pair<core::dimension2du, unsigned> >
SPTexture::compressTexture(std::shared_ptr<video::IImage>& image)

View File

@ -38,6 +38,10 @@ class Material;
using namespace irr;
// ----------------------------------------------------------------------------
extern "C" void squishCompressImage(uint8_t* rgba, int width, int height,
int pitch, void* blocks, unsigned flags);
namespace SP
{
@ -58,9 +62,6 @@ private:
const bool m_undo_srgb;
// ------------------------------------------------------------------------
void squishCompressImage(uint8_t* rgba, int width, int height, int pitch,
void* blocks, unsigned flags);
// ------------------------------------------------------------------------
void generateHQMipmap(void* in,
const std::vector<std::pair<core::dimension2du,