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

Add an example app using the DirectComposition API #2458

Merged
merged 75 commits into from Mar 12, 2018
Merged
Changes from 51 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
4f67edd
Initial commit
SimonSapin Jan 30, 2018
7dfa0b4
Copy example program from glutin’s README
SimonSapin Jan 30, 2018
5c80249
Start to translate "How to initialize DirectComposition" example
SimonSapin Jan 30, 2018
fdab306
Return these pointers we so arduously obtained
SimonSapin Jan 30, 2018
307b3f0
Add a COM pointer abstraction
SimonSapin Feb 13, 2018
0507f4c
Import fewer single-use things
SimonSapin Feb 13, 2018
58b9ab1
*mut *mut _ alreay requires unsafe to break invariants
SimonSapin Feb 13, 2018
a91c6a8
Return a struct
SimonSapin Feb 13, 2018
fc680d6
Switch to wio::ComPtr
SimonSapin Feb 14, 2018
7ff8ed1
Create some DirectComposition visuals
SimonSapin Feb 14, 2018
f758d2b
Use the winit crate directly
SimonSapin Feb 14, 2018
9effb30
Drop GL for now
SimonSapin Feb 15, 2018
e637e08
Remove click event handling
SimonSapin Feb 15, 2018
955b210
Create swap chains
SimonSapin Feb 20, 2018
7fd064c
Fork wio::ComPtr
SimonSapin Feb 20, 2018
8d40fdf
First D3D rectangle!
SimonSapin Feb 20, 2018
5c6ae42
Try to provide safe APIs. (Still needs safety review.)
SimonSapin Feb 22, 2018
e10178d
Separate the library crate from the demo app
SimonSapin Feb 22, 2018
17c3957
Make the lib more library-like, keep hard-coded values in the demo app.
SimonSapin Feb 22, 2018
aff6c39
Move demo app’s unsafe code into a safe function
SimonSapin Feb 22, 2018
39c125c
Two D3D things in the same window
SimonSapin Feb 22, 2018
828c285
Alpha transparency!
SimonSapin Feb 22, 2018
c7eea0b
Scrolling without re-rendering
SimonSapin Feb 22, 2018
281a511
Remove unused and unsound DerefMut
SimonSapin Feb 22, 2018
b8458bb
Repaint one of the visuals on click
SimonSapin Feb 22, 2018
b69dbd6
OMSetRenderTargets appears to be unnecessary here
SimonSapin Feb 23, 2018
a730f98
Keep one shared D3D context
SimonSapin Feb 23, 2018
ce02a22
Make sure we can keep rendering both visuals
SimonSapin Feb 23, 2018
1842280
Rewrite ComPtr::cast in terms of new_with_uuid
SimonSapin Feb 23, 2018
c264c97
Keep a "render target view" together with each visual
SimonSapin Feb 23, 2018
6a46a4b
Make D3D stuff public, move rendering to the app
SimonSapin Feb 23, 2018
bb9fbbf
Add EGL bindings through the gl_generator crate
SimonSapin Feb 23, 2018
d380331
Remove obsolete comment
SimonSapin Feb 23, 2018
118d0b1
Link to ANGLE (but assume it is already built)
SimonSapin Feb 23, 2018
fa95b12
Create and initialize an EGL display
SimonSapin Feb 26, 2018
ac0ac79
Create EGL configs and surfaces
SimonSapin Feb 26, 2018
8553788
Create EGL contexts and call MakeCurrent()
SimonSapin Feb 27, 2018
e7b9614
Use gleam
SimonSapin Feb 27, 2018
be24ac2
Attempt to use OpenGL through gleam + ANGLE + EGL… but the window sta…
SimonSapin Feb 27, 2018
fede929
Add Visual::present
SimonSapin Feb 27, 2018
68d8fef
Remove public APIs for rendering with Direct3D
SimonSapin Feb 27, 2018
6327ed1
Check for EGL errors more systematically
SimonSapin Feb 27, 2018
959a56a
More well-defined public API for EGL things
SimonSapin Feb 27, 2018
567f82c
Keep a reference to shared EGL things next to per-visual things
SimonSapin Feb 27, 2018
f3c21a3
Destroy EGL things in Drop
SimonSapin Feb 27, 2018
c35ccfc
Keep the DXGI back buffer object alive, in case that matters
SimonSapin Feb 27, 2018
3c36118
Call glFinish() before IDXGISwapChain::Present
SimonSapin Feb 27, 2018
023022a
Demo add: access gleam through visuals
SimonSapin Feb 27, 2018
0b45cf7
Rename the crate with a dash
SimonSapin Feb 27, 2018
75901d6
Move to a subdirectory, for merging into the webrender repo
SimonSapin Feb 27, 2018
efae554
Merge branch 'master' of ../rust-directcomposition into dcomp
SimonSapin Feb 27, 2018
70937d6
Add direct-composition to the webrender workspace.
SimonSapin Feb 27, 2018
9c11d75
Turns out ClearColor doesn’t clear, it just sets the color… Thanks So…
SimonSapin Feb 28, 2018
9d99193
Keeping a reference to back_buffer isn’t necessary.
SimonSapin Feb 28, 2018
872f356
Switch to gl_generator::StaticGenerator
SimonSapin Feb 28, 2018
9238751
Add a private Check trait for EGL return values
SimonSapin Feb 28, 2018
a3b8aba
Move to-be-implemented EGL config choice to a function
SimonSapin Feb 28, 2018
41d4283
eglDestroyContext/Surface already do the right thing for the current …
SimonSapin Feb 28, 2018
6c8928d
Panic in the library on COM errors
SimonSapin Feb 28, 2018
3dd8117
Use tuples for RGBA colors
SimonSapin Feb 28, 2018
6c71656
One same EGL context can be used with multiple surfaces.
SimonSapin Feb 28, 2018
f942806
Initialize WebRender in the DComp demo app
SimonSapin Feb 28, 2018
eb5de06
Rename D3DVisual to AngleVisual
SimonSapin Feb 28, 2018
cc4980f
Add a Rectangle abstraction in the demo app
SimonSapin Feb 28, 2018
42a25b4
Render with WebRender
SimonSapin Mar 1, 2018
27be710
Block until the pipeline is ready, before rendering.
SimonSapin Mar 2, 2018
8766dfc
Use the mozangle crate
SimonSapin Mar 3, 2018
aa39ae5
Use mozangle instead of the other angle crate.
SimonSapin Mar 4, 2018
83edf97
Merge remote-tracking branch 'origin/master' into dcomp
SimonSapin Mar 9, 2018
87812ae
Make the direct-composition demo compile to an empty program on non-w…
SimonSapin Mar 9, 2018
9d92ad5
direct-composition tidy
SimonSapin Mar 9, 2018
19b3df1
Update mozangle
SimonSapin Mar 9, 2018
2062501
Appveyor: Switch to MSVC toolchain
SimonSapin Mar 9, 2018
60d8f08
Build the direct-composition demo on Appveyor CI
SimonSapin Mar 9, 2018
c4a859d
Revert AppVeyor image change
SimonSapin Mar 9, 2018
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -0,0 +1,15 @@
[package]
name = "direct-composition"
version = "0.1.0"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]

