1 //
2 // Copyright 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/Format.h"
15 #include "libANGLE/renderer/d3d/DisplayD3D.h"
16 #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
17 #include "libANGLE/renderer/d3d/RendererD3D.h"
18 #include "libANGLE/renderer/d3d/SwapChainD3D.h"
19 #include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
20
21 #include <EGL/eglext.h>
22 #include <tchar.h>
23 #include <algorithm>
24
25 namespace rx
26 {
27
SurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLNativeWindowType window,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)28 SurfaceD3D::SurfaceD3D(const egl::SurfaceState &state,
29 RendererD3D *renderer,
30 egl::Display *display,
31 EGLNativeWindowType window,
32 EGLenum buftype,
33 EGLClientBuffer clientBuffer,
34 const egl::AttributeMap &attribs)
35 : SurfaceImpl(state),
36 mRenderer(renderer),
37 mDisplay(display),
38 mFixedSize(window == nullptr || attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE),
39 mFixedWidth(0),
40 mFixedHeight(0),
41 mOrientation(static_cast<EGLint>(attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0))),
42 mRenderTargetFormat(state.config->renderTargetFormat),
43 mDepthStencilFormat(state.config->depthStencilFormat),
44 mColorFormat(nullptr),
45 mSwapChain(nullptr),
46 mSwapIntervalDirty(true),
47 mNativeWindow(renderer->createNativeWindow(window, state.config, attribs)),
48 mWidth(static_cast<EGLint>(attribs.get(EGL_WIDTH, 0))),
49 mHeight(static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0))),
50 mSwapInterval(1),
51 mShareHandle(0),
52 mD3DTexture(nullptr),
53 mBuftype(buftype)
54 {
55 if (window != nullptr && !mFixedSize)
56 {
57 mWidth = -1;
58 mHeight = -1;
59 }
60
61 if (mFixedSize)
62 {
63 mFixedWidth = mWidth;
64 mFixedHeight = mHeight;
65 }
66
67 switch (buftype)
68 {
69 case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
70 mShareHandle = static_cast<HANDLE>(clientBuffer);
71 break;
72
73 case EGL_D3D_TEXTURE_ANGLE:
74 mD3DTexture = static_cast<IUnknown *>(clientBuffer);
75 ASSERT(mD3DTexture != nullptr);
76 mD3DTexture->AddRef();
77 break;
78
79 default:
80 break;
81 }
82 }
83
~SurfaceD3D()84 SurfaceD3D::~SurfaceD3D()
85 {
86 releaseSwapChain();
87 SafeDelete(mNativeWindow);
88 SafeRelease(mD3DTexture);
89 }
90
releaseSwapChain()91 void SurfaceD3D::releaseSwapChain()
92 {
93 SafeDelete(mSwapChain);
94 }
95
initialize(const egl::Display * display)96 egl::Error SurfaceD3D::initialize(const egl::Display *display)
97 {
98 if (mNativeWindow->getNativeWindow())
99 {
100 if (!mNativeWindow->initialize())
101 {
102 return egl::EglBadSurface();
103 }
104 }
105
106 if (mBuftype == EGL_D3D_TEXTURE_ANGLE)
107 {
108 ANGLE_TRY(mRenderer->getD3DTextureInfo(mState.config, mD3DTexture, mState.attributes,
109 &mFixedWidth, &mFixedHeight, nullptr, nullptr,
110 &mColorFormat));
111 if (mState.attributes.contains(EGL_GL_COLORSPACE))
112 {
113 if (mColorFormat->id != angle::FormatID::R8G8B8A8_TYPELESS &&
114 mColorFormat->id != angle::FormatID::B8G8R8A8_TYPELESS)
115 {
116 return egl::EglBadMatch()
117 << "EGL_GL_COLORSPACE may only be specified for TYPELESS textures";
118 }
119 }
120 if (mColorFormat->id == angle::FormatID::R8G8B8A8_TYPELESS)
121 {
122 EGLAttrib colorspace =
123 mState.attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR);
124 if (colorspace == EGL_GL_COLORSPACE_SRGB)
125 {
126 mColorFormat = &angle::Format::Get(angle::FormatID::R8G8B8A8_TYPELESS_SRGB);
127 }
128 }
129 if (mColorFormat->id == angle::FormatID::B8G8R8A8_TYPELESS)
130 {
131 EGLAttrib colorspace =
132 mState.attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR);
133 if (colorspace == EGL_GL_COLORSPACE_SRGB)
134 {
135 mColorFormat = &angle::Format::Get(angle::FormatID::B8G8R8A8_TYPELESS_SRGB);
136 }
137 }
138 mRenderTargetFormat = mColorFormat->fboImplementationInternalFormat;
139 }
140
141 ANGLE_TRY(resetSwapChain(display));
142 return egl::NoError();
143 }
144
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & data)145 FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Context *context,
146 const gl::FramebufferState &data)
147 {
148 return mRenderer->createDefaultFramebuffer(data);
149 }
150
bindTexImage(const gl::Context *,gl::Texture *,EGLint)151 egl::Error SurfaceD3D::bindTexImage(const gl::Context *, gl::Texture *, EGLint)
152 {
153 return egl::NoError();
154 }
155
releaseTexImage(const gl::Context *,EGLint)156 egl::Error SurfaceD3D::releaseTexImage(const gl::Context *, EGLint)
157 {
158 return egl::NoError();
159 }
160
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)161 egl::Error SurfaceD3D::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
162 {
163 if (!mState.directComposition)
164 {
165 return egl::EglBadSurface()
166 << "getSyncValues: surface requires Direct Composition to be enabled";
167 }
168
169 return mSwapChain->getSyncValues(ust, msc, sbc);
170 }
171
getMscRate(EGLint * numerator,EGLint * denominator)172 egl::Error SurfaceD3D::getMscRate(EGLint *numerator, EGLint *denominator)
173 {
174 UNIMPLEMENTED();
175 return egl::EglBadAccess();
176 }
177
resetSwapChain(const egl::Display * display)178 egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display)
179 {
180 ASSERT(!mSwapChain);
181
182 int width;
183 int height;
184
185 if (!mFixedSize)
186 {
187 RECT windowRect;
188 if (!mNativeWindow->getClientRect(&windowRect))
189 {
190 ASSERT(false);
191
192 return egl::EglBadSurface() << "Could not retrieve the window dimensions";
193 }
194
195 width = windowRect.right - windowRect.left;
196 height = windowRect.bottom - windowRect.top;
197 }
198 else
199 {
200 // non-window surface - size is determined at creation
201 width = mFixedWidth;
202 height = mFixedHeight;
203 }
204
205 mSwapChain =
206 mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat,
207 mDepthStencilFormat, mOrientation, mState.config->samples);
208 if (!mSwapChain)
209 {
210 return egl::EglBadAlloc();
211 }
212
213 // This is a bit risky to pass the proxy context here, but it can happen at almost any time.
214 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(display);
215 egl::Error error = resetSwapChain(displayD3D, width, height);
216 if (error.isError())
217 {
218 SafeDelete(mSwapChain);
219 return error;
220 }
221
222 return egl::NoError();
223 }
224
resizeSwapChain(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)225 egl::Error SurfaceD3D::resizeSwapChain(DisplayD3D *displayD3D,
226 int backbufferWidth,
227 int backbufferHeight)
228 {
229 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
230 ASSERT(mSwapChain);
231
232 EGLint status =
233 mSwapChain->resize(displayD3D, std::max(1, backbufferWidth), std::max(1, backbufferHeight));
234
235 if (status == EGL_CONTEXT_LOST)
236 {
237 mDisplay->notifyDeviceLost();
238 return egl::Error(status);
239 }
240 else if (status != EGL_SUCCESS)
241 {
242 return egl::Error(status);
243 }
244
245 mWidth = backbufferWidth;
246 mHeight = backbufferHeight;
247
248 return egl::NoError();
249 }
250
resetSwapChain(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)251 egl::Error SurfaceD3D::resetSwapChain(DisplayD3D *displayD3D,
252 int backbufferWidth,
253 int backbufferHeight)
254 {
255 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
256 ASSERT(mSwapChain);
257
258 EGLint status = mSwapChain->reset(displayD3D, std::max(1, backbufferWidth),
259 std::max(1, backbufferHeight), mSwapInterval);
260
261 if (status == EGL_CONTEXT_LOST)
262 {
263 mRenderer->notifyDeviceLost();
264 return egl::Error(status);
265 }
266 else if (status != EGL_SUCCESS)
267 {
268 return egl::Error(status);
269 }
270
271 mWidth = backbufferWidth;
272 mHeight = backbufferHeight;
273 mSwapIntervalDirty = false;
274
275 return egl::NoError();
276 }
277
swapRect(DisplayD3D * displayD3D,EGLint x,EGLint y,EGLint width,EGLint height)278 egl::Error SurfaceD3D::swapRect(DisplayD3D *displayD3D,
279 EGLint x,
280 EGLint y,
281 EGLint width,
282 EGLint height)
283 {
284 if (!mSwapChain)
285 {
286 return egl::NoError();
287 }
288
289 if (x + width > mWidth)
290 {
291 width = mWidth - x;
292 }
293
294 if (y + height > mHeight)
295 {
296 height = mHeight - y;
297 }
298
299 if (width != 0 && height != 0)
300 {
301 EGLint status = mSwapChain->swapRect(displayD3D, x, y, width, height);
302
303 if (status == EGL_CONTEXT_LOST)
304 {
305 mRenderer->notifyDeviceLost();
306 return egl::Error(status);
307 }
308 else if (status != EGL_SUCCESS)
309 {
310 return egl::Error(status);
311 }
312 }
313
314 ANGLE_TRY(checkForOutOfDateSwapChain(displayD3D));
315
316 return egl::NoError();
317 }
318
checkForOutOfDateSwapChain(DisplayD3D * displayD3D)319 egl::Error SurfaceD3D::checkForOutOfDateSwapChain(DisplayD3D *displayD3D)
320 {
321 RECT client;
322 int clientWidth = getWidth();
323 int clientHeight = getHeight();
324 bool sizeDirty = false;
325 if (!mFixedSize && !mNativeWindow->isIconic())
326 {
327 // The window is automatically resized to 150x22 when it's minimized, but the swapchain
328 // shouldn't be resized because that's not a useful size to render to.
329 if (!mNativeWindow->getClientRect(&client))
330 {
331 UNREACHABLE();
332 return egl::NoError();
333 }
334
335 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing
336 // information.
337 clientWidth = client.right - client.left;
338 clientHeight = client.bottom - client.top;
339 sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
340 }
341 else if (mFixedSize)
342 {
343 clientWidth = mFixedWidth;
344 clientHeight = mFixedHeight;
345 sizeDirty = mFixedWidth != getWidth() || mFixedHeight != getHeight();
346 }
347
348 if (mSwapIntervalDirty)
349 {
350 ANGLE_TRY(resetSwapChain(displayD3D, clientWidth, clientHeight));
351 }
352 else if (sizeDirty)
353 {
354 ANGLE_TRY(resizeSwapChain(displayD3D, clientWidth, clientHeight));
355 }
356
357 return egl::NoError();
358 }
359
swap(const gl::Context * context)360 egl::Error SurfaceD3D::swap(const gl::Context *context)
361 {
362 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay());
363 return swapRect(displayD3D, 0, 0, mWidth, mHeight);
364 }
365
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)366 egl::Error SurfaceD3D::postSubBuffer(const gl::Context *context,
367 EGLint x,
368 EGLint y,
369 EGLint width,
370 EGLint height)
371 {
372 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay());
373 return swapRect(displayD3D, x, y, width, height);
374 }
375
getSwapChain() const376 rx::SwapChainD3D *SurfaceD3D::getSwapChain() const
377 {
378 return mSwapChain;
379 }
380
setSwapInterval(EGLint interval)381 void SurfaceD3D::setSwapInterval(EGLint interval)
382 {
383 if (mSwapInterval == interval)
384 {
385 return;
386 }
387
388 mSwapInterval = interval;
389 mSwapIntervalDirty = true;
390 }
391
setFixedWidth(EGLint width)392 void SurfaceD3D::setFixedWidth(EGLint width)
393 {
394 mFixedWidth = width;
395 }
396
setFixedHeight(EGLint height)397 void SurfaceD3D::setFixedHeight(EGLint height)
398 {
399 mFixedHeight = height;
400 }
401
getWidth() const402 EGLint SurfaceD3D::getWidth() const
403 {
404 return mWidth;
405 }
406
getHeight() const407 EGLint SurfaceD3D::getHeight() const
408 {
409 return mHeight;
410 }
411
isPostSubBufferSupported() const412 EGLint SurfaceD3D::isPostSubBufferSupported() const
413 {
414 // post sub buffer is always possible on D3D surfaces
415 return EGL_TRUE;
416 }
417
getSwapBehavior() const418 EGLint SurfaceD3D::getSwapBehavior() const
419 {
420 return EGL_BUFFER_PRESERVED;
421 }
422
querySurfacePointerANGLE(EGLint attribute,void ** value)423 egl::Error SurfaceD3D::querySurfacePointerANGLE(EGLint attribute, void **value)
424 {
425 if (attribute == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE)
426 {
427 *value = mSwapChain->getShareHandle();
428 }
429 else if (attribute == EGL_DXGI_KEYED_MUTEX_ANGLE)
430 {
431 *value = mSwapChain->getKeyedMutex();
432 }
433 else
434 UNREACHABLE();
435
436 return egl::NoError();
437 }
438
getD3DTextureColorFormat() const439 const angle::Format *SurfaceD3D::getD3DTextureColorFormat() const
440 {
441 return mColorFormat;
442 }
443
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)444 angle::Result SurfaceD3D::getAttachmentRenderTarget(const gl::Context *context,
445 GLenum binding,
446 const gl::ImageIndex &imageIndex,
447 GLsizei samples,
448 FramebufferAttachmentRenderTarget **rtOut)
449 {
450 if (binding == GL_BACK)
451 {
452 *rtOut = mSwapChain->getColorRenderTarget();
453 }
454 else
455 {
456 *rtOut = mSwapChain->getDepthStencilRenderTarget();
457 }
458 return angle::Result::Continue;
459 }
460
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)461 angle::Result SurfaceD3D::initializeContents(const gl::Context *context,
462 const gl::ImageIndex &imageIndex)
463 {
464 if (mState.config->renderTargetFormat != GL_NONE)
465 {
466 ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getColorRenderTarget()));
467 }
468 if (mState.config->depthStencilFormat != GL_NONE)
469 {
470 ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getDepthStencilRenderTarget()));
471 }
472 return angle::Result::Continue;
473 }
474
WindowSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLNativeWindowType window,const egl::AttributeMap & attribs)475 WindowSurfaceD3D::WindowSurfaceD3D(const egl::SurfaceState &state,
476 RendererD3D *renderer,
477 egl::Display *display,
478 EGLNativeWindowType window,
479 const egl::AttributeMap &attribs)
480 : SurfaceD3D(state, renderer, display, window, 0, static_cast<EGLClientBuffer>(0), attribs)
481 {}
482
~WindowSurfaceD3D()483 WindowSurfaceD3D::~WindowSurfaceD3D() {}
484
PbufferSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)485 PbufferSurfaceD3D::PbufferSurfaceD3D(const egl::SurfaceState &state,
486 RendererD3D *renderer,
487 egl::Display *display,
488 EGLenum buftype,
489 EGLClientBuffer clientBuffer,
490 const egl::AttributeMap &attribs)
491 : SurfaceD3D(state,
492 renderer,
493 display,
494 static_cast<EGLNativeWindowType>(0),
495 buftype,
496 clientBuffer,
497 attribs)
498 {}
499
~PbufferSurfaceD3D()500 PbufferSurfaceD3D::~PbufferSurfaceD3D() {}
501
502 } // namespace rx
503