@@ -59,6 +59,86 @@ WGPUBuffer WebGPUGraph::create_scratch_buffer(size_t nbytes) {
5959 return buffer;
6060}
6161
62+ void WebGPUGraph::update_symints_from_inputs (
63+ const std::vector<std::pair<const void *, size_t >>& inputs) {
64+ for (const auto & src : symint_sources_) {
65+ int pos = -1 ;
66+ for (size_t i = 0 ; i < input_ids_.size (); i++) {
67+ if (input_ids_[i] == src.input_tensor_id ) {
68+ pos = static_cast <int >(i);
69+ break ;
70+ }
71+ }
72+ if (pos < 0 || pos >= static_cast <int >(inputs.size ())) {
73+ throw std::runtime_error (
74+ " select_as_symint: source tensor is not a graph input" );
75+ }
76+ const auto & dims = tensors_[src.input_tensor_id ].dims ;
77+ int dim = src.dim < 0 ? src.dim + static_cast <int >(dims.size ()) : src.dim ;
78+ if (dim < 0 || dim >= static_cast <int >(dims.size ())) {
79+ throw std::runtime_error (" select_as_symint: dim out of range" );
80+ }
81+ int index = src.index ;
82+ if (index < 0 ) {
83+ index += static_cast <int >(dims[dim]);
84+ }
85+ if (index < 0 || index >= static_cast <int >(dims[dim])) {
86+ throw std::runtime_error (" select_as_symint: index out of range" );
87+ }
88+ int64_t numel = 1 ;
89+ for (int64_t d : dims) {
90+ numel *= d;
91+ }
92+ if (numel <= 0 ) {
93+ throw std::runtime_error (" select_as_symint: empty input tensor" );
94+ }
95+ int64_t stride = 1 ;
96+ for (size_t i = static_cast <size_t >(dim) + 1 ; i < dims.size (); i++) {
97+ stride *= dims[i];
98+ }
99+ // Reads the [0,..,index,..,0] element; symint sources are scalar-ish.
100+ const int64_t offset = static_cast <int64_t >(index) * stride;
101+ // elem_size back-derived from build-time numel (sources are static-shaped).
102+ const void * host = inputs[pos].first ;
103+ const size_t elem_size = inputs[pos].second / static_cast <size_t >(numel);
104+ int32_t val;
105+ if (elem_size == sizeof (int64_t )) {
106+ val = static_cast <int32_t >(static_cast <const int64_t *>(host)[offset]);
107+ } else if (elem_size == sizeof (int32_t )) {
108+ val = static_cast <const int32_t *>(host)[offset];
109+ } else {
110+ throw std::runtime_error (
111+ " select_as_symint: unsupported input element size" );
112+ }
113+ set_symint (src.symint_id , val);
114+ }
115+ }
116+
117+ void WebGPUGraph::set_symint (int id, int32_t val) {
118+ auto it = symints_.find (id);
119+ if (it == symints_.end ()) {
120+ throw std::runtime_error (" WebGPUGraph::set_symint: id is not a SymInt" );
121+ }
122+ if (it->second .value != val) {
123+ it->second .value = val;
124+ wgpuQueueWriteBuffer (
125+ queue_, it->second .buffer , 0 , &it->second .value , sizeof (int32_t ));
126+ dirty_symints_.insert (id);
127+ }
128+ }
129+
130+ void WebGPUGraph::propagate_resize () {
131+ if (dirty_symints_.empty ()) {
132+ return ;
133+ }
134+ for (auto & hook : resize_hooks_) {
135+ if (dirty_symints_.count (hook.symint_id ) != 0 ) {
136+ hook.fn (*this );
137+ }
138+ }
139+ dirty_symints_.clear ();
140+ }
141+
62142WebGPUGraph::~WebGPUGraph () {
63143 for (size_t i = 0 ; i < tensors_.size (); i++) {
64144 if (tensors_[i].buffer &&
@@ -76,6 +156,16 @@ WebGPUGraph::~WebGPUGraph() {
76156 wgpuBufferRelease (buf);
77157 }
78158 }
159+ for (auto & buf : owned_uniform_buffers_) {
160+ if (buf) {
161+ wgpuBufferRelease (buf);
162+ }
163+ }
164+ for (auto & kv : symints_) {
165+ if (kv.second .buffer ) {
166+ wgpuBufferRelease (kv.second .buffer );
167+ }
168+ }
79169 for (auto & buf : output_staging_buffers_) {
80170 if (buf) {
81171 wgpuBufferRelease (buf);
@@ -236,6 +326,27 @@ void WebGPUGraph::build(
236326 bools_[i] = val->value_as_Bool ()->bool_val ();
237327 break ;
238328 }
329+ case vkgraph::GraphTypes::SymInt: {
330+ // Live scalar: small Uniform buffer the CPU rewrites per execute.
331+ value_types_[i] = ValueType::SymInt;
332+ SymIntSlot slot;
333+ slot.value = static_cast <int32_t >(val->value_as_SymInt ()->value ());
334+ // 16B matches the backend uniform-struct alignment; int32 in first 4.
335+ constexpr size_t kSymIntUniformBytes = 16 ;
336+ WGPUBufferDescriptor d = {};
337+ d.size = kSymIntUniformBytes ;
338+ d.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
339+ d.mappedAtCreation = true ;
340+ slot.buffer = wgpuDeviceCreateBuffer (device_, &d);
341+ void * mapped =
342+ wgpuBufferGetMappedRange (slot.buffer , 0 , kSymIntUniformBytes );
343+ std::memset (mapped, 0 , kSymIntUniformBytes );
344+ std::memcpy (mapped, &slot.value , sizeof (int32_t ));
345+ wgpuBufferUnmap (slot.buffer );
346+ symints_[i] = slot;
347+ add_uniform_buffer_bytes (kSymIntUniformBytes );
348+ break ;
349+ }
239350 default :
240351 value_types_[i] = ValueType::Null;
241352 break ;
0 commit comments