From b4b0ddc620d129e65cc11d43e01d1d6e0f2fe0ff Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 6 Aug 2022 09:48:11 +0800 Subject: [PATCH] Add GECompressorS3TCBC3 --- CMakeLists.txt | 30 +++--- lib/graphics_engine/CMakeLists.txt | 3 + lib/graphics_engine/include/ge_main.hpp | 7 ++ .../src/ge_compressor_s3tc_bc3.cpp | 98 +++++++++++++++++++ .../src/ge_compressor_s3tc_bc3.hpp | 23 +++++ .../src/ge_mipmap_generator.hpp | 3 +- .../src/ge_vulkan_features.cpp | 14 +++ .../src/ge_vulkan_features.hpp | 2 + lib/graphics_engine/src/ge_vulkan_texture.cpp | 17 +++- src/graphics/sp/sp_texture.cpp | 51 +--------- src/graphics/sp/sp_texture.hpp | 7 +- 11 files changed, 186 insertions(+), 69 deletions(-) create mode 100644 lib/graphics_engine/src/ge_compressor_s3tc_bc3.cpp create mode 100644 lib/graphics_engine/src/ge_compressor_s3tc_bc3.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bea9ac82..463d3a66c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/lib/graphics_engine/CMakeLists.txt b/lib/graphics_engine/CMakeLists.txt index 4bcfb47ad..88f9b8329 100644 --- a/lib/graphics_engine/CMakeLists.txt +++ b/lib/graphics_engine/CMakeLists.txt @@ -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}) diff --git a/lib/graphics_engine/include/ge_main.hpp b/lib/graphics_engine/include/ge_main.hpp index 5234e6275..ec799b3a5 100644 --- a/lib/graphics_engine/include/ge_main.hpp +++ b/lib/graphics_engine/include/ge_main.hpp @@ -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 diff --git a/lib/graphics_engine/src/ge_compressor_s3tc_bc3.cpp b/lib/graphics_engine/src/ge_compressor_s3tc_bc3.cpp new file mode 100644 index 000000000..5e8c53abc --- /dev/null +++ b/lib/graphics_engine/src/ge_compressor_s3tc_bc3.cpp @@ -0,0 +1,98 @@ +#include "ge_compressor_s3tc_bc3.hpp" +#include "ge_main.hpp" + +#include +#include + +#include +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(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& 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 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 + +} diff --git a/lib/graphics_engine/src/ge_compressor_s3tc_bc3.hpp b/lib/graphics_engine/src/ge_compressor_s3tc_bc3.hpp new file mode 100644 index 000000000..63f60981d --- /dev/null +++ b/lib/graphics_engine/src/ge_compressor_s3tc_bc3.hpp @@ -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& size, + bool normal_map); + // ------------------------------------------------------------------------ + ~GECompressorS3TCBC3() { delete [] m_compressed_data; } +}; // GECompressorS3TCBC3 + +} + +#endif diff --git a/lib/graphics_engine/src/ge_mipmap_generator.hpp b/lib/graphics_engine/src/ge_mipmap_generator.hpp index 57be1b8b3..bee26fd3c 100644 --- a/lib/graphics_engine/src/ge_mipmap_generator.hpp +++ b/lib/graphics_engine/src/ge_mipmap_generator.hpp @@ -26,6 +26,7 @@ class GEMipmapGenerator private: imMipmapCascade* m_cascade; +protected: unsigned m_mipmap_sizes; std::vector 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; } // ------------------------------------------------------------------------ diff --git a/lib/graphics_engine/src/ge_vulkan_features.cpp b/lib/graphics_engine/src/ge_vulkan_features.cpp index 2895d70d4..46b569e69 100644 --- a/lib/graphics_engine/src/ge_vulkan_features.cpp +++ b/lib/graphics_engine/src/ge_vulkan_features.cpp @@ -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 + } diff --git a/lib/graphics_engine/src/ge_vulkan_features.hpp b/lib/graphics_engine/src/ge_vulkan_features.hpp index de948e3d4..656ee1347 100644 --- a/lib/graphics_engine/src/ge_vulkan_features.hpp +++ b/lib/graphics_engine/src/ge_vulkan_features.hpp @@ -34,6 +34,8 @@ bool supportsMultiDrawIndirect(); bool supportsBaseVertexRendering(); // ---------------------------------------------------------------------------- bool supportsComputeInMainQueue(); +// ---------------------------------------------------------------------------- +bool supportsS3TCBC3(); }; // GEVulkanFeatures } diff --git a/lib/graphics_engine/src/ge_vulkan_texture.cpp b/lib/graphics_engine/src/ge_vulkan_texture.cpp index 01d5d10f9..58de43243 100644 --- a/lib/graphics_engine/src/ge_vulkan_texture.cpp +++ b/lib/graphics_engine/src/ge_vulkan_texture.cpp @@ -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(); } diff --git a/src/graphics/sp/sp_texture.cpp b/src/graphics/sp/sp_texture.cpp index 000d66cff..6d9f86f66 100644 --- a/src/graphics/sp/sp_texture.cpp +++ b/src/graphics/sp/sp_texture.cpp @@ -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(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 > SPTexture::compressTexture(std::shared_ptr& image) diff --git a/src/graphics/sp/sp_texture.hpp b/src/graphics/sp/sp_texture.hpp index e81ff5d1c..19a5bc81c 100644 --- a/src/graphics/sp/sp_texture.hpp +++ b/src/graphics/sp/sp_texture.hpp @@ -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