Skip to content

Commit

Permalink
feat(gpu): 🎸 the wgpu implementation is compatible with WebGL
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo authored and rchangelog[bot] committed May 15, 2024
1 parent e18362c commit 07ee2e9
Show file tree
Hide file tree
Showing 17 changed files with 199 additions and 162 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he
- **core**: The split functions in `StateReader::map_reader`, `StateWriter::map_writer`, and `StateWriter::split_writer` no longer need to return a reference. (#568 @M-Adoo)
- **core**: Introduced `StateWatcher` for watching state modifies, which was previously the responsibility of `StateReader`. This results in a cleaner and more compact `StateReader` implementation. (#556, @M-Adoo)
- **gpu**: Introduced `GPUBackendImpl::max_textures_per_draw` to set a limit on textures per draw phase (#562 @M-Adoo)
- **gpu**: Updated the `wgpu` implementation of the GPU backend to support WebGL. (#578, @M-Adoo)

### Fixed

- **gpu**: Retrieve the texture limit size from the GPU instead of using a hardcoded value. (#pr, @M-Adoo)
- **gpu**: Retrieve the texture limit size from the GPU instead of using a hardcoded value. (#578, @M-Adoo)

### Changed

Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ resolver = "2"
debug = true

[profile.release]
debug = true
lto = true
strip = true
codegen-units = 1

[workspace.package]
authors = ["RibirX<[email protected]>"]
Expand Down
1 change: 1 addition & 0 deletions examples/wordle_game/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod wordle;
fn main() {
App::run(wordle_game())
.with_app_theme(material::purple::light())
.with_size(Size::new(700., 620.))
.with_title("Wordle Game");
}

Expand Down
51 changes: 30 additions & 21 deletions gpu/src/gpu_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,21 @@ where
self.begin_draw_phase();
let output_size = output.size();
for cmd in commands.into_iter() {
let max_tex_per_draw = self.gpu_impl.limits().max_tex_load_per_draw;
let maybe_used = match cmd {
PaintCommand::ImgPath { .. } => 2,
PaintCommand::PopClip => 0,
_ => 1,
};
if self.tex_ids_map.all_textures().len() + maybe_used
>= self.gpu_impl.load_tex_limit_per_draw()
|| !self.continues_cmd(&cmd)
{
if !self.can_batch(&cmd) {
// if the next command may hit the texture limit, submit the current draw phase.
// And start a new draw phase.
self.draw_triangles(output);
self.end_draw_phase();
self.begin_draw_phase();

assert!(
self.tex_ids_map.all_textures().len() + maybe_used
< self.gpu_impl.load_tex_limit_per_draw(),
self.tex_ids_map.all_textures().len() + maybe_used < max_tex_per_draw,
"The GPUBackend implementation does not provide a sufficient texture limit per draw."
)
}
Expand Down Expand Up @@ -195,7 +192,7 @@ where
let img_start = img_slice.rect.origin.to_f32().to_array();
let img_size = img_slice.rect.size.to_f32().to_array();
let mask_head_and_tex_idx =
(mask_head as i32) << 16 | self.tex_ids_map.tex_idx(img_slice.tex_id) as i32;
mask_head << 16 | self.tex_ids_map.tex_idx(img_slice.tex_id) as i32;
let prim_idx = self.img_prims.len() as u32;
let prim = ImgPrimitive {
transform: ts.inverse().unwrap().to_array(),
Expand Down Expand Up @@ -242,8 +239,7 @@ where
let ts = path.transform;
if let Some((rect, mask_head)) = self.new_mask_layer(path) {
let stop = (self.linear_gradient_stops.len() << 16 | linear_gradient.stops.len()) as u32;
let mask_head_and_spread =
(mask_head as i32) << 16 | linear_gradient.spread_method as i32;
let mask_head_and_spread = mask_head << 16 | linear_gradient.spread_method as i32;
let prim: LinearGradientPrimitive = LinearGradientPrimitive {
transform: ts.inverse().unwrap().to_array(),
stop,
Expand Down Expand Up @@ -343,17 +339,30 @@ where
self.linear_gradient_stops.clear();
}

fn continues_cmd(&self, cmd: &PaintCommand) -> bool {
matches!(
(self.current_phase, cmd),
(CurrentPhase::None, _)
| (_, PaintCommand::Clip(_))
| (_, PaintCommand::PopClip)
| (CurrentPhase::Color, PaintCommand::ColorPath { .. })
| (CurrentPhase::Img, PaintCommand::ImgPath { .. })
| (CurrentPhase::RadialGradient, PaintCommand::RadialGradient { .. })
| (CurrentPhase::LinearGradient, PaintCommand::LinearGradient { .. })
)
fn can_batch(&self, cmd: &PaintCommand) -> bool {
let limits = self.gpu_impl.limits();
let tex_used = self.tex_ids_map.all_textures().len();
match (self.current_phase, cmd) {
(CurrentPhase::None, _) | (_, PaintCommand::PopClip) => true,
(_, PaintCommand::Clip(_)) | (CurrentPhase::Color, PaintCommand::ColorPath { .. }) => {
tex_used < limits.max_tex_load_per_draw
}
(CurrentPhase::Img, PaintCommand::ImgPath { .. }) => {
tex_used < limits.max_tex_load_per_draw - 1
&& self.img_prims.len() < limits.max_image_primitives_per_draw
}
(CurrentPhase::RadialGradient, PaintCommand::RadialGradient { .. }) => {
tex_used < limits.max_tex_load_per_draw
&& self.radial_gradient_prims.len() < limits.max_radial_gradient_primitives_per_draw
&& self.radial_gradient_stops.len() < limits.max_gradient_stop_primitives_per_draw
}
(CurrentPhase::LinearGradient, PaintCommand::LinearGradient { .. }) => {
tex_used < limits.max_tex_load_per_draw
&& self.linear_gradient_prims.len() < limits.max_linear_gradient_primitives_per_draw
&& self.linear_gradient_stops.len() < limits.max_gradient_stop_primitives_per_draw
}
_ => false,
}
}

fn current_clip_mask_index(&self) -> i32 {
Expand Down Expand Up @@ -412,7 +421,7 @@ where
gpu_impl.load_mask_layers(&self.mask_layers);

let textures = self.tex_ids_map.all_textures();
let max_textures = gpu_impl.load_tex_limit_per_draw();
let max_textures = gpu_impl.limits().max_tex_load_per_draw;
let mut tex_buffer = Vec::with_capacity(max_textures);
textures.iter().take(max_textures).for_each(|id| {
tex_buffer.push(self.tex_mgr.texture(*id));
Expand Down
7 changes: 4 additions & 3 deletions gpu/src/gpu_backend/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ where
pub(crate) fn end_frame(&mut self) {
self
.cache
.end_frame(&self.config.label)
.end_frame(self.config.label)
.for_each(|h| release_handle!(self, h));
self
.islands
Expand Down Expand Up @@ -232,14 +232,15 @@ mod tests {
#[test]
fn resource_clear() {
let mut wgpu = block_on(WgpuImpl::headless());
let size = wgpu.limits().texture_size_limit;
let mut atlas = Atlas::<WgpuTexture, _, _>::new(
AtlasConfig::new("", DeviceSize::new(4096, 4096)),
AtlasConfig::new("", size),
ColorFormat::Rgba8,
AntiAliasing::None,
&mut wgpu,
);
atlas.allocate(1, (), DeviceSize::new(32, 32), &mut wgpu);
atlas.allocate(2, (), DeviceSize::new(4097, 16), &mut wgpu);
atlas.allocate(2, (), size, &mut wgpu);
atlas.end_frame();
atlas.end_frame();
wgpu.end_frame();
Expand Down
2 changes: 1 addition & 1 deletion gpu/src/gpu_backend/textures_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ where
T::Host: GPUBackendImpl<Texture = T>,
{
pub(super) fn new(gpu_impl: &mut T::Host, anti_aliasing: AntiAliasing) -> Self {
let max_size = gpu_impl.texture_size_limit();
let max_size = gpu_impl.limits().texture_size_limit;

Self {
alpha_atlas: Atlas::new(
Expand Down
30 changes: 24 additions & 6 deletions gpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,8 @@ pub trait GPUBackendImpl {
/// A frame start, call once per frame
fn begin_frame(&mut self);

/// Returns the maximum number of textures that the backend can load in a
/// single draw phase.
#[inline]
fn load_tex_limit_per_draw(&self) -> usize { 8 }

fn texture_size_limit(&self) -> DeviceSize;
/// Returns the limits of the GPU backend.
fn limits(&self) -> GPULimits;

/// Create a texture.
fn new_texture(
Expand Down Expand Up @@ -167,6 +163,28 @@ pub trait GPUBackendImpl {
fn end_frame(&mut self);
}

/// Represents the sets of limits an GPU backend can provide.
pub struct GPULimits {
/// The maximum size of the texture that the backend can create.
pub texture_size_limit: DeviceSize,
/// The maximum number of textures that the backend can load in a single draw
pub max_tex_load_per_draw: usize,
/// The maximum number of mask layers that the backend can load in a single
/// draw phase
pub max_image_primitives_per_draw: usize,
/// The maximum number of radial gradient primitives that the backend can load
/// in a single draw
pub max_radial_gradient_primitives_per_draw: usize,
/// The maximum number of linear gradient primitives that the backend can load
/// in a single draw
pub max_linear_gradient_primitives_per_draw: usize,
/// The maximum number of gradient stops that the backend can load in a single
/// draw phase
pub max_gradient_stop_primitives_per_draw: usize,
/// The maximum number of mask layers that the backend can load in a single
pub max_mask_layers_per_draw: usize,
}

#[repr(packed)]
#[derive(AsBytes, PartialEq, Clone, Copy)]
pub struct ColorAttr {
Expand Down
Loading

0 comments on commit 07ee2e9

Please sign in to comment.