// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // 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. // Config.cpp: Implements the egl::Config class, describing the format, type // and size for an egl::Surface. Implements EGLConfig and related functionality. // [EGL 1.4] section 3.4 page 15. #include "Config.h" #include "Display.h" #include "common/debug.h" #include #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) #include #endif #include #include #include #include #include using namespace std; namespace egl { Config::Config(sw::Format displayFormat, EGLint minInterval, EGLint maxInterval, sw::Format renderTargetFormat, sw::Format depthStencilFormat, EGLint multiSample) : mRenderTargetFormat(renderTargetFormat), mDepthStencilFormat(depthStencilFormat), mMultiSample(multiSample) { mBindToTextureRGB = EGL_FALSE; mBindToTextureRGBA = EGL_FALSE; // Initialize to a high value to lower the preference of formats for which there's no native support mNativeVisualID = 0x7FFFFFFF; switch(renderTargetFormat) { case sw::FORMAT_A1R5G5B5: mRedSize = 5; mGreenSize = 5; mBlueSize = 5; mAlphaSize = 1; break; case sw::FORMAT_A2R10G10B10: mRedSize = 10; mGreenSize = 10; mBlueSize = 10; mAlphaSize = 2; break; case sw::FORMAT_A8R8G8B8: mRedSize = 8; mGreenSize = 8; mBlueSize = 8; mAlphaSize = 8; mBindToTextureRGBA = EGL_TRUE; #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) mNativeVisualID = HAL_PIXEL_FORMAT_BGRA_8888; #else mNativeVisualID = 2; // Arbitrary; prefer over ABGR #endif break; case sw::FORMAT_A8B8G8R8: mRedSize = 8; mGreenSize = 8; mBlueSize = 8; mAlphaSize = 8; mBindToTextureRGBA = EGL_TRUE; #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) mNativeVisualID = HAL_PIXEL_FORMAT_RGBA_8888; #endif break; case sw::FORMAT_R5G6B5: mRedSize = 5; mGreenSize = 6; mBlueSize = 5; mAlphaSize = 0; #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) mNativeVisualID = HAL_PIXEL_FORMAT_RGB_565; #endif break; case sw::FORMAT_X8R8G8B8: mRedSize = 8; mGreenSize = 8; mBlueSize = 8; mAlphaSize = 0; mBindToTextureRGB = EGL_TRUE; #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) mNativeVisualID = 0x1FF; // HAL_PIXEL_FORMAT_BGRX_8888 #else mNativeVisualID = 1; // Arbitrary; prefer over XBGR #endif break; case sw::FORMAT_X8B8G8R8: mRedSize = 8; mGreenSize = 8; mBlueSize = 8; mAlphaSize = 0; mBindToTextureRGB = EGL_TRUE; #if defined(__ANDROID__) && !defined(ANDROID_NDK_BUILD) mNativeVisualID = HAL_PIXEL_FORMAT_RGBX_8888; #endif break; default: UNREACHABLE(renderTargetFormat); } mLuminanceSize = 0; mBufferSize = mRedSize + mGreenSize + mBlueSize + mLuminanceSize + mAlphaSize; mAlphaMaskSize = 0; mColorBufferType = EGL_RGB_BUFFER; mConfigCaveat = EGL_NONE; mConfigID = 0; mConformant = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT; switch(depthStencilFormat) { case sw::FORMAT_NULL: mDepthSize = 0; mStencilSize = 0; break; // case sw::FORMAT_D16_LOCKABLE: // mDepthSize = 16; // mStencilSize = 0; // break; case sw::FORMAT_D32: mDepthSize = 32; mStencilSize = 0; break; // case sw::FORMAT_D15S1: // mDepthSize = 15; // mStencilSize = 1; // break; case sw::FORMAT_D24S8: mDepthSize = 24; mStencilSize = 8; break; case sw::FORMAT_D24X8: mDepthSize = 24; mStencilSize = 0; break; // case sw::FORMAT_D24X4S4: // mDepthSize = 24; // mStencilSize = 4; // break; case sw::FORMAT_D16: mDepthSize = 16; mStencilSize = 0; break; // case sw::FORMAT_D32F_LOCKABLE: // mDepthSize = 32; // mStencilSize = 0; // break; // case sw::FORMAT_D24FS8: // mDepthSize = 24; // mStencilSize = 8; // break; default: UNREACHABLE(depthStencilFormat); } mLevel = 0; mMatchNativePixmap = EGL_NONE; mMaxPBufferWidth = 4096; mMaxPBufferHeight = 4096; mMaxPBufferPixels = mMaxPBufferWidth * mMaxPBufferHeight; mMaxSwapInterval = maxInterval; mMinSwapInterval = minInterval; mNativeRenderable = EGL_FALSE; mNativeVisualType = 0; mRenderableType = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT; mSampleBuffers = (multiSample > 0) ? 1 : 0; mSamples = multiSample; mSurfaceType = EGL_PBUFFER_BIT | EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT | EGL_MULTISAMPLE_RESOLVE_BOX_BIT; mTransparentType = EGL_NONE; mTransparentRedValue = 0; mTransparentGreenValue = 0; mTransparentBlueValue = 0; // Although we could support any format as an Android HWComposer compatible config by converting when necessary, // the intent of EGL_ANDROID_framebuffer_target is to prevent any copies or conversions. mFramebufferTargetAndroid = (displayFormat == renderTargetFormat) ? EGL_TRUE : EGL_FALSE; mRecordableAndroid = EGL_TRUE; mBindToTextureTargetANGLE = EGL_TEXTURE_RECTANGLE_ANGLE; } EGLConfig Config::getHandle() const { return (EGLConfig)(size_t)mConfigID; } // This ordering determines the config ID bool CompareConfig::operator()(const Config &x, const Config &y) const { #define SORT_SMALLER(attribute) \ if(x.attribute != y.attribute) \ { \ return x.attribute < y.attribute; \ } static_assert(EGL_NONE < EGL_SLOW_CONFIG && EGL_SLOW_CONFIG < EGL_NON_CONFORMANT_CONFIG, ""); SORT_SMALLER(mConfigCaveat); static_assert(EGL_RGB_BUFFER < EGL_LUMINANCE_BUFFER, ""); SORT_SMALLER(mColorBufferType); SORT_SMALLER(mRedSize); SORT_SMALLER(mGreenSize); SORT_SMALLER(mBlueSize); SORT_SMALLER(mAlphaSize); SORT_SMALLER(mBufferSize); SORT_SMALLER(mSampleBuffers); SORT_SMALLER(mSamples); SORT_SMALLER(mDepthSize); SORT_SMALLER(mStencilSize); SORT_SMALLER(mAlphaMaskSize); SORT_SMALLER(mNativeVisualType); SORT_SMALLER(mNativeVisualID); #undef SORT_SMALLER // Strict ordering requires sorting all non-equal fields above assert(memcmp(&x, &y, sizeof(Config)) == 0); return false; } // Function object used by STL sorting routines for ordering Configs according to [EGL] section 3.4.1 page 24. class SortConfig { public: explicit SortConfig(const EGLint *attribList); bool operator()(const Config *x, const Config *y) const; private: EGLint wantedComponentsSize(const Config *config) const; bool mWantRed; bool mWantGreen; bool mWantBlue; bool mWantAlpha; bool mWantLuminance; }; SortConfig::SortConfig(const EGLint *attribList) : mWantRed(false), mWantGreen(false), mWantBlue(false), mWantAlpha(false), mWantLuminance(false) { // [EGL] section 3.4.1 page 24 // Sorting rule #3: by larger total number of color bits, // not considering components that are 0 or don't-care. for(const EGLint *attr = attribList; attr[0] != EGL_NONE; attr += 2) { // When multiple instances of the same attribute are present, last wins. bool isSpecified = attr[1] && attr[1] != EGL_DONT_CARE; switch(attr[0]) { case EGL_RED_SIZE: mWantRed = isSpecified; break; case EGL_GREEN_SIZE: mWantGreen = isSpecified; break; case EGL_BLUE_SIZE: mWantBlue = isSpecified; break; case EGL_ALPHA_SIZE: mWantAlpha = isSpecified; break; case EGL_LUMINANCE_SIZE: mWantLuminance = isSpecified; break; } } } EGLint SortConfig::wantedComponentsSize(const Config *config) const { EGLint total = 0; if(mWantRed) total += config->mRedSize; if(mWantGreen) total += config->mGreenSize; if(mWantBlue) total += config->mBlueSize; if(mWantAlpha) total += config->mAlphaSize; if(mWantLuminance) total += config->mLuminanceSize; return total; } bool SortConfig::operator()(const Config *x, const Config *y) const { #define SORT_SMALLER(attribute) \ if(x->attribute != y->attribute) \ { \ return x->attribute < y->attribute; \ } static_assert(EGL_NONE < EGL_SLOW_CONFIG && EGL_SLOW_CONFIG < EGL_NON_CONFORMANT_CONFIG, ""); SORT_SMALLER(mConfigCaveat); static_assert(EGL_RGB_BUFFER < EGL_LUMINANCE_BUFFER, ""); SORT_SMALLER(mColorBufferType); // By larger total number of color bits, only considering those that are requested to be > 0. EGLint xComponentsSize = wantedComponentsSize(x); EGLint yComponentsSize = wantedComponentsSize(y); if(xComponentsSize != yComponentsSize) { return xComponentsSize > yComponentsSize; } SORT_SMALLER(mBufferSize); SORT_SMALLER(mSampleBuffers); SORT_SMALLER(mSamples); SORT_SMALLER(mDepthSize); SORT_SMALLER(mStencilSize); SORT_SMALLER(mAlphaMaskSize); SORT_SMALLER(mNativeVisualType); SORT_SMALLER(mConfigID); #undef SORT_SMALLER return false; } ConfigSet::ConfigSet() { } void ConfigSet::add(sw::Format displayFormat, EGLint minSwapInterval, EGLint maxSwapInterval, sw::Format renderTargetFormat, sw::Format depthStencilFormat, EGLint multiSample) { Config conformantConfig(displayFormat, minSwapInterval, maxSwapInterval, renderTargetFormat, depthStencilFormat, multiSample); mSet.insert(conformantConfig); } size_t ConfigSet::size() const { return mSet.size(); } bool ConfigSet::getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig) { vector passed; passed.reserve(mSet.size()); /* Conformance expects for multiple instances of the same attribute that the * last instance `wins`. Reduce the attribute list first to comply with * this. */ /* TODO: C++11: unordered_map would be fine here */ map attribs; const EGLint *attribute = attribList; while (attribute[0] != EGL_NONE) { attribs[attribute[0]] = attribute[1]; attribute += 2; } for(Iterator config = mSet.begin(); config != mSet.end(); config++) { bool match = true; bool caveatMatch = (config->mConfigCaveat == EGL_NONE); for (map::iterator attribIt = attribs.begin(); attribIt != attribs.end(); attribIt++) { if(attribIt->second != EGL_DONT_CARE) { switch(attribIt->first) { case EGL_BUFFER_SIZE: match = config->mBufferSize >= attribIt->second; break; case EGL_ALPHA_SIZE: match = config->mAlphaSize >= attribIt->second; break; case EGL_BLUE_SIZE: match = config->mBlueSize >= attribIt->second; break; case EGL_GREEN_SIZE: match = config->mGreenSize >= attribIt->second; break; case EGL_RED_SIZE: match = config->mRedSize >= attribIt->second; break; case EGL_DEPTH_SIZE: match = config->mDepthSize >= attribIt->second; break; case EGL_STENCIL_SIZE: match = config->mStencilSize >= attribIt->second; break; case EGL_CONFIG_CAVEAT: match = config->mConfigCaveat == (EGLenum)attribIt->second; break; case EGL_CONFIG_ID: match = config->mConfigID == attribIt->second; break; case EGL_LEVEL: match = config->mLevel >= attribIt->second; break; case EGL_NATIVE_RENDERABLE: match = config->mNativeRenderable == (EGLBoolean)attribIt->second; break; case EGL_NATIVE_VISUAL_TYPE: match = config->mNativeVisualType == attribIt->second; break; case EGL_SAMPLES: match = config->mSamples >= attribIt->second; break; case EGL_SAMPLE_BUFFERS: match = config->mSampleBuffers >= attribIt->second; break; case EGL_SURFACE_TYPE: match = (config->mSurfaceType & attribIt->second) == attribIt->second; break; case EGL_TRANSPARENT_TYPE: match = config->mTransparentType == (EGLenum)attribIt->second; break; case EGL_TRANSPARENT_BLUE_VALUE: match = config->mTransparentBlueValue == attribIt->second; break; case EGL_TRANSPARENT_GREEN_VALUE: match = config->mTransparentGreenValue == attribIt->second; break; case EGL_TRANSPARENT_RED_VALUE: match = config->mTransparentRedValue == attribIt->second; break; case EGL_BIND_TO_TEXTURE_RGB: match = config->mBindToTextureRGB == (EGLBoolean)attribIt->second; break; case EGL_BIND_TO_TEXTURE_RGBA: match = config->mBindToTextureRGBA == (EGLBoolean)attribIt->second; break; case EGL_MIN_SWAP_INTERVAL: match = config->mMinSwapInterval == attribIt->second; break; case EGL_MAX_SWAP_INTERVAL: match = config->mMaxSwapInterval == attribIt->second; break; case EGL_LUMINANCE_SIZE: match = config->mLuminanceSize >= attribIt->second; break; case EGL_ALPHA_MASK_SIZE: match = config->mAlphaMaskSize >= attribIt->second; break; case EGL_COLOR_BUFFER_TYPE: match = config->mColorBufferType == (EGLenum)attribIt->second; break; case EGL_RENDERABLE_TYPE: match = (config->mRenderableType & attribIt->second) == attribIt->second; break; case EGL_MATCH_NATIVE_PIXMAP: match = false; UNIMPLEMENTED(); break; case EGL_CONFORMANT: match = (config->mConformant & attribIt->second) == attribIt->second; break; case EGL_RECORDABLE_ANDROID: match = config->mRecordableAndroid == (EGLBoolean)attribIt->second; break; case EGL_FRAMEBUFFER_TARGET_ANDROID: match = config->mFramebufferTargetAndroid == (EGLBoolean)attribIt->second; break; case EGL_BIND_TO_TEXTURE_TARGET_ANGLE: match = config->mBindToTextureTargetANGLE == (EGLBoolean)attribIt->second; break; // Ignored attributes case EGL_MAX_PBUFFER_WIDTH: case EGL_MAX_PBUFFER_HEIGHT: case EGL_MAX_PBUFFER_PIXELS: case EGL_NATIVE_VISUAL_ID: break; default: *numConfig = 0; return false; } if(!match) { break; } } if(attribIt->first == EGL_CONFIG_CAVEAT) { caveatMatch = match; } } if(match && caveatMatch) // We require the caveats to be NONE or the requested flags { passed.push_back(&*config); } } if(configs) { sort(passed.begin(), passed.end(), SortConfig(attribList)); EGLint index; for(index = 0; index < configSize && index < static_cast(passed.size()); index++) { configs[index] = passed[index]->getHandle(); } *numConfig = index; } else { *numConfig = static_cast(passed.size()); } return true; } const egl::Config *ConfigSet::get(EGLConfig configHandle) { for(Iterator config = mSet.begin(); config != mSet.end(); config++) { if(config->getHandle() == configHandle) { return &(*config); } } return nullptr; } }