Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for raw-window-handle #962

Merged
merged 5 commits into from Feb 4, 2020
Merged
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
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ optional = true

[dev-dependencies]
rand = "^0.7"
wgpu = "^0.4.0"
glsl-to-spirv = "^0.1.7"

[target.'cfg(target_os = "macos")'.dev-dependencies.objc]
version = "^0.2.7"

[dependencies.raw-window-handle]
version = "0.3.3"
optional = true

[features]

Expand Down Expand Up @@ -136,3 +145,7 @@ name = "ttf-demo"

[[example]]
name = "window-properties"

[[example]]
required-features = ["raw-window-handle"]
name = "raw-window-handle-with-wgpu"
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,28 @@ fn main() {

```

# Support for raw-window-handle

`raw-window-handle` can be enabled using the feature name:

```toml
[dependencies.sdl2]
version = "0.32"
features = ["raw-window-handle"]
```

An example working with [`wgpu`](https://crates.io/crates/wgpu) is also available:

```bash
cargo run --example raw-window-handle-with-wgpu --features raw-window-handle
```

### sdl2 with raw-window-handle on macOS:

On macOS the `RawWindowHandle.ns_view` field is returned null. Libraries consuming the `RawWindowHandle` (such as
`wgpu`) should determine a sane default for `ns_view`. If they do not, please file an issue with the associated
project.

# When things go wrong
Rust, and Rust-SDL2, are both still heavily in development, and you may run
into teething issues when using this. Before panicking, check that you're using
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Ignore unknown bits in `SDL_Keysym`'s `mod` field (key modifiers) when construct
[PR #898](https://github.com/Rust-SDL2/rust-sdl2/pull/898):
Implements `TryFrom<PixelFormatEnum>` for `PixelFormat`

[PR #962](https://github.com/Rust-SDL2/rust-sdl2/pull/962):
Added `raw-window-handle` support for Windows, Linux (X11 and Wayland) and macOS.

### v0.32.2

[PR #868](https://github.com/Rust-SDL2/rust-sdl2/pull/868):
Expand Down
191 changes: 191 additions & 0 deletions examples/raw-window-handle-with-wgpu/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/// Minimal example for getting sdl2 and wgpu working together with raw-window-handle.
///
/// TODO: update wgpu and this example after https://github.com/gfx-rs/wgpu/pull/462 ships

extern crate glsl_to_spirv;
extern crate libc;
#[cfg(target_os = "macos")]
extern crate objc;
extern crate raw_window_handle;
extern crate sdl2;
extern crate wgpu;

use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};

use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode;
use sdl2::video::Window;

/// sdl2 implements raw-window-handle correctly, but wgpu does not for macOS
/// wgpu wrongly expects an NSView to be provided by raw-window-handle, so we have to do a little more work
struct WindowWrapper<'a>(pub &'a Window);

unsafe impl<'a> HasRawWindowHandle for WindowWrapper<'a> {
#[cfg(not(target_os = "macos"))]
/// all non-mac platforms work correctly, so return the handle directly
fn raw_window_handle(&self) -> RawWindowHandle {
self.0.raw_window_handle()
}

#[cfg(target_os = "macos")]
/// do some work on macOS to get the root NSView for the NSWindow returned by sdl2
fn raw_window_handle(&self) -> RawWindowHandle {
use objc::{msg_send, sel, sel_impl};
use objc::runtime::Object;
use raw_window_handle::macos::MacOSHandle;
let handle = self.0.raw_window_handle();
match handle {
RawWindowHandle::MacOS(macos_handle) => {
RawWindowHandle::MacOS(MacOSHandle {
ns_window: macos_handle.ns_window,
ns_view: unsafe { msg_send![macos_handle.ns_window as *mut Object, contentView] },
..MacOSHandle::empty()
})
}
_ => unreachable!()
}
}
}

fn load_glsl(code: &str, ty: glsl_to_spirv::ShaderType) -> Result<Vec<u32>, String> {
let spirv = glsl_to_spirv::compile(&code, ty)?;
let result = wgpu::read_spirv(spirv).map_err(|e| e.to_string())?;
Ok(result)
}

fn main() -> Result<(), String> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
let window = video_subsystem
.window("Raw Window Handle Example", 800, 600)
.position_centered()
.resizable()
.build()
.map_err(|e| e.to_string())?;
let (width, height) = window.size();

let adapter_opt = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
backends: wgpu::BackendBit::PRIMARY,
});
let adapter = match adapter_opt {
Some(a) => a,
None => return Err(String::from("No adapter found")),
};

let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: false,
},
limits: wgpu::Limits::default(),
});

let vs = include_str!("shader.vert");
let vs_spirv = &load_glsl(vs, glsl_to_spirv::ShaderType::Vertex)?;
let vs_module = device.create_shader_module(vs_spirv);

let fs = include_str!("shader.frag");
let fs_spirv = &load_glsl(fs, glsl_to_spirv::ShaderType::Fragment)?;
let fs_module = device.create_shader_module(fs_spirv);

let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[] });
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
bindings: &[],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
});

let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::None,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[],
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});

// use the WindowWrapper to make sure wgpu will work on macOS
let surface = wgpu::Surface::create(&WindowWrapper(&window));

let mut sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width,
height,
present_mode: wgpu::PresentMode::Vsync,
};

let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);

let mut event_pump = sdl_context.event_pump()?;
'running: loop {
for event in event_pump.poll_iter() {
match event {
Event::Window {
win_event: WindowEvent::Resized(width, height),
..
} => {
sc_desc.width = width as u32;
sc_desc.height = height as u32;
swap_chain = device.create_swap_chain(&surface, &sc_desc);
}
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
break 'running;
}
_ => {}
}
}

let frame = swap_chain.get_next_texture();
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store,
clear_color: wgpu::Color::GREEN,
}],
depth_stencil_attachment: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.draw(0 .. 3, 0 .. 1);
}

queue.submit(&[encoder.finish()]);
}

Ok(())
}
7 changes: 7 additions & 0 deletions examples/raw-window-handle-with-wgpu/shader.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#version 450

layout(location = 0) out vec4 outColor;

void main() {
outColor = vec4(1.0, 0.0, 0.0, 1.0);
}
15 changes: 15 additions & 0 deletions examples/raw-window-handle-with-wgpu/shader.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 450

out gl_PerVertex {
vec4 gl_Position;
};

const vec2 positions[3] = vec2[3](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);

void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}
3 changes: 3 additions & 0 deletions src/sdl2/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,6 @@ pub mod gfx;
mod common;
// Export return types and such from the common module.
pub use crate::common::IntegerOrSdlError;

#[cfg(feature = "raw-window-handle")]
pub mod raw_window_handle;
Loading