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, ¶ms);
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