asset compression
Some checks failed
CI / build-test-release (push) Failing after 15m11s

This commit is contained in:
Jorijn van der Graaf 2026-05-12 01:16:40 +02:00
commit 03717b5f33
9 changed files with 230 additions and 14 deletions

View file

@ -23,6 +23,7 @@ import :Clang;
import :Platform;
import :Test;
import :Progress;
import :Asset;
namespace fs = std::filesystem;
using namespace Crafter;
@ -291,6 +292,38 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
});
}
// Asset compilation: each .png/.obj listed in cfg.assets is run through
// Crafter.Asset's library (linked into crafter-build directly — see
// :Asset_impl) to produce the SaveCompressed .ctex/.cmesh in outputDir.
// Skipped if the output is newer than the source. Parallel with shaders
// and module compiles; cancellation participates in the same flag.
auto assetOutput = [](const fs::path& asset) -> std::optional<fs::path> {
std::string ext = asset.extension().string();
if (ext == ".png") return fs::path(asset.filename()).replace_extension(".ctex");
if (ext == ".obj") return fs::path(asset.filename()).replace_extension(".cmesh");
return std::nullopt;
};
for (const fs::path& asset : config.assets) {
std::optional<fs::path> outName = assetOutput(asset);
if (!outName) {
buildCancelled.store(true);
buildError = std::format("{}: unsupported asset extension (expected .png or .obj)", asset.string());
break;
}
fs::path out = outputDir / *outName;
if (fs::exists(out) && fs::exists(asset) && fs::last_write_time(asset) <= fs::last_write_time(out)) continue;
threads.emplace_back([&asset, out = std::move(out), &buildError, &buildCancelled]() {
Progress::Task task(std::format("Compressing asset {}", asset.filename().string()));
if (buildCancelled.load(std::memory_order_relaxed)) return;
std::string result = CompressAsset(asset, out);
if (result.empty()) return;
bool expected = false;
if (buildCancelled.compare_exchange_strong(expected, true)) {
buildError = std::move(result);
}
});
}
threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() {
Progress::Task task(std::format("Copying files for {}", config.name));
if (buildCancelled.load(std::memory_order_relaxed)) return;
@ -432,16 +465,20 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
depThreads.reserve(config.dependencies.size());
std::atomic<bool> repack(false);
// -I propagation that's valid for both C and C++ compiles. Module-only
// bits (-fprebuilt-module-path) stay on `command` only.
std::string includeFlags;
{
std::unordered_set<Configuration*> seen;
std::function<void(Configuration*)> addFlags = [&](Configuration* dep) {
if (!seen.insert(dep).second) return;
for (const auto& entry : fs::recursive_directory_iterator(dep->path)) {
if (entry.is_directory() && entry.path().filename() == "include") {
command += " -I" + entry.path().string();
includeFlags += " -I" + entry.path().string();
}
}
command += std::format(" -I{} -fprebuilt-module-path={}", dep->path.string(), dep->PcmDir().string());
includeFlags += std::format(" -I{}", dep->path.string());
command += std::format(" -fprebuilt-module-path={}", dep->PcmDir().string());
for (Configuration* sub : dep->dependencies) {
addFlags(sub);
}
@ -450,6 +487,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
addFlags(dep);
}
}
command += includeFlags;
for(Configuration* dep : config.dependencies) {
depThreads.emplace_back([&, dep](){
@ -507,17 +545,25 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
});
}
// Defines belong on both C and C++ compiles so vendored C dependencies
// can see configuration-level macros consistently with module sources.
std::string defineFlags;
for(const Define& define : config.defines) {
if(define.value.empty()) {
command += std::format(" -D {}", define.name);
defineFlags += std::format(" -D {}", define.name);
} else {
command += std::format(" -D {}={}", define.name, define.value);
defineFlags += std::format(" -D {}={}", define.name, define.value);
}
}
command += defineFlags;
// Track caller-provided compileFlags separately so the .c compile can
// pick them up too (vendored C deps usually need -I from this set).
std::string userFlags;
for(const std::string& flag : config.compileFlags) {
command += " " + flag;
userFlags += " " + flag;
}
command += userFlags;
std::string cmakeBuildType;
@ -534,11 +580,11 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
const std::string objPath = (buildDir / cFile.filename()).string() + "_source.o";
const std::string srcPath = cFile.string() + ".c";
if (!fs::exists(objPath) || (fs::exists(srcPath) && fs::last_write_time(srcPath) > fs::last_write_time(objPath))) {
threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled, &config]() {
threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled, &config, &includeFlags, &defineFlags, &userFlags]() {
Progress::Task task(std::format("Compiling {}.c", cFile.filename().string()));
if (buildCancelled.load(std::memory_order_relaxed)) return;
std::string result = RunCommand(std::format("clang {}.c --target={} -march={} -mtune={} -O3 -c -o {}_source.o", cFile.string(), config.target, config.march, config.mtune, (buildDir / cFile.filename()).string()));
std::string result = RunCommand(std::format("clang {}.c --target={} -march={} -mtune={} -O3 -c{}{}{} -o {}_source.o", cFile.string(), config.target, config.march, config.mtune, includeFlags, defineFlags, userFlags, (buildDir / cFile.filename()).string()));
if (result.empty()) return;
bool expected = false;
@ -632,6 +678,16 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
if (!fs::exists(src)) continue;
copyTree(src, outputDir / additionalFile.filename());
}
for (const fs::path& asset : dep->assets) {
std::string ext = asset.extension().string();
fs::path srcName = asset.filename();
if (ext == ".png") srcName.replace_extension(".ctex");
else if (ext == ".obj") srcName.replace_extension(".cmesh");
else continue;
fs::path src = depBinDir / srcName;
if (!fs::exists(src)) continue;
copyTree(src, outputDir / srcName);
}
for (Configuration* sub : dep->dependencies) forwardDepArtifacts(sub);
};
for (Configuration* dep : config.dependencies) forwardDepArtifacts(dep);