Pass RocButton all the way to rendering

This commit is contained in:
Richard Feldman 2022-02-22 20:53:18 -05:00
parent 683b505e3a
commit 4399a6dfee
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
8 changed files with 202 additions and 312 deletions

View file

@ -1,9 +1,19 @@
use cgmath::Vector2; use cgmath::Vector2;
/// These fields are ordered this way because in Roc, the corresponding stuct is:
///
/// { top : F32, left : F32, width : F32, height : F32 }
///
/// alphabetically, that's { height, left, top, width } - which works out to the same as:
///
/// height: f32, pos: Vector2<f32>, width: f32
///
/// ...because Vector2<f32> is a repr(C) struct of { x: f32, y: f32 }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Rect { pub struct Rect {
pub color: (f32, f32, f32, f32),
pub height: f32,
pub top_left_coords: Vector2<f32>, pub top_left_coords: Vector2<f32>,
pub width: f32, pub width: f32,
pub height: f32,
pub color: (f32, f32, f32, f32),
} }

View file

@ -6,4 +6,4 @@ app "hello-gui"
render = render =
# btn = button { onPress : \prev, _ -> Action.none } (text "Hello, button!") # btn = button { onPress : \prev, _ -> Action.none } (text "Hello, button!")
Button (Text "Hello, World!") Button (Text "Hello, World!") { left: 300, top: 400, height: 300, width: 400 }

View file

@ -5,7 +5,9 @@ platform "examples/hello-world"
imports [] imports []
provides [ renderForHost ] provides [ renderForHost ]
Elem : [ Text Str, Button Elem ] Dim : { left : F32, top : F32, width : F32, height : F32 }
Elem : [ Button Elem Dim, Col (List Elem), Row (List Elem), Text Str ]
renderForHost : Elem renderForHost : Elem
renderForHost = render renderForHost = render

View file

