| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "GLESVersionDetector.h" |
| |
| #include "OpenGLESDispatch/EGLDispatch.h" |
| |
| #include "aemu/base/system/System.h" |
| #include "aemu/base/misc/StringUtils.h" |
| #include "host-common/feature_control.h" |
| #include "host-common/opengl/misc.h" |
| |
| #include <algorithm> |
| |
| namespace gfxstream { |
| namespace gl { |
| |
| // Config + context attributes to query the underlying OpenGL if it is |
| // a OpenGL ES backend. Only try for OpenGL ES 3, and assume OpenGL ES 2 |
| // exists (if it doesn't, this is the least of our problems). |
| static const EGLint gles3ConfigAttribs[] = |
| { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT, EGL_NONE }; |
| |
| static const EGLint pbufAttribs[] = |
| { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; |
| |
| static const EGLint gles31Attribs[] = |
| { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, |
| EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL_NONE }; |
| |
| static const EGLint gles30Attribs[] = |
| { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, |
| EGL_CONTEXT_MINOR_VERSION_KHR, 0, EGL_NONE }; |
| |
| static bool sTryContextCreation(EGLDisplay dpy, GLESDispatchMaxVersion ver) { |
| EGLConfig config; |
| EGLSurface surface; |
| |
| const EGLint* contextAttribs = nullptr; |
| |
| // Assume ES2 capable. |
| if (ver == GLES_DISPATCH_MAX_VERSION_2) return true; |
| |
| switch (ver) { |
| case GLES_DISPATCH_MAX_VERSION_3_0: |
| contextAttribs = gles30Attribs; |
| break; |
| case GLES_DISPATCH_MAX_VERSION_3_1: |
| contextAttribs = gles31Attribs; |
| break; |
| default: |
| break; |
| } |
| |
| if (!contextAttribs) return false; |
| |
| int numConfigs; |
| if (!s_egl.eglChooseConfig( |
| dpy, gles3ConfigAttribs, &config, 1, &numConfigs) || |
| numConfigs == 0) { |
| return false; |
| } |
| |
| surface = s_egl.eglCreatePbufferSurface(dpy, config, pbufAttribs); |
| if (surface == EGL_NO_SURFACE) { |
| return false; |
| } |
| |
| EGLContext ctx = s_egl.eglCreateContext(dpy, config, EGL_NO_CONTEXT, |
| contextAttribs); |
| |
| if (ctx == EGL_NO_CONTEXT) { |
| s_egl.eglDestroySurface(dpy, surface); |
| return false; |
| } else { |
| s_egl.eglDestroyContext(dpy, ctx); |
| s_egl.eglDestroySurface(dpy, surface); |
| return true; |
| } |
| } |
| |
| GLESDispatchMaxVersion calcMaxVersionFromDispatch(const gfxstream::host::FeatureSet& features, |
| EGLDisplay dpy) { |
| // TODO: 3.1 is the highest |
| GLESDispatchMaxVersion maxVersion = |
| GLES_DISPATCH_MAX_VERSION_3_1; |
| |
| // TODO: CTS conformance for OpenGL ES 3.1 |
| bool playStoreImage = features.PlayStoreImage.enabled; |
| |
| if (emugl::getRenderer() == SELECTED_RENDERER_HOST |
| || emugl::getRenderer() == SELECTED_RENDERER_SWIFTSHADER_INDIRECT |
| || emugl::getRenderer() == SELECTED_RENDERER_ANGLE_INDIRECT |
| || emugl::getRenderer() == SELECTED_RENDERER_ANGLE9_INDIRECT) { |
| if (s_egl.eglGetMaxGLESVersion) { |
| maxVersion = |
| (GLESDispatchMaxVersion)s_egl.eglGetMaxGLESVersion(dpy); |
| } |
| } else { |
| if (playStoreImage || |
| !sTryContextCreation(dpy, GLES_DISPATCH_MAX_VERSION_3_1)) { |
| maxVersion = GLES_DISPATCH_MAX_VERSION_3_0; |
| if (!sTryContextCreation(dpy, GLES_DISPATCH_MAX_VERSION_3_0)) { |
| maxVersion = GLES_DISPATCH_MAX_VERSION_2; |
| } |
| } |
| } |
| |
| if (playStoreImage) { |
| maxVersion = |
| std::min(maxVersion, |
| GLES_DISPATCH_MAX_VERSION_3_0); |
| } |
| |
| int maj = 2; int min = 0; |
| switch (maxVersion) { |
| case GLES_DISPATCH_MAX_VERSION_2: |
| maj = 2; min = 0; break; |
| case GLES_DISPATCH_MAX_VERSION_3_0: |
| maj = 3; min = 0; break; |
| case GLES_DISPATCH_MAX_VERSION_3_1: |
| maj = 3; min = 1; break; |
| case GLES_DISPATCH_MAX_VERSION_3_2: |
| maj = 3; min = 2; break; |
| default: |
| break; |
| } |
| |
| emugl::setGlesVersion(maj, min); |
| |
| return maxVersion; |
| } |
| |
| // For determining whether or not to use core profile OpenGL. |
| // (Note: This does not affect the detection of possible core profile configs, |
| // just whether to use them) |
| bool shouldEnableCoreProfile() { |
| int dispatchMaj, dispatchMin; |
| |
| emugl::getGlesVersion(&dispatchMaj, &dispatchMin); |
| return emugl::getRenderer() == SELECTED_RENDERER_HOST && |
| dispatchMaj > 2; |
| } |
| |
| void sAddExtensionIfSupported(GLESDispatchMaxVersion currVersion, |
| const std::string& from, |
| GLESDispatchMaxVersion extVersion, |
| const std::string& ext, |
| std::string& to) { |
| // If we chose a GLES version less than or equal to |
| // the |extVersion| the extension |ext| is tagged with, |
| // filter it according to the whitelist. |
| if (emugl::hasExtension(from.c_str(), ext.c_str()) && |
| currVersion > extVersion) { |
| to += ext; |
| to += " "; |
| } |
| } |
| |
| static bool sWhitelistedExtensionsGLES2(const std::string& hostExt) { |
| |
| #define WHITELIST(ext) \ |
| if (hostExt == #ext) return true; \ |
| |
| WHITELIST(GL_OES_compressed_ETC1_RGB8_texture) |
| WHITELIST(GL_OES_depth24) |
| WHITELIST(GL_OES_depth32) |
| WHITELIST(GL_OES_depth_texture) |
| WHITELIST(GL_OES_depth_texture_cube_map) |
| WHITELIST(GL_OES_EGL_image) |
| WHITELIST(GL_OES_EGL_image_external) |
| WHITELIST(GL_OES_EGL_sync) |
| WHITELIST(GL_OES_element_index_uint) |
| WHITELIST(GL_OES_framebuffer_object) |
| WHITELIST(GL_OES_packed_depth_stencil) |
| WHITELIST(GL_OES_rgb8_rgba8) |
| WHITELIST(GL_OES_standard_derivatives) |
| WHITELIST(GL_OES_texture_float) |
| WHITELIST(GL_OES_texture_float_linear) |
| WHITELIST(GL_OES_texture_half_float) |
| WHITELIST(GL_OES_texture_half_float_linear) |
| WHITELIST(GL_OES_texture_npot) |
| WHITELIST(GL_OES_texture_3D) |
| WHITELIST(GL_OVR_multiview2) |
| WHITELIST(GL_EXT_multiview_texture_multisample) |
| WHITELIST(GL_EXT_blend_minmax) |
| WHITELIST(GL_EXT_color_buffer_half_float) |
| WHITELIST(GL_EXT_draw_buffers) |
| WHITELIST(GL_EXT_instanced_arrays) |
| WHITELIST(GL_EXT_occlusion_query_boolean) |
| WHITELIST(GL_EXT_read_format_bgra) |
| WHITELIST(GL_EXT_texture_compression_rgtc) |
| WHITELIST(GL_EXT_texture_filter_anisotropic) |
| WHITELIST(GL_EXT_texture_format_BGRA8888) |
| WHITELIST(GL_EXT_texture_rg) |
| WHITELIST(GL_ANGLE_framebuffer_blit) |
| WHITELIST(GL_ANGLE_framebuffer_multisample) |
| WHITELIST(GL_ANGLE_instanced_arrays) |
| WHITELIST(GL_CHROMIUM_texture_filtering_hint) |
| WHITELIST(GL_NV_fence) |
| WHITELIST(GL_NV_framebuffer_blit) |
| WHITELIST(GL_NV_read_depth) |
| |
| #if defined(__linux__) |
| WHITELIST(GL_EXT_texture_compression_bptc) |
| WHITELIST(GL_EXT_texture_compression_s3tc) |
| #endif |
| |
| #undef WHITELIST |
| |
| return false; |
| } |
| |
| std::string filterExtensionsBasedOnMaxVersion(const gfxstream::host::FeatureSet& features, |
| GLESDispatchMaxVersion ver, |
| const std::string& exts) { |
| // We need to advertise ES 2 extensions if: |
| // a. the dispatch version on the host is ES 2 |
| // b. the guest image is not updated for ES 3+ |
| // (GLESDynamicVersion is disabled) |
| if (ver > GLES_DISPATCH_MAX_VERSION_2 && features.GlesDynamicVersion.enabled) { |
| return exts; |
| } |
| |
| std::string filteredExtensions; |
| filteredExtensions.reserve(4096); |
| auto add = [&filteredExtensions](const std::string& hostExt) { |
| if (!hostExt.empty() && |
| sWhitelistedExtensionsGLES2(hostExt)) { |
| filteredExtensions += hostExt; |
| filteredExtensions += " "; |
| } |
| }; |
| |
| android::base::split<std::string>(exts, " ", add); |
| |
| return filteredExtensions; |
| } |
| |
| } // namespace gl |
| } // namespace gfxstream |