Compare commits

...

16 Commits

14 changed files with 540 additions and 170 deletions

View File

@ -1,9 +1,11 @@
[workspace]
members = [
"apps/android",
"apps/magpie",
"apps/music-player",
"apps/sandbox",
"crates/script",
"renderers/wgpu",
"scripts/music-player",
"scripts/sao-ui",
]
@ -28,3 +30,10 @@ wasmtime = "0.38"
[dependencies.font-kit]
version = "*"
default-features = false
[profile.dev]
opt-level = "z"
debug = false
lto = "fat"
strip = true

15
apps/android/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "canary-android-demo"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["lib", "cdylib"]
[dependencies]
canary = { path = "../.." }
canary-wgpu = { path = "../../renderers/wgpu" }
pollster = "0.2"
ndk-glue = "0.7"
winit = { version = "0.27", default-features = false }
wgpu = "0.14"

159
apps/android/src/lib.rs Normal file
View File

@ -0,0 +1,159 @@
use std::borrow::Cow;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
async fn run(event_loop: EventLoop<()>, window: Window) {
println!("Waiting for NativeScreen");
loop {
match ndk_glue::native_window().as_ref() {
Some(_) => {
println!("NativeScreen Found:{:?}", ndk_glue::native_window());
break;
}
None => (),
}
}
let size = window.inner_size();
println!("Creating instance...");
let instance = wgpu::Instance::new(wgpu::Backends::all());
let surface = unsafe { instance.create_surface(&window) };
println!("Requesting adapter...");
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
// Request an adapter which can render to our surface
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropriate adapter");
// Create the logical device and command queue
println!("Creating logical device...");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
limits: wgpu::Limits::downlevel_webgl2_defaults()
.using_resolution(adapter.limits()),
},
None,
)
.await
.expect("Failed to create device");
println!("No more async code!");
// Load the shaders from disk
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let swapchain_format = wgpu::TextureFormat::Rgba8UnormSrgb;
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(swapchain_format.into())],
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
let mut config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: wgpu::CompositeAlphaMode::Auto,
};
surface.configure(&device, &config);
event_loop.run(move |event, _, control_flow| {
// Have the closure take ownership of the resources.
// `event_loop.run` never returns, therefore we must do this to ensure
// the resources are properly cleaned up.
let _ = (&instance, &adapter, &shader, &pipeline_layout);
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
// Reconfigure the surface with the new size
config.width = size.width;
config.height = size.height;
surface.configure(&device, &config);
// On macos the window needs to be redrawn manually after resizing
window.request_redraw();
}
Event::RedrawRequested(_) => {
let frame = surface
.get_current_texture()
.expect("Failed to acquire next swap chain texture");
let view = frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
store: true,
},
})],
depth_stencil_attachment: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.draw(0..3, 0..1);
}
queue.submit(Some(encoder.finish()));
frame.present();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => {}
}
});
}
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn main() {
let event_loop = EventLoop::new();
let window = winit::window::Window::new(&event_loop).unwrap();
pollster::block_on(run(event_loop, window));
}

View File

@ -0,0 +1,11 @@
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}

View File

