Skip to content

Commit

Permalink
add mandelbrot
Browse files Browse the repository at this point in the history
  • Loading branch information
boralg committed Jun 24, 2024
1 parent 50db4ae commit b148941
Show file tree
Hide file tree
Showing 11 changed files with 464 additions and 34 deletions.
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ path = "src/hello_triangle/main.rs"
name = "spinning_cube"
path = "src/spinning_cube/main.rs"

[[bin]]
name = "mandelbrot"
path = "src/mandelbrot/main.rs"


[dependencies]
bytemuck = { version = "1.16.0", features = ["derive"] }
Expand Down
13 changes: 8 additions & 5 deletions examples/src/hello_triangle/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use sursface::{app::App, wgpu::{self, Color, CommandEncoder, RenderPass, RenderPipeline, Surface, SurfaceTexture, TextureView}};
use sursface::{app::App, wgpu::{self, Color, CommandEncoder, RenderPass, RenderPipeline, Surface, SurfaceTexture, TextureView}, winit::event::WindowEvent};

#[cfg(not(target_arch = "wasm32"))]
fn main() {
use sursface::winit::dpi::PhysicalSize;
sursface::start::create_window_desktop(PhysicalSize::new(1280, 720), &init, &render);
use sursface::winit::{dpi::PhysicalSize, event};
sursface::start::create_window_desktop(PhysicalSize::new(1280, 720), &init, &render, &event);
}

#[cfg(target_arch = "wasm32")]
Expand All @@ -15,7 +15,7 @@ fn main() {}
pub fn start_browser(canvas: sursface::wgpu::web_sys::HtmlCanvasElement) {
use sursface::{start, wasm_bindgen};

start::create_window_browser(canvas, &init, &render);
start::create_window_browser(canvas, &init, &render, &event);
}

struct TriangleState {
Expand Down Expand Up @@ -122,4 +122,7 @@ pub fn draw_triangle<'a>(
) {
rpass.set_pipeline(pipeline);
rpass.draw(0..3, 0..1);
}
}


fn event<'a>(_app: &mut App<TriangleState>, _state: &mut TriangleState, _event: WindowEvent) {}
10 changes: 6 additions & 4 deletions examples/src/hello_window/main.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use sursface::{app::App, wgpu};
use sursface::{app::App, wgpu, winit::event::WindowEvent};
#[cfg(target_arch = "wasm32")]
use sursface::wasm_bindgen;

#[cfg(not(target_arch = "wasm32"))]
fn main() {
use sursface::winit::dpi::PhysicalSize;
sursface::start::create_window_desktop(PhysicalSize::new(1280, 720), &init, &render);
sursface::start::create_window_desktop(PhysicalSize::new(1280, 720), &init, &render, &event);
}


#[cfg(target_arch = "wasm32")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn start_browser(canvas: sursface::wgpu::web_sys::HtmlCanvasElement) {
use sursface::{start, wasm_bindgen};
start::create_window_browser(canvas, &init, &render);
start::create_window_browser(canvas, &init, &render, &event);
}

#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -74,4 +74,6 @@ fn clear_screen<'a>(app: &mut App<EmptyState>, color: sursface::wgpu::Color) ->
fn present<'a>(app: &mut App<EmptyState>, output: sursface::wgpu::SurfaceTexture) -> Result<(), wgpu::SurfaceError> {
output.present();
Ok(())
}
}

fn event<'a>(_app: &mut App<EmptyState>, _state: &mut EmptyState, _event: WindowEvent) {}
23 changes: 23 additions & 0 deletions examples/src/mandelbrot/assets/shader.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
struct Uniforms {
model_view_proj: mat4x4<f32>,
};

@group(0) @binding(0) var<uniform> uniforms: Uniforms;

struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) fragUV: vec2<f32>,
};

@vertex
fn vs_main(@location(0) position: vec3<f32>, @location(1) uv: vec2<f32>) -> VertexOutput {
var output: VertexOutput;
output.position = uniforms.model_view_proj * vec4<f32>(position, 1.0);
output.fragUV = uv;
return output;
}

@fragment
fn fs_main(@location(0) fragUV: vec2<f32>) -> @location(0) vec4<f32> {
return vec4<f32>(1, 1, 1, 1);
}
295 changes: 295 additions & 0 deletions examples/src/mandelbrot/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
use std::time::Instant;
use sursface::wgpu::{BindGroup, BindGroupLayout, Buffer, Device, Queue, RenderPass, Surface, VertexBufferLayout};
use sursface::winit::dpi::PhysicalPosition;
use sursface::winit::event::WindowEvent;
use sursface::{app::App, wgpu};
use viewport::INDICES;
use wgpu::{util::DeviceExt, Color, CommandEncoder, RenderPipeline, SurfaceTexture, TextureView};
use cgmath::{perspective, Deg, Matrix4, Point3, Rad, Transform, Vector2, Vector3, Zero};
use cgmath::SquareMatrix;
use image::{GenericImageView, ImageFormat};

#[cfg(target_arch = "wasm32")]
use sursface::wasm_bindgen;

use crate::viewport::Vertex;

pub mod viewport;

#[cfg(not(target_arch = "wasm32"))]
fn main() {
use sursface::winit::dpi::PhysicalSize;
sursface::start::create_window_desktop(PhysicalSize::new(1280, 720), &init, &render, &event);
}


