// Effect implementation #include "gpu/effect.h" #include "gpu/gpu.h" #include "gpu/sequence.h" #include "util/fatal_error.h" Effect::Effect(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, float start_time, float end_time) : ctx_(ctx), input_nodes_(inputs), output_nodes_(outputs), start_time_(start_time), end_time_(end_time) { FATAL_CHECK(!inputs.empty(), "Effect must have at least one input\n"); FATAL_CHECK(!outputs.empty(), "Effect must have at least one output\n"); FATAL_CHECK(start_time <= end_time, "Invalid time range: %f > %f\n", start_time, end_time); HEADLESS_RETURN_IF_NULL(ctx_.device); } void Effect::dispatch_render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) { // Check if effect is active at current time const bool active = (params.time >= start_time_ && params.time < end_time_); // Auto-passthrough for 1:1 input/output effects outside active range if (!active && input_nodes_.size() == 1 && output_nodes_.size() == 1) { blit_input_to_output(encoder, nodes); } else if (active) { render(encoder, params, nodes); } // Multi-output effects: output undefined when inactive (validated at compile time) } void Effect::blit_input_to_output(WGPUCommandEncoder encoder, NodeRegistry& nodes) { HEADLESS_RETURN_IF_NULL(encoder); WGPUTexture src = nodes.get_texture(input_nodes_[0]); WGPUTexture dst = nodes.get_texture(output_nodes_[0]); // Skip passthrough if textures are external (source/sink) or invalid if (!src || !dst) { return; } GpuTextureCopyInfo src_copy = { .texture = src, .mipLevel = 0, .origin = {0, 0, 0}}; GpuTextureCopyInfo dst_copy = { .texture = dst, .mipLevel = 0, .origin = {0, 0, 0}}; WGPUExtent3D extent = {(unsigned int)(width_), (unsigned int)(height_), 1}; wgpuCommandEncoderCopyTextureToTexture(encoder, &src_copy, &dst_copy, &extent); } void Effect::init_uniforms_buffer() { uniforms_buffer_.init(ctx_.device); } void Effect::create_linear_sampler() { sampler_ = gpu_create_linear_sampler(ctx_.device); } void Effect::create_nearest_sampler() { sampler_ = gpu_create_nearest_sampler(ctx_.device); } void Effect::create_dummy_scene_texture() { TextureWithView dummy = gpu_create_dummy_scene_texture(ctx_.device); dummy_texture_ = dummy.texture; dummy_texture_view_ = dummy.view; }