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

Transparent background for d2d examples? #541

Closed
Ciantic opened this issue Feb 12, 2021 · 11 comments
Closed

Transparent background for d2d examples? #541

Ciantic opened this issue Feb 12, 2021 · 11 comments
Labels
question Further information is requested

Comments

@Ciantic
Copy link

Ciantic commented Feb 12, 2021

There is cross platform Rust GUI toolkit called Druid, we've been working on to create a transparent background for the window. Would it be possible to have an example of transparent window in this repo?

I found @kennykerr your tutorial for "high performance" version of it from 2014. As of writing, 2021, is that still the most high performant way to do it?

In Druid we also use Swap Chains but they won't work correctly for transparent windows because we use CreateSwapChainForHwnd and reading your article we have to switch using CreateSwapChainForComposition.

Mac, and Linux GTK versions came really fast, but I'm struggling with this D2D scrabble still.

@kennykerr kennykerr added the question Further information is requested label Feb 12, 2021
@Ciantic
Copy link
Author

Ciantic commented Feb 12, 2021

If there was d2d clock example with "most performant" transparency, it would be helpful.

@kennykerr
Copy link
Collaborator

Sure, I'll see what I can do about updating the sample. Yes, that's basically still the way to do it. If you're in a hurry I also cover high-performance transparency here:

https://www.pluralsight.com/courses/directcomposition-in-action

My other DirectX courses are also helpful if you need more information about the mechanics of it all. Here's a teaser of the card game demo. 😉

https://www.youtube.com/watch?v=FVWGDfE_f5w

@robmikh
Copy link
Member

robmikh commented Feb 12, 2021

If you use composition to host your swapchain, the WS_EX_NOREDIRECTIONBITMAP window style will be your friend to get transparency.

EDIT: Oops, I was late to the party 🙂

@Ciantic
Copy link
Author

Ciantic commented Feb 12, 2021

Oh great, I will check these contents gladly! I'm not myself D2D guru, just someone who wanted a transparent windows in Druid. It's very nice to see that how rust effort progresses.

Transparent window need pops up many times, I knew few ways to do them several years back with WinAPI, all of them are outdated (layered windows and some other ways like with CreateHwndRenderTarget), but I was never aware what is the latest and greatest, it's nice to have an official reference on which to refer.

@kennykerr
Copy link
Collaborator

@robmikh is on the composition team 😉 so yes WS_EX_NOREDIRECTIONBITMAP is what I describe in the original article from 2014 (Leo B originally helped me figure that out).

@knopp
Copy link

knopp commented Feb 20, 2021

I apologize if I'm hijacking this thread, the transparency itself seems pretty straightforward, but the artifacts during resizing are giving me all kinds of trouble. Any kind of insights (@robmikh ?) would be appreciated.

Is there any way to have DirectComposition (CreateSwapChainForComposition + WS_EX_NOREDIRECTIONBITMAP) updates synchronized with DWM resizing? The absolute closest to reasonable approximation I came to is to resize the the surface in WM_NCCALCSIZE, render and present before WM_NCCALCSIZE finishes. (Even doing this in WM_SIZE is just too late).

But this, while mostly gives "non-wobbly" content, causes weird transparency issues at the right and bottom edges of window.

Somewhat weirdly, CreateSwapChainForHwnd + normal window frame resizes nicely, but everything else (custom frame through WM_NCCALCSIZE, direct composition, no redirection bitmap) doesn't. Yet somehow doesn't seem to be a problem for WinUI apps.

@Ciantic
Copy link
Author

Ciantic commented Feb 20, 2021

I'm also interested about resizing artefacts, even though I don't have persistent artefacts, we are seeing some during resizing for split second when the window resizes I think.

And in addition, I must say, nothing about transparency was straight forward 😀 in Mac it was straight forward, basically opaque(false), in Windows you had to pull few resources from your hat like this:

        // Create IDCompositionDevice
        let mut ptr: *mut c_void = null_mut();
        DCompositionCreateDevice(
            d3d11_device.raw_ptr() as *mut IDXGIDevice,
            &IDCompositionDevice::uuidof(),
            &mut ptr,
        );
        let composition_device = ComPtr::<IDCompositionDevice>::from_raw(ptr as _);

        // Create IDCompositionTarget for the window
        let mut ptr: *mut IDCompositionTarget = null_mut();
        composition_device.CreateTargetForHwnd(hwnd as _, 1, &mut ptr);
        let composition_target = ComPtr::from_raw(ptr);

        // Create IDCompositionVisual and assign to swap chain
        let mut ptr: *mut IDCompositionVisual = null_mut();
        composition_device.CreateVisual(&mut ptr);
        let composition_visual = ComPtr::from_raw(ptr);
        composition_visual.SetContent(swap_chain as *mut IUnknown);

        // Set the root as composition target and commit
        composition_target.SetRoot(composition_visual.as_raw());
        composition_device.Commit();