@ -4,7 +4,6 @@
// //
// Thank you, Benjamin! // Thank you, Benjamin!
// Contains parts of https://github.com/iced-rs/iced/blob/adce9e04213803bd775538efddf6e7908d1c605e/wgpu/src/shader/quad.wgsl // Contains parts of https://github.com/iced-rs/iced/blob/adce9e04213803bd775538efddf6e7908d1c605e/wgpu/src/shader/quad.wgsl
// By Héctor Ramón, Iced contributors Licensed under the MIT license. // By Héctor Ramón, Iced contributors Licensed under the MIT license.
// The license is included in the LEGAL_DETAILS file in the root directory of this distribution. // The license is included in the LEGAL_DETAILS file in the root directory of this distribution.
@ -13,9 +12,9 @@
use std::mem; use std::mem;
use super::{vertex::Vertex, quad::Quad}; use super::{quad::Quad, vertex::Vertex};
use crate::graphics::{colors::to_slice, primitives::rect::RectElt}; use crate::graphics::{colors::to_slice, primitives::rect::RectElt};
use wgpu::util::{ DeviceExt}; use wgpu::util::DeviceExt;
pub struct RectBuffers { pub struct RectBuffers {
pub vertex_buffer: wgpu::Buffer, pub vertex_buffer: wgpu::Buffer,
@ -47,7 +46,6 @@ pub fn create_rect_buffers(
cmd_encoder: &mut wgpu::CommandEncoder, cmd_encoder: &mut wgpu::CommandEncoder,
rects: &[RectElt], rects: &[RectElt],
) -> RectBuffers { ) -> RectBuffers {
let vertex_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let vertex_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
contents: bytemuck::cast_slice(&QUAD_VERTS), contents: bytemuck::cast_slice(&QUAD_VERTS),
@ -67,10 +65,9 @@ pub fn create_rect_buffers(
mapped_at_creation: false, mapped_at_creation: false,
}); });
let quads: Vec<Quad> = rects.iter().map(|rect| to_quad(rect)).collect();
let quads: Vec<Quad> = rects.iter().map(|rect| {to_quad(rect)}).collect();
let buffer_size = (quads.len() as u64 ) * Quad::SIZE; let buffer_size = (quads.len() as u64) * Quad::SIZE;
let staging_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let staging_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
@ -79,7 +76,6 @@ pub fn create_rect_buffers(
}); });
cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size); cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size);
RectBuffers { RectBuffers {
vertex_buffer, vertex_buffer,
@ -90,7 +86,7 @@ pub fn create_rect_buffers(
pub fn to_quad(rect_elt: &RectElt) -> Quad { pub fn to_quad(rect_elt: &RectElt) -> Quad {
Quad { Quad {
pos: rect_elt.rect.top_left_coords.into(), pos: rect_elt.rect.pos.into(),
width: rect_elt.rect.width, width: rect_elt.rect.width,
height: rect_elt.rect.height, height: rect_elt.rect.height,
color: to_slice(rect_elt.color), color: to_slice(rect_elt.color),

View file

@ -8,9 +8,19 @@ pub struct RectElt {
pub border_color: (f32, f32, f32, f32), pub border_color: (f32, f32, f32, f32),
} }
/// These fields are ordered this way because in Roc, the corresponding stuct is:
///
/// { top : F32, left : F32, width : F32, height : F32 }
///
/// alphabetically, that's { height, left, top, width } - which works out to the same as:
///
/// struct Rect { height: f32, pos: Vector2<f32>, width: f32 }
///
/// ...because Vector2<f32> is a repr(C) struct of { x: f32, y: f32 }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Rect { pub struct Rect {
pub top_left_coords: Vector2<f32>,
pub width: f32,
pub height: f32, pub height: f32,
pub pos: Vector2<f32>,
pub width: f32,
} }

View file

@ -134,7 +134,7 @@ fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect {
let top_y = glyph_top_y(&glyph.glyph); let top_y = glyph_top_y(&glyph.glyph);
Rect { Rect {
top_left_coords: [position.x, top_y].into(), pos: [position.x, top_y].into(),
width, width,
height, height,
} }

View file

@ -2,11 +2,14 @@ use crate::{
graphics::{ graphics::{
colors::{self, from_hsb, to_wgpu_color}, colors::{self, from_hsb, to_wgpu_color},
lowlevel::buffer::create_rect_buffers, lowlevel::buffer::create_rect_buffers,
lowlevel::{ortho::update_ortho_buffer, buffer::MAX_QUADS}, lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer},
lowlevel::{pipelines, buffer::QUAD_INDICES}, lowlevel::{buffer::QUAD_INDICES, pipelines},
primitives::{text::{build_glyph_brush, Text}, rect::{Rect, RectElt}}, primitives::{
rect::{Rect, RectElt},
text::{build_glyph_brush, Text},
},
}, },
rects_and_texts::RectsAndTexts, roc::{RocElem, RocElemTag},
}; };
use pipelines::RectResources; use pipelines::RectResources;
use roc_std::RocStr; use roc_std::RocStr;
@ -26,7 +29,7 @@ use winit::{
// //
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/ // See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box<dyn Error>> { fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
// Open window and create a surface // Open window and create a surface
let mut event_loop = winit::event_loop::EventLoop::new(); let mut event_loop = winit::event_loop::EventLoop::new();
@ -197,36 +200,36 @@ fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box
.texture .texture
.create_view(&wgpu::TextureViewDescriptor::default()); .create_view(&wgpu::TextureViewDescriptor::default());
draw_rects( // for text_section in &rects_and_texts.text_sections_behind {
&rects_and_texts.rects_behind, // let borrowed_text = text_section.to_borrowed();
&mut cmd_encoder,
&view,
&gpu_device,
&rect_resources,
wgpu::LoadOp::Clear(to_wgpu_color(from_hsb(240, 10, 19))),
);
for text_section in &rects_and_texts.text_sections_behind { // glyph_brush.queue(borrowed_text);
let borrowed_text = text_section.to_borrowed(); // }
glyph_brush.queue(borrowed_text);
}
// draw first layer of text // draw first layer of text
glyph_brush // glyph_brush
.draw_queued( // .draw_queued(
&gpu_device, // &gpu_device,
&mut staging_belt, // &mut staging_belt,
&mut cmd_encoder, // &mut cmd_encoder,
&view, // &view,
size.width, // size.width,
size.height, // size.height,
) // )
.expect("Failed to draw first layer of text."); // .expect("Failed to draw first layer of text.");
// draw rects on top of first text layer // draw rects on top of first text layer
draw_rects( // draw_rects(
&rects_and_texts.rects_front, // &rects_and_texts.rects_front,
// &mut cmd_encoder,
// &view,
// &gpu_device,
// &rect_resources,
// wgpu::LoadOp::Load,
// );
display_elem(
&root,
&mut cmd_encoder, &mut cmd_encoder,
&view, &view,
&gpu_device, &gpu_device,
@ -234,23 +237,23 @@ fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box
wgpu::LoadOp::Load, wgpu::LoadOp::Load,
); );
for text_section in &rects_and_texts.text_sections_front { // for text_section in &rects_and_texts.text_sections_front {
let borrowed_text = text_section.to_borrowed(); // let borrowed_text = text_section.to_borrowed();
glyph_brush.queue(borrowed_text); // glyph_brush.queue(borrowed_text);
} // }
// draw text // draw text
glyph_brush // glyph_brush
.draw_queued( // .draw_queued(
&gpu_device, // &gpu_device,
&mut staging_belt, // &mut staging_belt,
&mut cmd_encoder, // &mut cmd_encoder,
&view, // &view,
size.width, // size.width,
size.height, // size.height,
) // )
.expect("Failed to draw queued text."); // .expect("Failed to draw queued text.");
staging_belt.finish(); staging_belt.finish();
cmd_queue.submit(Some(cmd_encoder.finish())); cmd_queue.submit(Some(cmd_encoder.finish()));
@ -319,74 +322,124 @@ fn begin_render_pass<'a>(
}) })
} }
pub fn render(title: RocStr) { pub fn render(title: RocStr, root: RocElem) {
let rects_behind = vec![ // let rects_behind = vec![
RectElt { // RectElt {
rect: Rect { // rect: Rect {
top_left_coords: (20.0, 20.0).into(), // top_left_coords: (20.0, 20.0).into(),
width: 200.0, // width: 200.0,
height: 100.0 // height: 100.0
}, // },
color: (0.4, 0.2, 0.5, 1.0), // color: (0.4, 0.2, 0.5, 1.0),
border_width: 5.0, // border_width: 5.0,
border_color: (0.75, 0.5, 0.5, 1.0) // border_color: (0.75, 0.5, 0.5, 1.0)
}, // },
RectElt { // RectElt {
rect: Rect { // rect: Rect {
top_left_coords: (420.0, 420.0).into(), // top_left_coords: (420.0, 420.0).into(),
width: 150.0, // width: 150.0,
height: 150.0 // height: 150.0
}, // },
color: (0.9, 0.2, 0.5, 1.0), // color: (0.9, 0.2, 0.5, 1.0),
border_width: 10.0, // border_width: 10.0,
border_color: (0.2, 0.5, 0.5, 1.0) // border_color: (0.2, 0.5, 0.5, 1.0)
}, // },
RectElt { // RectElt {
rect: Rect { // rect: Rect {
top_left_coords: (571.0, 420.0).into(), // top_left_coords: (571.0, 420.0).into(),
width: 150.0, // width: 150.0,
height: 150.0 // height: 150.0
}, // },
color: (0.2, 0.2, 0.5, 1.0), // color: (0.2, 0.2, 0.5, 1.0),
border_width: 10.0, // border_width: 10.0,
border_color: (0.2, 0.5, 0.5, 1.0) // border_color: (0.2, 0.5, 0.5, 1.0)
} // }
]; // ];
let texts_behind = vec![ // let texts_behind = vec![
Text { // Text {
position: (50.0, 50.0).into(), // position: (50.0, 50.0).into(),
color: colors::WHITE, // color: colors::WHITE,
text: "Back", // text: "Back",
size: 40.0, // size: 40.0,
..Default::default() // ..Default::default()
} // }
]; // ];
let rects_front = vec![ // let rects_front = vec![
RectElt { // RectElt {
rect: Rect { // rect: Rect {
top_left_coords: (30.0, 30.0).into(), // top_left_coords: (30.0, 30.0).into(),
width: 70.0, // width: 70.0,
height: 70.0 // height: 70.0
}, // },
color: (0.7, 0.2, 0.2, 0.6), // color: (0.7, 0.2, 0.2, 0.6),
border_width: 10.0, // border_width: 10.0,
border_color: (0.75, 0.5, 0.5, 1.0) // border_color: (0.75, 0.5, 0.5, 1.0)
} // }
]; // ];
let texts_front = vec![ // let texts_front = vec![
Text { // Text {
position: (70.0, 70.0).into(), // position: (70.0, 70.0).into(),
color: colors::WHITE, // color: colors::WHITE,
text: "Front", // text: "Front",
size: 40.0, // size: 40.0,
..Default::default() // ..Default::default()
} // }
]; // ];
let rects_and_texts = RectsAndTexts::init(rects_behind, texts_behind, rects_front, texts_front); // let rects_and_texts = RectsAndTexts::init(rects_behind, texts_behind, rects_front, texts_front);
run_event_loop(title.as_str(), rects_and_texts).expect("Error running event loop"); run_event_loop(title.as_str(), root).expect("Error running event loop");
}
fn display_elem(
elem: &RocElem,
cmd_encoder: &mut CommandEncoder,
texture_view: &TextureView,
gpu_device: &wgpu::Device,
rect_resources: &RectResources,
load_op: LoadOp<wgpu::Color>,
) {
use RocElemTag::*;
match elem.tag() {
Button => {
let button = unsafe { &elem.entry().button };
let rect_elt = RectElt {
rect: button.bounds,
color: (0.2, 0.2, 0.5, 1.0),
border_width: 10.0,
border_color: (0.2, 0.5, 0.5, 1.0),
};
draw_rects(
&[rect_elt],
cmd_encoder,
texture_view,
gpu_device,
rect_resources,
load_op,
);
display_elem(
&*button.child,
cmd_encoder,
texture_view,
gpu_device,
rect_resources,
load_op,
);
}
Text => {
let text = unsafe { &elem.entry().text };
}
Row => {
todo!("Row");
}
Col => {
todo!("Col");
}
}
} }

