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