/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * Copyright 2015 Intel Corporation * * 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. * *//*! * \file * \brief surfaceless platform *//*--------------------------------------------------------------------*/ #include "tcuSurfacelessPlatform.hpp" #include <string> #include <vector> #include <sys/utsname.h> #include "deDynamicLibrary.hpp" #include "deMemory.h" #include "deSTLUtil.hpp" #include "egluUtil.hpp" #include "egluGLUtil.hpp" #include "eglwEnums.hpp" #include "eglwLibrary.hpp" #include "gluPlatform.hpp" #include "gluRenderConfig.hpp" #include "glwInitES20Direct.hpp" #include "glwInitES30Direct.hpp" #include "glwInitFunctions.hpp" #include "tcuFunctionLibrary.hpp" #include "tcuPixelFormat.hpp" #include "tcuPlatform.hpp" #include "tcuRenderTarget.hpp" #include "vkPlatform.hpp" #include <EGL/egl.h> using std::string; using std::vector; #if !defined(EGL_KHR_create_context) #define EGL_CONTEXT_FLAGS_KHR 0x30FC #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_KHR_create_context 1 #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF #define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE #define EGL_OPENGL_ES3_BIT_KHR 0x00000040 #endif // EGL_KHR_create_context // Default library names #if !defined(DEQP_GLES2_LIBRARY_PATH) # define DEQP_GLES2_LIBRARY_PATH "libGLESv2.so" #endif #if !defined(DEQP_GLES3_LIBRARY_PATH) # define DEQP_GLES3_LIBRARY_PATH DEQP_GLES2_LIBRARY_PATH #endif #if !defined(DEQP_OPENGL_LIBRARY_PATH) # define DEQP_OPENGL_LIBRARY_PATH "libGL.so" #endif namespace tcu { namespace surfaceless { class VulkanLibrary : public vk::Library { public: VulkanLibrary (void) : m_library ("libvulkan.so.1") , m_driver (m_library) { } const vk::PlatformInterface& getPlatformInterface (void) const { return m_driver; } const tcu::FunctionLibrary& getFunctionLibrary (void) const { return m_library; } private: const tcu::DynamicFunctionLibrary m_library; const vk::PlatformDriver m_driver; }; // Copied from tcuX11Platform.cpp class VulkanPlatform : public vk::Platform { public: vk::Library* createLibrary (void) const { return new VulkanLibrary(); } void describePlatform (std::ostream& dst) const { utsname sysInfo; deMemset(&sysInfo, 0, sizeof(sysInfo)); if (uname(&sysInfo) != 0) throw std::runtime_error("uname() failed"); dst << "OS: " << sysInfo.sysname << " " << sysInfo.release << " " << sysInfo.version << "\n"; dst << "CPU: " << sysInfo.machine << "\n"; } // FINISHME: Query actual memory limits. // // These hard-coded memory limits were copied from tcuX11Platform.cpp, // and they work well enough for Intel platforms. void getMemoryLimits (vk::PlatformMemoryLimits& limits) const { limits.totalSystemMemory = 256*1024*1024; limits.totalDeviceLocalMemory = 128*1024*1024; limits.deviceMemoryAllocationGranularity = 64*1024; limits.devicePageSize = 4096; limits.devicePageTableEntrySize = 8; limits.devicePageTableHierarchyLevels = 3; } }; bool isEGLExtensionSupported( const eglw::Library& egl, eglw::EGLDisplay display, const std::string& extName) { const vector<string> exts = eglu::getClientExtensions(egl); return de::contains(exts.begin(), exts.end(), extName); } class GetProcFuncLoader : public glw::FunctionLoader { public: GetProcFuncLoader(const eglw::Library& egl): m_egl(egl) { } glw::GenericFuncType get(const char* name) const { return (glw::GenericFuncType)m_egl.getProcAddress(name); } protected: const eglw::Library& m_egl; }; class DynamicFuncLoader : public glw::FunctionLoader { public: DynamicFuncLoader(de::DynamicLibrary* library): m_library(library) { } glw::GenericFuncType get(const char* name) const { return (glw::GenericFuncType)m_library->getFunction(name); } private: de::DynamicLibrary* m_library; }; class Platform : public tcu::Platform, public glu::Platform { public: Platform (void); const glu::Platform& getGLPlatform (void) const { return *this; } const vk::Platform& getVulkanPlatform (void) const { return m_vkPlatform; } private: VulkanPlatform m_vkPlatform; }; class ContextFactory : public glu::ContextFactory { public: ContextFactory (void); glu::RenderContext* createContext (const glu::RenderConfig& config, const tcu::CommandLine& cmdLine) const; }; class EglRenderContext : public glu::RenderContext { public: EglRenderContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine); ~EglRenderContext(void); glu::ContextType getType (void) const { return m_contextType; } const glw::Functions& getFunctions (void) const { return m_glFunctions; } const tcu::RenderTarget& getRenderTarget (void) const; void postIterate (void); private: const eglw::DefaultLibrary m_egl; const glu::ContextType m_contextType; eglw::EGLDisplay m_eglDisplay; eglw::EGLContext m_eglContext; de::DynamicLibrary* m_glLibrary; glw::Functions m_glFunctions; tcu::RenderTarget m_renderTarget; }; Platform::Platform(void) { m_contextFactoryRegistry.registerFactory(new ContextFactory()); } ContextFactory::ContextFactory() : glu::ContextFactory("default", "EGL surfaceless context") {} glu::RenderContext* ContextFactory::createContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine) const { return new EglRenderContext(config, cmdLine); } EglRenderContext::EglRenderContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine) : m_egl("libEGL.so") , m_contextType(config.type) , m_eglDisplay(EGL_NO_DISPLAY) , m_eglContext(EGL_NO_CONTEXT) , m_renderTarget( config.width, config.height, tcu::PixelFormat( config.redBits, config.greenBits, config.blueBits, config.alphaBits), config.depthBits, config.stencilBits, config.numSamples) { vector<eglw::EGLint> context_attribs; vector<eglw::EGLint> frame_buffer_attribs; vector<eglw::EGLint> surface_attribs; const glu::ContextType& contextType = config.type; eglw::EGLint eglMajorVersion; eglw::EGLint eglMinorVersion; eglw::EGLint flags = 0; eglw::EGLint num_configs; eglw::EGLConfig egl_config; eglw::EGLSurface egl_surface; (void) cmdLine; m_eglDisplay = m_egl.getDisplay(NULL); EGLU_CHECK_MSG(m_egl, "eglGetDisplay()"); if (m_eglDisplay == EGL_NO_DISPLAY) throw tcu::ResourceError("eglGetDisplay() failed"); EGLU_CHECK_CALL(m_egl, initialize(m_eglDisplay, &eglMajorVersion, &eglMinorVersion)); frame_buffer_attribs.push_back(EGL_RENDERABLE_TYPE); switch(contextType.getMajorVersion()) { case 3: frame_buffer_attribs.push_back(EGL_OPENGL_ES3_BIT); break; case 2: frame_buffer_attribs.push_back(EGL_OPENGL_ES2_BIT); break; default: frame_buffer_attribs.push_back(EGL_OPENGL_ES_BIT); } frame_buffer_attribs.push_back(EGL_SURFACE_TYPE); switch (config.surfaceType) { case glu::RenderConfig::SURFACETYPE_DONT_CARE: frame_buffer_attribs.push_back(EGL_DONT_CARE); break; case glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE: break; case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC: frame_buffer_attribs.push_back(EGL_PBUFFER_BIT); surface_attribs.push_back(EGL_WIDTH); surface_attribs.push_back(config.width); surface_attribs.push_back(EGL_HEIGHT); surface_attribs.push_back(config.height); break; case glu::RenderConfig::SURFACETYPE_WINDOW: throw tcu::NotSupportedError("surfaceless platform does not support --deqp-surface-type=window"); case glu::RenderConfig::SURFACETYPE_LAST: TCU_CHECK_INTERNAL(false); } surface_attribs.push_back(EGL_NONE); frame_buffer_attribs.push_back(EGL_RED_SIZE); frame_buffer_attribs.push_back(config.redBits); frame_buffer_attribs.push_back(EGL_GREEN_SIZE); frame_buffer_attribs.push_back(config.greenBits); frame_buffer_attribs.push_back(EGL_BLUE_SIZE); frame_buffer_attribs.push_back(config.blueBits); frame_buffer_attribs.push_back(EGL_ALPHA_SIZE); frame_buffer_attribs.push_back(config.alphaBits); frame_buffer_attribs.push_back(EGL_DEPTH_SIZE); frame_buffer_attribs.push_back(config.depthBits); frame_buffer_attribs.push_back(EGL_STENCIL_SIZE); frame_buffer_attribs.push_back(config.stencilBits); frame_buffer_attribs.push_back(EGL_NONE); if (!eglChooseConfig(m_eglDisplay, &frame_buffer_attribs[0], NULL, 0, &num_configs)) throw tcu::ResourceError("surfaceless couldn't find any config"); if (!eglChooseConfig(m_eglDisplay, &frame_buffer_attribs[0], &egl_config, 1, &num_configs)) throw tcu::ResourceError("surfaceless couldn't find any config"); switch (config.surfaceType) { case glu::RenderConfig::SURFACETYPE_DONT_CARE: egl_surface = EGL_NO_SURFACE; break; case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC: egl_surface = eglCreatePbufferSurface(m_eglDisplay, egl_config, &surface_attribs[0]); break; } context_attribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); context_attribs.push_back(contextType.getMajorVersion()); context_attribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR); context_attribs.push_back(contextType.getMinorVersion()); switch (contextType.getProfile()) { case glu::PROFILE_ES: EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_ES_API)); break; case glu::PROFILE_CORE: EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API)); context_attribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR); context_attribs.push_back(EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR); break; case glu::PROFILE_COMPATIBILITY: EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API)); context_attribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR); context_attribs.push_back(EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR); break; case glu::PROFILE_LAST: TCU_CHECK_INTERNAL(false); } if ((contextType.getFlags() & glu::CONTEXT_DEBUG) != 0) flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; if ((contextType.getFlags() & glu::CONTEXT_ROBUST) != 0) flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; if ((contextType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0) flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; context_attribs.push_back(EGL_CONTEXT_FLAGS_KHR); context_attribs.push_back(flags); context_attribs.push_back(EGL_NONE); m_eglContext = m_egl.createContext(m_eglDisplay, egl_config, EGL_NO_CONTEXT, &context_attribs[0]); EGLU_CHECK_MSG(m_egl, "eglCreateContext()"); if (!m_eglContext) throw tcu::ResourceError("eglCreateContext failed"); EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, egl_surface, egl_surface, m_eglContext)); if ((eglMajorVersion == 1 && eglMinorVersion >= 5) || isEGLExtensionSupported(m_egl, m_eglDisplay, "EGL_KHR_get_all_proc_addresses") || isEGLExtensionSupported(m_egl, EGL_NO_DISPLAY, "EGL_KHR_client_get_all_proc_addresses")) { // Use eglGetProcAddress() for core functions GetProcFuncLoader funcLoader(m_egl); glu::initCoreFunctions(&m_glFunctions, &funcLoader, contextType.getAPI()); } #if !defined(DEQP_GLES2_RUNTIME_LOAD) else if (contextType.getAPI() == glu::ApiType::es(2,0)) { glw::initES20Direct(&m_glFunctions); } #endif #if !defined(DEQP_GLES3_RUNTIME_LOAD) else if (contextType.getAPI() == glu::ApiType::es(3,0)) { glw::initES30Direct(&m_glFunctions); } #endif else { const char* libraryPath = NULL; if (glu::isContextTypeES(contextType)) { if (contextType.getMinorVersion() <= 2) libraryPath = DEQP_GLES2_LIBRARY_PATH; else libraryPath = DEQP_GLES3_LIBRARY_PATH; } else { libraryPath = DEQP_OPENGL_LIBRARY_PATH; } m_glLibrary = new de::DynamicLibrary(libraryPath); DynamicFuncLoader funcLoader(m_glLibrary); glu::initCoreFunctions(&m_glFunctions, &funcLoader, contextType.getAPI()); } { GetProcFuncLoader extLoader(m_egl); glu::initExtensionFunctions(&m_glFunctions, &extLoader, contextType.getAPI()); } } EglRenderContext::~EglRenderContext(void) { try { if (m_eglDisplay != EGL_NO_DISPLAY) { EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); if (m_eglContext != EGL_NO_CONTEXT) EGLU_CHECK_CALL(m_egl, destroyContext(m_eglDisplay, m_eglContext)); } EGLU_CHECK_CALL(m_egl, terminate(m_eglDisplay)); } catch (...) { } } const tcu::RenderTarget& EglRenderContext::getRenderTarget(void) const { return m_renderTarget; } void EglRenderContext::postIterate(void) { this->getFunctions().finish(); } } // namespace surfaceless } // namespace tcu tcu::Platform* createPlatform(void) { return new tcu::surfaceless::Platform(); }