| /*------------------------------------------------------------------------- |
| * 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 |