Implement headless rendering

This commit is contained in:
Maximilian Ammann 2022-05-22 12:46:33 +02:00
parent e59dacdb45
commit 1e63718dfc
16 changed files with 478 additions and 113 deletions

View File

@ -17,6 +17,9 @@ env_logger = "0.9"
maplibre = { path = "../maplibre", version = "0.0.2" }
maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
tokio = "1.18"
wgpu = "0.12"
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", optional = true }
tracing-tracy = { version = "0.8", optional = true }

View File

@ -1,8 +1,15 @@
use maplibre::error::Error;
use maplibre::io::scheduler::ScheduleMethod;
use maplibre::io::source_client::HTTPClient;
use maplibre::map_state::MapSchedule;
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::render::settings::RendererSettings;
use maplibre::window::{MapWindow, MapWindowConfig, Runnable, WindowSize};
use maplibre::MapBuilder;
use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
use wgpu::TextureFormat;
#[cfg(feature = "trace")]
fn enable_tracing() {
@ -13,6 +20,54 @@ fn enable_tracing() {
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}
pub struct HeadlessMapWindowConfig;
impl MapWindowConfig for HeadlessMapWindowConfig {
type MapWindow = HeadlessMapWindow;
fn create(&self) -> Self::MapWindow {
Self::MapWindow {}
}
}
pub struct HeadlessMapWindow;
impl MapWindow for HeadlessMapWindow {
type EventLoop = ();
type RawWindow = ();
fn size(&self) -> WindowSize {
WindowSize::new(1920, 1080).unwrap()
}
fn inner(&self) -> &Self::RawWindow {
&()
}
}
impl<MWC, SM, HC> Runnable<MWC, SM, HC> for HeadlessMapWindow
where
MWC: MapWindowConfig<MapWindow = HeadlessMapWindow>,
SM: ScheduleMethod,
HC: HTTPClient,
{
fn run(mut self, mut map_state: MapSchedule<MWC, SM, HC>, max_frames: Option<u64>) {
let arc = map_state.map_context.renderer.device.clone();
tokio::task::spawn_blocking(move || loop {
arc.poll(wgpu::Maintain::Wait);
});
for i in 0..3 {
match map_state.update_and_redraw() {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
if e.should_exit() {}
}
e => eprintln!("{:?}", e),
};
}
}
}
fn run_in_window() {
run_multithreaded(async {
@ -27,11 +82,28 @@ fn run_in_window() {
})
}
fn run_headless() {
run_multithreaded(async {
MapBuilder::new()
.with_map_window_config(HeadlessMapWindowConfig)
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.with_renderer_settings(RendererSettings {
texture_format: TextureFormat::Rgba8UnormSrgb,
..RendererSettings::default()
})
.build()
.initialize_headless()
.await
.run()
})
}
fn main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
#[cfg(feature = "trace")]
enable_tracing();
run_in_window()
run_headless()
}

View File

