diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gpu/effects/shaders.cc | 4 | ||||
| -rw-r--r-- | src/gpu/effects/shaders.h | 1 | ||||
| -rw-r--r-- | src/gpu/texture_manager.cc | 196 | ||||
| -rw-r--r-- | src/gpu/texture_manager.h | 20 | ||||
| -rw-r--r-- | src/tests/test_gpu_procedural.cc | 56 | ||||
| -rw-r--r-- | src/util/asset_manager.h | 1 |
6 files changed, 278 insertions, 0 deletions
diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc index 2e1cfe5..5c6dd37 100644 --- a/src/gpu/effects/shaders.cc +++ b/src/gpu/effects/shaders.cc @@ -99,6 +99,10 @@ const char* chroma_aberration_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_CHROMA_ABERRATION); +const char* gen_noise_compute_wgsl = + + SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_NOISE); + const char* vignette_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_VIGNETTE); diff --git a/src/gpu/effects/shaders.h b/src/gpu/effects/shaders.h index 50b4f32..b629e30 100644 --- a/src/gpu/effects/shaders.h +++ b/src/gpu/effects/shaders.h @@ -18,3 +18,4 @@ extern const char* solarize_shader_wgsl; extern const char* distort_shader_wgsl; extern const char* chroma_aberration_shader_wgsl; extern const char* vignette_shader_wgsl; +extern const char* gen_noise_compute_wgsl; diff --git a/src/gpu/texture_manager.cc b/src/gpu/texture_manager.cc index 0c30c94..aff106a 100644 --- a/src/gpu/texture_manager.cc +++ b/src/gpu/texture_manager.cc @@ -2,7 +2,10 @@ // It implements the TextureManager. #include "gpu/texture_manager.h" +#include "gpu/effects/shader_composer.h" +#include "platform/platform.h" #include <cstdio> +#include <cstring> #include <vector> #if defined(DEMO_CROSS_COMPILE_WIN32) @@ -18,6 +21,7 @@ void TextureManager::init(WGPUDevice device, WGPUQueue queue) { device_ = device; queue_ = queue; + noise_compute_pipeline_ = nullptr; } void TextureManager::shutdown() { @@ -26,6 +30,10 @@ void TextureManager::shutdown() { wgpuTextureRelease(pair.second.texture); } textures_.clear(); + if (noise_compute_pipeline_) { + wgpuComputePipelineRelease(noise_compute_pipeline_); + noise_compute_pipeline_ = nullptr; + } } void TextureManager::create_procedural_texture( @@ -112,3 +120,191 @@ WGPUTextureView TextureManager::get_texture_view(const std::string& name) { } return nullptr; } + +void TextureManager::dispatch_noise_compute(WGPUTexture target, + const GpuProceduralParams& params) { + // Lazy-init compute pipeline + if (!noise_compute_pipeline_) { + extern const char* gen_noise_compute_wgsl; + + // Resolve #include directives using ShaderComposer + ShaderComposer& composer = ShaderComposer::Get(); + std::string resolved_shader = composer.Compose({}, gen_noise_compute_wgsl); + + WGPUShaderSourceWGSL wgsl_src = {}; + wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; + wgsl_src.code = str_view(resolved_shader.c_str()); + WGPUShaderModuleDescriptor shader_desc = {}; + shader_desc.nextInChain = &wgsl_src.chain; + WGPUShaderModule shader_module = + wgpuDeviceCreateShaderModule(device_, &shader_desc); + + // Bind group layout (storage texture + uniform) + WGPUBindGroupLayoutEntry bgl_entries[2] = {}; + bgl_entries[0].binding = 0; + bgl_entries[0].visibility = WGPUShaderStage_Compute; + bgl_entries[0].storageTexture.access = WGPUStorageTextureAccess_WriteOnly; + bgl_entries[0].storageTexture.format = WGPUTextureFormat_RGBA8Unorm; + bgl_entries[0].storageTexture.viewDimension = WGPUTextureViewDimension_2D; + + bgl_entries[1].binding = 1; + bgl_entries[1].visibility = WGPUShaderStage_Compute; + bgl_entries[1].buffer.type = WGPUBufferBindingType_Uniform; + bgl_entries[1].buffer.minBindingSize = 16; // sizeof(NoiseParams) + + WGPUBindGroupLayoutDescriptor bgl_desc = {}; + bgl_desc.entryCount = 2; + bgl_desc.entries = bgl_entries; + WGPUBindGroupLayout bind_group_layout = + wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); + + WGPUPipelineLayoutDescriptor pl_desc = {}; + pl_desc.bindGroupLayoutCount = 1; + pl_desc.bindGroupLayouts = &bind_group_layout; + WGPUPipelineLayout pipeline_layout = + wgpuDeviceCreatePipelineLayout(device_, &pl_desc); + + WGPUComputePipelineDescriptor pipeline_desc = {}; + pipeline_desc.layout = pipeline_layout; + pipeline_desc.compute.module = shader_module; + pipeline_desc.compute.entryPoint = str_view("main"); + + noise_compute_pipeline_ = + wgpuDeviceCreateComputePipeline(device_, &pipeline_desc); + + wgpuPipelineLayoutRelease(pipeline_layout); + wgpuBindGroupLayoutRelease(bind_group_layout); + wgpuShaderModuleRelease(shader_module); + } + + // Create uniform buffer (width, height, seed, frequency) + struct NoiseParams { + uint32_t width; + uint32_t height; + float seed; + float frequency; + }; + NoiseParams uniform_data = {(uint32_t)params.width, (uint32_t)params.height, + params.params[0], params.params[1]}; + WGPUBufferDescriptor buf_desc = {}; + buf_desc.size = sizeof(NoiseParams); + buf_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst; + buf_desc.mappedAtCreation = WGPUOptionalBool_True; + WGPUBuffer uniform_buf = wgpuDeviceCreateBuffer(device_, &buf_desc); + void* mapped = wgpuBufferGetMappedRange(uniform_buf, 0, sizeof(NoiseParams)); + memcpy(mapped, &uniform_data, sizeof(NoiseParams)); + wgpuBufferUnmap(uniform_buf); + + // Create storage texture view + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = WGPUTextureFormat_RGBA8Unorm; + view_desc.dimension = WGPUTextureViewDimension_2D; + view_desc.mipLevelCount = 1; + view_desc.arrayLayerCount = 1; + WGPUTextureView target_view = wgpuTextureCreateView(target, &view_desc); + + // Create bind group layout entries (must match pipeline) + WGPUBindGroupLayoutEntry bgl_entries[2] = {}; + bgl_entries[0].binding = 0; + bgl_entries[0].visibility = WGPUShaderStage_Compute; + bgl_entries[0].storageTexture.access = WGPUStorageTextureAccess_WriteOnly; + bgl_entries[0].storageTexture.format = WGPUTextureFormat_RGBA8Unorm; + bgl_entries[0].storageTexture.viewDimension = WGPUTextureViewDimension_2D; + bgl_entries[1].binding = 1; + bgl_entries[1].visibility = WGPUShaderStage_Compute; + bgl_entries[1].buffer.type = WGPUBufferBindingType_Uniform; + bgl_entries[1].buffer.minBindingSize = 16; + + WGPUBindGroupLayoutDescriptor bgl_desc = {}; + bgl_desc.entryCount = 2; + bgl_desc.entries = bgl_entries; + WGPUBindGroupLayout bind_group_layout = + wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); + + // Create bind group + WGPUBindGroupEntry bg_entries[2] = {}; + bg_entries[0].binding = 0; + bg_entries[0].textureView = target_view; + bg_entries[1].binding = 1; + bg_entries[1].buffer = uniform_buf; + bg_entries[1].size = sizeof(NoiseParams); + + WGPUBindGroupDescriptor bg_desc = {}; + bg_desc.layout = bind_group_layout; + bg_desc.entryCount = 2; + bg_desc.entries = bg_entries; + WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device_, &bg_desc); + + // Dispatch compute + WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device_, &enc_desc); + WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr); + wgpuComputePassEncoderSetPipeline(pass, noise_compute_pipeline_); + wgpuComputePassEncoderSetBindGroup(pass, 0, bind_group, 0, nullptr); + wgpuComputePassEncoderDispatchWorkgroups(pass, (params.width + 7) / 8, + (params.height + 7) / 8, 1); + wgpuComputePassEncoderEnd(pass); + + WGPUCommandBufferDescriptor cmd_desc = {}; + WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(encoder, &cmd_desc); + wgpuQueueSubmit(queue_, 1, &cmd); + + // Cleanup + wgpuCommandBufferRelease(cmd); + wgpuCommandEncoderRelease(encoder); + wgpuComputePassEncoderRelease(pass); + wgpuBindGroupRelease(bind_group); + wgpuBindGroupLayoutRelease(bind_group_layout); + wgpuBufferRelease(uniform_buf); + wgpuTextureViewRelease(target_view); +} + +void TextureManager::create_gpu_noise_texture( + const std::string& name, const GpuProceduralParams& params) { + // Create storage texture + WGPUTextureDescriptor tex_desc = {}; + tex_desc.usage = + WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding; + tex_desc.dimension = WGPUTextureDimension_2D; + tex_desc.size = {(uint32_t)params.width, (uint32_t)params.height, 1}; + tex_desc.format = WGPUTextureFormat_RGBA8Unorm; + tex_desc.mipLevelCount = 1; + tex_desc.sampleCount = 1; + WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc); + + // Generate via compute + dispatch_noise_compute(texture, params); + + // Create view for sampling + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = WGPUTextureFormat_RGBA8Unorm; + view_desc.dimension = WGPUTextureViewDimension_2D; + view_desc.mipLevelCount = 1; + view_desc.arrayLayerCount = 1; + WGPUTextureView view = wgpuTextureCreateView(texture, &view_desc); + + // Store texture + GpuTexture gpu_tex; + gpu_tex.texture = texture; + gpu_tex.view = view; + gpu_tex.width = params.width; + gpu_tex.height = params.height; + textures_[name] = gpu_tex; + +#if !defined(STRIP_ALL) + printf("Generated GPU noise texture: %s (%dx%d)\n", name.c_str(), + params.width, params.height); +#endif +} + +#if !defined(STRIP_ALL) +WGPUTextureView TextureManager::get_or_generate_gpu_texture( + const std::string& name, const GpuProceduralParams& params) { + auto it = textures_.find(name); + if (it != textures_.end()) { + return it->second.view; + } + create_gpu_noise_texture(name, params); + return textures_[name].view; +} +#endif diff --git a/src/gpu/texture_manager.h b/src/gpu/texture_manager.h index 23fdbe8..0cffe0c 100644 --- a/src/gpu/texture_manager.h +++ b/src/gpu/texture_manager.h @@ -23,6 +23,13 @@ struct GpuTexture { int height; }; +struct GpuProceduralParams { + int width; + int height; + const float* params; + int num_params; +}; + class TextureManager { public: void init(WGPUDevice device, WGPUQueue queue); @@ -36,11 +43,24 @@ class TextureManager { void create_texture(const std::string& name, int width, int height, const uint8_t* data); + // GPU procedural generation + void create_gpu_noise_texture(const std::string& name, + const GpuProceduralParams& params); + +#if !defined(STRIP_ALL) + // On-demand lazy generation (stripped in final builds) + WGPUTextureView get_or_generate_gpu_texture(const std::string& name, + const GpuProceduralParams& params); +#endif + // Retrieves a texture view by name (returns nullptr if not found) WGPUTextureView get_texture_view(const std::string& name); private: + void dispatch_noise_compute(WGPUTexture target, + const GpuProceduralParams& params); WGPUDevice device_; WGPUQueue queue_; std::map<std::string, GpuTexture> textures_; + WGPUComputePipeline noise_compute_pipeline_ = nullptr; }; diff --git a/src/tests/test_gpu_procedural.cc b/src/tests/test_gpu_procedural.cc new file mode 100644 index 0000000..f68eb4f --- /dev/null +++ b/src/tests/test_gpu_procedural.cc @@ -0,0 +1,56 @@ +// This file is part of the 64k demo project. +// Tests GPU procedural texture generation. + +#include "gpu/gpu.h" +#include "gpu/texture_manager.h" +#include "platform/platform.h" +#include <cstdio> + +int main() { + printf("GPU Procedural Test: Starting...\n"); + + // Minimal GPU initialization for testing + PlatformState platform = platform_init(false, 256, 256); + if (!platform.window) { + fprintf(stderr, "Error: Failed to create window\n"); + return 1; + } + + gpu_init(&platform); + const GpuContext* ctx = gpu_get_context(); + + // Initialize shader composer (needed for #include resolution) + extern void InitShaderComposer(); + InitShaderComposer(); + + // Create TextureManager + TextureManager tex_mgr; + tex_mgr.init(ctx->device, ctx->queue); + + // Test GPU noise generation + GpuProceduralParams params = {}; + params.width = 256; + params.height = 256; + float proc_params[2] = {0.0f, 4.0f}; // seed, frequency + params.params = proc_params; + params.num_params = 2; + + tex_mgr.create_gpu_noise_texture("test_noise", params); + + // Verify texture exists + WGPUTextureView view = tex_mgr.get_texture_view("test_noise"); + if (!view) { + fprintf(stderr, "Error: GPU noise texture not created\n"); + tex_mgr.shutdown(); + gpu_shutdown(); + return 1; + } + + printf("SUCCESS: GPU noise texture created (256x256)\n"); + + // Cleanup + tex_mgr.shutdown(); + gpu_shutdown(); + platform_shutdown(&platform); + return 0; +} diff --git a/src/util/asset_manager.h b/src/util/asset_manager.h index 1e0638c..168bfca 100644 --- a/src/util/asset_manager.h +++ b/src/util/asset_manager.h @@ -10,6 +10,7 @@ struct AssetRecord { size_t size; // Size of the asset data bool is_procedural; // True if data was dynamically allocated by a procedural // generator + bool is_gpu_procedural; // True if GPU compute shader generates texture const char* proc_func_name_str; // Name of procedural generation function // (string literal) const float* proc_params; // Parameters for procedural generation (static, |
