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

Use a simpler approach to Direct2D presentation #1191

Merged
merged 6 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
197 changes: 0 additions & 197 deletions druid-shell/src/platform/windows/dcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,18 @@

// This module could become a general wrapper for DirectComposition, but
// for now we're just using what we need to get a swapchain up.
#![allow(unused)]

use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr::{null, null_mut};

use log::error;

use winapi::shared::dxgi::IDXGIDevice;
use winapi::shared::dxgi1_2::DXGI_ALPHA_MODE_IGNORE;
use winapi::shared::dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM;
use winapi::shared::minwindef::{FALSE, TRUE};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::d2d1::*;
use winapi::um::d2d1_1::*;
use winapi::um::d3d11::*;
use winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE;
use winapi::um::dcomp::*;
use winapi::um::dcompanimation::*;
use winapi::um::unknwnbase::IUnknown;
use winapi::um::winnt::HRESULT;
use winapi::Interface;
use wio::com::ComPtr;

use super::util::OPTIONAL_FUNCTIONS;

unsafe fn wrap<T, U, F>(hr: HRESULT, ptr: *mut T, f: F) -> Result<U, HRESULT>
where
F: Fn(ComPtr<T>) -> U,
Expand All @@ -55,25 +40,7 @@ where
}
}

fn unit_err(hr: HRESULT) -> Result<(), HRESULT> {
if SUCCEEDED(hr) {
Ok(())
} else {
Err(hr)
}
}

pub struct D3D11Device(ComPtr<ID3D11Device>);
pub struct D2D1Device(ComPtr<ID2D1Device>);
pub struct DCompositionDevice(ComPtr<IDCompositionDevice>);
pub struct DCompositionTarget(ComPtr<IDCompositionTarget>);
pub struct DCompositionVisual(ComPtr<IDCompositionVisual>);
pub struct DCompositionVirtualSurface(ComPtr<IDCompositionVirtualSurface>);

/// A trait for content which can be added to a visual.
pub(crate) trait Content {
unsafe fn unknown_ptr(&mut self) -> *mut IUnknown;
}

impl D3D11Device {
/// Creates a new device with basic defaults.
Expand All @@ -100,171 +67,7 @@ impl D3D11Device {
}
}

pub(crate) fn create_d2d1_device(&mut self) -> Result<D2D1Device, HRESULT> {
unsafe {
let mut dxgi_device: ComPtr<IDXGIDevice> = self.0.cast()?;
let mut d2d1_device: *mut ID2D1Device = null_mut();
let hr = D2D1CreateDevice(dxgi_device.as_raw(), null(), &mut d2d1_device);
wrap(hr, d2d1_device, D2D1Device)
}
}

pub(crate) fn raw_ptr(&mut self) -> *mut ID3D11Device {
self.0.as_raw()
}
}

impl D2D1Device {
/// Create a wrapped DCompositionDevice object. Note: returns Err(0) on systems
/// not supporting DirectComposition, available 8.1 and above.
pub(crate) fn create_composition_device(&mut self) -> Result<DCompositionDevice, HRESULT> {
unsafe {
let create = OPTIONAL_FUNCTIONS.DCompositionCreateDevice2.ok_or(0)?;
let mut dcomp_device: *mut IDCompositionDevice = null_mut();
let hr = create(
self.0.as_raw() as *mut IUnknown,
&IDCompositionDevice::uuidof(),
&mut dcomp_device as *mut _ as *mut _,
);
wrap(hr, dcomp_device, DCompositionDevice)
}
}
}

impl DCompositionDevice {
pub(crate) unsafe fn create_target_for_hwnd(
&mut self,
hwnd: HWND,
topmost: bool,
) -> Result<DCompositionTarget, HRESULT> {
let mut dcomp_target: *mut IDCompositionTarget = null_mut();
let hr =
self.0
.CreateTargetForHwnd(hwnd, if topmost { TRUE } else { FALSE }, &mut dcomp_target);
wrap(hr, dcomp_target, DCompositionTarget)
}

pub(crate) fn create_visual(&mut self) -> Result<DCompositionVisual, HRESULT> {
unsafe {
let mut visual: *mut IDCompositionVisual = null_mut();
let hr = self.0.CreateVisual(&mut visual);
wrap(hr, visual, DCompositionVisual)
}
}

/// Creates an RGB surface. Probably should allow more options (including alpha).
pub(crate) fn create_virtual_surface(
&mut self,
height: u32,
width: u32,
) -> Result<DCompositionVirtualSurface, HRESULT> {
unsafe {
let mut surface: *mut IDCompositionVirtualSurface = null_mut();
let hr = self.0.CreateVirtualSurface(
width,
height,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_IGNORE,
&mut surface,
);
wrap(hr, surface, DCompositionVirtualSurface)
}
}

pub(crate) fn commit(&mut self) -> Result<(), HRESULT> {
unsafe { unit_err(self.0.Commit()) }
}
}

