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;