[target.'cfg(windows)'.dependencies]
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}

# For the `main.rs` demo
[dependencies]
gleam = "0.4"
winit = "0.10"

[build-dependencies]
gl_generator = "0.9.0"
@@ -0,0 +1,44 @@
extern crate gl_generator;

use gl_generator::{Registry, Api, Profile, Fallbacks};
use std::env;
use std::fs::File;
use std::path::PathBuf;

fn main() {
// Building ANGLE is left as an exercise for the reader:
// https://chromium.googlesource.com/angle/angle/+/HEAD/doc/DevSetup.md
let relative_angle_dir = PathBuf::from("..").join("..").join("angle");

let angle_build_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
.join(relative_angle_dir)
// Assume `gn gen out/Debug` or `gn gen out/Release` like in build instructions.
.join("out")
.join(if &env::var_os("PROFILE").unwrap() == "release" { "Release" } else { "Debug" });
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());

// Assuming that OUT_DIR is something like `target/debug/build/directcomposition-*/out`,
// bin_dir is `target/debug` (where the final executable goes).
let bin_dir = out_dir.join("..").join("..").join("..");
for name in &[
"libEGL.dll.lib",
"libEGL.dll",
"libGLESv2.dll",
] {
std::fs::copy(angle_build_dir.join(name), bin_dir.join(name)).unwrap();
}
println!("cargo:rustc-link-search=native={}", bin_dir.display());
println!("cargo:rustc-link-lib=dylib=libEGL.dll");

let bindings = "egl_bindings.rs";
Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [
"EGL_ANGLE_device_d3d",
"EGL_EXT_platform_base",
"EGL_EXT_platform_device",
]).write_bindings(
gl_generator::StaticStructGenerator,
&mut File::create(&out_dir.join(bindings)).unwrap()
)
.unwrap();

}
@@ -0,0 +1,124 @@
use std::fmt;
use std::ops;
use std::ptr;
use winapi::Interface;
use winapi::ctypes::c_void;
use winapi::shared::guiddef::GUID;
use winapi::shared::winerror::HRESULT;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::unknwnbase::IUnknown;

pub type HResult<T> = Result<T, HResultError>;

/// An error code returned by a Windows API.
pub struct HResultError(HRESULT);

impl fmt::Debug for HResultError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x{:08X}", self.0 as u32)
}
}

impl From<HRESULT> for HResultError {
fn from(h: HRESULT) -> Self {
HResultError(h)
}
}

pub trait ToResult: Sized {
fn to_result(self) -> HResult<()>;
}

impl ToResult for HRESULT {
fn to_result(self) -> HResult<()> {
if SUCCEEDED(self) {
Ok(())
} else {
Err(HResultError(self))
}
}
}

pub fn as_ptr<T>(x: &T) -> *mut T {
x as *const T as _
}

/// Forked from <https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs>
#[derive(PartialEq, Debug)]
pub struct ComPtr<T>(*mut T) where T: Interface;

impl<T> ComPtr<T> where T: Interface {
/// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`.
pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
assert!(!ptr.is_null());
ComPtr(ptr)
}

/// For use with APIs that take an interface UUID and
/// "return" a new COM object through a `*mut *mut c_void` out-parameter.
pub unsafe fn new_with_uuid<F>(f: F) -> HResult<Self>
where F: FnOnce(&GUID, *mut *mut c_void) -> HRESULT
{
Self::new_with(|ptr| f(&T::uuidof(), ptr as _))
}

/// For use with APIs that "return" a new COM object through a `*mut *mut T` out-parameter.
pub unsafe fn new_with<F>(f: F) -> HResult<Self>
where F: FnOnce(*mut *mut T) -> HRESULT
{
let mut ptr = ptr::null_mut();
let status = f(&mut ptr);
if SUCCEEDED(status) {
Ok(ComPtr::from_raw(ptr))
} else {
if !ptr.is_null() {
let ptr = ptr as *mut IUnknown;
(*ptr).Release();
}
Err(HResultError(status))
}
}

pub fn as_raw(&self) -> *mut T {
self.0
}

fn as_unknown(&self) -> &IUnknown {
unsafe {
&*(self.0 as *mut IUnknown)
}
}

/// Performs QueryInterface fun.
pub fn cast<U>(&self) -> HResult<ComPtr<U>> where U: Interface {
unsafe {
ComPtr::<U>::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr))
}
}
}

impl<T> ops::Deref for ComPtr<T> where T: Interface {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}

impl<T> Clone for ComPtr<T> where T: Interface {
fn clone(&self) -> Self {
unsafe {
self.as_unknown().AddRef();
ComPtr(self.0)
}
}
}

impl<T> Drop for ComPtr<T> where T: Interface {
fn drop(&mut self) {
unsafe {
self.as_unknown().Release();
}
}
}
@@ -0,0 +1,208 @@
use std::ffi::CString;
use std::os::raw::{c_void, c_long};
use std::ptr;
use std::rc::Rc;
use winapi;
use winapi::um::d3d11::ID3D11Device;
use winapi::um::d3d11::ID3D11Texture2D;

pub struct SharedEglThings {
functions: Egl,
device: EGLDeviceEXT,
display: types::EGLDisplay,
config: types::EGLConfig,
}

fn cast_attributes(slice: &[types::EGLenum]) -> &EGLint {
unsafe {
&*(slice.as_ptr() as *const EGLint)
}
}

macro_rules! attributes {
($( $key: expr => $value: expr, )*) => {
cast_attributes(&[
$( $key, $value, )*
NONE,
])
}
}

impl SharedEglThings {
pub unsafe fn new(d3d_device: *mut ID3D11Device) -> Rc<Self> {
let functions = Egl;

let device = functions.check_mut_ptr(eglCreateDeviceANGLE(
D3D11_DEVICE_ANGLE,
d3d_device,
ptr::null(),
));
let display = functions.check_ptr(functions.GetPlatformDisplayEXT(
PLATFORM_DEVICE_EXT,
device,
attributes! [
EXPERIMENTAL_PRESENT_PATH_ANGLE => EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
],
));
functions.check_bool(functions.Initialize(display, ptr::null_mut(), ptr::null_mut()));

// Adapted from
// https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#635
let mut configs = [ptr::null(); 64];
let mut num_configs = 0;
functions.check_bool(functions.ChooseConfig(
display,
attributes! [
SURFACE_TYPE => WINDOW_BIT,
RENDERABLE_TYPE => OPENGL_ES2_BIT,
RED_SIZE => 8,
GREEN_SIZE => 8,
BLUE_SIZE => 8,
ALPHA_SIZE => 8,
],
configs.as_mut_ptr(),
configs.len() as i32,
&mut num_configs,
));
assert!(num_configs >= 0);
// FIXME: pick a preferable config?
let config = configs[0];

Rc::new(SharedEglThings { functions, device, display, config })
}

pub fn get_proc_address(&self, name: &str) -> *const c_void {
let name = CString::new(name.as_bytes()).unwrap();
unsafe {
self.functions.GetProcAddress(name.as_ptr()) as *const _ as _
}
}
}

