init
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "scene_graph/components/image/astc.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "core/util/profiling.hpp"
|
||||
|
||||
#include "common/glm_common.h"
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// Windows.h defines IGNORE, so we must #undef it to avoid clashes with astc header
|
||||
# undef IGNORE
|
||||
#endif
|
||||
#include <astcenc.h>
|
||||
|
||||
#include <filesystem/filesystem.hpp>
|
||||
|
||||
#define MAGIC_FILE_CONSTANT 0x5CA1AB13
|
||||
#define ASTC_CACHE_DIRECTORY "cache/astc_to_bin"
|
||||
|
||||
constexpr uint32_t ASTC_CACHE_HEADER_SIZE = 64;
|
||||
constexpr uint32_t ASTC_CACHE_SEED = 1619;
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
BlockDim to_blockdim(const VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
||||
return {4, 4, 1};
|
||||
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
||||
return {5, 4, 1};
|
||||
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
||||
return {5, 5, 1};
|
||||
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
||||
return {6, 5, 1};
|
||||
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
||||
return {6, 6, 1};
|
||||
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
||||
return {8, 5, 1};
|
||||
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
||||
return {8, 6, 1};
|
||||
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
||||
return {8, 8, 1};
|
||||
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
||||
return {10, 5, 1};
|
||||
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
||||
return {10, 6, 1};
|
||||
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
||||
return {10, 8, 1};
|
||||
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
||||
return {10, 10, 1};
|
||||
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
||||
return {12, 10, 1};
|
||||
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
||||
return {12, 12, 1};
|
||||
default:
|
||||
throw std::runtime_error{"Invalid astc format"};
|
||||
}
|
||||
}
|
||||
|
||||
inline astcenc_profile to_profile(const VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
||||
return ASTCENC_PRF_LDR;
|
||||
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
||||
return ASTCENC_PRF_LDR_SRGB;
|
||||
default:
|
||||
throw std::runtime_error{"Invalid astc format"};
|
||||
}
|
||||
}
|
||||
|
||||
struct AstcHeader
|
||||
{
|
||||
uint8_t magic[4];
|
||||
uint8_t blockdim_x;
|
||||
uint8_t blockdim_y;
|
||||
uint8_t blockdim_z;
|
||||
uint8_t xsize[3]; // x-size = xsize[0] + xsize[1] + xsize[2]
|
||||
uint8_t ysize[3]; // x-size, y-size and z-size are given in texels;
|
||||
uint8_t zsize[3]; // block count is inferred
|
||||
};
|
||||
|
||||
void Astc::init()
|
||||
{
|
||||
}
|
||||
|
||||
void Astc::decode(BlockDim blockdim, VkExtent3D extent, const uint8_t *compressed_data, uint32_t compressed_size)
|
||||
{
|
||||
PROFILE_SCOPE("Decode ASTC Image");
|
||||
|
||||
// Actual decoding
|
||||
astcenc_swizzle swizzle = {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
|
||||
// Configure the compressor run
|
||||
astcenc_config astc_config;
|
||||
auto atscresult = astcenc_config_init(
|
||||
ASTCENC_PRF_LDR_SRGB,
|
||||
blockdim.x,
|
||||
blockdim.y,
|
||||
blockdim.z,
|
||||
ASTCENC_PRE_FAST,
|
||||
ASTCENC_FLG_DECOMPRESS_ONLY,
|
||||
&astc_config);
|
||||
|
||||
if (atscresult != ASTCENC_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error{"Error initializing astc"};
|
||||
}
|
||||
|
||||
if (extent.width == 0 || extent.height == 0 || extent.depth == 0)
|
||||
{
|
||||
throw std::runtime_error{"Error reading astc: invalid size"};
|
||||
}
|
||||
|
||||
// Allocate working state given config and thread_count
|
||||
astcenc_context *astc_context;
|
||||
astcenc_context_alloc(&astc_config, 1, &astc_context);
|
||||
|
||||
astcenc_image decoded{};
|
||||
decoded.dim_x = extent.width;
|
||||
decoded.dim_y = extent.height;
|
||||
decoded.dim_z = extent.depth;
|
||||
decoded.data_type = ASTCENC_TYPE_U8;
|
||||
|
||||
// allocate storage for the decoded image
|
||||
// The astcenc_decompress_image function will write directly to the image data vector
|
||||
auto uncompressed_size = decoded.dim_x * decoded.dim_y * decoded.dim_z * 4;
|
||||
auto &decoded_data = get_mut_data();
|
||||
decoded_data.resize(uncompressed_size);
|
||||
void *data_ptr = static_cast<void *>(decoded_data.data());
|
||||
decoded.data = &data_ptr;
|
||||
|
||||
atscresult = astcenc_decompress_image(astc_context, compressed_data, compressed_size, &decoded, &swizzle, 0);
|
||||
if (atscresult != ASTCENC_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error("Error decoding astc");
|
||||
}
|
||||
astcenc_context_free(astc_context);
|
||||
|
||||
set_format(VK_FORMAT_R8G8B8A8_SRGB);
|
||||
set_width(decoded.dim_x);
|
||||
set_height(decoded.dim_y);
|
||||
set_depth(decoded.dim_z);
|
||||
}
|
||||
|
||||
Astc::Astc(const Image &image) :
|
||||
Image{image.get_name()}
|
||||
{
|
||||
init();
|
||||
|
||||
auto fs = vkb::filesystem::get();
|
||||
|
||||
size_t key = ASTC_CACHE_SEED;
|
||||
glm::detail::hash_combine(key, image.get_data_hash());
|
||||
|
||||
constexpr bool use_cache = true;
|
||||
constexpr uint32_t bytes_per_pixel = 4;
|
||||
constexpr const char file_cache_header[ASTC_CACHE_HEADER_SIZE] = "ASTCConvertedDataV01";
|
||||
const auto profile = to_profile(image.get_format());
|
||||
|
||||
auto can_load_from_file = [this, profile, fs, file_cache_header, bytes_per_pixel, use_cache](const Path &path, std::vector<uint8_t> &dst_data, uint32_t width, uint32_t height, uint32_t depth) {
|
||||
if (!use_cache)
|
||||
{
|
||||
LOGD("Device does not support ASTC format and cache is disabled. ASTC image {} will be decoded.", get_name())
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!fs->exists(path))
|
||||
{
|
||||
LOGW("Device does not support ASTC format and cache file {} does not exist. ASTC image {} will be decoded.", path.string(), get_name())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGD("Loading ASTC image {} from cache file {}", get_name(), path.string())
|
||||
}
|
||||
size_t offset = 0;
|
||||
|
||||
auto copy_from_file = [fs, path](void *dst, size_t *offset, size_t content_size) {
|
||||
const auto bin_content = fs->read_chunk(path, *offset, content_size);
|
||||
std::memcpy(dst, &bin_content[0], content_size);
|
||||
*offset += content_size;
|
||||
};
|
||||
|
||||
char header[ASTC_CACHE_HEADER_SIZE];
|
||||
copy_from_file(&header, &offset, ASTC_CACHE_HEADER_SIZE);
|
||||
if (std::strcmp(header, file_cache_header) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t file_width, file_height, file_depth;
|
||||
copy_from_file(&file_width, &offset, sizeof(std::uint32_t));
|
||||
copy_from_file(&file_height, &offset, sizeof(std::uint32_t));
|
||||
copy_from_file(&file_depth, &offset, sizeof(std::uint32_t));
|
||||
|
||||
if (file_width != width || width == 0 ||
|
||||
file_height != height || height == 0 ||
|
||||
file_depth != depth || depth == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_width(width);
|
||||
set_height(height);
|
||||
set_depth(depth);
|
||||
set_format(profile == ASTCENC_PRF_LDR_SRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM);
|
||||
}
|
||||
|
||||
auto image_size = width * height * depth * bytes_per_pixel;
|
||||
dst_data.resize(image_size);
|
||||
copy_from_file(dst_data.data(), &offset, image_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
LOGE("ERROR loading file {} from cache. Error: <{}>", path.string(), e.what())
|
||||
// file is truncated
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto save_to_file = [fs, file_cache_header, bytes_per_pixel, use_cache](const Path &path, uint8_t *dst_data, uint32_t width, uint32_t height, uint32_t depth) {
|
||||
if (!use_cache)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
LOGI("Saving ASTC cache data to file: {}", path.string());
|
||||
|
||||
auto image_size = width * height * depth * bytes_per_pixel;
|
||||
|
||||
std::vector<uint8_t> astc_file_content;
|
||||
astc_file_content.reserve(sizeof(file_cache_header) + (3 * sizeof(std::uint32_t)) + image_size);
|
||||
|
||||
auto append_to_file = [](std::vector<uint8_t> &dst_file, const std::uint8_t *content, size_t content_size) {
|
||||
dst_file.insert(dst_file.end(), content, content + content_size);
|
||||
};
|
||||
|
||||
append_to_file(astc_file_content, (uint8_t *) &file_cache_header, sizeof(file_cache_header));
|
||||
append_to_file(astc_file_content, (uint8_t *) &width, sizeof(uint32_t));
|
||||
append_to_file(astc_file_content, (uint8_t *) &height, sizeof(uint32_t));
|
||||
append_to_file(astc_file_content, (uint8_t *) &depth, sizeof(uint32_t));
|
||||
append_to_file(astc_file_content, (uint8_t *) dst_data, image_size);
|
||||
|
||||
fs->write_file(path, astc_file_content);
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
LOGE("ERROR: saving to file: {}\nError<{}>", path.string(), e.what())
|
||||
}
|
||||
};
|
||||
|
||||
// Locate mip #0 in the KTX. This is the first one in the data array for KTX1s, but the last one in KTX2s!
|
||||
auto mip_it = std::ranges::find_if(image.get_mipmaps(),
|
||||
[](auto &mip) { return mip.level == 0; });
|
||||
assert(mip_it != image.get_mipmaps().end() && "Mip #0 not found");
|
||||
|
||||
const std::string path = fmt::format("{}/{}.bin", ASTC_CACHE_DIRECTORY, uint64_t(key));
|
||||
|
||||
if (!can_load_from_file(path, get_mut_data(), mip_it->extent.width, mip_it->extent.height, mip_it->extent.depth))
|
||||
{
|
||||
// When decoding ASTC on CPU (as it is the case in here), we don't decode all mips in the mip chain.
|
||||
// Instead, we just decode mip #0 and re-generate the other LODs later (via image->generate_mipmaps()).
|
||||
const auto blockdim = to_blockdim(image.get_format());
|
||||
const auto &extent = mip_it->extent;
|
||||
auto size = extent.width * extent.height * extent.depth * 4;
|
||||
const uint8_t *data_ptr = image.get_data().data() + mip_it->offset;
|
||||
|
||||
decode(blockdim, mip_it->extent, data_ptr, size);
|
||||
|
||||
save_to_file(path, get_mut_data().data(), mip_it->extent.width, mip_it->extent.height, mip_it->extent.depth);
|
||||
}
|
||||
|
||||
update_hash(image.get_data_hash());
|
||||
}
|
||||
|
||||
Astc::Astc(const std::string &name, const std::vector<uint8_t> &data) :
|
||||
Image{name}
|
||||
{
|
||||
init();
|
||||
|
||||
// Read header
|
||||
if (data.size() < sizeof(AstcHeader))
|
||||
{
|
||||
throw std::runtime_error{"Error reading astc: invalid memory"};
|
||||
}
|
||||
AstcHeader header{};
|
||||
std::memcpy(&header, data.data(), sizeof(AstcHeader));
|
||||
uint32_t magicval = header.magic[0] + 256 * static_cast<uint32_t>(header.magic[1]) + 65536 * static_cast<uint32_t>(header.magic[2]) + 16777216 * static_cast<uint32_t>(header.magic[3]);
|
||||
if (magicval != MAGIC_FILE_CONSTANT)
|
||||
{
|
||||
throw std::runtime_error{"Error reading astc: invalid magic"};
|
||||
}
|
||||
|
||||
BlockDim blockdim = {
|
||||
/* xdim = */ header.blockdim_x,
|
||||
/* ydim = */ header.blockdim_y,
|
||||
/* zdim = */ header.blockdim_z};
|
||||
|
||||
VkExtent3D extent = {
|
||||
/* width = */ static_cast<uint32_t>(header.xsize[0] + 256 * header.xsize[1] + 65536 * header.xsize[2]),
|
||||
/* height = */ static_cast<uint32_t>(header.ysize[0] + 256 * header.ysize[1] + 65536 * header.ysize[2]),
|
||||
/* depth = */ static_cast<uint32_t>(header.zsize[0] + 256 * header.zsize[1] + 65536 * header.zsize[2])};
|
||||
|
||||
decode(blockdim, extent, data.data() + sizeof(AstcHeader), to_u32(data.size() - sizeof(AstcHeader)));
|
||||
|
||||
update_hash(get_data_hash());
|
||||
}
|
||||
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Copyright (c) 2019-2024, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/vk_common.h"
|
||||
#include "scene_graph/components/image.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
struct BlockDim
|
||||
{
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t z;
|
||||
};
|
||||
|
||||
class Astc : public Image
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Decodes an ASTC image
|
||||
* @param image Image to decode
|
||||
*/
|
||||
Astc(const Image &image);
|
||||
|
||||
/**
|
||||
* @brief Decodes ASTC data with an ASTC header
|
||||
* @param name Name of the component
|
||||
* @param data ASTC data with header
|
||||
*/
|
||||
Astc(const std::string &name, const std::vector<uint8_t> &data);
|
||||
|
||||
virtual ~Astc() = default;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Decodes ASTC data
|
||||
* @param blockdim Dimensions of the block
|
||||
* @param extent Extent of the image
|
||||
* @param data Pointer to ASTC image data
|
||||
*/
|
||||
void decode(BlockDim blockdim, VkExtent3D extent, const uint8_t *data, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Initializes ASTC library
|
||||
*/
|
||||
void init();
|
||||
};
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,184 @@
|
||||
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2019-2025, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "scene_graph/components/image/ktx.h"
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#include <ktx.h>
|
||||
#include <ktxvulkan.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
struct CallbackData final
|
||||
{
|
||||
ktxTexture *texture;
|
||||
std::vector<Mipmap> *mipmaps;
|
||||
};
|
||||
|
||||
/// Row padding is different between KTX (pad to 4) and Vulkan (none).
|
||||
/// Also region->bufferOffset, i.e. the start of each image, has
|
||||
/// to be a multiple of 4 and also a multiple of the element size.
|
||||
static ktx_error_code_e KTX_APIENTRY optimal_tiling_callback(int mip_level,
|
||||
int face,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
ktx_uint64_t face_lod_size,
|
||||
void *pixels,
|
||||
void *user_data)
|
||||
{
|
||||
auto *callback_data = reinterpret_cast<CallbackData *>(user_data);
|
||||
assert(static_cast<size_t>(mip_level) < callback_data->mipmaps->size() && "Not enough space in the mipmap vector");
|
||||
|
||||
ktx_size_t mipmap_offset = 0;
|
||||
auto result = ktxTexture_GetImageOffset(callback_data->texture, mip_level, 0, face, &mipmap_offset);
|
||||
if (result != KTX_SUCCESS)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto &mipmap = callback_data->mipmaps->at(mip_level);
|
||||
mipmap.level = mip_level;
|
||||
mipmap.offset = to_u32(mipmap_offset);
|
||||
mipmap.extent.width = width;
|
||||
mipmap.extent.height = height;
|
||||
mipmap.extent.depth = depth;
|
||||
|
||||
return KTX_SUCCESS;
|
||||
}
|
||||
|
||||
Ktx::Ktx(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type) :
|
||||
Image{name}
|
||||
{
|
||||
auto data_buffer = reinterpret_cast<const ktx_uint8_t *>(data.data());
|
||||
auto data_size = static_cast<ktx_size_t>(data.size());
|
||||
|
||||
ktxTexture *texture;
|
||||
auto load_ktx_result = ktxTexture_CreateFromMemory(data_buffer,
|
||||
data_size,
|
||||
KTX_TEXTURE_CREATE_NO_FLAGS,
|
||||
&texture);
|
||||
if (load_ktx_result != KTX_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error{"Error loading KTX texture: " + name};
|
||||
}
|
||||
|
||||
if (texture->pData)
|
||||
{
|
||||
// Already loaded
|
||||
set_data(texture->pData, texture->dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load
|
||||
auto &mut_data = get_mut_data();
|
||||
auto size = texture->dataSize;
|
||||
mut_data.resize(size);
|
||||
auto load_data_result = ktxTexture_LoadImageData(texture, mut_data.data(), size);
|
||||
if (load_data_result != KTX_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error{"Error loading KTX image data: " + name};
|
||||
}
|
||||
}
|
||||
|
||||
set_width(texture->baseWidth);
|
||||
set_height(texture->baseHeight);
|
||||
set_depth(texture->baseDepth);
|
||||
set_layers(texture->numLayers);
|
||||
update_hash();
|
||||
|
||||
bool cubemap = false;
|
||||
|
||||
// Use the faces if there are 6 (for cubemap)
|
||||
if (texture->numLayers == 1 && texture->numFaces == 6)
|
||||
{
|
||||
cubemap = true;
|
||||
set_layers(texture->numFaces);
|
||||
}
|
||||
|
||||
auto updated_format = ktxTexture_GetVkFormat(texture);
|
||||
|
||||
set_format(updated_format);
|
||||
|
||||
if (texture->classId == ktxTexture1_c && content_type == Color)
|
||||
{
|
||||
// KTX-1 files don't contain color space information. Color data is normally
|
||||
// in sRGB, but the format we get back won't report that, so this will adjust it
|
||||
// if necessary.
|
||||
coerce_format_to_srgb();
|
||||
}
|
||||
|
||||
auto &mipmap_levels = get_mut_mipmaps();
|
||||
mipmap_levels.resize(texture->numLevels);
|
||||
|
||||
CallbackData callback_data{};
|
||||
callback_data.texture = texture;
|
||||
callback_data.mipmaps = &mipmap_levels;
|
||||
|
||||
auto result = ktxTexture_IterateLevels(texture, optimal_tiling_callback, &callback_data);
|
||||
if (result != KTX_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error("Error loading KTX texture");
|
||||
}
|
||||
|
||||
// If the texture contains more than one layer, then populate the offsets otherwise take the mipmap level offsets
|
||||
if (texture->numLayers > 1 || cubemap)
|
||||
{
|
||||
uint32_t layer_count = cubemap ? texture->numFaces : texture->numLayers;
|
||||
|
||||
std::vector<std::vector<VkDeviceSize>> offsets;
|
||||
for (uint32_t layer = 0; layer < layer_count; layer++)
|
||||
{
|
||||
std::vector<VkDeviceSize> layer_offsets{};
|
||||
for (uint32_t level = 0; level < texture->numLevels; level++)
|
||||
{
|
||||
ktx_size_t offset;
|
||||
KTX_error_code result;
|
||||
if (cubemap)
|
||||
{
|
||||
result = ktxTexture_GetImageOffset(texture, level, 0, layer, &offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ktxTexture_GetImageOffset(texture, level, layer, 0, &offset);
|
||||
}
|
||||
layer_offsets.push_back(static_cast<VkDeviceSize>(offset));
|
||||
}
|
||||
offsets.push_back(layer_offsets);
|
||||
}
|
||||
set_offsets(offsets);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::vector<VkDeviceSize>> offsets{};
|
||||
offsets.resize(1);
|
||||
for (size_t level = 0; level < mipmap_levels.size(); level++)
|
||||
{
|
||||
offsets[0].push_back(static_cast<VkDeviceSize>(mipmap_levels[level].offset));
|
||||
}
|
||||
set_offsets(offsets);
|
||||
}
|
||||
|
||||
ktxTexture_Destroy(texture);
|
||||
}
|
||||
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 2019-2022, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene_graph/components/image.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
class Ktx : public Image
|
||||
{
|
||||
public:
|
||||
Ktx(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type);
|
||||
|
||||
virtual ~Ktx() = default;
|
||||
};
|
||||
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright (c) 2019-2022, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "scene_graph/components/image/stb.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
Stb::Stb(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type) :
|
||||
Image{name}
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int comp;
|
||||
int req_comp = 4;
|
||||
|
||||
auto data_buffer = reinterpret_cast<const stbi_uc *>(data.data());
|
||||
auto data_size = static_cast<int>(data.size());
|
||||
|
||||
auto raw_data = stbi_load_from_memory(data_buffer, data_size, &width, &height, &comp, req_comp);
|
||||
|
||||
if (!raw_data)
|
||||
{
|
||||
throw std::runtime_error{"Failed to load " + name + ": " + stbi_failure_reason()};
|
||||
}
|
||||
|
||||
set_data(raw_data, width * height * req_comp);
|
||||
stbi_image_free(raw_data);
|
||||
|
||||
set_format(content_type == Color ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM);
|
||||
set_width(to_u32(width));
|
||||
set_height(to_u32(height));
|
||||
set_depth(1u);
|
||||
}
|
||||
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 2019-2022, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene_graph/components/image.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace sg
|
||||
{
|
||||
class Stb : public Image
|
||||
{
|
||||
public:
|
||||
Stb(const std::string &name, const std::vector<uint8_t> &data, ContentType content_type);
|
||||
|
||||
virtual ~Stb() = default;
|
||||
};
|
||||
|
||||
} // namespace sg
|
||||
} // namespace vkb
|
||||
Reference in New Issue
Block a user