mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-08 20:01:30 +11:00
Start image rendering
Populates the piet-gpu subdir, with an extremely simple renderer. The main program saves the image to a PNG. Contains a few fixes (I was confused about the need for multiple bindings, as opposed to multiple descriptors within a binding).
This commit is contained in:
parent
5c147b8576
commit
86e52a3f47
72
Cargo.lock
generated
72
Cargo.lock
generated
|
@ -1,5 +1,11 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
|
||||
[[package]]
|
||||
name = "ash"
|
||||
version = "0.30.0"
|
||||
|
@ -9,12 +15,58 @@ dependencies = [
|
|||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflate"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.5.2"
|
||||
|
@ -25,6 +77,14 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "piet-gpu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"piet-gpu-hal",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "piet-gpu-derive"
|
||||
version = "0.0.0"
|
||||
|
@ -41,6 +101,18 @@ dependencies = [
|
|||
"ash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.10"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
|
||||
members = [
|
||||
"piet-gpu",
|
||||
"piet-gpu-derive",
|
||||
"piet-gpu-hal"
|
||||
]
|
||||
|
|
|
@ -74,6 +74,6 @@ pub trait CmdBuf<D: Device> {
|
|||
unsafe fn write_timestamp(&mut self, pool: &D::QueryPool, query: u32);
|
||||
}
|
||||
|
||||
pub trait MemFlags: Sized {
|
||||
pub trait MemFlags: Sized + Clone + Copy {
|
||||
fn host_coherent() -> Self;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ pub struct QueryPool {
|
|||
n_queries: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MemFlags(vk::MemoryPropertyFlags);
|
||||
|
||||
impl VkInstance {
|
||||
|
@ -169,23 +170,25 @@ impl crate::Device for VkDevice {
|
|||
|
||||
/// This creates a pipeline that runs over the buffer.
|
||||
///
|
||||
/// The code is included from "../comp.spv", and the descriptor set layout is just some
|
||||
/// number of buffers.
|
||||
/// The descriptor set layout is just some number of buffers (this will change).
|
||||
unsafe fn create_simple_compute_pipeline(
|
||||
&self,
|
||||
code: &[u8],
|
||||
n_buffers: u32,
|
||||
) -> Result<Pipeline, Error> {
|
||||
let device = &self.device.device;
|
||||
let descriptor_set_layout = device.create_descriptor_set_layout(
|
||||
&vk::DescriptorSetLayoutCreateInfo::builder().bindings(&[
|
||||
let bindings = (0..n_buffers)
|
||||
.map(|i| {
|
||||
vk::DescriptorSetLayoutBinding::builder()
|
||||
.binding(0)
|
||||
.binding(i)
|
||||
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
|
||||
.descriptor_count(n_buffers)
|
||||
.descriptor_count(1)
|
||||
.stage_flags(vk::ShaderStageFlags::COMPUTE)
|
||||
.build(),
|
||||
]),
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let descriptor_set_layout = device.create_descriptor_set_layout(
|
||||
&vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings),
|
||||
None,
|
||||
)?;
|
||||
|
||||
|
@ -248,25 +251,22 @@ impl crate::Device for VkDevice {
|
|||
.set_layouts(&descriptor_set_layouts),
|
||||
)
|
||||
.unwrap();
|
||||
let buf_infos = bufs
|
||||
.iter()
|
||||
.map(|buf| {
|
||||
vk::DescriptorBufferInfo::builder()
|
||||
.buffer(buf.buffer)
|
||||
.offset(0)
|
||||
.range(vk::WHOLE_SIZE)
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
device.update_descriptor_sets(
|
||||
&[vk::WriteDescriptorSet::builder()
|
||||
.dst_set(descriptor_sets[0])
|
||||
.dst_binding(0)
|
||||
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
|
||||
.buffer_info(&buf_infos)
|
||||
.build()],
|
||||
&[],
|
||||
);
|
||||
for (i, buf) in bufs.iter().enumerate() {
|
||||
let buf_info = vk::DescriptorBufferInfo::builder()
|
||||
.buffer(buf.buffer)
|
||||
.offset(0)
|
||||
.range(vk::WHOLE_SIZE)
|
||||
.build();
|
||||
device.update_descriptor_sets(
|
||||
&[vk::WriteDescriptorSet::builder()
|
||||
.dst_set(descriptor_sets[0])
|
||||
.dst_binding(i as u32)
|
||||
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
|
||||
.buffer_info(&[buf_info])
|
||||
.build()],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
Ok(DescriptorSet {
|
||||
descriptor_set: descriptor_sets[0],
|
||||
})
|
||||
|
@ -321,7 +321,10 @@ impl crate::Device for VkDevice {
|
|||
device.destroy_query_pool(pool.pool, None);
|
||||
let ts0 = buf[0];
|
||||
let tsp = self.timestamp_period as f64 * 1e-9;
|
||||
let result = buf[1..].iter().map(|ts| ts.wrapping_sub(ts0) as f64 * tsp).collect();
|
||||
let result = buf[1..]
|
||||
.iter()
|
||||
.map(|ts| ts.wrapping_sub(ts0) as f64 * tsp)
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
@ -354,7 +357,7 @@ impl crate::Device for VkDevice {
|
|||
result: &mut Vec<T>,
|
||||
) -> Result<(), Error> {
|
||||
let device = &self.device.device;
|
||||
let size = buffer.size as usize;
|
||||
let size = buffer.size as usize / std::mem::size_of::<T>();
|
||||
let buf = device.map_memory(
|
||||
buffer.buffer_memory,
|
||||
0,
|
||||
|
|
13
piet-gpu/Cargo.toml
Normal file
13
piet-gpu/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "piet-gpu"
|
||||
version = "0.1.0"
|
||||
authors = ["Raph Levien <raph.levien@gmail.com>"]
|
||||
description = "A compute-centric GPU 2D renderer."
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.piet-gpu-hal]
|
||||
path = "../piet-gpu-hal"
|
||||
|
||||
[dependencies]
|
||||
png = "0.16.2"
|
10
piet-gpu/shader/build.ninja
Normal file
10
piet-gpu/shader/build.ninja
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Build file for shaders.
|
||||
|
||||
# You must have glslangValidator in your path, or patch here.
|
||||
|
||||
glslang_validator = glslangValidator
|
||||
|
||||
rule glsl
|
||||
command = $glslang_validator -V -o $out $in
|
||||
|
||||
build image.spv: glsl image.comp
|
31
piet-gpu/shader/image.comp
Normal file
31
piet-gpu/shader/image.comp
Normal file
|
@ -0,0 +1,31 @@
|
|||
// A simple kernel to create an image.
|
||||
|
||||
// Right now, this kernel stores the image in a buffer, but a better
|
||||
// plan is to use a texture. This is because of limited support.
|
||||
|
||||
#version 450
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
layout(set = 0, binding = 0) readonly buffer SceneBuf {
|
||||
uint[] scene;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 1) buffer ImageBuf {
|
||||
uint[] image;
|
||||
};
|
||||
|
||||
// TODO: make the image size dynamic.
|
||||
#define IMAGE_WIDTH 2048
|
||||
#define IMAGE_HEIGHT 1535
|
||||
|
||||
void main() {
|
||||
uvec2 xy = gl_GlobalInvocationID.xy;
|
||||
vec2 uv = vec2(xy) * vec2(1.0 / IMAGE_WIDTH, 1.0 / IMAGE_HEIGHT);
|
||||
vec4 rgba = vec4(uv.xyy, 1.0);
|
||||
uvec4 s = uvec4(round(rgba * 255.0));
|
||||
uint rgba_packed = s.x | (s.y << 8) | (s.z << 16) | (s.w << 24);
|
||||
image[xy.y * IMAGE_WIDTH + xy.x] = rgba_packed;
|
||||
if (xy.y == 0 && xy.x < 8) {
|
||||
image[xy.x] = scene[xy.x];
|
||||
}
|
||||
}
|
BIN
piet-gpu/shader/image.spv
Normal file
BIN
piet-gpu/shader/image.spv
Normal file
Binary file not shown.
63
piet-gpu/src/main.rs
Normal file
63
piet-gpu/src/main.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
|
||||
use piet_gpu_hal::vulkan::VkInstance;
|
||||
use piet_gpu_hal::{CmdBuf, Device, MemFlags};
|
||||
|
||||
const WIDTH: usize = 2048;
|
||||
const HEIGHT: usize = 1536;
|
||||
|
||||
const TILE_W: usize = 16;
|
||||
const TILE_H: usize = 16;
|
||||
|
||||
fn main() {
|
||||
let instance = VkInstance::new().unwrap();
|
||||
unsafe {
|
||||
let device = instance.device().unwrap();
|
||||
let mem_flags = MemFlags::host_coherent();
|
||||
let src = (0..256).map(|x| x + 1).collect::<Vec<u32>>();
|
||||
let scene_buf = device
|
||||
.create_buffer(std::mem::size_of_val(&src[..]) as u64, mem_flags)
|
||||
.unwrap();
|
||||
device.write_buffer(&scene_buf, &src).unwrap();
|
||||
let image_buf = device
|
||||
.create_buffer((WIDTH * HEIGHT * 4) as u64, mem_flags)
|
||||
.unwrap();
|
||||
let code = include_bytes!("../shader/image.spv");
|
||||
let pipeline = device.create_simple_compute_pipeline(code, 2).unwrap();
|
||||
let descriptor_set = device
|
||||
.create_descriptor_set(&pipeline, &[&scene_buf, &image_buf])
|
||||
.unwrap();
|
||||
let query_pool = device.create_query_pool(2).unwrap();
|
||||
let mut cmd_buf = device.create_cmd_buf().unwrap();
|
||||
cmd_buf.begin();
|
||||
cmd_buf.write_timestamp(&query_pool, 0);
|
||||
cmd_buf.dispatch(
|
||||
&pipeline,
|
||||
&descriptor_set,
|
||||
((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1),
|
||||
);
|
||||
cmd_buf.write_timestamp(&query_pool, 1);
|
||||
cmd_buf.finish();
|
||||
device.run_cmd_buf(&cmd_buf).unwrap();
|
||||
let timestamps = device.reap_query_pool(query_pool).unwrap();
|
||||
println!("Render time: {:.3}ms", timestamps[0] * 1e3);
|
||||
let mut img_data: Vec<u8> = Default::default();
|
||||
// Note: because png can use a `&[u8]` slice, we could avoid an extra copy
|
||||
// (probably passing a slice into a closure). But for now: keep it simple.
|
||||
device.read_buffer(&image_buf, &mut img_data).unwrap();
|
||||
|
||||
// Write image as PNG file.
|
||||
let path = Path::new("image.png");
|
||||
let file = File::create(path).unwrap();
|
||||
let ref mut w = BufWriter::new(file);
|
||||
|
||||
let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32);
|
||||
encoder.set_color(png::ColorType::RGBA);
|
||||
encoder.set_depth(png::BitDepth::Eight);
|
||||
let mut writer = encoder.write_header().unwrap();
|
||||
|
||||
writer.write_image_data(&img_data).unwrap();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue