-
Notifications
You must be signed in to change notification settings - Fork 285
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
Enable fully custom Win2D effects: new ICanvasImageInterop API #888
Closed
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sergio0694
force-pushed
the
dev/custom-win2d-effects
branch
from
October 24, 2022 14:03
a0e1870
to
c69c2f2
Compare
… builds This changes the Win2D pdbs to use sha_256 instead of the default It also clears space during pipeline builds, since the VMs used in Azure were running out of space. This should only affect pipeline builds and not local builds.
Added link to the DirectX Landing Page at the top of this readme to make sure all DX-related components link back to our new single source of truth.
Sergio0694
force-pushed
the
dev/custom-win2d-effects
branch
from
November 11, 2022 12:32
6f294db
to
e6ee22d
Compare
Sergio0694
force-pushed
the
dev/custom-win2d-effects
branch
from
December 8, 2022 00:51
d77226d
to
d7ba74b
Compare
Sergio0694
force-pushed
the
dev/custom-win2d-effects
branch
from
December 8, 2022 01:03
d7ba74b
to
4cc1943
Compare
Changes from this PR were pushed to the internal repo, so closing this one. |
getrou
pushed a commit
that referenced
this pull request
Dec 23, 2022
…nterop API > This PR is a mirror of #888. ## Tracking issue https://microsoft.visualstudio.com/OS/_workitems/edit/41925646. ## Overview Three new APIs will be added to `ABI.Microsoft.Graphics.Canvas` in `winrt/published/Microsoft.Graphics.Canvas.native.h`: ```cpp // COM interface for external, Win2D-compatible effects class __declspec(uuid("E042D1F7-F9AD-4479-A713-67627EA31863")) ICanvasImageInterop : public IUnknown { public: IFACEMETHOD(GetDevice)(ICanvasDevice** device) = 0; IFACEMETHOD(GetD2DImage)( ICanvasDevice* device, ID2D1DeviceContext* deviceContext, WIN2D_GET_D2D_IMAGE_FLAGS flags, float targetDpi, float* realizeDpi, ID2D1Image** ppImage) = 0; }; // Options for fine-tuning the behavior of ICanvasImageInterop::GetD2DImage. typedef enum WIN2D_GET_D2D_IMAGE_FLAGS { WIN2D_GET_D2D_IMAGE_FLAGS_NONE = 0, WIN2D_GET_D2D_IMAGE_FLAGS_READ_DPI_FROM_DEVICE_CONTEXT = 1, WIN2D_GET_D2D_IMAGE_FLAGS_ALWAYS_INSERT_DPI_COMPENSATION = 2, WIN2D_GET_D2D_IMAGE_FLAGS_NEVER_INSERT_DPI_COMPENSATION = 4, WIN2D_GET_D2D_IMAGE_FLAGS_MINIMAL_REALIZATION = 8, WIN2D_GET_D2D_IMAGE_FLAGS_ALLOW_NULL_EFFECT_INPUTS = 16, WIN2D_GET_D2D_IMAGE_FLAGS_UNREALIZE_ON_FAILURE = 32, WIN2D_GET_D2D_IMAGE_FLAGS_FORCE_DWORD = 0xffffffff } WIN2D_GET_D2D_IMAGE_FLAGS; DEFINE_ENUM_FLAG_OPERATORS(WIN2D_GET_D2D_IMAGE_FLAGS) // Enables external objects implementing ICanvasImage to fully implement its APIs extern "C" __declspec(nothrow, dllexport) HRESULT __stdcall GetBoundsForICanvasImageInterop( ICanvasResourceCreator* resourceCreator, ICanvasImageInterop* image, ABI::Windows::Foundation::Numerics::Matrix3x2 const* transform, ABI::Windows::Foundation::Rect* rect); ``` Their rationale is: - `GetD2DImage` matches the internal `ICanvasImageInternal::GetD2DImage` and is used to retrieve an `ID2D1Image` by Win2D APIs receiving an `ICanvasImage`, so they can then draw them onto a device context. - `GetDevice` is necessary when custom `ICanvasImage` effects implementing this interface are receiving other effects as sources: this way they can `QueryInterface` those input effects (which might either be Win2D effects or other custom ones) to `ICanvasImageInterop` and then get the device that was used to realize them, if one is available, to ensure it matches the one the current effect is realized on. - `GetBoundsForICanvasImageInterop` is needed so that external objects can implement the `ICanvasImage` APIs correctly. To do this, they need'd access to a device context, which can only be retrieved internally. To avoid exposing internal implementation details and to make the code simple, this API allows external objects to instead easily leverage the same exact infrastructure that all images are using internally. ## Usage examples A full work in progress extension to support this in ComputeSharp.D2D1 is available at https://github.com/Sergio0694/ComputeSharp/tree/...
This pull request was closed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Background and motivation
Win2D is an amazing library that makes it really easy for everyone to build custom effects pipelines and implement all sorts of cool visual effects, especially in combination with the Composition layer. APIs such as
CanvasControl
,CanvasAnimatedControl
,CanvasImageSource
andCompositionDrawingSurface
make it very convenient for developers to just get a ready to use D2D canvas they can use to create anything they need, and there's lots of built-in APIs to draw images, strokes, shapes, text, and to apply effects. There is a limitation though in cases where advanced users had the need to implement custom effects, as that's currently not supported. That is, there is no way to implement anICanvasImage
outside of Win2D itself.The only way to add custom rendering steps into a drawing session is via the built-in
PixelShaderEffect
, which does allow developers to at least draw custom D2D1 pixel shaders, but that comes with its own set of limitations:That is:
PixelShaderEffect
itself has several drawbacks, and after experimenting with it for a bit I came to the conclusion that there isn't really a way to address them without introducing any breaking changes (which are of course out of question). Not just that, even that wouldn't actually address the core of the problem: there should be a way to allow any developer to freely extend Win2D's functionality and to implement their own fully customizable effects that can then interop seamlessly with Win2D.There is a way to do Win2D/D2D interop (as documented here), which is great and can help fill some gaps, but that still isn't an ideal scenario, for two main reasons:
ICanvasImage
effects that consumers can then easily use just like any other Win2D built-in effect. That includes eg. passing them to aDrawImage
call on a drawing session, or using them as inputs for other effects. Retrieving anID2D1DeviceContext1
from aCanvasDrawingSession
object would only allow developers to inject their own rendering logic right there, but it wouldn't enable them to author and publish custom effects that would feel on par with Win2D's ones, thus greatly limiting them and making them not feel "on the same level" as the built-in effects.ICanvasImage
, such as the staticCanvasImage.SaveAsync
.This proposal aims to fully address all of these concerns, and to enable all developers to author custom
ICanvasImage
-s.Proposed API
The way Win2D handles input
ICanvasImage
-s from any of the public APIs is by casting them toICanvasImageInternal
, which is a private, C++ API that exposes the necessary extension points to interoperate with these images. The proposal extends this by adding a three publicly documented APIs that developers will be able to use for custom effects.Three new APIs will be added to
ABI.Microsoft.Graphics.Canvas
inwinrt/published/Microsoft.Graphics.Canvas.native.h
:The reason for these three APIs is:
GetD2DImage
matches the internalICanvasImageInternal::GetD2DImage
(though with a stable COM ABI) and is used to retrieve anID2D1Image
by Win2D APIs receiving anICanvasImage
, so they can then draw them onto a device context.GetDevice
is necessary when customICanvasImage
effects implementing this interface are receiving other effects as sources: this way they canQueryInterface
those input effects (which might either be Win2D effects or other custom ones) toICanvasImageInterop
and then get the device that was used to realize them, if one is available, to ensure it matches the one the current effect is realized on. This matches what Win2D also does internally for its own effects, and is a necessary extension points to allow custom effects to easily interop with Win2D effects and custom effects as their own inputs.GetBoundsForICanvasImageInterop
is needed so that external objects can implement theICanvasImage
APIs correctly. To do this, they need'd access to a device context, which can only be retrieved internally. To avoid exposing internal implementation details and to make the code simple, this API allows external objects to instead easily leverage the same exact infrastructure that all images are using internally. They'd just call this, and Win2D would run the same logic as other images.Usage examples
Implementing a fully custom effect is a bit involved, but a full work in progress extension package to support this in ComputeSharp.D2D1 is available at https://github.com/Sergio0694/ComputeSharp/tree/dev/win2d-effect. This is a UWP-specific package (I will also provide a WinUI 3 one) that depends on ComputeSharp.D2D, which will expose a new
PixelShaderEffect<T>
effect that will be specifically tailored to support D2D1 pixel shaders authored with ComputeSharp.D2D1. This will give developers the ability to write and use custom pixel shaders extremely easily in their native Windows applications 🚀Here's a short video showcasing a proof of concept effect powered by ComputeSharp.D2D1.Uwp:
devenv_q6vgFmCPgR.mp4
This runs in a Win2D
CanvasAnimatedControl
. The same effect also works seamlessly as input for other Win2D effects, as well as using other Win2D effects as its own inputs. This is why both those APIs are needed, to get parity with Win2D's implementation.Alternative Designs
As mentioned above, it would also be possible to extend
PixelShaderEffect
, but:Risks
Relatively low: this only adds a COM API in the public header, and no new WinRT APIs. The internal implementation will keep the same behavior for all existing code, and will only use the new interface as a fallback, when one is available. That is, this should cause no functional changes at all for existing code, and it only affects the few public code paths taking an
ICanvasImage
object (eg.CanvasDrawingSession.DrawImage
).