blob: 4e313bfa00a1724bdec4a9a36f5633dfe57f7d53 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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;
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 &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(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