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;
/// 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)]
#[repr(C)]
pub struct Rect {
pub color: (f32, f32, f32, f32),
pub height: f32,
pub top_left_coords: Vector2<f32>,
pub width: f32,
pub height: f32,
pub color: (f32, f32, f32, f32),
}

View file

@ -6,4 +6,4 @@ app "hello-gui"
render =
# 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 []
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 = render

View file

@ -4,7 +4,6 @@
//
// Thank you, Benjamin!
// 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.
// The license is included in the LEGAL_DETAILS file in the root directory of this distribution.
@ -13,9 +12,9 @@
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 wgpu::util::{ DeviceExt};
use wgpu::util::DeviceExt;
pub struct RectBuffers {
pub vertex_buffer: wgpu::Buffer,
@ -47,7 +46,6 @@ pub fn create_rect_buffers(
cmd_encoder: &mut wgpu::CommandEncoder,
rects: &[RectElt],
) -> RectBuffers {
let vertex_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bytemuck::cast_slice(&QUAD_VERTS),
@ -67,8 +65,7 @@ pub fn create_rect_buffers(
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;
@ -80,7 +77,6 @@ pub fn create_rect_buffers(
cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size);
RectBuffers {
vertex_buffer,
index_buffer,
@ -90,7 +86,7 @@ pub fn create_rect_buffers(
pub fn to_quad(rect_elt: &RectElt) -> Quad {
Quad {
pos: rect_elt.rect.top_left_coords.into(),
pos: rect_elt.rect.pos.into(),
width: rect_elt.rect.width,
height: rect_elt.rect.height,
color: to_slice(rect_elt.color),

View file

@ -8,9 +8,19 @@ pub struct RectElt {
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)]
#[repr(C)]
pub struct Rect {
pub top_left_coords: Vector2<f32>,
pub width: 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);
Rect {
top_left_coords: [position.x, top_y].into(),
pos: [position.x, top_y].into(),
width,
height,
}

View file

@ -2,11 +2,14 @@ use crate::{
graphics::{
colors::{self, from_hsb, to_wgpu_color},
lowlevel::buffer::create_rect_buffers,
lowlevel::{ortho::update_ortho_buffer, buffer::MAX_QUADS},
lowlevel::{pipelines, buffer::QUAD_INDICES},
primitives::{text::{build_glyph_brush, Text}, rect::{Rect, RectElt}},
lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer},
lowlevel::{buffer::QUAD_INDICES, pipelines},
primitives::{
rect::{Rect, RectElt},
text::{build_glyph_brush, Text},
},
rects_and_texts::RectsAndTexts,
},
roc::{RocElem, RocElemTag},
};
use pipelines::RectResources;
use roc_std::RocStr;
@ -26,7 +29,7 @@ use winit::{
//
// 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
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
.create_view(&wgpu::TextureViewDescriptor::default());
draw_rects(
&rects_and_texts.rects_behind,
&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 {
// let borrowed_text = text_section.to_borrowed();
for text_section in &rects_and_texts.text_sections_behind {
let borrowed_text = text_section.to_borrowed();
glyph_brush.queue(borrowed_text);
}
// glyph_brush.queue(borrowed_text);
// }
// draw first layer of text
glyph_brush
.draw_queued(
&gpu_device,
&mut staging_belt,
&mut cmd_encoder,
&view,
size.width,
size.height,
)
.expect("Failed to draw first layer of text.");
// glyph_brush
// .draw_queued(
// &gpu_device,
// &mut staging_belt,
// &mut cmd_encoder,
// &view,
// size.width,
// size.height,
// )
// .expect("Failed to draw first layer of text.");
// draw rects on top of first text layer
draw_rects(
&rects_and_texts.rects_front,
// draw_rects(
// &rects_and_texts.rects_front,
// &mut cmd_encoder,
// &view,
// &gpu_device,
// &rect_resources,
// wgpu::LoadOp::Load,
// );
display_elem(
&root,
&mut cmd_encoder,
&view,
&gpu_device,
@ -234,23 +237,23 @@ fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box
wgpu::LoadOp::Load,
);
for text_section in &rects_and_texts.text_sections_front {
let borrowed_text = text_section.to_borrowed();
// for text_section in &rects_and_texts.text_sections_front {
// let borrowed_text = text_section.to_borrowed();
glyph_brush.queue(borrowed_text);
}
// glyph_brush.queue(borrowed_text);
// }
// draw text
glyph_brush
.draw_queued(
&gpu_device,
&mut staging_belt,
&mut cmd_encoder,
&view,
size.width,
size.height,
)
.expect("Failed to draw queued text.");
// glyph_brush
// .draw_queued(
// &gpu_device,
// &mut staging_belt,
// &mut cmd_encoder,
// &view,
// size.width,
// size.height,
// )
// .expect("Failed to draw queued text.");
staging_belt.finish();
cmd_queue.submit(Some(cmd_encoder.finish()));
@ -319,74 +322,124 @@ fn begin_render_pass<'a>(
})
}
pub fn render(title: RocStr) {
let rects_behind = vec![
RectElt {
rect: Rect {
top_left_coords: (20.0, 20.0).into(),
width: 200.0,
height: 100.0
},
color: (0.4, 0.2, 0.5, 1.0),
border_width: 5.0,
border_color: (0.75, 0.5, 0.5, 1.0)
},
RectElt {
rect: Rect {
top_left_coords: (420.0, 420.0).into(),
width: 150.0,
height: 150.0
},
color: (0.9, 0.2, 0.5, 1.0),
border_width: 10.0,
border_color: (0.2, 0.5, 0.5, 1.0)
},
RectElt {
rect: Rect {
top_left_coords: (571.0, 420.0).into(),
width: 150.0,
height: 150.0
},
pub fn render(title: RocStr, root: RocElem) {
// let rects_behind = vec![
// RectElt {
// rect: Rect {
// top_left_coords: (20.0, 20.0).into(),
// width: 200.0,
// height: 100.0
// },
// color: (0.4, 0.2, 0.5, 1.0),
// border_width: 5.0,
// border_color: (0.75, 0.5, 0.5, 1.0)
// },
// RectElt {
// rect: Rect {
// top_left_coords: (420.0, 420.0).into(),
// width: 150.0,
// height: 150.0
// },
// color: (0.9, 0.2, 0.5, 1.0),
// border_width: 10.0,
// border_color: (0.2, 0.5, 0.5, 1.0)
// },
// RectElt {
// rect: Rect {
// top_left_coords: (571.0, 420.0).into(),
// width: 150.0,
// height: 150.0
// },
// color: (0.2, 0.2, 0.5, 1.0),
// border_width: 10.0,
// border_color: (0.2, 0.5, 0.5, 1.0)
// }
// ];
// let texts_behind = vec![
// Text {
// position: (50.0, 50.0).into(),
// color: colors::WHITE,
// text: "Back",
// size: 40.0,
// ..Default::default()
// }
// ];
// let rects_front = vec![
// RectElt {
// rect: Rect {
// top_left_coords: (30.0, 30.0).into(),
// width: 70.0,
// height: 70.0
// },
// color: (0.7, 0.2, 0.2, 0.6),
// border_width: 10.0,
// border_color: (0.75, 0.5, 0.5, 1.0)
// }
// ];
// let texts_front = vec![
// Text {
// position: (70.0, 70.0).into(),
// color: colors::WHITE,
// text: "Front",
// size: 40.0,
// ..Default::default()
// }
// ];
// let rects_and_texts = RectsAndTexts::init(rects_behind, texts_behind, rects_front, texts_front);
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)
}
];
border_color: (0.2, 0.5, 0.5, 1.0),
};
let texts_behind = vec![
Text {
position: (50.0, 50.0).into(),
color: colors::WHITE,
text: "Back",
size: 40.0,
..Default::default()
}
];
draw_rects(
&[rect_elt],
cmd_encoder,
texture_view,
gpu_device,
rect_resources,
load_op,
);
let rects_front = vec![
RectElt {
rect: Rect {
top_left_coords: (30.0, 30.0).into(),
width: 70.0,
height: 70.0
},
color: (0.7, 0.2, 0.2, 0.6),
border_width: 10.0,
border_color: (0.75, 0.5, 0.5, 1.0)
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");
}
];
let texts_front = vec![
Text {
position: (70.0, 70.0).into(),
color: colors::WHITE,
text: "Front",
size: 40.0,
..Default::default()
}
];
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");
}

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 gui;
mod rects_and_texts;
mod roc;
use crate::roc::RocElem;
use roc_std::RocStr;
extern "C" {
#[link_name = "roc__renderForHost_1_exposed"]
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> {
Update(State),
DoNothing,
@ -151,101 +62,9 @@ struct AppState {
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
println!("Calling roc_render()...");
let root_elem = unsafe { roc_render() };
let elem = unsafe { roc_render() };
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());
gui::render("test title".into(), root_elem);
// Exit code
0