Draw each 2D mesh using a single CairoMeshPattern#5446
Draw each 2D mesh using a single CairoMeshPattern#5446ffreyer merged 20 commits intoMakieOrg:masterfrom
CairoMeshPattern#5446Conversation
It seems each pattern mesh in Cairo can contain multiple patches [1],
so move the pattern create and finalize out of the loop over patch
elements.
This reduces the PDF and SVG file sizes created by the sample code
below. Presumably the savings will be larger for figures with greater
number and/or more complex meshes.
| File | Before | After | Ratio |
|:------------:| ------:| ------:|:----- |
| `svgbug.svg` | 8168 B | 4197 B | 0.514 |
| `svgbug.pdf` | 2854 B | 2129 B | 0.746 |
```julia
function svgbug()
fig = Figure(size = (50, 50))
ax = Axis(fig[1, 1])
hidedecorations!(ax)
arrows2d!(ax, Point(0.0, 0.0), Point(1.0, 2.0), argmode = :endpoint, color = :firebrick3)
save("svgbug.svg", fig, backend = CairoMakie)
save("svgbug.pdf", fig, backend = CairoMakie)
return fig
end
svgbug();
```
[1] https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-create-mesh
Says: "Additional patches may be added with additional calls to
cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()."
[2] The name for `svgbug()` comes from figuring out a SVG rendering
problem, eventually tracked down to probably being bug
MakieOrg#4155
|
Did a bit of digging, the This should also apply to mesh3d btw |
|
Good find. I was hesitant to go beyond my already-shaky understanding of Cairo 2D into the 3D case, so the feedback is greatly appreciated. (Soon-ish) I'll take a stab at implementing the simple loop counter idea for both 2D and 3D meshes. |
|
The latest commit uses a simple counter to flush the pattern every Nth polygon (semi-arbitrarily chosen as N = 16384). Using the MWE from #2454, I ran
Master (merge-base, 1163053) → Branch (commit 2, 0786ab0) I can't actually reproduce the empty PNG case from #2454 on my machine (it renders even if I set N = |
- faceculling should run with clip planes too - faceculling should check the z component of the normal direction in view or clip space, or compare to viewdirection (vs), but checked normals in model space instead
|
Flushing the pattern on side switch seems reasonable to me. My main use case is for 2D meshes in a 2D camera anyway, where (if I understand correctly) this should render all mesh elements in a single patch. |
|
Thanks for making the improvements! FYI, I've re-run the benchmark I ran in #5446 (comment) and saw a significant slowdown, catastrophically so for SVG. To isolate which commit impacts the runtime, I ran every commit between 86ecf6b and 581f592:
Now, the benchmark is maybe not that important since it tries to generate a very high resolution vector mesh and (a) fails for SVG and (b) creates huge files for PDF — choosing to set
In the end, my use case is fixed by #5459 instead of this PR, so I don't have any opinion about whether there's a problem to fixed here or not. |
|
Yea I've noticed the slowdown too. I ran the example with 1001 instead of 8001 and got 50s -> 88s. In Maybe mixing patterns with few and many patches is just slow? Much slower than either extreme? The 1001 surface ends up flushing 510 564 times for 1 022 000 faces, with a group of about 50 faces/patches at the start/end and lots of single face/patch groups inbetween. Both never flushing on back/front face switches (like before my commits, ~20s) and always flushing (~50s, matches master) are significantly faster (than the 80s). Filesizes are still better than master though. I guess we should just revert to master for 3D, or add a check to only use this when the number of flushes remains low |
|
Was typing this as I saw the previous comment come in... I started running the SVG case in a so it seems the usage pattern is just a catastrophic case for how libcairo implements its SVG rasterization. |
|
Maybe it would be much easier to add a basic CPU mesh renderer with a z buffer than using the patch functionality in Cairo which is not actually optimized for triangles and pretty slow and degrades svg files somehow when used. Claude might be pretty good at transferring functionality from glmakie shaders. |
|
An even easier (partial) solution might be to just always set the |
The new Raytracing julia backend could be extended for that use case - i think we could even quite easily teach it rasterization ... |
|
I do want CPU based rasterization for Tyler in CairoMakie and GeoMakie meshimage in general, so would be happy to help push that through |





Disclaimer:
Running the test suite locally showed no change across this PR (one local failure), butI don't fully understand the Cairo drawing system. I noticed the opportunity for this change when trying to debug my own SVG problems, which ends up probably being #4155.It seems each pattern mesh in Cairo can contain multiple patches [1], so move the pattern create and finalize out of the loop over patch elements.
This reduces the PDF and SVG file sizes created by the sample code below. Presumably the savings will be larger for figures with greater number and/or more complex meshes.
svgbug.svgsvgbug.pdf[1] https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-create-mesh
Says: "Additional patches may be added with additional calls to
cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()."
Checklist