impl DCompositionTarget {
// alternatively could be set_root with an option
pub(crate) fn clear_root(&mut self) -> Result<(), HRESULT> {
unsafe { unit_err(self.0.SetRoot(null_mut())) }
}

pub(crate) fn set_root(&mut self, visual: &mut DCompositionVisual) -> Result<(), HRESULT> {
unsafe { unit_err(self.0.SetRoot(visual.0.as_raw())) }
}
}

impl DCompositionVisual {
pub(crate) fn set_content<T: Content>(&mut self, content: &mut T) -> Result<(), HRESULT> {
unsafe { self.set_content_raw(content.unknown_ptr()) }
}

// TODO: impl Content trait for swapchain, for type safety
pub(crate) unsafe fn set_content_raw(&mut self, content: *mut IUnknown) -> Result<(), HRESULT> {
unit_err(self.0.SetContent(content))
}

pub(crate) fn set_pos(&mut self, x: f32, y: f32) {
unsafe {
self.0.SetOffsetX_1(x);
self.0.SetOffsetY_1(y);
}
}
}

// We don't actually need to draw into DirectComposition virtual surfaces now, this is
// experimental and based on an older version of direct2d-rs. Probably delete.

/*
struct DcBacking(*mut ID2D1DeviceContext);
unsafe impl RenderTargetBacking for DcBacking {
fn create_target(self, _factory: &mut ID2D1Factory1) -> Result<*mut ID2D1RenderTarget, HRESULT> {
Ok(self.0 as *mut ID2D1RenderTarget)
}
}
// TODO: support common methods with DCompositionSurface, probably should be trait
impl DCompositionVirtualSurface {
// could try to expose more DeviceContext capability
pub fn begin_draw(&mut self, d2d_factory: &direct2d::Factory, rect: Option<RECT>)
-> Result<RenderTarget, HRESULT>
{
unsafe {
let mut dc: *mut ID2D1DeviceContext = null_mut();
let rect_ptr = match rect {
None => null(),
Some(r) => &r,
};
let mut offset: POINT = mem::uninitialized();
let hr = self.0.BeginDraw(rect_ptr, &ID2D1DeviceContext::uuidof(),
&mut dc as *mut _ as *mut _, &mut offset);
if !SUCCEEDED(hr) {
return Err(hr);
}
let backing = DcBacking(dc);
let mut rt = d2d_factory.create_render_target(backing).map_err(|e|
match e {
direct2d::Error::ComError(hr) => hr,
_ => 0,
})?;
// TODO: either move dpi scaling somewhere else or figure out how to
// set it correctly here.
rt.set_transform(&Matrix3x2F::new([[2.0, 0.0], [0.0, 2.0],
[offset.x as f32, offset.y as f32]]));
Ok(rt)
}
}
pub fn end_draw(&mut self) -> Result<(), HRESULT> {
unsafe {
unit_err(self.0.EndDraw())
}
}
pub fn resize(&mut self, width: u32, height: u32) -> Result<(), HRESULT> {
unsafe {
unit_err(self.0.Resize(width, height))
}
}
}
impl Content for DCompositionVirtualSurface {
unsafe fn unknown_ptr(&mut self) -> *mut IUnknown {
self.0.as_raw() as *mut IUnknown
}
}
*/
89 changes: 3 additions & 86 deletions druid-shell/src/platform/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,95 +45,12 @@ pub mod window;
// Basically, go from HwndRenderTarget or DxgiSurfaceRenderTarget (2d or 3d) to a Device Context.
// Go back up for particular needs.

use piet_common::d2d::{D2DFactory, DeviceContext};
use piet_common::d2d::DeviceContext;
use std::fmt::{Debug, Display, Formatter};
use winapi::shared::windef::HWND;
use winapi::shared::winerror::{HRESULT, SUCCEEDED};
use winapi::um::d2d1::{
ID2D1HwndRenderTarget, ID2D1RenderTarget, D2D1_HWND_RENDER_TARGET_PROPERTIES,
D2D1_RENDER_TARGET_PROPERTIES, D2D1_SIZE_U,
};
use winapi::um::dcommon::D2D1_PIXEL_FORMAT;
use winapi::shared::winerror::HRESULT;
use winapi::um::d2d1::ID2D1RenderTarget;
use wio::com::ComPtr;

#[derive(Clone)]
pub struct HwndRenderTarget {
ptr: ComPtr<ID2D1HwndRenderTarget>,
}

