diff --git a/crates/processing_glfw/src/lib.rs b/crates/processing_glfw/src/lib.rs index 740762b..975ddd9 100644 --- a/crates/processing_glfw/src/lib.rs +++ b/crates/processing_glfw/src/lib.rs @@ -14,7 +14,6 @@ pub struct GlfwContext { window: PWindow, events: GlfwReceiver<(f64, WindowEvent)>, surface: Option, - scale_factor: f32, } impl GlfwContext { @@ -31,14 +30,11 @@ impl GlfwContext { window.set_all_polling(true); window.show(); - let (scale_factor, _) = window.get_content_scale(); - Ok(Self { glfw, window, events, surface: None, - scale_factor, }) } @@ -127,8 +123,7 @@ impl GlfwContext { return false; } WindowEvent::CursorPos(x, y) => { - let s = self.scale_factor; - input_set_mouse_move(surface, x as f32 / s, y as f32 / s).unwrap(); + input_set_mouse_move(surface, x as f32, y as f32).unwrap(); } WindowEvent::MouseButton(button, action, _mods) => { if let Some(btn) = glfw_button_to_bevy(button) { @@ -182,6 +177,11 @@ impl GlfwContext { true } + pub fn content_scale(&self) -> f32 { + let (s, _) = self.window.get_content_scale(); + s + } + fn sync_cursor(&mut self, surface: Entity) { use bevy::window::CursorGrabMode; diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index 1d4ce31..5b0dbc9 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -146,6 +146,19 @@ impl Surface { None => true, // no-op, offscreen surfaces never close } } + + #[getter] + pub fn display_density(&self) -> PyResult { + match &self.glfw_ctx { + Some(ctx) => Ok(ctx.content_scale()), + None => Ok(1.0), + } + } + + pub fn set_pixel_density(&self, density: f32) -> PyResult<()> { + surface_set_pixel_density(self.entity, density) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) + } } impl Drop for Surface { diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 71362ed..01ddce9 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -1432,4 +1432,18 @@ mod mewnala { fn key_just_pressed(key_code: u32) -> PyResult { input::key_just_pressed(key_code) } + + #[pyfunction] + #[pyo3(pass_module)] + fn pixel_density(module: &Bound<'_, PyModule>, density: f32) -> PyResult<()> { + graphics!(module).surface.set_pixel_density(density) + } + + #[pyfunction] + #[pyo3(pass_module)] + fn display_density(module: &Bound<'_, PyModule>) -> PyResult { + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + graphics.surface.display_density() + } } diff --git a/crates/processing_render/src/graphics.rs b/crates/processing_render/src/graphics.rs index 11bae9d..da99aae 100644 --- a/crates/processing_render/src/graphics.rs +++ b/crates/processing_render/src/graphics.rs @@ -40,7 +40,8 @@ pub struct GraphicsPlugin; impl Plugin for GraphicsPlugin { fn build(&self, app: &mut App) { - app.init_resource::(); + app.init_resource::() + .add_systems(PostUpdate, sync_to_surface); } } @@ -149,14 +150,26 @@ pub fn create( mut commands: Commands, mut layer_manager: ResMut, p_images: Query<&Image, With>, + windows: Query<&Window, With>, render_device: Res, ) -> Result { // find the surface entity, if it is an image, we will render to that image // otherwise we will render to the window - let target = match p_images.get(surface_entity) { - Ok(p_image) => RenderTarget::Image(ImageRenderTarget::from(p_image.handle.clone())), + let (target, physical_width, physical_height) = match p_images.get(surface_entity) { + Ok(p_image) => ( + RenderTarget::Image(ImageRenderTarget::from(p_image.handle.clone())), + p_image.size.width, + p_image.size.height, + ), Err(QueryEntityError::QueryDoesNotMatch(..)) => { - RenderTarget::Window(WindowRef::Entity(surface_entity)) + let window = windows + .get(surface_entity) + .map_err(|_| ProcessingError::SurfaceNotFound)?; + ( + RenderTarget::Window(WindowRef::Entity(surface_entity)), + window.resolution.physical_width(), + window.resolution.physical_height(), + ) } Err(_) => return Err(ProcessingError::SurfaceNotFound), }; @@ -165,14 +178,14 @@ pub fn create( let render_layer = layer_manager.allocate(); let size = Extent3d { - width, - height, + width: physical_width, + height: physical_height, depth_or_array_layers: 1, }; let readback_buffer = create_readback_buffer( &render_device, - width, - height, + physical_width, + physical_height, texture_format, "Graphics Readback Buffer", ) @@ -241,6 +254,39 @@ pub fn resize( } } +pub fn sync_to_surface( + mut graphics_query: Query<(&mut Graphics, &RenderTarget)>, + windows: Query<&Window, (With, Changed)>, + render_device: Res, +) { + for (mut graphics, target) in graphics_query.iter_mut() { + let RenderTarget::Window(WindowRef::Entity(surface_entity)) = *target else { + continue; + }; + let Ok(window) = windows.get(surface_entity) else { + continue; + }; + let physical_w = window.resolution.physical_width(); + let physical_h = window.resolution.physical_height(); + if graphics.size.width == physical_w && graphics.size.height == physical_h { + continue; + } + graphics.size = Extent3d { + width: physical_w, + height: physical_h, + depth_or_array_layers: 1, + }; + graphics.readback_buffer = create_readback_buffer( + &render_device, + physical_w, + physical_h, + graphics.texture_format, + "Graphics Readback Buffer", + ) + .expect("Failed to reallocate readback buffer"); + } +} + pub fn mode_3d( In(entity): In, mut projections: Query<&mut Projection>, @@ -396,7 +442,7 @@ pub fn begin_draw(In(entity): In, mut state_query: Query<&mut RenderStat let mut state = state_query .get_mut(entity) .map_err(|_| ProcessingError::GraphicsNotFound)?; - state.reset(); + state.begin_frame(); Ok(()) } diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index 0920067..f2dd767 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -249,6 +249,14 @@ pub fn surface_resize(graphics_entity: Entity, width: u32, height: u32) -> error }) } +pub fn surface_set_pixel_density(entity: Entity, density: f32) -> error::Result<()> { + app_mut(|app| { + app.world_mut() + .run_system_cached_with(surface::set_pixel_density, (entity, density)) + .unwrap() + }) +} + /// Create a new graphics surface for rendering. pub fn graphics_create( surface_entity: Entity, diff --git a/crates/processing_render/src/render/mesh_builder.rs b/crates/processing_render/src/render/mesh_builder.rs index 84665e3..a3aa156 100644 --- a/crates/processing_render/src/render/mesh_builder.rs +++ b/crates/processing_render/src/render/mesh_builder.rs @@ -36,7 +36,7 @@ impl<'a> MeshBuilder<'a> { if let Some(VertexAttributeValues::Float32x4(colors)) = self.mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { - colors.push(self.color.to_srgba().to_f32_array()); + colors.push(self.color.to_linear().to_f32_array()); } if let Some(VertexAttributeValues::Float32x3(normals)) = diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index 669de54..4f73922 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -122,6 +122,11 @@ impl RenderState { self.shape_builder = None; } + pub fn begin_frame(&mut self) { + self.transform = TransformStack::new(); + self.shape_builder = None; + } + pub fn fill_is_transparent(&self) -> bool { self.fill_color.map(|c| c.alpha() < 1.0).unwrap_or(false) } diff --git a/crates/processing_render/src/render/primitive/quad.rs b/crates/processing_render/src/render/primitive/quad.rs index 2c89486..6550d04 100644 --- a/crates/processing_render/src/render/primitive/quad.rs +++ b/crates/processing_render/src/render/primitive/quad.rs @@ -70,7 +70,7 @@ fn simple_quad( if let Some(VertexAttributeValues::Float32x4(colors)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { - let color_array = color.to_srgba().to_f32_array(); + let color_array = color.to_linear().to_f32_array(); for _ in 0..4 { colors.push(color_array); } diff --git a/crates/processing_render/src/render/primitive/rect.rs b/crates/processing_render/src/render/primitive/rect.rs index 36decaa..d989612 100644 --- a/crates/processing_render/src/render/primitive/rect.rs +++ b/crates/processing_render/src/render/primitive/rect.rs @@ -87,7 +87,7 @@ fn simple_rect(mesh: &mut Mesh, x: f32, y: f32, w: f32, h: f32, color: Color) { if let Some(VertexAttributeValues::Float32x4(colors)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { - let color_array = color.to_srgba().to_f32_array(); + let color_array = color.to_linear().to_f32_array(); for _ in 0..4 { colors.push(color_array); } diff --git a/crates/processing_render/src/render/primitive/shape.rs b/crates/processing_render/src/render/primitive/shape.rs index 5b968aa..ff5674a 100644 --- a/crates/processing_render/src/render/primitive/shape.rs +++ b/crates/processing_render/src/render/primitive/shape.rs @@ -497,7 +497,7 @@ fn push_triangle( positions.push([x3, y3, 0.0]); } - let color_array = color.to_srgba().to_f32_array(); + let color_array = color.to_linear().to_f32_array(); if let Some(VertexAttributeValues::Float32x4(colors)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { @@ -556,7 +556,7 @@ fn push_quad( positions.push([x4, y4, 0.0]); } - let color_array = color.to_srgba().to_f32_array(); + let color_array = color.to_linear().to_f32_array(); if let Some(VertexAttributeValues::Float32x4(colors)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { diff --git a/crates/processing_render/src/render/primitive/triangle.rs b/crates/processing_render/src/render/primitive/triangle.rs index 3de0529..5b66b47 100644 --- a/crates/processing_render/src/render/primitive/triangle.rs +++ b/crates/processing_render/src/render/primitive/triangle.rs @@ -64,7 +64,7 @@ fn simple_triangle( if let Some(VertexAttributeValues::Float32x4(colors)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { - let color_array = color.to_srgba().to_f32_array(); + let color_array = color.to_linear().to_f32_array(); for _ in 0..3 { colors.push(color_array); } diff --git a/crates/processing_render/src/surface.rs b/crates/processing_render/src/surface.rs index bbda9d5..20f46d3 100644 --- a/crates/processing_render/src/surface.rs +++ b/crates/processing_render/src/surface.rs @@ -94,10 +94,13 @@ fn spawn_surface( let window_wrapper = WindowWrapper::new(glfw_window); let handle_wrapper = RawHandleWrapper::new(&window_wrapper)?; + let physical_width = (width as f32 * scale_factor) as u32; + let physical_height = (height as f32 * scale_factor) as u32; + Ok(commands .spawn(( Window { - resolution: WindowResolution::new(width, height) + resolution: WindowResolution::new(physical_width, physical_height) .with_scale_factor_override(scale_factor), ..default() }, @@ -362,8 +365,28 @@ pub fn resize( mut windows: Query<&mut Window>, ) -> Result<()> { if let Ok(mut window) = windows.get_mut(window_entity) { - window.resolution.set_physical_resolution(width, height); + let scale = window.resolution.scale_factor(); + let physical_w = (width as f32 * scale) as u32; + let physical_h = (height as f32 * scale) as u32; + window.resolution.set_physical_resolution(physical_w, physical_h); + Ok(()) + } else { + Err(error::ProcessingError::SurfaceNotFound) + } +} +pub fn set_pixel_density( + In((window_entity, density)): In<(Entity, f32)>, + mut windows: Query<&mut Window>, +) -> Result<()> { + if let Ok(mut window) = windows.get_mut(window_entity) { + let logical_w = window.resolution.width(); + let logical_h = window.resolution.height(); + window.resolution.set_scale_factor_override(Some(density)); + window.resolution.set_physical_resolution( + (logical_w * density) as u32, + (logical_h * density) as u32, + ); Ok(()) } else { Err(error::ProcessingError::SurfaceNotFound)