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