diff options
| -rw-r--r-- | cmake/DemoSourceLists.cmake | 1 | ||||
| -rw-r--r-- | doc/EFFECT_WORKFLOW.md | 27 | ||||
| -rw-r--r-- | src/effects/scene2_effect.cc | 47 | ||||
| -rw-r--r-- | src/effects/scene2_effect.h | 24 | ||||
| -rw-r--r-- | src/effects/shaders.cc | 1 | ||||
| -rw-r--r-- | src/effects/shaders.h | 1 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 2 | ||||
| -rwxr-xr-x | tools/shadertoy/convert_shadertoy.py | 16 | ||||
| -rw-r--r-- | workspaces/main/assets.txt | 1 | ||||
| -rw-r--r-- | workspaces/main/shaders/scene2.wgsl | 43 | ||||
| -rw-r--r-- | workspaces/main/timeline.seq | 7 |
11 files changed, 151 insertions, 19 deletions
diff --git a/cmake/DemoSourceLists.cmake b/cmake/DemoSourceLists.cmake index f4574c7..3439cd5 100644 --- a/cmake/DemoSourceLists.cmake +++ b/cmake/DemoSourceLists.cmake @@ -40,6 +40,7 @@ set(COMMON_GPU_EFFECTS src/effects/flash_effect.cc src/effects/peak_meter_effect.cc src/effects/scene1_effect.cc + src/effects/scene2_effect.cc # TODO: Port CNN effects to v2 (complex v1 dependencies) # cnn_v1/src/cnn_v1_effect.cc # cnn_v2/src/cnn_v2_effect.cc diff --git a/doc/EFFECT_WORKFLOW.md b/doc/EFFECT_WORKFLOW.md index 46aebd2..3e574f6 100644 --- a/doc/EFFECT_WORKFLOW.md +++ b/doc/EFFECT_WORKFLOW.md @@ -68,13 +68,11 @@ SHADER_<UPPER_NAME>, NONE, shaders/<name>.wgsl, "Description" Asset ID: `AssetId::ASSET_SHADER_<UPPER_NAME>` -### 3. Add to CMakeLists.txt +### 3. Add to DemoSourceLists.cmake -**File**: `CMakeLists.txt` +**File**: `cmake/DemoSourceLists.cmake` -Add `src/effects/<name>_effect.cc` to **BOTH** GPU_SOURCES sections: -- Headless mode (around line 141-167) -- Normal mode (around line 171-197) +Add `src/effects/<name>_effect.cc` to the `COMMON_GPU_EFFECTS` list. ### 4. Include in demo_effects.h @@ -84,7 +82,19 @@ Add `src/effects/<name>_effect.cc` to **BOTH** GPU_SOURCES sections: #include "effects/<name>_effect.h" ``` -### 5. Add to Timeline +### 5. Add to test_demo_effects.cc + +**File**: `src/tests/gpu/test_demo_effects.cc` + +Add entry to the effects vector: +```cpp +{"MyEffect", std::make_shared<MyEffect>( + fixture.ctx(), inputs, outputs, 0.0f, 1000.0f)}, +``` + +Run with: `./build/test_demo_effects` + +### 6. Add to Timeline **File**: `workspaces/main/timeline.seq` @@ -95,7 +105,7 @@ SEQUENCE <start> <priority> "name" **Priority modifiers** (REQUIRED): `+` (increment), `=` (same), `-` (decrement) -### 6. Regenerate and Build +### 7. Regenerate and Build ```bash # Regenerate timeline.cc @@ -241,8 +251,7 @@ class My3DEffect : public Effect { - Asset ID is `ASSET_` + uppercase entry name **Build Error: "undefined symbol"** -- Effect not in CMakeLists.txt GPU_SOURCES -- Must add to BOTH sections (headless + normal) +- Effect not in `cmake/DemoSourceLists.cmake` COMMON_GPU_EFFECTS **Runtime Error: "Node not found"** - Forgot `declare_nodes()` for temp nodes diff --git a/src/effects/scene2_effect.cc b/src/effects/scene2_effect.cc new file mode 100644 index 0000000..b1b9975 --- /dev/null +++ b/src/effects/scene2_effect.cc @@ -0,0 +1,47 @@ +// This file is part of the 64k demo project. +// Scene2 effect - ShaderToy conversion (scene) +// Generated by convert_shadertoy.py + +#include "effects/scene2_effect.h" +#include "effects/shaders.h" +#include "gpu/gpu.h" +#include "gpu/post_process_helper.h" +#include "util/fatal_error.h" + +Scene2Effect::Scene2Effect(const GpuContext& ctx, + const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, + float start_time, float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time) { + HEADLESS_RETURN_IF_NULL(ctx_.device); + create_nearest_sampler(); + create_dummy_scene_texture(); + pipeline_.set(create_post_process_pipeline( + ctx_.device, WGPUTextureFormat_RGBA8Unorm, scene2_shader_wgsl)); +} + +void Scene2Effect::render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) { + WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); + + // uniforms_buffer_ auto-updated by base class dispatch_render() + pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), + dummy_texture_view_.get(), uniforms_buffer_.get(), + {nullptr, 0}); + + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; + + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); +} diff --git a/src/effects/scene2_effect.h b/src/effects/scene2_effect.h new file mode 100644 index 0000000..effc19f --- /dev/null +++ b/src/effects/scene2_effect.h @@ -0,0 +1,24 @@ +// This file is part of the 64k demo project. +// Scene2 effect - ShaderToy conversion (scene) +// Generated by convert_shadertoy.py + +#pragma once + +#include "gpu/effect.h" +#include "gpu/wgpu_resource.h" + +class Scene2Effect : public Effect { + public: + Scene2Effect(const GpuContext& ctx, + const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, + float start_time, float end_time); + + void render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) override; + + private: + RenderPipeline pipeline_; + BindGroup bind_group_; +}; diff --git a/src/effects/shaders.cc b/src/effects/shaders.cc index 7e32aa6..c6a81d0 100644 --- a/src/effects/shaders.cc +++ b/src/effects/shaders.cc @@ -96,6 +96,7 @@ const char* rotating_cube_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_ROTATING_CUBE_V2); const char* flash_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_FLASH); const char* scene1_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_SCENE1); +const char* scene2_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_SCENE2); // Compute shaders const char* gen_noise_compute_wgsl = diff --git a/src/effects/shaders.h b/src/effects/shaders.h index ee6c65e..5fc442a 100644 --- a/src/effects/shaders.h +++ b/src/effects/shaders.h @@ -15,6 +15,7 @@ extern const char* particle_render_wgsl; extern const char* rotating_cube_wgsl; extern const char* flash_shader_wgsl; extern const char* scene1_shader_wgsl; +extern const char* scene2_shader_wgsl; // Compute shaders extern const char* gen_noise_compute_wgsl; diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index f746326..e1a9c17 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -27,6 +27,8 @@ #include "effects/placeholder_effect.h" #include "effects/rotating_cube_effect.h" #include "effects/scene1_effect.h" +#include "effects/scene2_effect.h" + // TODO: Port CNN effects // #include "../../cnn_v1/src/cnn_v1_effect.h" // #include "../../cnn_v2/src/cnn_v2_effect.h" diff --git a/tools/shadertoy/convert_shadertoy.py b/tools/shadertoy/convert_shadertoy.py index 22b3276..9df42bb 100755 --- a/tools/shadertoy/convert_shadertoy.py +++ b/tools/shadertoy/convert_shadertoy.py @@ -12,7 +12,7 @@ # Generates: # - src/effects/<effect>_effect.h # - src/effects/<effect>_effect.cc -# - src/shaders/<effect>.wgsl +# - workspaces/main/shaders/<effect>.wgsl # # The script performs basic ShaderToy→WGSL conversion: # - Converts types (float→f32, vec2→vec2f, etc.) @@ -333,7 +333,7 @@ def main(): print("This will generate:") print(" src/effects/<effect>_effect.h") print(" src/effects/<effect>_effect.cc") - print(" src/shaders/<effect>.wgsl") + print(" workspaces/main/shaders/<effect>.wgsl") sys.exit(1) shader_file = sys.argv[1] @@ -361,7 +361,7 @@ def main(): repo_root = Path(__file__).parent.parent.parent header_path = repo_root / "src" / "effects" / f"{snake_name}_effect.h" impl_path = repo_root / "src" / "effects" / f"{snake_name}_effect.cc" - shader_path = repo_root / "src" / "shaders" / f"{snake_name}.wgsl" + shader_path = repo_root / "workspaces" / "main" / "shaders" / f"{snake_name}.wgsl" # Generate files if shader_only: @@ -389,7 +389,7 @@ def main(): print("Next steps (see doc/EFFECT_WORKFLOW.md for details):") print() print("1. Add shader to workspaces/main/assets.txt:") - print(f" SHADER_{upper_name}, NONE, ../../src/shaders/{snake_name}.wgsl, \"{effect_name} effect\"") + print(f" SHADER_{upper_name}, NONE, shaders/{snake_name}.wgsl, \"{effect_name} effect\"") print() print("2. Add shader declaration to src/effects/shaders.h:") print(f" extern const char* {snake_name}_shader_wgsl;") @@ -400,7 +400,7 @@ def main(): print("4. Include header in src/gpu/demo_effects.h:") print(f' #include "effects/{snake_name}_effect.h"') print() - print("5. Add to CMakeLists.txt GPU_SOURCES (both headless and normal mode sections):") + print("5. Add to cmake/DemoSourceLists.cmake COMMON_GPU_EFFECTS list:") print(f" src/effects/{snake_name}_effect.cc") print() print("6. Add to timeline in workspaces/main/timeline.seq:") @@ -410,7 +410,11 @@ def main(): print(" python3 tools/seq_compiler.py workspaces/main/timeline.seq \\") print(" --output src/generated/timeline.cc") print() - print("8. Build and test:") + print("8. Add to src/tests/gpu/test_demo_effects.cc:") + print(f' {{"{effect_name}", std::make_shared<{effect_name}Effect>(') + print(f' fixture.ctx(), inputs, outputs, 0.0f, 1000.0f)}}') + print() + print("9. Build and test:") print(" cmake --build build -j4") print(" ./build/demo64k") print() diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt index 85ae1fc..56d0942 100644 --- a/workspaces/main/assets.txt +++ b/workspaces/main/assets.txt @@ -82,6 +82,7 @@ CIRCLE_MASK_COMPUTE_SHADER, NONE, shaders/circle_mask_compute.wgsl, "Circle mask 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" SHADER_SCENE1, NONE, shaders/scene1.wgsl, "Scene1 effect shader" +SHADER_SCENE2, NONE, shaders/scene2.wgsl, "Scene2 effect shader" # --- Sequence Shaders --- SHADER_SEQUENCE_V2_UNIFORMS, NONE, ../../src/shaders/sequence_uniforms.wgsl, "Sequence Uniforms Snippet" diff --git a/workspaces/main/shaders/scene2.wgsl b/workspaces/main/shaders/scene2.wgsl new file mode 100644 index 0000000..486f41e --- /dev/null +++ b/workspaces/main/shaders/scene2.wgsl @@ -0,0 +1,43 @@ +// Scene2 effect shader - ShaderToy conversion +// Generated by convert_shadertoy.py +// NOTE: Manual review recommended - conversion is basic + +#include "sequence_uniforms" +#include "render/fullscreen_uv_vs" + +@group(0) @binding(2) var<uniform> uniforms: UniformsSequenceParams; + + +fn N(vec3f a, vec3f p) { return abs(dot(sin(uniforms.time+.1*p.z+.3*p / a), vec3f(a+a))); } +const NUM_LAYERS: f32 = 12.; + + + +@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { + // Flip Y to match ShaderToy convention (origin at bottom-left) + let flipped = vec2f(in.position.x, uniforms.resolution.y - in.position.y); + let q = flipped / uniforms.resolution; + var coord = -1.0 + 2.0 * q; + coord.x *= uniforms.resolution.x / uniforms.resolution.y; + + f32 i,s; + vec3f p = vec3f[0),r = uniforms.resolution; + + vec2f uv = (u-.5*uniforms.resolution.xy)/uniforms.resolution.y; + + f32 t = uniforms.time*.0; + vec3f col = vec3f(0); + + u = (u+u-r.xy)/r.y; + for(o*=i; i++<1e2; ) { + p += vec3f(u * s, s); + s = 6.+(p.y); + s -= N(.08); + s -= N(.2); + s -= N(.6); + s = .1 + abs(s)*.2; + o += vec4f(4,2,1,0)/s; + } + o *= smoothstep(0.8, 0.75, abs(u.y)); + o = tanh(o / 2e3 / length(u)); +} diff --git a/workspaces/main/timeline.seq b/workspaces/main/timeline.seq index 2b843a7..6399bdf 100644 --- a/workspaces/main/timeline.seq +++ b/workspaces/main/timeline.seq @@ -9,10 +9,9 @@ SEQUENCE 4.00 0 "rotating_cube" EFFECT + RotatingCube source -> temp1 0.00 4.00 EFFECT + Placeholder temp1 -> sink 1.00 4.00 -SEQUENCE 8.00 0 "flash_cube" - # FlashCube (placeholder) -> Flash (placeholder) -> sink - EFFECT - Placeholder source -> temp1 0.00 4.02 - EFFECT + Placeholder temp1 -> sink 0.00 0.40 +SEQUENCE 8.00 0 "scene 2" + # Scene2 test + EFFECT + Scene2Effect source -> sink 0.0 4.0 SEQUENCE 12.00 1 "particles" # Particles -> Blur -> sink |
