| /*------------------------------------------------------------------------- |
| * 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 ¶ms) const; |
| |
| private: |
| const HINSTANCE m_instance; |
| }; |
| |
| class NativeWindow : public eglu::NativeWindow |
| { |
| public: |
| NativeWindow(NativeDisplay *nativeDisplay, HINSTANCE instance, const eglu::WindowParams ¶ms); |
| 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; |
| uint64_t 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 ¶ms) const |
| { |
| return new NativeWindow(dynamic_cast<NativeDisplay *>(nativeDisplay), m_instance, params); |
| } |
| |
| // NativeWindow |
| |
| NativeWindow::NativeWindow(NativeDisplay *nativeDisplay, HINSTANCE instance, const eglu::WindowParams ¶ms) |
| : 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(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 int64_t timeSinceVisibleUs = (int64_t)(deGetMicroseconds() - m_setVisibleTime); |
| |
| if (timeSinceVisibleUs < (int64_t)WAIT_WINDOW_VISIBLE_MS * 1000) |
| deSleep(WAIT_WINDOW_VISIBLE_MS - (uint32_t)(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; |
| } |
| } |
| |
| } // namespace |
| |
| 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(); |
| } |
| |
| } // namespace win32 |
| } // namespace tcu |