miniquad/graphics.rs
1//mod texture;
2
3use crate::native::gl::*;
4
5use std::{error::Error, fmt::Display};
6
7//pub use texture::{FilterMode, TextureAccess, TextureFormat, TextureParams, TextureWrap};
8
9mod gl;
10
11pub use gl::raw_gl;
12
13#[cfg(target_vendor = "apple")]
14mod metal;
15
16pub use gl::GlContext;
17
18#[cfg(target_vendor = "apple")]
19pub use metal::MetalContext;
20
21#[derive(Clone, Copy, Debug)]
22pub enum UniformType {
23 /// One 32-bit wide float (equivalent to `f32`)
24 Float1,
25 /// Two 32-bit wide floats (equivalent to `[f32; 2]`)
26 Float2,
27 /// Three 32-bit wide floats (equivalent to `[f32; 3]`)
28 Float3,
29 /// Four 32-bit wide floats (equivalent to `[f32; 4]`)
30 Float4,
31 /// One unsigned 32-bit integers (equivalent to `[u32; 1]`)
32 Int1,
33 /// Two unsigned 32-bit integers (equivalent to `[u32; 2]`)
34 Int2,
35 /// Three unsigned 32-bit integers (equivalent to `[u32; 3]`)
36 Int3,
37 /// Four unsigned 32-bit integers (equivalent to `[u32; 4]`)
38 Int4,
39 /// Four by four matrix of 32-bit floats
40 Mat4,
41}
42
43impl UniformType {
44 /// Byte size for a given UniformType
45 pub fn size(&self) -> usize {
46 match self {
47 UniformType::Float1 => 4,
48 UniformType::Float2 => 8,
49 UniformType::Float3 => 12,
50 UniformType::Float4 => 16,
51 UniformType::Int1 => 4,
52 UniformType::Int2 => 8,
53 UniformType::Int3 => 12,
54 UniformType::Int4 => 16,
55 UniformType::Mat4 => 64,
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
61pub struct UniformDesc {
62 pub name: String,
63 pub uniform_type: UniformType,
64 pub array_count: usize,
65}
66
67#[derive(Debug, Clone)]
68pub struct UniformBlockLayout {
69 pub uniforms: Vec<UniformDesc>,
70}
71
72impl UniformDesc {
73 pub fn new(name: &str, uniform_type: UniformType) -> UniformDesc {
74 UniformDesc {
75 name: name.to_string(),
76 uniform_type,
77 array_count: 1,
78 }
79 }
80
81 pub fn array(self, array_count: usize) -> UniformDesc {
82 UniformDesc {
83 array_count,
84 ..self
85 }
86 }
87}
88
89#[derive(Clone)]
90pub struct ShaderMeta {
91 pub uniforms: UniformBlockLayout,
92 pub images: Vec<String>,
93}
94
95#[derive(Clone, Copy, PartialEq, Debug)]
96pub enum VertexFormat {
97 /// One 32-bit wide float (equivalent to `f32`)
98 Float1,
99 /// Two 32-bit wide floats (equivalent to `[f32; 2]`)
100 Float2,
101 /// Three 32-bit wide floats (equivalent to `[f32; 3]`)
102 Float3,
103 /// Four 32-bit wide floats (equivalent to `[f32; 4]`)
104 Float4,
105 /// One unsigned 8-bit integer (equivalent to `u8`)
106 Byte1,
107 /// Two unsigned 8-bit integers (equivalent to `[u8; 2]`)
108 Byte2,
109 /// Three unsigned 8-bit integers (equivalent to `[u8; 3]`)
110 Byte3,
111 /// Four unsigned 8-bit integers (equivalent to `[u8; 4]`)
112 Byte4,
113 /// One unsigned 16-bit integer (equivalent to `u16`)
114 Short1,
115 /// Two unsigned 16-bit integers (equivalent to `[u16; 2]`)
116 Short2,
117 /// Tree unsigned 16-bit integers (equivalent to `[u16; 3]`)
118 Short3,
119 /// Four unsigned 16-bit integers (equivalent to `[u16; 4]`)
120 Short4,
121 /// One unsigned 32-bit integers (equivalent to `[u32; 1]`)
122 Int1,
123 /// Two unsigned 32-bit integers (equivalent to `[u32; 2]`)
124 Int2,
125 /// Three unsigned 32-bit integers (equivalent to `[u32; 3]`)
126 Int3,
127 /// Four unsigned 32-bit integers (equivalent to `[u32; 4]`)
128 Int4,
129 /// Four by four matrix of 32-bit floats
130 Mat4,
131}
132
133impl VertexFormat {
134 /// Number of components in this VertexFormat
135 /// it is called size in OpenGl, but do not confuse this with bytes size
136 /// basically, its an N from FloatN/IntN
137 pub fn components(&self) -> i32 {
138 match self {
139 VertexFormat::Float1 => 1,
140 VertexFormat::Float2 => 2,
141 VertexFormat::Float3 => 3,
142 VertexFormat::Float4 => 4,
143 VertexFormat::Byte1 => 1,
144 VertexFormat::Byte2 => 2,
145 VertexFormat::Byte3 => 3,
146 VertexFormat::Byte4 => 4,
147 VertexFormat::Short1 => 1,
148 VertexFormat::Short2 => 2,
149 VertexFormat::Short3 => 3,
150 VertexFormat::Short4 => 4,
151 VertexFormat::Int1 => 1,
152 VertexFormat::Int2 => 2,
153 VertexFormat::Int3 => 3,
154 VertexFormat::Int4 => 4,
155 VertexFormat::Mat4 => 16,
156 }
157 }
158
159 /// Size in bytes
160 pub fn size_bytes(&self) -> i32 {
161 match self {
162 VertexFormat::Float1 => 1 * 4,
163 VertexFormat::Float2 => 2 * 4,
164 VertexFormat::Float3 => 3 * 4,
165 VertexFormat::Float4 => 4 * 4,
166 VertexFormat::Byte1 => 1,
167 VertexFormat::Byte2 => 2,
168 VertexFormat::Byte3 => 3,
169 VertexFormat::Byte4 => 4,
170 VertexFormat::Short1 => 1 * 2,
171 VertexFormat::Short2 => 2 * 2,
172 VertexFormat::Short3 => 3 * 2,
173 VertexFormat::Short4 => 4 * 2,
174 VertexFormat::Int1 => 1 * 4,
175 VertexFormat::Int2 => 2 * 4,
176 VertexFormat::Int3 => 3 * 4,
177 VertexFormat::Int4 => 4 * 4,
178 VertexFormat::Mat4 => 16 * 4,
179 }
180 }
181
182 fn type_(&self) -> GLuint {
183 match self {
184 VertexFormat::Float1 => GL_FLOAT,
185 VertexFormat::Float2 => GL_FLOAT,
186 VertexFormat::Float3 => GL_FLOAT,
187 VertexFormat::Float4 => GL_FLOAT,
188 VertexFormat::Byte1 => GL_UNSIGNED_BYTE,
189 VertexFormat::Byte2 => GL_UNSIGNED_BYTE,
190 VertexFormat::Byte3 => GL_UNSIGNED_BYTE,
191 VertexFormat::Byte4 => GL_UNSIGNED_BYTE,
192 VertexFormat::Short1 => GL_UNSIGNED_SHORT,
193 VertexFormat::Short2 => GL_UNSIGNED_SHORT,
194 VertexFormat::Short3 => GL_UNSIGNED_SHORT,
195 VertexFormat::Short4 => GL_UNSIGNED_SHORT,
196 VertexFormat::Int1 => GL_UNSIGNED_INT,
197 VertexFormat::Int2 => GL_UNSIGNED_INT,
198 VertexFormat::Int3 => GL_UNSIGNED_INT,
199 VertexFormat::Int4 => GL_UNSIGNED_INT,
200 VertexFormat::Mat4 => GL_FLOAT,
201 }
202 }
203}
204
205#[derive(Clone, Copy, Debug, Default, PartialEq)]
206pub enum VertexStep {
207 #[default]
208 PerVertex,
209 PerInstance,
210}
211
212#[derive(Clone, Debug)]
213pub struct BufferLayout {
214 pub stride: i32,
215 pub step_func: VertexStep,
216 pub step_rate: i32,
217}
218
219impl Default for BufferLayout {
220 fn default() -> BufferLayout {
221 BufferLayout {
222 stride: 0,
223 step_func: VertexStep::PerVertex,
224 step_rate: 1,
225 }
226 }
227}
228
229#[derive(Clone, Debug)]
230pub struct VertexAttribute {
231 pub name: &'static str,
232 pub format: VertexFormat,
233 pub buffer_index: usize,
234 /// This flag affects integer VertexFormats, Byte*, Short*, Int*
235 /// Taking Byte4 as an example:
236 /// On Metal, it might be received as either `float4` or `uint4`
237 /// On OpenGl and `gl_pass_as_float = true` shaders should receive it as `vec4`
238 /// With `gl_pass_as_float = false`, as `uvec4`
239 ///
240 /// Note that `uvec4` requires at least `150` glsl version
241 /// Before setting `gl_pass_as_float` to false, better check `context.info().has_integer_attributes()` and double check that shaders are at least `150`
242 pub gl_pass_as_float: bool,
243}
244
245impl VertexAttribute {
246 pub const fn new(name: &'static str, format: VertexFormat) -> VertexAttribute {
247 Self::with_buffer(name, format, 0)
248 }
249
250 pub const fn with_buffer(
251 name: &'static str,
252 format: VertexFormat,
253 buffer_index: usize,
254 ) -> VertexAttribute {
255 VertexAttribute {
256 name,
257 format,
258 buffer_index,
259 gl_pass_as_float: true,
260 }
261 }
262}
263
264#[derive(Clone, Debug)]
265pub struct PipelineLayout {
266 pub buffers: &'static [BufferLayout],
267 pub attributes: &'static [VertexAttribute],
268}
269
270#[derive(Clone, Debug, Copy)]
271pub enum ShaderType {
272 Vertex,
273 Fragment,
274}
275
276impl Display for ShaderType {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 Self::Vertex => write!(f, "Vertex"),
280 Self::Fragment => write!(f, "Fragment"),
281 }
282 }
283}
284
285#[derive(Clone, Debug)]
286pub enum ShaderError {
287 CompilationError {
288 shader_type: ShaderType,
289 error_message: String,
290 },
291 LinkError(String),
292 /// Shader strings should never contains \00 in the middle
293 FFINulError(std::ffi::NulError),
294}
295
296impl From<std::ffi::NulError> for ShaderError {
297 fn from(e: std::ffi::NulError) -> ShaderError {
298 ShaderError::FFINulError(e)
299 }
300}
301
302impl Display for ShaderError {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 match self {
305 Self::CompilationError {
306 shader_type,
307 error_message,
308 } => write!(f, "{shader_type} shader error:\n{error_message}"),
309 Self::LinkError(msg) => write!(f, "Link shader error:\n{msg}"),
310 Self::FFINulError(e) => write!(f, "{e}"),
311 }
312 }
313}
314
315impl Error for ShaderError {}
316
317/// List of all the possible formats of input data when uploading to texture.
318/// The list is built by intersection of texture formats supported by 3.3 core profile and webgl1.
319#[repr(u8)]
320#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
321pub enum TextureFormat {
322 RGB8,
323 RGBA8,
324 RGBA16F,
325 Depth,
326 Depth32,
327 Alpha,
328}
329impl TextureFormat {
330 /// Returns the size in bytes of texture with `dimensions`.
331 pub fn size(self, width: u32, height: u32) -> u32 {
332 let square = width * height;
333 match self {
334 TextureFormat::RGB8 => 3 * square,
335 TextureFormat::RGBA8 => 4 * square,
336 TextureFormat::RGBA16F => 8 * square,
337 TextureFormat::Depth => 2 * square,
338 TextureFormat::Depth32 => 4 * square,
339 TextureFormat::Alpha => 1 * square,
340 }
341 }
342}
343
344/// Sets the wrap parameter for texture.
345#[derive(Debug, PartialEq, Eq, Clone, Copy)]
346pub enum TextureWrap {
347 /// Samples at coord x + 1 map to coord x.
348 Repeat,
349 /// Samples at coord x + 1 map to coord 1 - x.
350 Mirror,
351 /// Samples at coord x + 1 map to coord 1.
352 Clamp,
353}
354
355#[derive(Clone, Copy, Debug, PartialEq, Hash)]
356pub enum FilterMode {
357 Linear,
358 Nearest,
359}
360
361#[derive(Clone, Copy, Debug, PartialEq, Hash)]
362pub enum MipmapFilterMode {
363 None,
364 Linear,
365 Nearest,
366}
367
368#[derive(Debug, PartialEq, Eq, Clone, Copy)]
369pub enum TextureAccess {
370 /// Used as read-only from GPU
371 Static,
372 /// Can be written to from GPU
373 RenderTarget,
374}
375
376#[derive(Debug, Copy, Clone, PartialEq)]
377pub enum TextureKind {
378 Texture2D,
379 CubeMap,
380}
381
382#[derive(Debug, Copy, Clone)]
383pub struct TextureParams {
384 pub kind: TextureKind,
385 pub format: TextureFormat,
386 pub wrap: TextureWrap,
387 pub min_filter: FilterMode,
388 pub mag_filter: FilterMode,
389 pub mipmap_filter: MipmapFilterMode,
390 pub width: u32,
391 pub height: u32,
392 // All miniquad API could work without this flag being explicit.
393 // We can decide if mipmaps are required by the data provided
394 // And reallocate non-mipmapped texture(on metal) on generateMipmaps call
395 // But! Reallocating cubemaps is too much struggle, so leave it for later.
396 pub allocate_mipmaps: bool,
397 /// Only used for render textures. `sample_count > 1` allows anti-aliased render textures.
398 ///
399 /// On OpenGL, for a `sample_count > 1` render texture, render buffer object will
400 /// be created instead of a regulat texture.
401 ///
402 pub sample_count: i32,
403}
404
405impl Default for TextureParams {
406 fn default() -> Self {
407 TextureParams {
408 kind: TextureKind::Texture2D,
409 format: TextureFormat::RGBA8,
410 wrap: TextureWrap::Clamp,
411 min_filter: FilterMode::Linear,
412 mag_filter: FilterMode::Linear,
413 mipmap_filter: MipmapFilterMode::None,
414 width: 0,
415 height: 0,
416 allocate_mipmaps: false,
417 sample_count: 1,
418 }
419 }
420}
421
422#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
423pub struct ShaderId(usize);
424
425// Inner hence we can't have private data in enum fields
426#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
427pub(crate) enum TextureIdInner {
428 Managed(usize),
429 Raw(RawId),
430}
431
432#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
433pub struct TextureId(TextureIdInner);
434
435impl TextureId {
436 /// Wrap raw platform texture into a TextureId acceptable for miniquad
437 /// Without allocating any miniquad memory and without letting miniquad
438 /// manage the texture.
439 pub fn from_raw_id(raw_id: RawId) -> TextureId {
440 TextureId(TextureIdInner::Raw(raw_id))
441 }
442}
443
444/// Pixel arithmetic description for blending operations.
445/// Will be used in an equation:
446/// `equation(sfactor * source_color, dfactor * destination_color)`
447/// Where source_color is the new pixel color and destination color is color from the destination buffer.
448///
449/// Example:
450///```
451///# use miniquad::{BlendState, BlendFactor, BlendValue, Equation};
452///BlendState::new(
453/// Equation::Add,
454/// BlendFactor::Value(BlendValue::SourceAlpha),
455/// BlendFactor::OneMinusValue(BlendValue::SourceAlpha)
456///);
457///```
458/// This will be `source_color * source_color.a + destination_color * (1 - source_color.a)`
459/// Wich is quite common set up for alpha blending.
460#[derive(Debug, Copy, Clone, PartialEq)]
461pub struct BlendState {
462 equation: Equation,
463 sfactor: BlendFactor,
464 dfactor: BlendFactor,
465}
466
467impl BlendState {
468 pub fn new(equation: Equation, sfactor: BlendFactor, dfactor: BlendFactor) -> BlendState {
469 BlendState {
470 equation,
471 sfactor,
472 dfactor,
473 }
474 }
475}
476
477#[derive(Debug, Copy, Clone, PartialEq)]
478pub struct StencilState {
479 pub front: StencilFaceState,
480 pub back: StencilFaceState,
481}
482
483#[derive(Debug, Copy, Clone, PartialEq)]
484pub struct StencilFaceState {
485 /// Operation to use when stencil test fails
486 pub fail_op: StencilOp,
487
488 /// Operation to use when stencil test passes, but depth test fails
489 pub depth_fail_op: StencilOp,
490
491 /// Operation to use when both stencil and depth test pass,
492 /// or when stencil pass and no depth or depth disabled
493 pub pass_op: StencilOp,
494
495 /// Used for stencil testing with test_ref and test_mask: if (test_ref & test_mask) *test_func* (*stencil* && test_mask)
496 /// Default is Always, which means "always pass"
497 pub test_func: CompareFunc,
498
499 /// Default value: 0
500 pub test_ref: i32,
501
502 /// Default value: all 1s
503 pub test_mask: u32,
504
505 /// Specifies a bit mask to enable or disable writing of individual bits in the stencil planes
506 /// Default value: all 1s
507 pub write_mask: u32,
508}
509
510/// Operations performed on current stencil value when comparison test passes or fails.
511#[derive(Debug, PartialEq, Eq, Clone, Copy)]
512pub enum StencilOp {
513 /// Default value
514 Keep,
515 Zero,
516 Replace,
517 IncrementClamp,
518 DecrementClamp,
519 Invert,
520 IncrementWrap,
521 DecrementWrap,
522}
523
524/// Depth and stencil compare function
525#[derive(Debug, Copy, Clone, PartialEq)]
526pub enum CompareFunc {
527 /// Default value
528 Always,
529 Never,
530 Less,
531 Equal,
532 LessOrEqual,
533 Greater,
534 NotEqual,
535 GreaterOrEqual,
536}
537
538type ColorMask = (bool, bool, bool, bool);
539
540pub enum PassAction {
541 Nothing,
542 Clear {
543 color: Option<(f32, f32, f32, f32)>,
544 depth: Option<f32>,
545 stencil: Option<i32>,
546 },
547}
548
549impl PassAction {
550 pub fn clear_color(r: f32, g: f32, b: f32, a: f32) -> PassAction {
551 PassAction::Clear {
552 color: Some((r, g, b, a)),
553 depth: Some(1.),
554 stencil: None,
555 }
556 }
557}
558
559impl Default for PassAction {
560 fn default() -> PassAction {
561 PassAction::Clear {
562 color: Some((0.0, 0.0, 0.0, 0.0)),
563 depth: Some(1.),
564 stencil: None,
565 }
566 }
567}
568
569#[derive(Clone, Copy, Debug, PartialEq)]
570pub struct RenderPass(usize);
571
572pub const MAX_VERTEX_ATTRIBUTES: usize = 16;
573pub const MAX_SHADERSTAGE_IMAGES: usize = 12;
574
575#[derive(Clone, Debug)]
576pub struct Features {
577 pub instancing: bool,
578 /// Does current rendering backend support automatic resolve of
579 /// multisampled render passes on end_render_pass.
580 /// Would be false on WebGl1 and GL2.
581 ///
582 /// With resolve_attachments: false, not-none resolve_img in new_render_pass will
583 /// result in a runtime panic.
584 pub resolve_attachments: bool,
585}
586
587impl Default for Features {
588 fn default() -> Features {
589 Features {
590 instancing: true,
591 resolve_attachments: true,
592 }
593 }
594}
595
596/// Specify whether front- or back-facing polygons can be culled.
597#[derive(Debug, PartialEq, Eq, Clone, Copy)]
598pub enum CullFace {
599 Nothing,
600 Front,
601 Back,
602}
603
604/// Define front- and back-facing polygons.
605#[derive(Debug, PartialEq, Eq, Clone, Copy)]
606pub enum FrontFaceOrder {
607 Clockwise,
608 CounterClockwise,
609}
610
611/// A pixel-wise comparison function.
612#[derive(Debug, PartialEq, Eq, Clone, Copy)]
613pub enum Comparison {
614 Never,
615 Less,
616 LessOrEqual,
617 Greater,
618 GreaterOrEqual,
619 Equal,
620 NotEqual,
621 Always,
622}
623
624impl From<Comparison> for GLenum {
625 fn from(cmp: Comparison) -> Self {
626 match cmp {
627 Comparison::Never => GL_NEVER,
628 Comparison::Less => GL_LESS,
629 Comparison::LessOrEqual => GL_LEQUAL,
630 Comparison::Greater => GL_GREATER,
631 Comparison::GreaterOrEqual => GL_GEQUAL,
632 Comparison::Equal => GL_EQUAL,
633 Comparison::NotEqual => GL_NOTEQUAL,
634 Comparison::Always => GL_ALWAYS,
635 }
636 }
637}
638
639/// Specifies how incoming RGBA values (source) and the RGBA in framebuffer (destination)
640/// are combined.
641#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
642pub enum Equation {
643 /// Adds source and destination. Source and destination are multiplied
644 /// by blending parameters before addition.
645 #[default]
646 Add,
647 /// Subtracts destination from source. Source and destination are
648 /// multiplied by blending parameters before subtraction.
649 Subtract,
650 /// Subtracts source from destination. Source and destination are
651 /// multiplied by blending parameters before subtraction.
652 ReverseSubtract,
653}
654
655/// Blend values.
656#[derive(Debug, PartialEq, Eq, Clone, Copy)]
657pub enum BlendValue {
658 SourceColor,
659 SourceAlpha,
660 DestinationColor,
661 DestinationAlpha,
662}
663
664/// Blend factors.
665#[derive(Debug, PartialEq, Eq, Clone, Copy)]
666pub enum BlendFactor {
667 Zero,
668 One,
669 Value(BlendValue),
670 OneMinusValue(BlendValue),
671 SourceAlphaSaturate,
672}
673
674#[derive(Debug, PartialEq, Clone, Copy)]
675pub enum PrimitiveType {
676 Triangles,
677 Lines,
678 Points,
679}
680
681impl From<PrimitiveType> for GLenum {
682 fn from(primitive_type: PrimitiveType) -> Self {
683 match primitive_type {
684 PrimitiveType::Triangles => GL_TRIANGLES,
685 PrimitiveType::Lines => GL_LINES,
686 PrimitiveType::Points => GL_POINTS,
687 }
688 }
689}
690
691#[derive(Debug, PartialEq, Clone, Copy)]
692pub struct PipelineParams {
693 pub cull_face: CullFace,
694 pub front_face_order: FrontFaceOrder,
695 pub depth_test: Comparison,
696 pub depth_write: bool,
697 pub depth_write_offset: Option<(f32, f32)>,
698 /// Color (RGB) blend function. If None - blending will be disabled for this pipeline.
699 /// Usual use case to get alpha-blending:
700 ///```
701 ///# use miniquad::{PipelineParams, BlendState, BlendValue, BlendFactor, Equation};
702 ///PipelineParams {
703 /// color_blend: Some(BlendState::new(
704 /// Equation::Add,
705 /// BlendFactor::Value(BlendValue::SourceAlpha),
706 /// BlendFactor::OneMinusValue(BlendValue::SourceAlpha))
707 /// ),
708 /// ..Default::default()
709 ///};
710 ///```
711 pub color_blend: Option<BlendState>,
712 /// Alpha blend function. If None - alpha will be blended with same equation than RGB colors.
713 /// One of possible separate alpha channel blend settings is to avoid blending with WebGl background.
714 /// On webgl canvas's resulting alpha channel will be used to blend the whole canvas background.
715 /// To avoid modifying only alpha channel, but keep usual transparency:
716 ///```
717 ///# use miniquad::{PipelineParams, BlendState, BlendValue, BlendFactor, Equation};
718 ///PipelineParams {
719 /// color_blend: Some(BlendState::new(
720 /// Equation::Add,
721 /// BlendFactor::Value(BlendValue::SourceAlpha),
722 /// BlendFactor::OneMinusValue(BlendValue::SourceAlpha))
723 /// ),
724 /// alpha_blend: Some(BlendState::new(
725 /// Equation::Add,
726 /// BlendFactor::Zero,
727 /// BlendFactor::One)
728 /// ),
729 /// ..Default::default()
730 ///};
731 ///```
732 /// The same results may be achieved with ColorMask(true, true, true, false)
733 pub alpha_blend: Option<BlendState>,
734 pub stencil_test: Option<StencilState>,
735 pub color_write: ColorMask,
736 pub primitive_type: PrimitiveType,
737}
738
739// TODO(next major version bump): should be PipelineId
740#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
741pub struct Pipeline(usize);
742
743impl Default for PipelineParams {
744 fn default() -> PipelineParams {
745 PipelineParams {
746 cull_face: CullFace::Nothing,
747 front_face_order: FrontFaceOrder::CounterClockwise,
748 depth_test: Comparison::Always, // no depth test,
749 depth_write: false, // no depth write,
750 depth_write_offset: None,
751 color_blend: None,
752 alpha_blend: None,
753 stencil_test: None,
754 color_write: (true, true, true, true),
755 primitive_type: PrimitiveType::Triangles,
756 }
757 }
758}
759
760/// Geometry bindings
761#[derive(Clone, Debug)]
762pub struct Bindings {
763 /// Vertex buffers. Data contained in the buffer must match layout
764 /// specified in the `Pipeline`.
765 ///
766 /// Most commonly vertex buffer will contain `(x,y,z,w)` coordinates of the
767 /// vertex in 3d space, as well as `(u,v)` coordinates that map the vertex
768 /// to some position in the corresponding `Texture`.
769 pub vertex_buffers: Vec<BufferId>,
770 /// Index buffer which instructs the GPU in which order to draw vertices
771 /// from a vertex buffer, with each subsequent 3 indices forming a
772 /// triangle.
773 pub index_buffer: BufferId,
774 /// Textures to be used with when drawing the geometry in the fragment
775 /// shader.
776 pub images: Vec<TextureId>,
777}
778
779#[derive(Clone, Copy, Debug, PartialEq)]
780pub enum BufferType {
781 VertexBuffer,
782 IndexBuffer,
783}
784
785#[derive(Clone, Copy, Debug, PartialEq)]
786pub enum BufferUsage {
787 Immutable,
788 Dynamic,
789 Stream,
790}
791
792fn gl_buffer_target(buffer_type: &BufferType) -> GLenum {
793 match buffer_type {
794 BufferType::VertexBuffer => GL_ARRAY_BUFFER,
795 BufferType::IndexBuffer => GL_ELEMENT_ARRAY_BUFFER,
796 }
797}
798
799fn gl_usage(usage: &BufferUsage) -> GLenum {
800 match usage {
801 BufferUsage::Immutable => GL_STATIC_DRAW,
802 BufferUsage::Dynamic => GL_DYNAMIC_DRAW,
803 BufferUsage::Stream => GL_STREAM_DRAW,
804 }
805}
806
807#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
808pub struct BufferId(usize);
809
810/// `ElapsedQuery` is used to measure duration of GPU operations.
811///
812/// Usual timing/profiling methods are difficult apply to GPU workloads as draw calls are submitted
813/// asynchronously effectively hiding execution time of individual operations from the user.
814/// `ElapsedQuery` allows to measure duration of individual rendering operations, as though the time
815/// was measured on GPU rather than CPU side.
816///
817/// The query is created using [`ElapsedQuery::new()`] function.
818/// ```
819/// use miniquad::graphics::ElapsedQuery;
820/// // initialization
821/// let mut query = ElapsedQuery::new();
822/// ```
823/// Measurement is performed by calling [`ElapsedQuery::begin_query()`] and
824/// [`ElapsedQuery::end_query()`]
825///
826/// ```
827/// # use miniquad::graphics::ElapsedQuery;
828/// # let mut query = ElapsedQuery::new();
829///
830/// query.begin_query();
831/// // one or multiple calls to miniquad::GraphicsContext::draw()
832/// query.end_query();
833/// ```
834///
835/// Retreival of measured duration is only possible at a later point in time. Often a frame or
836/// couple frames later. Measurement latency can especially be high on WASM/WebGL target.
837///
838/// ```
839/// // couple frames later:
840/// # use miniquad::graphics::ElapsedQuery;
841/// # let mut query = ElapsedQuery::new();
842/// # query.begin_query();
843/// # query.end_query();
844/// if query.is_available() {
845/// let duration_nanoseconds = query.get_result();
846/// // use/display duration_nanoseconds
847/// }
848/// ```
849///
850/// And during finalization:
851/// ```
852/// // clean-up
853/// # use miniquad::graphics::ElapsedQuery;
854/// # let mut query = ElapsedQuery::new();
855/// # query.begin_query();
856/// # query.end_query();
857/// # if query.is_available() {
858/// # let duration_nanoseconds = query.get_result();
859/// # // use/display duration_nanoseconds
860/// # }
861/// query.delete();
862/// ```
863///
864/// It is only possible to measure single query at once.
865///
866/// On OpenGL/WebGL platforms implementation relies on [`EXT_disjoint_timer_query`] extension.
867///
868/// [`EXT_disjoint_timer_query`]: https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_disjoint_timer_query.txt
869///
870#[derive(Clone, Copy)]
871pub struct ElapsedQuery {
872 gl_query: GLuint,
873}
874
875impl Default for ElapsedQuery {
876 fn default() -> Self {
877 Self::new()
878 }
879}
880
881impl ElapsedQuery {
882 pub fn new() -> ElapsedQuery {
883 ElapsedQuery { gl_query: 0 }
884 }
885
886 /// Submit a beginning of elapsed-time query.
887 ///
888 /// Only a single query can be measured at any moment in time.
889 ///
890 /// Use [`ElapsedQuery::end_query()`] to finish the query and
891 /// [`ElapsedQuery::get_result()`] to read the result when rendering is complete.
892 ///
893 /// The query can be used again after retriving the result.
894 ///
895 /// Implemented as `glBeginQuery(GL_TIME_ELAPSED, ...)` on OpenGL/WebGL platforms.
896 ///
897 /// Use [`ElapsedQuery::is_supported()`] to check if functionality is available and the method can be called.
898 pub fn begin_query(&mut self) {
899 if self.gl_query == 0 {
900 unsafe { glGenQueries(1, &mut self.gl_query) };
901 }
902 unsafe { glBeginQuery(GL_TIME_ELAPSED, self.gl_query) };
903 }
904
905 /// Submit an end of elapsed-time query that can be read later when rendering is complete.
906 ///
907 /// This function is usd in conjunction with [`ElapsedQuery::begin_query()`] and
908 /// [`ElapsedQuery::get_result()`].
909 ///
910 /// Implemented as `glEndQuery(GL_TIME_ELAPSED)` on OpenGL/WebGL platforms.
911 pub fn end_query(&mut self) {
912 unsafe { glEndQuery(GL_TIME_ELAPSED) };
913 }
914
915 /// Retreieve measured duration in nanonseconds.
916 ///
917 /// Note that the result may be ready only couple frames later due to asynchronous nature of GPU
918 /// command submission. Use [`ElapsedQuery::is_available()`] to check if the result is
919 /// available for retrieval.
920 ///
921 /// Use [`ElapsedQuery::is_supported()`] to check if functionality is available and the method can be called.
922 pub fn get_result(&self) -> u64 {
923 // let mut time: GLuint64 = 0;
924 // assert!(self.gl_query != 0);
925 // unsafe { glGetQueryObjectui64v(self.gl_query, GL_QUERY_RESULT, &mut time) };
926 // time
927 0
928 }
929
930 /// Reports whenever elapsed timer is supported and other methods can be invoked.
931 pub fn is_supported() -> bool {
932 unimplemented!();
933 //unsafe { sapp_is_elapsed_timer_supported() }
934 }
935
936 /// Reports whenever result of submitted query is available for retrieval with
937 /// [`ElapsedQuery::get_result()`].
938 ///
939 /// Note that the result may be ready only couple frames later due to asynchrnous nature of GPU
940 /// command submission.
941 ///
942 /// Use [`ElapsedQuery::is_supported()`] to check if functionality is available and the method can be called.
943 pub fn is_available(&self) -> bool {
944 // let mut available: GLint = 0;
945
946 // // begin_query was not called yet
947 // if self.gl_query == 0 {
948 // return false;
949 // }
950
951 //unsafe { glGetQueryObjectiv(self.gl_query, GL_QUERY_RESULT_AVAILABLE, &mut available) };
952 //available != 0
953
954 false
955 }
956
957 /// Delete query.
958 ///
959 /// Note that the query is not deleted automatically when dropped.
960 ///
961 /// Implemented as `glDeleteQueries(...)` on OpenGL/WebGL platforms.
962 pub fn delete(&mut self) {
963 unsafe { glDeleteQueries(1, &self.gl_query) }
964 self.gl_query = 0;
965 }
966}
967
968/// A vtable-erased generic argument.
969/// Basically, the same thing as `fn f<U>(a: &U)`, but
970/// trait-object friendly.
971pub struct Arg<'a> {
972 ptr: *const std::ffi::c_void,
973 element_size: usize,
974 size: usize,
975 is_slice: bool,
976 _phantom: std::marker::PhantomData<&'a ()>,
977}
978
979pub enum TextureSource<'a> {
980 Empty,
981 Bytes(&'a [u8]),
982 /// Array of `[cubemap_face][mipmap_level][bytes]`
983 Array(&'a [&'a [&'a [u8]]]),
984}
985
986pub enum BufferSource<'a> {
987 Slice(Arg<'a>),
988 Empty { size: usize, element_size: usize },
989}
990impl<'a> BufferSource<'a> {
991 /// Empty buffer of `size * size_of::<T>` bytes
992 ///
993 /// Platform specific note, OpenGL:
994 /// For VertexBuffer T could be anything, it is only used to calculate total size,
995 /// but for IndexBuffers T should be either u8, u16 or u32, other
996 /// types are not supported.
997 ///
998 /// For vertex buffers it is OK to use `empty::<u8>(byte_size);`
999 pub fn empty<T>(size: usize) -> BufferSource<'a> {
1000 let element_size = std::mem::size_of::<T>();
1001 BufferSource::Empty {
1002 size: size * std::mem::size_of::<T>(),
1003 element_size,
1004 }
1005 }
1006
1007 pub fn slice<T>(data: &'a [T]) -> BufferSource<'a> {
1008 BufferSource::Slice(Arg {
1009 ptr: data.as_ptr() as _,
1010 size: std::mem::size_of_val(data),
1011 element_size: std::mem::size_of::<T>(),
1012 is_slice: true,
1013 _phantom: std::marker::PhantomData,
1014 })
1015 }
1016
1017 pub unsafe fn pointer(ptr: *const u8, size: usize, element_size: usize) -> BufferSource<'a> {
1018 BufferSource::Slice(Arg {
1019 ptr: ptr as _,
1020 size,
1021 element_size,
1022 is_slice: true,
1023 _phantom: std::marker::PhantomData,
1024 })
1025 }
1026}
1027
1028pub struct UniformsSource<'a>(Arg<'a>);
1029impl<'a> UniformsSource<'a> {
1030 pub fn table<T>(data: &'a T) -> UniformsSource<'a> {
1031 Self(Arg {
1032 ptr: data as *const T as _,
1033 size: std::mem::size_of_val(data),
1034 element_size: std::mem::size_of::<T>(),
1035 is_slice: false,
1036 _phantom: std::marker::PhantomData,
1037 })
1038 }
1039}
1040
1041#[derive(Debug)]
1042pub enum ShaderSource<'a> {
1043 Glsl { vertex: &'a str, fragment: &'a str },
1044 Msl { program: &'a str },
1045}
1046
1047#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
1048pub enum RawId {
1049 OpenGl(crate::native::gl::GLuint),
1050 #[cfg(target_vendor = "apple")]
1051 Metal(*mut objc::runtime::Object),
1052}
1053unsafe impl Send for RawId {}
1054unsafe impl Sync for RawId {}
1055
1056#[derive(Clone, Debug, Default)]
1057pub struct GlslSupport {
1058 pub v130: bool,
1059 pub v150: bool,
1060 pub v330: bool,
1061 pub v300es: bool,
1062 pub v100_ext: bool,
1063 pub v100: bool,
1064}
1065
1066#[derive(PartialEq, Clone, Copy, Debug)]
1067pub enum Backend {
1068 Metal,
1069 OpenGl,
1070}
1071
1072#[derive(Clone, Debug)]
1073pub struct ContextInfo {
1074 pub backend: Backend,
1075 /// GL_VERSION_STRING from OpenGL. Would be empty on metal.
1076 pub gl_version_string: String,
1077 /// OpenGL provides an enumeration over GL_SHADING_LANGUAGE_VERSION,
1078 /// allowing to see which glsl versions are actually supported.
1079 /// Unfortunately, it only works on GL4.3+... and even there it is not quite correct.
1080 ///
1081 /// miniquad will take a guess based on GL_VERSION_STRING, current platform and implementation
1082 /// details. Would be all false on metal.
1083 pub glsl_support: GlslSupport,
1084 /// List of platform-dependent features that miniquad failed to make cross-platforms
1085 /// and therefore they might be missing.
1086 pub features: Features,
1087}
1088
1089impl ContextInfo {
1090 pub fn has_integer_attributes(&self) -> bool {
1091 match self.backend {
1092 Backend::Metal => true,
1093 Backend::OpenGl => {
1094 self.glsl_support.v150 | self.glsl_support.v300es | self.glsl_support.v330
1095 }
1096 }
1097 }
1098}
1099
1100pub trait RenderingBackend {
1101 fn info(&self) -> ContextInfo;
1102 /// For metal context's ShaderSource should contain MSL source string, for GL - glsl.
1103 ///
1104 /// If in doubt, _most_ OpenGL contexts support "#version 100" glsl shaders.
1105 /// So far miniquad never encountered where it can create a rendering context,
1106 /// but `version 100` shaders are not supported.
1107 ///
1108 /// Typical `new_shader` invocation for an MSL and `glsl version 100` sources:
1109 /// ```ignore
1110 /// let source = match ctx.info().backend {
1111 /// Backend::OpenGl => ShaderSource::Glsl {
1112 /// vertex: display_shader::VERTEX,
1113 /// fragment: display_shader::FRAGMENT,
1114 /// },
1115 /// Backend::Metal => ShaderSource::Msl {
1116 /// program: display_shader::METAL
1117 /// },
1118 /// };
1119 /// let shader = ctx.new_shader(source, display_shader::meta()).unwrap();
1120 /// ```
1121 /// Or just
1122 /// ```ignore
1123 /// let shader = ctx.new_shader(ShaderSource::Glsl {...}, ...);
1124 /// ```
1125 /// for GL-only.
1126 fn new_shader(
1127 &mut self,
1128 shader: ShaderSource,
1129 meta: ShaderMeta,
1130 ) -> Result<ShaderId, ShaderError>;
1131 fn new_texture(
1132 &mut self,
1133 access: TextureAccess,
1134 data: TextureSource,
1135 params: TextureParams,
1136 ) -> TextureId;
1137 fn new_render_texture(&mut self, params: TextureParams) -> TextureId {
1138 self.new_texture(TextureAccess::RenderTarget, TextureSource::Empty, params)
1139 }
1140 fn new_texture_from_data_and_format(
1141 &mut self,
1142 bytes: &[u8],
1143 params: TextureParams,
1144 ) -> TextureId {
1145 self.new_texture(TextureAccess::Static, TextureSource::Bytes(bytes), params)
1146 }
1147 fn new_texture_from_rgba8(&mut self, width: u16, height: u16, bytes: &[u8]) -> TextureId {
1148 assert_eq!(width as usize * height as usize * 4, bytes.len());
1149
1150 self.new_texture_from_data_and_format(
1151 bytes,
1152 TextureParams {
1153 kind: TextureKind::Texture2D,
1154 width: width as _,
1155 height: height as _,
1156 format: TextureFormat::RGBA8,
1157 wrap: TextureWrap::Clamp,
1158 min_filter: FilterMode::Linear,
1159 mag_filter: FilterMode::Linear,
1160 mipmap_filter: MipmapFilterMode::None,
1161 allocate_mipmaps: false,
1162 sample_count: 1,
1163 },
1164 )
1165 }
1166 fn texture_params(&self, texture: TextureId) -> TextureParams;
1167 fn texture_size(&self, texture: TextureId) -> (u32, u32) {
1168 let params = self.texture_params(texture);
1169 (params.width, params.height)
1170 }
1171
1172 /// Get OpenGL's GLuint texture ID or metals ObjcId
1173 unsafe fn texture_raw_id(&self, texture: TextureId) -> RawId;
1174
1175 /// Update whole texture content
1176 /// bytes should be width * height * 4 size - non rgba8 textures are not supported yet anyway
1177 fn texture_update(&mut self, texture: TextureId, bytes: &[u8]) {
1178 let (width, height) = self.texture_size(texture);
1179 self.texture_update_part(texture, 0 as _, 0 as _, width as _, height as _, bytes)
1180 }
1181 fn texture_set_filter(
1182 &mut self,
1183 texture: TextureId,
1184 filter: FilterMode,
1185 mipmap_filter: MipmapFilterMode,
1186 ) {
1187 self.texture_set_min_filter(texture, filter, mipmap_filter);
1188 self.texture_set_mag_filter(texture, filter);
1189 }
1190 fn texture_set_min_filter(
1191 &mut self,
1192 texture: TextureId,
1193 filter: FilterMode,
1194 mipmap_filter: MipmapFilterMode,
1195 );
1196 fn texture_set_mag_filter(&mut self, texture: TextureId, filter: FilterMode);
1197 fn texture_set_wrap(&mut self, texture: TextureId, wrap_x: TextureWrap, wrap_y: TextureWrap);
1198 /// Metal-specific note: if texture was created without `params.generate_mipmaps`
1199 /// `generate_mipmaps` will do nothing.
1200 ///
1201 /// Also note that if MipmapFilter is set to None, mipmaps will not be visible, even if
1202 /// generated.
1203 fn texture_generate_mipmaps(&mut self, texture: TextureId);
1204 fn texture_resize(&mut self, texture: TextureId, width: u32, height: u32, bytes: Option<&[u8]>);
1205 fn texture_read_pixels(&mut self, texture: TextureId, bytes: &mut [u8]);
1206 fn texture_update_part(
1207 &mut self,
1208 texture: TextureId,
1209 x_offset: i32,
1210 y_offset: i32,
1211 width: i32,
1212 height: i32,
1213 bytes: &[u8],
1214 );
1215 fn new_render_pass(
1216 &mut self,
1217 color_img: TextureId,
1218 depth_img: Option<TextureId>,
1219 ) -> RenderPass {
1220 self.new_render_pass_mrt(&[color_img], None, depth_img)
1221 }
1222 /// Same as "new_render_pass", but allows multiple color attachments.
1223 /// if `resolve_img` is set, MSAA-resolve operation will happen in `end_render_pass`
1224 /// this operation require `color_img` to have sample_count > 1,resolve_img have
1225 /// sample_count == 1, and color_img.len() should be equal to resolve_img.len()
1226 ///
1227 /// Note that resolve attachments may be not supported by current backend!
1228 /// They are only available when `ctx.info().features.resolve_attachments` is true.
1229 fn new_render_pass_mrt(
1230 &mut self,
1231 color_img: &[TextureId],
1232 resolve_img: Option<&[TextureId]>,
1233 depth_img: Option<TextureId>,
1234 ) -> RenderPass;
1235 /// panics for depth-only or multiple color attachment render pass
1236 /// This function is, mostly, legacy. Using "render_pass_color_attachments"
1237 /// is recommended instead.
1238 fn render_pass_texture(&self, render_pass: RenderPass) -> TextureId {
1239 let textures = self.render_pass_color_attachments(render_pass);
1240 #[allow(clippy::len_zero)]
1241 if textures.len() == 0 {
1242 panic!("depth-only render pass");
1243 }
1244 if textures.len() != 1 {
1245 panic!("multiple render target render pass");
1246 }
1247 textures[0]
1248 }
1249 /// For depth-only render pass returns empty slice.
1250 fn render_pass_color_attachments(&self, render_pass: RenderPass) -> &[TextureId];
1251 fn delete_render_pass(&mut self, render_pass: RenderPass);
1252 fn new_pipeline(
1253 &mut self,
1254 buffer_layout: &[BufferLayout],
1255 attributes: &[VertexAttribute],
1256 shader: ShaderId,
1257 params: PipelineParams,
1258 ) -> Pipeline;
1259 fn apply_pipeline(&mut self, pipeline: &Pipeline);
1260 fn delete_pipeline(&mut self, pipeline: Pipeline);
1261
1262 /// Create a buffer resource object.
1263 /// ```ignore
1264 /// #[repr(C)]
1265 /// struct Vertex {
1266 /// pos: Vec2,
1267 /// uv: Vec2,
1268 /// }
1269 /// let vertices: [Vertex; 4] = [
1270 /// Vertex { pos : Vec2 { x: -0.5, y: -0.5 }, uv: Vec2 { x: 0., y: 0. } },
1271 /// Vertex { pos : Vec2 { x: 0.5, y: -0.5 }, uv: Vec2 { x: 1., y: 0. } },
1272 /// Vertex { pos : Vec2 { x: 0.5, y: 0.5 }, uv: Vec2 { x: 1., y: 1. } },
1273 /// Vertex { pos : Vec2 { x: -0.5, y: 0.5 }, uv: Vec2 { x: 0., y: 1. } },
1274 /// ];
1275 /// let buffer = ctx.new_buffer(
1276 /// BufferType::VertexBuffer,
1277 /// BufferUsage::Immutable,
1278 /// BufferSource::slice(&vertices),
1279 /// );
1280 /// ```
1281 fn new_buffer(&mut self, type_: BufferType, usage: BufferUsage, data: BufferSource)
1282 -> BufferId;
1283 fn buffer_update(&mut self, buffer: BufferId, data: BufferSource);
1284
1285 /// Size of buffer in bytes.
1286 /// For 1 element, u16 buffer this will return 2.
1287 fn buffer_size(&mut self, buffer: BufferId) -> usize;
1288
1289 /// Delete GPU buffer, leaving handle unmodified.
1290 ///
1291 /// More high-level code on top of miniquad probably is going to call this in Drop
1292 /// implementation of some more RAII buffer object.
1293 ///
1294 /// There is no protection against using deleted buffers later. However its not an UB in OpenGl
1295 /// and thats why this function is not marked as unsafe
1296 fn delete_buffer(&mut self, buffer: BufferId);
1297
1298 /// Delete GPU texture, leaving handle unmodified.
1299 ///
1300 /// More high-level code on top of miniquad probably is going to call this in Drop
1301 /// implementation of some more RAII buffer object.
1302 ///
1303 /// There is no protection against using deleted textures later. However its not a CPU-level UB
1304 /// and thats why this function is not marked as unsafe
1305 fn delete_texture(&mut self, texture: TextureId);
1306
1307 /// Delete GPU program, leaving handle unmodified.
1308 ///
1309 /// More high-level code on top of miniquad probably is going to call this in Drop
1310 /// implementation of some more RAII buffer object.
1311 ///
1312 /// There is no protection against using deleted programs later. However its not a CPU-level
1313 /// Porgram and thats why this function is not marked as unsafe
1314 fn delete_shader(&mut self, program: ShaderId);
1315
1316 /// Set a new viewport rectangle.
1317 /// Should be applied after begin_pass.
1318 fn apply_viewport(&mut self, x: i32, y: i32, w: i32, h: i32);
1319
1320 /// Set a new scissor rectangle.
1321 /// Should be applied after begin_pass.
1322 fn apply_scissor_rect(&mut self, x: i32, y: i32, w: i32, h: i32);
1323
1324 fn apply_bindings_from_slice(
1325 &mut self,
1326 vertex_buffers: &[BufferId],
1327 index_buffer: BufferId,
1328 textures: &[TextureId],
1329 );
1330
1331 fn apply_bindings(&mut self, bindings: &Bindings) {
1332 self.apply_bindings_from_slice(
1333 &bindings.vertex_buffers,
1334 bindings.index_buffer,
1335 &bindings.images,
1336 );
1337 }
1338
1339 fn apply_uniforms(&mut self, uniforms: UniformsSource) {
1340 self.apply_uniforms_from_bytes(uniforms.0.ptr as _, uniforms.0.size)
1341 }
1342 fn apply_uniforms_from_bytes(&mut self, uniform_ptr: *const u8, size: usize);
1343
1344 fn clear(
1345 &mut self,
1346 color: Option<(f32, f32, f32, f32)>,
1347 depth: Option<f32>,
1348 stencil: Option<i32>,
1349 );
1350 /// start rendering to the default frame buffer
1351 fn begin_default_pass(&mut self, action: PassAction);
1352 /// start rendering to an offscreen framebuffer
1353 fn begin_pass(&mut self, pass: Option<RenderPass>, action: PassAction);
1354
1355 fn end_render_pass(&mut self);
1356
1357 fn commit_frame(&mut self);
1358
1359 /// Draw elements using currently applied bindings and pipeline.
1360 ///
1361 /// + `base_element` specifies starting offset in `index_buffer`.
1362 /// + `num_elements` specifies length of the slice of `index_buffer` to draw.
1363 /// + `num_instances` specifies how many instances should be rendered.
1364 ///
1365 /// NOTE: num_instances > 1 might be not supported by the GPU (gl2.1 and gles2).
1366 /// `features.instancing` check is required.
1367 fn draw(&self, base_element: i32, num_elements: i32, num_instances: i32);
1368}