1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 3 * ---------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Win32 EGL native display factory 22 *//*--------------------------------------------------------------------*/ 23 24 #include "tcuWin32EGLNativeDisplayFactory.hpp" 25 26 #include "egluDefs.hpp" 27 #include "tcuWin32Window.hpp" 28 #include "tcuWin32API.h" 29 #include "tcuTexture.hpp" 30 #include "deMemory.h" 31 #include "deThread.h" 32 #include "deClock.h" 33 #include "eglwLibrary.hpp" 34 #include "eglwEnums.hpp" 35 36 // Assume no call translation is needed 37 DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(HDC)); 38 DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(HBITMAP)); 39 DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(HWND)); 40 41 namespace tcu 42 { 43 namespace win32 44 { 45 namespace 46 { 47 48 using namespace eglw; 49 50 enum 51 { 52 DEFAULT_SURFACE_WIDTH = 400, 53 DEFAULT_SURFACE_HEIGHT = 300, 54 WAIT_WINDOW_VISIBLE_MS = 500 //!< Time to wait before issuing screenshot after changing window visibility (hack for DWM) 55 }; 56 57 static const eglu::NativeDisplay::Capability DISPLAY_CAPABILITIES = eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY; 58 static const eglu::NativePixmap::Capability BITMAP_CAPABILITIES = eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY; 59 static const eglu::NativeWindow::Capability WINDOW_CAPABILITIES = (eglu::NativeWindow::Capability) 60 (eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY | 61 eglu::NativeWindow::CAPABILITY_GET_SURFACE_SIZE | 62 eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE | 63 eglu::NativeWindow::CAPABILITY_READ_SCREEN_PIXELS | 64 eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE | 65 eglu::NativeWindow::CAPABILITY_CHANGE_VISIBILITY); 66 67 class NativeDisplay : public eglu::NativeDisplay 68 { 69 public: 70 NativeDisplay (void); 71 virtual ~NativeDisplay (void) {} 72 73 virtual EGLNativeDisplayType getLegacyNative (void) { return m_deviceContext; } 74 const eglw::Library& getLibrary (void) const { return m_library; } 75 76 HDC getDeviceContext (void) { return m_deviceContext; } 77 78 private: 79 HDC m_deviceContext; 80 eglw::DefaultLibrary m_library; 81 }; 82 83 class NativePixmapFactory : public eglu::NativePixmapFactory 84 { 85 public: 86 NativePixmapFactory (void); 87 ~NativePixmapFactory (void) {} 88 89 virtual eglu::NativePixmap* createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const; 90 virtual eglu::NativePixmap* createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const; 91 }; 92 93 class NativePixmap : public eglu::NativePixmap 94 { 95 public: 96 NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth); 97 virtual ~NativePixmap (void); 98 99 EGLNativePixmapType getLegacyNative (void) { return m_bitmap; } 100 101 private: 102 HBITMAP m_bitmap; 103 }; 104 105 class NativeWindowFactory : public eglu::NativeWindowFactory 106 { 107 public: 108 NativeWindowFactory (HINSTANCE instance); 109 virtual ~NativeWindowFactory (void) {} 110 111 virtual eglu::NativeWindow* createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const; 112 113 private: 114 const HINSTANCE m_instance; 115 }; 116 117 class NativeWindow : public eglu::NativeWindow 118 { 119 public: 120 NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params); 121 virtual ~NativeWindow (void); 122 123 EGLNativeWindowType getLegacyNative (void) { return m_window.getHandle(); } 124 virtual IVec2 getSurfaceSize (void) const; 125 virtual IVec2 getScreenSize (void) const { return getSurfaceSize(); } 126 virtual void processEvents (void); 127 virtual void setSurfaceSize (IVec2 size); 128 virtual void setVisibility (eglu::WindowParams::Visibility visibility); 129 virtual void readScreenPixels (tcu::TextureLevel* dst) const; 130 131 private: 132 win32::Window m_window; 133 eglu::WindowParams::Visibility m_curVisibility; 134 deUint64 m_setVisibleTime; //!< Time window was set visible. 135 }; 136 137 // NativeDisplay 138 139 NativeDisplay::NativeDisplay (void) 140 : eglu::NativeDisplay (DISPLAY_CAPABILITIES) 141 , m_deviceContext ((HDC)EGL_DEFAULT_DISPLAY) 142 , m_library ("libEGL.dll") 143 { 144 } 145 146 // NativePixmap 147 148 NativePixmap::NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth) 149 : eglu::NativePixmap (BITMAP_CAPABILITIES) 150 , m_bitmap (DE_NULL) 151 { 152 const HDC deviceCtx = nativeDisplay->getDeviceContext(); 153 BITMAPINFO bitmapInfo; 154 155 memset(&bitmapInfo, 0, sizeof(bitmapInfo)); 156 157 if (bitDepth != 24 && bitDepth != 32) 158 throw NotSupportedError("Unsupported pixmap bit depth", DE_NULL, __FILE__, __LINE__); 159 160 bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo); 161 bitmapInfo.bmiHeader.biWidth = width; 162 bitmapInfo.bmiHeader.biHeight = height; 163 bitmapInfo.bmiHeader.biPlanes = 1; 164 bitmapInfo.bmiHeader.biBitCount = bitDepth; 165 bitmapInfo.bmiHeader.biCompression = BI_RGB; 166 bitmapInfo.bmiHeader.biSizeImage = 0; 167 bitmapInfo.bmiHeader.biXPelsPerMeter = 1; 168 bitmapInfo.bmiHeader.biYPelsPerMeter = 1; 169 bitmapInfo.bmiHeader.biClrUsed = 0; 170 bitmapInfo.bmiHeader.biClrImportant = 0; 171 172 void* bitmapPtr = DE_NULL; 173 m_bitmap = CreateDIBSection(deviceCtx, &bitmapInfo, DIB_RGB_COLORS, &bitmapPtr, NULL, 0); 174 175 if (!m_bitmap) 176 throw ResourceError("Failed to create bitmap", DE_NULL, __FILE__, __LINE__); 177 } 178 179 NativePixmap::~NativePixmap (void) 180 { 181 DeleteObject(m_bitmap); 182 } 183 184 // NativePixmapFactory 185 186 NativePixmapFactory::NativePixmapFactory (void) 187 : eglu::NativePixmapFactory ("bitmap", "Win32 Bitmap", BITMAP_CAPABILITIES) 188 { 189 } 190 191 eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const 192 { 193 const Library& egl = nativeDisplay->getLibrary(); 194 int redBits = 0; 195 int greenBits = 0; 196 int blueBits = 0; 197 int alphaBits = 0; 198 int bitSum = 0; 199 200 DE_ASSERT(display != EGL_NO_DISPLAY); 201 202 egl.getConfigAttrib(display, config, EGL_RED_SIZE, &redBits); 203 egl.getConfigAttrib(display, config, EGL_GREEN_SIZE, &greenBits); 204 egl.getConfigAttrib(display, config, EGL_BLUE_SIZE, &blueBits); 205 egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaBits); 206 EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()"); 207 208 bitSum = redBits+greenBits+blueBits+alphaBits; 209 210 return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, bitSum); 211 } 212 213 eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const 214 { 215 const int defaultDepth = 32; 216 return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, defaultDepth); 217 } 218 219 // NativeWindowFactory 220 221 NativeWindowFactory::NativeWindowFactory (HINSTANCE instance) 222 : eglu::NativeWindowFactory ("window", "Win32 Window", WINDOW_CAPABILITIES) 223 , m_instance (instance) 224 { 225 } 226 227 eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const 228 { 229 return new NativeWindow(dynamic_cast<NativeDisplay*>(nativeDisplay), m_instance, params); 230 } 231 232 // NativeWindow 233 234 NativeWindow::NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params) 235 : eglu::NativeWindow (WINDOW_CAPABILITIES) 236 , m_window (instance, 237 params.width == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH : params.width, 238 params.height == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT : params.height) 239 , m_curVisibility (eglu::WindowParams::VISIBILITY_HIDDEN) 240 , m_setVisibleTime (0) 241 { 242 if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE) 243 setVisibility(params.visibility); 244 } 245 246 void NativeWindow::setVisibility (eglu::WindowParams::Visibility visibility) 247 { 248 switch (visibility) 249 { 250 case eglu::WindowParams::VISIBILITY_HIDDEN: 251 m_window.setVisible(false); 252 m_curVisibility = visibility; 253 break; 254 255 case eglu::WindowParams::VISIBILITY_VISIBLE: 256 case eglu::WindowParams::VISIBILITY_FULLSCREEN: 257 // \todo [2014-03-12 pyry] Implement FULLSCREEN, or at least SW_MAXIMIZE. 258 m_window.setVisible(true); 259 m_curVisibility = eglu::WindowParams::VISIBILITY_VISIBLE; 260 m_setVisibleTime = deGetMicroseconds(); 261 break; 262 263 default: 264 DE_ASSERT(DE_FALSE); 265 } 266 } 267 268 NativeWindow::~NativeWindow (void) 269 { 270 } 271 272 IVec2 NativeWindow::getSurfaceSize (void) const 273 { 274 return m_window.getSize(); 275 } 276 277 void NativeWindow::processEvents (void) 278 { 279 m_window.processEvents(); 280 } 281 282 void NativeWindow::setSurfaceSize (IVec2 size) 283 { 284 m_window.setSize(size.x(), size.y()); 285 } 286 287 void NativeWindow::readScreenPixels (tcu::TextureLevel* dst) const 288 { 289 HDC windowDC = DE_NULL; 290 HDC screenDC = DE_NULL; 291 HDC tmpDC = DE_NULL; 292 HBITMAP tmpBitmap = DE_NULL; 293 RECT rect; 294 295 TCU_CHECK_INTERNAL(m_curVisibility != eglu::WindowParams::VISIBILITY_HIDDEN); 296 297 // Hack for DWM: There is no way to wait for DWM animations to finish, so we just have to wait 298 // for a while before issuing screenshot if window was just made visible. 299 { 300 const deInt64 timeSinceVisibleUs = (deInt64)(deGetMicroseconds()-m_setVisibleTime); 301 302 if (timeSinceVisibleUs < (deInt64)WAIT_WINDOW_VISIBLE_MS*1000) 303 deSleep(WAIT_WINDOW_VISIBLE_MS - (deUint32)(timeSinceVisibleUs/1000)); 304 } 305 306 TCU_CHECK(GetClientRect(m_window.getHandle(), &rect)); 307 308 try 309 { 310 const int width = rect.right - rect.left; 311 const int height = rect.bottom - rect.top; 312 BITMAPINFOHEADER bitmapInfo; 313 314 deMemset(&bitmapInfo, 0, sizeof(bitmapInfo)); 315 316 screenDC = GetDC(DE_NULL); 317 TCU_CHECK(screenDC); 318 319 windowDC = GetDC(m_window.getHandle()); 320 TCU_CHECK(windowDC); 321 322 tmpDC = CreateCompatibleDC(screenDC); 323 TCU_CHECK(tmpDC != DE_NULL); 324 325 MapWindowPoints(m_window.getHandle(), DE_NULL, (LPPOINT)&rect, 2); 326 327 tmpBitmap = CreateCompatibleBitmap(screenDC, width, height); 328 TCU_CHECK(tmpBitmap != DE_NULL); 329 330 TCU_CHECK(SelectObject(tmpDC, tmpBitmap) != DE_NULL); 331 332 TCU_CHECK(BitBlt(tmpDC, 0, 0, width, height, screenDC, rect.left, rect.top, SRCCOPY)); 333 334 335 bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); 336 bitmapInfo.biWidth = width; 337 bitmapInfo.biHeight = -height; 338 bitmapInfo.biPlanes = 1; 339 bitmapInfo.biBitCount = 32; 340 bitmapInfo.biCompression = BI_RGB; 341 bitmapInfo.biSizeImage = 0; 342 bitmapInfo.biXPelsPerMeter = 0; 343 bitmapInfo.biYPelsPerMeter = 0; 344 bitmapInfo.biClrUsed = 0; 345 bitmapInfo.biClrImportant = 0; 346 347 dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), width, height); 348 349 TCU_CHECK(GetDIBits(screenDC, tmpBitmap, 0, height, dst->getAccess().getDataPtr(), (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS)); 350 351 DeleteObject(tmpBitmap); 352 tmpBitmap = DE_NULL; 353 354 ReleaseDC(DE_NULL, screenDC); 355 screenDC = DE_NULL; 356 357 ReleaseDC(m_window.getHandle(), windowDC); 358 windowDC = DE_NULL; 359 360 DeleteDC(tmpDC); 361 tmpDC = DE_NULL; 362 } 363 catch (...) 364 { 365 if (screenDC) 366 ReleaseDC(DE_NULL, screenDC); 367 368 if (windowDC) 369 ReleaseDC(m_window.getHandle(), windowDC); 370 371 if (tmpBitmap) 372 DeleteObject(tmpBitmap); 373 374 if (tmpDC) 375 DeleteDC(tmpDC); 376 377 throw; 378 } 379 } 380 381 } // anonymous 382 383 EGLNativeDisplayFactory::EGLNativeDisplayFactory (HINSTANCE instance) 384 : eglu::NativeDisplayFactory ("win32", "Native Win32 Display", DISPLAY_CAPABILITIES) 385 , m_instance (instance) 386 { 387 m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(m_instance)); 388 m_nativePixmapRegistry.registerFactory(new NativePixmapFactory()); 389 } 390 391 EGLNativeDisplayFactory::~EGLNativeDisplayFactory (void) 392 { 393 } 394 395 eglu::NativeDisplay* EGLNativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const 396 { 397 DE_UNREF(attribList); 398 return new NativeDisplay(); 399 } 400 401 } // win32 402 } // tcu 403