mikke89 / RmlUi

RmlUi - The HTML/CSS User Interface library evolved
https://mikke89.github.io/RmlUiDoc/
MIT License
2.86k stars 312 forks source link

RmlUi 5.0 and 6.0 - Progress and feedback #307

Closed mikke89 closed 7 months ago

mikke89 commented 2 years ago

Edit 2022-12-11: The backends concept has been completed and released together with RmlUi 5.0! There has been a lot of progress on the filters and effects part of this post, but it has not yet been merged into master. Development continues in the ~filter~ effects branch and is now targeting RmlUi 6.0.


Hi all!

I wanted to discuss some ideas and gather feedback from everyone towards an eventual RmlUi 5.0 release. I have been working in the backends and filter branches which I intend to eventually merge into master. There are two main changes:

Backends

This is a change I've wanted to do for a long time. It intends to replace the current sample shell with a multitude of backends split into renderers and platforms (loosely inspired by how Dear ImGui organize things). This way it should be a lot easier to add new backends and maintain existing ones. See details and currently implemented backends here. This already includes a more modern OpenGL renderer which has been repeatedly requested since the very beginning of this library (#261), and also Emscripten support so RmlUi even runs in web browsers now (which I find kind of ironic).

This organization allows users to more easily pick-and-choose their desired backend. Ideally, the renderers and platforms are sufficiently general so that users can use them directly in their own projects. That way, any improvements to the renderer and platform can trivially be ported upstream to everyone's benefit.

Another advantage of this approach is that we can now easily switch between different backends and test them on all the samples. This has already revealed several limitations and bugs in the backends ported from the SDL and SFML samples, so these are already better supported.

Filters, effects, and shaders

This is probably the biggest change yet to the visual aspect of RmlUi. I have long been asked by users to add support for more advanced rendering features such as custom shaders, filters, and mask images. Actually this goes all the way back to the very first issue #1. I have been working on exactly these features in the filter branch.

The new rendering features allow us to implement CSS properties including filter, backdrop-filter, mask-image, and box-shadow. In fact, I already have these properties working, although all of this is still a work in progress. Filters and backdrop filters are implemented to support all effects in CSS. Mask image is designed such that all decorators can be used as mask. Box shadows are fully featured with blur, offsets, and insets. I have also added support for linear-gradient as a decorator, now with arbitrary rotation and color stops. And yes, this can be used as a mask!

I'm considering renaming decorator to background, leaving the decorator term to be more of an implementation detail which incorporates all of filters, masks, and backgrounds. This would make everything more in-line with CSS, e.g. background: linear-gradient(#fff, #000). I've also added a shader decorator which makes adding a custom shader effect in RCSS as simple as background/decorator: shader(my-custom-shader).

Now naturally, all of this comes with the caveat of a more complex renderer. This is also why the backends change is important, because then people can more easily reuse the included renderers and contribute to fix issues. Importantly though, everything will mostly be backward compatible, so if someone does not need support for these advanced effects they can simply skip implementing the new rendering functions to keep their simple renderer and everything will work just like before. Furthermore, these changes add surprisingly little complexity to the core library so it shouldn't bring any bloat for users not wanting it.

When starting this work I figured I wanted to implement all of these rendering features simultaneously instead of taking it in steps, mainly to understand and guide the necessary abstraction layer for the render interface. Here are the new render interface functions being added:

enum class StencilCommand { None, Clear, WriteValue, WriteIncrement, WriteDisable, TestEqual, TestDisable };
enum class RenderCommand { None, StackPush, StackPop, StackToTexture, StackToFilter, FilterToStack, StackToMask };

class RenderInterface {
     /* ... */
    virtual bool ExecuteStencilCommand(StencilCommand command, int value = 0, int mask = 0xff);

    virtual TextureHandle ExecuteRenderCommand(RenderCommand command, Vector2i offset = {}, Vector2i dimensions = {});

    virtual CompiledEffectHandle CompileEffect(const String& name, const Dictionary& parameters);
    virtual TextureHandle RenderEffect(CompiledEffectHandle effect, CompiledGeometryHandle geometry = {}, Vector2f translation = {});
    virtual void ReleaseCompiledEffect(CompiledEffectHandle effect);
}

This is very much a work-in-progress, there are still questions and improvements to be made. I'd love to hear some feedback in this regard.

Some notes and open questions regarding the interface and implementation:

Apologies this became longer than intended, and more like a combined blog entry and notes to myself, not sure if these notes make sense for anyone else. In any case, some general feedback would be highly appreciated.


I'll leave you with a screenshot which showcases many of the filters and effects. The really cool part is that all of these can be composited and stacked as desired by the user. Note that there are only two bitmaps used in this whole sample, the little invader saucer and the RmlUi logo.