@ -13,15 +13,27 @@ required-features = ["service"]
anyhow = { version = "1", optional = true }
byteorder = "1.4"
canary = { path = "../..", optional = true }
canary-wgpu = { path = "../../renderers/wgpu", optional = true }
futures-util = { version = "0.3", optional = true, features = ["io"] }
glium = { version = "0.32", optional = true}
mio = { version = "0.8", features = ["net", "os-poll"], optional = true }
mio-signals = { version = "0.2", optional = true }
parking_lot = { version = "0.12", optional = true}
parking_lot = { version = "0.12", optional = true }
pollster = { version = "0.2", optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
slab = { version = "0.4", optional = true}
slab = { version = "0.4", optional = true }
winit = { version = "0.27", optional = true }
[features]
async = ["dep:futures-util"]
service = ["dep:anyhow", "dep:canary", "dep:glium", "dep:mio", "dep:mio-signals", "dep:parking_lot", "dep:slab"]
service = [
"dep:anyhow",
"dep:canary",
"dep:canary-wgpu",
"dep:mio",
"dep:mio-signals",
"dep:parking_lot",
"dep:pollster",
"dep:slab",
"dep:winit",
]

View File

@ -1,7 +1,7 @@
// Copyright (c) 2022 Marceline Cramer
// SPDX-License-Identifier: AGPL-3.0-or-later
use glium::glutin::event_loop::EventLoopBuilder;
use winit::event_loop::EventLoopBuilder;
use canary_magpie::service::*;
use ipc::Ipc;

View File

@ -1,132 +0,0 @@
// Copyright (c) 2022 Marceline Cramer
// SPDX-License-Identifier: AGPL-3.0-or-later
use canary::{DrawCommand, Vec2, PX_PER_MM};
use glium::{program::ProgramCreationInput, Surface};
#[derive(Copy, Clone)]
pub struct Vertex {
pub position: [f32; 2],
pub color: [u8; 4],
}
glium::implement_vertex!(Vertex, position normalize(false), color normalize(true));
impl Vertex {
pub fn from_canary(size: Vec2, v: &canary::MeshVertex) -> Self {
// TODO do this in the vertex shader with a size uniform
let (r, g, b, a) = v.color.to_rgba_unmultiplied();
Self {
position: [
(v.position.x / size.x) * 2.0 - 1.0,
(v.position.y / size.y) * -2.0 + 1.0,
],
color: [r, g, b, a],
}
}
}
const VERTEX_SHADER_SRC: &str = r#"
#version 330
in vec2 position;
in vec4 color;
out vec4 frag_color;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
frag_color = color;
}
"#;
const FRAGMENT_SHADER_SRC: &str = r#"
#version 330
in vec4 frag_color;
out vec4 fb_color;
void main() {
fb_color = frag_color;
}
"#;
pub struct Graphics {
pub display: glium::Display,
pub program: glium::Program,
}
impl Graphics {
pub fn new(display: glium::Display) -> Self {
let program = glium::Program::new(
&display,
ProgramCreationInput::SourceCode {
vertex_shader: VERTEX_SHADER_SRC,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
geometry_shader: None,
fragment_shader: FRAGMENT_SHADER_SRC,
transform_feedback_varyings: None,
outputs_srgb: true, // don't automatically apply gamma correction
uses_point_size: false,
},
)
.unwrap();
Self { display, program }
}
pub fn draw(&mut self, commands: &[DrawCommand]) {
let mut joined_vs: Vec<Vertex> = Vec::new();
let mut joined_is = Vec::new();
let (width, height) = {
let size = self.display.gl_window().window().inner_size();
let (width, height) = (size.width as f32, size.height as f32);
(width * PX_PER_MM, height * PX_PER_MM)
};
let size = Vec2 {
x: width,
y: height,
};
for command in commands.iter() {
match command {
canary::DrawCommand::Mesh { vertices, indices } => {
let voff = joined_vs.len() as canary::MeshIndex;
joined_vs.extend(vertices.iter().map(|v| Vertex::from_canary(size, v)));
joined_is.extend(indices.iter().map(|i| i + voff));
}
_ => unimplemented!(),
}
}
let vertex_buffer = glium::VertexBuffer::new(&self.display, &joined_vs).unwrap();
let index_buffer = glium::IndexBuffer::new(
&self.display,
glium::index::PrimitiveType::TrianglesList,
&joined_is,
)
.unwrap();
let params = glium::DrawParameters {
blend: glium::Blend::alpha_blending(),
..Default::default()
};
let mut target = self.display.draw();
target.clear_color(0.0, 0.0, 0.0, 0.0);
target
.draw(
&vertex_buffer,
&index_buffer,
&self.program,
&glium::uniforms::EmptyUniforms,
&params,
)
.unwrap();
target.finish().unwrap();
}
}

View File

@ -1,6 +1,5 @@
// Copyright (c) 2022 Marceline Cramer
// SPDX-License-Identifier: AGPL-3.0-or-later
pub mod gl;
pub mod ipc;
pub mod window;

View File

