Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion core/src/processing/webgpu/PGraphicsWebGPU.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public void setSize(int w, int h) {
}
}

@Override
public void beginDraw() {
super.beginDraw();
checkSettings();
}

@Override
public void endDraw() {
super.endDraw();
Expand All @@ -35,7 +41,18 @@ public void endDraw() {
@Override
public void dispose() {
super.dispose();

if (windowId != 0) {
PWebGPU.destroySurface(windowId);
windowId = 0;
}
PWebGPU.exit();
}

@Override
protected void backgroundImpl() {
if (windowId == 0) {
return;
}
PWebGPU.backgroundColor(windowId, backgroundR, backgroundG, backgroundB, backgroundA);
}
}
28 changes: 27 additions & 1 deletion core/src/processing/webgpu/PWebGPU.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import processing.core.NativeLibrary;

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;

import static java.lang.foreign.MemorySegment.NULL;
import static processing.ffi.processing_h.*;
import processing.ffi.Color;

/**
* PWebGPU provides the native interface layer for libProcessing's WebGPU support.
Expand Down Expand Up @@ -47,6 +49,16 @@ public static long createSurface(long windowHandle, int width, int height, float
return windowId;
}

/**
* Destroys a WebGPU surface.
*
* @param windowId The window ID returned from createSurface
*/
public static void destroySurface(long windowId) {
processing_destroy_surface(windowId);
checkError();
}

/**
* Updates a window's size.
*
Expand All @@ -55,7 +67,7 @@ public static long createSurface(long windowHandle, int width, int height, float
* @param height New physical window height in pixels
*/
public static void windowResized(long windowId, int width, int height) {
processing_window_resized(windowId, width, height);
processing_resize_surface(windowId, width, height);
checkError();
}

Expand All @@ -75,6 +87,20 @@ public static void exit() {
checkError();
}

public static void backgroundColor(long windowId, float r, float g, float b, float a) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment color = Color.allocate(arena);

Color.r(color, r);
Color.g(color, g);
Color.b(color, b);
Color.a(color, a);

processing_background_color(windowId, color);
checkError();
}
}

/**
* Checks for errors from the native library and throws a PWebGPUException if an error occurred.
*/
Expand Down
1 change: 1 addition & 0 deletions libProcessing/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions libProcessing/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[workspace]
resolver = "3"
members = ["ffi","renderer"]

[workspace.dependencies]
bevy = { version = "0.17", no-default-features = true, features = [
"bevy_render",
"bevy_color",
] }
1 change: 1 addition & 0 deletions libProcessing/ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ crate-type = ["cdylib"]

[dependencies]
renderer = { path = "../renderer" }
bevy = { workspace = true }

[build-dependencies]
cbindgen = "0.29"
14 changes: 14 additions & 0 deletions libProcessing/ffi/src/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// A sRGB (?) color
#[repr(C)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}

impl From<Color> for bevy::color::Color {
fn from(color: Color) -> Self {
bevy::color::Color::srgba(color.r, color.g, color.b, color.a)
}
}
33 changes: 31 additions & 2 deletions libProcessing/ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use crate::color::Color;
use bevy::prelude::Entity;

mod color;
mod error;

/// Initialize libProcessing.
Expand Down Expand Up @@ -31,16 +35,41 @@ pub extern "C" fn processing_create_surface(
.unwrap_or(0)
}

/// Destroy the surface associated with the given window ID.
///
/// SAFETY:
/// - Init and create_surface have been called.
/// - window_id is a valid ID returned from create_surface.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_destroy_surface(window_id: u64) {
error::clear_error();
let window_entity = Entity::from_bits(window_id);
error::check(|| renderer::destroy_surface(window_entity));
}

/// Update window size when resized.
///
/// SAFETY:
/// - Init and create_surface have been called.
/// - window_id is a valid ID returned from create_surface.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_window_resized(window_id: u64, width: u32, height: u32) {
pub extern "C" fn processing_resize_surface(window_id: u64, width: u32, height: u32) {
error::clear_error();
let window_entity = Entity::from_bits(window_id);
error::check(|| renderer::resize_surface(window_entity, width, height));
}

