1use std::cell::Cell;
7
8use canvas_traits::webgl::{
9 ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError,
10 WebGLProgramId, WebGLResult, webgl_channel,
11};
12use dom_struct::dom_struct;
13use fnv::FnvHashSet;
14
15use crate::canvas_context::CanvasContext;
16use crate::dom::bindings::cell::{DomRefCell, Ref};
17use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
18use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
21use crate::dom::bindings::root::{DomRoot, MutNullableDom};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
24use crate::dom::webgl::webglobject::WebGLObject;
25use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
26use crate::dom::webgl::webglshader::WebGLShader;
27use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
31pub(crate) struct WebGLProgram {
32 webgl_object: WebGLObject,
33 #[no_trace]
34 id: WebGLProgramId,
35 is_in_use: Cell<bool>,
36 marked_for_deletion: Cell<bool>,
37 link_called: Cell<bool>,
38 linked: Cell<bool>,
39 link_generation: Cell<u64>,
40 fragment_shader: MutNullableDom<WebGLShader>,
41 vertex_shader: MutNullableDom<WebGLShader>,
42 #[no_trace]
43 active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
44 #[no_trace]
45 active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
46 #[no_trace]
47 active_uniform_blocks: DomRefCell<Box<[ActiveUniformBlockInfo]>>,
48 transform_feedback_varyings_length: Cell<i32>,
49 transform_feedback_mode: Cell<i32>,
50}
51
52impl WebGLProgram {
53 fn new_inherited(context: &WebGLRenderingContext, id: WebGLProgramId) -> Self {
54 Self {
55 webgl_object: WebGLObject::new_inherited(context),
56 id,
57 is_in_use: Default::default(),
58 marked_for_deletion: Default::default(),
59 link_called: Default::default(),
60 linked: Default::default(),
61 link_generation: Default::default(),
62 fragment_shader: Default::default(),
63 vertex_shader: Default::default(),
64 active_attribs: DomRefCell::new(vec![].into()),
65 active_uniforms: DomRefCell::new(vec![].into()),
66 active_uniform_blocks: DomRefCell::new(vec![].into()),
67 transform_feedback_varyings_length: Default::default(),
68 transform_feedback_mode: Default::default(),
69 }
70 }
71
72 pub(crate) fn maybe_new(
73 context: &WebGLRenderingContext,
74 can_gc: CanGc,
75 ) -> Option<DomRoot<Self>> {
76 let (sender, receiver) = webgl_channel().unwrap();
77 context.send_command(WebGLCommand::CreateProgram(sender));
78 receiver
79 .recv()
80 .unwrap()
81 .map(|id| WebGLProgram::new(context, id, can_gc))
82 }
83
84 pub(crate) fn new(
85 context: &WebGLRenderingContext,
86 id: WebGLProgramId,
87 can_gc: CanGc,
88 ) -> DomRoot<Self> {
89 reflect_dom_object(
90 Box::new(WebGLProgram::new_inherited(context, id)),
91 &*context.global(),
92 can_gc,
93 )
94 }
95}
96
97impl WebGLProgram {
98 pub(crate) fn id(&self) -> WebGLProgramId {
99 self.id
100 }
101
102 pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
104 if self.marked_for_deletion.get() {
105 return;
106 }
107 self.marked_for_deletion.set(true);
108 let cmd = WebGLCommand::DeleteProgram(self.id);
109 let context = self.upcast::<WebGLObject>().context();
110 match operation_fallibility {
111 Operation::Fallible => context.send_command_ignored(cmd),
112 Operation::Infallible => context.send_command(cmd),
113 }
114 if self.is_deleted() {
115 self.detach_shaders();
116 }
117 }
118
119 pub(crate) fn in_use(&self, value: bool) {
120 if self.is_in_use.get() == value {
121 return;
122 }
123 self.is_in_use.set(value);
124 if self.is_deleted() {
125 self.detach_shaders();
126 }
127 }
128
129 fn detach_shaders(&self) {
130 assert!(self.is_deleted());
131 if let Some(shader) = self.fragment_shader.get() {
132 shader.decrement_attached_counter();
133 self.fragment_shader.set(None);
134 }
135 if let Some(shader) = self.vertex_shader.get() {
136 shader.decrement_attached_counter();
137 self.vertex_shader.set(None);
138 }
139 }
140
141 pub(crate) fn is_in_use(&self) -> bool {
142 self.is_in_use.get()
143 }
144
145 pub(crate) fn is_marked_for_deletion(&self) -> bool {
146 self.marked_for_deletion.get()
147 }
148
149 pub(crate) fn is_deleted(&self) -> bool {
150 self.marked_for_deletion.get() && !self.is_in_use.get()
151 }
152
153 pub(crate) fn is_linked(&self) -> bool {
154 self.linked.get()
155 }
156
157 pub(crate) fn link(&self) -> WebGLResult<()> {
159 self.linked.set(false);
160 self.link_generation
161 .set(self.link_generation.get().checked_add(1).unwrap());
162 *self.active_attribs.borrow_mut() = Box::new([]);
163 *self.active_uniforms.borrow_mut() = Box::new([]);
164 *self.active_uniform_blocks.borrow_mut() = Box::new([]);
165
166 match self.fragment_shader.get() {
167 Some(ref shader) if shader.successfully_compiled() => {},
168 _ => return Ok(()), }
170
171 match self.vertex_shader.get() {
172 Some(ref shader) if shader.successfully_compiled() => {},
173 _ => return Ok(()), }
175
176 let (sender, receiver) = webgl_channel().unwrap();
177 self.upcast::<WebGLObject>()
178 .context()
179 .send_command(WebGLCommand::LinkProgram(self.id, sender));
180 let link_info = receiver.recv().unwrap();
181
182 {
183 let mut used_locs = FnvHashSet::default();
184 let mut used_names = FnvHashSet::default();
185 for active_attrib in &*link_info.active_attribs {
186 let Some(location) = active_attrib.location else {
187 continue;
188 };
189 let columns = match active_attrib.type_ {
190 constants::FLOAT_MAT2 => 2,
191 constants::FLOAT_MAT3 => 3,
192 constants::FLOAT_MAT4 => 4,
193 _ => 1,
194 };
195 assert!(used_names.insert(&*active_attrib.name));
196 for column in 0..columns {
197 if !used_locs.insert(location + column) {
199 return Ok(());
200 }
201 }
202 }
203 for active_uniform in &*link_info.active_uniforms {
204 if !used_names.insert(&*active_uniform.base_name) {
206 return Ok(());
207 }
208 }
209 }
210
211 self.linked.set(link_info.linked);
212 self.link_called.set(true);
213 self.transform_feedback_varyings_length
214 .set(link_info.transform_feedback_length);
215 self.transform_feedback_mode
216 .set(link_info.transform_feedback_mode);
217 *self.active_attribs.borrow_mut() = link_info.active_attribs;
218 *self.active_uniforms.borrow_mut() = link_info.active_uniforms;
219 *self.active_uniform_blocks.borrow_mut() = link_info.active_uniform_blocks;
220 Ok(())
221 }
222
223 pub(crate) fn active_attribs(&self) -> Ref<'_, [ActiveAttribInfo]> {
224 Ref::map(self.active_attribs.borrow(), |attribs| &**attribs)
225 }
226
227 pub(crate) fn active_uniforms(&self) -> Ref<'_, [ActiveUniformInfo]> {
228 Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms)
229 }
230
231 pub(crate) fn active_uniform_blocks(&self) -> Ref<'_, [ActiveUniformBlockInfo]> {
232 Ref::map(self.active_uniform_blocks.borrow(), |blocks| &**blocks)
233 }
234
235 pub(crate) fn validate(&self) -> WebGLResult<()> {
237 if self.is_deleted() {
238 return Err(WebGLError::InvalidOperation);
239 }
240 self.upcast::<WebGLObject>()
241 .context()
242 .send_command(WebGLCommand::ValidateProgram(self.id));
243 Ok(())
244 }
245
246 pub(crate) fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
248 if self.is_deleted() || shader.is_deleted() {
249 return Err(WebGLError::InvalidOperation);
250 }
251 let shader_slot = match shader.gl_type() {
252 constants::FRAGMENT_SHADER => &self.fragment_shader,
253 constants::VERTEX_SHADER => &self.vertex_shader,
254 _ => {
255 error!("detachShader: Unexpected shader type");
256 return Err(WebGLError::InvalidValue);
257 },
258 };
259
260 if shader_slot.get().is_some() {
261 return Err(WebGLError::InvalidOperation);
262 }
263
264 shader_slot.set(Some(shader));
265 shader.increment_attached_counter();
266
267 self.upcast::<WebGLObject>()
268 .context()
269 .send_command(WebGLCommand::AttachShader(self.id, shader.id()));
270
271 Ok(())
272 }
273
274 pub(crate) fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
276 if self.is_deleted() {
277 return Err(WebGLError::InvalidOperation);
278 }
279 let shader_slot = match shader.gl_type() {
280 constants::FRAGMENT_SHADER => &self.fragment_shader,
281 constants::VERTEX_SHADER => &self.vertex_shader,
282 _ => return Err(WebGLError::InvalidValue),
283 };
284
285 match shader_slot.get() {
286 Some(ref attached_shader) if attached_shader.id() != shader.id() => {
287 return Err(WebGLError::InvalidOperation);
288 },
289 None => return Err(WebGLError::InvalidOperation),
290 _ => {},
291 }
292
293 shader_slot.set(None);
294 shader.decrement_attached_counter();
295
296 self.upcast::<WebGLObject>()
297 .context()
298 .send_command(WebGLCommand::DetachShader(self.id, shader.id()));
299
300 Ok(())
301 }
302
303 pub(crate) fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
305 if self.is_deleted() {
306 return Err(WebGLError::InvalidOperation);
307 }
308
309 if !validate_glsl_name(&name)? {
310 return Ok(());
311 }
312 if name.starts_with("gl_") {
313 return Err(WebGLError::InvalidOperation);
314 }
315
316 self.upcast::<WebGLObject>()
317 .context()
318 .send_command(WebGLCommand::BindAttribLocation(
319 self.id,
320 index,
321 name.into(),
322 ));
323 Ok(())
324 }
325
326 pub(crate) fn get_active_uniform(
327 &self,
328 index: u32,
329 can_gc: CanGc,
330 ) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
331 if self.is_deleted() {
332 return Err(WebGLError::InvalidValue);
333 }
334 let uniforms = self.active_uniforms.borrow();
335 let data = uniforms
336 .get(index as usize)
337 .ok_or(WebGLError::InvalidValue)?;
338 Ok(WebGLActiveInfo::new(
339 self.global().as_window(),
340 data.size.unwrap_or(1),
341 data.type_,
342 data.name().into(),
343 can_gc,
344 ))
345 }
346
347 pub(crate) fn get_active_attrib(
349 &self,
350 index: u32,
351 can_gc: CanGc,
352 ) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
353 if self.is_deleted() {
354 return Err(WebGLError::InvalidValue);
355 }
356 let attribs = self.active_attribs.borrow();
357 let data = attribs
358 .get(index as usize)
359 .ok_or(WebGLError::InvalidValue)?;
360 Ok(WebGLActiveInfo::new(
361 self.global().as_window(),
362 data.size,
363 data.type_,
364 data.name.clone().into(),
365 can_gc,
366 ))
367 }
368
369 pub(crate) fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> {
371 if !self.is_linked() || self.is_deleted() {
372 return Err(WebGLError::InvalidOperation);
373 }
374
375 if !validate_glsl_name(&name)? {
376 return Ok(-1);
377 }
378 if name.starts_with("gl_") {
379 return Ok(-1);
380 }
381
382 let location = self
383 .active_attribs
384 .borrow()
385 .iter()
386 .find(|attrib| attrib.name == *name)
387 .and_then(|attrib| attrib.location.map(|l| l as i32))
388 .unwrap_or(-1);
389 Ok(location)
390 }
391
392 pub(crate) fn get_frag_data_location(&self, name: DOMString) -> WebGLResult<i32> {
394 if !self.is_linked() || self.is_deleted() {
395 return Err(WebGLError::InvalidOperation);
396 }
397
398 if !validate_glsl_name(&name)? {
399 return Ok(-1);
400 }
401 if name.starts_with("gl_") {
402 return Ok(-1);
403 }
404
405 let (sender, receiver) = webgl_channel().unwrap();
406 self.upcast::<WebGLObject>()
407 .context()
408 .send_command(WebGLCommand::GetFragDataLocation(
409 self.id,
410 name.into(),
411 sender,
412 ));
413 Ok(receiver.recv().unwrap())
414 }
415
416 pub(crate) fn get_uniform_location(
418 &self,
419 name: DOMString,
420 can_gc: CanGc,
421 ) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> {
422 if !self.is_linked() || self.is_deleted() {
423 return Err(WebGLError::InvalidOperation);
424 }
425
426 if !validate_glsl_name(&name)? {
427 return Ok(None);
428 }
429 if name.starts_with("gl_") {
430 return Ok(None);
431 }
432
433 let (size, type_) = {
434 let (base_name, array_index) = match parse_uniform_name(&name) {
435 Some((name, index)) if index.is_none_or(|i| i >= 0) => (name, index),
436 _ => return Ok(None),
437 };
438
439 let uniforms = self.active_uniforms.borrow();
440 match uniforms
441 .iter()
442 .find(|attrib| &*attrib.base_name == base_name)
443 {
444 Some(uniform) if array_index.is_none() || array_index < uniform.size => (
445 uniform
446 .size
447 .map(|size| size - array_index.unwrap_or_default()),
448 uniform.type_,
449 ),
450 _ => return Ok(None),
451 }
452 };
453
454 let (sender, receiver) = webgl_channel().unwrap();
455 self.upcast::<WebGLObject>()
456 .context()
457 .send_command(WebGLCommand::GetUniformLocation(
458 self.id,
459 name.into(),
460 sender,
461 ));
462 let location = receiver.recv().unwrap();
463 let context_id = self.upcast::<WebGLObject>().context().context_id();
464
465 Ok(Some(WebGLUniformLocation::new(
466 self.global().as_window(),
467 location,
468 context_id,
469 self.id,
470 self.link_generation.get(),
471 size,
472 type_,
473 can_gc,
474 )))
475 }
476
477 pub(crate) fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> {
478 if !self.link_called.get() || self.is_deleted() {
479 return Err(WebGLError::InvalidOperation);
480 }
481
482 if !validate_glsl_name(&name)? {
483 return Ok(constants2::INVALID_INDEX);
484 }
485
486 let (sender, receiver) = webgl_channel().unwrap();
487 self.upcast::<WebGLObject>()
488 .context()
489 .send_command(WebGLCommand::GetUniformBlockIndex(
490 self.id,
491 name.into(),
492 sender,
493 ));
494 Ok(receiver.recv().unwrap())
495 }
496
497 pub(crate) fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> {
498 if !self.link_called.get() || self.is_deleted() {
499 return Err(WebGLError::InvalidOperation);
500 }
501
502 let validation_errors = names
503 .iter()
504 .map(|name| validate_glsl_name(name))
505 .collect::<Vec<_>>();
506 let first_validation_error = validation_errors.iter().find(|result| result.is_err());
507 if let Some(error) = first_validation_error {
508 return Err(error.unwrap_err());
509 }
510
511 let names = names
512 .iter()
513 .map(|name| name.to_string())
514 .collect::<Vec<_>>();
515
516 let (sender, receiver) = webgl_channel().unwrap();
517 self.upcast::<WebGLObject>()
518 .context()
519 .send_command(WebGLCommand::GetUniformIndices(self.id, names, sender));
520 Ok(receiver.recv().unwrap())
521 }
522
523 pub(crate) fn get_active_uniforms(
524 &self,
525 indices: Vec<u32>,
526 pname: u32,
527 ) -> WebGLResult<Vec<i32>> {
528 if !self.is_linked() || self.is_deleted() {
529 return Err(WebGLError::InvalidOperation);
530 }
531
532 match pname {
533 constants2::UNIFORM_TYPE |
534 constants2::UNIFORM_SIZE |
535 constants2::UNIFORM_BLOCK_INDEX |
536 constants2::UNIFORM_OFFSET |
537 constants2::UNIFORM_ARRAY_STRIDE |
538 constants2::UNIFORM_MATRIX_STRIDE |
539 constants2::UNIFORM_IS_ROW_MAJOR => {},
540 _ => return Err(WebGLError::InvalidEnum),
541 }
542
543 if indices.len() > self.active_uniforms.borrow().len() {
544 return Err(WebGLError::InvalidValue);
545 }
546
547 let (sender, receiver) = webgl_channel().unwrap();
548 self.upcast::<WebGLObject>()
549 .context()
550 .send_command(WebGLCommand::GetActiveUniforms(
551 self.id, indices, pname, sender,
552 ));
553 Ok(receiver.recv().unwrap())
554 }
555
556 pub(crate) fn get_active_uniform_block_parameter(
557 &self,
558 block_index: u32,
559 pname: u32,
560 ) -> WebGLResult<Vec<i32>> {
561 if !self.link_called.get() || self.is_deleted() {
562 return Err(WebGLError::InvalidOperation);
563 }
564
565 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
566 return Err(WebGLError::InvalidValue);
567 }
568
569 match pname {
570 constants2::UNIFORM_BLOCK_BINDING |
571 constants2::UNIFORM_BLOCK_DATA_SIZE |
572 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS |
573 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES |
574 constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
575 constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {},
576 _ => return Err(WebGLError::InvalidEnum),
577 }
578
579 let (sender, receiver) = webgl_channel().unwrap();
580 self.upcast::<WebGLObject>().context().send_command(
581 WebGLCommand::GetActiveUniformBlockParameter(self.id, block_index, pname, sender),
582 );
583 Ok(receiver.recv().unwrap())
584 }
585
586 pub(crate) fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> {
587 if !self.link_called.get() || self.is_deleted() {
588 return Err(WebGLError::InvalidOperation);
589 }
590
591 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
592 return Err(WebGLError::InvalidValue);
593 }
594
595 let (sender, receiver) = webgl_channel().unwrap();
596 self.upcast::<WebGLObject>().context().send_command(
597 WebGLCommand::GetActiveUniformBlockName(self.id, block_index, sender),
598 );
599 Ok(receiver.recv().unwrap())
600 }
601
602 pub(crate) fn bind_uniform_block(
603 &self,
604 block_index: u32,
605 block_binding: u32,
606 ) -> WebGLResult<()> {
607 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
608 return Err(WebGLError::InvalidValue);
609 }
610
611 let mut active_uniforms = self.active_uniforms.borrow_mut();
612 if active_uniforms.len() > block_binding as usize {
613 active_uniforms[block_binding as usize].bind_index = Some(block_binding);
614 }
615
616 self.upcast::<WebGLObject>()
617 .context()
618 .send_command(WebGLCommand::UniformBlockBinding(
619 self.id,
620 block_index,
621 block_binding,
622 ));
623 Ok(())
624 }
625
626 pub(crate) fn get_info_log(&self) -> WebGLResult<String> {
628 if self.is_deleted() {
629 return Err(WebGLError::InvalidValue);
630 }
631 if self.link_called.get() {
632 let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
633 (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
634 _ => false,
635 };
636 if !shaders_compiled {
637 return Ok("One or more shaders failed to compile".to_string());
638 }
639 }
640 let (sender, receiver) = webgl_channel().unwrap();
641 self.upcast::<WebGLObject>()
642 .context()
643 .send_command(WebGLCommand::GetProgramInfoLog(self.id, sender));
644 Ok(receiver.recv().unwrap())
645 }
646
647 pub(crate) fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
648 if self.marked_for_deletion.get() {
649 return Err(WebGLError::InvalidValue);
650 }
651 Ok(
652 match (self.vertex_shader.get(), self.fragment_shader.get()) {
653 (Some(vertex_shader), Some(fragment_shader)) => {
654 vec![vertex_shader, fragment_shader]
655 },
656 (Some(shader), None) | (None, Some(shader)) => vec![shader],
657 (None, None) => vec![],
658 },
659 )
660 }
661
662 pub(crate) fn link_generation(&self) -> u64 {
663 self.link_generation.get()
664 }
665
666 pub(crate) fn transform_feedback_varyings_length(&self) -> i32 {
667 self.transform_feedback_varyings_length.get()
668 }
669
670 pub(crate) fn transform_feedback_buffer_mode(&self) -> i32 {
671 self.transform_feedback_mode.get()
672 }
673}
674
675impl Drop for WebGLProgram {
676 fn drop(&mut self) {
677 self.in_use(false);
678 self.mark_for_deletion(Operation::Fallible);
679 }
680}
681
682fn validate_glsl_name(name: &str) -> WebGLResult<bool> {
683 if name.is_empty() {
684 return Ok(false);
685 }
686 if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
687 return Err(WebGLError::InvalidValue);
688 }
689 for c in name.chars() {
690 validate_glsl_char(c)?;
691 }
692 if name.starts_with("webgl_") || name.starts_with("_webgl_") {
693 return Err(WebGLError::InvalidOperation);
694 }
695 Ok(true)
696}
697
698fn validate_glsl_char(c: char) -> WebGLResult<()> {
699 match c {
700 'a'..='z' |
701 'A'..='Z' |
702 '0'..='9' |
703 ' ' |
704 '\t' |
705 '\u{11}' |
706 '\u{12}' |
707 '\r' |
708 '\n' |
709 '_' |
710 '.' |
711 '+' |
712 '-' |
713 '/' |
714 '*' |
715 '%' |
716 '<' |
717 '>' |
718 '[' |
719 ']' |
720 '(' |
721 ')' |
722 '{' |
723 '}' |
724 '^' |
725 '|' |
726 '&' |
727 '~' |
728 '=' |
729 '!' |
730 ':' |
731 ';' |
732 ',' |
733 '?' => Ok(()),
734 _ => Err(WebGLError::InvalidValue),
735 }
736}
737
738fn parse_uniform_name(name: &str) -> Option<(&str, Option<i32>)> {
739 if !name.ends_with(']') {
740 return Some((name, None));
741 }
742 let bracket_pos = name[..name.len() - 1].rfind('[')?;
743 let index = name[(bracket_pos + 1)..(name.len() - 1)]
744 .parse::<i32>()
745 .ok()?;
746 Some((&name[..bracket_pos], Some(index)))
747}
748
749pub(crate) const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;