mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
give TEA example graphics
This commit is contained in:
parent
c59aa22402
commit
8f91574629
2
examples/tea/.gitignore
vendored
2
examples/tea/.gitignore
vendored
@ -1 +1 @@
|
||||
echo
|
||||
hello-rust
|
||||
|
@ -1,14 +1,12 @@
|
||||
app "hello"
|
||||
app "hello-rust"
|
||||
packages { pf: "platform" }
|
||||
imports []
|
||||
provides [ render ] to pf
|
||||
|
||||
render :
|
||||
{ width : F32, height : F32 } ->
|
||||
[
|
||||
Rectangle { top : F32, left : F32, bottom : F32, right : F32 },
|
||||
Circle { top : F32, left : F32, radius : F32 },
|
||||
# Text { top : F32, left : F32, text : Str },
|
||||
]
|
||||
render = \window ->
|
||||
Rectangle { top: 10, left: 10, bottom: 100, right: 100 }
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
name = "World!"
|
||||
|
||||
"\(hi), \(name)!\n"
|
||||
|
||||
render = greeting
|
||||
|
@ -1,8 +0,0 @@
|
||||
# Command Line Interface (CLI) Example
|
||||
|
||||
This is an example of how to make an extremely basic CLI in Roc.
|
||||
|
||||
There's not currently much documentation for the CLI platform (which also doesn't support many operations at this point!)
|
||||
but you can look at [the modules it includes](platform) - for example,
|
||||
multiple other modules use the [`Task`](platform/Task.roc) module, including the
|
||||
[`Stdin`](platform/Stdin.roc) and [`Stdout`](platform/Stdout.roc) modules.
|
3029
examples/tea/platform/Cargo.lock
generated
3029
examples/tea/platform/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,13 @@ version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
|
||||
links = "app"
|
||||
|
||||
# Needed to be able to run on non-Windows systems for some reason. Without this, cargo panics with:
|
||||
#
|
||||
# error: DX12 API enabled on non-Windows OS. If your project is not using resolver="2" in Cargo.toml, it should.
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
name = "host"
|
||||
path = "src/lib.rs"
|
||||
@ -19,5 +23,54 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
libc = "0.2"
|
||||
arrayvec = "0.7.2"
|
||||
page_size = "0.4.2"
|
||||
# once winit 0.26 is out, check if copypasta can be updated simultaneously so they use the same versions for their dependencies. This will save build time.
|
||||
winit = "0.25.0"
|
||||
wgpu = "0.11.0"
|
||||
wgpu_glyph = "0.15.1"
|
||||
glyph_brush = "0.7.2"
|
||||
log = "0.4.14"
|
||||
env_logger = "0.9.0"
|
||||
futures = "0.3.17"
|
||||
cgmath = "0.18.0"
|
||||
snafu = { version = "0.6.10", features = ["backtraces"] }
|
||||
colored = "2.0.0"
|
||||
pest = "2.1.3"
|
||||
pest_derive = "2.1.0"
|
||||
copypasta = "0.7.1"
|
||||
palette = "0.6.0"
|
||||
confy = { git = 'https://github.com/rust-cli/confy', features = [
|
||||
"yaml_conf"
|
||||
], default-features = false }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
nonempty = "0.7.0"
|
||||
fs_extra = "1.2.0"
|
||||
rodio = { version = "0.14.0", optional = true } # to play sounds
|
||||
threadpool = "1.8.1"
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
# confy is currently unused but should not be removed
|
||||
normal = ["confy"]
|
||||
#development = []
|
||||
#build = []
|
||||
|
||||
[features]
|
||||
default = []
|
||||
with_sound = ["rodio"]
|
||||
|
||||
[dependencies.bytemuck]
|
||||
version = "1.7.2"
|
||||
features = ["derive"]
|
||||
|
||||
[workspace]
|
||||
|
||||
# Optimizations based on https://deterministic.space/high-performance-rust.html
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
# debug = true # enable when profiling
|
||||
[profile.bench]
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
@ -1,24 +1,10 @@
|
||||
platform "examples/tea"
|
||||
requires {} {
|
||||
render :
|
||||
{ width : F32, height : F32 } ->
|
||||
[
|
||||
Rectangle { top : F32, left : F32, bottom : F32, right : F32 },
|
||||
Circle { top : F32, left : F32, radius : F32 },
|
||||
# Text { top : F32, left : F32, text : Str },
|
||||
]
|
||||
}
|
||||
platform "examples/hello-world"
|
||||
requires {} { render : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ renderForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
renderForHost :
|
||||
{ width : F32, height : F32 } ->
|
||||
[
|
||||
Rectangle { top : F32, left : F32, bottom : F32, right : F32 },
|
||||
Circle { top : F32, left : F32, radius : F32 },
|
||||
# Text { top : F32, left : F32, text : Str },
|
||||
]
|
||||
renderForHost : Str
|
||||
renderForHost = render
|
||||
|
@ -1,6 +0,0 @@
|
||||
interface Stdin
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task ]
|
||||
|
||||
line : Task.Task Str *
|
||||
line = Effect.after Effect.getLine Task.succeed# TODO FIXME Effect.getLine should suffice
|
@ -1,8 +0,0 @@
|
||||
interface Stdout
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
|
||||
# line : Str -> Task.Task {} *
|
||||
# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
line : Str -> Task {} *
|
||||
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
@ -1,94 +0,0 @@
|
||||
interface Task
|
||||
exposes [ Task, succeed, fail, await, map, onFail, attempt, forever, loop ]
|
||||
imports [ fx.Effect ]
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
forever : Task val err -> Task * err
|
||||
forever = \task ->
|
||||
looper = \{ } ->
|
||||
task
|
||||
|> Effect.map
|
||||
\res ->
|
||||
when res is
|
||||
Ok _ ->
|
||||
Step {}
|
||||
|
||||
Err e ->
|
||||
Done (Err e)
|
||||
|
||||
Effect.loop {} looper
|
||||
|
||||
loop : state, (state -> Task [ Step state, Done done ] err) -> Task done err
|
||||
loop = \state, step ->
|
||||
looper = \current ->
|
||||
step current
|
||||
|> Effect.map
|
||||
\res ->
|
||||
when res is
|
||||
Ok (Step newState) ->
|
||||
Step newState
|
||||
|
||||
Ok (Done result) ->
|
||||
Done (Ok result)
|
||||
|
||||
Err e ->
|
||||
Done (Err e)
|
||||
|
||||
Effect.loop state looper
|
||||
|
||||
succeed : val -> Task val *
|
||||
succeed = \val ->
|
||||
Effect.always (Ok val)
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \val ->
|
||||
Effect.always (Err val)
|
||||
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok ok ->
|
||||
transform (Ok ok)
|
||||
|
||||
Err err ->
|
||||
transform (Err err)
|
||||
|
||||
await : Task a err, (a -> Task b err) -> Task b err
|
||||
await = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a ->
|
||||
transform a
|
||||
|
||||
Err err ->
|
||||
Task.fail err
|
||||
|
||||
onFail : Task ok a, (a -> Task ok b) -> Task ok b
|
||||
onFail = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a ->
|
||||
Task.succeed a
|
||||
|
||||
Err err ->
|
||||
transform err
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a ->
|
||||
Task.succeed (transform a)
|
||||
|
||||
Err err ->
|
||||
Task.fail err
|
@ -1,3 +1,3 @@
|
||||
extern int rust_main();
|
||||
|
||||
int main() { return rust_main(); }
|
||||
int main() { return rust_main(); }
|
31
examples/tea/platform/src/graphics/colors.rs
Normal file
31
examples/tea/platform/src/graphics/colors.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use palette::{FromColor, Hsv, Srgb};
|
||||
|
||||
pub type RgbaTup = (f32, f32, f32, f32);
|
||||
pub const WHITE: RgbaTup = (1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
pub fn to_wgpu_color((r, g, b, a): RgbaTup) -> wgpu::Color {
|
||||
wgpu::Color {
|
||||
r: r as f64,
|
||||
g: g as f64,
|
||||
b: b as f64,
|
||||
a: a as f64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_slice((r, g, b, a): RgbaTup) -> [f32; 4] {
|
||||
[r, g, b, a]
|
||||
}
|
||||
|
||||
pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> RgbaTup {
|
||||
from_hsba(hue, saturation, brightness, 1.0)
|
||||
}
|
||||
|
||||
pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> RgbaTup {
|
||||
let rgb = Srgb::from_color(Hsv::new(
|
||||
hue as f32,
|
||||
(saturation as f32) / 100.0,
|
||||
(brightness as f32) / 100.0,
|
||||
));
|
||||
|
||||
(rgb.red, rgb.green, rgb.blue, alpha)
|
||||
}
|
168
examples/tea/platform/src/graphics/lowlevel/buffer.rs
Normal file
168
examples/tea/platform/src/graphics/lowlevel/buffer.rs
Normal file
@ -0,0 +1,168 @@
|
||||
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen - license information can be found in the COPYRIGHT
|
||||
// file in the root directory of this distribution.
|
||||
//
|
||||
// Thank you, Benjamin!
|
||||
use super::vertex::Vertex;
|
||||
use crate::graphics::colors::to_slice;
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
||||
|
||||
pub struct QuadBufferBuilder {
|
||||
vertex_data: Vec<Vertex>,
|
||||
index_data: Vec<u32>,
|
||||
current_quad: u32,
|
||||
}
|
||||
|
||||
impl QuadBufferBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vertex_data: Vec::new(),
|
||||
index_data: Vec::new(),
|
||||
current_quad: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_rect(self, rect: &Rect) -> Self {
|
||||
let coords = rect.top_left_coords;
|
||||
self.push_quad(
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.x + rect.width,
|
||||
coords.y + rect.height,
|
||||
to_slice(rect.color),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn push_quad(
|
||||
mut self,
|
||||
min_x: f32,
|
||||
min_y: f32,
|
||||
max_x: f32,
|
||||
max_y: f32,
|
||||
color: [f32; 4],
|
||||
) -> Self {
|
||||
self.vertex_data.extend(&[
|
||||
Vertex {
|
||||
position: (min_x, min_y).into(),
|
||||
color,
|
||||
},
|
||||
Vertex {
|
||||
position: (max_x, min_y).into(),
|
||||
color,
|
||||
},
|
||||
Vertex {
|
||||
position: (max_x, max_y).into(),
|
||||
color,
|
||||
},
|
||||
Vertex {
|
||||
position: (min_x, max_y).into(),
|
||||
color,
|
||||
},
|
||||
]);
|
||||
self.index_data.extend(&[
|
||||
self.current_quad * 4,
|
||||
self.current_quad * 4 + 1,
|
||||
self.current_quad * 4 + 2,
|
||||
self.current_quad * 4,
|
||||
self.current_quad * 4 + 2,
|
||||
self.current_quad * 4 + 3,
|
||||
]);
|
||||
self.current_quad += 1;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) {
|
||||
(
|
||||
StagingBuffer::new(device, &self.vertex_data),
|
||||
StagingBuffer::new(device, &self.index_data),
|
||||
self.index_data.len() as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QuadBufferBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RectBuffers {
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub index_buffer: wgpu::Buffer,
|
||||
pub num_rects: u32,
|
||||
}
|
||||
|
||||
pub fn create_rect_buffers(
|
||||
gpu_device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
rects: &[Rect],
|
||||
) -> RectBuffers {
|
||||
let nr_of_rects = rects.len() as u64;
|
||||
|
||||
let vertex_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: Vertex::SIZE * 4 * nr_of_rects,
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let u32_size = std::mem::size_of::<u32>() as wgpu::BufferAddress;
|
||||
|
||||
let index_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
size: u32_size * 6 * nr_of_rects,
|
||||
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let num_rects = {
|
||||
let mut quad_buffer_builder = QuadBufferBuilder::new();
|
||||
for rect in rects {
|
||||
quad_buffer_builder = quad_buffer_builder.push_rect(rect);
|
||||
}
|
||||
|
||||
let (stg_vertex, stg_index, num_indices) = quad_buffer_builder.build(gpu_device);
|
||||
|
||||
stg_vertex.copy_to_buffer(encoder, &vertex_buffer);
|
||||
stg_index.copy_to_buffer(encoder, &index_buffer);
|
||||
num_indices
|
||||
};
|
||||
|
||||
RectBuffers {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_rects,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StagingBuffer {
|
||||
buffer: wgpu::Buffer,
|
||||
size: wgpu::BufferAddress,
|
||||
}
|
||||
|
||||
impl StagingBuffer {
|
||||
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
|
||||
StagingBuffer {
|
||||
buffer: device.create_buffer_init(&BufferInitDescriptor {
|
||||
contents: bytemuck::cast_slice(data),
|
||||
usage: wgpu::BufferUsages::COPY_SRC,
|
||||
label: Some("Staging Buffer"),
|
||||
}),
|
||||
size: size_of_slice(data) as wgpu::BufferAddress,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) {
|
||||
encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size)
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen - license information can be found in the COPYRIGHT
|
||||
// file in the root directory of this distribution.
|
||||
//
|
||||
// Thank you, Benjamin!
|
||||
pub fn size_of_slice<T: Sized>(slice: &[T]) -> usize {
|
||||
std::mem::size_of::<T>() * slice.len()
|
||||
}
|
4
examples/tea/platform/src/graphics/lowlevel/mod.rs
Normal file
4
examples/tea/platform/src/graphics/lowlevel/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod buffer;
|
||||
pub mod ortho;
|
||||
pub mod pipelines;
|
||||
pub mod vertex;
|
118
examples/tea/platform/src/graphics/lowlevel/ortho.rs
Normal file
118
examples/tea/platform/src/graphics/lowlevel/ortho.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use cgmath::{Matrix4, Ortho};
|
||||
use wgpu::util::DeviceExt;
|
||||
use wgpu::{
|
||||
BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, Buffer,
|
||||
ShaderStages,
|
||||
};
|
||||
|
||||
// orthographic projection is used to transform pixel coords to the coordinate system used by wgpu
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Uniforms {
|
||||
// We can't use cgmath with bytemuck directly so we'll have
|
||||
// to convert the Matrix4 into a 4x4 f32 array
|
||||
ortho: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
fn new(w: u32, h: u32) -> Self {
|
||||
let ortho: Matrix4<f32> = Ortho::<f32> {
|
||||
left: 0.0,
|
||||
right: w as f32,
|
||||
bottom: h as f32,
|
||||
top: 0.0,
|
||||
near: -1.0,
|
||||
far: 1.0,
|
||||
}
|
||||
.into();
|
||||
Self {
|
||||
ortho: ortho.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update orthographic buffer according to new window size
|
||||
pub fn update_ortho_buffer(
|
||||
inner_width: u32,
|
||||
inner_height: u32,
|
||||
gpu_device: &wgpu::Device,
|
||||
ortho_buffer: &Buffer,
|
||||
cmd_queue: &wgpu::Queue,
|
||||
) {
|
||||
let new_uniforms = Uniforms::new(inner_width, inner_height);
|
||||
|
||||
let new_ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Ortho uniform buffer"),
|
||||
contents: bytemuck::cast_slice(&[new_uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_SRC,
|
||||
});
|
||||
|
||||
// get a command encoder for the current frame
|
||||
let mut encoder = gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Resize"),
|
||||
});
|
||||
|
||||
// overwrite the new buffer over the old one
|
||||
encoder.copy_buffer_to_buffer(
|
||||
&new_ortho_buffer,
|
||||
0,
|
||||
ortho_buffer,
|
||||
0,
|
||||
(std::mem::size_of::<Uniforms>() * vec![new_uniforms].as_slice().len())
|
||||
as wgpu::BufferAddress,
|
||||
);
|
||||
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrthoResources {
|
||||
pub buffer: Buffer,
|
||||
pub bind_group_layout: BindGroupLayout,
|
||||
pub bind_group: BindGroup,
|
||||
}
|
||||
|
||||
pub fn init_ortho(
|
||||
inner_width: u32,
|
||||
inner_height: u32,
|
||||
gpu_device: &wgpu::Device,
|
||||
) -> OrthoResources {
|
||||
let uniforms = Uniforms::new(inner_width, inner_height);
|
||||
|
||||
let ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Ortho uniform buffer"),
|
||||
contents: bytemuck::cast_slice(&[uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
// bind groups consist of extra resources that are provided to the shaders
|
||||
let ortho_bind_group_layout = gpu_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
label: Some("Ortho bind group layout"),
|
||||
});
|
||||
|
||||
let ortho_bind_group = gpu_device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &ortho_bind_group_layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: ortho_buffer.as_entire_binding(),
|
||||
}],
|
||||
label: Some("Ortho bind group"),
|
||||
});
|
||||
|
||||
OrthoResources {
|
||||
buffer: ortho_buffer,
|
||||
bind_group_layout: ortho_bind_group_layout,
|
||||
bind_group: ortho_bind_group,
|
||||
}
|
||||
}
|
70
examples/tea/platform/src/graphics/lowlevel/pipelines.rs
Normal file
70
examples/tea/platform/src/graphics/lowlevel/pipelines.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use super::ortho::{init_ortho, OrthoResources};
|
||||
use super::vertex::Vertex;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct RectResources {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
pub ortho: OrthoResources,
|
||||
}
|
||||
|
||||
pub fn make_rect_pipeline(
|
||||
gpu_device: &wgpu::Device,
|
||||
surface_config: &wgpu::SurfaceConfiguration,
|
||||
) -> RectResources {
|
||||
let ortho = init_ortho(surface_config.width, surface_config.height, gpu_device);
|
||||
|
||||
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&ortho.bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
label: Some("Rectangle pipeline layout"),
|
||||
});
|
||||
let pipeline = create_render_pipeline(
|
||||
gpu_device,
|
||||
&pipeline_layout,
|
||||
surface_config.format,
|
||||
&wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/shader.wgsl"))),
|
||||
},
|
||||
);
|
||||
|
||||
RectResources { pipeline, ortho }
|
||||
}
|
||||
|
||||
pub fn create_render_pipeline(
|
||||
device: &wgpu::Device,
|
||||
layout: &wgpu::PipelineLayout,
|
||||
color_format: wgpu::TextureFormat,
|
||||
shader_module_desc: &wgpu::ShaderModuleDescriptor,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let shader = device.create_shader_module(shader_module_desc);
|
||||
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render pipeline"),
|
||||
layout: Some(layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[Vertex::DESC],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: color_format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
},
|
||||
alpha: wgpu::BlendComponent::REPLACE,
|
||||
}),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
}],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
})
|
||||
}
|
37
examples/tea/platform/src/graphics/lowlevel/vertex.rs
Normal file
37
examples/tea/platform/src/graphics/lowlevel/vertex.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// Taken from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen - license information can be found in the COPYRIGHT
|
||||
// file in the root directory of this distribution.
|
||||
//
|
||||
// Thank you, Benjamin!
|
||||
use cgmath::Vector2;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vertex {
|
||||
pub position: Vector2<f32>,
|
||||
pub color: [f32; 4],
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Vertex {}
|
||||
unsafe impl bytemuck::Zeroable for Vertex {}
|
||||
|
||||
impl Vertex {
|
||||
pub const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||
pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||
array_stride: Self::SIZE,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
// position
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
// color
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
4
examples/tea/platform/src/graphics/mod.rs
Normal file
4
examples/tea/platform/src/graphics/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod colors;
|
||||
pub mod lowlevel;
|
||||
pub mod primitives;
|
||||
pub mod style;
|
2
examples/tea/platform/src/graphics/primitives/mod.rs
Normal file
2
examples/tea/platform/src/graphics/primitives/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod rect;
|
||||
pub mod text;
|
9
examples/tea/platform/src/graphics/primitives/rect.rs
Normal file
9
examples/tea/platform/src/graphics/primitives/rect.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use cgmath::Vector2;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Rect {
|
||||
pub top_left_coords: Vector2<f32>,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub color: (f32, f32, f32, f32),
|
||||
}
|
162
examples/tea/platform/src/graphics/primitives/text.rs
Normal file
162
examples/tea/platform/src/graphics/primitives/text.rs
Normal file
@ -0,0 +1,162 @@
|
||||
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen - license information can be found in the COPYRIGHT
|
||||
// file in the root directory of this distribution.
|
||||
//
|
||||
// Thank you, Benjamin!
|
||||
|
||||
use super::rect::Rect;
|
||||
use crate::graphics::colors;
|
||||
use crate::graphics::colors::RgbaTup;
|
||||
use crate::graphics::style::DEFAULT_FONT_SIZE;
|
||||
use ab_glyph::{FontArc, Glyph, InvalidFont};
|
||||
use cgmath::{Vector2, Vector4};
|
||||
use glyph_brush::OwnedSection;
|
||||
use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Text<'a> {
|
||||
pub position: Vector2<f32>,
|
||||
pub area_bounds: Vector2<f32>,
|
||||
pub color: RgbaTup,
|
||||
pub text: &'a str,
|
||||
pub size: f32,
|
||||
pub visible: bool,
|
||||
pub centered: bool,
|
||||
}
|
||||
|
||||
impl<'a> Default for Text<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
position: (0.0, 0.0).into(),
|
||||
area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
|
||||
color: colors::WHITE,
|
||||
text: "",
|
||||
size: DEFAULT_FONT_SIZE,
|
||||
visible: true,
|
||||
centered: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// necessary to get dimensions for caret
|
||||
pub fn example_code_glyph_rect(glyph_brush: &mut GlyphBrush<()>, font_size: f32) -> Rect {
|
||||
let code_text = Text {
|
||||
position: (0.0, 0.0).into(),
|
||||
area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
|
||||
color: colors::WHITE,
|
||||
text: "a",
|
||||
size: font_size,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let layout = layout_from_text(&code_text);
|
||||
|
||||
let section = section_from_text(&code_text, layout);
|
||||
|
||||
let mut glyph_section_iter = glyph_brush.glyphs_custom_layout(section, &layout);
|
||||
|
||||
if let Some(glyph) = glyph_section_iter.next() {
|
||||
glyph_to_rect(glyph)
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout_from_text(text: &Text) -> wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker> {
|
||||
wgpu_glyph::Layout::default().h_align(if text.centered {
|
||||
wgpu_glyph::HorizontalAlign::Center
|
||||
} else {
|
||||
wgpu_glyph::HorizontalAlign::Left
|
||||
})
|
||||
}
|
||||
|
||||
fn section_from_text<'a>(
|
||||
text: &'a Text,
|
||||
layout: wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker>,
|
||||
) -> wgpu_glyph::Section<'a> {
|
||||
Section {
|
||||
screen_position: text.position.into(),
|
||||
bounds: text.area_bounds.into(),
|
||||
layout,
|
||||
..Section::default()
|
||||
}
|
||||
.add_text(
|
||||
wgpu_glyph::Text::new(text.text)
|
||||
.with_color(Vector4::from(text.color))
|
||||
.with_scale(text.size),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn owned_section_from_text(text: &Text) -> OwnedSection {
|
||||
let layout = layout_from_text(text);
|
||||
|
||||
OwnedSection {
|
||||
screen_position: text.position.into(),
|
||||
bounds: text.area_bounds.into(),
|
||||
layout,
|
||||
..OwnedSection::default()
|
||||
}
|
||||
.add_text(
|
||||
glyph_brush::OwnedText::new(text.text)
|
||||
.with_color(Vector4::from(text.color))
|
||||
.with_scale(text.size),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn owned_section_from_glyph_texts(
|
||||
text: Vec<glyph_brush::OwnedText>,
|
||||
screen_position: (f32, f32),
|
||||
area_bounds: (f32, f32),
|
||||
layout: wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker>,
|
||||
) -> glyph_brush::OwnedSection {
|
||||
glyph_brush::OwnedSection {
|
||||
screen_position,
|
||||
bounds: area_bounds,
|
||||
layout,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) {
|
||||
let layout = layout_from_text(text);
|
||||
|
||||
let section = section_from_text(text, layout);
|
||||
|
||||
glyph_brush.queue(section.clone());
|
||||
}
|
||||
|
||||
fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect {
|
||||
let position = glyph.glyph.position;
|
||||
let px_scale = glyph.glyph.scale;
|
||||
let width = glyph_width(&glyph.glyph);
|
||||
let height = px_scale.y;
|
||||
let top_y = glyph_top_y(&glyph.glyph);
|
||||
|
||||
Rect {
|
||||
top_left_coords: [position.x, top_y].into(),
|
||||
width,
|
||||
height,
|
||||
color: colors::WHITE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glyph_top_y(glyph: &Glyph) -> f32 {
|
||||
let height = glyph.scale.y;
|
||||
|
||||
glyph.position.y - height * 0.75
|
||||
}
|
||||
|
||||
pub fn glyph_width(glyph: &Glyph) -> f32 {
|
||||
glyph.scale.x * 0.4765
|
||||
}
|
||||
|
||||
pub fn build_glyph_brush(
|
||||
gpu_device: &wgpu::Device,
|
||||
render_format: wgpu::TextureFormat,
|
||||
) -> Result<GlyphBrush<()>, InvalidFont> {
|
||||
let inconsolata = FontArc::try_from_slice(include_bytes!(
|
||||
"../../../../../../editor/Inconsolata-Regular.ttf"
|
||||
))?;
|
||||
|
||||
Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format))
|
||||
}
|
31
examples/tea/platform/src/graphics/shaders/shader.wgsl
Normal file
31
examples/tea/platform/src/graphics/shaders/shader.wgsl
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
struct VertexOutput {
|
||||
[[location(0)]] color: vec4<f32>;
|
||||
[[builtin(position)]] position: vec4<f32>;
|
||||
};
|
||||
|
||||
[[block]]
|
||||
struct Globals {
|
||||
ortho: mat4x4<f32>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]]
|
||||
var<uniform> u_globals: Globals;
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main(
|
||||
[[location(0)]] in_position: vec2<f32>,
|
||||
[[location(1)]] in_color: vec4<f32>,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
out.position = u_globals.ortho * vec4<f32>(in_position, 0.0, 1.0);
|
||||
out.color = in_color;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
|
||||
return in.color;
|
||||
}
|
1
examples/tea/platform/src/graphics/style.rs
Normal file
1
examples/tea/platform/src/graphics/style.rs
Normal file
@ -0,0 +1 @@
|
||||
pub const DEFAULT_FONT_SIZE: f32 = 30.0;
|
@ -1,34 +1,21 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use libc;
|
||||
use roc_std::RocStr;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
mod graphics;
|
||||
mod tea;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__renderForHost_1_exposed"]
|
||||
fn roc_render(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__renderForHost_size"]
|
||||
fn roc_render_size() -> i64;
|
||||
|
||||
#[link_name = "roc__renderForHost_1_Fx_caller"]
|
||||
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[link_name = "roc__renderForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
||||
#[link_name = "roc__renderForHost_1_Fx_result_size"]
|
||||
fn size_Fx_result() -> i64;
|
||||
fn roc_render() -> RocStr;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -38,12 +25,12 @@ pub unsafe extern "C" fn roc_realloc(
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -71,56 +58,10 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
let size = unsafe { roc_render_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let roc_str = unsafe { roc_render() };
|
||||
|
||||
unsafe {
|
||||
// TODO allocate on the stack if it's under a certain size
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
roc_render(buffer);
|
||||
|
||||
let result = call_the_closure(buffer);
|
||||
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
dbg!(result);
|
||||
};
|
||||
tea::render(roc_str);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
||||
let size = size_Fx_result() as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let buffer = std::alloc::alloc(layout) as *mut u8;
|
||||
|
||||
call_Fx(
|
||||
// This flags pointer will never get dereferenced
|
||||
MaybeUninit::uninit().as_ptr(),
|
||||
closure_data_ptr as *const u8,
|
||||
buffer as *mut u8,
|
||||
);
|
||||
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getLine() -> RocStr {
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
let stdin = io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocStr::from_slice(line1.as_bytes())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_putLine(line: ManuallyDrop<RocStr>) {
|
||||
let bytes = line.as_slice();
|
||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||
println!("{}", string);
|
||||
}
|
||||
|
306
examples/tea/platform/src/tea.rs
Normal file
306
examples/tea/platform/src/tea.rs
Normal file
@ -0,0 +1,306 @@
|
||||
use crate::graphics::{
|
||||
lowlevel::buffer::create_rect_buffers, lowlevel::ortho::update_ortho_buffer,
|
||||
lowlevel::pipelines, primitives::rect::Rect, primitives::text::build_glyph_brush,
|
||||
};
|
||||
use pipelines::RectResources;
|
||||
use roc_std::RocStr;
|
||||
use std::error::Error;
|
||||
use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView};
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event,
|
||||
event::{Event, ModifiersState},
|
||||
event_loop::ControlFlow,
|
||||
platform::run_return::EventLoopExtRunReturn,
|
||||
};
|
||||
|
||||
// Inspired by:
|
||||
// https://github.com/sotrh/learn-wgpu by Benjamin Hansen, which is licensed under the MIT license
|
||||
// https://github.com/cloudhead/rgx by Alexis Sellier, which is licensed under the MIT license
|
||||
//
|
||||
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
|
||||
|
||||
fn run_event_loop(title: &str) -> Result<(), Box<dyn Error>> {
|
||||
// Open window and create a surface
|
||||
let mut event_loop = winit::event_loop::EventLoop::new();
|
||||
let mut needs_repaint = true;
|
||||
|
||||
let window = winit::window::WindowBuilder::new()
|
||||
.with_inner_size(PhysicalSize::new(1900.0, 1000.0))
|
||||
.with_title("The Roc Editor - Work In Progress")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
|
||||
let surface = unsafe { instance.create_surface(&window) };
|
||||
|
||||
// Initialize GPU
|
||||
let (gpu_device, cmd_queue) = futures::executor::block_on(async {
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
})
|
||||
.await
|
||||
.expect(r#"Request adapter
|
||||
If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor
|
||||
"#);
|
||||
|
||||
adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::empty(),
|
||||
limits: wgpu::Limits::default(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("Request device")
|
||||
});
|
||||
|
||||
// Create staging belt and a local pool
|
||||
let mut staging_belt = wgpu::util::StagingBelt::new(1024);
|
||||
let mut local_pool = futures::executor::LocalPool::new();
|
||||
let local_spawner = local_pool.spawner();
|
||||
|
||||
// Prepare swap chain
|
||||
let render_format = wgpu::TextureFormat::Bgra8Unorm;
|
||||
let mut size = window.inner_size();
|
||||
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: render_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
};
|
||||
|
||||
surface.configure(&gpu_device, &surface_config);
|
||||
|
||||
let rect_resources = pipelines::make_rect_pipeline(&gpu_device, &surface_config);
|
||||
|
||||
let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
|
||||
|
||||
let is_animating = true;
|
||||
|
||||
let mut keyboard_modifiers = ModifiersState::empty();
|
||||
|
||||
// Render loop
|
||||
window.request_redraw();
|
||||
|
||||
event_loop.run_return(|event, _, control_flow| {
|
||||
// TODO dynamically switch this on/off depending on whether any
|
||||
// animations are running. Should conserve CPU usage and battery life!
|
||||
if is_animating {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
} else {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
}
|
||||
|
||||
match event {
|
||||
//Close
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
//Resize
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::Resized(new_size),
|
||||
..
|
||||
} => {
|
||||
size = new_size;
|
||||
|
||||
surface.configure(
|
||||
&gpu_device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: render_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
},
|
||||
);
|
||||
|
||||
update_ortho_buffer(
|
||||
size.width,
|
||||
size.height,
|
||||
&gpu_device,
|
||||
&rect_resources.ortho.buffer,
|
||||
&cmd_queue,
|
||||
);
|
||||
}
|
||||
//Received Character
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::ReceivedCharacter(ch),
|
||||
..
|
||||
} => {
|
||||
// let input_outcome_res =
|
||||
// app_update::handle_new_char(&ch, &mut app_model, keyboard_modifiers);
|
||||
// if let Err(e) = input_outcome_res {
|
||||
// print_err(&e)
|
||||
// } else if let Ok(InputOutcome::Ignored) = input_outcome_res {
|
||||
// println!("Input '{}' ignored!", ch);
|
||||
// }
|
||||
todo!("TODO handle character input");
|
||||
}
|
||||
//Keyboard Input
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::KeyboardInput { input, .. },
|
||||
..
|
||||
} => {
|
||||
// if let Some(virtual_keycode) = input.virtual_keycode {
|
||||
// if let Some(ref mut ed_model) = app_model.ed_model_opt {
|
||||
// if ed_model.has_focus {
|
||||
// let keydown_res = keyboard_input::handle_keydown(
|
||||
// input.state,
|
||||
// virtual_keycode,
|
||||
// keyboard_modifiers,
|
||||
// &mut app_model,
|
||||
// );
|
||||
|
||||
// if let Err(e) = keydown_res {
|
||||
// print_err(&e)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
todo!("TODO handle keyboard input");
|
||||
}
|
||||
//Modifiers Changed
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::ModifiersChanged(modifiers),
|
||||
..
|
||||
} => {
|
||||
keyboard_modifiers = modifiers;
|
||||
}
|
||||
Event::MainEventsCleared => window.request_redraw(),
|
||||
Event::RedrawRequested { .. } => {
|
||||
// Get a command encoder for the current frame
|
||||
let mut encoder =
|
||||
gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Redraw"),
|
||||
});
|
||||
|
||||
let surface_texture = surface
|
||||
.get_current_texture()
|
||||
.expect("Failed to acquire next SwapChainTexture");
|
||||
|
||||
let view = surface_texture
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
// for text_section in &rendered_wgpu.text_sections_behind {
|
||||
// let borrowed_text = text_section.to_borrowed();
|
||||
|
||||
// glyph_brush.queue(borrowed_text);
|
||||
// }
|
||||
|
||||
// draw first layer of text
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&gpu_device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
&view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Failed to draw first layer of text.");
|
||||
|
||||
// draw rects on top of first text layer
|
||||
// draw_rects(
|
||||
// &rendered_wgpu.rects_front,
|
||||
// &mut encoder,
|
||||
// &view,
|
||||
// &gpu_device,
|
||||
// &rect_resources,
|
||||
// wgpu::LoadOp::Load,
|
||||
// );
|
||||
|
||||
// for text_section in &rendered_wgpu.text_sections_front {
|
||||
// let borrowed_text = text_section.to_borrowed();
|
||||
|
||||
// glyph_brush.queue(borrowed_text);
|
||||
// }
|
||||
|
||||
// draw text
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&gpu_device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
&view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Failed to draw queued text.");
|
||||
|
||||
staging_belt.finish();
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
surface_texture.present();
|
||||
|
||||
// Recall unused staging buffers
|
||||
use futures::task::SpawnExt;
|
||||
|
||||
local_spawner
|
||||
.spawn(staging_belt.recall())
|
||||
.expect("Recall staging belt");
|
||||
|
||||
local_pool.run_until_stalled();
|
||||
}
|
||||
_ => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_rects(
|
||||
all_rects: &[Rect],
|
||||
encoder: &mut CommandEncoder,
|
||||
texture_view: &TextureView,
|
||||
gpu_device: &wgpu::Device,
|
||||
rect_resources: &RectResources,
|
||||
load_op: LoadOp<wgpu::Color>,
|
||||
) {
|
||||
let rect_buffers = create_rect_buffers(gpu_device, encoder, all_rects);
|
||||
|
||||
let mut render_pass = begin_render_pass(encoder, texture_view, load_op);
|
||||
|
||||
render_pass.set_pipeline(&rect_resources.pipeline);
|
||||
render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]);
|
||||
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(
|
||||
rect_buffers.index_buffer.slice(..),
|
||||
wgpu::IndexFormat::Uint32,
|
||||
);
|
||||
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
|
||||
}
|
||||
|
||||
fn begin_render_pass<'a>(
|
||||
encoder: &'a mut CommandEncoder,
|
||||
texture_view: &'a TextureView,
|
||||
load_op: LoadOp<wgpu::Color>,
|
||||
) -> RenderPass<'a> {
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
view: texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: load_op,
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
label: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render(title: RocStr) {
|
||||
run_event_loop(title.as_str()).expect("Error running event loop")
|
||||
}
|
Loading…
Reference in New Issue
Block a user