• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2015 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 // DXGISwapChainWindowSurfaceWGL.cpp: WGL implementation of egl::Surface for windows using a DXGI
8 // swapchain.
9 
10 #include "libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h"
11 
12 #include "libANGLE/formatutils.h"
13 #include "libANGLE/renderer/gl/FramebufferGL.h"
14 #include "libANGLE/renderer/gl/RendererGL.h"
15 #include "libANGLE/renderer/gl/StateManagerGL.h"
16 #include "libANGLE/renderer/gl/TextureGL.h"
17 #include "libANGLE/renderer/gl/wgl/DisplayWGL.h"
18 #include "libANGLE/renderer/gl/wgl/FunctionsWGL.h"
19 
20 #include <EGL/eglext.h>
21 
22 namespace rx
23 {
24 
DXGISwapChainWindowSurfaceWGL(const egl::SurfaceState & state,StateManagerGL * stateManager,EGLNativeWindowType window,ID3D11Device * device,HANDLE deviceHandle,HDC deviceContext,const FunctionsGL * functionsGL,const FunctionsWGL * functionsWGL,EGLint orientation)25 DXGISwapChainWindowSurfaceWGL::DXGISwapChainWindowSurfaceWGL(const egl::SurfaceState &state,
26                                                              StateManagerGL *stateManager,
27                                                              EGLNativeWindowType window,
28                                                              ID3D11Device *device,
29                                                              HANDLE deviceHandle,
30                                                              HDC deviceContext,
31                                                              const FunctionsGL *functionsGL,
32                                                              const FunctionsWGL *functionsWGL,
33                                                              EGLint orientation)
34     : SurfaceWGL(state),
35       mWindow(window),
36       mStateManager(stateManager),
37       mFunctionsGL(functionsGL),
38       mFunctionsWGL(functionsWGL),
39       mDevice(device),
40       mDeviceHandle(deviceHandle),
41       mWGLDevice(deviceContext),
42       mSwapChainFormat(DXGI_FORMAT_UNKNOWN),
43       mSwapChainFlags(0),
44       mDepthBufferFormat(GL_NONE),
45       mFirstSwap(true),
46       mSwapChain(nullptr),
47       mSwapChain1(nullptr),
48       mColorRenderbufferID(0),
49       mRenderbufferBufferHandle(nullptr),
50       mDepthRenderbufferID(0),
51       mTextureID(0),
52       mTextureHandle(nullptr),
53       mWidth(0),
54       mHeight(0),
55       mSwapInterval(1),
56       mOrientation(orientation)
57 {}
58 
~DXGISwapChainWindowSurfaceWGL()59 DXGISwapChainWindowSurfaceWGL::~DXGISwapChainWindowSurfaceWGL()
60 {
61     if (mRenderbufferBufferHandle != nullptr)
62     {
63         mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mRenderbufferBufferHandle);
64         mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle);
65     }
66 
67     if (mColorRenderbufferID != 0)
68     {
69         mStateManager->deleteRenderbuffer(mColorRenderbufferID);
70         mColorRenderbufferID = 0;
71     }
72 
73     if (mDepthRenderbufferID != 0)
74     {
75         mStateManager->deleteRenderbuffer(mDepthRenderbufferID);
76         mDepthRenderbufferID = 0;
77     }
78 
79     SafeRelease(mSwapChain);
80     SafeRelease(mSwapChain1);
81 }
82 
initialize(const egl::Display * display)83 egl::Error DXGISwapChainWindowSurfaceWGL::initialize(const egl::Display *display)
84 {
85     if (mOrientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE)
86     {
87         // TODO(geofflang): Support the orientation extensions fully.  Currently only inverting Y is
88         // supported.  To support all orientations, an intermediate framebuffer will be needed with
89         // a blit before swap.
90         return egl::EglBadAttribute() << "DXGISwapChainWindowSurfaceWGL requires an orientation of "
91                                          "EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE.";
92     }
93 
94     RECT rect;
95     if (!GetClientRect(mWindow, &rect))
96     {
97         return egl::EglBadNativeWindow() << "Failed to query the window size.";
98     }
99     mWidth  = rect.right - rect.left;
100     mHeight = rect.bottom - rect.top;
101 
102     mSwapChainFormat   = DXGI_FORMAT_R8G8B8A8_UNORM;
103     mSwapChainFlags    = 0;
104     mDepthBufferFormat = GL_DEPTH24_STENCIL8;
105 
106     mFunctionsGL->genRenderbuffers(1, &mColorRenderbufferID);
107     mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbufferID);
108 
109     mFunctionsGL->genRenderbuffers(1, &mDepthRenderbufferID);
110     mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbufferID);
111 
112     return createSwapChain();
113 }
114 
makeCurrent(const gl::Context * context)115 egl::Error DXGISwapChainWindowSurfaceWGL::makeCurrent(const gl::Context *context)
116 {
117     return egl::NoError();
118 }
119 
swap(const gl::Context * context)120 egl::Error DXGISwapChainWindowSurfaceWGL::swap(const gl::Context *context)
121 {
122     mFunctionsGL->flush();
123 
124     ANGLE_TRY(setObjectsLocked(false));
125 
126     HRESULT result = mSwapChain->Present(mSwapInterval, 0);
127     mFirstSwap     = false;
128 
129     ANGLE_TRY(setObjectsLocked(true));
130 
131     if (FAILED(result))
132     {
133         return egl::EglBadAlloc() << "Failed to present swap chain, " << gl::FmtHR(result);
134     }
135 
136     return checkForResize();
137 }
138 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)139 egl::Error DXGISwapChainWindowSurfaceWGL::postSubBuffer(const gl::Context *context,
140                                                         EGLint x,
141                                                         EGLint y,
142                                                         EGLint width,
143                                                         EGLint height)
144 {
145     ASSERT(width > 0 && height > 0);
146     ASSERT(mSwapChain1 != nullptr);
147 
148     mFunctionsGL->flush();
149 
150     ANGLE_TRY(setObjectsLocked(false));
151 
152     HRESULT result = S_OK;
153     if (mFirstSwap)
154     {
155         result     = mSwapChain1->Present(mSwapInterval, 0);
156         mFirstSwap = false;
157     }
158     else
159     {
160         RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
161                      static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)};
162         DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr};
163         result                         = mSwapChain1->Present1(mSwapInterval, 0, &params);
164     }
165 
166     ANGLE_TRY(setObjectsLocked(true));
167 
168     if (FAILED(result))
169     {
170         return egl::EglBadAlloc() << "Failed to present swap chain, " << gl::FmtHR(result);
171     }
172 
173     return checkForResize();
174 }
175 
querySurfacePointerANGLE(EGLint attribute,void ** value)176 egl::Error DXGISwapChainWindowSurfaceWGL::querySurfacePointerANGLE(EGLint attribute, void **value)
177 {
178     UNREACHABLE();
179     return egl::NoError();
180 }
181 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)182 egl::Error DXGISwapChainWindowSurfaceWGL::bindTexImage(const gl::Context *context,
183                                                        gl::Texture *texture,
184                                                        EGLint buffer)
185 {
186     ASSERT(mTextureHandle == nullptr);
187 
188     const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
189     GLuint textureID           = textureGL->getTextureID();
190 
191     ID3D11Texture2D *colorBuffer = nullptr;
192     HRESULT result               = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
193                                            reinterpret_cast<void **>(&colorBuffer));
194     if (FAILED(result))
195     {
196         return egl::EglBadAlloc() << "Failed to query texture from swap chain, "
197                                   << gl::FmtHR(result);
198     }
199 
200     mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, textureID,
201                                                        GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV);
202     SafeRelease(colorBuffer);
203     if (mTextureHandle == nullptr)
204     {
205         return egl::EglBadAlloc() << "Failed to register D3D object, "
206                                   << gl::FmtErr(HRESULT_CODE(GetLastError()));
207     }
208 
209     if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, 1, &mTextureHandle))
210     {
211         mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle);
212         mTextureHandle = nullptr;
213 
214         return egl::EglBadAlloc() << "Failed to lock D3D object, "
215                                   << gl::FmtErr(HRESULT_CODE(GetLastError()));
216     }
217 
218     mTextureID = textureID;
219 
220     return egl::NoError();
221 }
222 
releaseTexImage(const gl::Context * context,EGLint buffer)223 egl::Error DXGISwapChainWindowSurfaceWGL::releaseTexImage(const gl::Context *context, EGLint buffer)
224 {
225     ASSERT(mTextureHandle != nullptr);
226 
227     if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, 1, &mTextureHandle))
228     {
229         return egl::EglBadAlloc() << "Failed to unlock D3D object, "
230                                   << gl::FmtErr(HRESULT_CODE(GetLastError()));
231     }
232 
233     if (!mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle))
234     {
235         return egl::EglBadAlloc() << "Failed to unregister D3D object, "
236                                   << gl::FmtErr(HRESULT_CODE(GetLastError()));
237     }
238 
239     mTextureID     = 0;
240     mTextureHandle = nullptr;
241 
242     return egl::NoError();
243 }
244 
setSwapInterval(EGLint interval)245 void DXGISwapChainWindowSurfaceWGL::setSwapInterval(EGLint interval)
246 {
247     mSwapInterval = interval;
248 }
249 
getWidth() const250 EGLint DXGISwapChainWindowSurfaceWGL::getWidth() const
251 {
252     return static_cast<EGLint>(mWidth);
253 }
254 
getHeight() const255 EGLint DXGISwapChainWindowSurfaceWGL::getHeight() const
256 {
257     return static_cast<EGLint>(mHeight);
258 }
259 
isPostSubBufferSupported() const260 EGLint DXGISwapChainWindowSurfaceWGL::isPostSubBufferSupported() const
261 {
262     return mSwapChain1 != nullptr;
263 }
264 
getSwapBehavior() const265 EGLint DXGISwapChainWindowSurfaceWGL::getSwapBehavior() const
266 {
267     return EGL_BUFFER_DESTROYED;
268 }
269 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & data)270 FramebufferImpl *DXGISwapChainWindowSurfaceWGL::createDefaultFramebuffer(
271     const gl::Context *context,
272     const gl::FramebufferState &data)
273 {
274     const FunctionsGL *functions = GetFunctionsGL(context);
275     StateManagerGL *stateManager = GetStateManagerGL(context);
276 
277     GLuint framebufferID = 0;
278     functions->genFramebuffers(1, &framebufferID);
279     stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferID);
280     functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
281                                        mColorRenderbufferID);
282 
283     if (mDepthBufferFormat != GL_NONE)
284     {
285         const gl::InternalFormat &depthStencilFormatInfo =
286             gl::GetSizedInternalFormatInfo(mDepthBufferFormat);
287         if (depthStencilFormatInfo.depthBits > 0)
288         {
289             functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
290                                                mDepthRenderbufferID);
291         }
292         if (depthStencilFormatInfo.stencilBits > 0)
293         {
294             functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
295                                                GL_RENDERBUFFER, mDepthRenderbufferID);
296         }
297     }
298 
299     return new FramebufferGL(data, framebufferID, true, false);
300 }
301 
getDC() const302 HDC DXGISwapChainWindowSurfaceWGL::getDC() const
303 {
304     return mWGLDevice;
305 }
306 
setObjectsLocked(bool locked)307 egl::Error DXGISwapChainWindowSurfaceWGL::setObjectsLocked(bool locked)
308 {
309     if (mRenderbufferBufferHandle == nullptr)
310     {
311         ASSERT(mTextureHandle == nullptr);
312         return egl::NoError();
313     }
314 
315     HANDLE resources[] = {
316         mRenderbufferBufferHandle,
317         mTextureHandle,
318     };
319     GLint count = (mTextureHandle != nullptr) ? 2 : 1;
320 
321     if (locked)
322     {
323         if (!mFunctionsWGL->dxLockObjectsNV(mDeviceHandle, count, resources))
324         {
325             return egl::EglBadAlloc()
326                    << "Failed to lock object, " << gl::FmtErr(HRESULT_CODE(GetLastError()));
327         }
328     }
329     else
330     {
331         if (!mFunctionsWGL->dxUnlockObjectsNV(mDeviceHandle, count, resources))
332         {
333             return egl::EglBadAlloc()
334                    << "Failed to lock object, " << gl::FmtErr(HRESULT_CODE(GetLastError()));
335         }
336     }
337 
338     return egl::NoError();
339 }
340 
checkForResize()341 egl::Error DXGISwapChainWindowSurfaceWGL::checkForResize()
342 {
343     RECT rect;
344     if (!GetClientRect(mWindow, &rect))
345     {
346         return egl::EglBadNativeWindow() << "Failed to query the window size.";
347     }
348 
349     size_t newWidth  = rect.right - rect.left;
350     size_t newHeight = rect.bottom - rect.top;
351     if (newWidth != mWidth || newHeight != mHeight)
352     {
353         mWidth  = newWidth;
354         mHeight = newHeight;
355 
356         // TODO(geofflang): Handle resize by resizing the swap chain instead of re-creating it.
357         egl::Error error = createSwapChain();
358         if (error.isError())
359         {
360             return error;
361         }
362     }
363 
364     return egl::NoError();
365 }
366 
GetDXGIFactoryFromDevice(ID3D11Device * device)367 static IDXGIFactory *GetDXGIFactoryFromDevice(ID3D11Device *device)
368 {
369     IDXGIDevice *dxgiDevice = nullptr;
370     HRESULT result =
371         device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDevice));
372     if (FAILED(result))
373     {
374         return nullptr;
375     }
376 
377     IDXGIAdapter *dxgiAdapter = nullptr;
378     result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void **>(&dxgiAdapter));
379     SafeRelease(dxgiDevice);
380     if (FAILED(result))
381     {
382         return nullptr;
383     }
384 
385     IDXGIFactory *dxgiFactory = nullptr;
386     result =
387         dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void **>(&dxgiFactory));
388     SafeRelease(dxgiAdapter);
389     if (FAILED(result))
390     {
391         return nullptr;
392     }
393 
394     return dxgiFactory;
395 }
396 
createSwapChain()397 egl::Error DXGISwapChainWindowSurfaceWGL::createSwapChain()
398 {
399     egl::Error error = setObjectsLocked(false);
400     if (error.isError())
401     {
402         return error;
403     }
404 
405     if (mRenderbufferBufferHandle)
406     {
407         mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mRenderbufferBufferHandle);
408         mRenderbufferBufferHandle = nullptr;
409     }
410 
411     // If this surface is bound to a texture, unregister it.
412     bool hadBoundSurface = (mTextureHandle != nullptr);
413     if (hadBoundSurface)
414     {
415         mFunctionsWGL->dxUnregisterObjectNV(mDeviceHandle, mTextureHandle);
416         mTextureHandle = nullptr;
417     }
418 
419     IDXGIFactory *dxgiFactory = GetDXGIFactoryFromDevice(mDevice);
420     if (dxgiFactory == nullptr)
421     {
422         return egl::EglBadNativeWindow() << "Failed to query the DXGIFactory.";
423     }
424 
425     IDXGIFactory2 *dxgiFactory2 = nullptr;
426     HRESULT result              = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2),
427                                                  reinterpret_cast<void **>(&dxgiFactory2));
428     if (SUCCEEDED(result))
429     {
430         ASSERT(dxgiFactory2 != nullptr);
431 
432         DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
433         swapChainDesc.BufferCount           = 1;
434         swapChainDesc.Format                = mSwapChainFormat;
435         swapChainDesc.Width                 = static_cast<UINT>(mWidth);
436         swapChainDesc.Height                = static_cast<UINT>(mHeight);
437         swapChainDesc.Format                = mSwapChainFormat;
438         swapChainDesc.Stereo                = FALSE;
439         swapChainDesc.SampleDesc.Count      = 1;
440         swapChainDesc.SampleDesc.Quality    = 0;
441         swapChainDesc.BufferUsage =
442             DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER;
443         swapChainDesc.BufferCount = 1;
444         swapChainDesc.Scaling     = DXGI_SCALING_STRETCH;
445         swapChainDesc.SwapEffect  = DXGI_SWAP_EFFECT_SEQUENTIAL;
446         swapChainDesc.AlphaMode   = DXGI_ALPHA_MODE_UNSPECIFIED;
447         swapChainDesc.Flags       = mSwapChainFlags;
448 
449         result = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mWindow, &swapChainDesc, nullptr,
450                                                       nullptr, &mSwapChain1);
451         SafeRelease(dxgiFactory2);
452         SafeRelease(dxgiFactory);
453         if (FAILED(result))
454         {
455             return egl::EglBadAlloc()
456                    << "Failed to create swap chain for window, " << gl::FmtHR(result);
457         }
458 
459         mSwapChain = mSwapChain1;
460         mSwapChain->AddRef();
461     }
462     else
463     {
464         DXGI_SWAP_CHAIN_DESC swapChainDesc               = {};
465         swapChainDesc.BufferCount                        = 1;
466         swapChainDesc.BufferDesc.Format                  = mSwapChainFormat;
467         swapChainDesc.BufferDesc.Width                   = static_cast<UINT>(mWidth);
468         swapChainDesc.BufferDesc.Height                  = static_cast<UINT>(mHeight);
469         swapChainDesc.BufferDesc.Scaling                 = DXGI_MODE_SCALING_UNSPECIFIED;
470         swapChainDesc.BufferDesc.ScanlineOrdering        = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
471         swapChainDesc.BufferDesc.RefreshRate.Numerator   = 0;
472         swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
473         swapChainDesc.BufferUsage =
474             DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER;
475         swapChainDesc.Flags              = mSwapChainFlags;
476         swapChainDesc.OutputWindow       = mWindow;
477         swapChainDesc.SampleDesc.Count   = 1;
478         swapChainDesc.SampleDesc.Quality = 0;
479         swapChainDesc.Windowed           = TRUE;
480         swapChainDesc.SwapEffect         = DXGI_SWAP_EFFECT_DISCARD;
481 
482         result = dxgiFactory->CreateSwapChain(mDevice, &swapChainDesc, &mSwapChain);
483         SafeRelease(dxgiFactory);
484         if (FAILED(result))
485         {
486             return egl::EglBadAlloc()
487                    << "Failed to create swap chain for window, " << gl::FmtHR(result);
488         }
489     }
490 
491     ID3D11Texture2D *colorBuffer = nullptr;
492     result                       = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
493                                    reinterpret_cast<void **>(&colorBuffer));
494     if (FAILED(result))
495     {
496         return egl::EglBadAlloc() << "Failed to query texture from swap chain, "
497                                   << gl::FmtHR(result);
498     }
499 
500     mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbufferID);
501     mRenderbufferBufferHandle =
502         mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mColorRenderbufferID,
503                                           GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV);
504     SafeRelease(colorBuffer);
505     if (mRenderbufferBufferHandle == nullptr)
506     {
507         return egl::EglBadAlloc() << "Failed to register D3D object, "
508                                   << gl::FmtErr(HRESULT_CODE(GetLastError()));
509     }
510 
511     // Rebind the surface to the texture if needed.
512     if (hadBoundSurface)
513     {
514         mTextureHandle = mFunctionsWGL->dxRegisterObjectNV(mDeviceHandle, colorBuffer, mTextureID,
515                                                            GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV);
516         if (mTextureHandle == nullptr)
517         {
518             return egl::EglBadAlloc()
519                    << "Failed to register D3D object, " << gl::FmtErr(HRESULT_CODE(GetLastError()));
520         }
521     }
522 
523     error = setObjectsLocked(true);
524     if (error.isError())
525     {
526         return error;
527     }
528 
529     if (mDepthBufferFormat != GL_NONE)
530     {
531         ASSERT(mDepthRenderbufferID != 0);
532         mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbufferID);
533         mFunctionsGL->renderbufferStorage(GL_RENDERBUFFER, mDepthBufferFormat,
534                                           static_cast<GLsizei>(mWidth),
535                                           static_cast<GLsizei>(mHeight));
536     }
537 
538     mFirstSwap = true;
539 
540     return egl::NoError();
541 }
542 }  // namespace rx
543