Why should I prefer zengl over moderngl? #37
-
Hi! I saw that you were also the main developer of ModernGL. I was wondering what was the main reasons one should prefer zengl over moderngl outside of its simplicity. |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments
-
I hope this helps. ModernGLModernGL was first released around 2016. However it gained not much traction until version 4.x or 5.x and those versions are very different from the previous ones. An average ModernGL users does not use these new features. program = ctx.program(...)
texture = ctx.texture(...)
model = ctx.simple_vertex_array(...)
program.use()
texture.use()
model.render() Later samplers were introduced, and were possible to bind independently from textures. Same goes for the scope objects. vao.scope = ctx.scope(samplers=...)
ctx.enable(DEPTH_TEST)
vao.render() # depth is not enabled here as the vao.scope has an empty enable only Yet again the ctx.enable() was not deprecated in favor of the most common use cases. ModernGL has a lot of features that are probably less reliable than worth using. For ModernGL there were nothing I could do despite I tried it many times. ZenGLI designed ZenGL from zero, based on the experience gained implementing ModernGL. Does this mean ModernGL has a better OpenGL supports? Well, no. Here is why: ZenGL has no compute shaders, but most of the compute shaders you plan to implement with ModernGL could have been implemented with a fragment shader. ZenGL has no storage buffers, read/write images in shaders, atomic counter, and so on. ZenGL has less vertex and image formats available, but here is the thing: ZenGL uses the same subset of vertex and image formats as WebGPU. So using ZenGL you might find no float16x3 for size optimized normal vectors. Adding a bit of padding and using float16x4 is actually way faster. For a beginner ModernGL might look easier to use and learn. For a seasoned OpenGL developer, ZenGL provides a richer experience mostly due to the self-contained pipelines and internal caching. The features ModernGL could not have landed in ZenGL. Self Contained PipelinesThis was actually done in moderngl as scopes. But due to the global state it is not as realiable as I wanted them to be. CachingThe title is a bit of misleading. Caching here actually means the ability to know what is in use and not to rebind it. For example: Having two ZenGL pipelines with the same global state, rendering the second pipeline does not set the global state. Caching is nothing fancy on the inside. Immutable ObjectsIn ZenGL Pipelines are immutable. Does the pipeline render to the wrong framebuffer? -- it was defined when the pipeline was created. Error CheckingSelf-Contained, Immutable pipelines allows us to actually check if the state is complete: Having a uniform buffer without a binding is likely an error. I really wanted this in ModernGL but is clearly not possible without Self-Contained, Immutable pipelines. Bottom LineTo pick between ModernGL and ZenGL is mostly the matter of taste. Implementing the rendering is mostly the effort of writing shaders, not just how you structure your code. ZenGL has web support, this is a fairly new thing. |
Beta Was this translation helpful? Give feedback.
-
To answer your question short: ZenGL has self-contained immutable pipelines with error checking. Internal caching helps with the number of api calls. |
Beta Was this translation helpful? Give feedback.
-
This is such a great response. Thank you for your time. Immutable PipelineI understand your logic behind Immutable Pipeline, but I fear this design will have the same flaw as Vulkan's bake pipeline. By constructing an immutable pipeline, you need, as the developer, to know every possible combination from the start. This can be quickly unmanageable based on my limited experience in Vulkan. Is it something that could happen with Zengl? If yes, how would you try to overcome this drawback? For example, if you need to resize your viewport, you now need to recreate every pipeline. |
Beta Was this translation helpful? Give feedback.
-
Indeed I missed to add the exceptions. The viewport, raw uniform values, vertex/index count, vertex offset, instance count are part of the dynamic state in ZenGL. pipeline.viewport = (0, 0, w, h) you can even bind these values to a memoryview. for multiple pipelines you can have a single piece of memory holding the viewport values. ZenGL objects are actually lightweight. you can have a builder function to avoid the problem knowing all the combination ahead of time: def make_pipeline(ctx, texture, **material_info):
return ctx.pipeline(
vertex_shader='...',
fragment_shader='...',
layout=...,
resources=[
{ ... texture goes here },
],
uniforms=material_info,
) early in the prototyping phase i often have a pipeline builder function like the one above. not used pipelines can be released. also please note that the vertex_shader, and fragment_shader code was passed as a string in the make_pipeline. due to the caching it won't be compiled N times just once the first time. |
Beta Was this translation helpful? Give feedback.
-
Thank you once more! I think you should put a link on the main readme so that others can evaluate the tradeoff between the two. |
Beta Was this translation helpful? Give feedback.
-
Thank you, I will |
Beta Was this translation helpful? Give feedback.
-
Would like to see zengl-window similar to moderngl-window |
Beta Was this translation helpful? Give feedback.
I hope this helps.
ModernGL
ModernGL was first released around 2016. However it gained not much traction until version 4.x or 5.x and those versions are very different from the previous ones.
The most recent version of moderngl is mostly the same idea as it was in 4.0.0 (2017-05-20).
Since then new features were requested and added. Some of them duplicated existing functionality.
An average ModernGL users does not use these new features.
Instead, they mostly rely on the very basic set of features that were already present in 4.x.
Take textures for example:
L…