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

Expose bindings to access the ResourceManager cache #910

Closed
Sergio0694 opened this issue Mar 12, 2023 · 5 comments
Closed

Expose bindings to access the ResourceManager cache #910

Sergio0694 opened this issue Mar 12, 2023 · 5 comments
Labels

Comments

@Sergio0694
Copy link
Member

Sergio0694 commented Mar 12, 2023

Note: follow up to #888 and #894.

With the work done in the two previous issues, Win2D now exposes a set of APIs to allow developers to implement custom effects. This works very well, and it's already being leveraged by ComputeSharp, which is used in the Microsoft Store to power several custom graphics components in its UI. While the new APIs cover most scenarios, there is one that is unfortunately not supported just yet, and that is retrieving WinRT wrappers for D2D images. While this is a more niche scenario, it would be nice for custom effects to benefit ffrom the same level of support of built-in Win2D effects (this has been the goal for all other features as well).

Consider this example:

  • You have some Win2D effect (either built-in, or custom)
  • You get the ID2D1Image (which is an ID2D1Effect) from it
  • You create an instance of a custom effect
  • You also get the ID2D1Image from that
  • You manually set this image as input for the previous effect
  • You then ask that first Win2D effect for the WinRT wrapper for that input

Win2D will see that the effect is realized, so it will use the native D2D effect as source of truth. It will call ID2D1Effect::GetInput, and then invoke the resource manage to get a WinRT wrapper. Since the GUID of this D2D image is not one that's recognized by Win2D (it's just some custom effect), it will then fail to resolve a wrapper, and throw an exception:

API proposal

To solve this, we should add two new APIs to allow custom effects to interact with Win2D's ResourceManager. Since that type is already accessible to resolve WinRT wrappers through the GetOrCreate API, we could add the two new APIs on that same activation factory. This would also make sense given these operations are related to WinRT wrappers in general, not just COM interop.

class __declspec(uuid("695C440D-04B3-4EDD-BFD9-63E51E9F7202"))
ICanvasFactoryNative : public IInspectable
{
public:
    IFACEMETHOD(Add)(IUnknown* resource, IInspectable* wrapper) = 0;
    IFACEMETHOD(Remove)(IUnknown* resource) = 0;
};

And accompanying C++/CX helpers (just like for GetOrCreate):

template<class WRAPPER>
bool Add(IUnknown* resource, WRAPPER^ wrapper);

bool Remove(IUnknown* resource);

Note: the ICanvasFactoryNative interface already exists, the proposal is just to add these two new methods to it.

Custom effects would use these two APIs in the same way as Win2D already uses the ResourceManager class. That is, every time they realize an effect they'd call Add, and every time they unrealize an effect they'd call Remove. This would ensure that each D2D effect would be registered in the cache, so that Win2D would be able to resolve WinRT wrappers even when the effects are directly set via the D2D layer and without touching any WinRT wrappers. This would make the scenario mentioned above work correctly.

While not part of the API signature, callers of Add are required to also have the passed IInspectable object (ie. the custom effect) to implement IWeakReferenceSource as well. This matches what Win2D already does, and prevents memory leaks due to a reference cycle between the underlying D2D resource and its wrapping WinRT object.

@Sergio0694 Sergio0694 changed the title Expose bindings to allow custom effects to access the ResourceManager cache Expose bindings to access the ResourceManager cache Mar 12, 2023
@sylveon
Copy link

sylveon commented Mar 13, 2023

While not part of the API signature, callers of Add are required to also have the passed IInspectable object (ie. the custom effect) to implement IWeakReferenceSource as well. This matches what Win2D already does, and prevents memory leaks due to a reference cycle between the underlying D2D resource and its wrapping WinRT object.

Why not make it part of the API signature then? E.g. IFACEMETHOD(Add)(IUnknown* resource, IWeakReferenceSource* wrapper) = 0;

@Sergio0694
Copy link
Member Author

Same reason as to why ResourceManager uses IInspectable*: to enforce that wrapper objects have to be WinRT objects. IWeakReferenceSource* is just a COM interface, not WinRT. This will all "just work" anyway for all C++ devs, since the C++/CX/WRL/WinRT tooling already enables IWeakReferenceSource by default for all types automatically, it's just C# devs that'll have to manually add that interface, but it's pretty easy to do. This makes it clearer that the wrapper object being tracked has to be the Win2D-compatible WinRT object that the D2D image was retrieved from, and not some other COM object.

@rickbrew
Copy link

So the implementation of Add(IUnknown*, IInspectable*) would check the object for both interfaces and return (e.g.) E_NOINTERFACE if it doesn't support both?

@Sergio0694
Copy link
Member Author

Sergio0694 commented Mar 13, 2023

"for both interfaces"

What do you mean by that? Those two are distinct objects. Eg. for a custom Win2D effect:

  • The IUnknown* would be the realized ID2D1Effect* object.
  • The IInspectable* would be the WinRT object representing the custom effect.

Eg. in ComputeSharp terms, the IInspectable* would be the CCW for PixelShaderEffect<T>.

@Sergio0694 Sergio0694 added the D2D label Mar 15, 2023
@Sergio0694
Copy link
Member Author

This was implemented internally, will be included in the next preview releases. Closing this 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants