added underdeveloped but working image support (no freeing or samplers)
This commit is contained in:
@@ -8,7 +8,7 @@ pub struct Regioned {
|
||||
impl<Ctx: 'static> Widget<Ctx> for Regioned {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
painter.region.select(&self.region);
|
||||
painter.draw(&self.inner);
|
||||
painter.draw_inner(&self.inner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
src/core/image.rs
Normal file
63
src/core/image.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use image::DynamicImage;
|
||||
|
||||
use crate::{Color, TextureHandle, Widget, WidgetFnRet, render::RectPrimitive};
|
||||
|
||||
pub struct Image {
|
||||
handle: Option<TextureHandle>,
|
||||
}
|
||||
|
||||
impl<Ctx> Widget<Ctx> for Image {
|
||||
fn draw(&self, painter: &mut crate::Painter<Ctx>) {
|
||||
if let Some(handle) = &self.handle {
|
||||
painter.draw_texture(handle);
|
||||
} else {
|
||||
painter.write(RectPrimitive {
|
||||
color: Color::MAGENTA,
|
||||
inner_radius: 0.0,
|
||||
radius: 0.0,
|
||||
thickness: 0.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn image<Ctx>(image: impl LoadableImage) -> WidgetFnRet!(Image, Ctx) {
|
||||
let image = match image.get_image() {
|
||||
Ok(image) => Some(image),
|
||||
Err(e) => {
|
||||
println!("Failed to load image: {e}");
|
||||
None
|
||||
}
|
||||
};
|
||||
move |ui| Image {
|
||||
handle: image.map(|image| ui.add_texture(image)),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LoadableImage {
|
||||
fn get_image(self) -> Result<DynamicImage, String>;
|
||||
}
|
||||
|
||||
impl LoadableImage for &str {
|
||||
fn get_image(self) -> Result<DynamicImage, String> {
|
||||
image::open(self).map_err(|e| format!("{e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl LoadableImage for String {
|
||||
fn get_image(self) -> Result<DynamicImage, String> {
|
||||
image::open(self).map_err(|e| format!("{e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> LoadableImage for &[u8; LEN] {
|
||||
fn get_image(self) -> Result<DynamicImage, String> {
|
||||
image::load_from_memory(self).map_err(|e| format!("{e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl LoadableImage for DynamicImage {
|
||||
fn get_image(self) -> Result<DynamicImage, String> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod frame;
|
||||
mod image;
|
||||
mod num;
|
||||
mod rect;
|
||||
mod sense;
|
||||
@@ -7,6 +8,7 @@ mod stack;
|
||||
mod trait_fns;
|
||||
|
||||
pub use frame::*;
|
||||
pub use image::*;
|
||||
pub use num::*;
|
||||
pub use rect::*;
|
||||
pub use sense::*;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{primitive::RoundedRectData, Painter, UiNum, UiColor, Widget};
|
||||
use crate::{Painter, UiColor, UiNum, Widget, render::RectPrimitive};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Rect {
|
||||
@@ -29,7 +29,7 @@ impl Rect {
|
||||
|
||||
impl<Ctx> Widget<Ctx> for Rect {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
painter.write(RoundedRectData {
|
||||
painter.write(RectPrimitive {
|
||||
color: self.color,
|
||||
radius: self.radius,
|
||||
thickness: self.thickness,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Dir, Painter, Sign, UiNum, UiRegion, UIScalar, Widget, WidgetId};
|
||||
use crate::{Dir, Painter, Sign, UIScalar, UiNum, UiRegion, Widget, WidgetId};
|
||||
|
||||
pub struct Span {
|
||||
pub children: Vec<(WidgetId, SpanLen)>,
|
||||
|
||||
@@ -7,7 +7,7 @@ pub struct Stack {
|
||||
impl<Ctx> Widget<Ctx> for Stack {
|
||||
fn draw(&self, painter: &mut crate::Painter<Ctx>) {
|
||||
for child in &self.children {
|
||||
painter.draw_within(child, painter.region);
|
||||
painter.draw(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
mod color;
|
||||
mod id;
|
||||
mod painter;
|
||||
mod region;
|
||||
mod sense;
|
||||
mod texture;
|
||||
mod ui;
|
||||
mod vec2;
|
||||
mod widget;
|
||||
mod id;
|
||||
|
||||
pub use color::*;
|
||||
pub use id::*;
|
||||
pub use painter::*;
|
||||
pub use region::*;
|
||||
pub use sense::*;
|
||||
pub use texture::*;
|
||||
pub use ui::*;
|
||||
pub use vec2::*;
|
||||
pub use widget::*;
|
||||
pub use id::*;
|
||||
|
||||
pub type UiColor = Color<u8>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ActiveSensors, SensorMap, UiRegion, WidgetId, Widgets,
|
||||
primitive::{Primitive, Primitives},
|
||||
ActiveSensors, SensorMap, TextureHandle, UiRegion, WidgetId, Widgets,
|
||||
render::{Primitive, Primitives},
|
||||
};
|
||||
|
||||
pub struct Painter<'a, Ctx: 'static> {
|
||||
@@ -32,7 +32,9 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
||||
self.primitives.write(data, self.region);
|
||||
}
|
||||
|
||||
pub fn draw<W>(&mut self, id: &WidgetId<W>)
|
||||
/// Draws a widget but does NOT maintain the region.
|
||||
/// Useful if you only have a single widget to draw.
|
||||
pub fn draw_inner<W>(&mut self, id: &WidgetId<W>)
|
||||
where
|
||||
Ctx: 'static,
|
||||
{
|
||||
@@ -42,16 +44,34 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
||||
self.nodes.get_dyn(id).draw(self);
|
||||
}
|
||||
|
||||
pub fn draw_within(&mut self, node: &WidgetId, region: UiRegion)
|
||||
/// Draws a widget and maintains the original region.
|
||||
/// Useful if you need to draw multiple overlapping child widgets.
|
||||
pub fn draw<W>(&mut self, id: &WidgetId<W>)
|
||||
where
|
||||
Ctx: 'static,
|
||||
{
|
||||
let old = self.region;
|
||||
self.draw_inner(id);
|
||||
self.region = old;
|
||||
}
|
||||
|
||||
/// Draws a widget within an inner region and maintains the original region.
|
||||
/// Useful if you need to draw multiple child widgets in select areas within the original
|
||||
/// region.
|
||||
pub fn draw_within(&mut self, id: &WidgetId, region: UiRegion)
|
||||
where
|
||||
Ctx: 'static,
|
||||
{
|
||||
let old = self.region;
|
||||
self.region.select(®ion);
|
||||
self.draw(node);
|
||||
self.draw_inner(id);
|
||||
self.region = old;
|
||||
}
|
||||
|
||||
pub fn draw_texture(&mut self, handle: &TextureHandle) {
|
||||
self.write(handle.inner);
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Primitives {
|
||||
self.primitives
|
||||
}
|
||||
|
||||
48
src/layout/texture.rs
Normal file
48
src/layout/texture.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use image::DynamicImage;
|
||||
|
||||
use crate::render::TexturePrimitive;
|
||||
|
||||
/// TODO: proper resource management
|
||||
pub struct TextureHandle {
|
||||
pub inner: TexturePrimitive,
|
||||
}
|
||||
|
||||
/// a texture manager for a ui
|
||||
/// note that this is heavily oriented towards wgpu's renderer so the primitives don't need mapped
|
||||
#[derive(Default)]
|
||||
pub struct Textures {
|
||||
/// TODO: these are images, not views rn
|
||||
views: Vec<DynamicImage>,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
pub struct TextureUpdates<'a> {
|
||||
pub images: Option<&'a [DynamicImage]>,
|
||||
}
|
||||
|
||||
impl Textures {
|
||||
pub fn add(&mut self, image: DynamicImage) -> TextureHandle {
|
||||
let view_idx = self.views.len() as u32;
|
||||
self.views.push(image);
|
||||
// 0 == default in renderer; TODO: actually create samplers here
|
||||
let sampler_idx = 0;
|
||||
self.changed = true;
|
||||
TextureHandle {
|
||||
inner: TexturePrimitive {
|
||||
view_idx,
|
||||
sampler_idx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updates(&mut self) -> TextureUpdates<'_> {
|
||||
if self.changed {
|
||||
self.changed = false;
|
||||
TextureUpdates {
|
||||
images: Some(&self.views),
|
||||
}
|
||||
} else {
|
||||
TextureUpdates { images: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use image::DynamicImage;
|
||||
|
||||
use crate::{
|
||||
ActiveSensors, HashMap, Painter, SensorMap, Widget, WidgetId, WidgetLike,
|
||||
primitive::Primitives,
|
||||
util::{IDTracker, Id},
|
||||
ActiveSensors, HashMap, Painter, SensorMap, TextureHandle, TextureUpdates, Textures, Widget,
|
||||
WidgetId, WidgetLike,
|
||||
render::Primitives,
|
||||
util::{Id, IdTracker},
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@@ -10,20 +13,24 @@ use std::{
|
||||
};
|
||||
|
||||
pub struct Ui<Ctx> {
|
||||
ids: IDTracker,
|
||||
base: Option<WidgetId>,
|
||||
widgets: Widgets<Ctx>,
|
||||
updates: Vec<WidgetId>,
|
||||
del_recv: Receiver<Id>,
|
||||
del_send: Sender<Id>,
|
||||
primitives: Primitives,
|
||||
textures: Textures,
|
||||
full_redraw: bool,
|
||||
|
||||
pub(super) active_sensors: ActiveSensors,
|
||||
pub(super) sensor_map: SensorMap<Ctx>,
|
||||
primitives: Primitives,
|
||||
full_redraw: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Widgets<Ctx>(HashMap<Id, Box<dyn Widget<Ctx>>>);
|
||||
pub struct Widgets<Ctx> {
|
||||
ids: IdTracker,
|
||||
map: HashMap<Id, Box<dyn Widget<Ctx>>>,
|
||||
}
|
||||
|
||||
impl<Ctx> Ui<Ctx> {
|
||||
pub fn add<W: Widget<Ctx>, Tag>(
|
||||
@@ -68,7 +75,15 @@ impl<Ctx> Ui<Ctx> {
|
||||
}
|
||||
|
||||
pub fn id<W: Widget<Ctx>>(&mut self) -> WidgetId<W> {
|
||||
WidgetId::new(self.ids.next(), TypeId::of::<W>(), self.del_send.clone())
|
||||
WidgetId::new(
|
||||
self.widgets.reserve(),
|
||||
TypeId::of::<W>(),
|
||||
self.del_send.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
||||
self.textures.add(image)
|
||||
}
|
||||
|
||||
pub fn redraw_all(&mut self, ctx: &mut Ctx)
|
||||
@@ -83,12 +98,12 @@ impl<Ctx> Ui<Ctx> {
|
||||
&mut self.active_sensors,
|
||||
);
|
||||
if let Some(base) = &self.base {
|
||||
painter.draw(base);
|
||||
painter.draw_inner(base);
|
||||
}
|
||||
self.primitives = painter.finish();
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ctx: &mut Ctx) -> Option<&Primitives>
|
||||
pub fn update(&mut self, ctx: &mut Ctx) -> UiRenderUpdates
|
||||
where
|
||||
Ctx: 'static,
|
||||
{
|
||||
@@ -99,14 +114,24 @@ impl<Ctx> Ui<Ctx> {
|
||||
if self.full_redraw {
|
||||
self.redraw_all(ctx);
|
||||
self.full_redraw = false;
|
||||
return Some(&self.primitives);
|
||||
UiRenderUpdates {
|
||||
primitives: Some(&self.primitives),
|
||||
textures: self.textures.updates(),
|
||||
}
|
||||
} else if self.updates.is_empty() {
|
||||
UiRenderUpdates {
|
||||
primitives: None,
|
||||
textures: self.textures.updates(),
|
||||
}
|
||||
} else {
|
||||
// TODO: partial updates
|
||||
self.redraw_all(ctx);
|
||||
self.updates.drain(..);
|
||||
UiRenderUpdates {
|
||||
primitives: Some(&self.primitives),
|
||||
textures: self.textures.updates(),
|
||||
}
|
||||
}
|
||||
if self.updates.is_empty() {
|
||||
return None;
|
||||
}
|
||||
self.redraw_all(ctx);
|
||||
self.updates.drain(..);
|
||||
Some(&self.primitives)
|
||||
}
|
||||
|
||||
pub fn needs_redraw(&self) -> bool {
|
||||
@@ -135,39 +160,51 @@ impl<W: Widget<Ctx>, Ctx> IndexMut<&WidgetId<W>> for Ui<Ctx> {
|
||||
|
||||
impl<Ctx> Widgets<Ctx> {
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
Self {
|
||||
ids: IdTracker::default(),
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dyn<W>(&self, id: &WidgetId<W>) -> &dyn Widget<Ctx> {
|
||||
self.0.get(&id.id).unwrap().as_ref()
|
||||
self.map.get(&id.id).unwrap().as_ref()
|
||||
}
|
||||
|
||||
pub fn get<W: Widget<Ctx>>(&self, id: &WidgetId<W>) -> Option<&W> {
|
||||
self.0.get(&id.id).unwrap().as_any().downcast_ref()
|
||||
self.map.get(&id.id).unwrap().as_any().downcast_ref()
|
||||
}
|
||||
|
||||
pub fn get_mut<W: Widget<Ctx>>(&mut self, id: &WidgetId<W>) -> Option<&mut W> {
|
||||
self.0.get_mut(&id.id).unwrap().as_any_mut().downcast_mut()
|
||||
self.map
|
||||
.get_mut(&id.id)
|
||||
.unwrap()
|
||||
.as_any_mut()
|
||||
.downcast_mut()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, id: Id, widget: impl Widget<Ctx>) {
|
||||
self.0.insert(id, Box::new(widget));
|
||||
self.map.insert(id, Box::new(widget));
|
||||
}
|
||||
|
||||
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget<Ctx>>) {
|
||||
self.0.insert(id, widget);
|
||||
self.map.insert(id, widget);
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, id: Id) {
|
||||
self.0.remove(&id);
|
||||
self.map.remove(&id);
|
||||
self.ids.free(id);
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self) -> Id {
|
||||
self.ids.next()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
self.map.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,11 +222,11 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
|
||||
fn default() -> Self {
|
||||
let (del_send, del_recv) = channel();
|
||||
Self {
|
||||
ids: Default::default(),
|
||||
base: Default::default(),
|
||||
widgets: Widgets::new(),
|
||||
updates: Default::default(),
|
||||
primitives: Default::default(),
|
||||
textures: Textures::default(),
|
||||
full_redraw: false,
|
||||
active_sensors: Default::default(),
|
||||
sensor_map: Default::default(),
|
||||
@@ -198,3 +235,8 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiRenderUpdates<'a> {
|
||||
pub primitives: Option<&'a Primitives>,
|
||||
pub textures: TextureUpdates<'a>,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{util::{impl_op, F32Util}, UiNum};
|
||||
use std::ops::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vec2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
@@ -60,3 +60,9 @@ impl<T: UiNum, U: UiNum> From<(T, U)> for Vec2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Vec2 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ pub(crate) use WidgetFnRet;
|
||||
impl<W: Widget<Ctx>, Ctx, F: FnOnce(&mut Ui<Ctx>) -> W> WidgetLike<Ctx, FnTag> for F {
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
|
||||
let w = self(ui);
|
||||
ui.add(w)
|
||||
self(ui).add(ui)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
#![feature(trait_alias)]
|
||||
#![feature(negative_impls)]
|
||||
|
||||
mod layout;
|
||||
mod render;
|
||||
mod util;
|
||||
mod core;
|
||||
mod layout;
|
||||
pub mod render;
|
||||
mod util;
|
||||
|
||||
pub use layout::*;
|
||||
pub use render::*;
|
||||
pub use core::*;
|
||||
pub use layout::*;
|
||||
|
||||
pub type HashMap<K, V> = std::collections::HashMap<K, V>;
|
||||
pub type HashSet<K> = std::collections::HashSet<K>;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::num::NonZero;
|
||||
|
||||
use crate::{
|
||||
primitive::{PrimitiveBuffers, Primitives},
|
||||
render::{data::PrimitiveInstance, util::ArrBuf},
|
||||
Ui, UiRenderUpdates,
|
||||
render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf},
|
||||
};
|
||||
use data::WindowUniform;
|
||||
use wgpu::{
|
||||
@@ -10,41 +12,55 @@ use wgpu::{
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
mod data;
|
||||
pub mod primitive;
|
||||
mod primitive;
|
||||
mod texture;
|
||||
mod util;
|
||||
|
||||
pub use primitive::*;
|
||||
|
||||
const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
|
||||
|
||||
pub struct UIRenderNode {
|
||||
layout0: BindGroupLayout,
|
||||
group0: BindGroup,
|
||||
pub struct UiRenderer {
|
||||
uniform_layout: BindGroupLayout,
|
||||
uniform_group: BindGroup,
|
||||
primitive_layout: BindGroupLayout,
|
||||
primitive_group: BindGroup,
|
||||
rsc_layout: BindGroupLayout,
|
||||
rsc_group: BindGroup,
|
||||
|
||||
pipeline: RenderPipeline,
|
||||
|
||||
window_buffer: Buffer,
|
||||
instance: ArrBuf<PrimitiveInstance>,
|
||||
primitives: PrimitiveBuffers,
|
||||
|
||||
limits: UiLimits,
|
||||
textures: GpuTextures,
|
||||
}
|
||||
|
||||
impl UIRenderNode {
|
||||
impl UiRenderer {
|
||||
pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) {
|
||||
if self.instance.len() != 0 {
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.group0, &[]);
|
||||
pass.set_bind_group(1, &self.primitive_group, &[]);
|
||||
pass.set_vertex_buffer(0, self.instance.buffer.slice(..));
|
||||
pass.draw(0..4, 0..self.instance.len() as u32);
|
||||
if self.instance.len() == 0 {
|
||||
return;
|
||||
}
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_group, &[]);
|
||||
pass.set_bind_group(1, &self.primitive_group, &[]);
|
||||
pass.set_bind_group(2, &self.rsc_group, &[]);
|
||||
pass.set_vertex_buffer(0, self.instance.buffer.slice(..));
|
||||
pass.draw(0..4, 0..self.instance.len() as u32);
|
||||
}
|
||||
|
||||
pub fn update(&mut self, device: &Device, queue: &Queue, primitives: Option<&Primitives>) {
|
||||
if let Some(primitives) = primitives {
|
||||
pub fn update(&mut self, device: &Device, queue: &Queue, updates: UiRenderUpdates) {
|
||||
if let Some(primitives) = updates.primitives {
|
||||
self.instance.update(device, queue, &primitives.instances);
|
||||
self.primitives.update(device, queue, &primitives.data);
|
||||
self.primitive_group =
|
||||
Self::primitive_group(device, &self.primitive_layout, self.primitives.buffers())
|
||||
}
|
||||
if self.textures.apply(updates.textures) {
|
||||
self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: &PhysicalSize<u32>, queue: &Queue) {
|
||||
@@ -55,7 +71,12 @@ impl UIRenderNode {
|
||||
queue.write_buffer(&self.window_buffer, 0, bytemuck::cast_slice(slice));
|
||||
}
|
||||
|
||||
pub fn new(device: &Device, config: &SurfaceConfiguration) -> Self {
|
||||
pub fn new(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
config: &SurfaceConfiguration,
|
||||
limits: UiLimits,
|
||||
) -> Self {
|
||||
let shader = device.create_shader_module(ShaderModuleDescriptor {
|
||||
label: Some("UI Shape Shader"),
|
||||
source: ShaderSource::Wgsl(SHAPE_SHADER.into()),
|
||||
@@ -75,7 +96,7 @@ impl UIRenderNode {
|
||||
);
|
||||
let primitives = PrimitiveBuffers::new(device);
|
||||
|
||||
let layout0 = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
@@ -89,7 +110,7 @@ impl UIRenderNode {
|
||||
label: Some("window"),
|
||||
});
|
||||
|
||||
let group0 = Self::bind_group_0(device, &layout0, &window_buffer);
|
||||
let uniform_group = Self::bind_group_0(device, &uniform_layout, &window_buffer);
|
||||
|
||||
let primitive_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &core::array::from_fn::<_, { PrimitiveBuffers::LEN }, _>(|i| {
|
||||
@@ -110,9 +131,13 @@ impl UIRenderNode {
|
||||
let primitive_group =
|
||||
Self::primitive_group(device, &primitive_layout, primitives.buffers());
|
||||
|
||||
let tex_manager = GpuTextures::new(device, queue);
|
||||
let rsc_layout = Self::rsc_layout(device, &limits);
|
||||
let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager);
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||
label: Some("UI Shape Pipeline Layout"),
|
||||
bind_group_layouts: &[&layout0, &primitive_layout],
|
||||
bind_group_layouts: &[&uniform_layout, &primitive_layout, &rsc_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||
@@ -154,18 +179,22 @@ impl UIRenderNode {
|
||||
});
|
||||
|
||||
Self {
|
||||
layout0,
|
||||
group0,
|
||||
uniform_layout,
|
||||
uniform_group,
|
||||
primitive_layout,
|
||||
primitive_group,
|
||||
rsc_layout,
|
||||
rsc_group,
|
||||
pipeline,
|
||||
window_buffer,
|
||||
instance,
|
||||
primitives,
|
||||
limits,
|
||||
textures: tex_manager,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_group_0(
|
||||
fn bind_group_0(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
window_buffer: &Buffer,
|
||||
@@ -180,18 +209,86 @@ impl UIRenderNode {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn primitive_group(
|
||||
fn primitive_group(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
buffers: [&Buffer; PrimitiveBuffers::LEN],
|
||||
buffers: [(u32, &Buffer); PrimitiveBuffers::LEN],
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &buffers.each_ref().map(|b| BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: b.as_entire_binding(),
|
||||
entries: &buffers.map(|(binding, buf)| BindGroupEntry {
|
||||
binding,
|
||||
resource: buf.as_entire_binding(),
|
||||
}),
|
||||
label: Some("ui primitives"),
|
||||
})
|
||||
}
|
||||
|
||||
fn rsc_layout(device: &Device, limits: &UiLimits) -> BindGroupLayout {
|
||||
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Texture {
|
||||
sample_type: TextureSampleType::Float { filterable: false },
|
||||
view_dimension: TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: Some(NonZero::new(limits.max_textures).unwrap()),
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||
count: Some(NonZero::new(limits.max_samplers).unwrap()),
|
||||
},
|
||||
],
|
||||
label: Some("ui rsc"),
|
||||
})
|
||||
}
|
||||
|
||||
fn rsc_group(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
tex_manager: &GpuTextures,
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::TextureViewArray(&tex_manager.views()),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
|
||||
},
|
||||
],
|
||||
label: Some("ui rsc"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiLimits {
|
||||
max_textures: u32,
|
||||
max_samplers: u32,
|
||||
}
|
||||
|
||||
impl Default for UiLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_textures: 100000,
|
||||
max_samplers: 1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiLimits {
|
||||
pub fn max_binding_array_elements_per_shader_stage(&self) -> u32 {
|
||||
self.max_textures + self.max_samplers
|
||||
}
|
||||
pub fn max_binding_array_sampler_elements_per_shader_stage(&self) -> u32 {
|
||||
self.max_samplers
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,9 @@ macro_rules! primitives {
|
||||
|
||||
impl PrimitiveBuffers {
|
||||
pub const LEN: usize = primitives!(@count $($name)*);
|
||||
pub fn buffers(&self) -> [&Buffer; Self::LEN] {
|
||||
pub fn buffers(&self) -> [(u32, &Buffer); Self::LEN] {
|
||||
[
|
||||
$(&self.$name.buffer)*
|
||||
$((<$ty>::BINDING, &self.$name.buffer),)*
|
||||
]
|
||||
}
|
||||
pub fn new(device: &Device) -> Self {
|
||||
@@ -46,7 +46,7 @@ macro_rules! primitives {
|
||||
device,
|
||||
BufferUsages::STORAGE | BufferUsages::COPY_DST,
|
||||
stringify!($name),
|
||||
))*
|
||||
),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,14 +62,10 @@ macro_rules! primitives {
|
||||
}
|
||||
)*
|
||||
};
|
||||
(@count $t1:tt, $($t:tt),+) => { 1 + gen!(@count $($t),+) };
|
||||
(@count $t1:tt $($t:tt)+) => { 1 + primitives!(@count $($t),+) };
|
||||
(@count $t:tt) => { 1 };
|
||||
}
|
||||
|
||||
primitives!(
|
||||
rects: RoundedRectData => 0,
|
||||
);
|
||||
|
||||
impl Primitives {
|
||||
pub fn write<P: Primitive>(&mut self, data: P, region: UiRegion) {
|
||||
let vec = P::vec(&mut self.data);
|
||||
@@ -83,11 +79,23 @@ impl Primitives {
|
||||
}
|
||||
}
|
||||
|
||||
primitives!(
|
||||
rects: RectPrimitive => 0,
|
||||
textures: TexturePrimitive => 1,
|
||||
);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RoundedRectData {
|
||||
pub struct RectPrimitive {
|
||||
pub color: Color<u8>,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TexturePrimitive {
|
||||
pub view_idx: u32,
|
||||
pub sampler_idx: u32,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
const RECT: u32 = 0;
|
||||
const TEXTURE: u32 = 1;
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> window: WindowUniform;
|
||||
@group(1) @binding(RECT)
|
||||
var<storage> rects: array<RoundedRect>;
|
||||
var<storage> rects: array<Rect>;
|
||||
@group(1) @binding(TEXTURE)
|
||||
var<storage> textures: array<TextureInfo>;
|
||||
|
||||
struct Rect {
|
||||
color: u32,
|
||||
radius: f32,
|
||||
thickness: f32,
|
||||
inner_radius: f32,
|
||||
}
|
||||
|
||||
struct TextureInfo {
|
||||
view_idx: u32,
|
||||
sampler_idx: u32,
|
||||
}
|
||||
|
||||
@group(2) @binding(0)
|
||||
var views: binding_array<texture_2d<f32>>;
|
||||
@group(2) @binding(1)
|
||||
var samplers: binding_array<sampler>;
|
||||
|
||||
struct WindowUniform {
|
||||
dim: vec2<f32>,
|
||||
@@ -18,23 +38,18 @@ struct InstanceInput {
|
||||
@location(5) idx: u32,
|
||||
}
|
||||
|
||||
struct RoundedRect {
|
||||
color: u32,
|
||||
radius: f32,
|
||||
thickness: f32,
|
||||
inner_radius: f32,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@location(0) top_left: vec2<f32>,
|
||||
@location(1) bot_right: vec2<f32>,
|
||||
@location(2) binding: u32,
|
||||
@location(3) idx: u32,
|
||||
@location(2) uv: vec2<f32>,
|
||||
@location(3) binding: u32,
|
||||
@location(4) idx: u32,
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
struct Region {
|
||||
pos: vec2<f32>,
|
||||
uv: vec2<f32>,
|
||||
top_left: vec2<f32>,
|
||||
bot_right: vec2<f32>,
|
||||
}
|
||||
@@ -50,12 +65,13 @@ fn vs_main(
|
||||
let bot_right = in.bottom_right_anchor * window.dim + in.bottom_right_offset;
|
||||
let size = bot_right - top_left;
|
||||
|
||||
var pos = top_left + vec2<f32>(
|
||||
let uv = vec2<f32>(
|
||||
f32(vi % 2u),
|
||||
f32(vi / 2u)
|
||||
) * size;
|
||||
pos = pos / window.dim * 2.0 - 1.0;
|
||||
);
|
||||
let pos = (top_left + uv * size) / window.dim * 2.0 - 1.0;
|
||||
out.clip_position = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
|
||||
out.uv = uv;
|
||||
out.binding = in.binding;
|
||||
out.idx = in.idx;
|
||||
out.top_left = top_left;
|
||||
@@ -69,19 +85,27 @@ fn fs_main(
|
||||
in: VertexOutput
|
||||
) -> @location(0) vec4<f32> {
|
||||
let pos = in.clip_position.xy;
|
||||
let region = Region(pos, in.top_left, in.bot_right);
|
||||
let region = Region(pos, in.uv, in.top_left, in.bot_right);
|
||||
let i = in.idx;
|
||||
switch in.binding {
|
||||
case RECT: {
|
||||
return draw_rounded_rect(region, rects[i]);
|
||||
}
|
||||
case TEXTURE: {
|
||||
return draw_texture(region, textures[i]);
|
||||
}
|
||||
default: {
|
||||
return vec4(1.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_rounded_rect(region: Region, rect: RoundedRect) -> vec4<f32> {
|
||||
// TODO: this seems really inefficient (per frag indexing)?
|
||||
fn draw_texture(region: Region, info: TextureInfo) -> vec4<f32> {
|
||||
return textureSample(views[info.view_idx], samplers[info.sampler_idx], region.uv);
|
||||
}
|
||||
|
||||
fn draw_rounded_rect(region: Region, rect: Rect) -> vec4<f32> {
|
||||
var color = read_color(rect.color);
|
||||
|
||||
let edge = 0.5;
|
||||
@@ -106,7 +130,7 @@ fn distance_from_rect(pixel_pos: vec2<f32>, rect_center: vec2<f32>, rect_corner:
|
||||
let p = pixel_pos - rect_center;
|
||||
// vec from inner rect corner to pixel
|
||||
let q = abs(p) - (rect_corner - radius);
|
||||
return length(max(q, vec2<f32>(0.0, 0.0))) - radius;
|
||||
return length(max(q, vec2(0.0))) - radius;
|
||||
}
|
||||
|
||||
fn read_color(c: u32) -> vec4<f32> {
|
||||
|
||||
98
src/render/texture.rs
Normal file
98
src/render/texture.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use image::{DynamicImage, EncodableLayout};
|
||||
use wgpu::{util::DeviceExt, *};
|
||||
|
||||
use crate::TextureUpdates;
|
||||
|
||||
pub struct GpuTextures {
|
||||
device: Device,
|
||||
queue: Queue,
|
||||
views: Vec<TextureView>,
|
||||
samplers: Vec<Sampler>,
|
||||
no_views: Vec<TextureView>,
|
||||
}
|
||||
|
||||
impl GpuTextures {
|
||||
pub fn apply(&mut self, updates: TextureUpdates) -> bool {
|
||||
if let Some(images) = updates.images {
|
||||
for img in images {
|
||||
self.add_view(img);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn add_view(&mut self, image: &DynamicImage) {
|
||||
let image = image.to_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let texture = self.device.create_texture_with_data(
|
||||
&self.queue,
|
||||
&TextureDescriptor {
|
||||
label: None,
|
||||
size: Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
},
|
||||
wgt::TextureDataOrder::MipMajor,
|
||||
image.as_bytes(),
|
||||
);
|
||||
let view = texture.create_view(&TextureViewDescriptor::default());
|
||||
self.views.push(view);
|
||||
}
|
||||
|
||||
pub fn new(device: &Device, queue: &Queue) -> Self {
|
||||
Self {
|
||||
device: device.clone(),
|
||||
queue: queue.clone(),
|
||||
views: Vec::new(),
|
||||
samplers: vec![default_sampler(device)],
|
||||
no_views: vec![null_texture_view(device)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn views(&self) -> Vec<&TextureView> {
|
||||
if self.views.is_empty() {
|
||||
&self.no_views
|
||||
} else {
|
||||
&self.views
|
||||
}
|
||||
.iter()
|
||||
.by_ref()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn samplers(&self) -> Vec<&Sampler> {
|
||||
self.samplers.iter().by_ref().collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn null_texture_view(device: &Device) -> TextureView {
|
||||
device
|
||||
.create_texture(&TextureDescriptor {
|
||||
label: Some("null"),
|
||||
size: Extent3d {
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
})
|
||||
.create_view(&TextureViewDescriptor::default())
|
||||
}
|
||||
|
||||
pub fn default_sampler(device: &Device) -> Sampler {
|
||||
device.create_sampler(&SamplerDescriptor::default())
|
||||
}
|
||||
@@ -32,7 +32,7 @@ impl<T: Pod> ArrBuf<T> {
|
||||
fn init_buf(device: &Device, size: usize, usage: BufferUsages, label: &'static str) -> Buffer {
|
||||
let mut size = size as u64;
|
||||
if usage.contains(BufferUsages::STORAGE) {
|
||||
size = size.max(1);
|
||||
size = size.max(std::mem::size_of::<T>() as u64);
|
||||
}
|
||||
device.create_buffer(&BufferDescriptor {
|
||||
label: Some(label),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use gui::Ui;
|
||||
use ui::Ui;
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
|
||||
BIN
src/testing/assets/sungals.png
Executable file
BIN
src/testing/assets/sungals.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -1,4 +1,4 @@
|
||||
use gui::{CursorState, Vec2};
|
||||
use ui::{CursorState, Vec2};
|
||||
use winit::event::WindowEvent;
|
||||
|
||||
use crate::testing::Client;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use app::App;
|
||||
use gui::*;
|
||||
use render::Renderer;
|
||||
use ui::*;
|
||||
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
|
||||
|
||||
use crate::testing::input::Input;
|
||||
@@ -76,6 +76,8 @@ impl Client {
|
||||
let span_add_test = ui.add(Span::empty(Dir::RIGHT).id(&span_add));
|
||||
let main: WidgetId<Regioned> = ui.id();
|
||||
|
||||
let image_test = ui.add(image(include_bytes!("assets/sungals.png")));
|
||||
|
||||
fn switch_button<To>(
|
||||
color: UiColor,
|
||||
main: &WidgetId<Regioned>,
|
||||
@@ -104,8 +106,9 @@ impl Client {
|
||||
switch_button(UiColor::RED, &main, &pad_test),
|
||||
switch_button(UiColor::GREEN, &main, &span_test),
|
||||
switch_button(UiColor::BLUE, &main, &span_add_test),
|
||||
switch_button(UiColor::MAGENTA, &main, &image_test),
|
||||
)
|
||||
.span(Dir::RIGHT, [1, 1, 1]),
|
||||
.span(Dir::RIGHT, [1, 1, 1, 1]),
|
||||
);
|
||||
let test_button = Rect::new(Color::PURPLE)
|
||||
.radius(30)
|
||||
@@ -156,8 +159,8 @@ impl Client {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
let primitives = ui.update(self);
|
||||
self.renderer.update(primitives);
|
||||
let updates = ui.update(self);
|
||||
self.renderer.update(updates);
|
||||
self.renderer.draw()
|
||||
}
|
||||
WindowEvent::Resized(size) => self.renderer.resize(&size),
|
||||
|
||||
@@ -1,48 +1,51 @@
|
||||
use gui::{UIRenderNode, primitive::Primitives};
|
||||
use pollster::FutureExt;
|
||||
use std::sync::Arc;
|
||||
use wgpu::util::StagingBelt;
|
||||
use ui::{
|
||||
UiRenderUpdates,
|
||||
render::{UiLimits, UiRenderer},
|
||||
};
|
||||
use wgpu::{util::StagingBelt, *};
|
||||
use winit::{dpi::PhysicalSize, window::Window};
|
||||
|
||||
pub const CLEAR_COLOR: wgpu::Color = wgpu::Color::BLACK;
|
||||
pub const CLEAR_COLOR: Color = Color::BLACK;
|
||||
|
||||
pub struct Renderer {
|
||||
window: Arc<Window>,
|
||||
surface: wgpu::Surface<'static>,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
encoder: wgpu::CommandEncoder,
|
||||
surface: Surface<'static>,
|
||||
device: Device,
|
||||
queue: Queue,
|
||||
config: SurfaceConfiguration,
|
||||
encoder: CommandEncoder,
|
||||
staging_belt: StagingBelt,
|
||||
ui_node: UIRenderNode,
|
||||
ui: UiRenderer,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn update(&mut self, primitives: Option<&Primitives>) {
|
||||
self.ui_node.update(&self.device, &self.queue, primitives);
|
||||
pub fn update(&mut self, updates: UiRenderUpdates) {
|
||||
self.ui.update(&self.device, &self.queue, updates);
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
let output = self.surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
.create_view(&TextureViewDescriptor::default());
|
||||
|
||||
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
|
||||
{
|
||||
let render_pass = &mut encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
let render_pass = &mut encoder.begin_render_pass(&RenderPassDescriptor {
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(CLEAR_COLOR),
|
||||
store: wgpu::StoreOp::Store,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(CLEAR_COLOR),
|
||||
store: StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
..Default::default()
|
||||
});
|
||||
self.ui_node.draw(render_pass);
|
||||
self.ui.draw(render_pass);
|
||||
}
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
@@ -55,11 +58,11 @@ impl Renderer {
|
||||
self.config.width = size.width;
|
||||
self.config.height = size.height;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
self.ui_node.resize(size, &self.queue);
|
||||
self.ui.resize(size, &self.queue);
|
||||
}
|
||||
|
||||
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder {
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
fn create_encoder(device: &Device) -> CommandEncoder {
|
||||
device.create_command_encoder(&CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
})
|
||||
}
|
||||
@@ -67,8 +70,8 @@ impl Renderer {
|
||||
pub fn new(window: Arc<Window>) -> Self {
|
||||
let size = window.inner_size();
|
||||
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
let instance = Instance::new(&InstanceDescriptor {
|
||||
backends: Backends::PRIMARY,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@@ -77,18 +80,28 @@ impl Renderer {
|
||||
.expect("Could not create window surface!");
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
.request_adapter(&RequestAdapterOptions {
|
||||
power_preference: PowerPreference::default(),
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
})
|
||||
.block_on()
|
||||
.expect("Could not get adapter!");
|
||||
|
||||
let ui_limits = UiLimits::default();
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
.request_device(&DeviceDescriptor {
|
||||
required_features: Features::TEXTURE_BINDING_ARRAY
|
||||
| Features::PARTIALLY_BOUND_BINDING_ARRAY
|
||||
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
required_limits: Limits {
|
||||
max_binding_array_elements_per_shader_stage: ui_limits
|
||||
.max_binding_array_elements_per_shader_stage(),
|
||||
max_binding_array_sampler_elements_per_shader_stage: ui_limits
|
||||
.max_binding_array_sampler_elements_per_shader_stage(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.block_on()
|
||||
@@ -102,12 +115,12 @@ impl Renderer {
|
||||
.find(|f| f.is_srgb())
|
||||
.unwrap_or(surface_caps.formats[0]);
|
||||
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
let config = SurfaceConfiguration {
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
present_mode: PresentMode::AutoVsync,
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
desired_maximum_frame_latency: 2,
|
||||
view_formats: vec![],
|
||||
@@ -118,7 +131,7 @@ impl Renderer {
|
||||
let staging_belt = StagingBelt::new(4096 * 4);
|
||||
let encoder = Self::create_encoder(&device);
|
||||
|
||||
let shape_pipeline = UIRenderNode::new(&device, &config);
|
||||
let shape_pipeline = UiRenderer::new(&device, &queue, &config, ui_limits);
|
||||
|
||||
Self {
|
||||
surface,
|
||||
@@ -127,7 +140,7 @@ impl Renderer {
|
||||
config,
|
||||
encoder,
|
||||
staging_belt,
|
||||
ui_node: shape_pipeline,
|
||||
ui: shape_pipeline,
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
pub struct Id(u64);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IDTracker {
|
||||
pub struct IdTracker {
|
||||
free: Vec<Id>,
|
||||
cur: u64,
|
||||
}
|
||||
|
||||
impl IDTracker {
|
||||
impl IdTracker {
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn next(&mut self) -> Id {
|
||||
if let Some(id) = self.free.pop() {
|
||||
|
||||
Reference in New Issue
Block a user