Skip to content
This repository has been archived by the owner on Jul 27, 2023. It is now read-only.

Commit

Permalink
Merge pull request #21 from google/flip_hwnd2
Browse files Browse the repository at this point in the history
Switch to flip + hwnd hybrid approach
  • Loading branch information
raphlinus committed Apr 19, 2018
2 parents 82e6a7e + c851d8c commit ef61580
Show file tree
Hide file tree
Showing 11 changed files with 636 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions src/edit_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ impl EditView {
}
}

pub fn rebuild_resources(&mut self) {
self.resources = None;
}

pub fn size(&mut self, x: f32, y: f32) {
self.size = (x, y);
self.constrain_scroll();
Expand Down
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ impl WinHandler for MainWinHandler {
false
}

fn rebuild_resources(&self) {
let mut state = self.win.state.borrow_mut();
state.edit_view.rebuild_resources();
}

fn command(&self, id: u32) {
match id {
x if x == MenuEntries::Exit as u32 => {
Expand Down
1 change: 1 addition & 0 deletions xi-win-shell/Cargo.lock

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

3 changes: 2 additions & 1 deletion xi-win-shell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ description = "Windows-specific application shell used for xi editor."
[dependencies]
directwrite = "0.0.9"
direct2d = "0.0.9"
wio = "0.2"

lazy_static = "1.0"
time = "0.1.39"

[dependencies.winapi]
version = "0.3"
features = ["d2d1_1", "dwrite", "winbase", "libloaderapi", "errhandlingapi", "winuser", "shellscalingapi", "shobjidl", "combaseapi", "synchapi"]
features = ["d2d1_1", "dwrite", "winbase", "libloaderapi", "errhandlingapi", "winuser", "shellscalingapi", "shobjidl", "combaseapi", "synchapi", "dxgi1_3", "dcomp", "d3d11", "dwmapi"]
4 changes: 3 additions & 1 deletion xi-win-shell/examples/perftest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use directwrite::text_format;
use xi_win_shell::paint::PaintCtx;
use xi_win_shell::util::default_text_options;
use xi_win_shell::win_main;
use xi_win_shell::window::{WindowBuilder, WindowHandle, WinHandler};
use xi_win_shell::window::{PresentStrategy, WindowBuilder, WindowHandle, WinHandler};

struct PerfTest(RefCell<PerfState>);

Expand Down Expand Up @@ -133,6 +133,8 @@ fn main() {
};
builder.set_handler(Box::new(PerfTest(RefCell::new(perf_state))));
builder.set_title("Performance tester");
// Note: experiment with changing this
builder.set_present_strategy(PresentStrategy::FlipRedirect);
let window = builder.build().unwrap();
window.show();
run_loop.run();
Expand Down
243 changes: 243 additions & 0 deletions xi-win-shell/src/dcomp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Safe-ish wrappers for DirectComposition and related interfaces.

// 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::ops::{Deref, DerefMut};
use std::mem;
use std::ptr::{null, null_mut};
use winapi::Interface;
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::unknwnbase::IUnknown;
use winapi::um::dcomp::*;
use winapi::um::dcompanimation::*;
use winapi::um::winnt::HRESULT;
use wio::com::ComPtr;

use direct2d::{self, RenderTarget};
use direct2d::error::D2D1Error;
use direct2d::math::Matrix3x2F;
use direct2d::render_target::RenderTargetBacking;

use 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, T: Interface
{
if SUCCEEDED(hr) {
Ok(f(ComPtr::from_raw(ptr)))
} else {
Err(hr)
}
}

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 trait Content {
unsafe fn unknown_ptr(&mut self) -> *mut IUnknown;
}

impl D3D11Device {
/// Creates a new device with basic defaults.
pub fn new_simple() -> Result<D3D11Device, HRESULT> {
unsafe {
let mut d3d11_device: *mut ID3D11Device = null_mut();
let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; // could probably set single threaded
let hr = D3D11CreateDevice(null_mut(), D3D_DRIVER_TYPE_HARDWARE, null_mut(), flags,
null(), 0, D3D11_SDK_VERSION, &mut d3d11_device, null_mut(), null_mut());
wrap(hr, d3d11_device, D3D11Device)
}
}

pub 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 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 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 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 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 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 fn commit(&mut self) -> Result<(), HRESULT> {
unsafe {
unit_err(self.0.Commit())
}
}
}

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

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

impl DCompositionVisual {
pub 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 unsafe fn set_content_raw(&mut self, content: *mut IUnknown) -> Result<(), HRESULT> {
unit_err(self.0.SetContent(content))
}

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

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 {
D2D1Error::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
}
}
2 changes: 2 additions & 0 deletions xi-win-shell/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

extern crate winapi;
extern crate direct2d;
extern crate wio;
#[macro_use]
extern crate lazy_static;

mod dcomp;
pub mod menu;
pub mod paint;
pub mod util;
Expand Down
46 changes: 46 additions & 0 deletions xi-win-shell/src/paint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
use std::mem;
use std::ptr::null_mut;

use winapi::Interface;
use winapi::ctypes::{c_void};
use winapi::um::d2d1::*;
use winapi::um::d2d1_1::*;
use winapi::um::dcommon::*;
use winapi::um::winuser::*;
use winapi::shared::dxgi::*;
use winapi::shared::dxgi1_2::*;
use winapi::shared::dxgiformat::*;
use winapi::shared::windef::*;
use winapi::shared::winerror::*;
Expand All @@ -34,6 +38,7 @@ use direct2d;
use direct2d::render_target::{RenderTarget, RenderTargetBacking};

use Error;
use util::as_result;

/// Context for painting by app into window.
pub struct PaintCtx<'a> {
Expand Down Expand Up @@ -98,6 +103,47 @@ pub(crate) unsafe fn create_render_target(d2d_factory: &direct2d::Factory, hwnd:
d2d_factory.create_render_target(params).map_err(|_| Error::D2Error)
}

struct DxgiBacking(*mut IDXGISurface, f32);

unsafe impl RenderTargetBacking for DxgiBacking {
fn create_target(self, factory: &mut ID2D1Factory1) -> Result<*mut ID2D1RenderTarget, HRESULT> {
unsafe {
let props = D2D1_RENDER_TARGET_PROPERTIES {
_type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
pixelFormat: D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_B8G8R8A8_UNORM,
alphaMode: D2D1_ALPHA_MODE_IGNORE,
},
dpiX: self.1,
dpiY: self.1,
usage: D2D1_RENDER_TARGET_USAGE_NONE,
minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
};

let mut render_target: *mut ID2D1RenderTarget = null_mut();
let res = factory.CreateDxgiSurfaceRenderTarget(self.0, &props, &mut render_target);
if SUCCEEDED(res) {
//(*render_target).SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
Ok(render_target)
} else {
Err(res)
}
}
}
}

pub(crate) unsafe fn create_render_target_dxgi(d2d_factory: &direct2d::Factory,
swap_chain: *mut IDXGISwapChain1, dpi: f32) -> Result<RenderTarget, Error>
{
let mut buffer: *mut IDXGISurface = null_mut();
as_result((*swap_chain).GetBuffer(0, &IDXGISurface::uuidof(),
&mut buffer as *mut _ as *mut *mut c_void))?;
let backing = DxgiBacking(buffer, dpi);
let result = d2d_factory.create_render_target(backing);
(*buffer).Release();
result.map_err(|_| Error::D2Error)
}

impl<'a> PaintCtx<'a> {

/// Return the raw Direct2D factory for this painting context. Note: it's possible
Expand Down
Loading

0 comments on commit ef61580

Please sign in to comment.