impl HwndRenderTarget {
pub fn create(
factory: &D2DFactory,
hwnd: HWND,
width: u32,
height: u32,
) -> Result<Self, Error> {
// hardcode
// - RenderTargetType::Default
// - AlphaMode::Unknown
let rt_props = DEFAULT_PROPS;
let mut hwnd_props = DEFAULT_HWND_PROPS;

hwnd_props.hwnd = hwnd;
hwnd_props.pixelSize.width = width;
hwnd_props.pixelSize.height = height;

// now build
unsafe {
let mut ptr = std::ptr::null_mut();
let hr = (*factory.get_raw()).CreateHwndRenderTarget(&rt_props, &hwnd_props, &mut ptr);

if SUCCEEDED(hr) {
Ok(HwndRenderTarget::from_raw(ptr))
} else {
Err(hr.into())
}
}
}

/// construct from COM ptr
///
/// # Safety
/// TODO
pub unsafe fn from_ptr(ptr: ComPtr<ID2D1HwndRenderTarget>) -> Self {
Self { ptr }
}

/// construct from raw ptr
///
/// # Safety
/// TODO
pub unsafe fn from_raw(raw: *mut ID2D1HwndRenderTarget) -> Self {
Self::from_ptr(ComPtr::from_raw(raw))
}

pub fn get_comptr(&self) -> &ComPtr<ID2D1HwndRenderTarget> {
&self.ptr
}
}

// props for creating hwnd render target
const DEFAULT_PROPS: D2D1_RENDER_TARGET_PROPERTIES = D2D1_RENDER_TARGET_PROPERTIES {
_type: 0u32, //RenderTargetType::Default
pixelFormat: D2D1_PIXEL_FORMAT {
format: 87u32, //Format::B8G8R8A8Unorm, see https://docs.rs/dxgi/0.3.0-alpha4/src/dxgi/enums/format.rs.html#631
alphaMode: 0u32, //AlphaMode::Unknown
},
dpiX: 0.0,
dpiY: 0.0,
usage: 0,
minLevel: 0,
};

const DEFAULT_HWND_PROPS: D2D1_HWND_RENDER_TARGET_PROPERTIES = D2D1_HWND_RENDER_TARGET_PROPERTIES {
hwnd: std::ptr::null_mut(),
pixelSize: D2D1_SIZE_U {
width: 0,
height: 0,
},
presentOptions: 0,
};

#[derive(Clone)]
pub struct DxgiSurfaceRenderTarget {
ptr: ComPtr<ID2D1RenderTarget>,
Expand Down
34 changes: 1 addition & 33 deletions druid-shell/src/platform/windows/paint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,26 @@
//! dxgi render targets so we can use present options for minimal
//! invalidation and low-latency frame timing.

use std::mem;
use std::ptr::null_mut;

use winapi::ctypes::c_void;
use winapi::shared::dxgi::*;
use winapi::shared::dxgi1_2::*;
use winapi::shared::dxgiformat::*;
use winapi::shared::windef::*;
use winapi::shared::winerror::*;
use winapi::um::d2d1::*;
use winapi::um::dcommon::*;
use winapi::um::winuser::*;
use winapi::Interface;

use piet_common::d2d::D2DFactory;

use crate::platform::windows::{DeviceContext, DxgiSurfaceRenderTarget, HwndRenderTarget};
use crate::platform::windows::DxgiSurfaceRenderTarget;
use crate::scale::Scale;

use super::error::Error;
use super::util::as_result;
use super::window::SCALE_TARGET_DPI;

pub(crate) unsafe fn create_render_target(
d2d_factory: &D2DFactory,
hwnd: HWND,
) -> Result<DeviceContext, Error> {
let mut rect: RECT = mem::zeroed();
if GetClientRect(hwnd, &mut rect) == 0 {
log::warn!("GetClientRect failed.");
Err(Error::D2Error)
} else {
let width = (rect.right - rect.left) as u32;
let height = (rect.bottom - rect.top) as u32;
let res = HwndRenderTarget::create(d2d_factory, hwnd, width, height);

if let Err(ref e) = res {
log::error!("Creating hwnd render target failed: {:?}", e);
}
res.map(|hrt| cast_to_device_context(&hrt).expect("removethis"))
.map_err(|_| Error::D2Error)
}
}

/// Create a render target from a DXGI swapchain.
///
/// TODO: probably want to create a DeviceContext, it's more flexible.
Expand Down Expand Up @@ -100,11 +76,3 @@ pub(crate) unsafe fn create_render_target_dxgi(
Err(res.into())
}
}

/// Casts hwnd variant to DeviceTarget
unsafe fn cast_to_device_context(hrt: &HwndRenderTarget) -> Option<DeviceContext> {
hrt.get_comptr()
.cast()
.ok()
.map(|com_ptr| DeviceContext::new(com_ptr))
}
Loading