mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Improve headless rendering (#143)
* Remove util module in renderer * Implement default for ZoomLevel * Add function to clear the bufferpool * Calculate camera height dynamically * Deduplicate code and add sample tile-grid * Remove unused function
This commit is contained in:
parent
6f409d7aa2
commit
d1b6360360
@ -33,43 +33,10 @@ fn headless_render(c: &mut Criterion) {
|
||||
.initialize_headless()
|
||||
.await;
|
||||
|
||||
let http_source_client: HttpSourceClient<ReqwestHttpClient> =
|
||||
HttpSourceClient::new(ReqwestHttpClient::new(None));
|
||||
|
||||
let coords = WorldTileCoords::from((0, 0, ZoomLevel::default()));
|
||||
let request_id = 0;
|
||||
|
||||
let data = http_source_client
|
||||
.fetch(&coords)
|
||||
map.map_schedule
|
||||
.fetch_process(&WorldTileCoords::from((0, 0, ZoomLevel::default())))
|
||||
.await
|
||||
.unwrap()
|
||||
.into_boxed_slice();
|
||||
|
||||
let processor = HeadlessPipelineProcessor::default();
|
||||
let mut pipeline_context = PipelineContext::new(processor);
|
||||
let pipeline = build_vector_tile_pipeline();
|
||||
pipeline.process(
|
||||
(
|
||||
TileRequest {
|
||||
coords,
|
||||
layers: HashSet::from(["boundary".to_owned(), "water".to_owned()]),
|
||||
},
|
||||
request_id,
|
||||
data,
|
||||
),
|
||||
&mut pipeline_context,
|
||||
);
|
||||
|
||||
let mut processor = pipeline_context
|
||||
.take_processor::<HeadlessPipelineProcessor>()
|
||||
.unwrap();
|
||||
|
||||
while let Some(v) = processor.layers.pop() {
|
||||
map.map_schedule_mut()
|
||||
.map_context
|
||||
.tile_repository
|
||||
.put_tessellated_layer(v);
|
||||
}
|
||||
.expect("Failed to fetch and process!");
|
||||
|
||||
map
|
||||
});
|
||||
|
||||
@ -17,6 +17,8 @@ env_logger = "0.9.0"
|
||||
maplibre = { path = "../maplibre", version = "0.0.2", features = ["headless"] }
|
||||
maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
|
||||
|
||||
tile-grid = "0.3"
|
||||
|
||||
tracing = "0.1.35"
|
||||
tracing-subscriber = { version = "0.3.14", optional = true }
|
||||
tracing-tracy = { version = "0.8", optional = true }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use maplibre::benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer};
|
||||
use maplibre::coords::{WorldTileCoords, ZoomLevel};
|
||||
use maplibre::coords::{TileCoords, ViewRegion, WorldTileCoords, ZoomLevel};
|
||||
use maplibre::error::Error;
|
||||
use maplibre::headless::HeadlessMapWindowConfig;
|
||||
use maplibre::io::pipeline::Processable;
|
||||
@ -20,7 +20,11 @@ use maplibre::MapBuilder;
|
||||
use maplibre_winit::winit::WinitMapWindowConfig;
|
||||
|
||||
use maplibre::headless::utils::HeadlessPipelineProcessor;
|
||||
use maplibre::style::source::TileAddressingScheme;
|
||||
use maplibre::util::grid::google_mercator;
|
||||
use maplibre::util::math::Aabb2;
|
||||
use std::collections::HashSet;
|
||||
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
fn enable_tracing() {
|
||||
@ -61,52 +65,33 @@ fn run_headless() {
|
||||
.initialize_headless()
|
||||
.await;
|
||||
|
||||
let http_source_client: HttpSourceClient<ReqwestHttpClient> =
|
||||
HttpSourceClient::new(ReqwestHttpClient::new(None));
|
||||
|
||||
let coords = WorldTileCoords::from((0, 0, ZoomLevel::default()));
|
||||
let request_id = 0;
|
||||
|
||||
let data = http_source_client
|
||||
.fetch(&coords)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_boxed_slice();
|
||||
|
||||
let processor = HeadlessPipelineProcessor::default();
|
||||
let mut pipeline_context = PipelineContext::new(processor);
|
||||
let pipeline = build_vector_tile_pipeline();
|
||||
pipeline.process(
|
||||
(
|
||||
TileRequest {
|
||||
coords,
|
||||
layers: HashSet::from(["boundary".to_owned(), "water".to_owned()]),
|
||||
},
|
||||
request_id,
|
||||
data,
|
||||
),
|
||||
&mut pipeline_context,
|
||||
let tile_limits = google_mercator().tile_limits(
|
||||
extent_wgs84_to_merc(&Extent {
|
||||
minx: 11.3475219363,
|
||||
miny: 48.0345697188,
|
||||
maxx: 11.7917815798,
|
||||
maxy: 48.255861,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
let mut processor = pipeline_context
|
||||
.take_processor::<HeadlessPipelineProcessor>()
|
||||
.unwrap();
|
||||
for (z, x, y) in GridIterator::new(10, 10, tile_limits) {
|
||||
let coords = WorldTileCoords::from((x as i32, y as i32, z.into()));
|
||||
println!("Rendering {}", &coords);
|
||||
map.map_schedule
|
||||
.fetch_process(&coords)
|
||||
.await
|
||||
.expect("Failed to fetch and process!");
|
||||
|
||||
while let Some(v) = processor.layers.pop() {
|
||||
map.map_schedule_mut()
|
||||
.map_context
|
||||
.tile_repository
|
||||
.put_tessellated_layer(v);
|
||||
match map.map_schedule_mut().update_and_redraw() {
|
||||
Ok(_) => {}
|
||||
Err(Error::Render(e)) => {
|
||||
eprintln!("{}", e);
|
||||
if e.should_exit() {}
|
||||
}
|
||||
e => eprintln!("{:?}", e),
|
||||
};
|
||||
}
|
||||
|
||||
match map.map_schedule_mut().update_and_redraw() {
|
||||
Ok(_) => {}
|
||||
Err(Error::Render(e)) => {
|
||||
eprintln!("{}", e);
|
||||
if e.should_exit() {}
|
||||
}
|
||||
e => eprintln!("{:?}", e),
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@ -116,6 +101,6 @@ fn main() {
|
||||
#[cfg(feature = "trace")]
|
||||
enable_tracing();
|
||||
|
||||
run_headless();
|
||||
//run_headless();
|
||||
run_in_window();
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@ use crate::io::tile_repository::TileRepository;
|
||||
use crate::render::camera::{Camera, Perspective, ViewProjection};
|
||||
use crate::util::ChangeObserver;
|
||||
use crate::{Renderer, Style, WindowSize};
|
||||
use cgmath::Angle;
|
||||
use std::ops::Div;
|
||||
|
||||
/// Stores the camera configuration.
|
||||
pub struct ViewState {
|
||||
@ -12,10 +14,20 @@ pub struct ViewState {
|
||||
}
|
||||
|
||||
impl ViewState {
|
||||
pub fn new(window_size: &WindowSize, zoom: Zoom, center: LatLon, pitch: f64) -> Self {
|
||||
pub fn new<P: Into<cgmath::Rad<f64>>>(
|
||||
window_size: &WindowSize,
|
||||
zoom: Zoom,
|
||||
center: LatLon,
|
||||
pitch: f64,
|
||||
fovy: P,
|
||||
) -> Self {
|
||||
let tile_center = TILE_SIZE / 2.0;
|
||||
let fovy = fovy.into();
|
||||
let height = tile_center / (fovy / 2.0).tan();
|
||||
let position = WorldCoords::from_lat_lon(center, zoom);
|
||||
|
||||
let camera = Camera::new(
|
||||
(position.x, position.y, 150.0),
|
||||
(position.x, position.y, height),
|
||||
cgmath::Deg(-90.0),
|
||||
cgmath::Deg(pitch),
|
||||
window_size.width(),
|
||||
|
||||
@ -59,7 +59,7 @@ impl fmt::Debug for Quadkey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug)]
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug, Default)]
|
||||
pub struct ZoomLevel(u8);
|
||||
|
||||
impl ZoomLevel {
|
||||
@ -71,12 +71,6 @@ impl ZoomLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ZoomLevel {
|
||||
fn default() -> Self {
|
||||
ZoomLevel(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<u8> for ZoomLevel {
|
||||
type Output = ZoomLevel;
|
||||
|
||||
@ -206,7 +200,7 @@ impl SignificantlyDifferent for Zoom {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// The origin is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct InnerCoords {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
@ -218,7 +212,7 @@ pub struct InnerCoords {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// For Web Mercator the origin of the coordinate system is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Default)]
|
||||
pub struct TileCoords {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
@ -272,7 +266,7 @@ impl From<(u32, u32, ZoomLevel)> for TileCoords {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// The origin of the coordinate system is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct WorldTileCoords {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
@ -465,7 +459,7 @@ impl AlignedWorldTileCoords {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// The origin of the coordinate system is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct WorldCoords {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
use crate::context::{MapContext, ViewState};
|
||||
use crate::coords::{LatLon, ViewRegion, Zoom};
|
||||
use crate::coords::{LatLon, ViewRegion, WorldTileCoords, Zoom};
|
||||
use crate::error::Error;
|
||||
use crate::io::tile_repository::TileRepository;
|
||||
use crate::headless::utils::HeadlessPipelineProcessor;
|
||||
use crate::io::pipeline::PipelineContext;
|
||||
use crate::io::pipeline::Processable;
|
||||
use crate::io::source_client::HttpSourceClient;
|
||||
use crate::io::tile_pipelines::build_vector_tile_pipeline;
|
||||
use crate::io::tile_repository::{StoredLayer, TileRepository};
|
||||
use crate::io::tile_request_state::TileRequestState;
|
||||
use crate::io::TileRequest;
|
||||
use crate::render::camera::ViewProjection;
|
||||
use crate::render::eventually::Eventually;
|
||||
use crate::render::graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo};
|
||||
use crate::render::resource::{BufferDimensions, BufferedTextureHead, IndexEntry};
|
||||
use crate::render::resource::{Head, TrackedRenderPass};
|
||||
@ -14,6 +22,7 @@ use crate::schedule::{Schedule, Stage};
|
||||
use crate::{
|
||||
HttpClient, MapWindow, MapWindowConfig, Renderer, ScheduleMethod, Scheduler, Style, WindowSize,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
@ -81,6 +90,7 @@ where
|
||||
schedule: Schedule,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_client: HC,
|
||||
tile_request_state: TileRequestState,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> HeadlessMapSchedule<MWC, SM, HC>
|
||||
@ -105,6 +115,7 @@ where
|
||||
.map(|center| LatLon::new(center[0], center[1]))
|
||||
.unwrap_or_default(),
|
||||
style.pitch.unwrap_or_default(),
|
||||
cgmath::Deg(110.0),
|
||||
);
|
||||
let tile_repository = TileRepository::new();
|
||||
let mut schedule = Schedule::default();
|
||||
@ -134,13 +145,13 @@ where
|
||||
schedule,
|
||||
scheduler,
|
||||
http_client,
|
||||
tile_request_state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "update_and_redraw", skip_all)]
|
||||
pub fn update_and_redraw(&mut self) -> Result<(), Error> {
|
||||
self.schedule.run(&mut self.map_context);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -153,6 +164,55 @@ where
|
||||
pub fn http_client(&self) -> &HC {
|
||||
&self.http_client
|
||||
}
|
||||
|
||||
pub async fn fetch_process(&mut self, coords: &WorldTileCoords) -> Option<()> {
|
||||
let source_layers: HashSet<String> = self
|
||||
.map_context
|
||||
.style
|
||||
.layers
|
||||
.iter()
|
||||
.filter_map(|layer| layer.source_layer.clone())
|
||||
.collect();
|
||||
|
||||
let http_source_client: HttpSourceClient<HC> =
|
||||
HttpSourceClient::new(self.http_client.clone());
|
||||
|
||||
let data = http_source_client
|
||||
.fetch(&coords)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_boxed_slice();
|
||||
|
||||
let mut pipeline_context = PipelineContext::new(HeadlessPipelineProcessor::default());
|
||||
let pipeline = build_vector_tile_pipeline();
|
||||
|
||||
let request = TileRequest {
|
||||
coords: WorldTileCoords::default(),
|
||||
layers: source_layers,
|
||||
};
|
||||
|
||||
let request_id = self
|
||||
.tile_request_state
|
||||
.start_tile_request(request.clone())?;
|
||||
pipeline.process((request, request_id, data), &mut pipeline_context);
|
||||
self.tile_request_state.finish_tile_request(request_id);
|
||||
|
||||
let mut processor = pipeline_context
|
||||
.take_processor::<HeadlessPipelineProcessor>()
|
||||
.unwrap();
|
||||
|
||||
if let Eventually::Initialized(pool) = self.map_context.renderer.state.buffer_pool_mut() {
|
||||
pool.clear();
|
||||
}
|
||||
|
||||
while let Some(layer) = processor.layers.pop() {
|
||||
self.map_context
|
||||
.tile_repository
|
||||
.put_tessellated_layer(layer);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Node which copies the contents of the GPU-side texture in [`BufferedTextureHead`] to an
|
||||
@ -182,10 +242,11 @@ impl Node for CopySurfaceBufferNode {
|
||||
}: &mut RenderContext,
|
||||
state: &RenderState,
|
||||
) -> Result<(), NodeRunError> {
|
||||
match state.surface.head() {
|
||||
let surface = state.surface();
|
||||
match surface.head() {
|
||||
Head::Headed(_) => {}
|
||||
Head::Headless(buffered_texture) => {
|
||||
let size = state.surface.size();
|
||||
let size = surface.size();
|
||||
command_encoder.copy_texture_to_buffer(
|
||||
buffered_texture.texture.as_image_copy(),
|
||||
wgpu::ImageCopyBuffer {
|
||||
@ -229,7 +290,8 @@ impl Stage for WriteSurfaceBufferStage {
|
||||
..
|
||||
}: &mut MapContext,
|
||||
) {
|
||||
match state.surface.head() {
|
||||
let surface = state.surface();
|
||||
match surface.head() {
|
||||
Head::Headed(_) => {}
|
||||
Head::Headless(buffered_texture) => {
|
||||
let buffered_texture: Arc<BufferedTextureHead> = buffered_texture.clone();
|
||||
|
||||
@ -36,6 +36,7 @@ pub mod platform;
|
||||
// Exposed because of camera
|
||||
pub mod render;
|
||||
pub mod style;
|
||||
pub mod util;
|
||||
|
||||
pub mod window;
|
||||
// Exposed because of doc-strings
|
||||
@ -48,7 +49,6 @@ pub mod benchmarking;
|
||||
|
||||
// Internal modules
|
||||
pub(crate) mod tessellation;
|
||||
pub mod util;
|
||||
|
||||
/// The [`Map`] defines the public interface of the map renderer.
|
||||
// DO NOT IMPLEMENT INTERNALS ON THIS STRUCT.
|
||||
|
||||
@ -60,6 +60,7 @@ where
|
||||
.map(|center| LatLon::new(center[0], center[1]))
|
||||
.unwrap_or_default(),
|
||||
style.pitch.unwrap_or_default(),
|
||||
cgmath::Deg(110.0),
|
||||
);
|
||||
let tile_repository = TileRepository::new();
|
||||
let mut schedule = Schedule::default();
|
||||
|
||||
@ -1,39 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
|
||||
/// A wrapper type that enables ordering floats. This is a work around for the famous "rust float
|
||||
/// ordering" problem. By using it, you acknowledge that sorting NaN is undefined according to spec.
|
||||
/// This implementation treats NaN as the "smallest" float.
|
||||
#[derive(Debug, Copy, Clone, PartialOrd)]
|
||||
pub struct FloatOrd(pub f32);
|
||||
|
||||
impl PartialEq for FloatOrd {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.0.is_nan() && other.0.is_nan() {
|
||||
true
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FloatOrd {}
|
||||
|
||||
#[allow(clippy::derive_ord_xor_partial_ord)]
|
||||
impl Ord for FloatOrd {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.partial_cmp(&other.0).unwrap_or_else(|| {
|
||||
if self.0.is_nan() && !other.0.is_nan() {
|
||||
Ordering::Less
|
||||
} else if !self.0.is_nan() && other.0.is_nan() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a resource which can be initialized or uninitialized.
|
||||
/// Uninitialized resourced can be initialized by calling [`Eventually::initialize()`].
|
||||
pub enum Eventually<T> {
|
||||
@ -18,6 +18,7 @@
|
||||
//! We appreciate the design and implementation work which as gone into it.
|
||||
//!
|
||||
|
||||
use crate::render::eventually::Eventually;
|
||||
use crate::render::render_phase::RenderPhase;
|
||||
use crate::render::resource::{BufferPool, Globals, IndexEntry};
|
||||
use crate::render::resource::{Head, Surface};
|
||||
@ -25,7 +26,6 @@ use crate::render::resource::{Texture, TextureView};
|
||||
use crate::render::settings::{RendererSettings, WgpuSettings};
|
||||
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::{HeadedMapWindow, MapWindow};
|
||||
use log::info;
|
||||
@ -43,10 +43,10 @@ mod render_phase;
|
||||
mod shaders;
|
||||
mod tile_pipeline;
|
||||
mod tile_view_pattern;
|
||||
mod util;
|
||||
|
||||
// Public API
|
||||
pub mod camera;
|
||||
pub mod eventually;
|
||||
pub mod settings;
|
||||
|
||||
use crate::render::graph::{EmptyNode, RenderGraph, RenderGraphError};
|
||||
@ -79,7 +79,7 @@ pub struct RenderState {
|
||||
depth_texture: Eventually<Texture>,
|
||||
multisampling_texture: Eventually<Option<Texture>>,
|
||||
|
||||
pub surface: Surface,
|
||||
surface: Surface,
|
||||
|
||||
mask_phase: RenderPhase<TileInView>,
|
||||
tile_phase: RenderPhase<(IndexEntry, TileShape)>,
|
||||
@ -108,6 +108,25 @@ impl RenderState {
|
||||
{
|
||||
self.surface.recreate::<MW>(window, instance);
|
||||
}
|
||||
|
||||
pub fn surface(&self) -> &Surface {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
pub fn buffer_pool_mut(
|
||||
&mut self,
|
||||
) -> &mut Eventually<
|
||||
BufferPool<
|
||||
wgpu::Queue,
|
||||
wgpu::Buffer,
|
||||
ShaderVertex,
|
||||
IndexDataType,
|
||||
ShaderLayerMetadata,
|
||||
ShaderFeatureStyle,
|
||||
>,
|
||||
> {
|
||||
&mut self.buffer_pool
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
//! Specifies the instructions which are going to be sent to the GPU. Render commands can be concatenated
|
||||
//! into a new render command which executes multiple instruction sets.
|
||||
|
||||
use crate::render::eventually::Eventually::Initialized;
|
||||
use crate::render::render_phase::{PhaseItem, RenderCommand, RenderCommandResult};
|
||||
use crate::render::resource::{Globals, IndexEntry, TrackedRenderPass};
|
||||
use crate::render::tile_view_pattern::{TileInView, TileShape};
|
||||
use crate::render::util::Eventually::Initialized;
|
||||
use crate::render::INDEX_FORMAT;
|
||||
use crate::RenderState;
|
||||
|
||||
|
||||
@ -129,6 +129,10 @@ impl<Q: Queue<B>, B, V: Pod, I: Pod, TM: Pod, FM: Pod> BufferPool<Q, B, V, I, TM
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.index.clear()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn available_space(&self, typ: BackingBufferType) -> wgpu::BufferAddress {
|
||||
let gap = match typ {
|
||||
@ -480,6 +484,11 @@ impl RingIndex {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.linear_index.clear();
|
||||
self.tree_index.clear();
|
||||
}
|
||||
|
||||
pub fn front(&self) -> Option<&IndexEntry> {
|
||||
self.linear_index
|
||||
.front()
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
//! Utilities for handling surfaces which can be either headless or headed. A headed surface has
|
||||
//! a handle to a window. A headless surface renders to a texture.
|
||||
|
||||
use crate::render::eventually::HasChanged;
|
||||
use crate::render::resource::texture::TextureView;
|
||||
use crate::render::settings::RendererSettings;
|
||||
use crate::render::util::HasChanged;
|
||||
use crate::window::HeadedMapWindow;
|
||||
use crate::{MapWindow, WindowSize};
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
//! Utility for a texture view which can either be created by a [`TextureView`](wgpu::TextureView)
|
||||
//! or [`SurfaceTexture`](wgpu::SurfaceTexture)
|
||||
|
||||
use crate::render::eventually::HasChanged;
|
||||
use crate::render::settings::Msaa;
|
||||
use crate::render::util::HasChanged;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Describes a [`TextureView`].
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
use crate::context::MapContext;
|
||||
use crate::coords::ViewRegion;
|
||||
|
||||
use crate::render::util::Eventually::Initialized;
|
||||
use crate::render::eventually::Eventually::Initialized;
|
||||
use crate::schedule::Stage;
|
||||
use crate::{RenderState, Renderer};
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::context::MapContext;
|
||||
use crate::render::graph::RenderGraph;
|
||||
use crate::render::graph_runner::RenderGraphRunner;
|
||||
|
||||
use crate::render::util::Eventually::Initialized;
|
||||
use crate::render::eventually::Eventually::Initialized;
|
||||
use crate::schedule::Stage;
|
||||
use crate::Renderer;
|
||||
use log::error;
|
||||
|
||||
@ -4,8 +4,8 @@ use crate::context::MapContext;
|
||||
|
||||
use crate::render::resource::IndexEntry;
|
||||
|
||||
use crate::render::eventually::Eventually::Initialized;
|
||||
use crate::render::tile_view_pattern::TileInView;
|
||||
use crate::render::util::Eventually::Initialized;
|
||||
use crate::schedule::Stage;
|
||||
use crate::{RenderState, Renderer};
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::render::shaders::{
|
||||
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
|
||||
};
|
||||
|
||||
use crate::render::util::Eventually::Initialized;
|
||||
use crate::render::eventually::Eventually::Initialized;
|
||||
use crate::schedule::Stage;
|
||||
use crate::{RenderState, Renderer, Style};
|
||||
|
||||
|
||||
@ -345,6 +345,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that enables ordering floats. This is a work around for the famous "rust float
|
||||
/// ordering" problem. By using it, you acknowledge that sorting NaN is undefined according to spec.
|
||||
/// This implementation treats NaN as the "smallest" float.
|
||||
#[derive(Debug, Copy, Clone, PartialOrd)]
|
||||
pub struct FloatOrd(pub f32);
|
||||
|
||||
impl PartialEq for FloatOrd {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.0.is_nan() && other.0.is_nan() {
|
||||
true
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FloatOrd {}
|
||||
|
||||
#[allow(clippy::derive_ord_xor_partial_ord)]
|
||||
impl Ord for FloatOrd {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.partial_cmp(&other.0).unwrap_or_else(|| {
|
||||
if self.0.is_nan() && !other.0.is_nan() {
|
||||
Ordering::Less
|
||||
} else if !self.0.is_nan() && other.0.is_nan() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn div_away(lhs: i32, rhs: i32) -> i32 {
|
||||
if rhs < 0 {
|
||||
panic!("rhs must be positive")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user