summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/final/demo_assets.txt2
-rw-r--r--assets/final/shaders/compute/gen_grid.wgsl24
-rw-r--r--assets/final/shaders/compute/gen_perlin.wgsl44
-rw-r--r--src/gpu/effects/shaders.cc8
-rw-r--r--src/gpu/effects/shaders.h2
-rw-r--r--src/gpu/texture_manager.cc354
-rw-r--r--src/gpu/texture_manager.h10
-rw-r--r--src/tests/test_3d_render.cc39
-rw-r--r--tools/asset_packer.cc6
9 files changed, 473 insertions, 16 deletions
diff --git a/assets/final/demo_assets.txt b/assets/final/demo_assets.txt
index 1fdf2b4..0f7c267 100644
--- a/assets/final/demo_assets.txt
+++ b/assets/final/demo_assets.txt
@@ -53,6 +53,8 @@ MESH_CUBE, NONE, test_mesh.obj, "A simple cube mesh"
DODECAHEDRON, NONE, dodecahedron.obj, "A dodecahedron mesh"
SHADER_VIGNETTE, NONE, shaders/vignette.wgsl, "Vignette Shader"
SHADER_COMPUTE_GEN_NOISE, NONE, shaders/compute/gen_noise.wgsl, "GPU Noise Compute Shader"
+SHADER_COMPUTE_GEN_PERLIN, NONE, shaders/compute/gen_perlin.wgsl, "GPU Perlin Noise Compute Shader"
+SHADER_COMPUTE_GEN_GRID, NONE, shaders/compute/gen_grid.wgsl, "GPU Grid Compute Shader"
CIRCLE_MASK_COMPUTE_SHADER, NONE, shaders/circle_mask_compute.wgsl, "Circle mask compute shader"
CIRCLE_MASK_RENDER_SHADER, NONE, shaders/circle_mask_render.wgsl, "Circle mask render shader"
MASKED_CUBE_SHADER, NONE, shaders/masked_cube.wgsl, "Masked cube shader"
diff --git a/assets/final/shaders/compute/gen_grid.wgsl b/assets/final/shaders/compute/gen_grid.wgsl
new file mode 100644
index 0000000..cc5e189
--- /dev/null
+++ b/assets/final/shaders/compute/gen_grid.wgsl
@@ -0,0 +1,24 @@
+// GPU procedural grid pattern generator.
+// Simple grid lines with configurable spacing and thickness.
+
+struct GridParams {
+ width: u32,
+ height: u32,
+ grid_size: u32,
+ thickness: u32,
+}
+
+@group(0) @binding(0) var output_tex: texture_storage_2d<rgba8unorm, write>;
+@group(0) @binding(1) var<uniform> params: GridParams;
+
+@compute @workgroup_size(8, 8, 1)
+fn main(@builtin(global_invocation_id) id: vec3<u32>) {
+ if (id.x >= params.width || id.y >= params.height) { return; }
+
+ let on_line = (id.x % params.grid_size) < params.thickness ||
+ (id.y % params.grid_size) < params.thickness;
+
+ let val = select(0.0, 1.0, on_line);
+
+ textureStore(output_tex, id.xy, vec4<f32>(val, val, val, 1.0));
+}
diff --git a/assets/final/shaders/compute/gen_perlin.wgsl b/assets/final/shaders/compute/gen_perlin.wgsl
new file mode 100644
index 0000000..73816d6
--- /dev/null
+++ b/assets/final/shaders/compute/gen_perlin.wgsl
@@ -0,0 +1,44 @@
+// GPU procedural Perlin noise texture generator.
+// Fractional Brownian Motion using value noise.
+
+#include "math/noise"
+
+struct PerlinParams {
+ width: u32,
+ height: u32,
+ seed: f32,
+ frequency: f32,
+ amplitude: f32,
+ amplitude_decay: f32,
+ octaves: u32,
+ _pad0: f32, // Padding for alignment
+}
+
+@group(0) @binding(0) var output_tex: texture_storage_2d<rgba8unorm, write>;
+@group(0) @binding(1) var<uniform> params: PerlinParams;
+
+@compute @workgroup_size(8, 8, 1)
+fn main(@builtin(global_invocation_id) id: vec3<u32>) {
+ if (id.x >= params.width || id.y >= params.height) { return; }
+
+ let uv = vec2<f32>(f32(id.x) / f32(params.width),
+ f32(id.y) / f32(params.height));
+
+ var value = 0.0;
+ var amplitude = params.amplitude;
+ var frequency = params.frequency;
+ var total_amp = 0.0;
+
+ for (var o: u32 = 0u; o < params.octaves; o++) {
+ let p = uv * frequency + params.seed;
+ value += noise_2d(p) * amplitude;
+ total_amp += amplitude;
+ frequency *= 2.0;
+ amplitude *= params.amplitude_decay;
+ }
+
+ value /= total_amp;
+ let clamped = clamp(value, 0.0, 1.0);
+
+ textureStore(output_tex, id.xy, vec4<f32>(clamped, clamped, clamped, 1.0));
+}
diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc
index 5c6dd37..6ed82d5 100644
--- a/src/gpu/effects/shaders.cc
+++ b/src/gpu/effects/shaders.cc
@@ -103,6 +103,14 @@ const char* gen_noise_compute_wgsl =
SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_NOISE);
+const char* gen_perlin_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_PERLIN);
+
+const char* gen_grid_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_GRID);
+
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 b629e30..a0f91da 100644
--- a/src/gpu/effects/shaders.h
+++ b/src/gpu/effects/shaders.h
@@ -19,3 +19,5 @@ 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;
+extern const char* gen_perlin_compute_wgsl;
+extern const char* gen_grid_compute_wgsl;
diff --git a/src/gpu/texture_manager.cc b/src/gpu/texture_manager.cc
index aff106a..9a19957 100644
--- a/src/gpu/texture_manager.cc
+++ b/src/gpu/texture_manager.cc
@@ -22,6 +22,8 @@ void TextureManager::init(WGPUDevice device, WGPUQueue queue) {
device_ = device;
queue_ = queue;
noise_compute_pipeline_ = nullptr;
+ perlin_compute_pipeline_ = nullptr;
+ grid_compute_pipeline_ = nullptr;
}
void TextureManager::shutdown() {
@@ -34,6 +36,14 @@ void TextureManager::shutdown() {
wgpuComputePipelineRelease(noise_compute_pipeline_);
noise_compute_pipeline_ = nullptr;
}
+ if (perlin_compute_pipeline_) {
+ wgpuComputePipelineRelease(perlin_compute_pipeline_);
+ perlin_compute_pipeline_ = nullptr;
+ }
+ if (grid_compute_pipeline_) {
+ wgpuComputePipelineRelease(grid_compute_pipeline_);
+ grid_compute_pipeline_ = nullptr;
+ }
}
void TextureManager::create_procedural_texture(
@@ -297,6 +307,350 @@ void TextureManager::create_gpu_noise_texture(
#endif
}
+void TextureManager::dispatch_perlin_compute(WGPUTexture target,
+ const GpuProceduralParams& params) {
+ // Lazy-init compute pipeline
+ if (!perlin_compute_pipeline_) {
+ extern const char* gen_perlin_compute_wgsl;
+ ShaderComposer& composer = ShaderComposer::Get();
+ std::string resolved_shader = composer.Compose({}, gen_perlin_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);
+
+ 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 = 32; // sizeof(PerlinParams)
+
+ 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");
+
+ perlin_compute_pipeline_ =
+ wgpuDeviceCreateComputePipeline(device_, &pipeline_desc);
+
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuShaderModuleRelease(shader_module);
+ }
+
+ // Create uniform buffer
+ struct PerlinParams {
+ uint32_t width;
+ uint32_t height;
+ float seed;
+ float frequency;
+ float amplitude;
+ float amplitude_decay;
+ uint32_t octaves;
+ float _pad0;
+ };
+ PerlinParams uniform_data = {
+ (uint32_t)params.width,
+ (uint32_t)params.height,
+ params.params[0], // seed
+ params.params[1], // frequency
+ params.num_params > 2 ? params.params[2] : 1.0f, // amplitude
+ params.num_params > 3 ? params.params[3] : 0.5f, // amplitude_decay
+ params.num_params > 4 ? (uint32_t)params.params[4] : 4u, // octaves
+ 0.0f // padding
+ };
+
+ WGPUBufferDescriptor buf_desc = {};
+ buf_desc.size = sizeof(PerlinParams);
+ 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(PerlinParams));
+ memcpy(mapped, &uniform_data, sizeof(PerlinParams));
+ wgpuBufferUnmap(uniform_buf);
+
+ 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);
+
+ 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 = 32;
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = 2;
+ bgl_desc.entries = bgl_entries;
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ 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(PerlinParams);
+
+ 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);
+
+ WGPUCommandEncoderDescriptor enc_desc = {};
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device_, &enc_desc);
+ WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, perlin_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);
+
+ wgpuCommandBufferRelease(cmd);
+ wgpuCommandEncoderRelease(encoder);
+ wgpuComputePassEncoderRelease(pass);
+ wgpuBindGroupRelease(bind_group);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuBufferRelease(uniform_buf);
+ wgpuTextureViewRelease(target_view);
+}
+
+void TextureManager::create_gpu_perlin_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ 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);
+
+ dispatch_perlin_compute(texture, params);
+
+ 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);
+
+ 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 perlin texture: %s (%dx%d)\n", name.c_str(),
+ params.width, params.height);
+#endif
+}
+
+void TextureManager::dispatch_grid_compute(WGPUTexture target,
+ const GpuProceduralParams& params) {
+ // Lazy-init compute pipeline
+ if (!grid_compute_pipeline_) {
+ extern const char* gen_grid_compute_wgsl;
+ ShaderComposer& composer = ShaderComposer::Get();
+ std::string resolved_shader = composer.Compose({}, gen_grid_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);
+
+ 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(GridParams)
+
+ 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");
+
+ grid_compute_pipeline_ =
+ wgpuDeviceCreateComputePipeline(device_, &pipeline_desc);
+
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuShaderModuleRelease(shader_module);
+ }
+
+ // Create uniform buffer
+ struct GridParams {
+ uint32_t width;
+ uint32_t height;
+ uint32_t grid_size;
+ uint32_t thickness;
+ };
+ GridParams uniform_data = {
+ (uint32_t)params.width,
+ (uint32_t)params.height,
+ params.num_params > 0 ? (uint32_t)params.params[0] : 32u, // grid_size
+ params.num_params > 1 ? (uint32_t)params.params[1] : 2u // thickness
+ };
+
+ WGPUBufferDescriptor buf_desc = {};
+ buf_desc.size = sizeof(GridParams);
+ 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(GridParams));
+ memcpy(mapped, &uniform_data, sizeof(GridParams));
+ wgpuBufferUnmap(uniform_buf);
+
+ 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);
+
+ 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);
+
+ 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(GridParams);
+
+ 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);
+
+ WGPUCommandEncoderDescriptor enc_desc = {};
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device_, &enc_desc);
+ WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, grid_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);
+
+ wgpuCommandBufferRelease(cmd);
+ wgpuCommandEncoderRelease(encoder);
+ wgpuComputePassEncoderRelease(pass);
+ wgpuBindGroupRelease(bind_group);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuBufferRelease(uniform_buf);
+ wgpuTextureViewRelease(target_view);
+}
+
+void TextureManager::create_gpu_grid_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ 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);
+
+ dispatch_grid_compute(texture, params);
+
+ 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);
+
+ 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 grid 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) {
diff --git a/src/gpu/texture_manager.h b/src/gpu/texture_manager.h
index 0cffe0c..b2dea84 100644
--- a/src/gpu/texture_manager.h
+++ b/src/gpu/texture_manager.h
@@ -46,6 +46,10 @@ class TextureManager {
// GPU procedural generation
void create_gpu_noise_texture(const std::string& name,
const GpuProceduralParams& params);
+ void create_gpu_perlin_texture(const std::string& name,
+ const GpuProceduralParams& params);
+ void create_gpu_grid_texture(const std::string& name,
+ const GpuProceduralParams& params);
#if !defined(STRIP_ALL)
// On-demand lazy generation (stripped in final builds)
@@ -59,8 +63,14 @@ class TextureManager {
private:
void dispatch_noise_compute(WGPUTexture target,
const GpuProceduralParams& params);
+ void dispatch_perlin_compute(WGPUTexture target,
+ const GpuProceduralParams& params);
+ void dispatch_grid_compute(WGPUTexture target,
+ const GpuProceduralParams& params);
WGPUDevice device_;
WGPUQueue queue_;
std::map<std::string, GpuTexture> textures_;
WGPUComputePipeline noise_compute_pipeline_ = nullptr;
+ WGPUComputePipeline perlin_compute_pipeline_ = nullptr;
+ WGPUComputePipeline grid_compute_pipeline_ = nullptr;
};
diff --git a/src/tests/test_3d_render.cc b/src/tests/test_3d_render.cc
index fa13a43..eee46ba 100644
--- a/src/tests/test_3d_render.cc
+++ b/src/tests/test_3d_render.cc
@@ -220,25 +220,36 @@ int main(int argc, char** argv) {
g_renderer.resize(platform_state.width, platform_state.height);
g_textures.init(g_device, g_queue);
- ProceduralTextureDef noise_def;
- noise_def.width = 256;
- noise_def.height = 256;
- noise_def.gen_func = gen_periodic_noise;
- noise_def.params.push_back(1234.0f);
- noise_def.params.push_back(16.0f);
- g_textures.create_procedural_texture("noise", noise_def);
+ // GPU Noise texture (replaces CPU procedural)
+ GpuProceduralParams noise_params = {};
+ noise_params.width = 256;
+ noise_params.height = 256;
+ float noise_vals[2] = {1234.0f, 16.0f};
+ noise_params.params = noise_vals;
+ noise_params.num_params = 2;
+ g_textures.create_gpu_noise_texture("noise", noise_params);
g_renderer.set_noise_texture(g_textures.get_texture_view("noise"));
- ProceduralTextureDef sky_def;
- sky_def.width = 512;
- sky_def.height = 256;
- sky_def.gen_func = procedural::gen_perlin;
- sky_def.params = {42.0f, 4.0f, 1.0f, 0.5f, 6.0f};
- g_textures.create_procedural_texture("sky", sky_def);
-
+ // GPU Perlin texture for sky (replaces CPU procedural)
+ GpuProceduralParams sky_params = {};
+ sky_params.width = 512;
+ sky_params.height = 256;
+ float sky_vals[5] = {42.0f, 4.0f, 1.0f, 0.5f, 6.0f};
+ sky_params.params = sky_vals;
+ sky_params.num_params = 5;
+ g_textures.create_gpu_perlin_texture("sky", sky_params);
g_renderer.set_sky_texture(g_textures.get_texture_view("sky"));
+ // GPU Grid texture (new!)
+ GpuProceduralParams grid_params = {};
+ grid_params.width = 256;
+ grid_params.height = 256;
+ float grid_vals[2] = {32.0f, 2.0f}; // grid_size, thickness
+ grid_params.params = grid_vals;
+ grid_params.num_params = 2;
+ g_textures.create_gpu_grid_texture("grid", grid_params);
+
setup_scene();
g_camera.position = vec3(0, 5, 10);
diff --git a/tools/asset_packer.cc b/tools/asset_packer.cc
index 72592ae..4aaa0e7 100644
--- a/tools/asset_packer.cc
+++ b/tools/asset_packer.cc
@@ -230,9 +230,11 @@ int main(int argc, char* argv[]) {
}
// Validate GPU procedural function name
- if (info.proc_func_name != "gen_noise") {
+ if (info.proc_func_name != "gen_noise" &&
+ info.proc_func_name != "gen_perlin" &&
+ info.proc_func_name != "gen_grid") {
fprintf(stderr,
- "Error: PROC_GPU only supports gen_noise, got: %s for asset: %s\n",
+ "Error: PROC_GPU only supports gen_noise, gen_perlin, gen_grid, got: %s for asset: %s\n",
info.proc_func_name.c_str(), info.name.c_str());
return 1;
}