1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Provides a framework for specifying units of work that are to be executed by the GPU driver.
//!
//! WebGlitz's execution model centers around the concepts of "tasks" and "commands". A "task" is a
//! unit of work that is to be executed on a graphical processing unit (GPU). A "command" is an
//! atomic task; a task may composed of multiple commands. There is not a specific type that
//! represents a command in this library, both tasks and commands are represented by the [GpuTask]
//! trait. The term "command" is only used by convention for the atomic task building blocks
//! provided by WebGlitz that you may combine into more complex tasks.
//!
//! The following example shows how we may create a task consists of a single command that uploads
//! image data to the base level of a [Texture2D]:
//!
//! ```rust
//! # use web_glitz::runtime::RenderingContext;
//! # fn wrapper<Rc>(context: &Rc) where Rc: RenderingContext + Clone + 'static {
//! use web_glitz::image::{Image2DSource, MipmapLevels};
//! use web_glitz::image::format::RGB8;
//! use web_glitz::image::texture_2d::Texture2DDescriptor;
//!
//! // First we create the texture that we are going to upload to:
//! let texture = context.try_create_texture_2d(&Texture2DDescriptor {
//!     format: RGB8,
//!     width: 256,
//!     height: 256,
//!     levels: MipmapLevels::Complete
//! }).unwrap();
//!
//! // Next, we define some data we wish to upload
//! let pixels: Vec<[u8; 3]> = vec![[255, 0, 0]; 256 * 256];
//! let data = Image2DSource::from_pixels(pixels, 256, 256).unwrap();
//!
//! // Finally, we define a task that consist of a single upload command that will upload our data:
//! let task = texture.base_level().upload_command(data);
//! # }
//! ```
//!
//! Here `context` is a [RenderingContext]. For more information on textures, please refer to the
//! documentation for the [image] module.
//!
//! # Task combinators
//!
//! Combining tasks and commands into more complex tasks is done by "sequencing" or "joining". Both
//! sequencing and joining involve combining 2 or more sub-tasks into 1 new combined task, but they
//! have different guarantees about the order in which these sub-tasks are executed.
//!
//! A sequence may be created with functions such as [sequence], [sequence3], [sequence4],
//! [sequence5] or [sequence_iter], or the with [sequence_all] macro, which sequences any number of
//! sub-tasks. In all cases, the sub-tasks are considered to have an order that corresponds to the
//! order in which they were passed to these functions/the macro. A sub-task in a sequence only
//! begins executing after the previous sub-task in this ordering has finished executing completely.
//! The sequence is considered to be finished when the last sub-task has finished executing. The
//! following example expands on the previous example by adding a command that generates mipmap data
//! for the texture:
//!
//! ```rust
//! # use web_glitz::runtime::RenderingContext;
//! # fn wrapper<Rc>(context: &Rc) where Rc: RenderingContext + Clone + 'static {
//! use web_glitz::image::{Image2DSource, MipmapLevels};
//! use web_glitz::image::format::RGB8;
//! use web_glitz::image::texture_2d::Texture2DDescriptor;
//! use web_glitz::task::sequence;
//!
//! let texture = context.try_create_texture_2d(&Texture2DDescriptor {
//!     format: RGB8,
//!     width: 256,
//!     height: 256,
//!     levels: MipmapLevels::Complete
//! }).unwrap();
//!
//! let pixels: Vec<[u8; 3]> = vec![[255, 0, 0]; 256 * 256];
//! let data = Image2DSource::from_pixels(pixels, 256, 256).unwrap();
//!
//! // This time our tasks consists of a sequence of two commands:
//! let task = sequence(
//!     texture.base_level().upload_command(data),
//!     texture.generate_mipmap_command()
//! );
//! # }
//! ```
//!
//! This example will first upload our data to the texture's base level. Then, after the data has
//! finished uploading, the data for the remaining texture levels is generated with a "generate
//! mipmap" command. For details on mipmapping and mipmap levels, see the documentation for the
//! [image] module.
//!
//! A join may be created with functions such as [join], [join3], [join4], [join5] or [join_iter],
//! or the with [join_all] macro, which joins any number of sub-tasks. Joining is similar to
//! sequencing, except for that joining does not give any guarantees about the order in which the
//! sub-tasks begin executing. The join is considered to have finished when all sub-tasks have
//! finished executing. A join may be faster than a sequence.
//!
//! The following example shows how we might upload data to the base level for all faces of a
//! [TextureCube], before generating the data for the remaining levels with a "generate mipmap"
//! command:
//!
//! ```rust
//! # use web_glitz::runtime::RenderingContext;
//! # fn wrapper<Rc>(context: &Rc) where Rc: RenderingContext + Clone + 'static {
//! use web_glitz::image::{Image2DSource, MipmapLevels};
//! use web_glitz::image::format::RGB8;
//! use web_glitz::image::texture_cube::TextureCubeDescriptor;
//! use web_glitz::task::{join_all, sequence_all};
//!
//! // First we create the cube-map texture we are going to upload to:
//! let texture = context.try_create_texture_cube(&TextureCubeDescriptor {
//!     format: RGB8,
//!     width: 256,
//!     height: 256,
//!     levels: MipmapLevels::Complete
//! }).unwrap();
//!
//! // Then we define some data for each of the cube-map faces:
//! let positive_x_pixels: Vec<[u8; 3]> = vec![[255, 0, 0]; 256 * 256];
//! let positive_x_data = Image2DSource::from_pixels(positive_x_pixels, 256, 256).unwrap();
//! let negative_x_pixels: Vec<[u8; 3]> = vec![[0, 255, 0]; 256 * 256];
//! let negative_x_data = Image2DSource::from_pixels(negative_x_pixels, 256, 256).unwrap();
//! let positive_y_pixels: Vec<[u8; 3]> = vec![[0, 0, 255]; 256 * 256];
//! let positive_y_data = Image2DSource::from_pixels(positive_y_pixels, 256, 256).unwrap();
//! let negative_y_pixels: Vec<[u8; 3]> = vec![[255, 255, 0]; 256 * 256];
//! let negative_y_data = Image2DSource::from_pixels(negative_y_pixels, 256, 256).unwrap();
//! let positive_z_pixels: Vec<[u8; 3]> = vec![[255, 0, 255]; 256 * 256];
//! let positive_z_data = Image2DSource::from_pixels(positive_z_pixels, 256, 256).unwrap();
//! let negative_z_pixels: Vec<[u8; 3]> = vec![[0, 255, 255]; 256 * 256];
//! let negative_z_data = Image2DSource::from_pixels(negative_z_pixels, 256, 256).unwrap();
//!
//! // Finally, we define our task:
//! let task = sequence_all![
//!     join_all![
//!         texture.base_level().positive_x().upload_command(positive_x_data),
//!         texture.base_level().negative_x().upload_command(negative_x_data),
//!         texture.base_level().positive_y().upload_command(positive_y_data),
//!         texture.base_level().negative_y().upload_command(negative_y_data),
//!         texture.base_level().positive_z().upload_command(positive_z_data),
//!         texture.base_level().negative_z().upload_command(negative_z_data),
//!     ],
//!     texture.generate_mipmap_command()
//! ];
//! # }
//! ```
//!
//! In this case, we don't really care about the order in which the uploads to each of the cube's
//! faces finish, so we use the [join_all] macro to join them into a task. However, it is important
//! that we do not begin generating the mipmap data for the remaining levels before all uploads
//! have finished. We therefor use [sequence] to sequence our combined upload command with the
//! "generate mipmap" command.
//!
//! Note that all sequence and join functions and macros mentioned above also come in "left" and
//! "right" variants. For example, [sequence5] is accompanied by [sequence5_left] and
//! [sequence5_right]. The difference is in the combined task's output (the future result of the
//! task when it is submitted with [RenderingContext::submit], see below). [sequence5] outputs a
//! tuple of all 5 of the outputs of the 5 tasks it sequences. However, one is often only interested
//! in just the output of either the left-most (the first) or right-most (the last) task in the
//! sequence. This is where [sequence5_left] and [sequence5_right] come in: [sequence5_left] only
//! outputs the output of the left-most task and [sequence5_right] only outputs the output of the
//! right-most task. In all other aspects the behaviour of a task created with [sequence5_left] or
//! [sequence5_right] is identical to the behaviour of a task created with [sequence5].
//!
//! # Submitting tasks
//!
//! A task merely describes work for the GPU, it does not actually do anything until it is submitted
//! to a [RenderingContext] with [RenderingContext::submit]:
//!
//! ```rust
//! # use web_glitz::runtime::RenderingContext;
//! # fn wrapper<Rc>(context: &Rc) where Rc: RenderingContext + Clone + 'static {
//! # use web_glitz::image::{Image2DSource, MipmapLevels};
//! # use web_glitz::image::format::RGB8;
//! # use web_glitz::image::texture_2d::Texture2DDescriptor;
//! # let texture = context.try_create_texture_2d(&Texture2DDescriptor {
//! #     format: RGB8,
//! #     width: 256,
//! #     height: 256,
//! #     levels: MipmapLevels::Complete
//! # }).unwrap();
//! # let pixels: Vec<[u8; 3]> = vec![[255, 0, 0]; 256 * 256];
//! # let data = Image2DSource::from_pixels(pixels, 256, 256).unwrap();
//! # let task = texture.base_level().upload_command(data);
//! let future = context.submit(task);
//! # }
//! ```
//!
//! This will return a [Future] that will resolve with the task's output (see [GpuTask::Output])
//! after the task has finished executing.
//!
//! [Texture2D]: web_glitz::image::texture_2d::Texture2D
//! [RenderingContext]: web_glitz::runtime::RenderingContext
//! [TextureCube]: web_glitz::image::texture_cube::TextureCube
//! [Future]: std::future::Future