@ -3,16 +3,17 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use canary::{CursorEventKind, Panel, Runtime, Vec2, PX_PER_MM};
use glium::backend::glutin::DisplayCreationError;
use glium::{glutin, Surface};
use glutin::event::{ElementState, Event, MouseButton, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget};
use glutin::window::WindowId;
use canary_wgpu::{wgpu, DrawTarget, Renderer};
use pollster::FutureExt;
use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, MouseButton, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget};
use winit::window::{WindowBuilder, WindowId};
use crate::service::gl::Graphics;
use crate::service::ipc::{IpcMessage, IpcMessageSender};
#[derive(Clone, Debug)]
@ -37,7 +38,12 @@ pub type WindowMessageSender = EventLoopProxy<WindowMessage>;
pub struct Window {
pub ipc_sender: IpcMessageSender,
pub ipc_id: usize,
pub graphics: Graphics,
pub window: winit::window::Window,
pub surface: wgpu::Surface,
pub surface_config: wgpu::SurfaceConfiguration,
pub device: Arc<wgpu::Device>,
pub queue: Arc<wgpu::Queue>,
pub renderer: Arc<Renderer>,
pub panel: Panel,
pub last_update: Instant,
pub cursor_pos: Vec2,
@ -49,21 +55,40 @@ impl Window {
ipc_sender: IpcMessageSender,
ipc_id: usize,
panel: Panel,
instance: &wgpu::Instance,
adapter: &wgpu::Adapter,
device: Arc<wgpu::Device>,
queue: Arc<wgpu::Queue>,
renderer: Arc<Renderer>,
event_loop: &EventLoopWindowTarget<WindowMessage>,
) -> Result<Self, DisplayCreationError> {
let wb = glutin::window::WindowBuilder::new()
) -> anyhow::Result<Self> {
let window = WindowBuilder::new()
.with_transparent(true)
.with_decorations(false);
let cb = glutin::ContextBuilder::new()
.with_vsync(true)
.with_multisampling(4);
let display = glium::Display::new(wb, cb, &event_loop)?;
let graphics = Graphics::new(display);
.build(&event_loop)?;
let surface = unsafe { instance.create_surface(&window) };
let last_update = Instant::now();
let size = window.inner_size();
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8Unorm,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
};
surface.configure(&device, &surface_config);
Ok(Self {
ipc_sender,
ipc_id,
graphics,
window,
surface,
surface_config,
device,
queue,
renderer,
panel,
last_update,
cursor_pos: Vec2::ZERO,
@ -72,11 +97,11 @@ impl Window {
}
pub fn get_id(&self) -> WindowId {
self.graphics.display.gl_window().window().id()
self.window.id()
}
pub fn request_redraw(&mut self) {
self.graphics.display.gl_window().window().request_redraw();
self.window.request_redraw();
}
/// Receives all messages from the script and forwards them to IPC.
@ -98,8 +123,24 @@ impl Window {
}
pub fn draw(&mut self) {
let output = self.surface.get_current_texture().unwrap();
let view = output.texture.create_view(&Default::default());
let commands = self.panel.draw();
self.graphics.draw(&commands);
let size = Vec2::new(
self.surface_config.width as f32,
self.surface_config.height as f32,
) * PX_PER_MM;
let target = DrawTarget {
texture: &view,
size,
};
self.renderer.render(target, &commands);
output.present();
self.recv_messages();
}
@ -108,17 +149,23 @@ impl Window {
self.recv_messages();
}
pub fn resize(&mut self, new_size: Vec2) {
self.panel.on_resize(new_size);
pub fn resize(&mut self, new_size: PhysicalSize<u32>) {
let mm = Vec2::new(new_size.width as f32, new_size.height as f32) * PX_PER_MM;
self.panel.on_resize(mm);
self.recv_messages();
self.window.request_redraw();
if new_size.width > 0 && new_size.height > 0 {
self.surface_config.width = new_size.width;
self.surface_config.height = new_size.height;
self.surface.configure(&self.device, &self.surface_config);
}
}
pub fn on_event(&mut self, event: WindowEvent) {
match event {
WindowEvent::Resized(size) => {
self.resize(Vec2::new(size.width as f32, size.height as f32) * PX_PER_MM);
self.request_redraw()
}
WindowEvent::Resized(size) => self.resize(size),
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => self.resize(*new_inner_size),
WindowEvent::CursorMoved { position, .. } => {
let x = position.x as f32 * PX_PER_MM;
let y = position.y as f32 * PX_PER_MM;
@ -162,6 +209,11 @@ pub struct WindowStore {
pub ipc_to_window: HashMap<usize, WindowId>,
pub windows: HashMap<WindowId, Window>,
pub runtime: Runtime,
pub instance: wgpu::Instance,
pub adapter: wgpu::Adapter,
pub device: Arc<wgpu::Device>,
pub queue: Arc<wgpu::Queue>,
pub renderer: Arc<Renderer>,
}
impl WindowStore {
@ -169,11 +221,42 @@ impl WindowStore {
let backend = canary::backend::make_default_backend().unwrap();
let runtime = Runtime::new(backend).unwrap();
let instance = wgpu::Instance::new(wgpu::Backends::all());
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptionsBase {
power_preference: wgpu::PowerPreference::HighPerformance,
force_fallback_adapter: false,
compatible_surface: None,
})
.block_on()
.unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
},
None,
)
.block_on()
.unwrap();
let device = Arc::new(device);
let queue = Arc::new(queue);
let renderer = Renderer::new(device.to_owned(), queue.to_owned());
let renderer = Arc::new(renderer);
Self {
ipc_sender,
ipc_to_window: Default::default(),
windows: Default::default(),
runtime,
instance,
adapter,
device,
queue,
renderer,
}
}
@ -199,7 +282,19 @@ impl WindowStore {
let module = std::fs::read(script)?;
let mut script = self.runtime.load_module(&module)?;
let panel = script.create_panel(&protocol, vec![])?;
let window = Window::new(self.ipc_sender.to_owned(), id, panel, &event_loop)?;
let window = Window::new(
self.ipc_sender.to_owned(),
id,
panel,
&self.instance,
&self.adapter,
self.device.to_owned(),
self.queue.to_owned(),
self.renderer.to_owned(),
&event_loop,
)?;
let window_id = window.get_id();
self.windows.insert(window_id, window);
self.ipc_to_window.insert(id, window_id);

View File

@ -6,4 +6,5 @@ license = "AGPL-3.0-or-later"
[dependencies]
canary = { path = "../.." }
eframe = "0.18"
canary-wgpu = { path = "../../renderers/wgpu" }
eframe = { version = "0.19", features = ["wgpu"] }

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use canary::{CursorEventKind, Panel, Runtime, Script, PX_PER_MM};
use canary_wgpu::Renderer;
use eframe::egui;
use std::time::Instant;
@ -13,20 +14,19 @@ fn main() {
.to_owned();
let native_options = eframe::NativeOptions {
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"Canary Sandbox",
native_options,
Box::new(move |cc| {
cc.egui_ctx.set_visuals(egui::Visuals::dark());
Box::new(App::new(&module_path))
}),
Box::new(move |cc| Box::new(App::new(cc, &module_path))),
);
}
struct App {
renderer: Renderer,
script: Script,
panels: Vec<PanelWindow>,
next_idx: usize,
@ -37,13 +37,23 @@ struct App {
}
impl App {
pub fn new(module_path: &str) -> Self {
pub fn new(cc: &eframe::CreationContext, module_path: &str) -> Self {
cc.egui_ctx.set_visuals(egui::Visuals::dark());
let wgpu_state = cc
.wgpu_render_state
.as_ref()
.expect("eframe should be using wgpu but has no wgpu render state");
let renderer = Renderer::new(wgpu_state.device.to_owned(), wgpu_state.queue.to_owned());
let backend = canary::backend::make_default_backend().unwrap();
let runtime = Runtime::new(backend).unwrap();
let module = std::fs::read(module_path).unwrap();
let script = runtime.load_module(&module).unwrap();
Self {
renderer,
script,
panels: vec![],
next_idx: 0,

View File

@ -0,0 +1,9 @@
[package]
name = "canary-wgpu"
version = "0.1.0"
edition = "2021"
[dependencies]
bytemuck = { version = "1", features = ["derive"] }
canary = { path = "../.." }
wgpu = "0.13.1"

162
renderers/wgpu/src/lib.rs Normal file
View File

@ -0,0 +1,162 @@
use std::sync::Arc;
use bytemuck::{Pod, Zeroable};
use canary::{DrawCommand, Vec2};
use wgpu::{util::*, *};
pub use wgpu;
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct Vertex {
pub position: [f32; 2],
pub color: u32,
}
impl Vertex {
pub fn from_canary(size: Vec2, v: &canary::MeshVertex) -> Self {
// TODO do this in the vertex shader with a size uniform
Self {
position: [
(v.position.x / size.x) * 2.0 - 1.0,
(v.position.y / size.y) * -2.0 + 1.0,
],
color: v.color.0,
}
}
pub const BUFFER_LAYOUT: VertexBufferLayout<'static> = VertexBufferLayout {
array_stride: std::mem::size_of::<Self>() as BufferAddress,
step_mode: VertexStepMode::Vertex,
attributes: &[
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 0,
shader_location: 0,
},
VertexAttribute {
format: VertexFormat::Unorm8x4,
offset: std::mem::size_of::<[f32; 2]>() as BufferAddress,
shader_location: 1,
},
],
};
}
pub struct Renderer {
device: Arc<Device>,
queue: Arc<Queue>,
pipeline_layout: PipelineLayout,
pipeline: RenderPipeline,
}
impl Renderer {
pub fn new(device: Arc<Device>, queue: Arc<Queue>) -> Self {
let shader = device.create_shader_module(include_wgsl!("shader.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("Canary Pipeline Layout"),
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
label: Some("Canary Render Pipeline"),
layout: Some(&pipeline_layout),
vertex: VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[Vertex::BUFFER_LAYOUT],
},
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: Default::default(),
fragment: Some(FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format: TextureFormat::Bgra8Unorm,
blend: Some(BlendState::ALPHA_BLENDING),
write_mask: ColorWrites::ALL,
})],
}),
multiview: None,
});
Self {
device,
queue,
pipeline_layout,
pipeline,
}
}
pub fn render(&self, target: DrawTarget<'_>, cmds: &[DrawCommand]) {
let size = target.size;
let mut joined_vs = Vec::new();
let mut joined_is = Vec::new();
for cmd in cmds.iter() {
match cmd {
canary::DrawCommand::Mesh { vertices, indices } => {
let voff = joined_vs.len() as canary::MeshIndex;
joined_vs.extend(vertices.iter().map(|v| Vertex::from_canary(size, v)));
joined_is.extend(indices.iter().map(|i| i + voff));
}
_ => unimplemented!(),
}
}
let vertex_buffer = self.device.create_buffer_init(&BufferInitDescriptor {
label: Some("Canary Vertex Buffer"),
contents: bytemuck::cast_slice(joined_vs.as_slice()),
usage: BufferUsages::VERTEX,
});
let index_buffer = self.device.create_buffer_init(&BufferInitDescriptor {
label: Some("Canary Index Buffer"),
contents: bytemuck::cast_slice(joined_is.as_slice()),
usage: BufferUsages::INDEX,
});
let mut cmd_encoder = self.device.create_command_encoder(&Default::default());
let mut rp = cmd_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("Canary Render Pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: target.texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::TRANSPARENT),
store: true,
},
})],
depth_stencil_attachment: None,
});
let indices = 0..(joined_is.len() as u32);
rp.set_vertex_buffer(0, vertex_buffer.slice(..));
rp.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint32);
rp.set_pipeline(&self.pipeline);
rp.draw_indexed(indices, 0, 0..1);
drop(rp);
let cmd_buf = cmd_encoder.finish();
self.queue.submit(std::iter::once(cmd_buf));
}
}
pub struct DrawTarget<'a> {
pub texture: &'a TextureView,
pub size: Vec2,
}

View File

@ -0,0 +1,20 @@
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
};
@vertex
fn vs_main(
@location(0) position: vec2<f32>,
@location(1) color: vec4<f32>,
) -> VertexOutput {
var result: VertexOutput;
result.position = vec4<f32>(position, 0.0, 1.0);
result.color = color;
return result;
}
@fragment
fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
return vertex.color.abgr;
}