-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[rlgl] Add Software Rendering Support #4832
base: master
Are you sure you want to change the base?
Conversation
Here is a quick video example of some of the current capabilities. Note that the texture is rendered with bilinear filtering, although I agree that it may not be noticeable as it is. simplescreenrecorder-2025-03-12_01.34.46.mp4 |
This repeats a lot of code that RLGL does for you. RLGL already handles the OpenGL 1 like abstraction and the marxies, so I think it would make more sense to build from the modern opengl setup and render everything in |
Additionally, I chose to implement triangle rasterization using the scanline method rather than barycentric interpolation method. Barycentric interpolation can be much more efficient when implemented with SIMD optimizations and is also conceptually simpler (excluding SIMD considerations). However, I assumed that the goal here is not to achieve maximum performance on a modern desktop CPU but rather to enable raylib to run on platforms that lack OpenGL support, including software rendering. When properly implemented, the scanline method requires fewer computational resources and offers better cache locality, making it a more suitable choice for embedded systems and older hardware. |
@ColleagueRiley Yes, I know, and it is a deliberate choice for several reasons. The first is that we will be able to integrate its implementation directly into the parts currently dedicated only to OpenGL 1.1 only, we can therefore also be sure that it does not generate any duplication during the build. The second reason is that if we ever want to implement lighting support, the current matrix stack system in |
@Bigfoot71 I'm also not sure if the dedicated legacy OpenGL backend makes sense, when it can easily be integrated. |
@ColleagueRiley Do you also want to add shader support? Reinvent the Mesa driver for raylib? No one is going to do that. Unless we use function pointers or something like that, but it's simply not compatible. Anyway, the goal of this implementation is to enable raylib to run on machines that don't even have a software driver for OpenGL. We’re not going to implement what hasn’t been done for these machines ourselves, it would be absurd to think that. |
I meant it can be integrated into the modern API. I think it's more important RLGL is refactored to support additional backends. Not only to make this software rendering backend, but it would help users that want to add custom support for the native graphics API. |
TL;DR: Adding support for modern APIs will require abandoning OpenGL, and in such a case, adding software rendering support for platforms that don't support OpenGL would then be strange. Ah, excuse me, I see, but there would be several problems with that. Relying on the batch system would just be a waste of memory in this specific case, unless we attempt very specific software rendering architectures, which wouldn't necessarily be relevant for the target devices (where memory is limited). There is also the issue of the current management of matrices by rlgl, as I mentioned before. Moreover, this could become increasingly confusing as to which features are implemented or not for each backend. Also, I’ve reconsidered what you said about refactoring rlgl to allow the implementation of different backends. If this is with the intention of implementing Vulkan, Metal, and D3D12, there will be many other problems that will arise, such as, for example, one of the most obvious that comes in my mind: VAOs. Not to mention the specific implementations that will be needed to support OpenGL in order to align with the principles of immutable pipelines. Implementing a modern API in the right way will require sacrifices for older APIs. And here we are talking about a hypothesis that, if chosen, will take a very long time to materialize. I am ultimately quite skeptical. Favoring modern APIs, neglecting OpenGL, but still adding support for software rendering seems like a somewhat obscure decision. Anyway, this restructuring is a good idea in itself, but if it's done, then I find adding software rendering support quite strange. In any case, I am just offering a potential solution to Raysan's long-standing request; it's up to him to decide. |
@Bigfoot71 It wouldn't require too many changes, it would be more of a reorganization effort. I don't think there's much benefit for direct support for DirectX or Vulkan, but it would be nice to be able to support native APIs. This would not involve neglecting Legacy OpenGL, it could be implemented like any other custom backend. As for memory usage, I'm unsure how much that would be a problem. |
@ColleagueRiley There will inevitably be a choice to make regarding which APIs to prioritize. For example, with Metal, if OpenGL is favored, it requires some consideration because if the Metal support is poorly suited to work with OpenGL, then wouldn’t ANGLE already do a better job? Creating a low-level abstraction for multiple APIs, including both old and modern ones, will inevitably mean neglecting some, that's unavoidable... So, my opinion is that:
|
The purpose of software rendering is to support certain platforms that do not support OpenGL, but many of those platforms would support GPU rendering via alternative APIs, for example, the Wii has its graphics API. I don't think RLGL should be implementing these, but making it easier for users to implement their own would be nice. |
Mhh I see, but creating a basic API that we can only judge as being adapted to the maximum number of possible APIs, including proprietary ones, seems like a bad idea to me. |
@Bigfoot71 thank you very much for this fantastic header! From my point of view, simplicity is key. Being able to support some basic software rendering option for platforms with no OpenGL driver support with minimal changes in current raylib implementation seems a very valid reason to me. I specially have in mind the upcoming range of RISC-V micro-computers we will be probably seeing soon in the market (most of them with no GPU). About the implementation into raylib, I see most #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
#define RLSW_IMPL
#include <rlsw.h> // OpenGL 1.1 software implementation
#define GRAPHICS_API_OPENGL_11
#endif Beside that, some platform layer (RGFW?) should support a raw color framebuffer initialization and screen blitting. |
Okay, no problem, I'll continue writing right away. Since some features of GL 1.1 related to certain function parameters are not used in RLGL, I will simply write a binding using macros and omit these parameters. I will also create macros to generate functions based on rendering parameters and add line rasterization and blend modes. This could go very fast! |
This doesn't make much sense to me. If you're going to add a software rendering backend, then it makes more sense to implement it as a custom backend rather than working around the legacy OpenGL framework, as it could be more performant and useful. If you do not care about the performance, the legacy OpenGL backend can already easily do software rendering. Without any modifications to Raylib at all. If you wanted to ensure it's rendering to a pixel buffer, you could use OSMesa. That wouldn't require any changes to RLGL. |
@ColleagueRiley Explain why not implementing it through legacy OpenGL would be more efficient? Especially if you say "more useful", then what features would be missing? Knowing that any added feature will inevitably make it less efficient in some way.
We have already talked about it: #3928 (comment) And this may not be suitable for all scenarios considered by Ray. |
No matter what, what we are doing here is not incompatible with your suggestion to refactor rlgl in the future. And even if we implement the possibility of using different backends with rlgl, it does not solve the problem that each platform requires specific implementations for copying color buffers to the screen. |
@Bigfoot71 Just to clarify some points in case I missed something:
Thanks for your answers! 😄 |
Also replace the triangle rasterization functions with macros that generate specific functions for each state of the rendering system. Also, add the OpenGL definitions in order to add a binding for rlgl.
Yes
Yes
Not exactly, I made a few simplifications compared to what rlgl does with GL 1.1, but I will add a binding corresponding to the OpenGL API that handles these small differences. It will be done through macros, and there will be no overhead.
That's exactly it, in the end, nothing more should be necessary.
Exactly!
It’s a bit early to say, the first video I presented wasn’t optimized yet. I just added function generation based on the state (whether textures are being sampled or not, depth test on or off, etc.) With this new addition, I now see the counter going above 2000 FPS for the same example, so I would say an improvement of around ~x1.5 roughly. But it’s still early, other optimizations are still possible, and I will do thorough testing at the end.
Contained in a single header with the same functionalities, no, at least not to my knowledge. Edit: Aside from PortableGL, but it's different, as it aims to support more modern versions of OpenGL. |
OpenGL 1.0 will already use software rendering by default if there is no GPU. I don't think this is fixing a clearly defined problem. |
This depends on the platform and the driver See for example:
|
@ColleagueRiley Actually it will allow this:
For me this is the most notable achievement and step towards the future. Afaik, no other alternative allows that, at least in a simple way. Note that this will expand raylib to low-level embedded devices and microcontrollers. |
Ok, I think I've said most of what I want to say on this. I can help with the platform part. |
+ tweak function names
src/external/rlsw.h
Outdated
*color = SW_MALLOC(SW_COLOR_PIXEL_SIZE * size); | ||
*depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE * size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Is there anything from the Image API we could use here? The Image API does support different pixel formats, along with creating the image itself. Likely not completely needed, but an interesting idea that may save some duplicate code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably could, but the main issue is that rlsw.h
is in external
and shouldn't depend on raylib itself, unless you have something else in mind?
Also, raylib does not support the RGB 8-bit format or normalized integer formats other than 8-bit for grayscale, trough the Image API (I mean for one component 16/24 bit, 16 is half floating point).
However, this would indeed limit it for the sw_get_pixel_xxx
functions.
- `glDepthMask` - `glColorMask`
The support for the scissor test has been implemented for clearing as well as for triangle clipping. The implementation for lines and points is still missing. I also removed the 2D clipping of lines that used the Cohen-Sutherland algorithm, opting instead to always use the Liang-Barsky algorithm in all cases. This simplifies the implementation, and the 2D version would have caused issues when interpolating vertices in the future if we want to implement additional features.
@raysan5 I just completed the binding for OpenGL 1.1 and its integration into rlgl. So the work for raylib can now begin. Here’s a very quick example using rlgl, nothing impressive, I'm using SDL2: simplescreenrecorder-2025-03-22_20.01.58.mp4I currently had to add two functions to rlgl: #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
RLAPI void rlCopyFramebuffer(int x, int y, int w, int h, int format, void* pixels);
RLAPI void rlResizeFramebuffer(int width, int height);
#endif The first function, We could do something similar with The second function, |
@Bigfoot71 Thanks for the update! I'll try to do some tests as soon as possible! |
First of all, I forgot to mention something: Currently, texture creation does not copy the data; it only retains the pointers passed to Without thinking too much about it, I initially assumed that copying this data was unnecessary in this context. However, raylib obviously deletes the loaded data from RAM after the upload. Therefore, we should copy the data each time, but this could be problematic in terms of time and performance in the long run for certain devices. |
This issue has been resolved. I added a As a result, the data will also be freed or not when the texture is deleted. I also added a However, this currently relies on a kind of hack. Since union {
const void* cptr; //< NOTE: Is used for all data reads
void* ptr; //< WARNING: Should only be used to allocate and free data
} pixels; In C, AFAIK, as long as we do not write via In the code, The only cases where We can always remove this hack if we decide that the data will always be copied, but this might be annoying in some contexts... |
This PR aims to work on the integration of software rendering support into
rlgl
, through an external headerrlsw.h
I give to @raysan5 the privilege of choosing the name if
rlsw
doesn't suit him.Additionally, the header is delivered with an MIT license in my name. I also leave the choice of the license to @raysan5. Consider this project as a donation to the community; I will not redistribute it on my side.
This header
rlsw.h
is intended to provide, for now, all the functionalities offered byrlgl.h
for OpenGL 1.1.Currently, rendering is performed to a framebuffer that supports multiple formats, selectable at compile time:
Color Buffer:
Depth Buffer:
For the rest, I'll leave you to check the checklist.
If you notice any features that should be implemented and are missing from the checklist, please mention them or edit the post if you can.
note: This PR currently only contains the header. I will work on the integration once the checklist is complete and a decision has been made on how to integrate it into raylib.
Feature Checklist
Clipping
Rendering
Texture Support
Vertex Arrays
Misc