As promised at LiBoCon'22 I have finished an example implementation of a System-Dependent PrimitiveRenderer.
It renders on Direct2D, so Win only. I chose this with no special thoughts, did not even know too much about Direct2D, so had to read quite some docu. Still managed to do this in a view days.
NOTE: To make it run you need to switch off skia on Win probably due to some global extra stuff done there (?)
The Renderer is in a new file (ca. 1000 lines) in the drawinglayer libary. It implements all of the needed primitives, so capable of rendering EditViews (Draw/ Impress complete, Writer/Calc in parts, interactions, overlay, selection(s), etc.). It is used completely as replacement of the VclPixelProcessor2D which is not used in the patched version *at all*.
Just one primitive is missing:- InvertPrimitive2D: Not needed much (hopefully will
vanish), could also be added
It is not in production state, mostly intended as a demonstration how Primitives are intended: Render
*without* using an OutputDevice at all, directly to the traget GraphicsSystem. It can (and should) be used as pattern for other implementations.
NOTE: Inside the implementation you can do whatever you want, all is allowed. Only rule is to visualize the geometry defined by the Primitives.
This version does not need any buffer allocations to render and is thus pretty fast (uses ID2D1Layer). It also has no buffering, but that could easily be added. The needed classes/implementations to buffer system-dependent representations of data exist, so doable fast for Bitmaps, Polygons and PolyPolygons.
It runs stable and fast, but still has errors, so it is sometimes missing to render some stuff. I would have to debug/finetune it further, but I see this all as doable. It's far enough to be seen as POC and example.
I also added a not-urgently-need-to-be-implemented primitive, the UnifiedTransparencePrimitive2D, to demonstrate how to do that: Add a handler for it and render the defined geometry if you can do that more direct/better than the decomposition.
The rough to-do list would be:- add missing Primitives- InvertPrimitive2D- add buffering of- Bitmaps- Polygons- PolyPolygons- add TextRendering- using decompose to PolyPolygons currently, but looks reasonably good with PolyPolygon AA- general- polishing- bug fixing- stabilization(s)- speedups by adding PrimitiveHandlers- solve problem with TransparencePrimitive2D- had to use drawinglayer::createAlphaMask there, see comments
Made handling of TransparencePrimitive2D work, some fixes and changes, too. Still not happy with processTransparencePrimitive2D, it uses drawinglayer tooling (createAlphaMask) to do it's job. It works, but there must be a better way. It's okay for demonstration/POV though
Tried hard in processTransparencePrimitive2D to directly render the alpha channel to a Direct2D ID2D1BitmapRenderTarget using DXGI_FORMAT_A8_UNORM. It does work theoretically as it should, but I found no way to directly paingt to the AlphaChannel. It seems as if Direct2D *is* hard-wired to always alpha-blend, there seems to be no control or mode to *select* the BlendMode to be used. Since only that small step is missing I keep that code in (see bTryDirectPreparationOfAlphaAsBitmap). NOTE: No idea yet, but testing shows that the direct but not yet complete way is ca. 7 times faster.
I have now added PointArrayPrimitive2D, so only that InvertPrimitive2D is missing. I also added MarkerArrayPrimitive2D to make it more efficient (marker bitmap gets converted only once) and better looking (not AAed anymore). With both added, grid visualization looks good and is fast.
Added support for BackgroundColorPrimitive2D
Added BitmapBuffering for Direct2D. This is a little bit more complicated, theoretically all ID2D1Bitmaps are specific to the ID2D1RenderTarget they get created for, so theoretically they have to be (re)created every time the ID2D1RenderTarget aka OutputDevice changes - argh. There is CreateSharedBitmap though that allows re-usage of already created bitmaps for a new RenderTaget, but this leads to needing a LO-livetime RenderTarget to create all the Bitmaps for. So I implemented that and it works and is fast.
Added buffering for GeometryData, ID2D1PathGeometry. I prepared this already using the CreateTransformedGeometry functionality of Direct2D, so the B2DPolygon/B2DPolyPolygon can get buffered in an unchanged form, re-used as source for creating a ID2D1PathGeometry by calling CreateTransformedGeometry. This could also be done by manipulating the current transformation, but is not necessary.
Simplified buffering by directly using derivated helper classes (from basegfx::SystemDependentData) as data holders, thus they can reliably call Release() at incarnated Direct2D instances without the need to keep record of this outside.
Completely removed OutputDevice from the D2DPixelProcessor2D, it was anyways only used in the constructor.
Removed the need to hand over the pixel size of the pixel target when constructing a D2DPixelProcessor2D, instead get that values from HDC directly. Also made D2DPixelProcessor2D exported from drawinglayer for further experiments.
Checked if I can use higher-level Direct2D versions by using MS's QueryInterface to see if involved classes are higher-level ones. This worked and allows to paint the alpha channel for TransparencePrimitive2D now indeed using the BlendMode that is missing in the initial D2D stuff, so activated that (much faster).
Aded the environment variable (for Windows only) TEST_SYSTEM_PRIMITIVE_RENDERER to allow easy tesing of SystemPrimitiveRenderer. Since Skia enabled does collide in quite some places (see above) I added hat this also disables Skia in it's central isVCLSkiaEnabled() method. That way it is easy to compare/quickly change between versions.
Removed maCurrentTransformation, this is just not needed for a pixel-only renderer, see comment in
VclProcessor2D's header regarding that.
Added snap-to-pixel functionality.
Added direct support for PolygonStrokePrimitive2D, so all lines which- are fat or thin- have LineCap- have LineJoin- use dash can be directly rendered by Direct2D. This is especially good for dash since no decompositions will be created for these cases. There is an exception for our own rarely used style B2DLineJoin::NONE, that still needs to be decomposed, but could also be added to the impl if needed. There were also quite some 'conditions' for supporting B2DLineJoin::Miter in Direct2D, but I managed to solve them all, so Miter is fully done in Direct2D in this example. I also added example debug code to demonstrate how it is possible to simply add decompose paint and the system-specific implementation to optically compare and play around in a running office to identify possible problems/differences.
Improved handling of TransparencePrimitive2D massively to a more future-proof and more common methodology by using the Direct2D mechanism ID2D1Effect and the CLSID_D2D1LuminanceToAlpha effect. For details, see comment in comitted code at implCreateAlpha_Direct.
As explained in refs/changes/87/141087/22 I did add now a tooling derivate of D2DPixelProcessor2D that can be used to render sub-content without having to deal with patching the current internal state of the running instance, called D2DBitmapPixelProcessor2D. It resides in the cxx file (so being safe from other usages), has and creates an own ID2D1BitmapRenderTarget to work on and will come in handy for other occasions that need to prepare a in-between result.
Made stuff in the renderer itself more private and added access methods to protected section, use those in tooling.
Change-Id: Ie5803f9afa3f62dbaa8fd2f1bac8fd30a23066b5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141087
5f29cb2fad34 Example System-Dependent PrimitiveRenderer for Direct2D (Win)
drawinglayer/Library_drawinglayer.mk | 17 +
.../source/processor2d/d2dpixelprocessor2d.cxx | 1773 ++++++++++++++++++++
.../source/processor2d/processor2dtools.cxx | 75 +-
drawinglayer/source/tools/converters.cxx | 2 -
.../processor2d/d2dpixelprocessor2d.hxx | 114 ++
vcl/skia/SkiaHelper.cxx | 9 +
6 files changed, 1961 insertions(+), 29 deletions(-)