emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
22.44k stars 1.6k forks source link

egui-wgpu on web #1755

Closed Titaniumtown closed 2 years ago

Titaniumtown commented 2 years ago

How could one use the wgpu integration for egui on wasm? I can't find a way to do this. Is this possible?

emilk commented 2 years ago

I haven't tried it, but I really would like for it to work.

@expenses claims to be using egui-wgpu for WebGL in https://github.com/emilk/egui/pull/1671 - maybe they have some input?

expenses commented 2 years ago

Yep, but it's currently locked to using webgl and not webgpu: https://github.com/emilk/egui/blob/bd5f553c3a33fd9de0f396634044117bd519342d/egui-wgpu/Cargo.toml#L43

We'd just need to remove this feature to get it work for both backends

Titaniumtown commented 2 years ago

I assume this cannot be done as easily as eframe::start_web... how did you do it exactly?

felixfaire commented 2 years ago

We are using egui-wgpu successfully on wasm (with the webgl2 backend currently, though i don't think it should be different for webgpu).

We have an existing wgpu context and custom event loop, input events etc (driven by typescript side), i'm not au fait with how eframe does it, but we followed the 'integrations' part of the eframe repo and got it up and running fairly seamlessly. Which bit are you struggling with in particular? @Titaniumtown

geolehmann commented 2 years ago

I managed to create a minimum working example using egui-wgpu with a wgpu backend and wasm32 target, see https://github.com/geolehmann/egui-wgpu_wasm_example (event handling is not yet included), which took me way longer than it should. I had to use my own fork of egui tho with some fixes (I used some code from the fork of @expenses ).

I also found another working example: https://github.com/pierscowburn/egui_wgpu_failure_case, but here still the older egui-wgpu-backend crate is used.

emilk commented 2 years ago

@geolehmann thanks for that example! I notice that you use winit (and egui-winit) on the web, which is interesting (see https://github.com/emilk/egui/issues/1032).

metacean commented 2 years ago

I have done my own barebones integration of egui on top of a 'raw' wgpu app originally based on the 'learn wgpu' tutorials. this runs on native and in browser via WASM wth wgpu (no webgl fallback). I also have hooked up basic mouse move and button click events as i required them.

I dont really have a 'releaseable' example app i can publish (and I use camel case which likely makes me an heretic in the wider rust community) but, in case its a helpful to anyone - my renderers state struct has the following added:

guiContext: egui::Context,
guiRenderPass: egui_wgpu::renderer::RenderPass,
guiScreenDescriptor: egui_wgpu::renderer::ScreenDescriptor,
guiInput: egui::RawInput,
guiMousePositionInPoints: egui::Pos2, //cache our mouse position as we need it for events

these are initialised as follows:

let guiContext = egui::Context::default();
let guiRenderPass = egui_wgpu::renderer::RenderPass::new(&device, config.format, 1);
let guiScreenDescriptor = egui_wgpu::renderer::ScreenDescriptor {
            size_in_pixels: [config.width, config.height],
            pixels_per_point: 2.0,
        };
let guiInput = egui::RawInput::default();
let guiMousePositionInPoints = egui::pos2(0.0, 0.0);

and the main part of the rendering looks like:

// at beginning of render function create a second encoder to handle the gui drawing commands
let mut guiEncoder = self
            .device
            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
                label: Some("Gui Encoder"),
            });

{
self.guiContext.begin_frame(self.guiInput.take());

//some code to draw a few widgets - rot is a rotation in radians for the displayed model
egui::Window::new("My Window").show(&self.guiContext, |ui| {
    ui.label("Hello World!");
    if ui.button("A Button").clicked() {
         log::warn!("CLICKED BUTTON");
    };
    ui.add(
        egui::Slider::new(&mut self.rot, 0.0..=6.25)
                        .text("rot")
                        .fixed_decimals(2),
        );
   });

//code needed to render the gui
let guiOutput = self.guiContext.end_frame();
let prims = self.guiContext.tessellate(guiOutput.shapes);
self.guiRenderPass.update_buffers(
                &self.device,
                &self.queue,
                &prims[..],
                &self.guiScreenDescriptor,
            );
for (id, image_delta) in &guiOutput.textures_delta.set {
                self.guiRenderPass
                    .update_texture(&self.device, &self.queue, *id, image_delta);
            }
self.guiRenderPass.execute(
                &mut guiEncoder,
                &view,
                &prims[..],
                &self.guiScreenDescriptor,
                None,
            );
}

self.queue.submit(iter::once(guiEncoder.finish()));
output.present();

example of input gathering from the existing winit handlers is:

WindowEvent::CursorMoved {
                device_id,
                position,
                modifiers,
                ..
            } => {
                //log::warn!("CURSOR {:#?} {:#?}", position.x, position.y);
                let pos = egui::pos2(
                    position.x as f32 / self.guiScreenDescriptor.pixels_per_point,
                    position.y as f32 / self.guiScreenDescriptor.pixels_per_point,
                );
                self.guiMousePositionInPoints = pos;
                let pointerEvent = egui::Event::PointerMoved(pos);
                self.guiInput.events.push(pointerEvent);
                true
            }

I am a bit of a rust newbie (Most of my experience in this sort of thing is with raw Vulkan and C++) so have just followed my nose as to how to make this work, but this produces a usable gui overlaid on top of my existing 3d rendered output. If anyone else wants to try the same approach i'm happy to provide more detail

emilk commented 2 years ago

@metacean please use backticks for code

like this
luiswirth commented 2 years ago

I would be willing to provide an example, as I've got my own egui + winit + wgpu integration working on wasm!

But I would like to first await some responses to #2022 and #2023 :)