• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2012 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 // SwapChain11.cpp: Implements a back-end specific class for the D3D11 swap chain.
8 
9 #include "libANGLE/renderer/d3d/d3d11/SwapChain11.h"
10 
11 #include <EGL/eglext.h>
12 
13 #include "libANGLE/features.h"
14 #include "libANGLE/renderer/d3d/DisplayD3D.h"
15 #include "libANGLE/renderer/d3d/d3d11/NativeWindow11.h"
16 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
17 #include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
18 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
19 #include "libANGLE/renderer/d3d/d3d11/texture_format_table.h"
20 #include "libANGLE/trace.h"
21 
22 // Precompiled shaders
23 #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h"
24 #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h"
25 #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2dms11ps.h"
26 #include "libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvecolor2dps.h"
27 
28 #ifdef ANGLE_ENABLE_KEYEDMUTEX
29 #    define ANGLE_RESOURCE_SHARE_TYPE D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
30 #else
31 #    define ANGLE_RESOURCE_SHARE_TYPE D3D11_RESOURCE_MISC_SHARED
32 #endif
33 
34 namespace rx
35 {
36 
37 namespace
38 {
39 // To avoid overflow in QPC to Microseconds calculations, since we multiply
40 // by kMicrosecondsPerSecond, then the QPC value should not exceed
41 // (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply.
42 static constexpr int64_t kQPCOverflowThreshold  = 0x8637BD05AF7;
43 static constexpr int64_t kMicrosecondsPerSecond = 1000000;
44 
NeedsOffscreenTexture(Renderer11 * renderer,NativeWindow11 * nativeWindow,EGLint orientation)45 bool NeedsOffscreenTexture(Renderer11 *renderer, NativeWindow11 *nativeWindow, EGLint orientation)
46 {
47     // We don't need an offscreen texture if either orientation = INVERT_Y,
48     // or present path fast is enabled and we're not rendering onto an offscreen surface.
49     return orientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE &&
50            !(renderer->presentPathFastEnabled() && nativeWindow->getNativeWindow());
51 }
52 }  // anonymous namespace
53 
SwapChain11(Renderer11 * renderer,NativeWindow11 * nativeWindow,HANDLE shareHandle,IUnknown * d3dTexture,GLenum backBufferFormat,GLenum depthBufferFormat,EGLint orientation,EGLint samples)54 SwapChain11::SwapChain11(Renderer11 *renderer,
55                          NativeWindow11 *nativeWindow,
56                          HANDLE shareHandle,
57                          IUnknown *d3dTexture,
58                          GLenum backBufferFormat,
59                          GLenum depthBufferFormat,
60                          EGLint orientation,
61                          EGLint samples)
62     : SwapChainD3D(shareHandle, d3dTexture, backBufferFormat, depthBufferFormat),
63       mRenderer(renderer),
64       mWidth(-1),
65       mHeight(-1),
66       mOrientation(orientation),
67       mAppCreatedShareHandle(mShareHandle != nullptr),
68       mSwapInterval(0),
69       mPassThroughResourcesInit(false),
70       mNativeWindow(nativeWindow),
71       mFirstSwap(true),
72       mSwapChain(nullptr),
73       mSwapChain1(nullptr),
74       mKeyedMutex(nullptr),
75       mBackBufferTexture(),
76       mBackBufferRTView(),
77       mBackBufferSRView(),
78       mNeedsOffscreenTexture(NeedsOffscreenTexture(renderer, nativeWindow, orientation)),
79       mOffscreenTexture(),
80       mOffscreenRTView(),
81       mOffscreenSRView(),
82       mNeedsOffscreenTextureCopy(false),
83       mOffscreenTextureCopyForSRV(),
84       mDepthStencilTexture(),
85       mDepthStencilDSView(),
86       mDepthStencilSRView(),
87       mQuadVB(),
88       mPassThroughSampler(),
89       mPassThroughIL(),
90       mPassThroughVS(),
91       mPassThroughOrResolvePS(),
92       mPassThroughRS(),
93       mColorRenderTarget(this, renderer, false),
94       mDepthStencilRenderTarget(this, renderer, true),
95       mEGLSamples(samples)
96 {
97     // Sanity check that if present path fast is active then we're using the default orientation
98     ASSERT(!mRenderer->presentPathFastEnabled() || orientation == 0);
99 
100     // Get the performance counter
101     LARGE_INTEGER counterFreqency = {};
102     BOOL success                  = QueryPerformanceFrequency(&counterFreqency);
103     ASSERT(success);
104 
105     mQPCFrequency = counterFreqency.QuadPart;
106 }
107 
~SwapChain11()108 SwapChain11::~SwapChain11()
109 {
110     release();
111 }
112 
release()113 void SwapChain11::release()
114 {
115     // TODO(jmadill): Should probably signal that the RenderTarget is dirty.
116 
117     SafeRelease(mSwapChain1);
118     SafeRelease(mSwapChain);
119     SafeRelease(mKeyedMutex);
120     mBackBufferTexture.reset();
121     mBackBufferRTView.reset();
122     mBackBufferSRView.reset();
123     mOffscreenTexture.reset();
124     mOffscreenRTView.reset();
125     mOffscreenSRView.reset();
126     mDepthStencilTexture.reset();
127     mDepthStencilDSView.reset();
128     mDepthStencilSRView.reset();
129     mQuadVB.reset();
130     mPassThroughSampler.reset();
131     mPassThroughIL.reset();
132     mPassThroughVS.reset();
133     mPassThroughOrResolvePS.reset();
134     mPassThroughRS.reset();
135 
136     if (!mAppCreatedShareHandle)
137     {
138         mShareHandle = nullptr;
139     }
140 }
141 
releaseOffscreenColorBuffer()142 void SwapChain11::releaseOffscreenColorBuffer()
143 {
144     mOffscreenTexture.reset();
145     mOffscreenRTView.reset();
146     mOffscreenSRView.reset();
147     mNeedsOffscreenTextureCopy = false;
148     mOffscreenTextureCopyForSRV.reset();
149 }
150 
releaseOffscreenDepthBuffer()151 void SwapChain11::releaseOffscreenDepthBuffer()
152 {
153     mDepthStencilTexture.reset();
154     mDepthStencilDSView.reset();
155     mDepthStencilSRView.reset();
156 }
157 
resetOffscreenBuffers(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)158 EGLint SwapChain11::resetOffscreenBuffers(DisplayD3D *displayD3D,
159                                           int backbufferWidth,
160                                           int backbufferHeight)
161 {
162     if (mNeedsOffscreenTexture)
163     {
164         EGLint result = resetOffscreenColorBuffer(displayD3D, backbufferWidth, backbufferHeight);
165         if (result != EGL_SUCCESS)
166         {
167             return result;
168         }
169     }
170 
171     EGLint result = resetOffscreenDepthBuffer(displayD3D, backbufferWidth, backbufferHeight);
172     if (result != EGL_SUCCESS)
173     {
174         return result;
175     }
176 
177     mWidth  = backbufferWidth;
178     mHeight = backbufferHeight;
179 
180     return EGL_SUCCESS;
181 }
182 
resetOffscreenColorBuffer(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)183 EGLint SwapChain11::resetOffscreenColorBuffer(DisplayD3D *displayD3D,
184                                               int backbufferWidth,
185                                               int backbufferHeight)
186 {
187     ASSERT(mNeedsOffscreenTexture);
188 
189     ANGLE_TRACE_EVENT0("gpu.angle", "SwapChain11::resetOffscreenTexture");
190     ID3D11Device *device = mRenderer->getDevice();
191 
192     ASSERT(device != nullptr);
193 
194     // D3D11 does not allow zero size textures
195     ASSERT(backbufferWidth >= 1);
196     ASSERT(backbufferHeight >= 1);
197 
198     // Preserve the render target content
199     TextureHelper11 previousOffscreenTexture(std::move(mOffscreenTexture));
200     const int previousWidth  = mWidth;
201     const int previousHeight = mHeight;
202 
203     releaseOffscreenColorBuffer();
204 
205     const d3d11::Format &backbufferFormatInfo =
206         d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps());
207     D3D11_TEXTURE2D_DESC offscreenTextureDesc = {};
208 
209     // If the app passed in a share handle or D3D texture, open the resource
210     // See EGL_ANGLE_d3d_share_handle_client_buffer and EGL_ANGLE_d3d_texture_client_buffer
211     if (mAppCreatedShareHandle || mD3DTexture != nullptr)
212     {
213         if (mAppCreatedShareHandle)
214         {
215             ID3D11Resource *tempResource11;
216             HRESULT result = device->OpenSharedResource(mShareHandle, __uuidof(ID3D11Resource),
217                                                         (void **)&tempResource11);
218             if (FAILED(result))
219             {
220                 ERR() << "Could not open shared handle. " << gl::FmtHR(result);
221                 release();
222                 return EGL_BAD_SURFACE;
223             }
224 
225             mOffscreenTexture.set(d3d11::DynamicCastComObject<ID3D11Texture2D>(tempResource11),
226                                   backbufferFormatInfo);
227             SafeRelease(tempResource11);
228         }
229         else if (mD3DTexture != nullptr)
230         {
231             mOffscreenTexture.set(d3d11::DynamicCastComObject<ID3D11Texture2D>(mD3DTexture),
232                                   backbufferFormatInfo);
233         }
234         else
235         {
236             UNREACHABLE();
237         }
238         ASSERT(mOffscreenTexture.valid());
239         mOffscreenTexture.getDesc(&offscreenTextureDesc);
240 
241         // Fail if the offscreen texture is not renderable.
242         if ((offscreenTextureDesc.BindFlags & D3D11_BIND_RENDER_TARGET) == 0)
243         {
244             ERR() << "Could not use provided offscreen texture, texture not renderable.";
245             release();
246             return EGL_BAD_SURFACE;
247         }
248     }
249     else
250     {
251         const bool useSharedResource =
252             !mNativeWindow->getNativeWindow() && mRenderer->getShareHandleSupport();
253 
254         offscreenTextureDesc.Width              = backbufferWidth;
255         offscreenTextureDesc.Height             = backbufferHeight;
256         offscreenTextureDesc.Format             = backbufferFormatInfo.texFormat;
257         offscreenTextureDesc.MipLevels          = 1;
258         offscreenTextureDesc.ArraySize          = 1;
259         offscreenTextureDesc.SampleDesc.Count   = getD3DSamples();
260         offscreenTextureDesc.SampleDesc.Quality = 0;
261         offscreenTextureDesc.Usage              = D3D11_USAGE_DEFAULT;
262         offscreenTextureDesc.BindFlags      = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
263         offscreenTextureDesc.CPUAccessFlags = 0;
264         offscreenTextureDesc.MiscFlags      = useSharedResource ? ANGLE_RESOURCE_SHARE_TYPE : 0;
265 
266         angle::Result result = mRenderer->allocateTexture(displayD3D, offscreenTextureDesc,
267                                                           backbufferFormatInfo, &mOffscreenTexture);
268         if (result == angle::Result::Stop)
269         {
270             ERR() << "Could not create offscreen texture, " << displayD3D->getStoredErrorString();
271             release();
272             return EGL_BAD_ALLOC;
273         }
274 
275         mOffscreenTexture.setDebugName("Offscreen back buffer texture");
276 
277         // EGL_ANGLE_surface_d3d_texture_2d_share_handle requires that we store a share handle for
278         // the client
279         if (useSharedResource)
280         {
281             IDXGIResource *offscreenTextureResource = nullptr;
282             HRESULT hr                              = mOffscreenTexture.get()->QueryInterface(
283                 __uuidof(IDXGIResource), (void **)&offscreenTextureResource);
284 
285             // Fall back to no share handle on failure
286             if (FAILED(hr))
287             {
288                 ERR() << "Could not query offscreen texture resource, " << gl::FmtHR(hr);
289             }
290             else
291             {
292                 hr = offscreenTextureResource->GetSharedHandle(&mShareHandle);
293                 SafeRelease(offscreenTextureResource);
294 
295                 if (FAILED(hr))
296                 {
297                     mShareHandle = nullptr;
298                     ERR() << "Could not get offscreen texture shared handle, " << gl::FmtHR(hr);
299                 }
300             }
301         }
302     }
303 
304     // This may return null if the original texture was created without a keyed mutex.
305     mKeyedMutex = d3d11::DynamicCastComObject<IDXGIKeyedMutex>(mOffscreenTexture.get());
306 
307     D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
308     offscreenRTVDesc.Format = backbufferFormatInfo.rtvFormat;
309     offscreenRTVDesc.ViewDimension =
310         (mEGLSamples <= 1) ? D3D11_RTV_DIMENSION_TEXTURE2D : D3D11_RTV_DIMENSION_TEXTURE2DMS;
311     offscreenRTVDesc.Texture2D.MipSlice = 0;
312 
313     angle::Result result = mRenderer->allocateResource(displayD3D, offscreenRTVDesc,
314                                                        mOffscreenTexture.get(), &mOffscreenRTView);
315     ASSERT(result != angle::Result::Stop);
316     mOffscreenRTView.setDebugName("Offscreen back buffer render target");
317 
318     D3D11_SHADER_RESOURCE_VIEW_DESC offscreenSRVDesc;
319     offscreenSRVDesc.Format = backbufferFormatInfo.srvFormat;
320     offscreenSRVDesc.ViewDimension =
321         (mEGLSamples <= 1) ? D3D11_SRV_DIMENSION_TEXTURE2D : D3D11_SRV_DIMENSION_TEXTURE2DMS;
322     offscreenSRVDesc.Texture2D.MostDetailedMip = 0;
323     offscreenSRVDesc.Texture2D.MipLevels       = static_cast<UINT>(-1);
324 
325     if (offscreenTextureDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE)
326     {
327         result = mRenderer->allocateResource(displayD3D, offscreenSRVDesc, mOffscreenTexture.get(),
328                                              &mOffscreenSRView);
329         ASSERT(result != angle::Result::Stop);
330         mOffscreenSRView.setDebugName("Offscreen back buffer shader resource");
331     }
332     else
333     {
334         // Special case for external textures that cannot support sampling. Since internally we
335         // assume our SwapChain is always readable, we make a copy texture that is compatible.
336         mNeedsOffscreenTextureCopy = true;
337     }
338 
339     if (previousOffscreenTexture.valid())
340     {
341         D3D11_BOX sourceBox = {};
342         sourceBox.left      = 0;
343         sourceBox.right     = std::min(previousWidth, backbufferWidth);
344         sourceBox.top       = std::max(previousHeight - backbufferHeight, 0);
345         sourceBox.bottom    = previousHeight;
346         sourceBox.front     = 0;
347         sourceBox.back      = 1;
348 
349         ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
350         const int yoffset                  = std::max(backbufferHeight - previousHeight, 0);
351         deviceContext->CopySubresourceRegion(mOffscreenTexture.get(), 0, 0, yoffset, 0,
352                                              previousOffscreenTexture.get(), 0, &sourceBox);
353 
354         if (mSwapChain)
355         {
356             swapRect(displayD3D, 0, 0, backbufferWidth, backbufferHeight);
357         }
358     }
359 
360     return EGL_SUCCESS;
361 }
362 
resetOffscreenDepthBuffer(DisplayD3D * displayD3D,int backbufferWidth,int backbufferHeight)363 EGLint SwapChain11::resetOffscreenDepthBuffer(DisplayD3D *displayD3D,
364                                               int backbufferWidth,
365                                               int backbufferHeight)
366 {
367     releaseOffscreenDepthBuffer();
368 
369     if (mDepthBufferFormat != GL_NONE)
370     {
371         const d3d11::Format &depthBufferFormatInfo =
372             d3d11::Format::Get(mDepthBufferFormat, mRenderer->getRenderer11DeviceCaps());
373 
374         D3D11_TEXTURE2D_DESC depthStencilTextureDesc;
375         depthStencilTextureDesc.Width            = backbufferWidth;
376         depthStencilTextureDesc.Height           = backbufferHeight;
377         depthStencilTextureDesc.Format           = depthBufferFormatInfo.texFormat;
378         depthStencilTextureDesc.MipLevels        = 1;
379         depthStencilTextureDesc.ArraySize        = 1;
380         depthStencilTextureDesc.SampleDesc.Count = getD3DSamples();
381         depthStencilTextureDesc.Usage            = D3D11_USAGE_DEFAULT;
382         depthStencilTextureDesc.BindFlags        = D3D11_BIND_DEPTH_STENCIL;
383 
384         // If there is a multisampled offscreen color texture, the offscreen depth-stencil texture
385         // must also have the same quality value.
386         if (mOffscreenTexture.valid() && getD3DSamples() > 1)
387         {
388             D3D11_TEXTURE2D_DESC offscreenTextureDesc = {};
389             mOffscreenTexture.getDesc(&offscreenTextureDesc);
390             depthStencilTextureDesc.SampleDesc.Quality = offscreenTextureDesc.SampleDesc.Quality;
391         }
392         else
393         {
394             depthStencilTextureDesc.SampleDesc.Quality = 0;
395         }
396 
397         // Only create an SRV if it is supported
398         bool depthStencilSRV =
399             depthBufferFormatInfo.srvFormat != DXGI_FORMAT_UNKNOWN &&
400             (mRenderer->getRenderer11DeviceCaps().supportsMultisampledDepthStencilSRVs ||
401              depthStencilTextureDesc.SampleDesc.Count <= 1);
402         if (depthStencilSRV)
403         {
404             depthStencilTextureDesc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
405         }
406 
407         depthStencilTextureDesc.CPUAccessFlags = 0;
408         depthStencilTextureDesc.MiscFlags      = 0;
409 
410         angle::Result result = mRenderer->allocateTexture(
411             displayD3D, depthStencilTextureDesc, depthBufferFormatInfo, &mDepthStencilTexture);
412         if (result == angle::Result::Stop)
413         {
414             ERR() << "Could not create depthstencil surface for new swap chain, "
415                   << displayD3D->getStoredErrorString();
416             release();
417             return EGL_BAD_ALLOC;
418         }
419         mDepthStencilTexture.setDebugName("Offscreen depth stencil texture");
420 
421         D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilDesc;
422         depthStencilDesc.Format = depthBufferFormatInfo.dsvFormat;
423         depthStencilDesc.ViewDimension =
424             (mEGLSamples <= 1) ? D3D11_DSV_DIMENSION_TEXTURE2D : D3D11_DSV_DIMENSION_TEXTURE2DMS;
425         depthStencilDesc.Flags              = 0;
426         depthStencilDesc.Texture2D.MipSlice = 0;
427 
428         result = mRenderer->allocateResource(displayD3D, depthStencilDesc,
429                                              mDepthStencilTexture.get(), &mDepthStencilDSView);
430         ASSERT(result != angle::Result::Stop);
431         mDepthStencilDSView.setDebugName("Offscreen depth stencil view");
432 
433         if (depthStencilSRV)
434         {
435             D3D11_SHADER_RESOURCE_VIEW_DESC depthStencilSRVDesc;
436             depthStencilSRVDesc.Format        = depthBufferFormatInfo.srvFormat;
437             depthStencilSRVDesc.ViewDimension = (mEGLSamples <= 1)
438                                                     ? D3D11_SRV_DIMENSION_TEXTURE2D
439                                                     : D3D11_SRV_DIMENSION_TEXTURE2DMS;
440             depthStencilSRVDesc.Texture2D.MostDetailedMip = 0;
441             depthStencilSRVDesc.Texture2D.MipLevels       = static_cast<UINT>(-1);
442 
443             result = mRenderer->allocateResource(displayD3D, depthStencilSRVDesc,
444                                                  mDepthStencilTexture.get(), &mDepthStencilSRView);
445             ASSERT(result != angle::Result::Stop);
446             mDepthStencilSRView.setDebugName("Offscreen depth stencil shader resource");
447         }
448     }
449 
450     return EGL_SUCCESS;
451 }
452 
resize(DisplayD3D * displayD3D,EGLint backbufferWidth,EGLint backbufferHeight)453 EGLint SwapChain11::resize(DisplayD3D *displayD3D, EGLint backbufferWidth, EGLint backbufferHeight)
454 {
455     ANGLE_TRACE_EVENT0("gpu.angle", "SwapChain11::resize");
456     ID3D11Device *device = mRenderer->getDevice();
457 
458     if (device == nullptr)
459     {
460         return EGL_BAD_ACCESS;
461     }
462 
463     // EGL allows creating a surface with 0x0 dimension, however, DXGI does not like 0x0 swapchains
464     if (backbufferWidth < 1 || backbufferHeight < 1)
465     {
466         return EGL_SUCCESS;
467     }
468 
469     // Don't resize unnecessarily
470     if (mWidth == backbufferWidth && mHeight == backbufferHeight)
471     {
472         return EGL_SUCCESS;
473     }
474 
475     // Can only call resize if we have already created our swap buffer and resources
476     ASSERT(mSwapChain && mBackBufferTexture.valid() && mBackBufferRTView.valid() &&
477            mBackBufferSRView.valid());
478 
479     mBackBufferTexture.reset();
480     mBackBufferRTView.reset();
481     mBackBufferSRView.reset();
482 
483     // Resize swap chain
484     DXGI_SWAP_CHAIN_DESC desc;
485     HRESULT hr = mSwapChain->GetDesc(&desc);
486     if (FAILED(hr))
487     {
488         ERR() << "Error reading swap chain description, " << gl::FmtHR(hr);
489         release();
490         return EGL_BAD_ALLOC;
491     }
492 
493     hr = mSwapChain->ResizeBuffers(desc.BufferCount, backbufferWidth, backbufferHeight,
494                                    getSwapChainNativeFormat(), 0);
495 
496     if (FAILED(hr))
497     {
498         ERR() << "Error resizing swap chain buffers, " << gl::FmtHR(hr);
499         release();
500 
501         if (d3d11::isDeviceLostError(hr))
502         {
503             return EGL_CONTEXT_LOST;
504         }
505         else
506         {
507             return EGL_BAD_ALLOC;
508         }
509     }
510 
511     ID3D11Texture2D *backbufferTexture = nullptr;
512     hr                                 = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
513                                reinterpret_cast<void **>(&backbufferTexture));
514     ASSERT(SUCCEEDED(hr));
515     if (SUCCEEDED(hr))
516     {
517         const auto &format =
518             d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps());
519         mBackBufferTexture.set(backbufferTexture, format);
520         mBackBufferTexture.setDebugName("Back buffer texture");
521 
522         angle::Result result = mRenderer->allocateResourceNoDesc(
523             displayD3D, mBackBufferTexture.get(), &mBackBufferRTView);
524         ASSERT(result != angle::Result::Stop);
525         mBackBufferRTView.setDebugName("Back buffer render target");
526 
527         result = mRenderer->allocateResourceNoDesc(displayD3D, mBackBufferTexture.get(),
528                                                    &mBackBufferSRView);
529         ASSERT(result != angle::Result::Stop);
530         mBackBufferSRView.setDebugName("Back buffer shader resource");
531     }
532 
533     mFirstSwap = true;
534 
535     return resetOffscreenBuffers(displayD3D, backbufferWidth, backbufferHeight);
536 }
537 
getSwapChainNativeFormat() const538 DXGI_FORMAT SwapChain11::getSwapChainNativeFormat() const
539 {
540     // Return a render target format for offscreen rendering is supported by IDXGISwapChain.
541     // MSDN https://msdn.microsoft.com/en-us/library/windows/desktop/bb173064(v=vs.85).aspx
542     switch (mOffscreenRenderTargetFormat)
543     {
544         case GL_RGBA8:
545         case GL_RGBA4:
546         case GL_RGB5_A1:
547         case GL_RGB8:
548         case GL_RGB565:
549             return DXGI_FORMAT_R8G8B8A8_UNORM;
550 
551         case GL_BGRA8_EXT:
552             return DXGI_FORMAT_B8G8R8A8_UNORM;
553 
554         case GL_RGB10_A2:
555             return DXGI_FORMAT_R10G10B10A2_UNORM;
556 
557         case GL_RGBA16F:
558             return DXGI_FORMAT_R16G16B16A16_FLOAT;
559 
560         default:
561             UNREACHABLE();
562             return DXGI_FORMAT_UNKNOWN;
563     }
564 }
565 
reset(DisplayD3D * displayD3D,EGLint backbufferWidth,EGLint backbufferHeight,EGLint swapInterval)566 EGLint SwapChain11::reset(DisplayD3D *displayD3D,
567                           EGLint backbufferWidth,
568                           EGLint backbufferHeight,
569                           EGLint swapInterval)
570 {
571     mSwapInterval = static_cast<unsigned int>(swapInterval);
572     if (mSwapInterval > 4)
573     {
574         // IDXGISwapChain::Present documentation states that valid sync intervals are in the [0,4]
575         // range
576         return EGL_BAD_PARAMETER;
577     }
578 
579     // If the swap chain already exists, just resize
580     if (mSwapChain != nullptr)
581     {
582         return resize(displayD3D, backbufferWidth, backbufferHeight);
583     }
584 
585     ANGLE_TRACE_EVENT0("gpu.angle", "SwapChain11::reset");
586     ID3D11Device *device = mRenderer->getDevice();
587 
588     if (device == nullptr)
589     {
590         return EGL_BAD_ACCESS;
591     }
592 
593     // Release specific resources to free up memory for the new render target, while the
594     // old render target still exists for the purpose of preserving its contents.
595     SafeRelease(mSwapChain1);
596     SafeRelease(mSwapChain);
597     mBackBufferTexture.reset();
598     mBackBufferRTView.reset();
599 
600     // EGL allows creating a surface with 0x0 dimension, however, DXGI does not like 0x0 swapchains
601     if (backbufferWidth < 1 || backbufferHeight < 1)
602     {
603         releaseOffscreenColorBuffer();
604         return EGL_SUCCESS;
605     }
606 
607     if (mNativeWindow->getNativeWindow())
608     {
609         HRESULT hr = mNativeWindow->createSwapChain(
610             device, mRenderer->getDxgiFactory(), getSwapChainNativeFormat(), backbufferWidth,
611             backbufferHeight, mNeedsOffscreenTexture ? 1 : getD3DSamples(), &mSwapChain);
612 
613         if (FAILED(hr))
614         {
615             ERR() << "Could not create additional swap chains or offscreen surfaces, "
616                   << gl::FmtHR(hr);
617             release();
618 
619             if (d3d11::isDeviceLostError(hr))
620             {
621                 return EGL_CONTEXT_LOST;
622             }
623             else
624             {
625                 return EGL_BAD_ALLOC;
626             }
627         }
628 
629         if (mRenderer->getRenderer11DeviceCaps().supportsDXGI1_2)
630         {
631             mSwapChain1 = d3d11::DynamicCastComObject<IDXGISwapChain1>(mSwapChain);
632         }
633 
634         ID3D11Texture2D *backbufferTex = nullptr;
635         hr                             = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
636                                    reinterpret_cast<LPVOID *>(&backbufferTex));
637         ASSERT(SUCCEEDED(hr));
638         const auto &format =
639             d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps());
640         mBackBufferTexture.set(backbufferTex, format);
641         mBackBufferTexture.setDebugName("Back buffer texture");
642 
643         angle::Result result = mRenderer->allocateResourceNoDesc(
644             displayD3D, mBackBufferTexture.get(), &mBackBufferRTView);
645         ASSERT(result != angle::Result::Stop);
646         mBackBufferRTView.setDebugName("Back buffer render target");
647 
648         result = mRenderer->allocateResourceNoDesc(displayD3D, mBackBufferTexture.get(),
649                                                    &mBackBufferSRView);
650         ASSERT(result != angle::Result::Stop);
651         mBackBufferSRView.setDebugName("Back buffer shader resource view");
652     }
653 
654     mFirstSwap = true;
655 
656     return resetOffscreenBuffers(displayD3D, backbufferWidth, backbufferHeight);
657 }
658 
initPassThroughResources(DisplayD3D * displayD3D)659 angle::Result SwapChain11::initPassThroughResources(DisplayD3D *displayD3D)
660 {
661     if (mPassThroughResourcesInit)
662     {
663         return angle::Result::Continue;
664     }
665 
666     ANGLE_TRACE_EVENT0("gpu.angle", "SwapChain11::initPassThroughResources");
667     ID3D11Device *device = mRenderer->getDevice();
668 
669     ASSERT(device != nullptr);
670 
671     // Make sure our resources are all not allocated, when we create
672     ASSERT(!mQuadVB.valid() && !mPassThroughSampler.valid());
673     ASSERT(!mPassThroughIL.valid() && !mPassThroughVS.valid() && !mPassThroughOrResolvePS.valid());
674 
675     D3D11_BUFFER_DESC vbDesc;
676     vbDesc.ByteWidth           = sizeof(d3d11::PositionTexCoordVertex) * 4;
677     vbDesc.Usage               = D3D11_USAGE_DYNAMIC;
678     vbDesc.BindFlags           = D3D11_BIND_VERTEX_BUFFER;
679     vbDesc.CPUAccessFlags      = D3D11_CPU_ACCESS_WRITE;
680     vbDesc.MiscFlags           = 0;
681     vbDesc.StructureByteStride = 0;
682 
683     ANGLE_TRY(mRenderer->allocateResource(displayD3D, vbDesc, &mQuadVB));
684     mQuadVB.setDebugName("Swap chain quad vertex buffer");
685 
686     D3D11_SAMPLER_DESC samplerDesc;
687     samplerDesc.Filter         = D3D11_FILTER_MIN_MAG_MIP_POINT;
688     samplerDesc.AddressU       = D3D11_TEXTURE_ADDRESS_CLAMP;
689     samplerDesc.AddressV       = D3D11_TEXTURE_ADDRESS_CLAMP;
690     samplerDesc.AddressW       = D3D11_TEXTURE_ADDRESS_CLAMP;
691     samplerDesc.MipLODBias     = 0.0f;
692     samplerDesc.MaxAnisotropy  = 0;
693     samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
694     samplerDesc.BorderColor[0] = 0.0f;
695     samplerDesc.BorderColor[1] = 0.0f;
696     samplerDesc.BorderColor[2] = 0.0f;
697     samplerDesc.BorderColor[3] = 0.0f;
698     samplerDesc.MinLOD         = 0;
699     samplerDesc.MaxLOD         = D3D11_FLOAT32_MAX;
700 
701     ANGLE_TRY(mRenderer->allocateResource(displayD3D, samplerDesc, &mPassThroughSampler));
702     mPassThroughSampler.setDebugName("Swap chain pass through sampler");
703 
704     D3D11_INPUT_ELEMENT_DESC quadLayout[] = {
705         {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
706         {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
707     };
708 
709     InputElementArray quadElements(quadLayout);
710     ShaderData vertexShaderData(g_VS_Passthrough2D);
711 
712     ANGLE_TRY(
713         mRenderer->allocateResource(displayD3D, quadElements, &vertexShaderData, &mPassThroughIL));
714     mPassThroughIL.setDebugName("Swap chain pass through layout");
715 
716     ANGLE_TRY(mRenderer->allocateResource(displayD3D, vertexShaderData, &mPassThroughVS));
717     mPassThroughVS.setDebugName("Swap chain pass through vertex shader");
718 
719     if (mEGLSamples <= 1)
720     {
721         ShaderData pixelShaderData(g_PS_PassthroughRGBA2D);
722         ANGLE_TRY(
723             mRenderer->allocateResource(displayD3D, pixelShaderData, &mPassThroughOrResolvePS));
724     }
725     else
726     {
727         if (mNativeWindow->getNativeWindow() && mNeedsOffscreenTexture)
728         {
729             ShaderData pixelShaderData(g_PS_ResolveColor2D);
730             ANGLE_TRY(
731                 mRenderer->allocateResource(displayD3D, pixelShaderData, &mPassThroughOrResolvePS));
732         }
733         else
734         {
735             ShaderData pixelShaderData(g_PS_PassthroughRGBA2DMS);
736             ANGLE_TRY(
737                 mRenderer->allocateResource(displayD3D, pixelShaderData, &mPassThroughOrResolvePS));
738         }
739     }
740 
741     mPassThroughOrResolvePS.setDebugName("Swap chain pass through pixel shader");
742 
743     // Use the default rasterizer state but without culling
744     D3D11_RASTERIZER_DESC rasterizerDesc;
745     rasterizerDesc.FillMode              = D3D11_FILL_SOLID;
746     rasterizerDesc.CullMode              = D3D11_CULL_NONE;
747     rasterizerDesc.FrontCounterClockwise = FALSE;
748     rasterizerDesc.DepthBias             = 0;
749     rasterizerDesc.SlopeScaledDepthBias  = 0.0f;
750     rasterizerDesc.DepthBiasClamp        = 0.0f;
751     rasterizerDesc.DepthClipEnable       = TRUE;
752     rasterizerDesc.ScissorEnable         = FALSE;
753     rasterizerDesc.MultisampleEnable     = FALSE;
754     rasterizerDesc.AntialiasedLineEnable = FALSE;
755 
756     ANGLE_TRY(mRenderer->allocateResource(displayD3D, rasterizerDesc, &mPassThroughRS));
757     mPassThroughRS.setDebugName("Swap chain pass through rasterizer state");
758 
759     mPassThroughResourcesInit = true;
760     return angle::Result::Continue;
761 }
762 
763 // parameters should be validated/clamped by caller
swapRect(DisplayD3D * displayD3D,EGLint x,EGLint y,EGLint width,EGLint height)764 EGLint SwapChain11::swapRect(DisplayD3D *displayD3D,
765                              EGLint x,
766                              EGLint y,
767                              EGLint width,
768                              EGLint height)
769 {
770     if (mNeedsOffscreenTexture)
771     {
772         EGLint result = copyOffscreenToBackbuffer(displayD3D, x, y, width, height);
773         if (result != EGL_SUCCESS)
774         {
775             return result;
776         }
777     }
778 
779     EGLint result = present(displayD3D, x, y, width, height);
780     if (result != EGL_SUCCESS)
781     {
782         return result;
783     }
784 
785     mRenderer->onSwap();
786 
787     return EGL_SUCCESS;
788 }
789 
copyOffscreenToBackbuffer(DisplayD3D * displayD3D,EGLint x,EGLint y,EGLint width,EGLint height)790 EGLint SwapChain11::copyOffscreenToBackbuffer(DisplayD3D *displayD3D,
791                                               EGLint x,
792                                               EGLint y,
793                                               EGLint width,
794                                               EGLint height)
795 {
796     if (!mSwapChain)
797     {
798         return EGL_SUCCESS;
799     }
800 
801     if (initPassThroughResources(displayD3D) == angle::Result::Stop)
802     {
803         return EGL_BAD_ALLOC;
804     }
805 
806     ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
807 
808     // Set vertices
809     D3D11_MAPPED_SUBRESOURCE mappedResource;
810     HRESULT result =
811         deviceContext->Map(mQuadVB.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
812     if (FAILED(result))
813     {
814         return EGL_BAD_ACCESS;
815     }
816 
817     d3d11::PositionTexCoordVertex *vertices =
818         static_cast<d3d11::PositionTexCoordVertex *>(mappedResource.pData);
819 
820     // Create a quad in homogeneous coordinates
821     float x1 = (x / float(mWidth)) * 2.0f - 1.0f;
822     float y1 = (y / float(mHeight)) * 2.0f - 1.0f;
823     float x2 = ((x + width) / float(mWidth)) * 2.0f - 1.0f;
824     float y2 = ((y + height) / float(mHeight)) * 2.0f - 1.0f;
825 
826     float u1 = x / float(mWidth);
827     float v1 = y / float(mHeight);
828     float u2 = (x + width) / float(mWidth);
829     float v2 = (y + height) / float(mHeight);
830 
831     // Invert the quad vertices depending on the surface orientation.
832     if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE) != 0)
833     {
834         std::swap(x1, x2);
835     }
836     if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) != 0)
837     {
838         std::swap(y1, y2);
839     }
840 
841     d3d11::SetPositionTexCoordVertex(&vertices[0], x1, y1, u1, v1);
842     d3d11::SetPositionTexCoordVertex(&vertices[1], x1, y2, u1, v2);
843     d3d11::SetPositionTexCoordVertex(&vertices[2], x2, y1, u2, v1);
844     d3d11::SetPositionTexCoordVertex(&vertices[3], x2, y2, u2, v2);
845 
846     deviceContext->Unmap(mQuadVB.get(), 0);
847 
848     StateManager11 *stateManager = mRenderer->getStateManager();
849 
850     constexpr UINT stride = sizeof(d3d11::PositionTexCoordVertex);
851     stateManager->setSingleVertexBuffer(&mQuadVB, stride, 0);
852 
853     // Apply state
854     stateManager->setDepthStencilState(nullptr, 0xFFFFFFFF);
855     stateManager->setSimpleBlendState(nullptr);
856     stateManager->setRasterizerState(&mPassThroughRS);
857 
858     // Apply shaders
859     stateManager->setInputLayout(&mPassThroughIL);
860     stateManager->setPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
861     stateManager->setDrawShaders(&mPassThroughVS, nullptr, &mPassThroughOrResolvePS);
862 
863     // Apply render targets. Use the proxy context in display.
864     stateManager->setRenderTarget(mBackBufferRTView.get(), nullptr);
865 
866     // Set the viewport
867     stateManager->setSimpleViewport(mWidth, mHeight);
868 
869     // Apply textures
870     stateManager->setSimplePixelTextureAndSampler(mOffscreenSRView, mPassThroughSampler);
871 
872     // Draw
873     deviceContext->Draw(4, 0);
874 
875     return EGL_SUCCESS;
876 }
877 
present(DisplayD3D * displayD3D,EGLint x,EGLint y,EGLint width,EGLint height)878 EGLint SwapChain11::present(DisplayD3D *displayD3D, EGLint x, EGLint y, EGLint width, EGLint height)
879 {
880     if (!mSwapChain)
881     {
882         return EGL_SUCCESS;
883     }
884 
885     UINT swapInterval = mSwapInterval;
886 #if ANGLE_VSYNC == ANGLE_DISABLED
887     swapInterval = 0;
888 #endif
889 
890     HRESULT result = S_OK;
891 
892     // Use IDXGISwapChain1::Present1 with a dirty rect if DXGI 1.2 is available.
893     // Dirty rect present is not supported with a multisampled swapchain.
894     if (mSwapChain1 != nullptr && mEGLSamples <= 1)
895     {
896         if (mFirstSwap)
897         {
898             // Can't swap with a dirty rect if this swap chain has never swapped before
899             DXGI_PRESENT_PARAMETERS params = {0, nullptr, nullptr, nullptr};
900             result                         = mSwapChain1->Present1(swapInterval, 0, &params);
901         }
902         else
903         {
904             RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
905                          static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)};
906             DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr};
907             result                         = mSwapChain1->Present1(swapInterval, 0, &params);
908         }
909     }
910     else
911     {
912         result = mSwapChain->Present(swapInterval, 0);
913     }
914 
915     mFirstSwap = false;
916 
917     // Some swapping mechanisms such as DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL unbind the current render
918     // target. Mark it dirty. Use the proxy context in display since there is none available.
919     mRenderer->getStateManager()->invalidateRenderTarget();
920 
921     if (result == DXGI_ERROR_DEVICE_REMOVED)
922     {
923         ERR() << "Present failed: the D3D11 device was removed, "
924               << gl::FmtHR(mRenderer->getDevice()->GetDeviceRemovedReason());
925         return EGL_CONTEXT_LOST;
926     }
927     else if (result == DXGI_ERROR_DEVICE_RESET)
928     {
929         ERR() << "Present failed: the D3D11 device was reset from a bad command.";
930         return EGL_CONTEXT_LOST;
931     }
932     else if (FAILED(result))
933     {
934         ERR() << "Present failed with " << gl::FmtHR(result);
935     }
936 
937     mNativeWindow->commitChange();
938 
939     return EGL_SUCCESS;
940 }
941 
getOffscreenTexture()942 const TextureHelper11 &SwapChain11::getOffscreenTexture()
943 {
944     return mNeedsOffscreenTexture ? mOffscreenTexture : mBackBufferTexture;
945 }
946 
getRenderTarget()947 const d3d11::RenderTargetView &SwapChain11::getRenderTarget()
948 {
949     return mNeedsOffscreenTexture ? mOffscreenRTView : mBackBufferRTView;
950 }
951 
getRenderTargetShaderResource(d3d::Context * context)952 const d3d11::SharedSRV &SwapChain11::getRenderTargetShaderResource(d3d::Context *context)
953 {
954     if (!mNeedsOffscreenTexture)
955     {
956         ASSERT(mBackBufferSRView.valid());
957         return mBackBufferSRView;
958     }
959 
960     if (!mNeedsOffscreenTextureCopy)
961     {
962         ASSERT(mOffscreenSRView.valid());
963         return mOffscreenSRView;
964     }
965 
966     if (!mOffscreenTextureCopyForSRV.valid())
967     {
968         const d3d11::Format &backbufferFormatInfo =
969             d3d11::Format::Get(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps());
970 
971         D3D11_TEXTURE2D_DESC offscreenCopyDesc;
972         mOffscreenTexture.getDesc(&offscreenCopyDesc);
973 
974         offscreenCopyDesc.BindFlags      = D3D11_BIND_SHADER_RESOURCE;
975         offscreenCopyDesc.MiscFlags      = 0;
976         offscreenCopyDesc.CPUAccessFlags = 0;
977         angle::Result result             = mRenderer->allocateTexture(
978             context, offscreenCopyDesc, backbufferFormatInfo, &mOffscreenTextureCopyForSRV);
979         ASSERT(result != angle::Result::Stop);
980         mOffscreenTextureCopyForSRV.setDebugName("Offscreen back buffer copy for SRV");
981 
982         D3D11_SHADER_RESOURCE_VIEW_DESC offscreenSRVDesc;
983         offscreenSRVDesc.Format = backbufferFormatInfo.srvFormat;
984         offscreenSRVDesc.ViewDimension =
985             (mEGLSamples <= 1) ? D3D11_SRV_DIMENSION_TEXTURE2D : D3D11_SRV_DIMENSION_TEXTURE2DMS;
986         offscreenSRVDesc.Texture2D.MostDetailedMip = 0;
987         offscreenSRVDesc.Texture2D.MipLevels       = static_cast<UINT>(-1);
988 
989         result = mRenderer->allocateResource(context, offscreenSRVDesc,
990                                              mOffscreenTextureCopyForSRV.get(), &mOffscreenSRView);
991         ASSERT(result != angle::Result::Stop);
992         mOffscreenSRView.setDebugName("Offscreen back buffer shader resource");
993     }
994 
995     // Need to copy the offscreen texture into the shader-readable copy, since it's external and
996     // we don't know if the copy is up-to-date. This works around the problem we have when the app
997     // passes in a texture that isn't shader-readable.
998     mRenderer->getDeviceContext()->CopyResource(mOffscreenTextureCopyForSRV.get(),
999                                                 mOffscreenTexture.get());
1000     return mOffscreenSRView;
1001 }
1002 
getDepthStencil()1003 const d3d11::DepthStencilView &SwapChain11::getDepthStencil()
1004 {
1005     return mDepthStencilDSView;
1006 }
1007 
getDepthStencilShaderResource()1008 const d3d11::SharedSRV &SwapChain11::getDepthStencilShaderResource()
1009 {
1010     return mDepthStencilSRView;
1011 }
1012 
getDepthStencilTexture()1013 const TextureHelper11 &SwapChain11::getDepthStencilTexture()
1014 {
1015     return mDepthStencilTexture;
1016 }
1017 
getKeyedMutex()1018 void *SwapChain11::getKeyedMutex()
1019 {
1020     return mKeyedMutex;
1021 }
1022 
recreate()1023 void SwapChain11::recreate()
1024 {
1025     // possibly should use this method instead of reset
1026 }
1027 
getColorRenderTarget()1028 RenderTargetD3D *SwapChain11::getColorRenderTarget()
1029 {
1030     return &mColorRenderTarget;
1031 }
1032 
getDepthStencilRenderTarget()1033 RenderTargetD3D *SwapChain11::getDepthStencilRenderTarget()
1034 {
1035     return &mDepthStencilRenderTarget;
1036 }
1037 
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)1038 egl::Error SwapChain11::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
1039 {
1040     if (!mSwapChain)
1041     {
1042         return egl::EglNotInitialized() << "Swap chain uninitialized";
1043     }
1044 
1045     DXGI_FRAME_STATISTICS stats = {};
1046     HRESULT result              = mSwapChain->GetFrameStatistics(&stats);
1047 
1048     if (FAILED(result))
1049     {
1050         return egl::EglBadAlloc() << "Failed to get frame statistics, " << gl::FmtHR(result);
1051     }
1052 
1053     // Conversion from DXGI_FRAME_STATISTICS to the output values:
1054     // stats.SyncRefreshCount -> msc
1055     // stats.PresentCount -> sbc
1056     // stats.SyncQPCTime -> ust with conversion to microseconds via QueryPerformanceFrequency
1057     *msc = stats.SyncRefreshCount;
1058     *sbc = stats.PresentCount;
1059 
1060     LONGLONG syncQPCValue = stats.SyncQPCTime.QuadPart;
1061     // If the QPC Value is below the overflow threshold, we proceed with
1062     // simple multiply and divide.
1063     if (syncQPCValue < kQPCOverflowThreshold)
1064     {
1065         *ust = syncQPCValue * kMicrosecondsPerSecond / mQPCFrequency;
1066     }
1067     else
1068     {
1069         // Otherwise, calculate microseconds in a round about manner to avoid
1070         // overflow and precision issues.
1071         int64_t wholeSeconds  = syncQPCValue / mQPCFrequency;
1072         int64_t leftoverTicks = syncQPCValue - (wholeSeconds * mQPCFrequency);
1073         *ust                  = wholeSeconds * kMicrosecondsPerSecond +
1074                leftoverTicks * kMicrosecondsPerSecond / mQPCFrequency;
1075     }
1076 
1077     return egl::NoError();
1078 }
1079 
getD3DSamples() const1080 UINT SwapChain11::getD3DSamples() const
1081 {
1082     return (mEGLSamples == 0) ? 1 : mEGLSamples;
1083 }
1084 
1085 }  // namespace rx
1086