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