1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwindowsdirect2dcontext.h"
41 #include "qwindowsdirect2dbitmap.h"
42 #include "qwindowsdirect2dwindow.h"
43 #include "qwindowsdirect2ddevicecontext.h"
44 #include "qwindowsdirect2dhelpers.h"
45 #include "qwindowsdirect2dplatformpixmap.h"
46 
47 #include <d3d11.h>
48 #include <d2d1_1.h>
49 #include <dxgi1_2.h>
50 
51 using Microsoft::WRL::ComPtr;
52 
53 QT_BEGIN_NAMESPACE
54 
QWindowsDirect2DWindow(QWindow * window,const QWindowsWindowData & data)55 QWindowsDirect2DWindow::QWindowsDirect2DWindow(QWindow *window, const QWindowsWindowData &data)
56     : QWindowsWindow(window, data)
57     , m_directRendering(!(data.flags & Qt::FramelessWindowHint && window->format().hasAlpha()))
58 {
59     if (window->type() == Qt::Desktop)
60         return; // No further handling for Qt::Desktop
61 
62     if (m_directRendering)
63         setupSwapChain();
64 
65     HRESULT hr = QWindowsDirect2DContext::instance()->d2dDevice()->CreateDeviceContext(
66                 D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
67                 m_deviceContext.GetAddressOf());
68     if (FAILED(hr))
69         qWarning("%s: Couldn't create Direct2D Device context: %#lx", __FUNCTION__, hr);
70 }
71 
~QWindowsDirect2DWindow()72 QWindowsDirect2DWindow::~QWindowsDirect2DWindow()
73 {
74 }
75 
setWindowFlags(Qt::WindowFlags flags)76 void QWindowsDirect2DWindow::setWindowFlags(Qt::WindowFlags flags)
77 {
78     m_directRendering = !(flags & Qt::FramelessWindowHint && window()->format().hasAlpha());
79     if (!m_directRendering)
80         m_swapChain.Reset(); // No need for the swap chain; release from memory
81     else if (!m_swapChain)
82         setupSwapChain();
83 
84     QWindowsWindow::setWindowFlags(flags);
85 }
86 
pixmap()87 QPixmap *QWindowsDirect2DWindow::pixmap()
88 {
89     setupBitmap();
90 
91     return m_pixmap.data();
92 }
93 
flush(QWindowsDirect2DBitmap * bitmap,const QRegion & region,const QPoint & offset)94 void QWindowsDirect2DWindow::flush(QWindowsDirect2DBitmap *bitmap, const QRegion &region, const QPoint &offset)
95 {
96     QSize size;
97     if (m_directRendering) {
98         DXGI_SWAP_CHAIN_DESC1 desc;
99         HRESULT hr = m_swapChain->GetDesc1(&desc);
100         QRect geom = geometry();
101 
102         if (FAILED(hr) || (desc.Width != UINT(geom.width()) || (desc.Height != UINT(geom.height())))) {
103             resizeSwapChain(geom.size());
104             m_swapChain->GetDesc1(&desc);
105         }
106         size.setWidth(int(desc.Width));
107         size.setHeight(int(desc.Height));
108     } else {
109         size = geometry().size();
110     }
111 
112     setupBitmap();
113     if (!m_bitmap)
114         return;
115 
116     if (bitmap != m_bitmap.data()) {
117         m_bitmap->deviceContext()->begin();
118 
119         ID2D1DeviceContext *dc = m_bitmap->deviceContext()->get();
120         if (!m_needsFullFlush) {
121             QRegion clipped = region;
122             clipped &= QRect(QPoint(), size);
123 
124             for (const QRect &rect : clipped) {
125                 QRectF rectF(rect);
126                 dc->DrawBitmap(bitmap->bitmap(),
127                                to_d2d_rect_f(rectF),
128                                1.0,
129                                D2D1_INTERPOLATION_MODE_LINEAR,
130                                to_d2d_rect_f(rectF.translated(offset.x(), offset.y())));
131             }
132         } else {
133             QRectF rectF(QPoint(), size);
134             dc->DrawBitmap(bitmap->bitmap(),
135                            to_d2d_rect_f(rectF),
136                            1.0,
137                            D2D1_INTERPOLATION_MODE_LINEAR,
138                            to_d2d_rect_f(rectF.translated(offset.x(), offset.y())));
139             m_needsFullFlush = false;
140         }
141 
142         m_bitmap->deviceContext()->end();
143     }
144 }
145 
present(const QRegion & region)146 void QWindowsDirect2DWindow::present(const QRegion &region)
147 {
148     if (m_directRendering) {
149         m_swapChain->Present(0, 0);
150         return;
151     }
152 
153     ComPtr<IDXGISurface> bitmapSurface;
154     HRESULT hr = m_bitmap->bitmap()->GetSurface(&bitmapSurface);
155     Q_ASSERT(SUCCEEDED(hr));
156     ComPtr<IDXGISurface1> dxgiSurface;
157     hr = bitmapSurface.As(&dxgiSurface);
158     Q_ASSERT(SUCCEEDED(hr));
159 
160     HDC hdc;
161     hr = dxgiSurface->GetDC(FALSE, &hdc);
162     if (FAILED(hr)) {
163         qErrnoWarning(hr, "Failed to get DC for presenting the surface");
164         return;
165     }
166 
167     const QRect bounds = window()->geometry();
168     const SIZE size = { bounds.width(), bounds.height() };
169     const POINT ptDst = { bounds.x(), bounds.y() };
170     const POINT ptSrc = { 0, 0 };
171     const BLENDFUNCTION blend = { AC_SRC_OVER, 0, BYTE(255.0 * opacity()), AC_SRC_ALPHA };
172     const QRect r = region.boundingRect();
173     const RECT dirty = { r.left(), r.top(), r.left() + r.width(), r.top() + r.height() };
174     UPDATELAYEREDWINDOWINFO info = { sizeof(UPDATELAYEREDWINDOWINFO), nullptr,
175                                      &ptDst, &size, hdc, &ptSrc, 0, &blend, ULW_ALPHA, &dirty };
176     if (!UpdateLayeredWindowIndirect(handle(), &info))
177         qErrnoWarning(int(GetLastError()), "Failed to update the layered window");
178 
179     hr = dxgiSurface->ReleaseDC(nullptr);
180     if (FAILED(hr))
181         qErrnoWarning(hr, "Failed to release the DC for presentation");
182 }
183 
setupSwapChain()184 void QWindowsDirect2DWindow::setupSwapChain()
185 {
186     DXGI_SWAP_CHAIN_DESC1 desc = {};
187 
188     desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
189     desc.SampleDesc.Count = 1;
190     desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
191     desc.BufferCount = 1;
192     desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
193 
194     HRESULT hr = QWindowsDirect2DContext::instance()->dxgiFactory()->CreateSwapChainForHwnd(
195                 QWindowsDirect2DContext::instance()->d3dDevice(), // [in]   IUnknown *pDevice
196                 handle(),                                         // [in]   HWND hWnd
197                 &desc,                                            // [in]   const DXGI_SWAP_CHAIN_DESC1 *pDesc
198                 nullptr,                                          // [in]   const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc
199                 nullptr,                                          // [in]   IDXGIOutput *pRestrictToOutput
200                 m_swapChain.ReleaseAndGetAddressOf());            // [out]  IDXGISwapChain1 **ppSwapChain
201 
202     if (FAILED(hr))
203         qWarning("%s: Could not create swap chain: %#lx", __FUNCTION__, hr);
204 
205     m_needsFullFlush = true;
206 }
207 
resizeSwapChain(const QSize & size)208 void QWindowsDirect2DWindow::resizeSwapChain(const QSize &size)
209 {
210     m_pixmap.reset();
211     m_bitmap.reset();
212     m_deviceContext->SetTarget(nullptr);
213     m_needsFullFlush = true;
214 
215     if (!m_swapChain)
216         return;
217 
218     HRESULT hr = m_swapChain->ResizeBuffers(0,
219                                             UINT(size.width()), UINT(size.height()),
220                                             DXGI_FORMAT_UNKNOWN,
221                                             0);
222     if (FAILED(hr))
223         qWarning("%s: Could not resize swap chain: %#lx", __FUNCTION__, hr);
224 }
225 
copyBackBuffer() const226 QSharedPointer<QWindowsDirect2DBitmap> QWindowsDirect2DWindow::copyBackBuffer() const
227 {
228     const QSharedPointer<QWindowsDirect2DBitmap> null_result;
229 
230     if (!m_bitmap)
231         return null_result;
232 
233     D2D1_PIXEL_FORMAT format = m_bitmap->bitmap()->GetPixelFormat();
234     D2D1_SIZE_U size = m_bitmap->bitmap()->GetPixelSize();
235 
236     FLOAT dpiX, dpiY;
237     m_bitmap->bitmap()->GetDpi(&dpiX, &dpiY);
238 
239     D2D1_BITMAP_PROPERTIES1 properties = {
240         format,                     // D2D1_PIXEL_FORMAT pixelFormat;
241         dpiX,                       // FLOAT dpiX;
242         dpiY,                       // FLOAT dpiY;
243         D2D1_BITMAP_OPTIONS_TARGET, // D2D1_BITMAP_OPTIONS bitmapOptions;
244         nullptr                   // _Field_size_opt_(1) ID2D1ColorContext *colorContext;
245     };
246     ComPtr<ID2D1Bitmap1> copy;
247     HRESULT hr = m_deviceContext.Get()->CreateBitmap(size, nullptr, 0, properties, &copy);
248 
249     if (FAILED(hr)) {
250         qWarning("%s: Could not create staging bitmap: %#lx", __FUNCTION__, hr);
251         return null_result;
252     }
253 
254     hr = copy.Get()->CopyFromBitmap(nullptr, m_bitmap->bitmap(), nullptr);
255     if (FAILED(hr)) {
256         qWarning("%s: Could not copy from bitmap! %#lx", __FUNCTION__, hr);
257         return null_result;
258     }
259 
260     return QSharedPointer<QWindowsDirect2DBitmap>(new QWindowsDirect2DBitmap(copy.Get(), nullptr));
261 }
262 
setupBitmap()263 void QWindowsDirect2DWindow::setupBitmap()
264 {
265     if (m_bitmap)
266         return;
267 
268     if (!m_deviceContext)
269         return;
270 
271     if (m_directRendering && !m_swapChain)
272         return;
273 
274     HRESULT hr;
275     ComPtr<IDXGISurface1> backBufferSurface;
276     if (m_directRendering) {
277         hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBufferSurface));
278         if (FAILED(hr)) {
279             qWarning("%s: Could not query backbuffer for DXGI Surface: %#lx", __FUNCTION__, hr);
280             return;
281         }
282     } else {
283         const QRect rect = geometry();
284         CD3D11_TEXTURE2D_DESC backBufferDesc(DXGI_FORMAT_B8G8R8A8_UNORM, UINT(rect.width()), UINT(rect.height()), 1, 1);
285         backBufferDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
286         backBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;
287         ComPtr<ID3D11Texture2D> backBufferTexture;
288         HRESULT hr = QWindowsDirect2DContext::instance()->d3dDevice()->CreateTexture2D(&backBufferDesc, nullptr, &backBufferTexture);
289         if (FAILED(hr)) {
290             qErrnoWarning(hr, "Failed to create backing texture for indirect rendering");
291             return;
292         }
293 
294         hr = backBufferTexture.As(&backBufferSurface);
295         if (FAILED(hr)) {
296             qErrnoWarning(hr, "Failed to cast back buffer surface to DXGI surface");
297             return;
298         }
299     }
300 
301     ComPtr<ID2D1Bitmap1> backBufferBitmap;
302     hr = m_deviceContext->CreateBitmapFromDxgiSurface(backBufferSurface.Get(), nullptr, backBufferBitmap.GetAddressOf());
303     if (FAILED(hr)) {
304         qWarning("%s: Could not create Direct2D Bitmap from DXGI Surface: %#lx", __FUNCTION__, hr);
305         return;
306     }
307 
308     m_bitmap.reset(new QWindowsDirect2DBitmap(backBufferBitmap.Get(), m_deviceContext.Get()));
309 
310     QWindowsDirect2DPaintEngine::Flags flags = QWindowsDirect2DPaintEngine::NoFlag;
311     if (!m_directRendering)
312         flags |= QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow;
313     auto *pp = new QWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType,
314                                                                             flags,
315                                                                             m_bitmap.data());
316     m_pixmap.reset(new QPixmap(pp));
317 }
318 
319 QT_END_NAMESPACE
320