mod gpu_task;
pub use self::gpu_task::{ContextId, Empty, GpuTask, GpuTaskExt, Progress};

mod join;
pub use self::join::{
    join, join3, join3_left, join3_right, join4, join4_left, join4_right, join5, join5_left,
    join5_right, join_iter, join_left, join_right, Join, Join3, Join3Left, Join3Right, Join4,
    Join4Left, Join4Right, Join5, Join5Left, Join5Right, JoinIter, JoinLeft, JoinRight,
};

mod map;
pub use self::map::Map;

mod option_task;
pub use self::option_task::OptionTask;

mod sequence;
pub use self::sequence::{
    sequence, sequence3, sequence3_left, sequence3_right, sequence4, sequence4_left,
    sequence4_right, sequence5, sequence5_left, sequence5_right, sequence_iter, sequence_left,
    sequence_right, Sequence, Sequence3, Sequence3Left, Sequence3Right, Sequence4, Sequence4Left,
    Sequence4Right, Sequence5, Sequence5Left, Sequence5Right, SequenceIter, SequenceLeft,
    SequenceRight,
};

mod maybe_done;

/// Macro that joins all tasks.
pub use crate::join_all;

/// Macro that joins all tasks and returns only the output of the left-most task.
pub use crate::join_all_left;

/// Macro that joins all tasks and returns only the output of the right-most task.
pub use crate::join_all_right;

/// Macro that sequences all tasks and returns a tuple of all outputs.
pub use crate::sequence_all;

/// Macro that sequences all tasks and returns only the output of the left-most task.
pub use crate::sequence_all_left;

/// Macro that sequences all tasks and returns only the output of the right-most task.
pub use crate::sequence_all_right;