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