Skip to content

Commit

Permalink
Draw triangle
Browse files Browse the repository at this point in the history
  • Loading branch information
nanoqsh committed Jan 1, 2024
1 parent 4aeadd9 commit cdc1bc9
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 164 deletions.
15 changes: 9 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install mesa
- name: Build
run: cargo build --profile ci --verbose
- name: Clippy
run: cargo --profile ci clippy
- name: Install vulkan
run: |
set -e
sudo apt-get update -y -qq
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
sudo add-apt-repository ppa:kisak/kisak-mesa
sudo apt-get update
sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk mesa-vulkan-drivers
- name: Build
run: cargo build --verbose
- name: Clippy
run: cargo clippy
- name: Tests
run: cargo test --verbose
run: cargo test --profile ci --verbose
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["dunge", "dunge_macros", "dunge_shader"]
members = ["dunge", "dunge_macros", "dunge_shader", "helpers"]
default-members = ["dunge"]
resolver = "2"

Expand All @@ -16,6 +16,13 @@ split-debuginfo = "unpacked"
[profile.dev.package."*"]
opt-level = 3

[profile.ci]
inherits = "dev"
opt-level = 1

[profile.ci.package."*"]
opt-level = 1

[profile.release]
codegen-units = 1
lto = true
Expand Down
2 changes: 2 additions & 0 deletions dunge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ default-features = false
features = ["android-native-activity"]

[dev-dependencies]
helpers = { path = "../helpers" }
async-channel = "2.1"
glam = "0.25"
futures = { version = "2.1", package = "futures-lite" }

Expand Down
16 changes: 13 additions & 3 deletions dunge/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use {
},
vertex::Vertex,
},
std::{future::IntoFuture, sync::Arc},
std::{error, fmt, future::IntoFuture, sync::Arc},
};

#[derive(Clone)]
Expand Down Expand Up @@ -62,13 +62,12 @@ impl Context {
CopyBuffer::new(&self.0, size)
}

pub async fn map_view<'a, S, R, F>(&self, view: CopyBufferView<'a>, chan: (S, R)) -> Mapped<'a>
pub async fn map_view<'a, S, R, F>(&self, view: CopyBufferView<'a>, tx: S, rx: R) -> Mapped<'a>
where
S: FnOnce(MapResult) + wgpu::WasmNotSend + 'static,
R: FnOnce() -> F,
F: IntoFuture<Output = MapResult>,
{
let (tx, rx) = chan;
view.map(&self.0, tx, rx).await
}

Expand Down Expand Up @@ -100,3 +99,14 @@ pub enum Error {
BackendSelection,
RequestDevice,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BackendSelection => write!(f, "failed to select backend"),
Self::RequestDevice => write!(f, "failed to get device"),
}
}
}

impl error::Error for Error {}
25 changes: 22 additions & 3 deletions dunge/src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use {
state::State,
vertex::{self, Vertex},
},
std::{borrow::Cow, marker::PhantomData, mem, slice},
std::{borrow::Cow, error, fmt, marker::PhantomData, mem, slice},
wgpu::{Buffer, RenderPass},
};

Expand All @@ -28,7 +28,7 @@ impl<'a, V> Data<'a, V> {
pub fn new(verts: &'a [V], indxs: &'a [Face]) -> Result<Self, Error> {
let len: u16 = verts.len().try_into().map_err(|_| Error::TooManyVertices)?;
if let Some(index) = indxs.iter().flatten().copied().find(|&i| i >= len) {
return Err(Error::WrongIndex { index });
return Err(Error::InvalidIndex { index });
}

let indxs = Some(Cow::Borrowed(indxs));
Expand Down Expand Up @@ -68,13 +68,32 @@ pub enum Error {
TooManyVertices,

/// The vertex index is out of bounds of the vertex slice.
WrongIndex { index: u16 },
InvalidIndex { index: u16 },
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooManyVertices => write!(f, "too many vertices"),
Self::InvalidIndex { index } => write!(f, "invalid index: {index}"),
}
}
}

impl error::Error for Error {}

/// Vertices length doesn't fit in [`u16`](std::u16) integer.
#[derive(Debug)]
pub struct TooManyVertices;

impl fmt::Display for TooManyVertices {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "too many vertices")
}
}

impl error::Error for TooManyVertices {}

