[drape][gl_functions]: Handle ES2 scenario on Linux #2947
Labels
No labels
Accessibility
Accessibility
Address
Address
Android
Android
Android Auto
Android Auto
Android Automotive (AAOS)
Android Automotive (AAOS)
API
API
AppGallery
AppGallery
AppStore
AppStore
Battery and Performance
Battery and Performance
Blocker
Blocker
Bookmarks and Tracks
Bookmarks and Tracks
Borders
Borders
Bug
Bug
Build
Build
CarPlay
CarPlay
Classificator
Classificator
Community
Community
Core
Core
CrashReports
CrashReports
Cycling
Cycling
Desktop
Desktop
DevEx
DevEx
DevOps
DevOps
dev_sandbox
dev_sandbox
Directions
Directions
Documentation
Documentation
Downloader
Downloader
Drape
Drape
Driving
Driving
Duplicate
Duplicate
Editor
Editor
Elevation
Elevation
Enhancement
Enhancement
Epic
Epic
External Map Datasets
External Map Datasets
F-Droid
F-Droid
Fonts
Fonts
Frequently User Reported
Frequently User Reported
Fund
Fund
Generator
Generator
Good first issue
Good first issue
Google Play
Google Play
GPS
GPS
GSoC
GSoC
iCloud
iCloud
Icons
Icons
iOS
iOS
Legal
Legal
Linux Desktop
Linux Desktop
Linux packaging
Linux packaging
Linux Phone
Linux Phone
Mac OS
Mac OS
Map Data
Map Data
Metro
Metro
Navigation
Navigation
Need Feedback
Need Feedback
Night Mode
Night Mode
NLnet 2024-06-281
NLnet 2024-06-281
No Feature Parity
No Feature Parity
Opening Hours
Opening Hours
Outdoors
Outdoors
POI Info
POI Info
Privacy
Privacy
Public Transport
Public Transport
Raw Idea
Raw Idea
Refactoring
Refactoring
Regional
Regional
Regression
Regression
Releases
Releases
RoboTest
RoboTest
Route Planning
Route Planning
Routing
Routing
Ruler
Ruler
Search
Search
Security
Security
Styles
Styles
Tests
Tests
Track Recording
Track Recording
Translations
Translations
TTS
TTS
UI
UI
UX
UX
Walk Navigation
Walk Navigation
Watches
Watches
Web
Web
Wikipedia
Wikipedia
Windows
Windows
Won't fix
Won't fix
World Map
World Map
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference: organicmaps/organicmaps-tmp#2947
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "wayland_es2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes flathub/app.organicmaps.desktop#1 for devices that are only capable of ES2.
Signed-off-by: Ferenc Géczi ferenc.gm@gmail.com
Let's discuss and test this a bit before potentially merging.
It means that after merging this PR the build will fail on any Linux without EGL installed/available. That's not good for contributors.
C++17 supports this:
We can use it for autodetection of the installed GL version for all platforms.
And without this there is no linking to EGL:
gl_functions.cpp:260: error: undefined reference to 'eglGetProcAddress'
And how would you go about ensuring that the code in this block doesn't suddenly turn into completely dead code, in case the CI environment stops providing that header?
Oh you mean that this should be conditional? I will look into that.
#error
?But here you said that the code should build for contributors even without EGL, so it can't be an
#error
.The idea is to build with
Or fail with a clear message if there are no GL/EGL headers/libs installed
This part is conditional already.
I have made EGL conditional.
Does somebody have an idea why this is?
No, but looks like you should check dp::UnpackFormat function, where layout and pixelType vars are initialized. There are magic comments like:
So you can experiment with some other constants combination, like GLRed (GL_RED).
"Linux Wayland" is a mobile OS? E.g. if we are checking for OGL ES here, should we enable OMIM_OS_MOBILE in this case? If yes, should we move this checks (ES) above under the OMIM_OS_MOBILE?
But first of all should make this combination (Linux + ES2) works and then prettify defines.
Thanks for the tip @vng.
At first glance, using
GLRed
does seem to help theGL_INVALID_OPERATION
here.And it turns out that the blurryness is possibly independent from that
GL_INVALID_OPERATION
here,it is caused by something else, somewhere else.
+1 to vng
We should choose a proper way for Linux versions. The easiest is probably this one:
Build a separate desktop version with OpenGL, and a mobile version with OpenGL ES.
The more complex way is to build one version that can detect the OpenGL version in runtime and switch appropriately.
I build this for Linux Ubuntu laptop - https://github.com/organicmaps/organicmaps/blob/master/docs/INSTALL.md
However when I transfer to Librem device I get "cannot execute binary file: Exec format error"
probably because Librem phone is an ARM CPU? How do you crosscompile it for ARM?
You need to set up the CMake toolchain for the cross-compilation and test if it works. There should be plenty of different manuals for that. Then (in a polished form, to avoid breaking other platforms) these changes can be merged into the main OM repo.
I don't know about configuring "CMake", can you point to where to start with that?
Why is there not an option for multi arch in "tools/unix/build_omim.sh -h" ?
Perhaps putting the 10GB build directory on device and running the same build would build it on the arm computer that is the Librem5 and maybe that would work?
Building it locally would work, you may only need to disable unity builds (or reduce unity batch size) if you have any unexpected crashes. And build only a
desktop
target, of course.It should be conditional on the presence of EGL files, detected by CMake. It requires the installation of mesa packages, right? That also requires updating build documentation. And in reality, it is required only for the desktop target build, e.g. not for the generator_tool.
OMIM_OS_MOBILE now uses GLES instead of GL in the code. Should it be enabled also in this case?
But not in the CMake, right?
@ -245,6 +245,13 @@ void MapWidget::Build()
Why did you choose highp here? Should it be chosen according to some dynamic conditions?
@ -245,6 +245,13 @@ void MapWidget::Build()
Qt dynamically changes that by prepending code to the shader program. so if
highp
happens to be unsupported, then it gets turned intomediump
automatically:@ -245,6 +245,13 @@ void MapWidget::Build()
Oh and since there is no
#version
specification in this shader, then when this is running on a hardware where there is only desktop OpenGL available and the default is#version 110
, where manual precision specification would normally not be allowed at all, then that case is also covered, because there Qt prependsdefine
s that replace the precision keywords with empty tokens:@ -245,6 +245,13 @@ void MapWidget::Build()
This is clear. What is unclear is why this highp is needed at all here?
@ -245,6 +245,13 @@ void MapWidget::Build()
Because otherwise on actual ES 2.0 HW, where GLSL
#version 100
is selected instead of#version 110
you don't get a valid shader program. You can check with the official Khronos providedglslangValidator
validator, that will report'float' : type requires declaration of default precision qualifier
, which is also what you get in runtime if you try to run this on ES 2.0 HW without the precision qualifier. Qt won't do any magic to make the shader program valid.@ -245,6 +245,13 @@ void MapWidget::Build()
Thanks for the explanation.
@ -245,6 +245,13 @@ void MapWidget::Build()
It is in the GLSL specification for OpenGL ES 2.0. Quote:
compilation units that do not include a #version directive will be treated as targeting version 100.
And hardware, that is only capable of ES most of the time does not support
#version 110
. Because after#version 100
, the next step is#version 300 es
, there is a table about that, which corresponds to observing HW.So vertex shaders don't need it because there is a built-in default precisions there even in ES 2.0. So only ES 2.0 fragment shaders need it. The other fragment shader in
map_widget.cpp
has so far been explicitly written for#version 150 core
where there is a built-in default precision. And the rest of the fragment shaders are generated bygl_shaders_preprocessor.py
which inserts a statement which defines the default precision.So right now, for our use cases I am not aware of any similar issues.
Should docs/INSTALL.md be updated? Is it feasible/does it worth it to make mesa a conditional dependency? What are pros and cons?
Or, alternatively, should we rewrite/remove OMIM_OS_MOBILE to dynamically initialize GLES if it's present, and fall back to GL if it's not?
Can you please add similar comments to other
#endif
clauses here for easier readability? It looks complex now.@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Shouldn't the available es2 or es3 version be detected in runtime automatically?
@ -245,6 +245,13 @@ void MapWidget::Build()
Thank you for the details!
The rest of this file uses that indentation? Shouldn't we follow that?
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Ideally yes. And I think that should be a topic of a separate PR, because this PR is already very old.
Do you think it is acceptable to do that in a separate PR?
I think I would remove the dependency on EGL, and then this would not be so complex anymore.
I think I would remove the dependency on EGL, and then no change is needed here.
I am not entirely sure what you mean by dynamically initialize GLES in this context, but
OMIM_OS_MOBILE
should probably be removed from here. And maybe even completely. In the past it made sense to useOMIM_OS_MOBILE
for switching between the mobile and the desktop wikipedia base url, but by now it is the same everywhere.So I would consider removing
OMIM_OS_MOBILE
completely in a separate PR. What do you think?Now on Android and iOS GLES is initialized via OMIM_OS_MOBILE
Should we do it differently now? GLES may not be available on desktop platforms. Btw, we can remove it completely on iOS too, it should use Metal instead. But for Android it can be used as a fallback from Vulkan for <24 APIs and also to work-around some Vulkan issues.
Regarding Wikipedia, why do you think it's better to remove different links? One less redirect means a better UX.
My observation is that for android it is iniitialized via it's own
OMIM_OS_ANDROID
so for GLES purposesOMIM_OS_MOBILE
should probalby be replaced with something likeOMIM_OS_IPHONE
.In my experience, If one opens the mobile version on a desktop, then there is no redirect, and the latest desktop experience is pretty close to the mobile one.
In any case, it might make sense to keep
OMIM_OS_MOBILE
for the wiki links, while the GLES init should probably be refactored.@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
It will probably prolong the review process, but I have addedd code for that anyway.
This code has changed regardless, please check again.
Anyhow, now that macro directive has been removed.
For now all removed.
The idea is good. But I afraid of the slower startup caused by double GL initialization. Can it be optimized?
Is it portable?
Why?
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
It's not our braces style )
Ditto
These comments are redundant )
If it's used only here, then define can be avoided and this line can be used directly in if else.
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Removed.
Changed.
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Changed.
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Changed.
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Changed.
@ -169,19 +170,8 @@ int main(int argc, char * argv[])
int returnCode = -1;
if (eulaAccepted) // User has accepted EULA
{
Changed.
At the moment I can't think of any typical scenario in which it wouldn't work.
Perhaps some rare corner cases, in which there are multiple displays attached to different GPUs,
which can only do ES 2.0.
But ES 2.0 only hardware is rarer and rarer anyway, any such corner cases are unsupported anyway.
At the moment I don't consider it a high priority to cover corner cases in which this wouldn't work.
I think if the project wants to keep up with technological development at large,
then we should make this ES 2.0 support leap as soon as possible, and move on to ES 3.0 and beyond,
and see if anyone is even interested in some obscure, old setup.
Nothing relies on
2, 1
in a desktop GL scenario, and AFAIK the proper way of requesting ES 2.0 is this.And all this
setVersion
call is just a polite request anyway, which might or might not be abided.So it's not super relevant anyway.
If it is a big eye soar, then I can re-test with
2, 0
, I just don't see much value in it.Why it was chosen in the first place instead of 2.0? Was there some reasoning in commit/pr for mapsme/omim repo?
From the git history It is unclear to me. It was simply selected in
3434dd895e
,for unknown reasons, even though the commit was not even about ES2, but desktop OpenGL 3.
And previous to that commit there was no call to
QSurfaceFormat::setVersion
in the codebase whatsoever.And if I may add my commentary, to
3434dd895e
, then there it already appears to be inconsistent with the shader codes IMHO. Becase both the fragment and the vertex shaders were added without version directive, which AFAIK had to default to#version 110
, and that does not correspond to2, 1
but to2. 0
, if I am not mistaken.@ -247,7 +262,7 @@ int main(int argc, char * argv[])
#endif // BUILD_DESIGNER
Framework framework(frameworkParams);
Do we need it here since you moved initialization code into MapWidget::initializeGL() ?
Or there is no problem to call setDefaultFormat twice?
Looks like this var can be local here, not in MapWidget member scope.
The old code was working with the desktop OpenGL 2.1 and then (after the mentioned patch) with 3.0. ES is not related here.
Requesting (non-ES) OpenGL 2.1 instead of 2.0 is a good idea.
@ -340,0 +390,4 @@
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setVersion(2, 1);
}
QSurfaceFormat::setDefaultFormat(fmt);
Is this approach portable also to Windows?
It is OpenGL 2.0, OpenGL 3.0, and OpenGLES3 branch, right?
LOG(CRITICAL, "") should stop execution here in debug and release.
Are different shaders used now?
Isn't the default format already defined at this moment?
All right then, reverted to
2, 1
, but do we know why?No, I didn't consider it part of this PR.
Yes, we are updating it here, just to be sure, that any consequent surfaces will use it too.
The original idea was to stop execution in the
else
branch in the final version.I have left that one out for test purposes, but anyhow changed it for now.
No OpenGL 2.0 is also handled with 2.0 ES in the previous one. In any case I have updated the comment.
@ -340,0 +390,4 @@
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setVersion(2, 1);
}
QSurfaceFormat::setDefaultFormat(fmt);
As portable as any other Qt functions. This is Qt code like the rest. The doc doesn't state any restrictions.
m_apiOpenGLES3
is also used inCreateEngine()
and any locally set value should somehow get out of the scope ofinitializeGL()
which cannot change signature, because it is an implementation of a virtual parent function.So if we can't take in output parameter, and can't return anything then, I don't see any better option than a member field.
@ -247,7 +262,7 @@ int main(int argc, char * argv[])
#endif // BUILD_DESIGNER
Framework framework(frameworkParams);
There is absolutely no limit how many times you can call
setDefaultFormat
, every time you call it it will be in effect for the next surface that is either created manually or automatically by Qt.Yes. Because supporting OpenGL 2.1 instead of 2.0 is better. And it was already working for many years.
LGTM. @pastk Would you like to test on your Linux?
Done.
Great, we're almost there. But it is not easy to understand now how es2/es3 are mixed though...
Actually, is there a case for Linux where OpenGLES2 is still used? Can we remove it completely?
@ -260,0 +263,4 @@
glDeleteVertexArrayFn = (TglDeleteVertexArrayFn)dlsym(libhandle,"glDeleteVertexArraysOES");
glMapBufferFn = (TglMapBufferFn)dlsym(libhandle, "glMapBufferOES");
glUnmapBufferFn = (TglUnmapBufferFn)dlsym(libhandle, "glUnmapBufferOES");
glMapBufferRangeFn = (TglMapBufferRangeFn)dlsym(libhandle, "glMapBufferRangeEXT");
Isn't mapbuffer available only for GLES 2.0 (according to the if above)?
Is GLES2 used now only on Mac? Can we use GL3+/GLES3 everywhere?
I've tested es3 on my 2014 Macbook Pro with Big Sur, and it works fine.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
Does it mean that GLES3 is not working yet on Linux?
Removed
The trailing whitespace has been removed.
Space added.
Spaces supplied.
Empty line has been removed.
I have eliminated the variables.
OK, then I have flipped
GLES3
on for Mac.Apparently this conditional statement is not even needed. Removed.
@ -260,0 +263,4 @@
glDeleteVertexArrayFn = (TglDeleteVertexArrayFn)dlsym(libhandle,"glDeleteVertexArraysOES");
glMapBufferFn = (TglMapBufferFn)dlsym(libhandle, "glMapBufferOES");
glUnmapBufferFn = (TglUnmapBufferFn)dlsym(libhandle, "glUnmapBufferOES");
glMapBufferRangeFn = (TglMapBufferRangeFn)dlsym(libhandle, "glMapBufferRangeEXT");
This is the 2.0 branch, and mapbuffer is not exclusive to 2.0.
It's ok, but I still don't understand why is there TODO for gles3 for Linux.
@ -260,0 +260,4 @@
LOG(LCRITICAL, ("Failed to open libGL.so.1:", dlerror()));
glGenVertexArraysFn = (TglGenVertexArraysFn)dlsym(libhandle,"glGenVertexArraysOES");
glBindVertexArrayFn = (TglBindVertexArrayFn)dlsym(libhandle, "glBindVertexArrayOES");
glDeleteVertexArrayFn = (TglDeleteVertexArrayFn)dlsym(libhandle,"glDeleteVertexArraysOES");
These extensions for the non-ES opengl do not have OES suffix, right? Like
glGenVertexArrays
. Does the current code mean that on Linux we always use OpenGL ES and newer use OpenGL?@ -260,0 +260,4 @@
LOG(LCRITICAL, ("Failed to open libGL.so.1:", dlerror()));
glGenVertexArraysFn = (TglGenVertexArraysFn)dlsym(libhandle,"glGenVertexArraysOES");
glBindVertexArrayFn = (TglBindVertexArrayFn)dlsym(libhandle, "glBindVertexArrayOES");
glDeleteVertexArrayFn = (TglDeleteVertexArrayFn)dlsym(libhandle,"glDeleteVertexArraysOES");
This branch and these function pointers are for ES 2.0 only.
The code that uses the functions without the OES suffix,
that is shared between multiple platforms, and desktop GL and ES 3.0 etc is between line 294 and 301.
So no it does not mean that we never use OpenGL.
dlclose
would decrement the reference counter to the.so
, and would potentially unload functions that we will have to keep using till the end of the application's runtime.@ -260,0 +260,4 @@
LOG(LCRITICAL, ("Failed to open libGL.so.1:", dlerror()));
glGenVertexArraysFn = (TglGenVertexArraysFn)dlsym(libhandle,"glGenVertexArraysOES");
glBindVertexArrayFn = (TglBindVertexArrayFn)dlsym(libhandle, "glBindVertexArrayOES");
glDeleteVertexArrayFn = (TglDeleteVertexArrayFn)dlsym(libhandle,"glDeleteVertexArraysOES");
I see, my bad. Thanks!
Does RTLD_NOW resolve all functions? If yes, then does LAZY resolve only the requested ones? We need only a few of them.
We prefer to properly clean up all requested resources, but it may be complicated in this case. Question: is it possible to use
eglGetProcAddress
here instead of dlopen?eglGetProcAddress
because it linked to EGL, which was also a new dependency, and that (new dependencies) was commented as a source of potential problems and impacts. So what has changed?Now I remember, thanks for the reminder. I've re-read the comments, and looks like I was wrong, and confused EGL with GLES 🤦 that time. The platform-correct way likely would be to link EGL on Linux and use its methods where necessary. But the existing approach doesn't create additional build dependency so it also makes good sense.
Maybe a comment mentioning that an "appropriate" way to get these addresses would be to use eglGetProcAddress and EGL would be enough ATM.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
IIUIC, it means LibGLES context is currently used for GLES2 only, and for GLES3 - LibGL context is used.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
@bam80
Not exactly, it's more complicated than that.
The current Qt code path in OMaps (where the relevant code path is ancient), for some inexplicable reason, uses the
m_apiOpenGLES3
boolean to switch between two shader codes.#version
directive and therefore is compatible withOpenGL 2.0
andOpenGL ES 2.0
#version 150 core
and therefore is only compatible with desktopOpenGL 3.2
and higher, so no ES.So if
m_apiOpenGLES3
istrue
-> Desktop!OpenGL 3.2
and higher is neededm_apiOpenGLES3
isfalse
-> EitherOpenGL ES 2.0
or desktopOpenGL 2.0
In the past I made an attempt to clean up this ancient mess, as it seemed easy.
The idea is to create a new
ApiVersion
here so we can finally separateGLES3
fromGL3
, and use separate shader codes in Qt forGLES2
,GLES3
andGL3
, and forget thism_apiOpenGLES3
mess.Now as easy it seemed in theory, in practice have faced some inexplicable shader problems resulting in a completely black map. Nowadays I don't have time to play with that, but I am still confident that this would be the right way forward.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
@Ferenc- thanks for the details!
m_apiOpenGLES3 == true
? ES is a subset of the desktop GL, so it should just work, no?@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
The subset (ES) is smaller than the superset (desktop), the current shader code, that we have on the
m_apiOpenGLES3 == true
path (is for desktop GL not for ES!!!) and is using the superset, ergo the subset is not enough.That part makes perfect sense, where I don't see the logic anymore is, where the shader code is written with
#version 150 core
which requires desktop GL, and likely won't compile on mobile renderers.Pros:
Vulkan would make things easier and more maintainable, as well as future proof.
Cons:
Well it takes some effort to implement the change.
And even though
OpenGL ES 3.0
is a subset of the currently required superset of desktopOpenGL 3.2
.Simply because
OpenGL ES 3.0
is actually chronologically, 7 years newer thanOpenGL 3.2
.It can happen, that some older desktop GPUs that don't support Vulkan, but fall into that seven years,
in which
OpenGL 3.2
existed butOpenGL ES 3.0
didn't.And when they try to compile the shader code explicitly written to
OpenGL ES 3.0
with#version 300 es
,then this tiny little unknown
es
at the end is coming from the future, and prevents the shader code from compiling.But this is a cornercase, and there could be workarounds for it anyway.
So in general I very much like this proposition, and I think the project should go for it.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
As I can see, it's actually GLSL version which doesn't work, not GL one.
But GL version implies GLSL one by spec, even if it's artificially.
So we can enforce GL version thus influencing GLSL one implicitly, providing fallback otherwise. That would consists the fix.
@ -340,3 +394,4 @@
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
@Ferenc-
m_apiOpenGLES3 == true
path works perfectly on all androids now. You can force it by typing?gl
in the search and restarting OM, and return back to vulkan by typing?vulkan
.For example, from a recent log:
@ -248,3 +263,3 @@
Framework framework(frameworkParams);
qt::MainWindow w(framework, apiOpenGLES3, std::move(screenshotParams),
qt::MainWindow w(framework, std::move(screenshotParams),
@Ferenc- sorry if it was discussed earlier, could you expand the problem to me?
What versions get applied by default without this patch, for XCB and Cocoa?