Closed ypujante closed 1 year ago
As I am investigating further the issue, I realized that you actually don't even need a texture.
Using this simple code will show the issue:
int main()
{
InitWindow(800, 450, "raylib issue");
constexpr Color ImGuiCol_FrameBg{40, 73, 122, 137};
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle(10, 10, 100, 100, ImGuiCol_FrameBg);
EndDrawing();
}
CloseWindow();
return 0;
}
I am using the color that ImGui uses for their dark style frame background.
Using OpenGL ES 2.0 / Google Angle you get this:
Using OpenGL / Native you get this (which is the "right" blue color for ImGui with a black background)
Does the same happen if you set a BlendMode? E.g.:
BeginDrawing();
ClearBackground(BLACK);
BeginBlendMode(0);
DrawRectangle(10, 10, 100, 100, ImGuiCol_FrameBg);
EndBlendMode();
EndDrawing();
@ubkp no it does not help
It looks like it is a RGB vs sRGB buffer issue vs blending issue, according to this thread
I have no idea how to fix it yet
@ypujante Please, could you try using different alpha premultiply modes:
BeginDrawing();
ClearBackground(BLACK);
BeginBlendMode(BLEND_MULTIPLIED); // Try also with BLEND_ALPHA_PREMULTIPLY
DrawRectangle(10, 10, 100, 100, ImGuiCol_FrameBg);
EndBlendMode();
EndDrawing();
@ypujante Just finished reading the thread you shared and it seems the issue could be in the ANGLE implementation, did you try building it for web platform? It is supposed to also use ANGLE backend, despite the WebGL implementation, just to confirm the isssue is due to ANGLE. Also, it should be tested in other platforms using OpenGL ES but with custom implementation (not GLFW), like Android or Raspberry Pi.
@raysan5 I did try with pretty much every blend mode possible and none of them fixed the problem
In my latest attempts (described in the thread you mention) I did have a (minor) breakthrough if I add the hint glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL);
prior to calling InitWindow
: the blending/color issue is completely gone.
But now there is a frame rate issue which, even in unlocked mode, seems to be very low (but not below 60fps). Something seems very odd (if it was purely a performance issue why would it stick to 60fps...)
I am going to research this further as I just encountered this problem.
But if that leads nowhere then I will go down one more layer into the Angle library. I have been trying to avoid this as much as possible since it requires installing build tools and others to compile (not a cmake based library...) and it is pretty daunting...
In my latest attempts (described in the thread you mention) I did have a (minor) breakthrough if I add the hint
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL);
prior to calling InitWindow: the blending/color issue is completely gone.
Yeah, I read it, that's very strange... but actually, it's stranger the severe performance hit!
But if that leads nowhere then I will go down one more layer into the Angle library. I have been trying to avoid this as much as possible since it requires installing build tools and others to compile (not a cmake based library...) and it is pretty daunting...
Please, don't... it could be a real rabbit-hole of despair, I think it does not worth it...
@raysan5 I did go down the rabbit hole :) Compiling the library is actually not that bad. It is the debugging/stepping in the code that is a challenge... until I figured out how to do it with CLion...
That being said, after many hours of more investigating, I have compiled my findings which I have posted on the Angle forum.
I don't have a solution yet, but still investigating. If you feel like you want to close this issue, it's ok. I will still post what I come with either way. At this stage I am pretty convinced that it is not a raylib issue.
Finding the glfw hint really was the significant breakthrough that forced using metal and was clearly completely misunderstood by the article I had read on how to use Angle on macOS (the internet....), since they were using the non metal implementation in the end (which is really fast but renders wrong with alpha).
@ypujante Wow! Thanks for reporting! I'll keep this issue open for a while, I'm curious about the cause of this issue.
Also, did you try this example with a Rapsberry Pi for PLATFORM_DRM
? It uses OpenGL ES 2.0 and afaik, the Mesa provided implementation for it, probably differs from ANGLE implementation...
I would also recommend giving a try to PLATFORM_WEB
, it uses the browser WebGL implementation, that internally translates to ANGLE, but I don't know what configuration uses (set by the browser).
No I did not get a chance to try with a different platform. How would I go about trying PLATFORM_WEB
? Can you point me to an example that would use this?
Actually it's quite straight forward, raylib examples Makefile comes preconfigured for it, but I can try it right now because I already have the environment setup (basically, emscripten installed).
In any case, here some detailed instructions: https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)
Results in some minutes...
Here the result!
Specs:
@ypujante Just updated the previous comment, for some reason yesterday I was not able to uplaod images...
It seems that WebGL backend results are the expected ones (afaik, internally it uses ANGLE, most probably mapped to Direct3D).
@raysan5 Thank you very much for running this test. And of course the result is exactly as expected with the proper color...
I am getting more and more convinced that there is an issue with Angle when using the gl/clg renderer (ANGLE (Apple, Apple M2 Max, OpenGL 4.1 Metal - 83.1)).
I think I reached a conclusion for my project which I will share soon after I collect more data.
In order to get a feeling of what is really going on with the various limitations I ran a benchmark on my machine.
The benchmark code is the following:
// glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL);
std::random_device rd; // a seed source for the random number engine
std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> random_color(64, 255);
std::uniform_int_distribution<> random_x(0, 700);
std::uniform_int_distribution<> random_y(0, 350);
constexpr int N = 100000;
InitWindow(800, 450, "raylib issue");
// SetTargetFPS(60);
glfwSwapInterval(0);
long frame = 0;
auto begin = std::chrono::steady_clock::now();
while(!WindowShouldClose())
{
frame++;
BeginDrawing();
ClearBackground(BLACK);
for(int i = 0; i < N; i++)
DrawRectangle(random_x(gen), random_y(gen), 100, 100, Color{(unsigned char) random_color(gen), (unsigned char) random_color(gen), (unsigned char) random_color(gen), 255});
DrawFPS(190, 300);
EndDrawing();
}
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - begin).count();
std::cout << frame << " frames in " << elapsed <<" seconds => " << static_cast<float>(frame) / static_cast<float>(elapsed) << " fps\n";
CloseWindow();
return 0;
The 3 "platforms" for the benchmark are
ANGLE (Apple, ANGLE Metal Renderer: Apple M2 Max, Version 13.4 (Build 22F2073))
and is achieved by compiling with option OPENGL_VERSION "ES 2.0"
, linking with Google Angle and uncommenting glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL)
ANGLE (Apple, Apple M2 Max, OpenGL 4.1 Metal - 83.1)
and is achieved by compiling with option OPENGL_VERSION "ES 2.0"
, linking with Google Angle (no glfw hint!)Apple M2 Max
and is achieved by compiling/linking without Google Angle (uses deprecated native macOS OpenGL api)This test displays enough triangles that we are well below the 60fps threshold.
Platform | CPU | GPU | FPS |
---|---|---|---|
Angle / Metal | 100% | 80% | 27.5 |
Angle / OpenGL | 100% | 98% | 26.5 |
Native OpenGL | 90% | 93% | 27.5 |
This test displays less triangles and as a result can achieve more than 60fps.
Platform | CPU | GPU | FPS |
---|---|---|---|
Angle / Metal | 23% | 45% | 62.5 |
Angle / OpenGL | 100% | 98% | 258.4 |
Native OpenGL | 60% | 96% | 174.2 |
Same test as the previous one but with a 60fps cap.
Platform | CPU | GPU | FPS |
---|---|---|---|
Angle / Metal | 35% | 18% | 59.8 |
Angle / OpenGL | 35% | 56% | 58.5 |
Native OpenGL | 33% | 38% | 59.2 |
This was run on my machine so take everything I say with a grain of salt as numbers may vary if run on a different machine of course. It seems that Angle / Metal overall is the winner. Yes there is a clear limitation when uncapped, but I am not entirely sure of the value of running uncapped, certainly not in my use case... It is definitely a hassle to use it though as I found out for example that the libraries I embedded in the test zip file, end up triggering security warnings when I tried on a different machine (had to go into the macOS settings/privacy and security panel to authorize the library to be used... PITA...). I am sure if they were properly code signed that might be less of an issue... Clearly using straight OpenGL is a good alternative. My real worry is that OpenGL is deprecated on macOS so one day or another Apple will yank support entirely at which point, using something like Angle might become a necessity...
@ypujante @raysan5 Sorry I'm a bit late, but I managed to replicate the issue on PLATFORM_WEB
on Firefox (102.11.0esr 64-bit) and Chrome (114.0.5735.106 64-bit) for Linux (Linux Mint 21.1 x86_64). I'm not very good at render, but I'll take a look at it.
Minimal reproduction code to test the issue:
cd raylib/src/
make PLATFORM=PLATFORM_WEB -B
And:
#include "raylib.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
void Update(void) {
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle(10, 10, 100, 100, (Color) {40, 73, 122, 137});
EndDrawing();
}
int main(void) {
InitWindow(400, 400, "Test");
#if defined(PLATFORM_WEB)
emscripten_set_main_loop(Update, 0, 1);
#else
SetTargetFPS(60);
while (!WindowShouldClose()) Update();
#endif
CloseWindow();
return 0;
}
@ubkp Thank you so much for taking the time to test this out and reproducing the problem! I will add your findings to my thread on the Angle forum. It is clearly an issue with Angle...
@ypujante No problem :)
Some updates:
GLFW_SRGB_CAPABLE
, OpenGL ES2 will enforce it.raylib/rlgl.h
. I could be wrong, but it appears that rlgl.h
calculations are using linear colorspace, but sRGB uses non-linear colorspace, thus the issue.fragColor.a
. Under my testing texelColor
is correct, colDiffuse
is correct, and fragColor.rgb
is also correct. The only "wrong" value is fragColor.a
which is exactly the alpha.fragColor.a
forced to 1.0
(it gets all colors correctly):pow()
method but didn't work (more likely, I didn't understand how to apply it correctly). I'll continue working on it.Edit 1: it's working correctly. I was missing the rlgl.h
in my include dir. Check this and this comments.
Edit 2: the issue, for me, was actually on the shell.html
file. Check this comment.
@ubkp Note that on my test, it worked as expected for PLATFORM_WEB
under Windows 10 and Chrome browser.
@raysan5 Asked a friend here to test it on his machine (Windows 11 with Chrome), he was able to reproduce the issue:
If you can, could you please check if this:
#include "raylib.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
void Update(void) {
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle( 10, 10, 100, 100, (Color) { 40, 73, 122, 137});
DrawRectangle( 10, 120, 100, 100, (Color) { 40, 73, 122, 255});
EndDrawing();
}
int main(void) {
InitWindow(450, 450, "Test");
#if defined(PLATFORM_WEB)
emscripten_set_main_loop(Update, 0, 1);
#else
SetTargetFPS(60);
while (!WindowShouldClose()) Update();
#endif
CloseWindow();
return 0;
}
Outputs this:
@ubkp Re-testing again, that's what I got:
I'm using latest raylib from github master branch, please, could you check the console output in case of Windows 11 test?
EDIT: Tested code:
#include "raylib.h"
int main(void)
{
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib alpha test");
SetTargetFPS(60);
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle(10, 10, 100, 100, (Color){ 40, 73, 122, 137});
DrawRectangle(10, 120, 100, 100, (Color){ 40, 73, 122, 255});
EndDrawing();
}
CloseWindow();
return 0;
}
Compilation line:
emcc -o alpha_test.html alpha_test.c -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result -O3 -I. -I../src -I../src/external -L. -L../src -L../src -s USE_GLFW=3 -s TOTAL_MEMORY=134217728 -s FORCE_FILESYSTEM=1 -s ASYNCIFY --shell-file ../src/minshell.html ../src/libraylib.a -DPLATFORM_WEB
@raysan5 Thank you very much for testing!
Recompiled it with the latest master branch (6094869) using exactly your code and your compilation line. And the results are exactly like yours on both Linux:
And Windows:
However, if I compile using these instructions (With ASYNCIFY) from the wiki:
emcc -o game.html game.c -Os -Wall ./path-to/libraylib.a -I. -Ipath-to-raylib-h -L. -Lpath-to-libraylib-a -s USE_GLFW=3 -s ASYNCIFY --shell-file path-to/shell.html -DPLATFORM_WEB
That, in this case, become:
emcc -o alpha_test.html alpha_test.c -Os -Wall ./libRayHtml5/libraylib.a -I. -IincRay -L. -LlibRayHtml5 -s USE_GLFW=3 -s ASYNCIFY --shell-file ./shell.html -DPLATFORM_WEB
I get the results I was having:
So now I'm completely lost. Are the instructions from the wiki no longer valid? Am I doing something wrong?
Edit 1: it was my fault. I was missing the rlgl.h
on the include dir. Don't know how I didn't see that (actually don't know how that was even working). I'm so sorry. My apologies.
Edit 2: the issue, for me, was actually on the shell.html
file. Check this comment.
@ypujante Just to be sure, could you please check if you're not missing the rlgl.h
header in your include directory like I did?
@ubkp Sorry, I don't understand, rlgl.h
is not required to be in include directory, it's already linked into raylib. The library is only provided in case advance users want to use it independently of raylib. It's a single-file header-only OpenGL abstraction layer... but note that it's already linked into raylib by default, you can see it here.
@raysan5 You're absolutely right. rlgl.h
had nothing to do with it. The problem was in the shell.html
file.
If you comment or remove the background-color: black;
from the canvas.emscripten
CSS on the shell.html
file (minshell.html
L35 or shell.html
L78), it will cause that. On mine, I had that CSS property removed.
Full test:
~$ ls
alpha_test.c raylib-master.zip
~$ cat alpha_test.c
#include "raylib.h"
int main(void)
{
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib alpha test");
SetTargetFPS(60);
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(BLACK);
DrawRectangle(10, 10, 100, 100, (Color){ 40, 73, 122, 137});
DrawRectangle(10, 120, 100, 100, (Color){ 40, 73, 122, 255});
EndDrawing();
}
CloseWindow();
return 0;
}
~$ mkdir test/
~$ mkdir test/include/ test/library/
~$ cp alpha_test.c test/
~$ cp raylib-master.zip test/
~$ cd test/
~/test$ ls
alpha_test.c include library raylib-master.zip
~/test$ unzip raylib-master.zip
[...]
~/test$ cd raylib-master/src/
~/test/raylib-master/src$ make PLATFORM=PLATFORM_WEB -B
[...]
raylib library generated (libraylib.a)!
~/test/raylib-master/src$ cp libraylib.a ../../library/
~/test/raylib-master/src$ cp raylib.h ../../include/
~/test/raylib-master/src$ cp minshell.html ../../
~/test/raylib-master/src$ cd ../../
~/test$ ls include/ library/
include/:
raylib.h
library/:
libraylib.a
~/test$ ls
alpha_test.c include library minshell.html raylib-master raylib-master.zip
~/test$ emcc -o alpha_test.html alpha_test.c -Os -Wall ./library/libraylib.a -I. -Iinclude -L. -Llibrary -s USE_GLFW=3 -s ASYNCIFY --shell-file ./minshell.html -DPLATFORM_WEB
~/test$ ls
alpha_test.c alpha_test.js include minshell.html raylib-master.zip
alpha_test.html alpha_test.wasm library raylib-master
~/test$ busybox httpd -f -p 8000
Output:
Now, removing the background: black;
from the shell file:
~/test$ cp minshell.html minshell.html.original
~/test$ vim minshell.html
~/test$ diff minshell.html.original minshell.html
35c35
< canvas.emscripten { border: 0px none; background-color: black; }
---
> canvas.emscripten { border: 0px none; }
~/test$ emcc -o alpha_test.html alpha_test.c -Os -Wall ./library/libraylib.a -I. -Iinclude -L. -Llibrary -s USE_GLFW=3 -s ASYNCIFY --shell-file ./minshell.html -DPLATFORM_WEB
~/test$ ls
alpha_test.c alpha_test.js include minshell.html raylib-master
alpha_test.html alpha_test.wasm library minshell.html.original raylib-master.zip
~/test$ busybox httpd -f -p 8000
Output:
I'm really sorry, I didn't know that the CSS background on canvas.emscripten
was required. I assumed that the ClearBackground(BLACK);
was enough.
Edit: typos.
@ubkp Wow! That's a good catch! Sincerely, I can't remember when it was added or how it affected rendering!!! So... it seem it mixes with the framebuffer color.... interesting... 🤔
I think there is something there... clearly there is a way to reproduce it outside the macOS / Angle platform!
@ypujante could it be that ANGLE implementation is intended to work on web and already considers the canvas
background for the blending but when running on desktop there is no canvas
and the resulting framebuffer is not blended properly?
@ypujante @raysan5 I think I found an explanation for this issue and three possible solutions. This answer on StackOverflow explains it perfectly. To sum it up: the transparency problem is due to compositing. The possible solutions, as mentioned on that answer, are:
const gl = canvas.getContext("webgl", { alpha: false });
to the canvas function on the shell file, so it looks like this:
[...]
<body>
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
<p id="output" />
<script>
var Module = {
print: (function() {
var element = document.getElementById('output');
if (element) element.value = ''; // clear browser cache
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
if (element) {
element.value += text + "\n";
element.scrollTop = element.scrollHeight; // focus on bottom
}
};
})(),
canvas: (function() {
var canvas = document.getElementById('canvas');
const gl = canvas.getContext("webgl", { alpha: false });
return canvas;
})()
};
</script>
{{{ SCRIPT }}}
</body>
[...]
rlgl.h
.IMHO, the third option is the best because:
ClearBackground()
to be the one that sets the actual color being used for the background, so that value can be directly or dynamically defined by the user inside the program/game without having to either change the "fixed" CSS value on the shell file or having to workaround by calling emscripten macros;ClearBackground()
.I'll send a PR with the proposed changes shortly.
@raysan5 I do not know exactly, but the last answer on the Google Angle forum stated this:
Window surfaces on MacOS are all emulated by ANGLE, they don't see a ton of usage because many of our integrators just use ANGLE to render into IOSurfaces and then composite themselves.
- It looks like frame pacing is not implemented in our CGL backend. That's why you're seeing 17k fps.
- I'm not sure how blending is determined when compositing.
- Apart from the erratic values you see, it sounds like displaySyncEnabled is doing what it's supposed to but not blocking in the eglSwapBuffers call because the first time the drawable is used in each frame is for the clear.
I'd recommend doing window management and compositing yourself on MacOS. Grab an IOSurface out of the window and make an EGLImage with it so ANGLE can render into it.
At the end of the day, due to my earlier benchmark and @ubkp tests, depending on the platform, the recommended approaches are the following:
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL);
canvas.emscripten { border: 0px none; background-color: black; }
After @raysan5 reply on the PR, I realized I'm on a totally wrong approach here. I was trying to work around the issue, instead of fixing the actual problem: why OpenGL ES 2.0
is not considering the ClearBackground()
color when there's compositing. I'll focus on trying to solve that.
@ypujante @ubkp sincerely, I think at this point the best solution is just leave it as it is. The problem has been tracked down and it seems to be beyond OpenGL, but on the underlaying composite system of the target platform, that's far beyond raylib domain.
The current solutions should work for any user:
glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, GLFW_ANGLE_PLATFORM_TYPE_METAL);
canvas.emscripten { border: 0px none; background-color: black; }
Maybe document it in the Wiki (despite this issue is already a good documentation) for users facing this same problem.
@ubkp As per the Google Angle forum description, problem seems to be on EGL configuration. This issue should be tested on PLATFORM_DRM
on Raspberry Pi, that uses OpenGL ES 2.0 native (no ANGLE layer) and configs the composite manually through EGL and KMS libs. If it works there (that I presume it will work as expected) then not much can be done on raylib side.
Thank you very much you both for the deep investigation on this issue!
Waiting for your comments to definitely close this issue.
@raysan5 Yeah, I think you're right. At the moment I can't find a definitive solution for it. I'm still puzzled about why ClearBackground()
is not being considered on OpenGL ES 2.0
, but that may be out of my reach for the time being.
On my part, feel free to close the issue. If I come across any new information about it, I'll post an update.
Yes I agree it's time to move on which is what I implied when I commented with the benchmark
@ypujante @ubkp thanks again for all your time and effort tracking this issue! Closing it! 😄
This is a follow up to my post on discord
The issue on macOS is that when using OpenGL ES 2.0 (implemented by Google Angle on macOS), then the texture is rendered with a "halo" where there is transparency:
When using the macOS (deprecated) built-in OpenGL there is no such issue:
The code is very simple and does not do anything fancy:
I am attaching a fully self-contained project which demonstrates the issue. It contains raylib 4.5 (but note that I did try with the latest version on master with the same outcome). There is a README with instructions on how to build and run:
Build
It is a CMake project, so it builds very simply:
The option
USE_GOOGLE_ANGLE_MAC_OS
isON
by default which demonstrates the problem. If you set toOFF
(make sure to delete the build dir and redo all the steps), it won't use OpenGL ES 2.0/Google Angle at which point there is no issue.Run
build/raylib_issue.app/Content/MacOS/raylib_issue
(to see the log in the output)Note that I do not know if it is a raylib issue, a GLFW issue, a Google Angle issue or anything else. The Google Angle libraries are coming from Google Chrome (I did not build them), so I assume they should be working quite well...
raylib-issue.zip