pub struct Mesh<V> {
verts: Buffer,
indxs: Option<Buffer>,
Expand Down
2 changes: 1 addition & 1 deletion dunge/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub struct Render {

#[derive(Clone, Copy, Default)]
pub struct Options {
clear_color: Option<Rgba>,
pub clear_color: Option<Rgba>,
}

impl Options {
Expand Down
41 changes: 30 additions & 11 deletions dunge/src/texture.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::state::State,
std::{future::IntoFuture, mem},
std::{error, fmt, future::IntoFuture, mem},
wgpu::{
Buffer, BufferAsyncError, BufferSlice, BufferView, CommandEncoder, TextureFormat,
TextureUsages, TextureView, WasmNotSend,
Expand Down Expand Up @@ -71,10 +71,29 @@ pub enum Error {
InvalidLen,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ZeroSized => write!(f, "zero sized data"),
Self::InvalidLen => write!(f, "invalid data length"),
}
}
}

impl error::Error for Error {}

/// The [texture data](crate::texture::Data) is zero sized.
#[derive(Debug)]
pub struct ZeroSized;

impl fmt::Display for ZeroSized {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "zero sized data")
}
}

impl error::Error for ZeroSized {}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Format {
RgbAlpha,
Expand Down Expand Up @@ -174,6 +193,16 @@ impl Texture {
}
}

pub(crate) fn make<M>(state: &State, data: M) -> M::Out
where
M: Make,
{
data.make(Maker {
state,
usage: TextureUsages::empty(),
})
}

pub struct Sampler(wgpu::Sampler);

impl Sampler {
Expand All @@ -198,16 +227,6 @@ impl Sampler {
}
}

pub(crate) fn make<M>(state: &State, data: M) -> M::Out
where
M: Make,
{
data.make(Maker {
state,
usage: TextureUsages::empty(),
})
}

pub struct CopyBuffer {
buf: Buffer,
size: (u32, u32),
Expand Down
80 changes: 74 additions & 6 deletions dunge/tests/triangle.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,88 @@
use {
dunge::{
context::{Context, Error},
sl::{Index, Out},
color::Rgba,
context::Context,
draw,
sl::{self, Index, Out},
state::{Options, Render},
texture::{Data, Format},
},
futures::future,
glam::Vec4,
helpers::Image,
std::{error, fs},
};

type Error = Box<dyn error::Error>;

#[test]
fn render() -> Result<(), Error> {
let triangle = |Index(_): Index| Out {
place: Vec4::splat(1.),
color: Vec4::splat(1.),
use std::f32::consts;

const THIRD: f32 = consts::TAU / 3.;

let triangle = |Index(index): Index| {
let [x, y] = sl::share(sl::f32(index) * THIRD);
Out {
place: sl::vec4(sl::cos(x), sl::sin(y), 0., 1.),
color: Vec4::new(1., 0., 0., 1.),
}
};

let cx = future::block_on(Context::new())?;
_ = cx.make_shader(triangle);
let shader = cx.make_shader(triangle);
let bind = cx.make_binder(&shader).into_binding();
let layer = cx.make_layer(Format::RgbAlpha, &shader);

let size = (300, 300);
let view = {
let data = Data::empty(size, Format::RgbAlpha)?.with_draw().with_copy();
cx.make_texture(data)
};

let clear = Rgba::from_standard([0., 0., 0., 1.]);
let buffer = cx.make_copy_buffer(size);
let options = Options {
clear_color: Some(clear),
};

let draw = draw::from_fn(|mut frame| {
frame.layer(&layer, options).bind(&bind).draw_triangles(1);
frame.copy_texture(&buffer, &view);
});

let mut render = Render::default();
cx.draw_to_texture(&mut render, &view, draw);

let mapped = future::block_on({
let (tx, rx) = async_channel::bounded(1);
cx.map_view(
buffer.view(),
move |r| tx.send_blocking(r).expect("send mapped result"),
|| async move { rx.recv().await.expect("recv mapped result") },
)
});

let mapped = mapped.data();
let image = Image {
data: {
let (width, height) = size;
let mut data = vec![0; (width * height * 4) as usize].into_boxed_slice();
for y in 0..height {
for x in 0..width {
let (actual_width, _) = buffer.size();
let idx = x + y * actual_width;
let loc = (x + y * width) * 4;
data[loc as usize..loc as usize + 4].copy_from_slice(&mapped[idx as usize]);
}
}

data
},
size,
};

let data = helpers::encode_png(&image);
fs::write("tests/triangle.png", data)?;
Ok(())
}
Loading

0 comments on commit cdc1bc9

Please sign in to comment.