effects
xland commented 2 years ago

All the work you doing is amazing! Is there a pre-release branch for testing?

paulocoutinhox commented 2 years ago

Hi,

Im trying run in iOS and MacOS, but can run it in Metal.

#error "Only the opengl sdl backend is supported."

Can you add support for Metal?

Thanks for this great library!

mikke89 commented 2 years ago

Thanks guys! Yeah, you can find these changes in the backends and filter branches. Regarding Metal support, see #193.

xland commented 2 years ago

@mikke89

What's the difference between backends branch and filter branch? If I want to try the new features, which branch should I choose? Will there be a 5.0 branch in the future?

mikke89 commented 2 years ago

@xland Good question, I could have been more clear here.

So backends implements the Backends as described in the original post. The filter branch is based on top of the backends branch and contains the advanced graphical features discussed.

I intend to merge backends to master soon, as I think this is getting close to some sort of stability. The filter branch on the other hand is more experimental and might change a lot, so I intend to keep it in a separate branch for a while.

The filter branch is where you find the latest and greatest, but consider it highly experimental :) You'll find the new effect sample there which is shown in the screenshot.

stephenap07 commented 2 years ago

I think the Effect addition to the API would probably work well. ExecuteRenderCommand sounds generic enough to apply to all rendering though. Would these render commands only be applied for filters? I personally use RmlUi to render in a game world in 3d space without framebuffers. It just looks better to use individual mipmapped textures for rendering the GUI. I haven't tested yet, but I suspect SDF fonts will also look better from various distances with this method as well. I use stencil tests instead of scissor regions for this reason. Since there will be extensive use of stencils now with this API, how much control over the mask values will the client have?

Good work, by the way.

mikke89 commented 2 years ago

@stephenap07 Yeah that's a good point. Do I understand you correctly that you mean it is better to render the RmlUi geometry directly to the 3d-world, rather than rendering to a flat texture and then rendering that texture in the 3d-world? That is a use case I would need to keep in mind indeed, I think it should be compatible currently though.

This is a similar situation to what happens internally when we have filter + transforms applied, and I'm honestly not sure what the best approach is.

One change I've currently implemented is that the stencil command is used instead of the scissor region automatically whenever a transform is applied. In your case you would still have to direct the scissor region to stencil operations manually I guess. Alternatively, you could also add a transform to the document itself, placing it into the world. This way you could just use the commands as usual, no need for another manual transformation/projection step.

Yeah, you make a good point that we should accommodate client-side use of the stencil buffer as well. I was originally thinking that we would require an 8-bit stencil buffer (and thereby mask), and never go above this. But I guess stencil buffers are often/sometimes limited to 8-bit, so maybe we should reserve, say, the lowest 4-bit of that for the RmlUi commands and let the client use the rest?

Yeah, ExecuteRenderCommand is a bit general, do you have a suggestion for a better name or other functions? You can see the commands in the enum, and they mostly/all have to do with the render stack. Implementation of this will be completely optional and only needed to support new properties: filter, backdrop-filter, mask-image, and box-shadow.

xland commented 2 years ago

@mikke89

Is there a plan for RmlUi 5.0? I'll start a new project in the middle of June. Should I wait for RmlUi 5.0?

RmlUi is a very nice project. Thanks very much.

mikke89 commented 2 years ago

I don't have a any dates in mind yet, it will probably be some time. I don't expect any major breaking changes so you should be fine starting with RmlUi 4 now or even using the master branch, and then upgrading once the new version is out.

Thanks for the kind words!

mikke89 commented 2 years ago

The backends branch has now been merged into master :)

The filter branch is very much still experimental and open to API changes.

paulocoutinhox commented 2 years ago

Hi,

This is working for metal backend?

Thanks.

mikke89 commented 2 years ago

No, there is no work being done towards a Metal backend right now. That would be entirely up to contributions from users. As previously noted, there is already #193 for discussion on this topic.

enesaltinkaya commented 2 years ago

Any chance of using this library in plain C ?

mikke89 commented 2 years ago

We don't have any official C bindings so you would have to make your own.

enesaltinkaya commented 2 years ago

Alright, thanks.

YTN0 commented 2 years ago

Just wanted to see what the progress on this was. I am specifically interested in the custom shaders aspect.

Would there be a way to specify additional or custom metadata to an object / tag for use by custom shaders? E.g. for an image / texture display, I might want to attach a normal map or other maps (occlusion etc) for use by the custom shader that will render it.

mikke89 commented 2 years ago

Hey, appreciate the interest. It has come a long way, I spent a lot of time trying to arrive at an API I'm happy with. There have been a lot of changes in this regard, and it seems I'm converging on something I quite like. In particular, you can see the latest render interface here.

There are quite a lot of changes, so I want to make sure to get things right before starting to merge things. I'm currently considering making an RmlUi 5.0 release without the advanced effects, as there has already been quite a lot of useful additions and fixes in the library. And then we can bring in the advanced effects later on.

Right now I've implemented a "shader" decorator which takes a single string as a parameter. That way you can in principle specify whatever you wish in RCSS, but you'll have to parse it yourself. Do you think this is suitable for your case? For more detailed control you can make your own decorator. Although I do think this will be a rather common use case so perhaps there are some things we could do to make this case require less setup.

YTN0 commented 2 years ago

Thanks for the update. I think that could work. I agree that flexible shader decorator would be best as opposed to having to go the custom route. I suspect the shader decorator would get a fair amount of use.

Let me know when you have something testable, and I'd be happy to give it a whirl.

mikke89 commented 2 years ago

Alright, sounds good. I'll probably put up a pull request once I'm ready for feedback, I'll try to make sure to ping you :)

YTN0 commented 2 years ago

👍

mikke89 commented 1 year ago

Hey, just an update now that RmlUi 5.0 has been released. The backends concept has been fully merged, and released with the new version! There has been a lot of progress on the filters and effects part as well, but it has not yet been merged into master. Development continues in the filter branch and is now targeting RmlUi 6.0.

YTN0 commented 1 year ago

@mikke89 Appreciate the ping. Will definitely give this a try. Thanks for your continued work on this!

andreasschultes commented 1 year ago

I See that Context CreateContext(const String& name, const Vector2i dimensions, RenderInterface custom_render_interface) was removed in the last commits. I currently use different context with different render interfaces because I render to different render targets. For example render to a texture and use the texture than on some rendered objects. Or UI on screen which is blurred and UI on screen which is not blurred. I'm not sure, how such cases will be handled in future.

mikke89 commented 1 year ago

Yeah, you're right. I decided to make this change for the reasons outlined in the commit message: https://github.com/mikke89/RmlUi/commit/0bbd0193cb1a8464832fa2ec91a5a8240570e205.

I also work with render-to-texture and effects similar to what you describe. For me its mostly about setting up the render state before issuing the call to Context::Render. I hope you'll find a way to work with these changes.

SirNate0 commented 11 months ago

@mikke89, from the documentation it seems that PopLayer will be used to actually render the layer, rather than just removing it from the layer stack. I am wondering if ApplyLayer might be a better name (and then perhaps CreateLayer instead of PushLayer)? That said, I see that it is also supposed to return something, but it seems not to, so if the documentation is just out of date, sorry. https://github.com/mikke89/RmlUi/blob/4ad61d053c8492062a9e6dfd6d73be8ffbe6b41a/Include/RmlUi/Core/RenderInterface.h#L131-L137

mikke89 commented 11 months ago

@SirNate0, the development has moved to the effects branch and has seen many updates since. I understand this wasn't very clear from the initial post, so I updated that too. I intend to put up a pull request relatively soon, and looking forward to any feedback on that.

With that said, the functions you refer to have the same names, so your feedback is very much still applicable:

https://github.com/mikke89/RmlUi/blob/e72de1d6f949dd88a32f53e30a8e103b1117f253/Include/RmlUi/Core/RenderInterface.h#L143-L149

I agree that Create/ApplyLayer much better communicates that rendering is occurring. One challenge I see is that normally we are blending onto the layer below while popping. However, without push/pop, it isn't clear that it is supposed to be a stack of layers, and the term "layer below" doesn't really have a meaning. Some alternatives that we might want to consider:

  1. Maybe keep push/pop, but make a separate Blend/CompositeLayers function.
  2. Drop the concept of the stack entirely, use CreateLayer and let it return a user handle for that new layer. Then, let the apply/blend/composite function use the layer handles. I guess we would also need a ReleaseLayer.

Both of these might complicate implementations a bit, especially the latter one. But I think it's something worth investigating. Any thoughts? Also, the current PushLayer has different ways of initializing the layer (clearing or cloning the current layer), not sure how this fits into the picture?

mikke89 commented 9 months ago

A pull request is now out, implementing all of the effects mentioned here, and some more: #594

I hope you'll like it :) And I'd appreciate any help with testing it.

mikke89 commented 7 months ago

The PR has been merged, and I just wanted to give an update on the feedback by @SirNate0.

  1. Maybe keep push/pop, but make a separate Blend/CompositeLayers function.

I decided to go with this approach in the end, and I think it helped a lot. Both to simplify the API usage, since we now can composite two arbitrary layers, and it also works a lot better semantically in my view. I kept the push/pop render stack, since it simplifies things to have one main active (top) layer that all draw functions render to. And the push/pop nature plays nicely with how the library uses layers generally. Thanks for the feedback, it definitely helped to make the interface better.