#[cfg(target_arch = "wasm32")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn start_browser(canvas: sursface::wgpu::web_sys::HtmlCanvasElement) {
use sursface::{start, wasm_bindgen};
start::create_window_browser(canvas, &init, &render, &event);
}

#[cfg(target_arch = "wasm32")]
fn main() {}

struct MandelbrotState {
render_pipeline: RenderPipeline,
vertex_buffer: Buffer,
index_buffer: Buffer,
uniform_buffer: wgpu::Buffer,
uniform_bind_group: wgpu::BindGroup,
translation: Vector2<f64>,
scale: f64,
uniforms: Uniforms,
translation_speed: f64,
scale_speed: f64
}

#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Uniforms {
model_view_proj: [[f32; 4]; 4]
}

fn create_uniforms(device: &Device) -> (Buffer, BindGroupLayout, BindGroup) {
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[Uniforms { model_view_proj: Matrix4::identity().into() }]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});

let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Uniform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});

let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Uniform Bind Group"),
layout: &uniform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});

(uniform_buffer, uniform_bind_group_layout, uniform_bind_group)
}

fn init(app: &mut App<MandelbrotState>) -> MandelbrotState {
use std::borrow::Cow;

let display = app.display.as_ref().unwrap();
let device = &display.device;

let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("assets/shader.wgsl"))),
});

let (uniform_buffer, uniform_bind_group_layout, uniform_bind_group ) = create_uniforms(device);

let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&uniform_bind_group_layout],
push_constant_ranges: &[],
});

let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[Vertex::desc(),],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(display.config.format.into())],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});

let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(viewport::VERTICES),
usage: wgpu::BufferUsages::VERTEX,
});

let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(viewport::INDICES),
usage: wgpu::BufferUsages::INDEX,
});

MandelbrotState {
render_pipeline,
vertex_buffer,
index_buffer,
uniform_buffer,
uniform_bind_group,
uniforms: Uniforms { model_view_proj: Matrix4::identity().into() },
translation: Vector2::zero(),
scale: 1f64,
translation_speed: 1f64,
scale_speed: 0.1f64,
}
}

fn render(app: &mut App<MandelbrotState>, state: &mut MandelbrotState) {
let clear_color = Color {
r: 100.0 / 255.0,
g: 149.0 / 255.0,
b: 237.0 / 255.0,
a: 255.0 / 255.0,
};

let output = {
let display = app.display.as_ref().unwrap();

let mut encoder = display.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Encoder"),
});

let (output, view) = get_framebuffer(&display.surface);

{
let mut rpass = clear_screen(&view, &mut encoder, clear_color);

let display = app.display.as_ref().unwrap();
let aspect_ratio = app.display.as_ref().unwrap().config.width as f32 / app.display.as_ref().unwrap().config.height as f32;

let mvp = Matrix4::from_scale(state.scale) *
Matrix4::from_translation(Vector3::<f64>::new(state.translation.x, state.translation.y, 0f64));
state.uniforms.model_view_proj = mvp.cast().unwrap().into();

let queue = &display.queue;
queue.write_buffer(&state.uniform_buffer, 0, bytemuck::cast_slice(&[state.uniforms]));

draw_mandelbrot(&mut rpass, &state.render_pipeline, state);
}

{
let display = app.display.as_ref().unwrap();
display.queue.submit(std::iter::once(encoder.finish()));
}

output
};

output.present();
}

fn get_framebuffer(surface: &Surface) -> (SurfaceTexture, TextureView) {
let output = surface.get_current_texture().unwrap();
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
(output, view)
}

fn clear_screen<'a>(
framebuffer_view: &'a TextureView,
encoder: &'a mut CommandEncoder,
color: Color,
) -> RenderPass<'a> {
let rpass_descriptor = wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: framebuffer_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(color),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: Default::default(),
occlusion_query_set: Default::default(),
};

encoder.begin_render_pass(&rpass_descriptor)
}


fn draw_mandelbrot<'a>(
rpass: &mut wgpu::RenderPass<'a>,
pipeline: &'a RenderPipeline,
state: &'a MandelbrotState
) {
rpass.set_pipeline(pipeline);
rpass.set_bind_group(0, &state.uniform_bind_group, &[]);
rpass.set_vertex_buffer(0, state.vertex_buffer.slice(..));
rpass.set_index_buffer(state.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
rpass.draw_indexed(0..INDICES.len() as u32, 0, 0..1);
}


fn event<'a>(app: &mut App<MandelbrotState>, state: &mut MandelbrotState, event: WindowEvent) {
let mut x = 0f64;
let mut y = 0f64;

let moved = {
match event {
WindowEvent::Touch(a) => {
x = a.location.x;
y = a.location.y;

true
}
WindowEvent::CursorMoved { device_id, position } => {
x = position.x;
y = position.y;

true
}
_ => false
}
};

let mut scale = 0f64;
let scaled = {
match event {
WindowEvent::MouseWheel { device_id, delta, phase } => {
match delta {
sursface::winit::event::MouseScrollDelta::PixelDelta(PhysicalPosition { x: _, y: d }) => {
scale = d;
true
}
_ => false
}
}
_ => false
}
};

if moved {
state.translation.x = (x - state.translation.x) * state.translation_speed;
state.translation.y = (y - state.translation.y) * state.translation_speed;
}

if scaled {
state.scale += scale * state.scale_speed;
}
}
Loading

0 comments on commit b148941

Please sign in to comment.