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 =
55 500 //!< Time to wait before issuing screenshot after changing window visibility (hack for DWM)
56 };
57
58 static const eglu::NativeDisplay::Capability DISPLAY_CAPABILITIES = eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY;
59 static const eglu::NativePixmap::Capability BITMAP_CAPABILITIES = eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY;
60 static const eglu::NativeWindow::Capability WINDOW_CAPABILITIES = (eglu::NativeWindow::Capability)(
61 eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY | eglu::NativeWindow::CAPABILITY_GET_SURFACE_SIZE |
62 eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE | eglu::NativeWindow::CAPABILITY_READ_SCREEN_PIXELS |
63 eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE | eglu::NativeWindow::CAPABILITY_CHANGE_VISIBILITY);
64
65 class NativeDisplay : public eglu::NativeDisplay
66 {
67 public:
68 NativeDisplay(void);
~NativeDisplay(void)69 virtual ~NativeDisplay(void)
70 {
71 }
72
getLegacyNative(void)73 virtual EGLNativeDisplayType getLegacyNative(void)
74 {
75 return m_deviceContext;
76 }
getLibrary(void) const77 const eglw::Library &getLibrary(void) const
78 {
79 return m_library;
80 }
81
getDeviceContext(void)82 HDC getDeviceContext(void)
83 {
84 return m_deviceContext;
85 }
86
87 private:
88 HDC m_deviceContext;
89 eglw::DefaultLibrary m_library;
90 };
91
92 class NativePixmapFactory : public eglu::NativePixmapFactory
93 {
94 public:
95 NativePixmapFactory(void);
~NativePixmapFactory(void)96 ~NativePixmapFactory(void)
97 {
98 }
99
100 virtual eglu::NativePixmap *createPixmap(eglu::NativeDisplay *nativeDisplay, int width, int height) const;
101 virtual eglu::NativePixmap *createPixmap(eglu::NativeDisplay *nativeDisplay, EGLDisplay display, EGLConfig config,
102 const EGLAttrib *attribList, int width, int height) const;
103 };
104
105 class NativePixmap : public eglu::NativePixmap
106 {
107 public:
108 NativePixmap(NativeDisplay *nativeDisplay, int width, int height, int bitDepth);
109 virtual ~NativePixmap(void);
110
getLegacyNative(void)111 EGLNativePixmapType getLegacyNative(void)
112 {
113 return m_bitmap;
114 }
115
116 private:
117 HBITMAP m_bitmap;
118 };
119
120 class NativeWindowFactory : public eglu::NativeWindowFactory
121 {
122 public:
123 NativeWindowFactory(HINSTANCE instance);
~NativeWindowFactory(void)124 virtual ~NativeWindowFactory(void)
125 {
126 }
127
128 virtual eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay,
129 const eglu::WindowParams ¶ms) const;
130
131 private:
132 const HINSTANCE m_instance;
133 };
134
135 class NativeWindow : public eglu::NativeWindow
136 {
137 public:
138 NativeWindow(NativeDisplay *nativeDisplay, HINSTANCE instance, const eglu::WindowParams ¶ms);
139 virtual ~NativeWindow(void);
140
getLegacyNative(void)141 EGLNativeWindowType getLegacyNative(void)
142 {
143 return m_window.getHandle();
144 }
145 virtual IVec2 getSurfaceSize(void) const;
getScreenSize(void) const146 virtual IVec2 getScreenSize(void) const
147 {
148 return getSurfaceSize();
149 }
150 virtual void processEvents(void);
151 virtual void setSurfaceSize(IVec2 size);
152 virtual void setVisibility(eglu::WindowParams::Visibility visibility);
153 virtual void readScreenPixels(tcu::TextureLevel *dst) const;
154
155 private:
156 win32::Window m_window;
157 eglu::WindowParams::Visibility m_curVisibility;
158 uint64_t m_setVisibleTime; //!< Time window was set visible.
159 };
160
161 // NativeDisplay
162
NativeDisplay(void)163 NativeDisplay::NativeDisplay(void)
164 : eglu::NativeDisplay(DISPLAY_CAPABILITIES)
165 , m_deviceContext((HDC)EGL_DEFAULT_DISPLAY)
166 , m_library("libEGL.dll")
167 {
168 }
169
170 // NativePixmap
171
NativePixmap(NativeDisplay * nativeDisplay,int width,int height,int bitDepth)172 NativePixmap::NativePixmap(NativeDisplay *nativeDisplay, int width, int height, int bitDepth)
173 : eglu::NativePixmap(BITMAP_CAPABILITIES)
174 , m_bitmap(DE_NULL)
175 {
176 const HDC deviceCtx = nativeDisplay->getDeviceContext();
177 BITMAPINFO bitmapInfo;
178
179 memset(&bitmapInfo, 0, sizeof(bitmapInfo));
180
181 if (bitDepth != 24 && bitDepth != 32)
182 throw NotSupportedError("Unsupported pixmap bit depth", DE_NULL, __FILE__, __LINE__);
183
184 bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo);
185 bitmapInfo.bmiHeader.biWidth = width;
186 bitmapInfo.bmiHeader.biHeight = height;
187 bitmapInfo.bmiHeader.biPlanes = 1;
188 bitmapInfo.bmiHeader.biBitCount = bitDepth;
189 bitmapInfo.bmiHeader.biCompression = BI_RGB;
190 bitmapInfo.bmiHeader.biSizeImage = 0;
191 bitmapInfo.bmiHeader.biXPelsPerMeter = 1;
192 bitmapInfo.bmiHeader.biYPelsPerMeter = 1;
193 bitmapInfo.bmiHeader.biClrUsed = 0;
194 bitmapInfo.bmiHeader.biClrImportant = 0;
195
196 void *bitmapPtr = DE_NULL;
197 m_bitmap = CreateDIBSection(deviceCtx, &bitmapInfo, DIB_RGB_COLORS, &bitmapPtr, NULL, 0);
198
199 if (!m_bitmap)
200 throw ResourceError("Failed to create bitmap", DE_NULL, __FILE__, __LINE__);
201 }
202
~NativePixmap(void)203 NativePixmap::~NativePixmap(void)
204 {
205 DeleteObject(m_bitmap);
206 }
207
208 // NativePixmapFactory
209
NativePixmapFactory(void)210 NativePixmapFactory::NativePixmapFactory(void)
211 : eglu::NativePixmapFactory("bitmap", "Win32 Bitmap", BITMAP_CAPABILITIES)
212 {
213 }
214
createPixmap(eglu::NativeDisplay * nativeDisplay,EGLDisplay display,EGLConfig config,const EGLAttrib * attribList,int width,int height) const215 eglu::NativePixmap *NativePixmapFactory::createPixmap(eglu::NativeDisplay *nativeDisplay, EGLDisplay display,
216 EGLConfig config, const EGLAttrib *attribList, int width,
217 int height) const
218 {
219 const Library &egl = nativeDisplay->getLibrary();
220 int redBits = 0;
221 int greenBits = 0;
222 int blueBits = 0;
223 int alphaBits = 0;
224 int bitSum = 0;
225
226 DE_ASSERT(display != EGL_NO_DISPLAY);
227
228 egl.getConfigAttrib(display, config, EGL_RED_SIZE, &redBits);
229 egl.getConfigAttrib(display, config, EGL_GREEN_SIZE, &greenBits);
230 egl.getConfigAttrib(display, config, EGL_BLUE_SIZE, &blueBits);
231 egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaBits);
232 EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()");
233
234 bitSum = redBits + greenBits + blueBits + alphaBits;
235
236 return new NativePixmap(dynamic_cast<NativeDisplay *>(nativeDisplay), width, height, bitSum);
237 }
238
createPixmap(eglu::NativeDisplay * nativeDisplay,int width,int height) const239 eglu::NativePixmap *NativePixmapFactory::createPixmap(eglu::NativeDisplay *nativeDisplay, int width, int height) const
240 {
241 const int defaultDepth = 32;
242 return new NativePixmap(dynamic_cast<NativeDisplay *>(nativeDisplay), width, height, defaultDepth);
243 }
244
245 // NativeWindowFactory
246
NativeWindowFactory(HINSTANCE instance)247 NativeWindowFactory::NativeWindowFactory(HINSTANCE instance)
248 : eglu::NativeWindowFactory("window", "Win32 Window", WINDOW_CAPABILITIES)
249 , m_instance(instance)
250 {
251 }
252
createWindow(eglu::NativeDisplay * nativeDisplay,const eglu::WindowParams & params) const253 eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay,
254 const eglu::WindowParams ¶ms) const
255 {
256 return new NativeWindow(dynamic_cast<NativeDisplay *>(nativeDisplay), m_instance, params);
257 }
258
259 // NativeWindow
260
NativeWindow(NativeDisplay * nativeDisplay,HINSTANCE instance,const eglu::WindowParams & params)261 NativeWindow::NativeWindow(NativeDisplay *nativeDisplay, HINSTANCE instance, const eglu::WindowParams ¶ms)
262 : eglu::NativeWindow(WINDOW_CAPABILITIES)
263 , m_window(instance, params.width == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH : params.width,
264 params.height == eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT : params.height)
265 , m_curVisibility(eglu::WindowParams::VISIBILITY_HIDDEN)
266 , m_setVisibleTime(0)
267 {
268 if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE)
269 setVisibility(params.visibility);
270 }
271
setVisibility(eglu::WindowParams::Visibility visibility)272 void NativeWindow::setVisibility(eglu::WindowParams::Visibility visibility)
273 {
274 switch (visibility)
275 {
276 case eglu::WindowParams::VISIBILITY_HIDDEN:
277 m_window.setVisible(false);
278 m_curVisibility = visibility;
279 break;
280
281 case eglu::WindowParams::VISIBILITY_VISIBLE:
282 case eglu::WindowParams::VISIBILITY_FULLSCREEN:
283 // \todo [2014-03-12 pyry] Implement FULLSCREEN, or at least SW_MAXIMIZE.
284 m_window.setVisible(true);
285 m_curVisibility = eglu::WindowParams::VISIBILITY_VISIBLE;
286 m_setVisibleTime = deGetMicroseconds();
287 break;
288
289 default:
290 DE_ASSERT(false);
291 }
292 }
293
~NativeWindow(void)294 NativeWindow::~NativeWindow(void)
295 {
296 }
297
getSurfaceSize(void) const298 IVec2 NativeWindow::getSurfaceSize(void) const
299 {
300 return m_window.getSize();
301 }
302
processEvents(void)303 void NativeWindow::processEvents(void)
304 {
305 m_window.processEvents();
306 }
307
setSurfaceSize(IVec2 size)308 void NativeWindow::setSurfaceSize(IVec2 size)
309 {
310 m_window.setSize(size.x(), size.y());
311 }
312
readScreenPixels(tcu::TextureLevel * dst) const313 void NativeWindow::readScreenPixels(tcu::TextureLevel *dst) const
314 {
315 HDC windowDC = DE_NULL;
316 HDC screenDC = DE_NULL;
317 HDC tmpDC = DE_NULL;
318 HBITMAP tmpBitmap = DE_NULL;
319 RECT rect;
320
321 TCU_CHECK_INTERNAL(m_curVisibility != eglu::WindowParams::VISIBILITY_HIDDEN);
322
323 // Hack for DWM: There is no way to wait for DWM animations to finish, so we just have to wait
324 // for a while before issuing screenshot if window was just made visible.
325 {
326 const int64_t timeSinceVisibleUs = (int64_t)(deGetMicroseconds() - m_setVisibleTime);
327
328 if (timeSinceVisibleUs < (int64_t)WAIT_WINDOW_VISIBLE_MS * 1000)
329 deSleep(WAIT_WINDOW_VISIBLE_MS - (uint32_t)(timeSinceVisibleUs / 1000));
330 }
331
332 TCU_CHECK(GetClientRect(m_window.getHandle(), &rect));
333
334 try
335 {
336 const int width = rect.right - rect.left;
337 const int height = rect.bottom - rect.top;
338 BITMAPINFOHEADER bitmapInfo;
339
340 deMemset(&bitmapInfo, 0, sizeof(bitmapInfo));
341
342 screenDC = GetDC(DE_NULL);
343 TCU_CHECK(screenDC);
344
345 windowDC = GetDC(m_window.getHandle());
346 TCU_CHECK(windowDC);
347
348 tmpDC = CreateCompatibleDC(screenDC);
349 TCU_CHECK(tmpDC != DE_NULL);
350
351 MapWindowPoints(m_window.getHandle(), DE_NULL, (LPPOINT)&rect, 2);
352
353 tmpBitmap = CreateCompatibleBitmap(screenDC, width, height);
354 TCU_CHECK(tmpBitmap != DE_NULL);
355
356 TCU_CHECK(SelectObject(tmpDC, tmpBitmap) != DE_NULL);
357
358 TCU_CHECK(BitBlt(tmpDC, 0, 0, width, height, screenDC, rect.left, rect.top, SRCCOPY));
359
360 bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
361 bitmapInfo.biWidth = width;
362 bitmapInfo.biHeight = -height;
363 bitmapInfo.biPlanes = 1;
364 bitmapInfo.biBitCount = 32;
365 bitmapInfo.biCompression = BI_RGB;
366 bitmapInfo.biSizeImage = 0;
367 bitmapInfo.biXPelsPerMeter = 0;
368 bitmapInfo.biYPelsPerMeter = 0;
369 bitmapInfo.biClrUsed = 0;
370 bitmapInfo.biClrImportant = 0;
371
372 dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), width, height);
373
374 TCU_CHECK(GetDIBits(screenDC, tmpBitmap, 0, height, dst->getAccess().getDataPtr(), (BITMAPINFO *)&bitmapInfo,
375 DIB_RGB_COLORS));
376
377 DeleteObject(tmpBitmap);
378 tmpBitmap = DE_NULL;
379
380 ReleaseDC(DE_NULL, screenDC);
381 screenDC = DE_NULL;
382
383 ReleaseDC(m_window.getHandle(), windowDC);
384 windowDC = DE_NULL;
385
386 DeleteDC(tmpDC);
387 tmpDC = DE_NULL;
388 }
389 catch (...)
390 {
391 if (screenDC)
392 ReleaseDC(DE_NULL, screenDC);
393
394 if (windowDC)
395 ReleaseDC(m_window.getHandle(), windowDC);
396
397 if (tmpBitmap)
398 DeleteObject(tmpBitmap);
399
400 if (tmpDC)
401 DeleteDC(tmpDC);
402
403 throw;
404 }
405 }
406
407 } // namespace
408
EGLNativeDisplayFactory(HINSTANCE instance)409 EGLNativeDisplayFactory::EGLNativeDisplayFactory(HINSTANCE instance)
410 : eglu::NativeDisplayFactory("win32", "Native Win32 Display", DISPLAY_CAPABILITIES)
411 , m_instance(instance)
412 {
413 m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(m_instance));
414 m_nativePixmapRegistry.registerFactory(new NativePixmapFactory());
415 }
416
~EGLNativeDisplayFactory(void)417 EGLNativeDisplayFactory::~EGLNativeDisplayFactory(void)
418 {
419 }
420
createDisplay(const EGLAttrib * attribList) const421 eglu::NativeDisplay *EGLNativeDisplayFactory::createDisplay(const EGLAttrib *attribList) const
422 {
423 DE_UNREF(attribList);
424 return new NativeDisplay();
425 }
426
427 } // namespace win32
428 } // namespace tcu
429