/*------------------------------------------------------------------------- * 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. * *//*! * \file * \brief Win32 EGL native display factory *//*--------------------------------------------------------------------*/ #include "tcuWin32EGLNativeDisplayFactory.hpp" #include "egluDefs.hpp" #include "tcuWin32Window.hpp" #include "tcuWin32API.h" #include "tcuTexture.hpp" #include "deMemory.h" #include "deThread.h" #include "deClock.h" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" // Assume no call translation is needed DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(HDC)); DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(HBITMAP)); DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(HWND)); namespace tcu { namespace win32 { namespace { using namespace eglw; enum { DEFAULT_SURFACE_WIDTH = 400, DEFAULT_SURFACE_HEIGHT = 300, WAIT_WINDOW_VISIBLE_MS = 500 //!< Time to wait before issuing screenshot after changing window visibility (hack for DWM) }; static const eglu::NativeDisplay::Capability DISPLAY_CAPABILITIES = eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY; static const eglu::NativePixmap::Capability BITMAP_CAPABILITIES = eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY; static const eglu::NativeWindow::Capability WINDOW_CAPABILITIES = (eglu::NativeWindow::Capability) (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 NativeDisplay : public eglu::NativeDisplay { public: NativeDisplay (void); virtual ~NativeDisplay (void) {} virtual EGLNativeDisplayType getLegacyNative (void) { return m_deviceContext; } const eglw::Library& getLibrary (void) const { return m_library; } HDC getDeviceContext (void) { return m_deviceContext; } private: HDC m_deviceContext; eglw::DefaultLibrary m_library; }; class NativePixmapFactory : public eglu::NativePixmapFactory { public: NativePixmapFactory (void); ~NativePixmapFactory (void) {} virtual eglu::NativePixmap* createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const; virtual eglu::NativePixmap* createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const; }; class NativePixmap : public eglu::NativePixmap { public: NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth); virtual ~NativePixmap (void); EGLNativePixmapType getLegacyNative (void) { return m_bitmap; } private: HBITMAP m_bitmap; }; class NativeWindowFactory : public eglu::NativeWindowFactory { public: NativeWindowFactory (HINSTANCE instance); virtual ~NativeWindowFactory (void) {} virtual eglu::NativeWindow* createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const; private: const HINSTANCE m_instance; }; class NativeWindow : public eglu::NativeWindow { public: NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params); virtual ~NativeWindow (void); EGLNativeWindowType getLegacyNative (void) { return m_window.getHandle(); } virtual IVec2 getSurfaceSize (void) const; virtual IVec2 getScreenSize (void) const { return getSurfaceSize(); } virtual void processEvents (void); virtual void setSurfaceSize (IVec2 size); virtual void setVisibility (eglu::WindowParams::Visibility visibility); virtual void readScreenPixels (tcu::TextureLevel* dst) const; private: win32::Window m_window; eglu::WindowParams::Visibility m_curVisibility; deUint64 m_setVisibleTime; //!< Time window was set visible. }; // NativeDisplay NativeDisplay::NativeDisplay (void) : eglu::NativeDisplay (DISPLAY_CAPABILITIES) , m_deviceContext ((HDC)EGL_DEFAULT_DISPLAY) , m_library ("libEGL.dll") { } // NativePixmap NativePixmap::NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth) : eglu::NativePixmap (BITMAP_CAPABILITIES) , m_bitmap (DE_NULL) { const HDC deviceCtx = nativeDisplay->getDeviceContext(); BITMAPINFO bitmapInfo; memset(&bitmapInfo, 0, sizeof(bitmapInfo)); if (bitDepth != 24 && bitDepth != 32) throw NotSupportedError("Unsupported pixmap bit depth", DE_NULL, __FILE__, __LINE__); bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo); bitmapInfo.bmiHeader.biWidth = width; bitmapInfo.bmiHeader.biHeight = height; bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biBitCount = bitDepth; bitmapInfo.bmiHeader.biCompression = BI_RGB; bitmapInfo.bmiHeader.biSizeImage = 0; bitmapInfo.bmiHeader.biXPelsPerMeter = 1; bitmapInfo.bmiHeader.biYPelsPerMeter = 1; bitmapInfo.bmiHeader.biClrUsed = 0; bitmapInfo.bmiHeader.biClrImportant = 0; void* bitmapPtr = DE_NULL; m_bitmap = CreateDIBSection(deviceCtx, &bitmapInfo, DIB_RGB_COLORS, &bitmapPtr, NULL, 0); if (!m_bitmap) throw ResourceError("Failed to create bitmap", DE_NULL, __FILE__, __LINE__); } NativePixmap::~NativePixmap (void) { DeleteObject(m_bitmap); } // NativePixmapFactory NativePixmapFactory::NativePixmapFactory (void) : eglu::NativePixmapFactory ("bitmap", "Win32 Bitmap", BITMAP_CAPABILITIES) { } eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const { const Library& egl = nativeDisplay->getLibrary(); int redBits = 0; int greenBits = 0; int blueBits = 0; int alphaBits = 0; int bitSum = 0; DE_ASSERT(display != EGL_NO_DISPLAY); egl.getConfigAttrib(display, config, EGL_RED_SIZE, &redBits); egl.getConfigAttrib(display, config, EGL_GREEN_SIZE, &greenBits); egl.getConfigAttrib(display, config, EGL_BLUE_SIZE, &blueBits); egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaBits); EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()"); bitSum = redBits+greenBits+blueBits+alphaBits; return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, bitSum); } eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const { const int defaultDepth = 32; return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, defaultDepth); } // NativeWindowFactory NativeWindowFactory::NativeWindowFactory (HINSTANCE instance) : eglu::NativeWindowFactory ("window", "Win32 Window", WINDOW_CAPABILITIES) , m_instance (instance) { } eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const { return new NativeWindow(dynamic_cast<NativeDisplay*>(nativeDisplay), m_instance, params); } // NativeWindow NativeWindow::NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params) : eglu::NativeWindow (WINDOW_CAPABILITIES) , m_window (instance, params.width == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH : params.width, params.height == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT : params.height) , m_curVisibility (eglu::WindowParams::VISIBILITY_HIDDEN) , m_setVisibleTime (0) { if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE) setVisibility(params.visibility); } void NativeWindow::setVisibility (eglu::WindowParams::Visibility visibility) { switch (visibility) { case eglu::WindowParams::VISIBILITY_HIDDEN: m_window.setVisible(false); m_curVisibility = visibility; break; case eglu::WindowParams::VISIBILITY_VISIBLE: case eglu::WindowParams::VISIBILITY_FULLSCREEN: // \todo [2014-03-12 pyry] Implement FULLSCREEN, or at least SW_MAXIMIZE. m_window.setVisible(true); m_curVisibility = eglu::WindowParams::VISIBILITY_VISIBLE; m_setVisibleTime = deGetMicroseconds(); break; default: DE_ASSERT(DE_FALSE); } } NativeWindow::~NativeWindow (void) { } IVec2 NativeWindow::getSurfaceSize (void) const { return m_window.getSize(); } void NativeWindow::processEvents (void) { m_window.processEvents(); } void NativeWindow::setSurfaceSize (IVec2 size) { m_window.setSize(size.x(), size.y()); } void NativeWindow::readScreenPixels (tcu::TextureLevel* dst) const { HDC windowDC = DE_NULL; HDC screenDC = DE_NULL; HDC tmpDC = DE_NULL; HBITMAP tmpBitmap = DE_NULL; RECT rect; TCU_CHECK_INTERNAL(m_curVisibility != eglu::WindowParams::VISIBILITY_HIDDEN); // Hack for DWM: There is no way to wait for DWM animations to finish, so we just have to wait // for a while before issuing screenshot if window was just made visible. { const deInt64 timeSinceVisibleUs = (deInt64)(deGetMicroseconds()-m_setVisibleTime); if (timeSinceVisibleUs < (deInt64)WAIT_WINDOW_VISIBLE_MS*1000) deSleep(WAIT_WINDOW_VISIBLE_MS - (deUint32)(timeSinceVisibleUs/1000)); } TCU_CHECK(GetClientRect(m_window.getHandle(), &rect)); try { const int width = rect.right - rect.left; const int height = rect.bottom - rect.top; BITMAPINFOHEADER bitmapInfo; deMemset(&bitmapInfo, 0, sizeof(bitmapInfo)); screenDC = GetDC(DE_NULL); TCU_CHECK(screenDC); windowDC = GetDC(m_window.getHandle()); TCU_CHECK(windowDC); tmpDC = CreateCompatibleDC(screenDC); TCU_CHECK(tmpDC != DE_NULL); MapWindowPoints(m_window.getHandle(), DE_NULL, (LPPOINT)&rect, 2); tmpBitmap = CreateCompatibleBitmap(screenDC, width, height); TCU_CHECK(tmpBitmap != DE_NULL); TCU_CHECK(SelectObject(tmpDC, tmpBitmap) != DE_NULL); TCU_CHECK(BitBlt(tmpDC, 0, 0, width, height, screenDC, rect.left, rect.top, SRCCOPY)); bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); bitmapInfo.biWidth = width; bitmapInfo.biHeight = -height; bitmapInfo.biPlanes = 1; bitmapInfo.biBitCount = 32; bitmapInfo.biCompression = BI_RGB; bitmapInfo.biSizeImage = 0; bitmapInfo.biXPelsPerMeter = 0; bitmapInfo.biYPelsPerMeter = 0; bitmapInfo.biClrUsed = 0; bitmapInfo.biClrImportant = 0; dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), width, height); TCU_CHECK(GetDIBits(screenDC, tmpBitmap, 0, height, dst->getAccess().getDataPtr(), (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS)); DeleteObject(tmpBitmap); tmpBitmap = DE_NULL; ReleaseDC(DE_NULL, screenDC); screenDC = DE_NULL; ReleaseDC(m_window.getHandle(), windowDC); windowDC = DE_NULL; DeleteDC(tmpDC); tmpDC = DE_NULL; } catch (...) { if (screenDC) ReleaseDC(DE_NULL, screenDC); if (windowDC) ReleaseDC(m_window.getHandle(), windowDC); if (tmpBitmap) DeleteObject(tmpBitmap); if (tmpDC) DeleteDC(tmpDC); throw; } } } // anonymous EGLNativeDisplayFactory::EGLNativeDisplayFactory (HINSTANCE instance) : eglu::NativeDisplayFactory ("win32", "Native Win32 Display", DISPLAY_CAPABILITIES) , m_instance (instance) { m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(m_instance)); m_nativePixmapRegistry.registerFactory(new NativePixmapFactory()); } EGLNativeDisplayFactory::~EGLNativeDisplayFactory (void) { } eglu::NativeDisplay* EGLNativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const { DE_UNREF(attribList); return new NativeDisplay(); } } // win32 } // tcu