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 ®ion, 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 ®ion)
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, ©);
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