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
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & data)144 FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Context *context,
145 const gl::FramebufferState &data)
146 {
147 return mRenderer->createDefaultFramebuffer(data);
148 }
149
bindTexImage(const gl::Context *,gl::Texture *,EGLint)150 egl::Error SurfaceD3D::bindTexImage(const gl::Context *, gl::Texture *, EGLint)
151 {
152 return egl::NoError();
153 }
154
releaseTexImage(const gl::Context *,EGLint)155 egl::Error SurfaceD3D::releaseTexImage(const gl::Context *, EGLint)
156 {
157 return egl::NoError();
158 }
159
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)160 egl::Error SurfaceD3D::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
161 {
162 if (!mState.directComposition)
163 {
164 return egl::EglBadSurface()
165 << "getSyncValues: surface requires Direct Composition to be enabled";
166 }
167
168 return mSwapChain->getSyncValues(ust, msc, sbc);
169 }
170
getMscRate(EGLint * numerator,EGLint * denominator)171 egl::Error SurfaceD3D::getMscRate(EGLint *numerator, EGLint *denominator)
172 {
173 UNIMPLEMENTED();
174 return egl::EglBadAccess();
175 }
176
resetSwapChain(const egl::Display * display)177 egl::Error SurfaceD3D::resetSwapChain(const egl::Display *display)
178 {
179 ASSERT(!mSwapChain);
180
181 int width;
182 int height;
183
184 if (!mFixedSize)
185 {
186 RECT windowRect;
187 if (!mNativeWindow->getClientRect(&windowRect))
188 {
189 ASSERT(false);
190
191 return egl::EglBadSurface() << "Could not retrieve the window dimensions";
192 }
193
194 width = windowRect.right - windowRect.left;
195 height = windowRect.bottom - windowRect.top;
196 }
197 else
198 {
199 // non-window surface - size is determined at creation
200 width = mFixedWidth;
201 height = mFixedHeight;
202 }
203
204 mSwapChain =
205 mRenderer->createSwapChain(mNativeWindow, mShareHandle, mD3DTexture, mRenderTargetFormat,
206 mDepthStencilFormat, mOrientation, mState.config->samples);
207 if (!mSwapChain)
208 {
209 return egl::EglBadAlloc();
210 }
211
212 // This is a bit risky to pass the proxy context here, but it can happen at almost any time.
213 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(display);
214 egl::Error error = resetSwapChain(displayD3D, width, height);
215 if (error.isError())
216 {
217 SafeDelete(mSwapChain);
218 return error;
219 }
220
221 return egl::NoError();
222 }
223
resizeSwapChain(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)224 egl::Error SurfaceD3D::resizeSwapChain(DisplayD3D *displayD3D,
225 int backbufferWidth,
226 int backbufferHeight)
227 {
228 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
229 ASSERT(mSwapChain);
230
231 EGLint status =
232 mSwapChain->resize(displayD3D, std::max(1, backbufferWidth), std::max(1, backbufferHeight));
233
234 if (status == EGL_CONTEXT_LOST)
235 {
236 mDisplay->notifyDeviceLost();
237 return egl::Error(status);
238 }
239 else if (status != EGL_SUCCESS)
240 {
241 return egl::Error(status);
242 }
243
244 mWidth = backbufferWidth;
245 mHeight = backbufferHeight;
246
247 return egl::NoError();
248 }
249
resetSwapChain(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)250 egl::Error SurfaceD3D::resetSwapChain(DisplayD3D *displayD3D,
251 int backbufferWidth,
252 int backbufferHeight)
253 {
254 ASSERT(backbufferWidth >= 0 && backbufferHeight >= 0);
255 ASSERT(mSwapChain);
256
257 EGLint status = mSwapChain->reset(displayD3D, std::max(1, backbufferWidth),
258 std::max(1, backbufferHeight), mSwapInterval);
259
260 if (status == EGL_CONTEXT_LOST)
261 {
262 mRenderer->notifyDeviceLost();
263 return egl::Error(status);
264 }
265 else if (status != EGL_SUCCESS)
266 {
267 return egl::Error(status);
268 }
269
270 mWidth = backbufferWidth;
271 mHeight = backbufferHeight;
272 mSwapIntervalDirty = false;
273
274 return egl::NoError();
275 }
276
swapRect(DisplayD3D * displayD3D,EGLint x,EGLint y,EGLint width,EGLint height)277 egl::Error SurfaceD3D::swapRect(DisplayD3D *displayD3D,
278 EGLint x,
279 EGLint y,
280 EGLint width,
281 EGLint height)
282 {
283 if (!mSwapChain)
284 {
285 return egl::NoError();
286 }
287
288 if (x + width > mWidth)
289 {
290 width = mWidth - x;
291 }
292
293 if (y + height > mHeight)
294 {
295 height = mHeight - y;
296 }
297
298 if (width != 0 && height != 0)
299 {
300 EGLint status = mSwapChain->swapRect(displayD3D, x, y, width, height);
301
302 if (status == EGL_CONTEXT_LOST)
303 {
304 mRenderer->notifyDeviceLost();
305 return egl::Error(status);
306 }
307 else if (status != EGL_SUCCESS)
308 {
309 return egl::Error(status);
310 }
311 }
312
313 ANGLE_TRY(checkForOutOfDateSwapChain(displayD3D));
314
315 return egl::NoError();
316 }
317
checkForOutOfDateSwapChain(DisplayD3D * displayD3D)318 egl::Error SurfaceD3D::checkForOutOfDateSwapChain(DisplayD3D *displayD3D)
319 {
320 RECT client;
321 int clientWidth = getWidth();
322 int clientHeight = getHeight();
323 bool sizeDirty = false;
324 if (!mFixedSize && !mNativeWindow->isIconic())
325 {
326 // The window is automatically resized to 150x22 when it's minimized, but the swapchain
327 // shouldn't be resized because that's not a useful size to render to.
328 if (!mNativeWindow->getClientRect(&client))
329 {
330 UNREACHABLE();
331 return egl::NoError();
332 }
333
334 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing
335 // information.
336 clientWidth = client.right - client.left;
337 clientHeight = client.bottom - client.top;
338 sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
339 }
340 else if (mFixedSize)
341 {
342 clientWidth = mFixedWidth;
343 clientHeight = mFixedHeight;
344 sizeDirty = mFixedWidth != getWidth() || mFixedHeight != getHeight();
345 }
346
347 if (mSwapIntervalDirty)
348 {
349 ANGLE_TRY(resetSwapChain(displayD3D, clientWidth, clientHeight));
350 }
351 else if (sizeDirty)
352 {
353 ANGLE_TRY(resizeSwapChain(displayD3D, clientWidth, clientHeight));
354 }
355
356 return egl::NoError();
357 }
358
swap(const gl::Context * context)359 egl::Error SurfaceD3D::swap(const gl::Context *context)
360 {
361 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay());
362 return swapRect(displayD3D, 0, 0, mWidth, mHeight);
363 }
364
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)365 egl::Error SurfaceD3D::postSubBuffer(const gl::Context *context,
366 EGLint x,
367 EGLint y,
368 EGLint width,
369 EGLint height)
370 {
371 DisplayD3D *displayD3D = GetImplAs<DisplayD3D>(context->getDisplay());
372 return swapRect(displayD3D, x, y, width, height);
373 }
374
getSwapChain() const375 rx::SwapChainD3D *SurfaceD3D::getSwapChain() const
376 {
377 return mSwapChain;
378 }
379
setSwapInterval(EGLint interval)380 void SurfaceD3D::setSwapInterval(EGLint interval)
381 {
382 if (mSwapInterval == interval)
383 {
384 return;
385 }
386
387 mSwapInterval = interval;
388 mSwapIntervalDirty = true;
389 }
390
setFixedWidth(EGLint width)391 void SurfaceD3D::setFixedWidth(EGLint width)
392 {
393 mFixedWidth = width;
394 }
395
setFixedHeight(EGLint height)396 void SurfaceD3D::setFixedHeight(EGLint height)
397 {
398 mFixedHeight = height;
399 }
400
getWidth() const401 EGLint SurfaceD3D::getWidth() const
402 {
403 return mWidth;
404 }
405
getHeight() const406 EGLint SurfaceD3D::getHeight() const
407 {
408 return mHeight;
409 }
410
isPostSubBufferSupported() const411 EGLint SurfaceD3D::isPostSubBufferSupported() const
412 {
413 // post sub buffer is always possible on D3D surfaces
414 return EGL_TRUE;
415 }
416
getSwapBehavior() const417 EGLint SurfaceD3D::getSwapBehavior() const
418 {
419 return EGL_BUFFER_PRESERVED;
420 }
421
querySurfacePointerANGLE(EGLint attribute,void ** value)422 egl::Error SurfaceD3D::querySurfacePointerANGLE(EGLint attribute, void **value)
423 {
424 if (attribute == EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE)
425 {
426 *value = mSwapChain->getShareHandle();
427 }
428 else if (attribute == EGL_DXGI_KEYED_MUTEX_ANGLE)
429 {
430 *value = mSwapChain->getKeyedMutex();
431 }
432 else
433 UNREACHABLE();
434
435 return egl::NoError();
436 }
437
getD3DTextureColorFormat() const438 const angle::Format *SurfaceD3D::getD3DTextureColorFormat() const
439 {
440 return mColorFormat;
441 }
442
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)443 angle::Result SurfaceD3D::getAttachmentRenderTarget(const gl::Context *context,
444 GLenum binding,
445 const gl::ImageIndex &imageIndex,
446 GLsizei samples,
447 FramebufferAttachmentRenderTarget **rtOut)
448 {
449 if (binding == GL_BACK)
450 {
451 *rtOut = mSwapChain->getColorRenderTarget();
452 }
453 else
454 {
455 *rtOut = mSwapChain->getDepthStencilRenderTarget();
456 }
457 return angle::Result::Continue;
458 }
459
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)460 angle::Result SurfaceD3D::initializeContents(const gl::Context *context,
461 const gl::ImageIndex &imageIndex)
462 {
463 if (mState.config->renderTargetFormat != GL_NONE)
464 {
465 ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getColorRenderTarget()));
466 }
467 if (mState.config->depthStencilFormat != GL_NONE)
468 {
469 ANGLE_TRY(mRenderer->initRenderTarget(context, mSwapChain->getDepthStencilRenderTarget()));
470 }
471 return angle::Result::Continue;
472 }
473
WindowSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLNativeWindowType window,const egl::AttributeMap & attribs)474 WindowSurfaceD3D::WindowSurfaceD3D(const egl::SurfaceState &state,
475 RendererD3D *renderer,
476 egl::Display *display,
477 EGLNativeWindowType window,
478 const egl::AttributeMap &attribs)
479 : SurfaceD3D(state, renderer, display, window, 0, static_cast<EGLClientBuffer>(0), attribs)
480 {}
481
~WindowSurfaceD3D()482 WindowSurfaceD3D::~WindowSurfaceD3D() {}
483
PbufferSurfaceD3D(const egl::SurfaceState & state,RendererD3D * renderer,egl::Display * display,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)484 PbufferSurfaceD3D::PbufferSurfaceD3D(const egl::SurfaceState &state,
485 RendererD3D *renderer,
486 egl::Display *display,
487 EGLenum buftype,
488 EGLClientBuffer clientBuffer,
489 const egl::AttributeMap &attribs)
490 : SurfaceD3D(state,
491 renderer,
492 display,
493 static_cast<EGLNativeWindowType>(0),
494 buftype,
495 clientBuffer,
496 attribs)
497 {}
498
~PbufferSurfaceD3D()499 PbufferSurfaceD3D::~PbufferSurfaceD3D() {}
500
501 } // namespace rx
502