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