1use std::cell::Cell;
7use std::collections::HashSet;
8
9use canvas_traits::webgl::{
10 ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError,
11 WebGLProgramId, WebGLResult, webgl_channel,
12};
13use dom_struct::dom_struct;
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 = HashSet::new();
184 let mut used_names = HashSet::new();
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_str("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_str("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_str("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_str("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.iter().map(validate_glsl_name).collect::<Vec<_>>();
503 let first_validation_error = validation_errors.iter().find(|result| result.is_err());
504 if let Some(error) = first_validation_error {
505 return Err(error.unwrap_err());
506 }
507
508 let names = names
509 .iter()
510 .map(|name| name.to_string())
511 .collect::<Vec<_>>();
512
513 let (sender, receiver) = webgl_channel().unwrap();
514 self.upcast::<WebGLObject>()
515 .context()
516 .send_command(WebGLCommand::GetUniformIndices(self.id, names, sender));
517 Ok(receiver.recv().unwrap())
518 }
519
520 pub(crate) fn get_active_uniforms(
521 &self,
522 indices: Vec<u32>,
523 pname: u32,
524 ) -> WebGLResult<Vec<i32>> {
525 if !self.is_linked() || self.is_deleted() {
526 return Err(WebGLError::InvalidOperation);
527 }
528
529 match pname {
530 constants2::UNIFORM_TYPE |
531 constants2::UNIFORM_SIZE |
532 constants2::UNIFORM_BLOCK_INDEX |
533 constants2::UNIFORM_OFFSET |
534 constants2::UNIFORM_ARRAY_STRIDE |
535 constants2::UNIFORM_MATRIX_STRIDE |
536 constants2::UNIFORM_IS_ROW_MAJOR => {},
537 _ => return Err(WebGLError::InvalidEnum),
538 }
539
540 if indices.len() > self.active_uniforms.borrow().len() {
541 return Err(WebGLError::InvalidValue);
542 }
543
544 let (sender, receiver) = webgl_channel().unwrap();
545 self.upcast::<WebGLObject>()
546 .context()
547 .send_command(WebGLCommand::GetActiveUniforms(
548 self.id, indices, pname, sender,
549 ));
550 Ok(receiver.recv().unwrap())
551 }
552
553 pub(crate) fn get_active_uniform_block_parameter(
554 &self,
555 block_index: u32,
556 pname: u32,
557 ) -> WebGLResult<Vec<i32>> {
558 if !self.link_called.get() || self.is_deleted() {
559 return Err(WebGLError::InvalidOperation);
560 }
561
562 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
563 return Err(WebGLError::InvalidValue);
564 }
565
566 match pname {
567 constants2::UNIFORM_BLOCK_BINDING |
568 constants2::UNIFORM_BLOCK_DATA_SIZE |
569 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS |
570 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES |
571 constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
572 constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {},
573 _ => return Err(WebGLError::InvalidEnum),
574 }
575
576 let (sender, receiver) = webgl_channel().unwrap();
577 self.upcast::<WebGLObject>().context().send_command(
578 WebGLCommand::GetActiveUniformBlockParameter(self.id, block_index, pname, sender),
579 );
580 Ok(receiver.recv().unwrap())
581 }
582
583 pub(crate) fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> {
584 if !self.link_called.get() || self.is_deleted() {
585 return Err(WebGLError::InvalidOperation);
586 }
587
588 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
589 return Err(WebGLError::InvalidValue);
590 }
591
592 let (sender, receiver) = webgl_channel().unwrap();
593 self.upcast::<WebGLObject>().context().send_command(
594 WebGLCommand::GetActiveUniformBlockName(self.id, block_index, sender),
595 );
596 Ok(receiver.recv().unwrap())
597 }
598
599 pub(crate) fn bind_uniform_block(
600 &self,
601 block_index: u32,
602 block_binding: u32,
603 ) -> WebGLResult<()> {
604 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
605 return Err(WebGLError::InvalidValue);
606 }
607
608 let mut active_uniforms = self.active_uniforms.borrow_mut();
609 if active_uniforms.len() > block_binding as usize {
610 active_uniforms[block_binding as usize].bind_index = Some(block_binding);
611 }
612
613 self.upcast::<WebGLObject>()
614 .context()
615 .send_command(WebGLCommand::UniformBlockBinding(
616 self.id,
617 block_index,
618 block_binding,
619 ));
620 Ok(())
621 }
622
623 pub(crate) fn get_info_log(&self) -> WebGLResult<String> {
625 if self.is_deleted() {
626 return Err(WebGLError::InvalidValue);
627 }
628 if self.link_called.get() {
629 let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
630 (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
631 _ => false,
632 };
633 if !shaders_compiled {
634 return Ok("One or more shaders failed to compile".to_string());
635 }
636 }
637 let (sender, receiver) = webgl_channel().unwrap();
638 self.upcast::<WebGLObject>()
639 .context()
640 .send_command(WebGLCommand::GetProgramInfoLog(self.id, sender));
641 Ok(receiver.recv().unwrap())
642 }
643
644 pub(crate) fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
645 if self.marked_for_deletion.get() {
646 return Err(WebGLError::InvalidValue);
647 }
648 Ok(
649 match (self.vertex_shader.get(), self.fragment_shader.get()) {
650 (Some(vertex_shader), Some(fragment_shader)) => {
651 vec![vertex_shader, fragment_shader]
652 },
653 (Some(shader), None) | (None, Some(shader)) => vec![shader],
654 (None, None) => vec![],
655 },
656 )
657 }
658
659 pub(crate) fn link_generation(&self) -> u64 {
660 self.link_generation.get()
661 }
662
663 pub(crate) fn transform_feedback_varyings_length(&self) -> i32 {
664 self.transform_feedback_varyings_length.get()
665 }
666
667 pub(crate) fn transform_feedback_buffer_mode(&self) -> i32 {
668 self.transform_feedback_mode.get()
669 }
670}
671
672impl Drop for WebGLProgram {
673 fn drop(&mut self) {
674 self.in_use(false);
675 self.mark_for_deletion(Operation::Fallible);
676 }
677}
678
679fn validate_glsl_name(name: &DOMString) -> WebGLResult<bool> {
680 if name.is_empty() {
681 return Ok(false);
682 }
683 if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
684 return Err(WebGLError::InvalidValue);
685 }
686 for c in name.str().chars() {
687 validate_glsl_char(c)?;
688 }
689 if name.starts_with_str("webgl_") || name.starts_with_str("_webgl_") {
690 return Err(WebGLError::InvalidOperation);
691 }
692 Ok(true)
693}
694
695fn validate_glsl_char(c: char) -> WebGLResult<()> {
696 match c {
697 'a'..='z' |
698 'A'..='Z' |
699 '0'..='9' |
700 ' ' |
701 '\t' |
702 '\u{11}' |
703 '\u{12}' |
704 '\r' |
705 '\n' |
706 '_' |
707 '.' |
708 '+' |
709 '-' |
710 '/' |
711 '*' |
712 '%' |
713 '<' |
714 '>' |
715 '[' |
716 ']' |
717 '(' |
718 ')' |
719 '{' |
720 '}' |
721 '^' |
722 '|' |
723 '&' |
724 '~' |
725 '=' |
726 '!' |
727 ':' |
728 ';' |
729 ',' |
730 '?' => Ok(()),
731 _ => Err(WebGLError::InvalidValue),
732 }
733}
734
735fn parse_uniform_name(name: &DOMString) -> Option<(String, Option<i32>)> {
736 let name = name.str();
737 if !name.ends_with(']') {
738 return Some((String::from(name), None));
739 }
740 let bracket_pos = name[..name.len() - 1].rfind('[')?;
741 let index = name[(bracket_pos + 1)..(name.len() - 1)]
742 .parse::<i32>()
743 .ok()?;
744 Some((String::from(&name[..bracket_pos]), Some(index)))
745}
746
747pub(crate) const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;