/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * Copyright 2014 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 "egluNativeDisplay.hpp" #include "tcuANGLENativeDisplayFactory.h" #include #include #include "deClock.h" #include "deMemory.h" #include "egluDefs.hpp" #include "eglwLibrary.hpp" #include "tcuTexture.hpp" #include "util/OSPixmap.h" #include "util/OSWindow.h" // clang-format off #if (DE_OS == DE_OS_WIN32) #define ANGLE_EGL_LIBRARY_FULL_NAME ANGLE_EGL_LIBRARY_NAME ".dll" #elif (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_ANDROID) #define ANGLE_EGL_LIBRARY_FULL_NAME ANGLE_EGL_LIBRARY_NAME ".so" #elif (DE_OS == DE_OS_OSX) #define ANGLE_EGL_LIBRARY_FULL_NAME ANGLE_EGL_LIBRARY_NAME ".dylib" #else #error "Unsupported platform" #endif // clang-format on #if defined(ANGLE_USE_X11) # include #endif namespace tcu { namespace { template destType bitCast(sourceType source) { constexpr size_t copySize = sizeof(destType) < sizeof(sourceType) ? sizeof(destType) : sizeof(sourceType); destType output(0); memcpy(&output, &source, copySize); return output; } enum { DEFAULT_SURFACE_WIDTH = 400, DEFAULT_SURFACE_HEIGHT = 300, }; constexpr eglu::NativeDisplay::Capability kDisplayCapabilities = static_cast( eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_PLATFORM | eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_PLATFORM_EXT); constexpr eglu::NativePixmap::Capability kBitmapCapabilities = eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY; constexpr eglu::NativeWindow::Capability kWindowCapabilities = static_cast( eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY | eglu::NativeWindow::CAPABILITY_GET_SURFACE_SIZE | eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE | eglu::NativeWindow::CAPABILITY_READ_SCREEN_PIXELS | eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE | eglu::NativeWindow::CAPABILITY_CHANGE_VISIBILITY); class ANGLENativeDisplay : public eglu::NativeDisplay { public: explicit ANGLENativeDisplay(EGLNativeDisplayType display, std::vector attribs); ~ANGLENativeDisplay() override = default; void *getPlatformNative() override { // On OSX 64bits mDeviceContext is a 32 bit integer, so we can't simply // use reinterpret_cast. return bitCast(mDeviceContext); } const eglw::EGLAttrib *getPlatformAttributes() const override { return &mPlatformAttributes[0]; } const eglw::Library &getLibrary() const override { return mLibrary; } EGLNativeDisplayType getDeviceContext() const { return mDeviceContext; } private: EGLNativeDisplayType mDeviceContext; eglw::DefaultLibrary mLibrary; std::vector mPlatformAttributes; }; class NativePixmapFactory : public eglu::NativePixmapFactory { public: NativePixmapFactory(); ~NativePixmapFactory() override = default; eglu::NativePixmap *createPixmap(eglu::NativeDisplay *nativeDisplay, int width, int height) const override; eglu::NativePixmap *createPixmap(eglu::NativeDisplay *nativeDisplay, eglw::EGLDisplay display, eglw::EGLConfig config, const eglw::EGLAttrib *attribList, int width, int height) const override; }; class NativePixmap : public eglu::NativePixmap { public: NativePixmap(EGLNativeDisplayType display, int width, int height, int bitDepth); virtual ~NativePixmap(); eglw::EGLNativePixmapType getLegacyNative() override; private: OSPixmap *mPixmap; }; class NativeWindowFactory : public eglu::NativeWindowFactory { public: explicit NativeWindowFactory(EventState *eventState, uint32_t preRotation); ~NativeWindowFactory() override = default; eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms) const override; eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay, eglw::EGLDisplay display, eglw::EGLConfig config, const eglw::EGLAttrib *attribList, const eglu::WindowParams ¶ms) const override; private: EventState *mEvents; uint32_t mPreRotation; }; class NativeWindow : public eglu::NativeWindow { public: NativeWindow(ANGLENativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms, EventState *eventState, uint32_t preRotation); ~NativeWindow() override; eglw::EGLNativeWindowType getLegacyNative() override; IVec2 getSurfaceSize() const override; IVec2 getScreenSize() const override { return getSurfaceSize(); } void processEvents() override; void setSurfaceSize(IVec2 size) override; void setVisibility(eglu::WindowParams::Visibility visibility) override; void readScreenPixels(tcu::TextureLevel *dst) const override; private: OSWindow *mWindow; EventState *mEvents; uint32_t mPreRotation; }; // ANGLE NativeDisplay ANGLENativeDisplay::ANGLENativeDisplay(EGLNativeDisplayType display, std::vector attribs) : eglu::NativeDisplay(kDisplayCapabilities, EGL_PLATFORM_ANGLE_ANGLE, "EGL_EXT_platform_base"), mDeviceContext(display), mLibrary(ANGLE_EGL_LIBRARY_FULL_NAME), mPlatformAttributes(std::move(attribs)) {} // NativePixmap NativePixmap::NativePixmap(EGLNativeDisplayType display, int width, int height, int bitDepth) : eglu::NativePixmap(kBitmapCapabilities), mPixmap(CreateOSPixmap()) { #if (DE_OS != DE_OS_UNIX) throw tcu::NotSupportedError("Pixmap not supported"); #else if (!mPixmap) { throw ResourceError("Failed to create pixmap", DE_NULL, __FILE__, __LINE__); } if (!mPixmap->initialize(display, width, height, bitDepth)) { throw ResourceError("Failed to initialize pixmap", DE_NULL, __FILE__, __LINE__); } #endif } NativePixmap::~NativePixmap() { delete mPixmap; } eglw::EGLNativePixmapType NativePixmap::getLegacyNative() { return reinterpret_cast(mPixmap->getNativePixmap()); } // NativePixmapFactory NativePixmapFactory::NativePixmapFactory() : eglu::NativePixmapFactory("bitmap", "ANGLE Bitmap", kBitmapCapabilities) {} eglu::NativePixmap *NativePixmapFactory::createPixmap(eglu::NativeDisplay *nativeDisplay, eglw::EGLDisplay display, eglw::EGLConfig config, const eglw::EGLAttrib *attribList, int width, int height) const { const eglw::Library &egl = nativeDisplay->getLibrary(); int nativeVisual = 0; DE_ASSERT(display != EGL_NO_DISPLAY); egl.getConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisual); EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()"); return new NativePixmap(dynamic_cast(nativeDisplay)->getDeviceContext(), width, height, nativeVisual); } eglu::NativePixmap *NativePixmapFactory::createPixmap(eglu::NativeDisplay *nativeDisplay, int width, int height) const { const int defaultDepth = 32; return new NativePixmap(dynamic_cast(nativeDisplay)->getDeviceContext(), width, height, defaultDepth); } // NativeWindowFactory NativeWindowFactory::NativeWindowFactory(EventState *eventState, uint32_t preRotation) : eglu::NativeWindowFactory("window", "ANGLE Window", kWindowCapabilities), mEvents(eventState), mPreRotation(preRotation) {} eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms) const { DE_ASSERT(DE_FALSE); return nullptr; } eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay, eglw::EGLDisplay display, eglw::EGLConfig config, const eglw::EGLAttrib *attribList, const eglu::WindowParams ¶ms) const { return new NativeWindow(dynamic_cast(nativeDisplay), params, mEvents, mPreRotation); } // NativeWindow NativeWindow::NativeWindow(ANGLENativeDisplay *nativeDisplay, const eglu::WindowParams ¶ms, EventState *eventState, uint32_t preRotation) : eglu::NativeWindow(kWindowCapabilities), mWindow(OSWindow::New()), mEvents(eventState), mPreRotation(preRotation) { int osWindowWidth = params.width == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH : params.width; int osWindowHeight = params.height == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT : params.height; if (mPreRotation == 90 || mPreRotation == 270) { std::swap(osWindowWidth, osWindowHeight); } bool initialized = mWindow->initialize("dEQP ANGLE Tests", osWindowWidth, osWindowHeight); TCU_CHECK(initialized); if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE) NativeWindow::setVisibility(params.visibility); } void NativeWindow::setVisibility(eglu::WindowParams::Visibility visibility) { switch (visibility) { case eglu::WindowParams::VISIBILITY_HIDDEN: mWindow->setVisible(false); break; case eglu::WindowParams::VISIBILITY_VISIBLE: case eglu::WindowParams::VISIBILITY_FULLSCREEN: mWindow->setVisible(true); break; default: DE_ASSERT(DE_FALSE); } } NativeWindow::~NativeWindow() { OSWindow::Delete(&mWindow); } eglw::EGLNativeWindowType NativeWindow::getLegacyNative() { return reinterpret_cast(mWindow->getNativeWindow()); } IVec2 NativeWindow::getSurfaceSize() const { int width = mWindow->getWidth(); int height = mWindow->getHeight(); if (mPreRotation == 90 || mPreRotation == 270) { // Return the original dimensions dEQP asked for. This ensures that the dEQP code is never // aware of the window actually being rotated. std::swap(width, height); } return IVec2(width, height); } void NativeWindow::processEvents() { mWindow->messageLoop(); // Look for a quit event to forward to the EventState Event event = {}; while (mWindow->popEvent(&event)) { if (event.Type == Event::EVENT_CLOSED) { mEvents->signalQuitEvent(); } } } void NativeWindow::setSurfaceSize(IVec2 size) { int osWindowWidth = size.x(); int osWindowHeight = size.y(); if (mPreRotation == 90 || mPreRotation == 270) { std::swap(osWindowWidth, osWindowHeight); } mWindow->resize(osWindowWidth, osWindowHeight); } void NativeWindow::readScreenPixels(tcu::TextureLevel *dst) const { dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), mWindow->getWidth(), mWindow->getHeight()); if (!mWindow->takeScreenshot(reinterpret_cast(dst->getAccess().getDataPtr()))) { throw InternalError("Failed to read screen pixels", DE_NULL, __FILE__, __LINE__); } if (mPreRotation != 0) { throw InternalError("Read screen pixels with prerotation is not supported", DE_NULL, __FILE__, __LINE__); } } } // namespace ANGLENativeDisplayFactory::ANGLENativeDisplayFactory( const std::string &name, const std::string &description, std::vector platformAttributes, EventState *eventState) : eglu::NativeDisplayFactory(name, description, kDisplayCapabilities, EGL_PLATFORM_ANGLE_ANGLE, "EGL_EXT_platform_base"), mNativeDisplay(bitCast(EGL_DEFAULT_DISPLAY)), mPlatformAttributes(std::move(platformAttributes)) { #if (DE_OS == DE_OS_UNIX) && defined(ANGLE_USE_X11) // Make sure to only open the X display once so that it can be used by the EGL display as well // as pixmaps mNativeDisplay = bitCast(XOpenDisplay(nullptr)); #endif // (DE_OS == DE_OS_UNIX) // If pre-rotating, let NativeWindowFactory know. uint32_t preRotation = 0; for (size_t attrIndex = 0; attrIndex < mPlatformAttributes.size() && mPlatformAttributes[attrIndex] != EGL_NONE; attrIndex += 2) { if (mPlatformAttributes[attrIndex] != EGL_FEATURE_OVERRIDES_ENABLED_ANGLE) { continue; } const char **enabledFeatures = reinterpret_cast(mPlatformAttributes[attrIndex + 1]); DE_ASSERT(enabledFeatures != nullptr && *enabledFeatures != nullptr); for (; *enabledFeatures; ++enabledFeatures) { if (strcmp(enabledFeatures[0], "emulatedPrerotation90") == 0) { preRotation = 90; } else if (strcmp(enabledFeatures[0], "emulatedPrerotation180") == 0) { preRotation = 180; } else if (strcmp(enabledFeatures[0], "emulatedPrerotation270") == 0) { preRotation = 270; } } break; } m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(eventState, preRotation)); m_nativePixmapRegistry.registerFactory(new NativePixmapFactory()); } ANGLENativeDisplayFactory::~ANGLENativeDisplayFactory() = default; eglu::NativeDisplay *ANGLENativeDisplayFactory::createDisplay( const eglw::EGLAttrib *attribList) const { DE_UNREF(attribList); return new ANGLENativeDisplay(bitCast(mNativeDisplay), mPlatformAttributes); } } // namespace tcu