/// Set the background color for the given window.
///
/// SAFETY:
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_background_color(window_id: u64, color: Color) {
error::clear_error();
error::check(|| renderer::window_resized(window_id, width, height));
let window_entity = Entity::from_bits(window_id);
error::check(|| renderer::background_color(window_entity, color.into()));
}

/// Step the application forward.
Expand Down
4 changes: 1 addition & 3 deletions libProcessing/renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ edition = "2024"
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
bevy = { version = "0.17", no-default-features = true, features = [
"bevy_render"
] }
bevy = { workspace = true }
thiserror = "2"
raw-window-handle = "0.6"

Expand Down
59 changes: 48 additions & 11 deletions libProcessing/renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@ use crate::error::Result;
use bevy::app::{App, AppExit};
use bevy::log::tracing_subscriber;
use bevy::prelude::*;
use bevy::window::{
RawHandleWrapper, Window, WindowResolution, WindowWrapper,
};
use bevy::window::{RawHandleWrapper, Window, WindowRef, WindowResolution, WindowWrapper};
use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
RawWindowHandle, WindowHandle,
};
use std::cell::RefCell;
use std::num::NonZero;
use std::sync::atomic::AtomicU32;
use std::sync::OnceLock;
use bevy::camera::RenderTarget;
use bevy::camera::visibility::RenderLayers;
use tracing::debug;

static IS_INIT: OnceLock<()> = OnceLock::new();
static WINDOW_COUNT: AtomicU32 = AtomicU32::new(0);

thread_local! {
static APP: OnceLock<RefCell<App>> = OnceLock::default();
}


fn app<T>(cb: impl FnOnce(&App) -> Result<T>) -> Result<T> {
let res = APP.with(|app_lock| {
let app = app_lock
Expand Down Expand Up @@ -144,7 +147,7 @@ pub fn create_surface(
let handle_wrapper = RawHandleWrapper::new(&window_wrapper)?;

let entity_id = app_mut(|app| {
let entity = app
let mut window = app
.world_mut()
.spawn((
Window {
Expand All @@ -153,22 +156,44 @@ pub fn create_surface(
..default()
},
handle_wrapper,
))
.id();
));

let count = WINDOW_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let render_layer = RenderLayers::none().with(count as usize);

// TODO: spawn a camera for this window with a render target of this window
let window_entity = window.id();
window.with_children(|parent| {
parent.spawn((
Camera3d::default(),
Camera {
target: RenderTarget::Window(WindowRef::Entity(window_entity)),
..default()
},
Projection::Orthographic(OrthographicProjection::default_3d()),
render_layer,
));
});

Ok(entity.to_bits())
Ok(window_entity.to_bits())
})?;

Ok(entity_id)
}

pub fn destroy_surface(window_entity: Entity) -> Result<()>{
app_mut(|app| {
if app.world_mut().get::<Window>(window_entity).is_some() {
app.world_mut().despawn(window_entity);
WINDOW_COUNT.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
}
Ok(())
})
}

/// Update window size when resized.
pub fn window_resized(window_id: u64, width: u32, height: u32) -> Result<()> {
pub fn resize_surface(window_entity: Entity, width: u32, height: u32) -> Result<()> {
app_mut(|app| {
let entity = Entity::from_bits(window_id);
if let Some(mut window) = app.world_mut().get_mut::<Window>(entity) {
if let Some(mut window) = app.world_mut().get_mut::<Window>(window_entity) {
window.resolution.set_physical_resolution(width, height);
Ok(())
} else {
Expand Down Expand Up @@ -240,6 +265,18 @@ pub fn exit(exit_code: u8) -> Result<()> {
})
}

pub fn background_color(window_entity: Entity, color: Color) -> Result<()> {
app_mut(|app| {
let mut camera_query = app.world_mut().query::<(&mut Camera, &ChildOf)>();
for (mut camera, parent) in camera_query.iter_mut(&mut app.world_mut()) {
if parent.parent() == window_entity {
camera.clear_color = ClearColorConfig::Custom(color);
}
}
Ok(())
})
}

fn setup_tracing() -> Result<()> {
let subscriber = tracing_subscriber::FmtSubscriber::new();
tracing::subscriber::set_global_default(subscriber)?;
Expand Down
Loading