They were in the tutorial that was great though!

@robmikh
Copy link
Member

robmikh commented Feb 21, 2021

Could you describe what you mean by "weird transparency issues"? Any images would be helpful. I think what you've outlined is the best that can be done with DirectComposition, but I can double check.

DirectComposition's successor, Windows.UI.Composition, solves this problem a different way. In the minesweeper-rs sample, we set the RelativeSizeAdjustment property. Setting a relative size of (1, 1) and a size of (0, 0) on the root will make it the size of its parent (the window). This will keep the root's size synchronized with the client area.

Here's where we do it in minesweeper-rs:

let compositor = Compositor::new()?;
let target = window.create_window_target(&compositor, false)?;

let root = compositor.create_container_visual()?;
root.set_relative_size_adjustment(Vector2 { x: 1.0, y: 1.0 })?;
target.set_root(&root)?;

@knopp
Copy link

knopp commented Feb 21, 2021

@robmikh, thank you for the response. However we need this to be working with DXGI (the content is rendered through Angle) and handle events through HWND. Regarding the artifacts, this is a brief overview of what different approaches do:

  1. Absolute best case - Default window frame, redirection surface (CreateSwapChainForHwnd),ResizeBuffers and Present done before WM_SIZE exits. Whatever magic DWM does here it seems to work pretty well.
frame-wmsize-redirection.mov

  1. Exactly same approach as before, only difference is frame removed through WM_NCCALCSIZE. You'll notice the overal wobbliness around the right edge. Seems that with custom WM_NCCALCSIZE, DWM is not even trying to do anything.
noframe-wmsize-redirection.mov

  1. Same approach as 2., except instead of resizing buffers and presenting in WM_SIZE, it is done in WM_NCCALCSIZE. Still using redirection surface. The artifacts seem to be originating from garbage in redirection surface and you can also see bit of window border. Less wobbliness around right corner, but more than 1.
noframe-nccalcsize-redirection.mov

  1. Finally, direct composition. Resizing and presenting is done in WM_NCCALCSIZE. There is weird transparency around right (and bottom border) where the surface should be painted but it isn't (even though it was resized to correct size). No garbage from redirection surface at least :). Wobbles about the same as 3. (occasional wobble, not too horrible).
noframe-wmnccalcsize-direct-composition.mov

@robmikh
Copy link
Member

robmikh commented Feb 21, 2021

However we need this to be working with DXGI (the content is rendered through Angle) and handle events through HWND.

Just to be clear, Windows.UI.Composition is able to do both of these things.

To use a DXGI swap chain as a surface, you'll need to QI the Compositor object for the ICompositorInterop interface. From there you can call CreateCompositionSurfaceForSwapChain which will give you an ICompositionSurface. You can place this surface into a CompositionSurfaceBrush and assign that as the Brush property to a SpriteVisual.

You can also use a plain old Win32 window to host Windows.UI.Composition content. That's what the snipet I posted earlier is doing. You'll need to QI the Compositor object for the ICompositorDesktopInterop interface. From there call CreateDesktopWindowTarget with your HWND to create a DesktopWindowTarget. Assign a visual to the Root property and you should be all hooked up.

I'll also take a look at those videos, thanks!

@knopp
Copy link

knopp commented Feb 21, 2021

@robmikh, I think you nudged me to the right track. Right now, we have top level HWND and a child HWND, that gets resized during top level HWND's WM_NCCSIZE. The IDCompositionTarget is created for the child hwnd. However I I actually create the IDComposition target for top level hwnd, this happens:

noframe-wmnccalcsize-direct-composition-no-glitches.mov

The transparency glitches are gone! Well, mostly gone. Occasionally the resizing won't keep up for a moment, as there doesn't seem to be any hard synchronization going on, but it's a night and day difference anyway.

@microsoft microsoft locked and limited conversation to collaborators Feb 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants