[DirectX 12] Backend - #648
Conversation
|
Thanks a lot for working on this, a DirectX 12 backend will be a very welcome addition! Let me know once you feel it is ready for review. I quite like the initiative for introducing such a "renderer initialization" type that provides the user's own parameters. Although, I'll have to think a bit more about this, and how it fits into the library and how it integrates. I think it's important to keep it so that we don't take control over the main loop and event system, to ensure that the library plugs in to their existing infrastructure. Which is why we currently say that users should copy the backend itself into their own code (as opposed to the renderer and platform parts of the backend). But at the same time, we want to make it easier to get started for users that perhaps only want to provide their renderer details, but otherwise use the backend as it is. This generally looks like a nice opt-in solution that could be helpful in many use cases, but I'll need to take some more rounds and think about it. Regarding issue templates. I actually quite like the informal nature of giving users a blank slate to describe their issues. I think that has some value as a community. Of course, I understand that with growing number of issues, some system like this could be helpful to speed things up, but I don't believe this is an issue for us at this point. I generally find that users are good at describing their issues. Regardless of templates, there will always be some times where more information is necessary. And that is perfectly fine in my view, there is also some value to this interaction as well. So I would prefer to keep this as it is. |
Just wanted to chime in on this one and say I agree; overly-complicated forms and templates have dissuaded me from making issues on other projects (same for something like stalebot, which I hate even more). I understand why some people use them but I find most people are pretty good with issue-posting when you don't give them any instructions at all. |
|
@mikke89 Hi and I have some good news! 😃
DirectX-12 probably is closing to be finished! Also I would like to add that I marked all methods for CPU and GPU profilers (GPU only those methods/functions where any calling from API is issued and it is helpful for debugging purposes for marking functions callings from API) Anyway, I need to warn that you should think about passing invalid viewport/scissors and how it handle from frontend side because in DirectX-12 the scissor test is always enabled and in such case we get error like nothing happens much on Debug you will receive error (like debugbreak every time) or if you suppress (what I did now) a warning and it is kinda annoying :D I did experiment and treated a such situation where invalid scissor and vp is passed like not suppressing from API side but manually and in such case half of frames are not rendered at all like you get empty clear window's background without those buttons and other subwindows regions so it didn't work out as expected. The warnings are like these:
Currently I need to finish some little things like checking Release build configuration (maybe I forgot something but geometry is kinda sus rendering but on Debug is all fine even validation layers didn't say anything illegal, no errors) and provide implementation of integration sample and to show minimal sample how RmlRenderInitInfo works (it is just matter of some hours, probably tomorrow I will write back here), and implement SDL, GLFW backend for DX12 it is small tasks too. Because now we get everything works like all existed samples + effects sample but on your side I would be glad if you check visual tests and report something unusual or just some mistakes (probably when I will write that it is ready to be merged). I won't touch Readme and you can fill all needed information like updating backend table :) I would like to hear any critique for default arguments that can be changed for/from user and they exist here And discuss it like what you like what you don't like and etc, because my aim was to handle all situations like why I choose preprocessors because some users can build RmlUI manually and they would like to change constants but themselves without CMake generations steps (configuration) from another hand there's a thing for CMake users too they can just pass their overrides and just use what they want to and if user doesn't need to change anything they will be used (and just used) as defaults in case if user didn't specify in RmlRenderInitInfo (I didn't make CMake side so it is better for someone to finish that feature, so it means that defaults will be generated and maybe as whole file through CMake) About RmlRenderInitInfo it serves for two purposes
Also something worries me about constant buffers (uniform buffers) I mean they're a lot of them and would be better to think about possible optimization how to pass that data to shaders in reduced manner, maybe it is okay, but if there's something that might be improved in terms of their amount it would be perfect (I didn't think personally about how to reduce them yet) I would like to hear other people and who used our backends and to hear their wishes/needs/discontent |
|
Hey, that's great news! Looks very promising, good job! I'll have to get back to you with more testing and after taking a closer look. Regarding the configuration parameters. I think it's good to have anything that should be tunable easily accessible like that, so I'm all for that. I would perhaps simplify it a bit, and just do Nice work! :) |
|
@mikke89 hi mikke I implemented the integration sample and I really recommend for you to implement the GL3 integration samples following my design and understanding my ideas and way of implementing things, but I tried to achieve two things:
But I just spend not so much time for making things beauty (in terms of code) and for that I recommend that thing for you :)) At least just to know that you would make things as you want to see because I can't know how's better to handle/write things from your side and not making any interferences you can finalize my idea about integration thing so better for you to refactor I just made things working. Then I really want to upgrade our Vulkan and make effects sample working and we will have new way of using our backends for user environment and they don't need to copy/paste files and just directly use our code of RmlUi library. 😄 🎊 💯 Yeah I will finish postprocess integration sample, but for now swapchain works it is just example of load document sample and I think I need to test on effects sample that will be renderered onto swapchain image. So my idea is to provide for user two integration examples:
So yeah, the main thing of integration sample it must be implemented from scratch demonstrating user engine's environment and show all things in one file and we just show using commentaries the needed steps for integration. I really want to see some users that have multithreaded renderers in their engines and thus they would like to render UI in some of separated thread and they have to report like there's no samples that will demonstrate a such integration, but still it must be a generalized solution, we will see I guess. The really last thing what I need to fix and resolve for Dx12 it is CompositeLayers function where Blur and DropShadow render somehow the image result is flickering and idk why I forced to use Flush method (you can see in RmlUi/Backends/RmlUi_Renderer_DX12.cpp Line 3405 in f96808d I tested my backend on three videocards they are all Nvidia videocards so GTX 1060 Ti 6 GB (PC), GTX 1660 Ti 6 GB (PC), RTX 3060 laptop 6 GB (notebook) No one of them reported any problems, but I suggest to your known testers (and you personally) to use their videocards and run our samples on debug make with RMLUI_DX_DEBUG preprocessor enabled in order to get and receive any missed barrier (because when I thought that I finished development and when my 3060 didn't show any troubles the GTX 1660 showed me the one missed barrier so it is quite reasonable to test on other videocards that users have), BUT it is really important to run without Flush calling where I show in CompositeLayers. Also funny fact the GTX 1060 renders well with or without Flush calling in CompositeLayers so I really hope it is possible to debug that thing and to understand where the problem on modern GPUs, but just FYI. Currently integration samples will add only on DX12 (using standard procedure cmake -B Build_Win32_DX12 -S . --preset samples -DRMLUI_BACKEND=Win32_DX12 -DCMAKE_TOOLCHAIN_FILE="//scripts/buildsystems/vcpkg.cmake"), but when you make GL3 you can add GL3 in cmake file of these samples by path Samples\basic\integration and Samples\basic\integration_2 (maybe I should rename as integration_swapchain and integration_2 as integration_postprocess but I thought it is too long names but whatever) And yes, since we have a situation where backend and shell samples can run under different window management libraries or native that means we have to provide all integration samples for each of window management libraries or native implementation, for now we have only win32 I added temporary the others sdl and glfw for integration samples but I didn't complete them. And yeah I know my explanation is kinda chaotic but it was needed to make RmlUi_Backend.cpp file where "generalized" functions will be called for user integration purposes (you will when you will try to implement on my pull GL3 samples). The main question about design is where to store implementation functions for each backend where user requests in Backend::Initialize and other related functions that have similar design approach as Backend::Initialize version for integration (see rmlui_sample_integration_as_swapchain on dx12) because for now in RmlUi_Backend.h I made namespace for each backend and their functions and maybe we need to isolate from public space and add prefix ___ like I did for renderer_type variable, for now only dx12 implemented but storing in native files like Win32_DX12.cpp it is idk so better for you to think about where to store them maybe it is okay since we stored in RmlRenderInitInfo information about window handle and that window handle will be proceed in respected ProcessEvents based on window management library or native implementation again it is still better for you to think about how to make things nicer I just wrote dumb variables but obviously we have to have some internal structure and better accessing and all that stuff, so see variables __renderer and p_integration_renderer). Also we need to think about FileSystem and how to pass that thing for user, maybe we can use default ShellFileInterface but specify root from init info or just force user to pass own file interface (but I guess it is better that user expects that it resolves by itself like just hidden in Backend::Initialize function and maybe first approach is better where user just passes root maybe we can determine root if it wasn't passed by ourselves expecting just root as folder where application is executed or maybe to force user to pass root string otherwise if it wasn't passed we just say like failed to initialized backend) RmlUi/Backends/RmlUi_Backend_Win32_DX12.cpp Line 274 in 4efd7cc Still I would like to hear any feedback from your side, thanks. 2025-07-05.18-08-38.mp4 |
|
@mikke89 hi, any news? 🙃 |
|
Hey, and sorry for the slow response, it's been a bit too busy for me lately. I will have some more time soon to delve into this, and get back to you. :) |
|
Hello again! First of all I want to say it's a really nice effort! It's a great feat to have implemented all the features in a new render interface. I have been testing it a bit now, and started looking through it. The effects sample seems to be working well for me. I do find some issues in the visual tests. It is a lot to chew over all at once. Especially with both the new render interface and the changes you're making with the backend initialization and integration. I would say these are really two very different set of changes, both of which are really large. And it's impossible to discuss all of that at once. The backend initialization in particular requires some more discussion and design iterations I think. Thus, I suggest that we split this pull request in two, or possibly more, and discuss each change separately. I'll keep the rest of this post about the DX12 renderer, assuming we take the other parts in a separate PR. I'm seeing some issues in the visual tests, I'll just list my findings here, in no particular order:
I think that's what I found so far. Otherwise, a lot of things are working really well, and the gradients and such looks correct from that I could tell, that's really nice. |
|
@mikke89 hi I pushed the fixes related to your last report and verify everything that works good please (and I didn't bring new bugs lol), for now I need to finish postprocess integration sample and that annoying blur bug that exists in effects sample. Personally I hope when I port Vulkan it will show me exact problem where the sync primitive is missed but for now it is only iterational approach to understand what works not good due to missed barrier. It is def compositelayers place but debug validation doesn't say anything bad on all video cards 🫤🫤🫤 |
|
@mikke89 just FYI, I refactored upload policy for textures and improved it so we create staging buffer that uses two fields check if user wants to upload texture that bigger than initial size of staging buffer than it has dynamic nature as it was before (uses temp buffer but allocates for requested size), but now we allocated that buffer once and until we didn't face a such big texture we just re-use existed buffer without constant allocation/deallocation that can leads to bad things as degraded performance with time and possible (in theory) memory fragmentation. Same thing I did for Vulkan. |
|
Yeah, I think that sounds very reasonable, to keep one buffer like a sort of scratch buffer. |
|
@mikke89 revisited again the trouble with effects sample where it was guessed that barrier was missed but now when Windows 11 dropped update related to DX12 SDK the bug was gone (aka stable image problem). Probably it was the SDK's or Driver's bug because my investigations showed that on our side it was clear and we didn't get any validation error report related to barriers usage so on trouble videocards the bug is gone on debug/release configurations. So in any user report it goes to vendors report not to us. So the last thing I need to finish and I will do that very soon it is postprocess sample and you can start to merge my work (also I need to fix all warnings :D). I fixed the new API validation error it is really minor but annoying. NVIDIA 3060 laptop RTX (Release) nv3060rtxlaptop.mp4 |
|
Very nice! Thanks for the update, looks really good! |
|
@mikke89 I finished my integration samples for win32 but for other input platforms like glfw3, sdl2, sdl3 (if they're not finished and probably they're not finished because it is separated files) it is just copy and paste code from native win32 samples and just change code (replace win32 loop messages and provide functions from these libraries respectively), anyway So you can start to merge my work, but keep in mind that:
What I didn't yet implement it is .is_execute_when_end_frame_issued when true and .p_command_list fields are true and nullptr (or it might not nullptr it depends, but still it is main thing is when .is_execute_when_end_frame_issued==true) respectively, so according to this it fits for multithreaded variant where user just push whole UI to different thread, we have probably dedicated queue from GAPI and we push commands and it executes separately to main render but I don't know maybe it is too much rn I just want to see at least half of my work will be merged and then we can talk further otherwise we could stuck for a long time with such timings...
So I hope you doing well, happy holidays Best regards |
|
That's great! Glad to hear that, and thanks for the continued work on this one. I'm a bit backlogged right now, but I'll come around to this eventually. I might make some changes or additions (like the other backends) as I see fit, and then I'll likely just merge it. It's been some time in progress, and I agree that it's better to have something merged in sooner, rather then discussing minor details, and then everyone can help improve it over time. Cheers! |
|
Hey, I've been working to get this one ready to merge lately. First of all, nice job getting so many of the effects working. I have pushed my changes now. To summarize quickly:
I found two issues that I consider merge-blocking:
Here is the full assertion and backtrace:
Could you take a look at these two issues? |
|
@mikke89 hi thanks for testing I will check |
|
@mikke89 I fixed the issues 😊 p.s. sorry for a long pause 😅 😅 |
|
That's really awesome, thanks! Good work :) I'll take one final glance over it and then expect to merge it soon. |
Co-authored-by: Michael Ragazzon <michael.ragazzon@gmail.com>
|
Great stuff, it seems to be working well in my testing. I know this was a lot of effort, so thanks again! |





Introduction
So it is a new backend from me and it's DirectX 12 for Microsoft NT platforms (>=Windows10).
The library comes to a stage that it is needed to provide more user friendly interaction between end user and library, what relates to rendering backend is a simple and naive integration like in the popular library ImGui by Ocornut where user already has an integrated backend and they need to pass an initialization structure with all required parameters.
Well I will describe more detailed this chapter but for now I just leave this message in a such poor state, because for now it is a reminder for me to finish this work and move to Vulkan implementation.
Backend architecture
I set a straight aim to provide an efficient and optimized resource management and provide to user the low level settings of resource allocation. I mean that backend strives for being simple from user's side (in terms of how to set things up) and from another side it optimizes in all ways possible.
Buffer management aka geometry
About buffer management the library allocates one buffer and utilizes all its space the offset calculation is based on this offset allocator implementation by sebbbi (see sources, MIT license).
But if buffer is out of memory or offset allocator can't allocate the required space it will allocate a new buffer.
And rendering system tries to deallocate the buffer if it is not used. (in optimized way of course)
For now I tested with a really small sized buffers like less 1Kb and it was stable, of course it is not optimal settings due to high allocation of buffers and defragmentation of GPU's memory. By default I picked an optimal size that for most cases should be enough for using, but as I stated earlier user has a right to change that allocation size on initialization stage.
Texture management
DirectX 12 has two types of allocation of textures.
First one is to send texture to GPU as a single buffer, like if it is 4 Mbs then we send 4 Mbs as one single buffer. It is straight and simple and generally used among all modern GAPIs.
But there's a second variant for allocating textures aka placement PlacedResource (see link). That means you can allocate multiple textures into one buffer like you allocate one big buffer and suballocate space for textures thus it is like you "placed" those textures into one buffer. But that works only for not big sized textures that less some specific size, because in official tests it won't give any profit from performance, but still useful for rational resource management.
So based on texture size and its calculation the system tries to choose best way for uploading it.
As a conclusion I tried to make backend that utilizes all memory efficiently as much as possible and make all possible to fill all buffers and buffers for textures in most optimal way that user doesn't need even think about it at all.
Warning
Since we formally force people to use our backend then I don't see any motivation to help to people who make own backends based on our implementations and there's a vital reason for this, because it is better to have "one" (not in directly meaning) backend that be tested on many devices across different OSes than having some custom backends that can't be well tested at all. It is important warning for all users who have tendency to make own wheel ignoring obvious sane way of living.
I need to say that I tried to keep things without breaking changes at all, like if you want to initialize RmlUI as you did before you just don't pass an init structure and from function calling it is just a default argument that you don't specify thus nothing is changed/broken.
But we will (at least me) accept all incoming that relate to backend and it's working state on different graphics cards and OSes for @mikke89 I suggest to create template issue that has the following form (just as an idea):
If you have trouble with rendering backend
Specify GAPI
Blah 12
Specify graphics card's driver
Blah 12.451.16 oct 2023
Specify graphics card
Blah RTX 24000
Specify OS
Blah 13
Specify version and commit of RmlIUI
6.0 d23a3da
I guess after finishing "modern" backends we will boost for some time the popularity of library but at the same time amount of running tests and examples would be high since everyone can easily integrate the library... :D
For now I am making support for multisampling feature and I hope I will finish this work till August.