impl Drop for SharedEglThings {
fn drop(&mut self) {
unsafe {
// FIXME does EGLDisplay or EGLConfig need clean up? How?
self.functions.check_bool(eglReleaseDeviceANGLE(self.device))
}
}
}

pub struct PerVisualEglThings {
shared: Rc<SharedEglThings>,
context: types::EGLContext,
surface: types::EGLSurface,
}

impl PerVisualEglThings {
pub unsafe fn new(shared: Rc<SharedEglThings>, buffer: *const ID3D11Texture2D,
width: u32, height: u32)
-> Self {
let shared = shared.clone();
let context = shared.functions.check_ptr(shared.functions.CreateContext(
shared.display,
shared.config,
NO_CONTEXT,
attributes![],
));

let surface = shared.functions.check_ptr(shared.functions.CreatePbufferFromClientBuffer(
shared.display,
D3D_TEXTURE_ANGLE,
buffer as types::EGLClientBuffer,
shared.config,
attributes! [
WIDTH => width,
HEIGHT => height,
FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE => TRUE,
],
));

PerVisualEglThings { shared, context, surface }
}

pub fn make_current(&self) {
unsafe {
self.shared.functions.check_bool(self.shared.functions.MakeCurrent(
self.shared.display, self.surface, self.surface, self.context,
))
}
}
}

impl Drop for PerVisualEglThings {
fn drop(&mut self) {
unsafe {
if self.shared.functions.GetCurrentContext() == self.context {
// release the current context without assigning a new one
self.shared.functions.check_bool(self.shared.functions.MakeCurrent(
self.shared.display, NO_SURFACE, NO_SURFACE, NO_CONTEXT,
))
}
self.shared.functions.check_bool(self.shared.functions.DestroyContext(
self.shared.display, self.context,
));
self.shared.functions.check_bool(self.shared.functions.DestroySurface(
self.shared.display, self.surface,
));
}
}
}

impl Egl {
fn check_error(&self) {
unsafe {
let error = self.GetError() as types::EGLenum;
assert_eq!(error, SUCCESS, "0x{:x} != 0x{:x}", error, SUCCESS);
}
}

fn check_ptr(&self, p: *const c_void) -> *const c_void {
self.check_error();
assert!(!p.is_null());
p
}

fn check_mut_ptr(&self, p: *mut c_void) -> *mut c_void {
self.check_error();
assert!(!p.is_null());
p
}

fn check_bool(&self, bool_result: types::EGLBoolean) {
self.check_error();
assert_eq!(bool_result, TRUE);
}
}

// Adapted from https://github.com/tomaka/glutin/blob/1f3b8360cb/src/api/egl/ffi.rs
#[allow(non_camel_case_types)] pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
#[allow(non_camel_case_types)] pub type khronos_uint64_t = u64;
#[allow(non_camel_case_types)] pub type khronos_ssize_t = c_long;
pub type EGLint = i32;
pub type EGLNativeDisplayType = *const c_void;
pub type EGLNativePixmapType = *const c_void;
pub type EGLNativeWindowType = winapi::shared::windef::HWND;
pub type NativeDisplayType = EGLNativeDisplayType;
pub type NativePixmapType = EGLNativePixmapType;
pub type NativeWindowType = EGLNativeWindowType;

include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));


// Adapted from https://chromium.googlesource.com/angle/angle/+/master/include/EGL/eglext_angle.h
type EGLDeviceEXT = *mut c_void;
const EXPERIMENTAL_PRESENT_PATH_ANGLE: types::EGLenum = 0x33A4;
const EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE: types::EGLenum = 0x33A9;
const D3D_TEXTURE_ANGLE: types::EGLenum = 0x33A3;
const FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE: types::EGLenum = 0x33A6;

extern "C" {
fn eglCreateDeviceANGLE(
device_type: types::EGLenum,
device: *mut ID3D11Device,
attrib_list: *const types::EGLAttrib,
) -> EGLDeviceEXT;

fn eglReleaseDeviceANGLE(device: EGLDeviceEXT) -> types::EGLBoolean;
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.