feat: assets module
This commit is contained in:
parent
0fe399a33e
commit
0c4b3dd0f9
6 changed files with 353 additions and 0 deletions
14
modules/assets/CMakeLists.txt
Normal file
14
modules/assets/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
add_library_module(assets
|
||||
shader.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
assets
|
||||
PUBLIC
|
||||
logger
|
||||
lt_debug
|
||||
)
|
||||
|
||||
add_test_module(assets
|
||||
shader.test.cpp
|
||||
)
|
73
modules/assets/private/shader.cpp
Normal file
73
modules/assets/private/shader.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include <assets/shader.hpp>
|
||||
|
||||
namespace lt::assets {
|
||||
|
||||
ShaderAsset::ShaderAsset(const std::filesystem::path &path)
|
||||
: m_stream(path, std::ios::binary | std::ios::beg)
|
||||
{
|
||||
constexpr auto total_metadata_size = //
|
||||
sizeof(AssetMetadata) //
|
||||
+ sizeof(Metadata) //
|
||||
+ sizeof(BlobMetadata);
|
||||
|
||||
ensure(m_stream.is_open(), "Failed to open shader asset at: {}", path.string());
|
||||
|
||||
m_stream.seekg(0, std::ifstream::end);
|
||||
const auto file_size = static_cast<size_t>(m_stream.tellg());
|
||||
|
||||
ensure(
|
||||
file_size > total_metadata_size,
|
||||
"Failed to open shader asset at: {}, file smaller than metadata: {} < {}",
|
||||
path.string(),
|
||||
total_metadata_size,
|
||||
file_size
|
||||
);
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
m_stream.seekg(0, std::ifstream::beg);
|
||||
m_stream.read((char *)&m_asset_metadata, sizeof(m_asset_metadata));
|
||||
m_stream.read((char *)&m_metadata, sizeof(m_metadata));
|
||||
m_stream.read((char *)&m_code_blob_metadata, sizeof(m_code_blob_metadata));
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
|
||||
ensure(
|
||||
m_asset_metadata.type == asset_type_identifier,
|
||||
"Failed to open shader asset at: {}, incorrect asset type: {} != {}",
|
||||
path.string(),
|
||||
m_asset_metadata.type,
|
||||
asset_type_identifier
|
||||
);
|
||||
|
||||
ensure(
|
||||
m_asset_metadata.version == current_version,
|
||||
"Failed to open shader asset at: {}, version mismatch: {} != {}",
|
||||
path.string(),
|
||||
m_asset_metadata.version,
|
||||
current_version
|
||||
);
|
||||
|
||||
ensure(
|
||||
std::to_underlying(m_metadata.type) <= std::to_underlying(Type::compute),
|
||||
"Failed to open shader asset at: {}, invalid shader type: {}",
|
||||
path.string(),
|
||||
std::to_underlying(m_metadata.type)
|
||||
);
|
||||
|
||||
ensure(
|
||||
m_code_blob_metadata.tag == std::to_underlying(BlobTag::code),
|
||||
"Failed to open shader asset at: {}, invalid blob tag: {}",
|
||||
path.string(),
|
||||
m_code_blob_metadata.tag
|
||||
);
|
||||
|
||||
ensure(
|
||||
m_code_blob_metadata.offset + m_code_blob_metadata.compressed_size <= file_size,
|
||||
"Failed to open shader asset at: {}, file smaller than blob: {} > {} + {}",
|
||||
path.string(),
|
||||
file_size,
|
||||
m_code_blob_metadata.offset,
|
||||
m_code_blob_metadata.compressed_size
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace lt::assets
|
89
modules/assets/private/shader.test.cpp
Normal file
89
modules/assets/private/shader.test.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <assets/shader.hpp>
|
||||
#include <ranges>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using ::lt::assets::AssetMetadata;
|
||||
using ::lt::assets::BlobMetadata;
|
||||
using ::lt::assets::ShaderAsset;
|
||||
using ::lt::test::Case;
|
||||
using ::lt::test::expect_eq;
|
||||
using ::lt::test::expect_throw;
|
||||
using ::lt::test::expect_true;
|
||||
using ::lt::test::Suite;
|
||||
|
||||
const auto test_data_path = std::filesystem::path { "./data/test_assets" };
|
||||
const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" };
|
||||
|
||||
Suite raii = "shader_raii"_suite = [] {
|
||||
std::filesystem::current_path(test_data_path);
|
||||
std::filesystem::create_directories(tmp_path);
|
||||
|
||||
Case { "happy path won't throw" } = [] {
|
||||
|
||||
};
|
||||
|
||||
Case { "many won't freeze/throw" } = [] {
|
||||
};
|
||||
|
||||
Case { "unhappy path throws" } = [] {
|
||||
expect_throw([] { ShaderAsset { "random_path" }; });
|
||||
};
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
|
||||
Suite packing = "shader_pack"_suite = [] {
|
||||
Case { "" } = [] {
|
||||
const auto out_path = tmp_path / "shader_packing";
|
||||
auto dummy_blob = lt::assets::Blob {};
|
||||
for (auto idx : std::views::iota(0, 255))
|
||||
{
|
||||
dummy_blob.emplace_back(static_cast<std::byte>(idx));
|
||||
}
|
||||
|
||||
const auto expected_size = //
|
||||
sizeof(AssetMetadata) //
|
||||
+ sizeof(ShaderAsset::Metadata) //
|
||||
+ sizeof(BlobMetadata) //
|
||||
+ dummy_blob.size();
|
||||
|
||||
ShaderAsset::pack(
|
||||
out_path,
|
||||
lt::assets::AssetMetadata {
|
||||
.version = lt::assets::current_version,
|
||||
.type = ShaderAsset::asset_type_identifier,
|
||||
},
|
||||
ShaderAsset::Metadata {
|
||||
.type = ShaderAsset::Type::vertex,
|
||||
},
|
||||
std::move(dummy_blob)
|
||||
);
|
||||
|
||||
auto stream = std::ifstream {
|
||||
out_path,
|
||||
std::ios::binary | std::ios::beg,
|
||||
};
|
||||
expect_true(stream.is_open());
|
||||
|
||||
stream.seekg(0, std::ios::end);
|
||||
const auto file_size = static_cast<size_t>(stream.tellg());
|
||||
expect_eq(file_size, expected_size);
|
||||
stream.close();
|
||||
|
||||
auto shader_asset = ShaderAsset { out_path };
|
||||
|
||||
const auto &asset_metadata = shader_asset.get_asset_metadata();
|
||||
expect_eq(asset_metadata.type, ShaderAsset::asset_type_identifier);
|
||||
expect_eq(asset_metadata.version, lt::assets::current_version);
|
||||
|
||||
const auto &metadata = shader_asset.get_metadata();
|
||||
expect_eq(metadata.type, ShaderAsset::Type::vertex);
|
||||
|
||||
auto blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
|
||||
expect_eq(blob.size(), 255);
|
||||
|
||||
for (auto idx : std::views::iota(0, 255))
|
||||
{
|
||||
expect_eq(blob[idx], static_cast<std::byte>(idx));
|
||||
}
|
||||
};
|
||||
};
|
3
modules/assets/public/compressors/lz4.hpp
Normal file
3
modules/assets/public/compressors/lz4.hpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
// TO BE DOOO
|
42
modules/assets/public/metadata.hpp
Normal file
42
modules/assets/public/metadata.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::assets {
|
||||
|
||||
using Type_T = std::array<const char, 16>;
|
||||
|
||||
using Tag_T = uint8_t;
|
||||
|
||||
using Version = uint8_t;
|
||||
|
||||
using Blob = std::vector<std::byte>;
|
||||
|
||||
constexpr auto current_version = Version { 1u };
|
||||
|
||||
enum class CompressionType : uint8_t
|
||||
{
|
||||
none,
|
||||
lz4,
|
||||
lz4_hc,
|
||||
};
|
||||
|
||||
struct AssetMetadata
|
||||
{
|
||||
Version version;
|
||||
|
||||
Type_T type;
|
||||
};
|
||||
|
||||
struct BlobMetadata
|
||||
{
|
||||
Tag_T tag;
|
||||
|
||||
size_t offset;
|
||||
|
||||
CompressionType compression_type;
|
||||
|
||||
size_t compressed_size;
|
||||
|
||||
size_t uncompressed_size;
|
||||
};
|
||||
|
||||
} // namespace lt::assets
|
132
modules/assets/public/shader.hpp
Normal file
132
modules/assets/public/shader.hpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
#include <assets/metadata.hpp>
|
||||
|
||||
namespace lt::assets {
|
||||
|
||||
class ShaderAsset
|
||||
{
|
||||
public:
|
||||
static constexpr auto asset_type_identifier = Type_T { "SHADER_________" };
|
||||
|
||||
enum class BlobTag : Tag_T
|
||||
{
|
||||
code,
|
||||
};
|
||||
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
vertex,
|
||||
fragment,
|
||||
geometry,
|
||||
compute,
|
||||
};
|
||||
|
||||
struct Metadata
|
||||
{
|
||||
Type type;
|
||||
};
|
||||
|
||||
static void pack(
|
||||
const std::filesystem::path &destination,
|
||||
AssetMetadata asset_metadata,
|
||||
Metadata metadata,
|
||||
Blob code_blob
|
||||
)
|
||||
{
|
||||
auto stream = std::ofstream {
|
||||
destination,
|
||||
std::ios::binary | std::ios::trunc,
|
||||
};
|
||||
ensure(stream.is_open(), "Failed to pack shader asset to {}", destination.string());
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
stream.write((char *)&asset_metadata, sizeof(asset_metadata));
|
||||
stream.write((char *)&metadata, sizeof(metadata));
|
||||
|
||||
auto code_blob_metadata = BlobMetadata {
|
||||
.tag = std::to_underlying(BlobTag::code),
|
||||
.offset = static_cast<size_t>(stream.tellp()) + sizeof(BlobMetadata),
|
||||
.compression_type = CompressionType::none,
|
||||
.compressed_size = code_blob.size(),
|
||||
.uncompressed_size = code_blob.size(),
|
||||
};
|
||||
stream.write((char *)&code_blob_metadata, sizeof(BlobMetadata));
|
||||
|
||||
stream.write((char *)code_blob.data(), static_cast<long long>(code_blob.size()));
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
}
|
||||
|
||||
ShaderAsset(const std::filesystem::path &path);
|
||||
|
||||
[[nodiscard]] auto get_asset_metadata() const -> const AssetMetadata &
|
||||
{
|
||||
return m_asset_metadata;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_metadata() const -> const Metadata &
|
||||
{
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_blob_metadata(BlobTag tag) const -> const BlobMetadata &
|
||||
{
|
||||
ensure(
|
||||
tag == BlobTag::code,
|
||||
"Invalid blob tag for shader asset: {}",
|
||||
std::to_underlying(tag)
|
||||
);
|
||||
|
||||
return m_code_blob_metadata;
|
||||
}
|
||||
|
||||
void unpack_to(BlobTag tag, std::span<std::byte> destination) const
|
||||
{
|
||||
ensure(
|
||||
tag == BlobTag::code,
|
||||
"Invalid blob tag for shader asset: {}",
|
||||
std::to_underlying(tag)
|
||||
);
|
||||
|
||||
ensure(
|
||||
destination.size() >= m_code_blob_metadata.uncompressed_size,
|
||||
"Failed to unpack shader blob {} to destination ({}) of size {} since it's smaller "
|
||||
"than the blobl's uncompressed size: {}",
|
||||
std::to_underlying(tag),
|
||||
(size_t)(destination.data()), // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
destination.size(),
|
||||
m_code_blob_metadata.uncompressed_size
|
||||
);
|
||||
|
||||
m_stream.seekg(static_cast<long long>(m_code_blob_metadata.offset));
|
||||
m_stream.read(
|
||||
(char *)destination.data(), // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
|
||||
static_cast<long long>(m_code_blob_metadata.uncompressed_size)
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto unpack(BlobTag tag) const -> Blob
|
||||
{
|
||||
ensure(
|
||||
tag == BlobTag::code,
|
||||
"Invalid blob tag for shader asset: {}",
|
||||
std::to_underlying(tag)
|
||||
);
|
||||
|
||||
auto blob = Blob(m_code_blob_metadata.uncompressed_size);
|
||||
unpack_to(tag, blob);
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
private:
|
||||
AssetMetadata m_asset_metadata {};
|
||||
|
||||
Metadata m_metadata {};
|
||||
|
||||
BlobMetadata m_code_blob_metadata {};
|
||||
|
||||
mutable std::ifstream m_stream;
|
||||
};
|
||||
|
||||
} // namespace lt::assets
|
Loading…
Add table
Reference in a new issue