targets/surfaceless: Add support for Chrome OS surfaceless
authorStéphane Marchesin <marcheu@chromium.org>
Tue, 1 Mar 2016 06:19:09 +0000 (22:19 -0800)
committerGurchetan Singh <gurchetansingh@google.com>
Tue, 10 Oct 2017 20:20:44 +0000 (13:20 -0700)
Implement a new dEQP target, "surfaceless", that supports offscreen
rendering using Chrome OS's EGL null platform. This is based on
intel's work to add support for "drm" target, but extends it to work
on ARM as well.

dEQP could be ran with --deqp-surface-type=fbo because the native EGL
platform does not support EGLSurfaces. However, because of b:27656575,
it must be run with --deqp-surface-type=pbuffer at the moment.

Unlike all of dEQP's other EGL-derived platforms (x11::egl::Platform,
Android::Platform, rpi::Platform, etc), class surfaceless::Platform does
not inherit from eglu::Platform and it reuses very little of dEQP's
existing EGL code. I had to re-invent the wheel when implementing the DRM
platform because dEQP's existing EGL code relies heavily on EGLSurfaces,
which Chrome's EGL surfaceless does not support.

BUG=chromium:543372
TEST=run deqp on veyron_jaq

framework/platform/CMakeLists.txt
framework/platform/surfaceless/tcuSurfacelessPlatform.cpp [new file with mode: 0644]
framework/platform/surfaceless/tcuSurfacelessPlatform.hpp [new file with mode: 0644]
targets/surfaceless/surfaceless.cmake [new file with mode: 0644]

index d13f8c9..77a5447 100644 (file)
@@ -83,6 +83,12 @@ if (NOT DEFINED TCUTIL_PLATFORM_SRCS)
                        include_directories(wayland)
                endif()
 
+       elseif (DE_OS_IS_UNIX AND DEQP_USE_SURFACELESS)
+               set(TCUTIL_PLATFORM_SRCS
+                       surfaceless/tcuSurfacelessPlatform.hpp
+                       surfaceless/tcuSurfacelessPlatform.cpp
+                       )
+
        elseif (DE_OS_IS_ANDROID)
                set(TCUTIL_PLATFORM_SRCS
                        android/tcuAndroidExecService.cpp
diff --git a/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp b/framework/platform/surfaceless/tcuSurfacelessPlatform.cpp
new file mode 100644 (file)
index 0000000..4ededd9
--- /dev/null
@@ -0,0 +1,412 @@
+/*-------------------------------------------------------------------------
+ * 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 "deDynamicLibrary.hpp"
+#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 "tcuPixelFormat.hpp"
+#include "tcuPlatform.hpp"
+#include "tcuRenderTarget.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
+{
+
+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; }
+};
+
+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();
+}
diff --git a/framework/platform/surfaceless/tcuSurfacelessPlatform.hpp b/framework/platform/surfaceless/tcuSurfacelessPlatform.hpp
new file mode 100644 (file)
index 0000000..01d703f
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _TCU_SURFACELESS_PLATFORM_HPP
+#define _TCU_SURFACELESS_PLATFORM_HPP
+
+/*-------------------------------------------------------------------------
+ * 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 DRM platform
+ *//*--------------------------------------------------------------------*/
+
+namespace tcu
+{
+       class Platform;
+}
+
+tcu::Platform* createPlatform (void);
+
+#endif
diff --git a/targets/surfaceless/surfaceless.cmake b/targets/surfaceless/surfaceless.cmake
new file mode 100644 (file)
index 0000000..f506112
--- /dev/null
@@ -0,0 +1,47 @@
+message("*** Using Surfaceless target")
+
+set(DEQP_TARGET_NAME "Surfaceless")
+
+include(FindPkgConfig)
+
+set(DEQP_USE_SURFACELESS ON)
+
+set(DEQP_SUPPORT_GLES2   ON)
+set(DEQP_SUPPORT_GLES3   ON)
+set(DEQP_SUPPORT_EGL     ON)
+
+find_library(GLES2_LIBRARIES GLESv2)
+find_library(GLES3_LIBRARIES GLESv3)
+find_path(GLES2_INCLUDE_PATH GLES2/gl2.h)
+find_path(GLES3_INCLUDE_PATH GLES3/gl3.h)
+
+if (GLES2_INCLUDE_PATH AND GLES2_LIBRARIES)
+        set(DEQP_GLES2_LIBRARIES ${GLES2_LIBRARIES})
+else ()
+        message (SEND_ERROR "GLESv2 support not found")
+endif ()
+
+if (GLES3_INCLUDE_PATH AND GLES3_LIBRARIES)
+        set(DEQP_GLES3_LIBRARIES ${GLES3_LIBRARIES})
+elseif (GLES3_INCLUDE_PATH AND GLES2_LIBRARIES)
+        # Assume GLESv2 provides ES3 symbols if gl3.h was found
+        # and the GLESv3 library was not.
+        set(DEQP_GLES3_LIBRARIES ${GLES2_LIBRARIES})
+else ()
+        message (FATAL_ERROR "GLESv3 support not found")
+endif ()
+
+pkg_check_modules(EGL REQUIRED egl)
+set(DEQP_EGL_LIBRARIES ${EGL_LIBRARIES})
+
+pkg_check_modules(GBM REQUIRED gbm)
+pkg_check_modules(KMS REQUIRED libkms)
+pkg_check_modules(DRM REQUIRED libdrm)
+
+include_directories(${GLES2_INCLUDE_PATH} ${GLES3_INCLUDE_PATH}
+                    ${EGL_INCLUDE_DIRS} ${GBM_INCLUDE_DIRS}
+                    ${KMS_INCLUDE_DIRS} ${DRM_INCLUDE_DIRS})
+
+set(DEQP_PLATFORM_LIBRARIES ${DEQP_GLES2_LIBRARIES} ${DEQP_GLES3_LIBRARIES}
+                            ${DEQP_EGL_LIBRARIES} ${GBM_LIBRARIES}
+                            ${KMS_LIBRARIES} ${DRM_LIBRARIES})