From c712874ece1ca7073904f5fb84cc866d28084de0 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 9 Feb 2026 13:52:37 +0100 Subject: feat(gpu): Add GPU procedural texture generation system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 implementation complete: - GPU compute shader for noise generation (gen_noise.wgsl) - TextureManager extensions: create_gpu_noise_texture(), dispatch_noise_compute() - Asset packer PROC_GPU() syntax support with validation - ShaderComposer integration for #include resolution - Zero CPU memory overhead (GPU-only textures) - Init-time and on-demand generation modes Technical details: - 8×8 workgroup size for 256×256 textures - UniformBuffer for params (width, height, seed, frequency) - Storage texture binding (rgba8unorm, write-only) - Lazy pipeline compilation on first use - ~300 bytes code (Phase 1) Testing: - New test: test_gpu_procedural.cc (passes) - All 34 tests passing (100%) Future phases: - Phase 2: Add gen_perlin, gen_grid compute shaders - Phase 3: Variable dimensions, async generation Co-Authored-By: Claude Sonnet 4.5 --- tools/asset_packer.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/asset_packer.cc b/tools/asset_packer.cc index 0d26cf6..72592ae 100644 --- a/tools/asset_packer.cc +++ b/tools/asset_packer.cc @@ -52,6 +52,7 @@ struct AssetBuildInfo { std::string name; std::string filename; // Original filename for static assets bool is_procedural; + bool is_gpu_procedural; std::string proc_func_name; // Function name string std::vector proc_params; // Parameters for procedural function @@ -182,9 +183,62 @@ int main(int argc, char* argv[]) { info.params_array_name = "ASSET_PROC_PARAMS_" + info.name; info.func_name_str_name = "ASSET_PROC_FUNC_STR_" + info.name; info.is_procedural = false; + info.is_gpu_procedural = false; - if (compression_type_str.rfind("PROC(", 0) == 0) { + if (compression_type_str.rfind("PROC_GPU(", 0) == 0) { info.is_procedural = true; + info.is_gpu_procedural = true; + size_t open_paren = compression_type_str.find('('); + size_t close_paren = compression_type_str.rfind(')'); + if (open_paren == std::string::npos || + close_paren == std::string::npos) { + fprintf(stderr, + "Error: Invalid PROC_GPU() syntax for asset: %s, string: [%s]\n", + info.name.c_str(), compression_type_str.c_str()); + return 1; + } + std::string func_and_params_str = compression_type_str.substr( + open_paren + 1, close_paren - open_paren - 1); + + size_t params_start = func_and_params_str.find(','); + if (params_start != std::string::npos) { + std::string params_str = func_and_params_str.substr(params_start + 1); + info.proc_func_name = func_and_params_str.substr(0, params_start); + + size_t current_pos = 0; + while (current_pos < params_str.length()) { + size_t comma_pos = params_str.find(',', current_pos); + std::string param_val_str = + (comma_pos == std::string::npos) + ? params_str.substr(current_pos) + : params_str.substr(current_pos, comma_pos - current_pos); + param_val_str.erase(0, param_val_str.find_first_not_of(" \t\r\n")); + param_val_str.erase(param_val_str.find_last_not_of(" \t\r\n") + 1); + try { + info.proc_params.push_back(std::stof(param_val_str)); + } catch (...) { + fprintf(stderr, "Error: Invalid proc param for %s: %s\n", + info.name.c_str(), param_val_str.c_str()); + return 1; + } + if (comma_pos == std::string::npos) + break; + current_pos = comma_pos + 1; + } + } else { + info.proc_func_name = func_and_params_str; + } + + // Validate GPU procedural function name + if (info.proc_func_name != "gen_noise") { + fprintf(stderr, + "Error: PROC_GPU only supports gen_noise, got: %s for asset: %s\n", + info.proc_func_name.c_str(), info.name.c_str()); + return 1; + } + } else if (compression_type_str.rfind("PROC(", 0) == 0) { + info.is_procedural = true; + info.is_gpu_procedural = false; size_t open_paren = compression_type_str.find('('); size_t close_paren = compression_type_str.rfind(')'); if (open_paren == std::string::npos || @@ -500,12 +554,13 @@ int main(int argc, char* argv[]) { for (const auto& info : asset_build_infos) { fprintf(assets_data_cc_file, " { "); if (info.is_procedural) { - fprintf(assets_data_cc_file, "nullptr, 0, true, %s, %s, %zu", + fprintf(assets_data_cc_file, "nullptr, 0, true, %s, %s, %s, %zu", + info.is_gpu_procedural ? "true" : "false", info.func_name_str_name.c_str(), info.params_array_name.c_str(), info.proc_params.size()); } else { fprintf(assets_data_cc_file, - "%s, ASSET_SIZE_%s, false, nullptr, nullptr, 0", + "%s, ASSET_SIZE_%s, false, false, nullptr, nullptr, 0", info.data_array_name.c_str(), info.name.c_str()); } fprintf(assets_data_cc_file, " },\n"); -- cgit v1.2.3