diff --git a/examples/gui/breakout/breakoutBROKEN.roc b/examples/gui/breakout/breakoutBROKEN.roc deleted file mode 100644 index b97cf49cdb..0000000000 --- a/examples/gui/breakout/breakoutBROKEN.roc +++ /dev/null @@ -1,164 +0,0 @@ -app [program, Model] { pf: platform "platform/main.roc" } - -import pf.Game exposing [Bounds, Elem, Event] - -paddleWidth = 0.2 # width of the paddle, as a % of screen width -paddleHeight = 50 # height of the paddle, in pixels -paddleSpeed = 65 # how many pixels the paddle moves per keypress -blockHeight = 80 # height of a block, in pixels -blockBorder = 0.025 # border of a block, as a % of its width -ballSize = 55 -numRows = 4 -numCols = 8 -numBlocks = numRows * numCols - -Model : { - # Screen height and width - height : F32, - width : F32, - # Paddle X-coordinate - paddleX : F32, - # Ball coordinates - ballX : F32, - ballY : F32, - dBallX : F32, - # delta x - how much it moves per tick - dBallY : F32, - # delta y - how much it moves per tick -} - -init : Bounds -> Model -init = \{ width, height } -> { - # Screen height and width - width, - height, - # Paddle X-coordinate - paddleX: (width * 0.5) - (paddleWidth * width * 0.5), - # Ball coordinates - ballX: width * 0.5, - ballY: height * 0.4, - # Delta - how much ball moves in each tick - dBallX: 4, - dBallY: 4, -} - -update : Model, Event -> Model -update = \model, event -> - when event is - Resize size -> - { model & width: size.width, height: size.height } - - KeyDown Left -> - { model & paddleX: model.paddleX - paddleSpeed } - - KeyDown Right -> - { model & paddleX: model.paddleX + paddleSpeed } - - Tick _ -> - tick model - - _ -> - model - -tick : Model -> Model -tick = \model -> - model - |> moveBall - -moveBall : Model -> Model -moveBall = \model -> - ballX = model.ballX + model.dBallX - ballY = model.ballY + model.dBallY - - paddleTop = model.height - blockHeight - (paddleHeight * 2) - paddleLeft = model.paddleX - paddleRight = paddleLeft + (model.width * paddleWidth) - - # If its y used to be less than the paddle, and now it's greater than or equal, - # then this is the frame where the ball collided with it. - crossingPaddle = model.ballY < paddleTop && ballY >= paddleTop - - # If it collided with the paddle, bounce off. - directionChange = - if crossingPaddle && (ballX >= paddleLeft && ballX <= paddleRight) then - -1f32 - else - 1f32 - - dBallX = model.dBallX * directionChange - dBallY = model.dBallY * directionChange - - { model & ballX, ballY, dBallX, dBallY } - -render : Model -> List Elem -render = \model -> - - blocks = List.map - (List.range { start: At 0, end: Length numBlocks }) - \index -> - col = - Num.rem index numCols - |> Num.toF32 - - row = - index - // numCols - |> Num.toF32 - - red = col / Num.toF32 numCols - green = row / Num.toF32 numRows - blue = Num.toF32 index / Num.toF32 numBlocks - - color = { r: red * 0.8, g: 0.2 + green * 0.6, b: 0.2 + blue * 0.8, a: 1 } - - { row, col, color } - - blockWidth = model.width / numCols - - rects = - List.joinMap - blocks - \{ row, col, color } -> - left = Num.toF32 col * blockWidth - top = Num.toF32 (row * blockHeight) - border = blockBorder * blockWidth - - outer = Rect { - left, - top, - width: blockWidth, - height: blockHeight, - color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 }, - } - - inner = Rect { - left: left + border, - top: top + border, - width: blockWidth - (border * 2), - height: blockHeight - (border * 2), - color, - } - - [outer, inner] - - ball = - color = { r: 0.7, g: 0.3, b: 0.9, a: 1.0 } - width = ballSize - height = ballSize - left = model.ballX - top = model.ballY - - Rect { left, top, width, height, color } - - paddle = - color = { r: 0.8, g: 0.8, b: 0.8, a: 1.0 } - width = model.width * paddleWidth - height = paddleHeight - left = model.paddleX - top = model.height - blockHeight - height - - Rect { left, top, width, height, color } - - List.concat rects [paddle, ball] - -program = { init, update, render } diff --git a/examples/gui/breakout/hello-guiBROKEN.roc b/examples/gui/breakout/hello-guiBROKEN.roc deleted file mode 100644 index 7a56c9c915..0000000000 --- a/examples/gui/breakout/hello-guiBROKEN.roc +++ /dev/null @@ -1,16 +0,0 @@ -app [program, Model] { pf: platform "platform/main.roc" } - -import pf.Game exposing [Bounds, Elem, Event] - -Model : { text : Str } - -init : Bounds -> Model -init = \_ -> { text: "Hello, World!" } - -update : Model, Event -> Model -update = \model, _ -> model - -render : Model -> List Elem -render = \model -> [Text { text: model.text, top: 0, left: 0, size: 40, color: { r: 1, g: 1, b: 1, a: 1 } }] - -program = { init, update, render } diff --git a/examples/gui/breakout/platform/Action.roc b/examples/gui/breakout/platform/Action.roc deleted file mode 100644 index 073c517f58..0000000000 --- a/examples/gui/breakout/platform/Action.roc +++ /dev/null @@ -1,15 +0,0 @@ -module [Action, none, update, map] - -Action state : [None, Update state] - -none : Action * -none = None - -update : state -> Action state -update = Update - -map : Action a, (a -> b) -> Action b -map = \action, transform -> - when action is - None -> None - Update state -> Update (transform state) diff --git a/examples/gui/breakout/platform/Cargo.toml b/examples/gui/breakout/platform/Cargo.toml deleted file mode 100644 index c4b624b284..0000000000 --- a/examples/gui/breakout/platform/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "host" -authors = ["The Roc Contributors"] -edition = "2021" -license = "UPL-1.0" -version = "0.0.1" - -[lib] -name = "host" -path = "src/lib.rs" -crate-type = ["staticlib", "lib"] - -[[bin]] -name = "host" -path = "src/main.rs" - -[dependencies] -arrayvec = "0.7.2" -libc = "0.2" -page_size = "0.4.2" -roc_std = { path = "../../../../crates/roc_std" } -cgmath = "0.18.0" -colored = "2.0.0" -copypasta = "0.7.1" -fs_extra = "1.2.0" -futures = "0.3.17" -glyph_brush = "0.7.2" -log = "0.4.14" -nonempty = "0.7.0" -palette = "0.6.0" -pest = "2.1.3" -pest_derive = "2.1.0" -serde = { version = "1.0.130", features = ["derive"] } -snafu = { version = "0.6.10", features = ["backtraces"] } -threadpool = "1.8.1" -wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "0545e36" } -wgpu_glyph = { git = "https://github.com/Anton-4/wgpu_glyph", rev = "257d109" } -winit = "0.26.1" - -[features] -default = [] - -[dependencies.bytemuck] -version = "1.7.2" -features = ["derive"] - -[workspace] - -# Optimizations based on https://deterministic.space/high-performance-rust.html -[profile.release] -lto = "fat" -codegen-units = 1 - -# debug = true # enable when profiling -[profile.bench] -lto = "thin" -codegen-units = 1 diff --git a/examples/gui/breakout/platform/Elem.roc b/examples/gui/breakout/platform/Elem.roc deleted file mode 100644 index dbfb4c04d2..0000000000 --- a/examples/gui/breakout/platform/Elem.roc +++ /dev/null @@ -1,192 +0,0 @@ -module [Elem, PressEvent, row, col, text, button, none, translate, list] - -import Action exposing [Action] - -Elem state : [ - # PERFORMANCE NOTE: - # If there are 8 or fewer tags here, then on a 64-bit system, the tag can be stored - # in the pointer - for massive memory savings. Try extremely hard to always limit the number - # of tags in this union to 8 or fewer! - Button (ButtonConfig state) (Elem state), - Text Str, - Col (List (Elem state)), - Row (List (Elem state)), - Lazy (Result { state, elem : Elem state } [NotCached] -> { state, elem : Elem state }), - # TODO FIXME: using this definition of Lazy causes a stack overflow in the compiler! - # Lazy (Result (Cached state) [NotCached] -> Cached state), - None, -] - -## Used internally in the type definition of Lazy -Cached state : { state, elem : Elem state } - -ButtonConfig state : { onPress : state, PressEvent -> Action state } - -PressEvent : { button : [Touch, Mouse [Left, Right, Middle]] } - -text : Str -> Elem * -text = \str -> - Text str - -button : { onPress : state, PressEvent -> Action state }, Elem state -> Elem state -button = \config, label -> - Button config label - -row : List (Elem state) -> Elem state -row = \children -> - Row children - -col : List (Elem state) -> Elem state -col = \children -> - Col children - -lazy : state, (state -> Elem state) -> Elem state -lazy = \state, render -> - # This function gets called by the host during rendering. It will - # receive the cached state and element (wrapped in Ok) if we've - # ever rendered this before, and Err otherwise. - Lazy - \result -> - when result is - Ok cached if cached.state == state -> - # If we have a cached value, and the new state is the - # same as the cached one, then we can return exactly - # what we had cached. - cached - - _ -> - # Either the state changed or else we didn't have a - # cached value to use. Either way, we need to render - # with the new state and store that for future use. - { state, elem: render state } - -none : Elem * -none = None # I've often wanted this in elm/html. Usually end up resorting to (Html.text "") - this seems nicer. -## Change an element's state type. -## -## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. -## State : { photo : Photo } -## -## render : State -> Elem State -## render = \state -> -## child : Elem State -## child = -## Photo.render state.photo -## |> Elem.translate .photo &photo -## -## col {} [child, otherElems] -## -translate = \child, toChild, toParent -> - when child is - Text str -> - Text str - - Col elems -> - Col (List.map elems \elem -> translate elem toChild toParent) - - Row elems -> - Row (List.map elems \elem -> translate elem toChild toParent) - - Button config label -> - onPress = \parentState, event -> - toChild parentState - |> config.onPress event - |> Action.map \c -> toParent parentState c - - Button { onPress } (translate label toChild toParent) - - Lazy renderChild -> - Lazy - \parentState -> - { elem, state } = renderChild (toChild parentState) - - { - elem: translate toChild toParent newChild, - state: toParent parentState state, - } - - None -> - None - -## Render a list of elements, using [Elem.translate] on each of them. -## -## Convenient when you have a [List] in your state and want to make -## a [List] of child elements out of it. -## -## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. -## State : { photos : List Photo } -## -## render : State -> Elem State -## render = \state -> -## children : List (Elem State) -## children = -## Elem.list Photo.render state .photos &photos -## -## col {} children -## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed -list : (child -> Elem child), parent, (parent -> List child), (parent, List child -> parent) -> List (Elem parent) -list = \renderChild, parent, toChildren, toParent -> - List.mapWithIndex - (toChildren parent) - \index, child -> - toChild = \par -> List.get (toChildren par) index - - newChild = translateOrDrop - child - toChild - \par, ch -> - toChildren par - |> List.set ch index - |> toParent - - renderChild newChild - -## Internal helper function for Elem.list -## -## Tries to translate a child to a parent, but -## if the child has been removed from the parent, -## drops it. -## -## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed -translateOrDrop : Elem child, (parent -> Result child *), (parent, child -> parent) -> Elem parent -translateOrDrop = \child, toChild, toParent -> - when child is - Text str -> - Text str - - Col elems -> - Col (List.map elems \elem -> translateOrDrop elem toChild toParent) - - Row elems -> - Row (List.map elems \elem -> translateOrDrop elem toChild toParent) - - Button config label -> - onPress = \parentState, event -> - when toChild parentState is - Ok newChild -> - newChild - |> config.onPress event - |> Action.map \c -> toParent parentState c - - Err _ -> - # The child was removed from the list before this onPress handler resolved. - # (For example, by a previous event handler that fired simultaneously.) - Action.none - - Button { onPress } (translateOrDrop label toChild toParent) - - Lazy childState renderChild -> - Lazy - (toParent childState) - \parentState -> - when toChild parentState is - Ok newChild -> - renderChild newChild - |> translateOrDrop toChild toParent - - Err _ -> - None - - # I don't think this should ever happen in practice. - None -> - None diff --git a/examples/gui/breakout/platform/Game.roc b/examples/gui/breakout/platform/Game.roc deleted file mode 100644 index 19f6fa31ca..0000000000 --- a/examples/gui/breakout/platform/Game.roc +++ /dev/null @@ -1,11 +0,0 @@ -module [Bounds, Elem, Event] - -Rgba : { r : F32, g : F32, b : F32, a : F32 } - -Bounds : { height : F32, width : F32 } - -Elem : [Rect { color : Rgba, left : F32, top : F32, width : F32, height : F32 }, Text { text : Str, color : Rgba, left : F32, top : F32, size : F32 }] - -KeyCode : [Left, Right, Other, Up, Down] - -Event : [Resize { width : F32, height : F32 }, KeyDown KeyCode, KeyUp KeyCode, Tick U128] diff --git a/examples/gui/breakout/platform/build.rs b/examples/gui/breakout/platform/build.rs deleted file mode 100644 index 47763b34c3..0000000000 --- a/examples/gui/breakout/platform/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - #[cfg(not(windows))] - println!("cargo:rustc-link-lib=dylib=app"); - - #[cfg(windows)] - println!("cargo:rustc-link-lib=dylib=libapp"); - - println!("cargo:rustc-link-search=."); -} diff --git a/examples/gui/breakout/platform/host.c b/examples/gui/breakout/platform/host.c deleted file mode 100644 index b9214bcf33..0000000000 --- a/examples/gui/breakout/platform/host.c +++ /dev/null @@ -1,3 +0,0 @@ -extern int rust_main(); - -int main() { return rust_main(); } \ No newline at end of file diff --git a/examples/gui/breakout/platform/main.roc b/examples/gui/breakout/platform/main.roc deleted file mode 100644 index 0a4acf501a..0000000000 --- a/examples/gui/breakout/platform/main.roc +++ /dev/null @@ -1,14 +0,0 @@ -platform "gui" - requires { Model } { program : _ } - exposes [Game] - packages {} - imports [Game.{ Bounds, Elem, Event }] - provides [programForHost] - -# TODO allow changing the window title - maybe via a Task, since that shouldn't happen all the time -programForHost : { - init : (Bounds -> Model) as Init, - update : (Model, Event -> Model) as Update, - render : (Model -> List Elem) as Render, -} -programForHost = program diff --git a/examples/gui/breakout/platform/src/graphics/colors.rs b/examples/gui/breakout/platform/src/graphics/colors.rs deleted file mode 100644 index 71be9c8fa5..0000000000 --- a/examples/gui/breakout/platform/src/graphics/colors.rs +++ /dev/null @@ -1,50 +0,0 @@ -use cgmath::Vector4; -use palette::{FromColor, Hsv, Srgb}; - -/// This order is optimized for what Roc will send -#[repr(C)] -#[derive(Copy, Clone, Debug, PartialEq, Default)] -pub struct Rgba { - a: f32, - b: f32, - g: f32, - r: f32, -} - -impl Rgba { - pub const WHITE: Self = Self::new(1.0, 1.0, 1.0, 1.0); - - pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { - Self { r, g, b, a } - } - - pub const fn to_array(self) -> [f32; 4] { - [self.r, self.g, self.b, self.a] - } - - pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> Self { - Self::from_hsba(hue, saturation, brightness, 1.0) - } - - pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> Self { - let rgb = Srgb::from_color(Hsv::new( - hue as f32, - (saturation as f32) / 100.0, - (brightness as f32) / 100.0, - )); - - Self::new(rgb.red, rgb.green, rgb.blue, alpha) - } -} - -impl From for [f32; 4] { - fn from(rgba: Rgba) -> Self { - rgba.to_array() - } -} - -impl From for Vector4 { - fn from(rgba: Rgba) -> Self { - Vector4::new(rgba.r, rgba.b, rgba.g, rgba.a) - } -} diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/buffer.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/buffer.rs deleted file mode 100644 index a5a7f54161..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/buffer.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Contains parts of https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// 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. - -// Thank you Héctor Ramón and Iced contributors! - -use std::mem; - -use super::{quad::Quad, vertex::Vertex}; -use crate::graphics::primitives::rect::RectElt; -use wgpu::util::DeviceExt; - -pub struct RectBuffers { - pub vertex_buffer: wgpu::Buffer, - pub index_buffer: wgpu::Buffer, - pub quad_buffer: wgpu::Buffer, -} - -pub const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; - -const QUAD_VERTS: [Vertex; 4] = [ - Vertex { - _position: [0.0, 0.0], - }, - Vertex { - _position: [1.0, 0.0], - }, - Vertex { - _position: [1.0, 1.0], - }, - Vertex { - _position: [0.0, 1.0], - }, -]; - -pub const MAX_QUADS: usize = 1_000; - -pub fn create_rect_buffers( - gpu_device: &wgpu::Device, - 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), - usage: wgpu::BufferUsages::VERTEX, - }); - - let index_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&QUAD_INDICES), - usage: wgpu::BufferUsages::INDEX, - }); - - let quad_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: mem::size_of::() as u64 * MAX_QUADS as u64, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let quads: Vec = rects.iter().map(|rect| to_quad(rect)).collect(); - - let buffer_size = (quads.len() as u64) * Quad::SIZE; - - let staging_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&quads), - usage: wgpu::BufferUsages::COPY_SRC, - }); - - cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size); - - RectBuffers { - vertex_buffer, - index_buffer, - quad_buffer, - } -} - -pub fn to_quad(rect_elt: &RectElt) -> Quad { - Quad { - pos: rect_elt.rect.pos.into(), - width: rect_elt.rect.width, - height: rect_elt.rect.height, - color: (rect_elt.color.to_array()), - border_color: rect_elt.border_color.into(), - border_width: rect_elt.border_width, - } -} diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/mod.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/mod.rs deleted file mode 100644 index 0add45385d..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod buffer; -pub mod ortho; -pub mod pipelines; -pub mod vertex; -pub mod quad; diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/ortho.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/ortho.rs deleted file mode 100644 index 2f4577871a..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/ortho.rs +++ /dev/null @@ -1,118 +0,0 @@ -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 = Ortho:: { - 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::() * 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, - } -} diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/pipelines.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/pipelines.rs deleted file mode 100644 index a0dc7908ec..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/pipelines.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::ortho::{init_ortho, OrthoResources}; -use super::quad::Quad; -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 { - label: None, - bind_group_layouts: &[&ortho.bind_group_layout], - push_constant_ranges: &[], - }); - 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/quad.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, Quad::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(), - multiview: None, - }) -} diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/quad.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/quad.rs deleted file mode 100644 index 7d8cf9dd8d..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/quad.rs +++ /dev/null @@ -1,36 +0,0 @@ - - -/// A polygon with 4 corners -#[derive(Copy, Clone)] -#[repr(C)] -pub struct Quad { - pub pos: [f32; 2], - pub width: f32, - pub height: f32, - pub color: [f32; 4], - pub border_color: [f32; 4], - pub border_width: f32, -} - -// Safety: Pod's contract says the type must -// not have any padding, and must be repr(C). -// As currrently defined, Quad does not have -// any padding. -unsafe impl bytemuck::Pod for Quad {} -unsafe impl bytemuck::Zeroable for Quad {} - -impl Quad { - pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() as wgpu::BufferAddress; - pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { - array_stride: Self::SIZE, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array!( - 1 => Float32x2, - 2 => Float32, - 3 => Float32, - 4 => Float32x4, - 5 => Float32x4, - 6 => Float32, - ), - }; -} diff --git a/examples/gui/breakout/platform/src/graphics/lowlevel/vertex.rs b/examples/gui/breakout/platform/src/graphics/lowlevel/vertex.rs deleted file mode 100644 index aa45bb7fb7..0000000000 --- a/examples/gui/breakout/platform/src/graphics/lowlevel/vertex.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Inspired by https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! - -// Inspired by 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. - -// Thank you Héctor Ramón and Iced contributors! -use bytemuck::{Pod, Zeroable}; - - -#[repr(C)] -#[derive(Copy, Clone, Zeroable, Pod)] -pub struct Vertex { - pub _position: [f32; 2], -} - -impl Vertex { - pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() 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, - }, - ], - }; -} diff --git a/examples/gui/breakout/platform/src/graphics/mod.rs b/examples/gui/breakout/platform/src/graphics/mod.rs deleted file mode 100644 index 0eb7fcd6da..0000000000 --- a/examples/gui/breakout/platform/src/graphics/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod colors; -pub mod lowlevel; -pub mod primitives; -pub mod style; diff --git a/examples/gui/breakout/platform/src/graphics/primitives/mod.rs b/examples/gui/breakout/platform/src/graphics/primitives/mod.rs deleted file mode 100644 index a9adb18862..0000000000 --- a/examples/gui/breakout/platform/src/graphics/primitives/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod rect; -pub mod text; diff --git a/examples/gui/breakout/platform/src/graphics/primitives/rect.rs b/examples/gui/breakout/platform/src/graphics/primitives/rect.rs deleted file mode 100644 index 8183fc6f7a..0000000000 --- a/examples/gui/breakout/platform/src/graphics/primitives/rect.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::graphics::colors::Rgba; -use cgmath::Vector2; - -#[derive(Debug, Copy, Clone)] -pub struct RectElt { - pub rect: Rect, - pub color: Rgba, - pub border_width: f32, - pub border_color: Rgba, -} - -/// 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, width: f32 } -/// -/// ...because Vector2 is a repr(C) struct of { x: f32, y: f32 } -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct Rect { - pub height: f32, - pub pos: Vector2, - pub width: f32, -} diff --git a/examples/gui/breakout/platform/src/graphics/primitives/text.rs b/examples/gui/breakout/platform/src/graphics/primitives/text.rs deleted file mode 100644 index 2ebccf9161..0000000000 --- a/examples/gui/breakout/platform/src/graphics/primitives/text.rs +++ /dev/null @@ -1,134 +0,0 @@ -// 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 crate::graphics::colors::Rgba; -use crate::graphics::style::DEFAULT_FONT_SIZE; -use ab_glyph::{FontArc, InvalidFont}; -use cgmath::Vector2; -use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder}; - -#[derive(Debug)] -pub struct Text<'a> { - pub position: Vector2, - pub area_bounds: Vector2, - pub color: Rgba, - 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: Rgba::WHITE, - text: "", - size: DEFAULT_FONT_SIZE, - visible: true, - centered: false, - } - } -} - -// pub fn layout_from_text(text: &Text) -> wgpu_glyph::Layout { -// 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::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(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, -// screen_position: (f32, f32), -// area_bounds: (f32, f32), -// layout: wgpu_glyph::Layout, -// ) -> 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 { -// pos: [position.x, top_y].into(), -// width, -// height, -// } -// } - -// 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, InvalidFont> { - let inconsolata = FontArc::try_from_slice(include_bytes!( - "../../../../../Inconsolata-Regular.ttf" - ))?; - - Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format)) -} diff --git a/examples/gui/breakout/platform/src/graphics/shaders/quad.wgsl b/examples/gui/breakout/platform/src/graphics/shaders/quad.wgsl deleted file mode 100644 index a561e2fc24..0000000000 --- a/examples/gui/breakout/platform/src/graphics/shaders/quad.wgsl +++ /dev/null @@ -1,60 +0,0 @@ - - -struct Globals { - ortho: mat4x4; -}; - -@group(0) -@binding(0) -var globals: Globals; - -struct VertexInput { - @location(0) position: vec2; -}; - -struct Quad { - @location(1) pos: vec2; // can't use the name "position" twice for compatibility with metal on MacOS - @location(2) width: f32; - @location(3) height: f32; - @location(4) color: vec4; - @location(5) border_color: vec4; - @location(6) border_width: f32; -}; - -struct VertexOutput { - @builtin(position) position: vec4; - @location(0) color: vec4; - @location(1) border_color: vec4; - @location(2) border_width: f32; -}; - -@stage(vertex) -fn vs_main( - input: VertexInput, - quad: Quad -) -> VertexOutput { - - var transform: mat4x4 = mat4x4( - vec4(quad.width, 0.0, 0.0, 0.0), - vec4(0.0, quad.height, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(quad.pos, 0.0, 1.0) - ); - - var out: VertexOutput; - - out.position = globals.ortho * transform * vec4(input.position, 0.0, 1.0);; - out.color = quad.color; - out.border_color = quad.border_color; - out.border_width = quad.border_width; - - return out; -} - - -@stage(fragment) -fn fs_main( - input: VertexOutput -) -> @location(0) vec4 { - return input.color; -} diff --git a/examples/gui/breakout/platform/src/graphics/style.rs b/examples/gui/breakout/platform/src/graphics/style.rs deleted file mode 100644 index 11e609075b..0000000000 --- a/examples/gui/breakout/platform/src/graphics/style.rs +++ /dev/null @@ -1 +0,0 @@ -pub const DEFAULT_FONT_SIZE: f32 = 30.0; diff --git a/examples/gui/breakout/platform/src/gui.rs b/examples/gui/breakout/platform/src/gui.rs deleted file mode 100644 index e4af20f705..0000000000 --- a/examples/gui/breakout/platform/src/gui.rs +++ /dev/null @@ -1,513 +0,0 @@ -use crate::{ - graphics::{ - colors::Rgba, - lowlevel::buffer::create_rect_buffers, - lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer}, - lowlevel::{buffer::QUAD_INDICES, pipelines}, - primitives::{ - rect::{Rect, RectElt}, - text::build_glyph_brush, - }, - }, - roc::{self, Bounds, RocElem, RocElemTag, RocEvent}, -}; -use cgmath::{Vector2, Vector4}; -use glyph_brush::{GlyphCruncher, OwnedSection}; -use pipelines::RectResources; -use std::{ - error::Error, - time::{Duration, Instant}, -}; -use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView}; -use wgpu_glyph::GlyphBrush; -use winit::{ - dpi::PhysicalSize, - event, - event::{ElementState, Event, ModifiersState, StartCause}, - 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/ - -const TIME_BETWEEN_TICKS: Duration = Duration::new(0, 1000 / 60); - -pub fn run_event_loop(title: &str, window_bounds: Bounds) -> Result<(), Box> { - let (mut model, mut elems) = roc::init_and_render(window_bounds); - - // Open window and create a surface - let mut event_loop = winit::event_loop::EventLoop::new(); - - let window = winit::window::WindowBuilder::new() - .with_inner_size(PhysicalSize::new(window_bounds.width, window_bounds.height)) - .with_title(title) - .build(&event_loop) - .unwrap(); - - macro_rules! update_and_rerender { - ($event:expr) => { - // TODO use (model, elems) = ... once we've upgraded rust versions - let pair = roc::update_and_render(model, $event); - - model = pair.0; - elems = pair.1; - - window.request_redraw(); - }; - } - - 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, run with: - `nixVulkanIntel `. - See extra docs here: github.com/guibou/nixGL - "#); - - 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 mut keyboard_modifiers = ModifiersState::empty(); - - // Render loop - let app_start_time = Instant::now(); - let mut next_tick = app_start_time + TIME_BETWEEN_TICKS; - - window.request_redraw(); - - event_loop.run_return(|event, _, control_flow| { - 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, - ); - - update_and_rerender!(RocEvent::Resize(Bounds { - height: size.height as f32, - width: size.width as f32, - })); - } - // Keyboard input - Event::WindowEvent { - event: - event::WindowEvent::KeyboardInput { - input: - event::KeyboardInput { - virtual_keycode: Some(keycode), - state: input_state, - .. - }, - .. - }, - .. - } => { - let roc_event = match input_state { - ElementState::Pressed => RocEvent::KeyDown(keycode.into()), - ElementState::Released => RocEvent::KeyUp(keycode.into()), - }; - - model = roc::update(model, roc_event); - } - // Modifiers Changed - Event::WindowEvent { - event: event::WindowEvent::ModifiersChanged(modifiers), - .. - } => { - keyboard_modifiers = modifiers; - } - Event::RedrawRequested { .. } => { - // Get a command cmd_encoder for the current frame - let mut cmd_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 elem in elems.iter() { - let (_bounds, drawable) = to_drawable( - elem, - Bounds { - width: size.width as f32, - height: size.height as f32, - }, - &mut glyph_brush, - ); - - process_drawable( - drawable, - &mut staging_belt, - &mut glyph_brush, - &mut cmd_encoder, - &view, - &gpu_device, - &rect_resources, - wgpu::LoadOp::Load, - Bounds { - width: size.width as f32, - height: size.height as f32, - }, - ); - } - - staging_belt.finish(); - cmd_queue.submit(Some(cmd_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(); - } - Event::NewEvents(StartCause::ResumeTimeReached { - requested_resume, .. - }) => { - // Only run this logic if this is the tick we originally requested. - if requested_resume == next_tick { - let now = Instant::now(); - - // Set a new next_tick *before* running update and rerender, - // so their runtime isn't factored into when we want to render next. - next_tick = now + TIME_BETWEEN_TICKS; - - let tick = now.saturating_duration_since(app_start_time); - - update_and_rerender!(RocEvent::Tick(tick)); - - *control_flow = winit::event_loop::ControlFlow::WaitUntil(next_tick); - } - } - _ => { - // Keep waiting until the next tick. - *control_flow = winit::event_loop::ControlFlow::WaitUntil(next_tick); - } - } - }); - - Ok(()) -} - -fn draw_rects( - all_rects: &[RectElt], - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, -) { - let rect_buffers = create_rect_buffers(gpu_device, cmd_encoder, all_rects); - - let mut render_pass = begin_render_pass(cmd_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_vertex_buffer(1, rect_buffers.quad_buffer.slice(..)); - - render_pass.set_index_buffer( - rect_buffers.index_buffer.slice(..), - wgpu::IndexFormat::Uint16, - ); - - render_pass.draw_indexed(0..QUAD_INDICES.len() as u32, 0, 0..MAX_QUADS as u32); -} - -fn begin_render_pass<'a>( - cmd_encoder: &'a mut CommandEncoder, - texture_view: &'a TextureView, - load_op: LoadOp, -) -> RenderPass<'a> { - cmd_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, - }) -} - -#[derive(Clone, Debug)] -struct Drawable { - pos: Vector2, - bounds: Bounds, - content: DrawableContent, -} - -#[derive(Clone, Debug)] -enum DrawableContent { - /// This stores an actual Section because an earlier step needs to know the bounds of - /// the text, and making a Section is a convenient way to compute those bounds. - Text(OwnedSection, Vector2), - FillRect { - color: Rgba, - border_width: f32, - border_color: Rgba, - }, -} - -fn process_drawable( - drawable: Drawable, - staging_belt: &mut wgpu::util::StagingBelt, - glyph_brush: &mut GlyphBrush<()>, - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, - texture_size: Bounds, -) { - draw( - drawable.bounds, - drawable.content, - drawable.pos, - staging_belt, - glyph_brush, - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - texture_size, - ); -} - -fn draw( - bounds: Bounds, - content: DrawableContent, - pos: Vector2, - staging_belt: &mut wgpu::util::StagingBelt, - glyph_brush: &mut GlyphBrush<()>, - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, - texture_size: Bounds, -) { - use DrawableContent::*; - - match content { - Text(section, offset) => { - glyph_brush.queue(section.with_screen_position(pos + offset).to_borrowed()); - - glyph_brush - .draw_queued( - gpu_device, - staging_belt, - cmd_encoder, - texture_view, - texture_size.width as u32, // TODO why do we make these be u32 and then cast to f32 in orthorgraphic_projection? - texture_size.height as u32, - ) - .expect("Failed to draw text element"); - } - FillRect { - color, - border_width, - border_color, - } => { - // TODO store all these colors and things in FillRect - let rect_elt = RectElt { - rect: Rect { - pos, - width: bounds.width, - height: bounds.height, - }, - color, - border_width, - border_color, - }; - - // TODO inline draw_rects into here! - draw_rects( - &[rect_elt], - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - ); - } - } -} - -/// focused_elem is the currently-focused element (or NULL if nothing has the focus) -fn to_drawable( - elem: &RocElem, - bounds: Bounds, - glyph_brush: &mut GlyphBrush<()>, -) -> (Bounds, Drawable) { - use RocElemTag::*; - - match elem.tag() { - Rect => { - let rect = unsafe { &elem.entry().rect }; - - let bounds = Bounds { - width: rect.width, - height: rect.height, - }; - - let drawable = Drawable { - pos: (rect.left, rect.top).into(), - bounds, - content: DrawableContent::FillRect { - color: rect.color, - border_width: 1.0, - border_color: rect.color, - }, - }; - - (bounds, drawable) - } - Text => { - let text = unsafe { &elem.entry().text }; - let is_centered = true; // TODO don't hardcode this - let layout = wgpu_glyph::Layout::default().h_align(if is_centered { - wgpu_glyph::HorizontalAlign::Center - } else { - wgpu_glyph::HorizontalAlign::Left - }); - - let section = owned_section_from_str(text.text.as_str(),text.color, text.size, bounds, layout); - - // Calculate the bounds and offset by measuring glyphs - let text_bounds; - let offset; - - match glyph_brush.glyph_bounds(section.to_borrowed()) { - Some(glyph_bounds) => { - text_bounds = Bounds { - width: glyph_bounds.max.x - glyph_bounds.min.x, - height: glyph_bounds.max.y - glyph_bounds.min.y, - }; - - offset = (-glyph_bounds.min.x, -glyph_bounds.min.y).into(); - } - None => { - text_bounds = Bounds { - width: 0.0, - height: 0.0, - }; - - offset = (0.0, 0.0).into(); - } - } - - let drawable = Drawable { - pos: (text.left, text.top).into(), - bounds: text_bounds, - content: DrawableContent::Text(section, offset), - }; - - (text_bounds, drawable) - } - } -} - -fn owned_section_from_str( - string: &str, - color: Rgba, - size: f32, - bounds: Bounds, - layout: wgpu_glyph::Layout, -) -> OwnedSection { - OwnedSection { - bounds: (bounds.width, bounds.height), - layout, - ..OwnedSection::default() - } - .add_text( - glyph_brush::OwnedText::new(string) - .with_color(Vector4::from(color)) - .with_scale(size), - ) -} diff --git a/examples/gui/breakout/platform/src/lib.rs b/examples/gui/breakout/platform/src/lib.rs deleted file mode 100644 index 51378f8ddc..0000000000 --- a/examples/gui/breakout/platform/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![allow(unused)] - -mod graphics; -mod gui; -mod roc; - -#[no_mangle] -pub extern "C" fn rust_main() -> i32 { - let bounds = roc::Bounds { - width: 1900.0, - height: 1000.0, - }; - - gui::run_event_loop("RocOut!", bounds).expect("Error running event loop"); - - // Exit code - 0 -} diff --git a/examples/gui/breakout/platform/src/main.rs b/examples/gui/breakout/platform/src/main.rs deleted file mode 100644 index 0765384f29..0000000000 --- a/examples/gui/breakout/platform/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - std::process::exit(host::rust_main() as _); -} diff --git a/examples/gui/breakout/platform/src/roc.rs b/examples/gui/breakout/platform/src/roc.rs deleted file mode 100644 index 8c651dbd07..0000000000 --- a/examples/gui/breakout/platform/src/roc.rs +++ /dev/null @@ -1,453 +0,0 @@ -use crate::graphics::colors::Rgba; -use core::alloc::Layout; -use core::ffi::c_void; -use core::mem::{self, ManuallyDrop}; -use roc_std::{RocList, RocStr}; -use std::ffi::CStr; -use std::fmt::Debug; -use std::mem::MaybeUninit; -use std::os::raw::c_char; -use std::time::Duration; -use winit::event::VirtualKeyCode; - -extern "C" { - // program - - // #[link_name = "roc__programForHost_1_exposed_generic"] - // fn roc_program(); - - // #[link_name = "roc__programForHost_1_exposed_size"] - // fn roc_program_size() -> i64; - - // init - - #[link_name = "roc__programForHost_0_caller"] - fn call_init(size: *const Bounds, closure_data: *const u8, output: *mut Model); - - #[link_name = "roc__programForHost_0_size"] - fn init_size() -> i64; - - #[link_name = "roc__programForHost_0_result_size"] - fn init_result_size() -> i64; - - // update - - #[link_name = "roc__programForHost_1_caller"] - fn call_update( - model: *const Model, - event: *const RocEvent, - closure_data: *const u8, - output: *mut Model, - ); - - #[link_name = "roc__programForHost_1_size"] - fn update_size() -> i64; - - #[link_name = "roc__programForHost_1_result_size"] - fn update_result_size() -> i64; - - // render - - #[link_name = "roc__programForHost_2_caller"] - fn call_render(model: *const Model, closure_data: *const u8, output: *mut RocList); - - #[link_name = "roc__programForHost_2_size"] - fn roc_render_size() -> i64; -} - -#[repr(C)] -pub union RocEventEntry { - pub key_down: RocKeyCode, - pub key_up: RocKeyCode, - pub resize: Bounds, - pub tick: [u8; 16], // u128 is unsupported in repr(C) -} - -#[repr(u8)] -#[allow(unused)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RocEventTag { - KeyDown = 0, - KeyUp, - Resize, - Tick, -} - -#[repr(C)] -#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits -pub struct RocEvent { - entry: RocEventEntry, - tag: RocEventTag, -} - -impl Debug for RocEvent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use RocEventTag::*; - - match self.tag() { - KeyDown => unsafe { self.entry().key_down }.fmt(f), - KeyUp => unsafe { self.entry().key_up }.fmt(f), - Resize => unsafe { self.entry().resize }.fmt(f), - Tick => unsafe { self.entry().tick }.fmt(f), - } - } -} - -impl RocEvent { - #[cfg(target_pointer_width = "64")] - pub fn tag(&self) -> RocEventTag { - self.tag - } - - pub fn entry(&self) -> &RocEventEntry { - &self.entry - } - - #[allow(non_snake_case)] - pub fn Resize(size: Bounds) -> Self { - Self { - tag: RocEventTag::Resize, - entry: RocEventEntry { resize: size }, - } - } - - #[allow(non_snake_case)] - pub fn KeyDown(keycode: RocKeyCode) -> Self { - Self { - tag: RocEventTag::KeyDown, - entry: RocEventEntry { key_down: keycode }, - } - } - - #[allow(non_snake_case)] - pub fn KeyUp(keycode: RocKeyCode) -> Self { - Self { - tag: RocEventTag::KeyUp, - entry: RocEventEntry { key_up: keycode }, - } - } - - #[allow(non_snake_case)] - pub fn Tick(duration: Duration) -> Self { - Self { - tag: RocEventTag::Tick, - entry: RocEventEntry { - tick: duration.as_nanos().to_ne_bytes(), - }, - } - } -} - -#[repr(u8)] -#[allow(unused)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RocKeyCode { - Down = 0, - Left, - Other, - Right, - Up, -} - -impl From for RocKeyCode { - fn from(keycode: VirtualKeyCode) -> Self { - use VirtualKeyCode::*; - - match keycode { - Left => RocKeyCode::Left, - Right => RocKeyCode::Right, - Up => RocKeyCode::Up, - Down => RocKeyCode::Down, - _ => RocKeyCode::Other, - } - } -} - -#[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(msg: *mut RocStr, tag_id: u32) { - match tag_id { - 0 => { - eprintln!("Roc standard library hit a panic: {}", &*msg); - } - 1 => { - eprintln!("Application hit a panic: {}", &*msg); - } - _ => unreachable!(), - } - std::process::exit(1); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dbg(loc: *mut RocStr, msg: *mut RocStr, src: *mut RocStr) { - eprintln!("[{}] {} = {}", &*loc, &*src, &*msg); -} - -#[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) -} - -#[repr(transparent)] -#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct ElemId(*const RocElemEntry); - -#[repr(C)] -pub union RocElemEntry { - pub rect: ManuallyDrop, - pub text: ManuallyDrop, -} - -#[repr(u8)] -#[allow(unused)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RocElemTag { - Rect = 0, - Text = 1, -} - -#[repr(C)] -#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits -pub struct RocElem { - entry: RocElemEntry, - tag: RocElemTag, -} - -impl Debug for RocElem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use RocElemTag::*; - - match self.tag() { - Rect => unsafe { &*self.entry().rect }.fmt(f), - Text => unsafe { &*self.entry().text }.fmt(f), - } - } -} - -impl RocElem { - #[cfg(target_pointer_width = "64")] - pub fn tag(&self) -> RocElemTag { - self.tag - } - - #[allow(unused)] - pub fn entry(&self) -> &RocElemEntry { - &self.entry - } - - #[allow(unused)] - pub fn rect(styles: ButtonStyles) -> RocElem { - todo!("restore rect() method") - // let rect = RocRect { styles }; - // let entry = RocElemEntry { - // rect: ManuallyDrop::new(rect), - // }; - - // Self::elem_from_tag(entry, RocElemTag::Rect) - } - - #[allow(unused)] - pub fn text>(into_roc_str: T) -> RocElem { - todo!("TODO restore text method") - // let entry = RocElemEntry { - // text: ManuallyDrop::new(into_roc_str.into()), - // }; - - // Self::elem_from_tag(entry, RocElemTag::Text) - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct RocRect { - pub color: Rgba, - - // These must be in this order for alphabetization! - pub height: f32, - pub left: f32, - pub top: f32, - pub width: f32, -} - -#[repr(C)] -#[derive(Debug, Clone)] -pub struct RocText { - pub text: RocStr, - pub color: Rgba, - pub left: f32, - pub size: f32, - pub top: f32, -} - -impl Clone for RocElem { - fn clone(&self) -> Self { - unsafe { - match self.tag() { - RocElemTag::Rect => Self { - tag: RocElemTag::Rect, - entry: RocElemEntry { - rect: self.entry.rect.clone(), - }, - }, - RocElemTag::Text => Self { - tag: RocElemTag::Text, - entry: RocElemEntry { - text: self.entry.text.clone(), - }, - }, - } - } - } -} - -impl Drop for RocElem { - fn drop(&mut self) { - unsafe { - match self.tag() { - RocElemTag::Rect => mem::drop(ManuallyDrop::take(&mut self.entry.rect)), - RocElemTag::Text => mem::drop(ManuallyDrop::take(&mut self.entry.text)), - } - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Default)] -pub struct ButtonStyles { - pub bg_color: Rgba, - pub border_color: Rgba, - pub border_width: f32, - pub text_color: Rgba, -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(C)] -pub struct Bounds { - pub height: f32, - pub width: f32, -} - -type Model = c_void; - -/// Call the app's init function, then render and return that result -pub fn init_and_render(bounds: Bounds) -> (*const Model, RocList) { - let closure_data_buf; - let closure_layout; - - // Call init to get the initial model - let model = unsafe { - let ret_val_layout = Layout::array::(init_result_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - let ret_val_buf = std::alloc::alloc(ret_val_layout) as *mut Model; - - closure_layout = Layout::array::(init_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - closure_data_buf = std::alloc::alloc(closure_layout); - - call_init(&bounds, closure_data_buf, ret_val_buf); - - ret_val_buf - }; - - // Call render passing the model to get the initial Elems - let elems = unsafe { - let mut ret_val: MaybeUninit> = MaybeUninit::uninit(); - - // Reuse the buffer from the previous closure if possible - let closure_data_buf = - std::alloc::realloc(closure_data_buf, closure_layout, roc_render_size() as usize); - - call_render(model, closure_data_buf, ret_val.as_mut_ptr()); - - std::alloc::dealloc(closure_data_buf, closure_layout); - - ret_val.assume_init() - }; - - (model, elems) -} - -/// Call the app's update function, then render and return that result -pub fn update(model: *const Model, event: RocEvent) -> *const Model { - let closure_data_buf; - let closure_layout; - - // Call update to get the new model - unsafe { - let ret_val_layout = Layout::array::(update_result_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - let ret_val_buf = std::alloc::alloc(ret_val_layout) as *mut Model; - - closure_layout = Layout::array::(update_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - closure_data_buf = std::alloc::alloc(closure_layout); - - call_update(model, &event, closure_data_buf, ret_val_buf); - - ret_val_buf - } -} - -/// Call the app's update function, then render and return that result -pub fn update_and_render(model: *const Model, event: RocEvent) -> (*const Model, RocList) { - let closure_data_buf; - let closure_layout; - - // Call update to get the new model - let model = unsafe { - let ret_val_layout = Layout::array::(update_result_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - let ret_val_buf = std::alloc::alloc(ret_val_layout) as *mut Model; - - closure_layout = Layout::array::(update_size() as usize).unwrap(); - - // TODO allocate on the stack if it's under a certain size - closure_data_buf = std::alloc::alloc(closure_layout); - - call_update(model, &event, closure_data_buf, ret_val_buf); - - ret_val_buf - }; - - // Call render passing the model to get the initial Elems - let elems = unsafe { - let mut ret_val: MaybeUninit> = MaybeUninit::uninit(); - - // Reuse the buffer from the previous closure if possible - let closure_data_buf = - std::alloc::realloc(closure_data_buf, closure_layout, roc_render_size() as usize); - - call_render(model, closure_data_buf, ret_val.as_mut_ptr()); - - std::alloc::dealloc(closure_data_buf, closure_layout); - - ret_val.assume_init() - }; - - (model, elems) -}