View file

@ -1,105 +1,16 @@
#![allow(non_snake_case)]
use core::ffi::c_void;
use core::mem::{self, ManuallyDrop};
use roc_std::RocStr;
use std::ffi::CStr;
use std::os::raw::c_char;
mod graphics; mod graphics;
mod gui; mod gui;
mod rects_and_texts; mod rects_and_texts;
mod roc;
use crate::roc::RocElem;
use roc_std::RocStr;
extern "C" { extern "C" {
#[link_name = "roc__renderForHost_1_exposed"] #[link_name = "roc__renderForHost_1_exposed"]
fn roc_render() -> RocElem; fn roc_render() -> RocElem;
} }
#[repr(transparent)]
#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits
struct RocElem {
entry: *const RocElemEntry,
}
impl RocElem {
#[cfg(target_pointer_width = "64")]
fn tag(&self) -> RocElemTag {
// On a 64-bit system, the last 3 bits of the pointer store the tag
unsafe { mem::transmute::<u8, RocElemTag>((self.entry as u8) & 0b0000_0111) }
}
pub fn entry(&self) -> &RocElemEntry {
// On a 64-bit system, the last 3 bits of the pointer store the tag
let cleared = self.entry as usize & !0b111;
unsafe { &*(cleared as *const RocElemEntry) }
}
#[cfg(target_pointer_width = "64")]
pub fn entry_mut(&mut self) -> &mut RocElemEntry {
// On a 64-bit system, the last 3 bits of the pointer store the tag
let cleared = self.entry as usize & !0b111;
unsafe { &mut *(cleared as *mut RocElemEntry) }
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
enum RocElemTag {
Button = 0,
Text,
}
#[repr(C)]
union RocElemEntry {
button: ManuallyDrop<RocElem>,
text: ManuallyDrop<RocStr>,
}
#[no_mangle]
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
return libc::malloc(size);
}
#[no_mangle]
pub unsafe extern "C" fn roc_realloc(
c_ptr: *mut c_void,
new_size: usize,
_old_size: usize,
_alignment: u32,
) -> *mut c_void {
return libc::realloc(c_ptr, new_size);
}
#[no_mangle]
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
return libc::free(c_ptr);
}
#[no_mangle]
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n)
}
enum Action<State> { enum Action<State> {
Update(State), Update(State),
DoNothing, DoNothing,
@ -151,101 +62,9 @@ struct AppState {
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_main() -> i32 { pub extern "C" fn rust_main() -> i32 {
println!("Calling roc_render()..."); let root_elem = unsafe { roc_render() };
let elem = unsafe { roc_render() }; gui::render("test title".into(), root_elem);
fn display_elem(elem: &RocElem) {
use RocElemTag::*;
println!("Got this tag: {:?}", elem.tag());
match elem.tag() {
Button => {
println!("Button!");
let child = unsafe { &elem.entry().button };
println!("Got this child:");
display_elem(child);
}
Text => {
let text = unsafe { &elem.entry().text };
println!("Text: {}", (*text).as_str());
}
}
}
display_elem(&elem);
// #[repr(C)]
// struct RocElem {
// entry: RocElemEntry
// tag: RocElemTag,
// }
// #[repr(u8)]
// enum RocElemTag {
// Button = 0,
// Text,
// }
// #[repr(C)]
// union RocElemEntry {
// text: ManuallyDrop<RocStr>,
// button: *const RocElem,
// }
// fn render(clicks: i64) -> Elem<i64> {
// let txt = Elem::Text(Key::null(), format!("Clicks: {}", clicks).as_str().into());
// Elem::Button(
// Key::null(),
// Box::new(move || Action::Update(clicks + 1)),
// Box::new(txt),
// )
// }
// fn draw_elem<T>(elem: Elem<T>) {
// use Elem::*;
// match elem {
// Button(_key, _on_click, label) => {
// print!("Drawing button label:\n\t");
// draw_elem(*label);
// }
// Text(_key, roc_str) => {
// println!("Drawing string \"{}\"", roc_str);
// }
// Col(_key, elems) => {
// println!("Drawing col contents...");
// for elem in elems {
// draw_elem(elem);
// }
// }
// Row(_key, elems) => {
// println!("Drawing row contents...");
// for elem in elems {
// draw_elem(elem);
// }
// }
// TextInput {
// key: _,
// text,
// on_change: _,
// } => {
// println!("Drawing text input with current text \"{}\"", text);
// }
// }
// }
// draw_elem(render(0));
// gui::render("test title".into());
// Exit code // Exit code
0 0