diff --git a/CMakeLists.txt b/CMakeLists.txt index b5bfc75457..fd0ccb601e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,6 +237,37 @@ if(IGL_WITH_SAMPLES) endif() endif() +# KTX-Software forcibly includes fmt so prefer its version when building IGL_WITH_IGLU or IGL_WITH_SAMPLES +if (NOT EMSCRIPTEN) + if (IGL_WITH_IGLU OR IGL_WITH_SAMPLES) + set(KTX_FEATURE_DOC OFF CACHE BOOL "") + set(KTX_FEATURE_GL_UPLOAD OFF CACHE BOOL "") + set(KTX_FEATURE_JNI OFF CACHE BOOL "") + set(KTX_FEATURE_KTX1 ON CACHE BOOL "") + set(KTX_FEATURE_KTX2 ON CACHE BOOL "") + set(KTX_FEATURE_LOADTEST_APPS OFF CACHE BOOL "") + set(KTX_FEATURE_STATIC_LIBRARY ON CACHE BOOL "") + set(KTX_FEATURE_TESTS OFF CACHE BOOL "") + set(KTX_FEATURE_TOOLS OFF CACHE BOOL "") + set(KTX_FEATURE_VK_UPLOAD OFF CACHE BOOL "") + add_subdirectory(third-party/deps/src/ktx-software) + igl_set_folder(astcenc-avx2-static "third-party/ktx-software") + igl_set_folder(ktx "third-party/ktx-software") + igl_set_folder(ktx_read "third-party/ktx-software") + igl_set_folder(ktx_version "third-party/ktx-software") + igl_set_folder(makedfd2vk "third-party/ktx-software") + igl_set_folder(makevk2dfd "third-party/ktx-software") + igl_set_folder(mkvk "third-party/ktx-software") + igl_set_folder(mkvkformatfiles "third-party/ktx-software") + igl_set_folder(obj_basisu_cbind "third-party/ktx-software") + igl_set_folder(objUtil "third-party/ktx-software") + igl_set_folder(fmt "third-party") + elseif (IGL_WITH_VULKAN) + add_subdirectory(third-party/deps/src/fmt "fmt") + igl_set_folder(fmt "third-party") + endif() +endif() + if(IGL_WITH_TRACY) target_link_libraries(IGLLibrary PUBLIC TracyClient) endif() diff --git a/LICENSE.md b/LICENSE.md index 48bc7c3072..bf1da3d443 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -53,9 +53,6 @@ https://github.com/nigels-com/glew/blob/master/LICENSE.txt glfw https://github.com/glfw/glfw/blob/master/LICENSE.md -gli -https://github.com/g-truc/gli#readme - glm https://github.com/g-truc/glm @@ -71,6 +68,9 @@ https://github.com/ocornut/imgui/blob/master/LICENSE.txt ios-cmake https://github.com/leetal/ios-cmake/blob/master/LICENSE.md +KTX-Software +https://github.com/KhronosGroup/KTX-Software/blob/main/LICENSE.md + Meshoptimizer https://github.com/zeux/meshoptimizer/blob/master/LICENSE.md diff --git a/samples/desktop/CMakeLists.txt b/samples/desktop/CMakeLists.txt index f948da3461..22bf3a971b 100644 --- a/samples/desktop/CMakeLists.txt +++ b/samples/desktop/CMakeLists.txt @@ -25,6 +25,7 @@ macro(ADD_DEMO app) target_link_libraries(${app} PRIVATE meshoptimizer) target_link_libraries(${app} PRIVATE tinyobjloader) target_link_libraries(${app} PRIVATE glfw) + target_link_libraries(${app} PRIVATE ktx) if(IGL_WITH_IGLU) target_link_libraries(${app} PRIVATE IGLUimgui) target_link_libraries(${app} PRIVATE IGLUsimple_renderer) diff --git a/samples/desktop/Tiny/Tiny_MeshLarge.cpp b/samples/desktop/Tiny/Tiny_MeshLarge.cpp index 2a41da24e6..e2e9f82fc4 100644 --- a/samples/desktop/Tiny/Tiny_MeshLarge.cpp +++ b/samples/desktop/Tiny/Tiny_MeshLarge.cpp @@ -28,15 +28,13 @@ #include #include +#include #include #include #include -// GLI should be included after GLM -#include -#include -#include +#include #include #include @@ -790,7 +788,7 @@ std::string convertFileName(std::string fileName) { std::replace(fileName.begin(), fileName.end(), '\\', '_'); // return absolute compressed filename - return compressedPathPrefix + fileName + ".ktx"; + return compressedPathPrefix + fileName + ".ktx2"; } [[maybe_unused]] void stringReplaceAll(std::string& s, const std::string& searchString, @@ -2110,22 +2108,32 @@ void generateCompressedTexture(LoadedImage img) { const auto numMipLevels = TextureDesc::calcNumMipLevels(img.w, img.h); // Go over all generated mipmap and create a compressed texture - gli::texture2d::extent_type extents; - extents.x = img.w; - extents.y = img.h; - // Create gli texture + // Create ktx2 texture // hard coded and support only BC7 format - gli::texture2d gliTex2d = - gli::texture2d(gli::FORMAT_RGBA_BP_UNORM_BLOCK16, extents, numMipLevels); + ktxTextureCreateInfo createInfo = { + .vkFormat = VK_FORMAT_BC7_UNORM_BLOCK, + .baseWidth = static_cast(img.w), + .baseHeight = static_cast(img.h), + .baseDepth = 1u, + .numDimensions = 2u, + .numLevels = numMipLevels, + .numLayers = 1u, + .numFaces = 1u, + .generateMipmaps = KTX_FALSE, // Mipmaps are explicitly provided + }; + + ktxTexture2* texture = nullptr; + auto error = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture); + IGL_ASSERT(error == KTX_SUCCESS); + + IGL_SCOPE_EXIT { + ktxTexture_Destroy(ktxTexture(texture)); + }; uint32_t w = img.w; uint32_t h = img.h; - size_t imgsize = 0; for (uint32_t i = 0; i < numMipLevels; ++i) { - // mipi level - gli::image gliImage = gliTex2d[i]; - std::vector destPixels(w * h * img.channels); // resize @@ -2141,8 +2149,13 @@ void generateCompressedTexture(LoadedImage img) { // compress auto packedImage16 = Compress::getCompressedImage( destPixels.data(), w, h, img.channels, false, &loaderShouldExit_); - memcpy(gliImage.data(), packedImage16.data(), sizeof(block16) * packedImage16.size()); - imgsize += gliTex2d.size(i); + ktxTexture_SetImageFromMemory(ktxTexture(texture), + i, + 0, + 0, + reinterpret_cast(packedImage16.data()), + sizeof(block16) * packedImage16.size()); + h = h > 1 ? h >> 1 : 1; w = w > 1 ? w >> 1 : 1; @@ -2151,21 +2164,7 @@ void generateCompressedTexture(LoadedImage img) { } } - gli::save_ktx(gliTex2d, img.compressedFileName.c_str()); -} - -igl::TextureFormat gli2iglTextureFormat(gli::texture2d::format_type format) { - switch (format) { - case gli::FORMAT_RGBA32_SFLOAT_PACK32: - return igl::TextureFormat::RGBA_F32; - case gli::FORMAT_RG16_SFLOAT_PACK16: - return igl::TextureFormat::RG_F16; - default: - break; - } - - IGL_ASSERT_NOT_REACHED(); - return igl::TextureFormat::RGBA_UNorm8; + ktxTexture_WriteToNamedFile(ktxTexture(texture), img.compressedFileName.c_str()); } LoadedImage loadImage(const char* fileName, int channels) { @@ -2257,34 +2256,45 @@ void loadCubemapTexture(const std::string& fileNameKTX, std::shared_ptrvkFormat == VK_FORMAT_R32G32B32A32_SFLOAT)) { IGL_ASSERT_MSG(false, "Texture format not supported"); return; } - auto texRefRange = - TextureRangeDesc::new2D(0, 0, (size_t)texRef.extent().x, (size_t)texRef.extent().y); + auto texRefRange = TextureRangeDesc::new2D( + 0, 0, static_cast(texture->baseWidth), static_cast(texture->baseHeight)); // If compression is enabled, upload all mip levels if (kEnableCompression) { texRefRange.numMipLevels = TextureDesc::calcNumMipLevels(texRefRange.width, texRefRange.height); } + auto desc = + TextureDesc::newCube(igl::vulkan::util::vkTextureFormatToTextureFormat(texture->vkFormat), + texRefRange.width, + texRefRange.height, + TextureDesc::TextureUsageBits::Sampled, + fileNameKTX.c_str()); + desc.numMipLevels = texRefRange.numMipLevels; + tex = device_->createTexture(desc, nullptr); + IGL_ASSERT(tex); for (uint8_t face = 0; face < 6; ++face) { - if (!tex) { - auto desc = TextureDesc::newCube(gli2iglTextureFormat(texRef.format()), - texRef.extent().x, - texRef.extent().y, - TextureDesc::TextureUsageBits::Sampled, - fileNameKTX.c_str()); - desc.numMipLevels = TextureDesc::calcNumMipLevels(texRef.extent().x, texRef.extent().y); - tex = device_->createTexture(desc, nullptr); - IGL_ASSERT(tex); - } + for (size_t i = 0; i < desc.numMipLevels; ++i) { + size_t offset; + error = ktxTexture_GetImageOffset(ktxTexture(texture), i, 0, face, &offset); + IGL_ASSERT(error == KTX_SUCCESS); - tex->upload(texRefRange.atFace(face), texRef.data(0, face, 0)); + tex->upload(tex->getCubeFaceRange(face, i), texture->pData + offset); + } } if (!kEnableCompression) { @@ -2293,7 +2303,7 @@ void loadCubemapTexture(const std::string& fileNameKTX, std::shared_ptr(w), + .baseHeight = static_cast(h), + .baseDepth = 1u, + .numDimensions = 2u, + .numLevels = numMipLevels, + .numLayers = 1u, + .numFaces = 6u, + .generateMipmaps = KTX_FALSE, // Mipmaps are explicitly provided + }; - gli::texture_cube gliTexCube = - gli::texture_cube(gli::FORMAT_RGBA32_SFLOAT_PACK32, extents, miplevels); + ktxTexture2* texture = nullptr; + auto error = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture); + IGL_ASSERT(error == KTX_SUCCESS); - const int numFacePixels = w * h; + const size_t numFacePixels = static_cast(w) * static_cast(h); + const std::unique_ptr facePixels = + std::make_unique(numFacePixels * static_cast(4)); for (size_t face = 0; face != 6; face++) { + float* dst = facePixels.get(); const vec3* src = reinterpret_cast(bmp.data_.data()) + face * numFacePixels; for (int y = 0; y != h; y++) { for (int x = 0; x != w; x++) { const vec3& rgb = src[x + y * w]; - gliTexCube[face].store(gli::texture2d::extent_type{x, y}, 0, vec4(rgb, 0.0f)); + *dst++ = rgb.x; + *dst++ = rgb.y; + *dst++ = rgb.z; + *dst++ = 0.0f; } } + ktxTexture_SetImageFromMemory(ktxTexture(texture), + 0, + 0, + face, + reinterpret_cast(facePixels.get()), + sizeof(float) * numFacePixels * static_cast(4)); } - return gliTexCube; + return texture; } -void generateMipmaps(const std::string& outFilename, gli::texture_cube& cubemap) { +void generateMipmaps(const std::string& outFilename, ktxTexture2* cubemap) { IGL_LOG_INFO("Generating mipmaps"); - auto prevWidth = cubemap.extent().x; - auto prevHeight = cubemap.extent().y; + const std::unique_ptr buffer = + std::make_unique(static_cast(cubemap->baseWidth) * + static_cast(cubemap->baseHeight) * static_cast(4)); + auto prevWidth = cubemap->baseWidth; + auto prevHeight = cubemap->baseHeight; for (size_t face = 0; face < 6; ++face) { IGL_LOG_INFO("."); - for (size_t miplevel = 1; miplevel <= cubemap.max_level(); ++miplevel) { + for (size_t miplevel = 1; miplevel < cubemap->numLevels; ++miplevel) { IGL_LOG_INFO(":"); const auto width = prevWidth > 1 ? prevWidth >> 1 : 1; const auto height = prevHeight > 1 ? prevWidth >> 1 : 1; - stbir_resize_float((const float*)cubemap.data(0, face, miplevel - 1), + size_t prevOffset; + auto error = + ktxTexture_GetImageOffset(ktxTexture(cubemap), miplevel - 1, 0, face, &prevOffset); + IGL_ASSERT(error == KTX_SUCCESS); + + stbir_resize_float(reinterpret_cast(cubemap->pData + prevOffset), prevWidth, prevHeight, 0, - (float*)cubemap.data(0, face, miplevel), + buffer.get(), width, height, 0, 4); + ktxTexture_SetImageFromMemory(ktxTexture(cubemap), + miplevel, + 0, + face, + reinterpret_cast(buffer.get()), + sizeof(float) * width * height * 4); + prevWidth = width; prevHeight = height; } - prevWidth = cubemap.extent().x; - prevHeight = cubemap.extent().y; + prevWidth = cubemap->baseWidth; + prevHeight = cubemap->baseHeight; } IGL_LOG_INFO("\n"); - gli::save_ktx(cubemap, outFilename); + ktxTexture_WriteToNamedFile(ktxTexture(cubemap), outFilename.c_str()); } void processCubemap(const std::string& inFilename, @@ -2376,7 +2426,7 @@ void processCubemap(const std::string& inFilename, { Bitmap bmp = convertEquirectangularMapToCubeMapFaces( Bitmap(sourceWidth, sourceHeight, 3, eBitmapFormat_Float, pxs)); - gli::texture_cube cube = gliToCube(bmp); + ktxTexture2* cube = bitmapToCube(bmp); generateMipmaps(outFilenameEnv, cube); } @@ -2390,8 +2440,12 @@ void processCubemap(const std::string& inFilename, Bitmap bmp = convertEquirectangularMapToCubeMapFaces( Bitmap(dstW, dstH, 3, eBitmapFormat_Float, out.data())); - gli::texture_cube cube = gliToCube(bmp); + ktxTexture2* cube = bitmapToCube(bmp); generateMipmaps(outFilenameIrr, cube); + + IGL_SCOPE_EXIT { + ktxTexture_Destroy(ktxTexture(cube)); + }; } } @@ -2400,9 +2454,9 @@ void loadSkyboxTexture() { static const std::string skyboxSubdir{"src/skybox_hdr/"}; static const std::string fileNameRefKTX = - contentRootFolder + skyboxFileName + "_ReferenceMap.ktx"; + contentRootFolder + skyboxFileName + "_ReferenceMap.ktx2"; static const std::string fileNameIrrKTX = - contentRootFolder + skyboxFileName + "_IrradianceMap.ktx"; + contentRootFolder + skyboxFileName + "_IrradianceMap.ktx2"; if (!std::filesystem::exists(fileNameRefKTX) || !std::filesystem::exists(fileNameIrrKTX)) { IGL_LOG_INFO("Cubemap in KTX format not found. Extracting from HDR file...\n"); @@ -2447,11 +2501,23 @@ std::shared_ptr createTexture(const LoadedImage& img) { #else // Uploading the texture const auto rangeDesc = TextureRangeDesc::new2D(0, 0, img.w, img.h, 0, desc.numMipLevels); - auto gliTex2d = gli::load_ktx(img.compressedFileName.c_str()); - if (IGL_UNEXPECTED(gliTex2d.empty())) { + ktxTexture* texture; + auto error = ktxTexture_CreateFromNamedFile( + img.compressedFileName.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture); + if (IGL_UNEXPECTED(error != KTX_SUCCESS)) { printf("Failed to load %s\n", img.compressedFileName.c_str()); } - tex->upload(rangeDesc, gliTex2d.data()); + IGL_SCOPE_EXIT { + ktxTexture_Destroy(ktxTexture(texture)); + }; + + for (size_t i = 0; i < desc.numMipLevels; ++i) { + size_t offset; + error = ktxTexture_GetImageOffset(ktxTexture(texture), i, 0, 0, &offset); + IGL_ASSERT(error == KTX_SUCCESS); + + tex->upload(rangeDesc.atMipLevel(i), texture->pData + offset); + } #endif } else { tex->upload(TextureRangeDesc::new2D(0, 0, img.w, img.h), img.pixels); diff --git a/src/igl/CMakeLists.txt b/src/igl/CMakeLists.txt index 370175be2b..46531dc3cb 100644 --- a/src/igl/CMakeLists.txt +++ b/src/igl/CMakeLists.txt @@ -22,12 +22,10 @@ add_library(IGLLibrary ${SRC_FILES} ${HEADER_FILES}) target_include_directories(IGLLibrary PUBLIC "${IGL_ROOT_DIR}/src") target_include_directories(IGLLibrary PUBLIC "${IGL_ROOT_DIR}/third-party/deps/src/glm") -add_subdirectory(${IGL_ROOT_DIR}/third-party/deps/src/fmt "fmt") target_link_libraries(IGLLibrary PUBLIC fmt) igl_set_cxxstd(IGLLibrary 17) igl_set_folder(IGLLibrary "IGL") -igl_set_folder(fmt "third-party") if(IGL_WITH_OPENGL OR IGL_WITH_OPENGLES OR IGL_WITH_WEBGL) add_subdirectory(opengl) diff --git a/third-party/bootstrap-deps.json b/third-party/bootstrap-deps.json index ee02dc0267..169f561688 100644 --- a/third-party/bootstrap-deps.json +++ b/third-party/bootstrap-deps.json @@ -67,14 +67,6 @@ "file": "bc7enc.py" } }, -{ - "name": "gli", - "source": { - "type": "git", - "url": "https://github.com/g-truc/gli.git", - "revision": "779b99ac6656e4d30c3b24e96e0136a59649a869" - } -}, { "name": "glm", "source": { @@ -162,5 +154,13 @@ "url": "https://github.com/facebook/igl/releases/download/SparkSL/SparkSL_Compiler-v0.0.1.zip", "sha1": "d636517b5fd3103f7aeca2ed6d4c9ad3e2e97964" } +}, +{ + "name": "ktx-software", + "source": { + "type": "git", + "url": "https://github.com/KhronosGroup/KTX-Software.git", + "revision": "170e75893e3d232a2c4f241afe199256f8abb6f9" + } } ]