@ -47,10 +47,6 @@ impl WinitMapWindowConfig {
}
}
impl MapWindowConfig for WinitMapWindowConfig {
type MapWindow = WinitMapWindow;
}
pub struct WinitMapWindow {
window: WinitWindow,
event_loop: Option<WinitEventLoop>,

View File

@ -10,25 +10,11 @@ use super::WinitMapWindow;
use super::WinitWindow;
use super::WinitMapWindowConfig;
use maplibre::window::{MapWindow, WindowSize};
use maplibre::window::{MapWindow, MapWindowConfig, WindowSize};
impl MapWindow for WinitMapWindow {
type EventLoop = WinitEventLoop;
type Window = WinitWindow;
type MapWindowConfig = WinitMapWindowConfig;
fn create(map_window_config: &Self::MapWindowConfig) -> Self {
let event_loop = WinitEventLoop::new();
let window = WindowBuilder::new()
.with_title(&map_window_config.title)
.build(&event_loop)
.unwrap();
Self {
window,
event_loop: Some(event_loop),
}
}
type RawWindow = WinitWindow;
fn size(&self) -> WindowSize {
let size = self.window.inner_size();
@ -44,7 +30,24 @@ impl MapWindow for WinitMapWindow {
window_size
}
fn inner(&self) -> &Self::Window {
fn inner(&self) -> &Self::RawWindow {
&self.window
}
}
impl MapWindowConfig for WinitMapWindowConfig {
type MapWindow = WinitMapWindow;
fn create(&self) -> Self::MapWindow {
let event_loop = WinitEventLoop::new();
let window = WindowBuilder::new()
.with_title(&self.title)
.build(&event_loop)
.unwrap();
Self::MapWindow {
window,
event_loop: Some(event_loop),
}
}
}

View File

@ -10,10 +10,40 @@ use winit::platform::web::WindowBuilderExtWebSys;
impl MapWindow for WinitMapWindow {
type EventLoop = WinitEventLoop;
type Window = WinitWindow;
type RawWindow = WinitWindow;
type MapWindowConfig = WinitMapWindowConfig;
fn create(map_window_config: &Self::MapWindowConfig) -> Self {
fn size(&self) -> WindowSize {
let size = self.window.inner_size();
WindowSize::new(size.width, size.height).expect("failed to get window dimensions.")
}
fn inner(&self) -> &Self::RawWindow {
&self.window
}
}
fn create(map_window_config: &Self::MapWindowConfig) -> Self {
let event_loop = WinitEventLoop::new();
let window: winit::window::Window = WindowBuilder::new()
.with_canvas(Some(get_canvas(&map_window_config.canvas_id)))
.build(&event_loop)
.unwrap();
let size = get_body_size().unwrap();
window.set_inner_size(size);
Self {
window,
event_loop: Some(event_loop),
}
}
impl MapWindowConfig for WinitMapWindowConfig {
type MapWindow = WinitMapWindow;
fn create(&self) -> Self::MapWindow {
let event_loop = WinitEventLoop::new();
let window: winit::window::Window = WindowBuilder::new()
@ -23,21 +53,11 @@ impl MapWindow for WinitMapWindow {
let size = get_body_size().unwrap();
window.set_inner_size(size);
Self {
Self::MapWindow {
window,
event_loop: Some(event_loop),
}
}
fn size(&self) -> WindowSize {
let size = self.window.inner_size();
WindowSize::new(size.width, size.height).expect("failed to get window dimensions.")
}
fn inner(&self) -> &Self::Window {
&self.window
}
}
pub fn get_body_size() -> Option<winit::dpi::LogicalSize<i32>> {

View File

@ -72,5 +72,7 @@ thiserror = "1"
downcast-rs = "1.2"
smallvec = "1.8"
png = "0.17"
[build-dependencies]
maplibre-build-tools = { path = "../maplibre-build-tools", version = "0.1.0", features = ["sqlite"] }

View File

@ -22,7 +22,7 @@ use crate::map_schedule::MapSchedule;
use crate::render::settings::{RendererSettings, WgpuSettings};
use crate::render::{RenderState, Renderer};
use crate::style::Style;
use crate::window::{MapWindow, MapWindowConfig, Runnable, WindowSize};
use crate::window::{HasRawWindow, MapWindow, MapWindowConfig, Runnable, WindowSize};
pub mod context;
pub mod coords;
@ -47,9 +47,9 @@ pub(crate) mod tessellation;
pub(crate) mod util;
/// Map's configuration and execution.
pub struct Map<W, SM, HC>
pub struct Map<MWC, SM, HC>
where
W: MapWindow,
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HTTPClient,
{
@ -57,9 +57,10 @@ where
window: W,
}
impl<W, SM, HC> Map<W, SM, HC>
impl<MWC, SM, HC> Map<MWC, SM, HC>
where
W: MapWindow + Runnable<W::MapWindowConfig, SM, HC>,
MWC: MapWindowConfig,
MWC::MapWindow: Runnable<MWC, SM, HC>,
SM: ScheduleMethod,
HC: HTTPClient,
{
@ -142,6 +143,29 @@ where
window,
}
}
pub async fn initialize_headless(self) -> Map<MWC, SM, HC> {
let window = self.map_window_config.create();
let renderer = Renderer::initialize_headless::<MWC>(
&window,
self.wgpu_settings,
self.renderer_settings,
)
.await
.unwrap();
Map {
map_state: MapSchedule::new(
window.size(),
self.map_window_config,
Some(renderer),
self.scheduler,
self.http_client,
self.style,
),
window,
}
}
}
pub struct MapBuilder<MWC, SM, HC>

View File

@ -0,0 +1,65 @@
use crate::render::graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo};
use crate::render::render_commands::{DrawMasks, DrawTiles};
use crate::render::render_phase::{PhaseItem, RenderCommand};
use crate::render::resource::{Head, TrackedRenderPass};
use crate::render::stages::draw_graph;
use crate::render::util::FloatOrd;
use crate::render::Eventually::Initialized;
use crate::render::RenderState;
use std::ops::{Deref, Range};
#[derive(Default)]
pub struct CopySurfaceBufferNode {}
impl CopySurfaceBufferNode {
pub fn new() -> Self {
Self {}
}
}
impl Node for CopySurfaceBufferNode {
fn input(&self) -> Vec<SlotInfo> {
vec![]
}
fn update(&mut self, _state: &mut RenderState) {}
fn run(
&self,
_graph: &mut RenderGraphContext,
RenderContext {
command_encoder, ..
}: &mut RenderContext,
state: &RenderState,
) -> Result<(), NodeRunError> {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let size = state.surface.size();
command_encoder.copy_texture_to_buffer(
buffered_texture.texture.as_image_copy(),
wgpu::ImageCopyBuffer {
buffer: &buffered_texture.output_buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(
std::num::NonZeroU32::new(
buffered_texture.buffer_dimensions.padded_bytes_per_row as u32,
)
.unwrap(),
),
rows_per_image: None,
},
},
wgpu::Extent3d {
width: size.width() as u32,
height: size.height() as u32,
depth_or_array_layers: 1,
},
);
}
}
Ok(())
}
}

View File

@ -27,10 +27,12 @@ use crate::render::shaders::{ShaderFeatureStyle, ShaderLayerMetadata};
use crate::render::tile_view_pattern::{TileInView, TileShape, TileViewPattern};
use crate::render::util::Eventually;
use crate::tessellation::IndexDataType;
use crate::MapWindow;
use crate::{HasRawWindow, MapWindow, MapWindowConfig};
use log::info;
use std::sync::Arc;
// Rendering internals
mod copy_surface_to_buffer_node;
mod graph;
mod graph_runner;
mod main_pass;
@ -52,7 +54,6 @@ pub use stages::register_render_stages;
pub const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; // Must match IndexDataType
#[derive(Default)]
pub struct RenderState {
render_target: Eventually<TextureView>,
@ -76,13 +77,33 @@ pub struct RenderState {
depth_texture: Eventually<Texture>,
multisampling_texture: Eventually<Option<Texture>>,
surface: Surface,
mask_phase: RenderPhase<TileInView>,
tile_phase: RenderPhase<(IndexEntry, TileShape)>,
}
impl RenderState {
pub fn new(surface: Surface) -> Self {
Self {
render_target: Default::default(),
buffer_pool: Default::default(),
tile_view_pattern: Default::default(),
tile_pipeline: Default::default(),
mask_pipeline: Default::default(),
globals_bind_group: Default::default(),
depth_texture: Default::default(),
multisampling_texture: Default::default(),
surface,
mask_phase: Default::default(),
tile_phase: Default::default(),
}
}
}
pub struct Renderer {
pub instance: wgpu::Instance,
pub device: wgpu::Device,
pub device: Arc<wgpu::Device>,
pub queue: wgpu::Queue,
pub adapter_info: wgpu::AdapterInfo,
@ -90,34 +111,29 @@ pub struct Renderer {
pub settings: RendererSettings,
pub state: RenderState,
pub surface: Surface,
}
impl Renderer {
/// Initializes the renderer by retrieving and preparing the GPU instance, device and queue
/// for the specified backend.
pub async fn initialize<MW>(
window: &MW,
pub async fn initialize<MWC>(
window: &MWC::MapWindow,
wgpu_settings: WgpuSettings,
settings: RendererSettings,
) -> Result<Self, wgpu::RequestDeviceError>
where
MW: MapWindow,
MWC::MapWindow: HasRawWindow,
MWC: MapWindowConfig,
/* <<MWC as MapWindowConfig>::MapWindow as MapWindow>::Window:
raw_window_handle::HasRawWindowHandle,*/
{
let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
let maybe_surface = match &settings.surface_type {
SurfaceType::Headless => None,
SurfaceType::Headed => Some(Surface::from_window(&instance, window, &settings)),
};
let surface = Surface::from_window::<MWC>(&instance, window, &settings);
let compatible_surface = if let Some(surface) = &maybe_surface {
match &surface.head() {
Head::Headed(window_head) => Some(window_head.surface()),
Head::Headless(_) => None,
}
} else {
None
let compatible_surface = match &surface.head() {
Head::Headed(window_head) => Some(window_head.surface()),
Head::Headless(_) => None,
};
let (device, queue, adapter_info) = Self::request_device(
@ -131,11 +147,6 @@ impl Renderer {
)
.await?;
let surface = maybe_surface.unwrap_or_else(|| match &settings.surface_type {
SurfaceType::Headless => Surface::from_image(&device, window, &settings),
SurfaceType::Headed => Surface::from_window(&instance, window, &settings),
});
match surface.head() {
Head::Headed(window) => window.configure(&device),
Head::Headless(_) => {}
@ -143,18 +154,51 @@ impl Renderer {
Ok(Self {
instance,
device,
device: Arc::new(device),
queue,
adapter_info,
wgpu_settings,
settings,
state: Default::default(),
surface,
state: RenderState::new(surface),
})
}
pub async fn initialize_headless<MWC>(
window: &MWC::MapWindow,
wgpu_settings: WgpuSettings,
settings: RendererSettings,
) -> Result<Self, wgpu::RequestDeviceError>
where
MWC: MapWindowConfig,
{
let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
let (device, queue, adapter_info) = Self::request_device(
&instance,
&wgpu_settings,
&wgpu::RequestAdapterOptions {
power_preference: wgpu_settings.power_preference,
force_fallback_adapter: false,
compatible_surface: None,
},
)
.await?;
let surface = Surface::from_image::<MWC>(&device, window, &settings);
Ok(Self {
instance,
device: Arc::new(device),
queue,
adapter_info,
wgpu_settings,
settings,
state: RenderState::new(surface),
})
}
pub fn resize(&mut self, width: u32, height: u32) {
self.surface.resize(width, height)
self.state.surface.resize(width, height)
}
/// Requests a device
@ -323,7 +367,7 @@ impl Renderer {
&self.state
}
pub fn surface(&self) -> &Surface {
&self.surface
&self.state.surface
}
}

View File

@ -4,14 +4,17 @@
use crate::render::resource::texture::TextureView;
use crate::render::settings::RendererSettings;
use crate::render::util::HasChanged;
use crate::{MapWindow, WindowSize};
use crate::{HasRawWindow, MapWindow, MapWindowConfig, WindowSize};
use std::fs::File;
use std::io::Write;
use std::mem::size_of;
use std::sync::Arc;
struct BufferDimensions {
width: usize,
height: usize,
unpadded_bytes_per_row: usize,
padded_bytes_per_row: usize,
pub struct BufferDimensions {
pub width: usize,
pub height: usize,
pub unpadded_bytes_per_row: usize,
pub padded_bytes_per_row: usize,
}
impl BufferDimensions {
@ -40,9 +43,11 @@ impl WindowHead {
self.surface.configure(device, &self.surface_config);
}
pub fn recreate_surface<MW>(&mut self, window: &MW, instance: &wgpu::Instance)
pub fn recreate_surface<MWC>(&mut self, window: &MWC::MapWindow, instance: &wgpu::Instance)
where
MW: MapWindow,
MWC: MapWindowConfig,
<<MWC as MapWindowConfig>::MapWindow as MapWindow>::RawWindow:
raw_window_handle::HasRawWindowHandle,
{
self.surface = unsafe { instance.create_surface(window.inner()) };
}
@ -52,14 +57,61 @@ impl WindowHead {
}
pub struct BufferedTextureHead {
texture: wgpu::Texture,
output_buffer: wgpu::Buffer,
buffer_dimensions: BufferDimensions,
pub texture: wgpu::Texture,
pub output_buffer: wgpu::Buffer,
pub buffer_dimensions: BufferDimensions,
}
impl BufferedTextureHead {
pub async fn create_png<'a>(
&self,
png_output_path: &str,
// device: &wgpu::Device,
) {
// Note that we're not calling `.await` here.
let buffer_slice = self.output_buffer.slice(..);
let buffer_future = buffer_slice.map_async(wgpu::MapMode::Read);
// Poll the device in a blocking manner so that our future resolves.
// In an actual application, `device.poll(...)` should
// be called in an event loop or on another thread.
//device.poll(wgpu::Maintain::Wait);
if let Ok(()) = buffer_future.await {
let padded_buffer = buffer_slice.get_mapped_range();
let mut png_encoder = png::Encoder::new(
File::create(png_output_path).unwrap(),
self.buffer_dimensions.width as u32,
self.buffer_dimensions.height as u32,
);
png_encoder.set_depth(png::BitDepth::Eight);
png_encoder.set_color(png::ColorType::Rgba);
let mut png_writer = png_encoder
.write_header()
.unwrap()
.into_stream_writer_with_size(self.buffer_dimensions.unpadded_bytes_per_row)
.unwrap();
// from the padded_buffer we write just the unpadded bytes into the image
for chunk in padded_buffer.chunks(self.buffer_dimensions.padded_bytes_per_row) {
png_writer
.write_all(&chunk[..self.buffer_dimensions.unpadded_bytes_per_row])
.unwrap();
}
png_writer.finish().unwrap();
// With the current interface, we have to make sure all mapped views are
// dropped before we unmap the buffer.
drop(padded_buffer);
self.output_buffer.unmap();
}
}
}
pub enum Head {
Headed(WindowHead),
Headless(BufferedTextureHead),
Headless(Arc<BufferedTextureHead>),
}
pub struct Surface {
@ -68,13 +120,16 @@ pub struct Surface {
}
impl Surface {
pub fn from_window<MW>(
pub fn from_window<MWC>(
instance: &wgpu::Instance,
window: &MW,
window: &MWC::MapWindow,
settings: &RendererSettings,
) -> Self
where
MW: MapWindow,
MWC: MapWindowConfig,
MWC::MapWindow: HasRawWindow,
/* <<MWC as MapWindowConfig>::MapWindow as MapWindow>::Window:
raw_window_handle::HasRawWindowHandle,*/
{
let size = window.size();
let surface_config = wgpu::SurfaceConfiguration {
@ -86,7 +141,7 @@ impl Surface {
present_mode: wgpu::PresentMode::Fifo, // VSync
};
let surface = unsafe { instance.create_surface(window.inner()) };
let surface = unsafe { instance.create_surface(window.raw_window()) };
Self {
size,
@ -97,9 +152,13 @@ impl Surface {
}
}
pub fn from_image<MW>(device: &wgpu::Device, window: &MW, settings: &RendererSettings) -> Self
pub fn from_image<MWC>(
device: &wgpu::Device,
window: &MWC::MapWindow,
settings: &RendererSettings,
) -> Self
where
MW: MapWindow,
MWC: MapWindowConfig,
{
let size = window.size();
@ -111,7 +170,7 @@ impl Surface {
BufferDimensions::new(size.width() as usize, size.height() as usize);
// The output buffer lets us retrieve the data as an array
let output_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
label: Some("BufferedTextureHead buffer"),
size: (buffer_dimensions.padded_bytes_per_row * buffer_dimensions.height) as u64,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
@ -133,11 +192,11 @@ impl Surface {
Self {
size,
head: Head::Headless(BufferedTextureHead {
head: Head::Headless(Arc::new(BufferedTextureHead {
texture,
output_buffer,
buffer_dimensions,
}),
})),
}
}
@ -159,7 +218,8 @@ impl Surface {
};
frame.into()
}
Head::Headless(BufferedTextureHead { texture, .. }) => texture
Head::Headless(arc) => arc
.texture
.create_view(&wgpu::TextureViewDescriptor::default())
.into(),
}
@ -191,13 +251,17 @@ impl Surface {
}
}
pub fn recreate<MW>(&mut self, window: &MW, instance: &wgpu::Instance)
pub fn recreate<MWC>(&mut self, window: &MWC::MapWindow, instance: &wgpu::Instance)
where
MW: MapWindow,
MWC: MapWindowConfig,
<<MWC as MapWindowConfig>::MapWindow as MapWindow>::RawWindow:
raw_window_handle::HasRawWindowHandle,
{
match &mut self.head {
Head::Headed(head) => {
head.recreate_surface(window, instance);
Head::Headed(window_head) => {
if window_head.has_changed(&(self.size.width(), self.size.height())) {
window_head.recreate_surface::<MWC>(window, instance);
}
}
Head::Headless(_) => {}
}

View File

@ -6,6 +6,7 @@
// 3. "sub graph" modules should be nested beneath their parent graph module
use crate::context::MapContext;
use crate::render::copy_surface_to_buffer_node::CopySurfaceBufferNode;
use crate::render::graph::{EmptyNode, RenderGraph};
use crate::render::graph_runner::RenderGraphRunner;
use crate::render::main_pass::{MainPassDriverNode, MainPassNode};
@ -24,6 +25,7 @@ pub mod draw_graph {
pub mod input {}
pub mod node {
pub const MAIN_PASS: &str = "main_pass";
pub const COPY: &str = "copy";
}
}
@ -34,17 +36,20 @@ pub struct GraphRunnerStage {
impl Default for GraphRunnerStage {
fn default() -> Self {
let pass_node = MainPassNode::new();
let mut graph = RenderGraph::default();
let mut draw_graph = RenderGraph::default();
draw_graph.add_node(draw_graph::node::MAIN_PASS, pass_node);
draw_graph.add_node(draw_graph::node::MAIN_PASS, MainPassNode::new());
let input_node_id = draw_graph.set_input(vec![]);
draw_graph
.add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)
.unwrap();
graph.add_sub_graph(draw_graph::NAME, draw_graph);
draw_graph.add_node(draw_graph::node::COPY, CopySurfaceBufferNode::default());
draw_graph
.add_node_edge(draw_graph::node::MAIN_PASS, draw_graph::node::COPY)
.unwrap();
graph.add_sub_graph(draw_graph::NAME, draw_graph);
graph.add_node(node::MAIN_PASS_DEPENDENCIES, EmptyNode);
graph.add_node(node::MAIN_PASS_DRIVER, MainPassDriverNode);
graph

View File

@ -11,10 +11,12 @@ mod phase_sort_stage;
mod queue_stage;
mod resource_stage;
mod upload_stage;
mod write_surface_buffer_stage;
use crate::multi_stage;
use crate::render::stages::phase_sort_stage::PhaseSortStage;
use crate::render::stages::queue_stage::QueueStage;
use crate::render::stages::write_surface_buffer_stage::WriteSurfaceBufferStage;
pub use graph_runner_stage::{draw_graph, node};
/// The labels of the default App rendering stages.
@ -53,4 +55,8 @@ pub fn register_render_stages(schedule: &mut Schedule) {
schedule.add_stage(RenderStageLabel::Queue, QueueStage::default());
schedule.add_stage(RenderStageLabel::PhaseSort, PhaseSortStage::default());
schedule.add_stage(RenderStageLabel::Render, GraphRunnerStage::default());
schedule.add_stage(
RenderStageLabel::Cleanup,
WriteSurfaceBufferStage::default(),
);
}

View File

@ -28,13 +28,14 @@ impl Stage for ResourceStage {
Renderer {
settings,
device,
surface,
state,
..
},
..
}: &mut MapContext,
) {
let surface = &mut state.surface;
let size = surface.size();
surface.reconfigure(device);

View File

@ -27,15 +27,7 @@ impl Stage for UploadStage {
view_state,
style,
tile_cache,
renderer:
Renderer {
settings: _,
device: _,
queue,
surface: _,
state,
..
},
renderer: Renderer { queue, state, .. },
..
}: &mut MapContext,
) {

View File

@ -0,0 +1,50 @@
//! Sorts items of the [RenderPhases](RenderPhase).
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::io::tile_cache::TileCache;
use crate::io::LayerTessellateMessage;
use crate::render::camera::ViewProjection;
use crate::render::render_phase::RenderPhase;
use crate::render::resource::{BufferDimensions, BufferedTextureHead, Head, IndexEntry};
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};
use std::fs::File;
use std::future::Future;
use std::io::Write;
use std::iter;
use std::ops::Deref;
use tokio::runtime::Handle;
use tokio::task;
use wgpu::{BufferAsyncError, BufferSlice};
#[derive(Default)]
pub struct WriteSurfaceBufferStage;
impl Stage for WriteSurfaceBufferStage {
fn run(
&mut self,
MapContext {
renderer: Renderer { state, .. },
..
}: &mut MapContext,
) {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let buffered_texture = buffered_texture.clone();
task::block_in_place(|| {
Handle::current().block_on(async {
buffered_texture.create_png("test.png").await;
})
});
}
}
}
}

View File

@ -1,22 +1,40 @@
//! Utilities for the window system.
use crate::{HTTPClient, MapSchedule, ScheduleMethod};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
/// Window with an optional [carte::window::WindowSize].
pub trait MapWindow {
type EventLoop;
type Window: raw_window_handle::HasRawWindowHandle; // FIXME: Not true for headless
type MapWindowConfig: MapWindowConfig<MapWindow = Self>;
fn create(map_window_config: &Self::MapWindowConfig) -> Self;
type RawWindow;
fn size(&self) -> WindowSize;
fn inner(&self) -> &Self::Window;
fn inner(&self) -> &Self::RawWindow;
}
pub trait HasRawWindow {
type HRWH: HasRawWindowHandle;
fn raw_window(&self) -> &Self::HRWH;
}
impl<MW> HasRawWindow for MW
where
MW: MapWindow,
MW::RawWindow: HasRawWindowHandle,
{
type HRWH = MW::RawWindow;
fn raw_window(&self) -> &Self::HRWH {
self.inner()
}
}
pub trait MapWindowConfig: 'static {
type MapWindow: MapWindow<MapWindowConfig = Self>;
type MapWindow: MapWindow;
fn create(&self) -> Self::MapWindow;
}
pub trait Runnable<MWC, SM, HC>