// Copyright (C) 2015 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 "EmulatedEglConfig.h" #include "OpenGLESDispatch/EGLDispatch.h" #include "host-common/opengl/emugl_config.h" #include "host-common/feature_control.h" #include "host-common/logging.h" #include "host-common/misc.h" #include "host-common/opengl/misc.h" #include #include namespace gfxstream { namespace gl { namespace { #ifndef EGL_PRESERVED_RESOURCES #define EGL_PRESERVED_RESOURCES 0x3030 #endif const GLuint kConfigAttributes[] = { EGL_DEPTH_SIZE, // must be first - see getDepthSize() EGL_STENCIL_SIZE, // must be second - see getStencilSize() EGL_RENDERABLE_TYPE,// must be third - see getRenderableType() EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType() EGL_CONFIG_ID, // must be fifth - see chooseConfig() EGL_BUFFER_SIZE, EGL_ALPHA_SIZE, EGL_BLUE_SIZE, EGL_GREEN_SIZE, EGL_RED_SIZE, EGL_CONFIG_CAVEAT, EGL_LEVEL, EGL_MAX_PBUFFER_HEIGHT, EGL_MAX_PBUFFER_PIXELS, EGL_MAX_PBUFFER_WIDTH, EGL_NATIVE_RENDERABLE, EGL_NATIVE_VISUAL_ID, EGL_NATIVE_VISUAL_TYPE, EGL_PRESERVED_RESOURCES, EGL_SAMPLES, EGL_SAMPLE_BUFFERS, EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_BLUE_VALUE, EGL_TRANSPARENT_GREEN_VALUE, EGL_TRANSPARENT_RED_VALUE, EGL_BIND_TO_TEXTURE_RGB, EGL_BIND_TO_TEXTURE_RGBA, EGL_MIN_SWAP_INTERVAL, EGL_MAX_SWAP_INTERVAL, EGL_LUMINANCE_SIZE, EGL_ALPHA_MASK_SIZE, EGL_COLOR_BUFFER_TYPE, //EGL_MATCH_NATIVE_PIXMAP, EGL_RECORDABLE_ANDROID, EGL_CONFORMANT }; const size_t kConfigAttributesLen = sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]); bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) { // Filter out configs which do not support pbuffers, since they // are used to implement window surfaces. EGLint surfaceType; s_egl.eglGetConfigAttrib( display, config, EGL_SURFACE_TYPE, &surfaceType); if (!(surfaceType & EGL_PBUFFER_BIT)) { return false; } // Filter out configs that do not support RGB pixel values. EGLint redSize = 0, greenSize = 0, blueSize = 0; s_egl.eglGetConfigAttrib( display, config,EGL_RED_SIZE, &redSize); s_egl.eglGetConfigAttrib( display, config, EGL_GREEN_SIZE, &greenSize); s_egl.eglGetConfigAttrib( display, config, EGL_BLUE_SIZE, &blueSize); if (!redSize || !greenSize || !blueSize) { return false; } return true; } } // namespace EmulatedEglConfig::EmulatedEglConfig(EGLint guestConfig, EGLConfig hostConfig, EGLDisplay hostDisplay) : mGuestConfig(guestConfig), mHostConfig(hostConfig), mAttribValues(kConfigAttributesLen) { for (size_t i = 0; i < kConfigAttributesLen; ++i) { mAttribValues[i] = 0; s_egl.eglGetConfigAttrib(hostDisplay, hostConfig, kConfigAttributes[i], &mAttribValues[i]); // This implementation supports guest window surfaces by wrapping // them around host Pbuffers, so always report it to the guest. if (kConfigAttributes[i] == EGL_SURFACE_TYPE) { mAttribValues[i] |= EGL_WINDOW_BIT; } // Don't report ES3 renderable type if we don't support it. if (kConfigAttributes[i] == EGL_RENDERABLE_TYPE) { if (!feature_is_enabled( kFeature_GLESDynamicVersion) && mAttribValues[i] & EGL_OPENGL_ES3_BIT) { mAttribValues[i] &= ~EGL_OPENGL_ES3_BIT; } } } } EmulatedEglConfigList::EmulatedEglConfigList(EGLDisplay display, GLESDispatchMaxVersion version) : mDisplay(display), mGlesDispatchMaxVersion(version) { if (display == EGL_NO_DISPLAY) { ERR("Invalid display value %p (EGL_NO_DISPLAY).", (void*)display); return; } EGLint numHostConfigs = 0; if (!s_egl.eglGetConfigs(display, NULL, 0, &numHostConfigs)) { ERR("Failed to get number of host EGL configs."); return; } std::vector hostConfigs(numHostConfigs); s_egl.eglGetConfigs(display, hostConfigs.data(), numHostConfigs, &numHostConfigs); for (EGLConfig hostConfig : hostConfigs) { // Filter out configs that are not compatible with our implementation. if (!isCompatibleHostConfig(hostConfig, display)) { continue; } const EGLint guestConfig = static_cast(mConfigs.size()); mConfigs.push_back(EmulatedEglConfig(guestConfig, hostConfig, display)); } } int EmulatedEglConfigList::chooseConfig(const EGLint* attribs, EGLint* configs, EGLint configsSize) const { EGLint numHostConfigs = 0; if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) { ERR("Failed to get number of host EGL configs."); return 0; } // If EGL_SURFACE_TYPE appears in |attribs|, the value passed to // eglChooseConfig should be forced to EGL_PBUFFER_BIT because that's // what it used by the current implementation, exclusively. This forces // the rewrite of |attribs| into a new array. bool hasSurfaceType = false; bool wantSwapPreserved = false; int surfaceTypeIdx = 0; int numAttribs = 0; std::vector newAttribs; while (attribs[numAttribs] != EGL_NONE) { if (attribs[numAttribs] == EGL_SURFACE_TYPE) { hasSurfaceType = true; surfaceTypeIdx = numAttribs; if (attribs[numAttribs+1] & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) { wantSwapPreserved = true; } } // Reject config if guest asked for ES3 and we don't have it. if (attribs[numAttribs] == EGL_RENDERABLE_TYPE) { if (attribs[numAttribs + 1] != EGL_DONT_CARE && attribs[numAttribs + 1] & EGL_OPENGL_ES3_BIT_KHR && (!feature_is_enabled( kFeature_GLESDynamicVersion) || mGlesDispatchMaxVersion < GLES_DISPATCH_MAX_VERSION_3_0)) { return 0; } } numAttribs += 2; } if (numAttribs) { newAttribs.resize(numAttribs); memcpy(&newAttribs[0], attribs, numAttribs * sizeof(EGLint)); } int apiLevel; emugl::getAvdInfo(NULL, &apiLevel); if (!hasSurfaceType) { newAttribs.push_back(EGL_SURFACE_TYPE); newAttribs.push_back(0); } else if (wantSwapPreserved && apiLevel <= 19) { newAttribs[surfaceTypeIdx + 1] &= ~(EGLint)EGL_SWAP_BEHAVIOR_PRESERVED_BIT; } if (emugl::getRenderer() == SELECTED_RENDERER_SWIFTSHADER || emugl::getRenderer() == SELECTED_RENDERER_SWIFTSHADER_INDIRECT || emugl::getRenderer() == SELECTED_RENDERER_ANGLE || emugl::getRenderer() == SELECTED_RENDERER_ANGLE_INDIRECT) { newAttribs.push_back(EGL_CONFIG_CAVEAT); newAttribs.push_back(EGL_DONT_CARE); } newAttribs.push_back(EGL_NONE); std::vector matchedConfigs(numHostConfigs); if (s_egl.eglChooseConfig(mDisplay, &newAttribs[0], matchedConfigs.data(), numHostConfigs, &numHostConfigs) == EGL_FALSE) { return -s_egl.eglGetError(); } int result = 0; for (int n = 0; n < numHostConfigs; ++n) { // Don't count or write more than |configsSize| items if |configs| // is not NULL. if (configs && configsSize > 0 && result >= configsSize) { break; } // Skip incompatible host configs. if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) { continue; } // Find the EmulatedEglConfig with the same EGL_CONFIG_ID EGLint hostConfigId; s_egl.eglGetConfigAttrib( mDisplay, matchedConfigs[n], EGL_CONFIG_ID, &hostConfigId); for (const EmulatedEglConfig& config : mConfigs) { if (config.getConfigId() == hostConfigId) { // There is a match. Write it to |configs| if it is not NULL. if (configs && result < configsSize) { configs[result] = config.getGuestEglConfig(); } result++; break; } } } return result; } void EmulatedEglConfigList::getPackInfo(EGLint* numConfigs, EGLint* numAttributes) const { if (numConfigs) { *numConfigs = mConfigs.size(); } if (numAttributes) { *numAttributes = static_cast(kConfigAttributesLen); } } EGLint EmulatedEglConfigList::packConfigs(GLuint bufferByteSize, GLuint* buffer) const { GLuint numAttribs = static_cast(kConfigAttributesLen); GLuint kGLuintSize = static_cast(sizeof(GLuint)); GLuint neededByteSize = (mConfigs.size() + 1) * numAttribs * kGLuintSize; if (!buffer || bufferByteSize < neededByteSize) { return -neededByteSize; } // Write to the buffer the config attribute ids, followed for each one // of the configs, their values. memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize); for (int i = 0; i < mConfigs.size(); ++i) { memcpy(buffer + (i + 1) * kConfigAttributesLen, mConfigs[i].mAttribValues.data(), kConfigAttributesLen * kGLuintSize); } return mConfigs.size(); } } // namespace gl } // namespace gfxstream