1 //
2 // Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // SurfaceD3D.cpp: D3D implementation of an EGL surface
8
9 #include "libANGLE/renderer/d3d/SurfaceD3D.h"
10
11 #include "libANGLE/Context.h"
12 #include "libANGLE/Display.h"
13 #include "libANGLE/Surface.h"
14 #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
15 #include "libANGLE/renderer/d3d/RendererD3D.h"
16 #include "libANGLE/renderer/d3d/SwapChainD3D.h"
17
18 #include <tchar.h>
19 #include <EGL/eglext.h>
20 #include <algorithm>
21
22 namespace rx
23 {
24
SurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLNativeWindowType window,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)25 SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state,
26 RendererD3D *renderer,
27 egl::Display *display,
28 EGLNativeWindowType window,
29 EGLenum buftype,
30 EGLClientBuffer clientBuffer,
31 const egl::AttributeMap &attribs)
32 : SurfaceImpl(state),
33 mRenderer(renderer),
34 mDisplay(display),
35 mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE),
36 mOrientation(static_cast<EGLint>(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))),
37 mRenderTargetFormat(state.config->renderTargetFormat),
38 mDepthStencilFormat(state.config->depthStencilFormat),
39 mSwapChain(nullptr),
40 mSwapIntervalDirty(true),
41 mWindowSubclassed(false),
42 mNativeWindow(renderer->createNativeWindow(window, state.config, attribs)),
43 mWidth(static_cast<EGLint>(attribs.get(EGL_WIDTH, 0))),
44 mHeight(static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0))),
45 mSwapInterval(1),
46 mShareHandle(0),
47 mD3DTexture(nullptr)
48 {
49 subclassWindow();
50 if (window != nullptr && !mFixedSize)
51 {
52 mWidth = -1;
53 mHeight = -1;
54 }
55
56 switch (buftype)
57 {
58 case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
59 mShareHandle = static_cast<HANDLE>(clientBuffer);
60 break;
61
62 case EGL_D3D_TEXTURE_ANGLE:
63 mD3DTexture = static_cast<IUnknown *>(clientBuffer);
64 ASSERT(mD3DTexture != nullptr);
65 mD3DTexture->AddRef();
66 ANGLE_SWALLOW_ERR(mRenderer->getD3DTextureInfo(state.config, mD3DTexture, &mWidth,
67 &mHeight, &mRenderTargetFormat));
68 break;
69
70 default:
71 break;
72 }
73 }
74
~SurfaceD3D()75 SurfaceD3D::~SurfaceD3D()
76 {
77 unsubclassWindow();
78 releaseSwapChain();
79 SafeDelete(mNativeWindow);
80 SafeRelease(mD3DTexture);
81 }
82
releaseSwapChain()83 void SurfaceD3D::releaseSwapChain()
84 {
85 SafeDelete(mSwapChain);
86 }
87
initialize(const egl::Display * display)88 egl::Error SurfaceD3D::initialize(const egl::Display *display)
89 {
90 if (mNativeWindow->getNativeWindow())
91 {
92 if (!mNativeWindow->initialize())
93 {
94 return egl::EglBadSurface();
95 }
96 }
97
98 ANGLE_TRY(resetSwapChain(display));
99 return egl::NoError();
100 }
101
createDefaultFramebuffer(const gl::FramebufferState & data)102 FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::FramebufferState &data)
103 {
104 return mRenderer->createDefaultFramebuffer(data);
105 }
106
bindTexImage(gl::Texture *,EGLint)107 egl::Error SurfaceD3D::bindTexImage(gl::Texture *, EGLint)
108 {
109 return egl::NoError();
110 }
111
releaseTexImage(EGLint)112 egl::Error SurfaceD3D::releaseTexImage(EGLint)
113 {
114 return egl::NoError();
115 }
116
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)117 egl::Error SurfaceD3D::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
118 {
119 return mSwapChain->getSyncValues(ust, msc, sbc);
120 }
121
resetSwapChain(const egl::Display * display)122 egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display)
123 {
124 ASSERT(!mSwapChain);
125
126 int width;
127 int height;
128
129 if (!mFixedSize)
130 {
131 RECT windowRect;
132 if (!mNativeWindow->getClientRect(&windowRect))
133 {
134 ASSERT(false);
135
136 return egl::EglBadSurface() << "Could not retrieve the window dimensions";
137 }
138
139 width = windowRect.right - windowRect.left;
140 height = windowRect.bottom - windowRect.top;
141 }
142 else
143 {
144 // non-window surface - size is determined at creation
145 width = mWidth;
146 height = mHeight;
147 }
148
149 mSwapChain =
150 mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat,
151 mDepthStencilFormat, mOrientation, mState.config->samples);
152 if (!mSwapChain)
153 {
154 return egl::EglBadAlloc();
155 }
156
157 // This is a bit risky to pass the proxy context here, but it can happen at almost any time.
158 egl::Error error = resetSwapChain(display->getProxyContext(), width, height);
159 if (error.isError())
160 {
161 SafeDelete(mSwapChain);
162 return error;
163 }
164
165 return egl::NoError();
166 }
167
resizeSwapChain(const gl::Context * context,int backbufferWidth,int backbufferHeight)168 egl::Error SurfaceD3D::resizeSwapChain(const gl::Context *context,
169 int backbufferWidth,
170 int backbufferHeight)
171 {
172 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
173 ASSERT(mSwapChain);
174
175 EGLint status =
176 mSwapChain->resize(context, std::max(1, backbufferWidth), std::max(1, backbufferHeight));
177
178 if (status == EGL_CONTEXT_LOST)
179 {
180 mDisplay->notifyDeviceLost();
181 return egl::Error(status);
182 }
183 else if (status != EGL_SUCCESS)
184 {
185 return egl::Error(status);
186 }
187
188 mWidth = backbufferWidth;
189 mHeight = backbufferHeight;
190
191 return egl::NoError();
192 }
193
resetSwapChain(const gl::Context * context,int backbufferWidth,int backbufferHeight)194 egl::Error SurfaceD3D::resetSwapChain(const gl::Context *context,
195 int backbufferWidth,
196 int backbufferHeight)
197 {
198 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
199 ASSERT(mSwapChain);
200
201 EGLint status = mSwapChain->reset(context, std::max(1, backbufferWidth),
202 std::max(1, backbufferHeight), mSwapInterval);
203
204 if (status == EGL_CONTEXT_LOST)
205 {
206 mRenderer->notifyDeviceLost();
207 return egl::Error(status);
208 }
209 else if (status != EGL_SUCCESS)
210 {
211 return egl::Error(status);
212 }
213
214 mWidth = backbufferWidth;
215 mHeight = backbufferHeight;
216 mSwapIntervalDirty = false;
217
218 return egl::NoError();
219 }
220
swapRect(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)221 egl::Error SurfaceD3D::swapRect(const gl::Context *context,
222 EGLint x,
223 EGLint y,
224 EGLint width,
225 EGLint height)
226 {
227 if (!mSwapChain)
228 {
229 return egl::NoError();
230 }
231
232 if (x + width > mWidth)
233 {
234 width = mWidth - x;
235 }
236
237 if (y + height > mHeight)
238 {
239 height = mHeight - y;
240 }
241
242 if (width != 0 && height != 0)
243 {
244 EGLint status = mSwapChain->swapRect(context, x, y, width, height);
245
246 if (status == EGL_CONTEXT_LOST)
247 {
248 mRenderer->notifyDeviceLost();
249 return egl::Error(status);
250 }
251 else if (status != EGL_SUCCESS)
252 {
253 return egl::Error(status);
254 }
255 }
256
257 ANGLE_TRY(checkForOutOfDateSwapChain(context));
258
259 return egl::NoError();
260 }
261
262 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
263 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
264 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
265 #define kDisplayProperty _TEXT("Egl::Display")
266
SurfaceWindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)267 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
268 {
269 if (message == WM_SIZE)
270 {
271 SurfaceD3D* surf = reinterpret_cast<SurfaceD3D*>(GetProp(hwnd, kSurfaceProperty));
272 if(surf)
273 {
274 egl::Display *display = reinterpret_cast<egl::Display *>(GetProp(hwnd, kDisplayProperty));
275 surf->checkForOutOfDateSwapChain(display->getProxyContext());
276 }
277 }
278 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
279 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
280 }
281 #endif
282
subclassWindow()283 void SurfaceD3D::subclassWindow()
284 {
285 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
286 HWND window = mNativeWindow->getNativeWindow();
287 if (!window)
288 {
289 return;
290 }
291
292 DWORD processId;
293 DWORD threadId = GetWindowThreadProcessId(window, &processId);
294 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
295 {
296 return;
297 }
298
299 SetLastError(0);
300 LONG_PTR oldWndProc = SetWindowLongPtr(window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
301 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
302 {
303 mWindowSubclassed = false;
304 return;
305 }
306
307 SetProp(window, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
308 SetProp(window, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
309 SetProp(window, kDisplayProperty, reinterpret_cast<HANDLE>(mDisplay));
310 mWindowSubclassed = true;
311 #endif
312 }
313
unsubclassWindow()314 void SurfaceD3D::unsubclassWindow()
315 {
316 if (!mWindowSubclassed)
317 {
318 return;
319 }
320
321 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
322 HWND window = mNativeWindow->getNativeWindow();
323 if (!window)
324 {
325 return;
326 }
327
328 // un-subclass
329 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(window, kParentWndProc));
330
331 // Check the windowproc is still SurfaceWindowProc.
332 // If this assert fails, then it is likely the application has subclassed the
333 // hwnd as well and did not unsubclass before destroying its EGL context. The
334 // application should be modified to either subclass before initializing the
335 // EGL context, or to unsubclass before destroying the EGL context.
336 if(parentWndFunc)
337 {
338 LONG_PTR prevWndFunc = SetWindowLongPtr(window, GWLP_WNDPROC, parentWndFunc);
339 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
340 }
341
342 RemoveProp(window, kSurfaceProperty);
343 RemoveProp(window, kParentWndProc);
344 RemoveProp(window, kDisplayProperty);
345 #endif
346 mWindowSubclassed = false;
347 }
348
349
checkForOutOfDateSwapChain(const gl::Context * context)350 egl::Error SurfaceD3D::checkForOutOfDateSwapChain(const gl::Context *context)
351 {
352 RECT client;
353 int clientWidth = getWidth();
354 int clientHeight = getHeight();
355 bool sizeDirty = false;
356 if (!mFixedSize && !mNativeWindow->isIconic())
357 {
358 // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized
359 // because that's not a useful size to render to.
360 if (!mNativeWindow->getClientRect(&client))
361 {
362 UNREACHABLE();
363 return egl::NoError();
364 }
365
366 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
367 clientWidth = client.right - client.left;
368 clientHeight = client.bottom - client.top;
369 sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
370 }
371
372 if (mSwapIntervalDirty)
373 {
374 ANGLE_TRY(resetSwapChain(context, clientWidth, clientHeight));
375 }
376 else if (sizeDirty)
377 {
378 ANGLE_TRY(resizeSwapChain(context, clientWidth, clientHeight));
379 }
380
381 return egl::NoError();
382 }
383
swap(const gl::Context * context)384 egl::Error SurfaceD3D::swap(const gl::Context *context)
385 {
386 return swapRect(context, 0, 0, mWidth, mHeight);
387 }
388
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)389 egl::Error SurfaceD3D::postSubBuffer(const gl::Context *context,
390 EGLint x,
391 EGLint y,
392 EGLint width,
393 EGLint height)
394 {
395 return swapRect(context, x, y, width, height);
396 }
397
getSwapChain() const398 rx::SwapChainD3D *SurfaceD3D::getSwapChain() const
399 {
400 return mSwapChain;
401 }
402
setSwapInterval(EGLint interval)403 void SurfaceD3D::setSwapInterval(EGLint interval)
404 {
405 if (mSwapInterval == interval)
406 {
407 return;
408 }
409
410 mSwapInterval = interval;
411 mSwapIntervalDirty = true;
412 }
413
getWidth() const414 EGLint SurfaceD3D::getWidth() const
415 {
416 return mWidth;
417 }
418
getHeight() const419 EGLint SurfaceD3D::getHeight() const
420 {
421 return mHeight;
422 }
423
isPostSubBufferSupported() const424 EGLint SurfaceD3D::isPostSubBufferSupported() const
425 {
426 // post sub buffer is always possible on D3D surfaces
427 return EGL_TRUE;
428 }
429
getSwapBehavior() const430 EGLint SurfaceD3D::getSwapBehavior() const
431 {
432 return EGL_BUFFER_PRESERVED;
433 }
434
querySurfacePointerANGLE(EGLint attribute,void ** value)435 egl::Error SurfaceD3D::querySurfacePointerANGLE(EGLint attribute, void **value)
436 {
437 if (attribute == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE)
438 {
439 *value = mSwapChain->getShareHandle();
440 }
441 else if (attribute == EGL_DXGI_KEYED_MUTEX_ANGLE)
442 {
443 *value = mSwapChain->getKeyedMutex();
444 }
445 else if (attribute == EGL_DEVICE_EXT)
446 {
447 *value = mSwapChain->getDevice();
448 }
449 else UNREACHABLE();
450
451 return egl::NoError();
452 }
453
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,FramebufferAttachmentRenderTarget ** rtOut)454 gl::Error SurfaceD3D::getAttachmentRenderTarget(const gl::Context *context,
455 GLenum binding,
456 const gl::ImageIndex &imageIndex,
457 FramebufferAttachmentRenderTarget **rtOut)
458 {
459 if (binding == GL_BACK)
460 {
461 *rtOut = mSwapChain->getColorRenderTarget();
462 }
463 else
464 {
465 *rtOut = mSwapChain->getDepthStencilRenderTarget();
466 }
467 return gl::NoError();
468 }
469
WindowSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLNativeWindowType window,const egl::AttributeMap & attribs)470 WindowSurfaceD3D::WindowSurfaceD3D(const egl::SurfaceState &state,
471 RendererD3D *renderer,
472 egl::Display *display,
473 EGLNativeWindowType window,
474 const egl::AttributeMap &attribs)
475 : SurfaceD3D(state,
476 renderer,
477 display,
478 window,
479 0,
480 static_cast<EGLClientBuffer>(0),
481 attribs)
482 {
483 }
484
~WindowSurfaceD3D()485 WindowSurfaceD3D::~WindowSurfaceD3D()
486 {
487 }
488
PbufferSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)489 PbufferSurfaceD3D::PbufferSurfaceD3D(const egl::SurfaceState &state,
490 RendererD3D *renderer,
491 egl::Display *display,
492 EGLenum buftype,
493 EGLClientBuffer clientBuffer,
494 const egl::AttributeMap &attribs)
495 : SurfaceD3D(state,
496 renderer,
497 display,
498 static_cast<EGLNativeWindowType>(0),
499 buftype,
500 clientBuffer,
501 attribs)
502 {
503 }
504
~PbufferSurfaceD3D()505 PbufferSurfaceD3D::~PbufferSurfaceD3D()
506 {
507 }
508
509 } // namespace rc
510