mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +11:00
Start async wiring
Make async versions of the main library entry points, and invoke those from the with_winit example. Right now this just prints the contents of the bump buffer before just running the fine dispatch, but it could apply conditional logic.
This commit is contained in:
parent
17907893af
commit
418256ffd1
|
@ -22,6 +22,7 @@ use std::{borrow::Cow, path::PathBuf, time::Instant};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use vello::{
|
use vello::{
|
||||||
|
block_on_wgpu,
|
||||||
kurbo::{Affine, Vec2},
|
kurbo::{Affine, Vec2},
|
||||||
util::RenderContext,
|
util::RenderContext,
|
||||||
Renderer, Scene, SceneBuilder,
|
Renderer, Scene, SceneBuilder,
|
||||||
|
@ -187,18 +188,25 @@ async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args) {
|
||||||
.surface
|
.surface
|
||||||
.get_current_texture()
|
.get_current_texture()
|
||||||
.expect("failed to get surface texture");
|
.expect("failed to get surface texture");
|
||||||
renderer
|
let fut = async {
|
||||||
.render_to_surface(
|
renderer
|
||||||
&device_handle.device,
|
.render_to_surface_async(
|
||||||
&device_handle.queue,
|
&device_handle.device,
|
||||||
&scene,
|
&device_handle.queue,
|
||||||
&surface_texture,
|
&scene,
|
||||||
width,
|
&surface_texture,
|
||||||
height,
|
width,
|
||||||
)
|
height,
|
||||||
.expect("failed to render to surface");
|
)
|
||||||
surface_texture.present();
|
.await
|
||||||
device_handle.device.poll(wgpu::Maintain::Wait);
|
.expect("failed to render to surface");
|
||||||
|
surface_texture.present();
|
||||||
|
};
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
block_on_wgpu(&device_handle.device, fut);
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
wasm_bindgen_futures::spawn_local(fut);
|
||||||
|
device_handle.device.poll(wgpu::Maintain::Poll);
|
||||||
}
|
}
|
||||||
Event::UserEvent(event) => match event {
|
Event::UserEvent(event) => match event {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
103
src/engine.rs
103
src/engine.rs
|
@ -21,12 +21,9 @@ use std::{
|
||||||
sync::atomic::{AtomicU64, Ordering},
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures_intrusive::channel::shared::GenericOneshotReceiver;
|
|
||||||
use parking_lot::RawMutex;
|
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
util::DeviceExt, BindGroup, BindGroupLayout, Buffer, BufferAsyncError, BufferSlice,
|
util::DeviceExt, BindGroup, BindGroupLayout, Buffer, BufferUsages, ComputePipeline, Device,
|
||||||
BufferUsages, BufferView, ComputePipeline, Device, Queue, Texture, TextureAspect,
|
Queue, Texture, TextureAspect, TextureFormat, TextureUsages, TextureView, TextureViewDimension,
|
||||||
TextureFormat, TextureUsages, TextureView, TextureViewDimension,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Error = Box<dyn std::error::Error>;
|
pub type Error = Box<dyn std::error::Error>;
|
||||||
|
@ -43,6 +40,7 @@ pub struct Engine {
|
||||||
shaders: Vec<Shader>,
|
shaders: Vec<Shader>,
|
||||||
pool: ResourcePool,
|
pool: ResourcePool,
|
||||||
bind_map: BindMap,
|
bind_map: BindMap,
|
||||||
|
downloads: HashMap<Id, Buffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
|
@ -101,11 +99,6 @@ pub enum Command {
|
||||||
FreeImage(ImageProxy),
|
FreeImage(ImageProxy),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Downloads {
|
|
||||||
buf_map: HashMap<Id, Buffer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of resource that will be bound to a slot in a shader.
|
/// The type of resource that will be bound to a slot in a shader.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BindType {
|
pub enum BindType {
|
||||||
|
@ -153,6 +146,7 @@ impl Engine {
|
||||||
shaders: vec![],
|
shaders: vec![],
|
||||||
pool: Default::default(),
|
pool: Default::default(),
|
||||||
bind_map: Default::default(),
|
bind_map: Default::default(),
|
||||||
|
downloads: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +247,7 @@ impl Engine {
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
recording: &Recording,
|
recording: &Recording,
|
||||||
external_resources: &[ExternalResource],
|
external_resources: &[ExternalResource],
|
||||||
) -> Result<Downloads, Error> {
|
) -> Result<(), Error> {
|
||||||
let mut downloads = Downloads::default();
|
|
||||||
let mut free_bufs: HashSet<Id> = Default::default();
|
let mut free_bufs: HashSet<Id> = Default::default();
|
||||||
let mut free_images: HashSet<Id> = Default::default();
|
let mut free_images: HashSet<Id> = Default::default();
|
||||||
|
|
||||||
|
@ -264,7 +257,9 @@ impl Engine {
|
||||||
Command::Upload(buf_proxy, bytes) => {
|
Command::Upload(buf_proxy, bytes) => {
|
||||||
let usage =
|
let usage =
|
||||||
BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
||||||
let buf = self.pool.get_buf(buf_proxy, usage, device);
|
let buf = self
|
||||||
|
.pool
|
||||||
|
.get_buf(buf_proxy.size, buf_proxy.name, usage, device);
|
||||||
// TODO: if buffer is newly created, might be better to make it mapped at creation
|
// TODO: if buffer is newly created, might be better to make it mapped at creation
|
||||||
// and copy. However, we expect reuse will be most common.
|
// and copy. However, we expect reuse will be most common.
|
||||||
queue.write_buffer(&buf, 0, bytes);
|
queue.write_buffer(&buf, 0, bytes);
|
||||||
|
@ -273,7 +268,9 @@ impl Engine {
|
||||||
Command::UploadUniform(buf_proxy, bytes) => {
|
Command::UploadUniform(buf_proxy, bytes) => {
|
||||||
let usage = BufferUsages::UNIFORM | BufferUsages::COPY_DST;
|
let usage = BufferUsages::UNIFORM | BufferUsages::COPY_DST;
|
||||||
// Same consideration as above
|
// Same consideration as above
|
||||||
let buf = self.pool.get_buf(buf_proxy, usage, device);
|
let buf = self
|
||||||
|
.pool
|
||||||
|
.get_buf(buf_proxy.size, buf_proxy.name, usage, device);
|
||||||
queue.write_buffer(&buf, 0, bytes);
|
queue.write_buffer(&buf, 0, bytes);
|
||||||
self.bind_map.insert_buf(buf_proxy, buf);
|
self.bind_map.insert_buf(buf_proxy, buf);
|
||||||
}
|
}
|
||||||
|
@ -351,14 +348,10 @@ impl Engine {
|
||||||
.buf_map
|
.buf_map
|
||||||
.get(&proxy.id)
|
.get(&proxy.id)
|
||||||
.ok_or("buffer not in map")?;
|
.ok_or("buffer not in map")?;
|
||||||
let buf = device.create_buffer(&wgpu::BufferDescriptor {
|
let usage = BufferUsages::MAP_READ | BufferUsages::COPY_DST;
|
||||||
label: Some(proxy.name),
|
let buf = self.pool.get_buf(proxy.size, "download", usage, device);
|
||||||
size: proxy.size,
|
|
||||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
encoder.copy_buffer_to_buffer(&src_buf.buffer, 0, &buf, 0, proxy.size);
|
encoder.copy_buffer_to_buffer(&src_buf.buffer, 0, &buf, 0, proxy.size);
|
||||||
downloads.buf_map.insert(proxy.id, buf);
|
self.downloads.insert(proxy.id, buf);
|
||||||
}
|
}
|
||||||
Command::Clear(proxy, offset, size) => {
|
Command::Clear(proxy, offset, size) => {
|
||||||
let buffer = self
|
let buffer = self
|
||||||
|
@ -393,7 +386,15 @@ impl Engine {
|
||||||
drop(view);
|
drop(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(downloads)
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_download(&self, buf: BufProxy) -> Option<&Buffer> {
|
||||||
|
self.downloads.get(&buf.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_download(&mut self, buf: BufProxy) {
|
||||||
|
self.downloads.remove(&buf.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,6 +442,10 @@ impl Recording {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare a buffer for downloading.
|
||||||
|
///
|
||||||
|
/// Currently this copies to a download buffer. The original buffer can be freed
|
||||||
|
/// immediately after.
|
||||||
pub fn download(&mut self, buf: BufProxy) {
|
pub fn download(&mut self, buf: BufProxy) {
|
||||||
self.push(Command::Download(buf));
|
self.push(Command::Download(buf));
|
||||||
}
|
}
|
||||||
|
@ -603,7 +608,7 @@ impl BindMap {
|
||||||
if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) {
|
if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) {
|
||||||
let usage =
|
let usage =
|
||||||
BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
||||||
let buf = pool.get_buf(&proxy, usage, device);
|
let buf = pool.get_buf(proxy.size, proxy.name, usage, device);
|
||||||
v.insert(BindMapBuffer {
|
v.insert(BindMapBuffer {
|
||||||
buffer: buf,
|
buffer: buf,
|
||||||
label: proxy.name,
|
label: proxy.name,
|
||||||
|
@ -685,7 +690,7 @@ impl BindMap {
|
||||||
Entry::Occupied(occupied) => Ok(&occupied.into_mut().buffer),
|
Entry::Occupied(occupied) => Ok(&occupied.into_mut().buffer),
|
||||||
Entry::Vacant(vacant) => {
|
Entry::Vacant(vacant) => {
|
||||||
let usage = BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
let usage = BufferUsages::COPY_SRC | BufferUsages::COPY_DST | BufferUsages::STORAGE;
|
||||||
let buf = pool.get_buf(&proxy, usage, device);
|
let buf = pool.get_buf(proxy.size, proxy.name, usage, device);
|
||||||
Ok(&vacant
|
Ok(&vacant
|
||||||
.insert(BindMapBuffer {
|
.insert(BindMapBuffer {
|
||||||
buffer: buf,
|
buffer: buf,
|
||||||
|
@ -697,52 +702,22 @@ impl BindMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DownloadsMapped<'a>(
|
|
||||||
HashMap<
|
|
||||||
Id,
|
|
||||||
(
|
|
||||||
BufferSlice<'a>,
|
|
||||||
GenericOneshotReceiver<RawMutex, Result<(), BufferAsyncError>>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Downloads {
|
|
||||||
// Discussion: should API change so we get one buffer, rather than mapping all?
|
|
||||||
pub fn map(&self) -> DownloadsMapped {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
for (id, buf) in &self.buf_map {
|
|
||||||
let buf_slice = buf.slice(..);
|
|
||||||
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
|
|
||||||
buf_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
|
|
||||||
map.insert(*id, (buf_slice, receiver));
|
|
||||||
}
|
|
||||||
DownloadsMapped(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DownloadsMapped<'a> {
|
|
||||||
pub async fn get_mapped(&self, proxy: BufProxy) -> Result<BufferView, Error> {
|
|
||||||
let (slice, recv) = self.0.get(&proxy.id).ok_or("buffer not in map")?;
|
|
||||||
if let Some(recv_result) = recv.receive().await {
|
|
||||||
recv_result?;
|
|
||||||
} else {
|
|
||||||
return Err("channel was closed".into());
|
|
||||||
}
|
|
||||||
Ok(slice.get_mapped_range())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SIZE_CLASS_BITS: u32 = 1;
|
const SIZE_CLASS_BITS: u32 = 1;
|
||||||
|
|
||||||
impl ResourcePool {
|
impl ResourcePool {
|
||||||
/// Get a buffer from the pool or create one.
|
/// Get a buffer from the pool or create one.
|
||||||
fn get_buf(&mut self, proxy: &BufProxy, usage: BufferUsages, device: &Device) -> Buffer {
|
fn get_buf(
|
||||||
let rounded_size = Self::size_class(proxy.size, SIZE_CLASS_BITS);
|
&mut self,
|
||||||
|
size: u64,
|
||||||
|
name: &'static str,
|
||||||
|
usage: BufferUsages,
|
||||||
|
device: &Device,
|
||||||
|
) -> Buffer {
|
||||||
|
let rounded_size = Self::size_class(size, SIZE_CLASS_BITS);
|
||||||
let props = BufferProperties {
|
let props = BufferProperties {
|
||||||
size: rounded_size,
|
size: rounded_size,
|
||||||
usages: usage,
|
usages: usage,
|
||||||
name: proxy.name,
|
name: name,
|
||||||
};
|
};
|
||||||
if let Some(buf_vec) = self.bufs.get_mut(&props) {
|
if let Some(buf_vec) = self.bufs.get_mut(&props) {
|
||||||
if let Some(buf) = buf_vec.pop() {
|
if let Some(buf) = buf_vec.pop() {
|
||||||
|
@ -751,7 +726,7 @@ impl ResourcePool {
|
||||||
}
|
}
|
||||||
device.create_buffer(&wgpu::BufferDescriptor {
|
device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
#[cfg(feature = "buffer_labels")]
|
#[cfg(feature = "buffer_labels")]
|
||||||
label: Some(proxy.name),
|
label: Some(name),
|
||||||
#[cfg(not(feature = "buffer_labels"))]
|
#[cfg(not(feature = "buffer_labels"))]
|
||||||
label: None,
|
label: None,
|
||||||
size: rounded_size,
|
size: rounded_size,
|
||||||
|
|
106
src/lib.rs
106
src/lib.rs
|
@ -29,9 +29,11 @@ pub mod encoding;
|
||||||
pub mod glyph;
|
pub mod glyph;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
use render::Render;
|
||||||
pub use scene::{Scene, SceneBuilder, SceneFragment};
|
pub use scene::{Scene, SceneBuilder, SceneFragment};
|
||||||
|
pub use util::block_on_wgpu;
|
||||||
|
|
||||||
use engine::{Engine, ExternalResource};
|
use engine::{Engine, ExternalResource, Recording};
|
||||||
use shaders::FullShaders;
|
use shaders::FullShaders;
|
||||||
|
|
||||||
use wgpu::{Device, Queue, SurfaceTexture, TextureFormat, TextureView};
|
use wgpu::{Device, Queue, SurfaceTexture, TextureFormat, TextureView};
|
||||||
|
@ -83,8 +85,7 @@ impl Renderer {
|
||||||
*target.as_image().unwrap(),
|
*target.as_image().unwrap(),
|
||||||
texture,
|
texture,
|
||||||
)];
|
)];
|
||||||
let _ = self
|
self.engine
|
||||||
.engine
|
|
||||||
.run_recording(device, queue, &recording, &external_resources)?;
|
.run_recording(device, queue, &recording, &external_resources)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -164,6 +165,105 @@ impl Renderer {
|
||||||
self.shaders = shaders;
|
self.shaders = shaders;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renders a scene to the target texture.
|
||||||
|
///
|
||||||
|
/// The texture is assumed to be of the specified dimensions and have been created with
|
||||||
|
/// the [wgpu::TextureFormat::Rgba8Unorm] format and the [wgpu::TextureUsages::STORAGE_BINDING]
|
||||||
|
/// flag set.
|
||||||
|
pub async fn render_to_texture_async(
|
||||||
|
&mut self,
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
scene: &Scene,
|
||||||
|
texture: &TextureView,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut render = Render::new();
|
||||||
|
let encoding = scene.data();
|
||||||
|
let recording = render.render_encoding_coarse(encoding, &self.shaders, width, height);
|
||||||
|
let target = render.out_image();
|
||||||
|
let bump_buf = render.bump_buf();
|
||||||
|
self.engine.run_recording(device, queue, &recording, &[])?;
|
||||||
|
if let Some(bump_buf) = self.engine.get_download(bump_buf) {
|
||||||
|
let buf_slice = bump_buf.slice(..);
|
||||||
|
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
|
||||||
|
buf_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
|
||||||
|
if let Some(recv_result) = receiver.receive().await {
|
||||||
|
recv_result?;
|
||||||
|
} else {
|
||||||
|
return Err("channel was closed".into());
|
||||||
|
}
|
||||||
|
let mapped = buf_slice.get_mapped_range();
|
||||||
|
println!("{:?}", bytemuck::cast_slice::<_, u32>(&mapped));
|
||||||
|
}
|
||||||
|
// TODO: apply logic to determine whether we need to rerun coarse, and also
|
||||||
|
// allocate the blend stack as needed.
|
||||||
|
self.engine.free_download(bump_buf);
|
||||||
|
// Maybe clear to reuse allocation?
|
||||||
|
let mut recording = Recording::default();
|
||||||
|
render.record_fine(&self.shaders, &mut recording);
|
||||||
|
let external_resources = [ExternalResource::Image(target, texture)];
|
||||||
|
self.engine
|
||||||
|
.run_recording(device, queue, &recording, &external_resources)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn render_to_surface_async(
|
||||||
|
&mut self,
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
scene: &Scene,
|
||||||
|
surface: &SurfaceTexture,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut target = self
|
||||||
|
.target
|
||||||
|
.take()
|
||||||
|
.unwrap_or_else(|| TargetTexture::new(device, width, height));
|
||||||
|
// TODO: implement clever resizing semantics here to avoid thrashing the memory allocator
|
||||||
|
// during resize, specifically on metal.
|
||||||
|
if target.width != width || target.height != height {
|
||||||
|
target = TargetTexture::new(device, width, height);
|
||||||
|
}
|
||||||
|
self.render_to_texture_async(device, queue, scene, &target.view, width, height)
|
||||||
|
.await?;
|
||||||
|
let mut encoder =
|
||||||
|
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
{
|
||||||
|
let surface_view = surface
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &self.blit.bind_layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&target.view),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color::default()),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
render_pass.set_pipeline(&self.blit.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &bind_group, &[]);
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
queue.submit(Some(encoder.finish()));
|
||||||
|
self.target = Some(target);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TargetTexture {
|
struct TargetTexture {
|
||||||
|
|
|
@ -196,6 +196,8 @@ pub fn render_encoding_full(
|
||||||
height: u32,
|
height: u32,
|
||||||
) -> (Recording, ResourceProxy) {
|
) -> (Recording, ResourceProxy) {
|
||||||
let mut render = Render::new();
|
let mut render = Render::new();
|
||||||
|
// TODO: leaks the download of the bump buf; a good way to fix would be to conditionalize
|
||||||
|
// that download.
|
||||||
let mut recording = render.render_encoding_coarse(encoding, shaders, width, height);
|
let mut recording = render.render_encoding_coarse(encoding, shaders, width, height);
|
||||||
let out_image = render.out_image();
|
let out_image = render.out_image();
|
||||||
render.record_fine(shaders, &mut recording);
|
render.record_fine(shaders, &mut recording);
|
||||||
|
@ -524,13 +526,14 @@ impl Render {
|
||||||
info_bin_data_buf,
|
info_bin_data_buf,
|
||||||
out_image,
|
out_image,
|
||||||
});
|
});
|
||||||
|
recording.download(*bump_buf.as_buf().unwrap());
|
||||||
|
recording.free_resource(bump_buf);
|
||||||
recording
|
recording
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run fine rasterization assuming the coarse phase succeeded.
|
/// Run fine rasterization assuming the coarse phase succeeded.
|
||||||
pub fn record_fine(&mut self, shaders: &FullShaders, recording: &mut Recording) {
|
pub fn record_fine(&mut self, shaders: &FullShaders, recording: &mut Recording) {
|
||||||
let fine = self.fine.take().unwrap();
|
let fine = self.fine.take().unwrap();
|
||||||
recording.free_resource(fine.bump_buf);
|
|
||||||
recording.dispatch(
|
recording.dispatch(
|
||||||
shaders.fine,
|
shaders.fine,
|
||||||
(self.width_in_tiles, self.height_in_tiles, 1),
|
(self.width_in_tiles, self.height_in_tiles, 1),
|
||||||
|
@ -559,4 +562,8 @@ impl Render {
|
||||||
pub fn out_image(&self) -> ImageProxy {
|
pub fn out_image(&self) -> ImageProxy {
|
||||||
self.fine.as_ref().unwrap().out_image
|
self.fine.as_ref().unwrap().out_image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bump_buf(&self) -> BufProxy {
|
||||||
|
*self.fine.as_ref().unwrap().bump_buf.as_buf().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
26
src/util.rs
26
src/util.rs
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
//! Simple helpers for managing wgpu state and surfaces.
|
//! Simple helpers for managing wgpu state and surfaces.
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
|
|
||||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||||
|
@ -132,3 +134,27 @@ pub struct RenderSurface {
|
||||||
pub config: SurfaceConfiguration,
|
pub config: SurfaceConfiguration,
|
||||||
pub dev_id: usize,
|
pub dev_id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NullWake;
|
||||||
|
|
||||||
|
impl std::task::Wake for NullWake {
|
||||||
|
fn wake(self: std::sync::Arc<Self>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block on a future, polling the device as needed.
|
||||||
|
///
|
||||||
|
/// This will deadlock if the future is awaiting anything other than GPU progress.
|
||||||
|
pub fn block_on_wgpu<F: Future>(device: &Device, mut fut: F) -> F::Output {
|
||||||
|
let waker = std::task::Waker::from(std::sync::Arc::new(NullWake));
|
||||||
|
let mut context = std::task::Context::from_waker(&waker);
|
||||||
|
// Same logic as `pin_mut!` macro from `pin_utils`.
|
||||||
|
let mut fut = unsafe { std::pin::Pin::new_unchecked(&mut fut) };
|
||||||
|
loop {
|
||||||
|
match fut.as_mut().poll(&mut context) {
|
||||||
|
std::task::Poll::Pending => {
|
||||||
|
device.poll(wgpu::Maintain::Wait);
|
||||||
|
}
|
||||||
|
std::task::Poll::Ready(item) => break item,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue