[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / guilib / GUIFontTTFDX.cpp
blob0f4a6b0282af02992502cfed6567cd360049f430
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIFontTTFDX.h"
11 #include "GUIFontManager.h"
12 #include "GUIShaderDX.h"
13 #include "TextureDX.h"
14 #include "rendering/dx/DeviceResources.h"
15 #include "rendering/dx/RenderContext.h"
16 #include "utils/log.h"
18 // stuff for freetype
19 #include <ft2build.h>
21 using namespace Microsoft::WRL;
23 #ifdef TARGET_WINDOWS_STORE
24 #define generic GenericFromFreeTypeLibrary
25 #endif
27 #include FT_FREETYPE_H
28 #include FT_GLYPH_H
30 namespace
32 constexpr size_t ELEMENT_ARRAY_MAX_CHAR_INDEX = 2000;
33 } /* namespace */
35 CGUIFontTTF* CGUIFontTTF::CreateGUIFontTTF(const std::string& fontIdent)
37 return new CGUIFontTTFDX(fontIdent);
40 CGUIFontTTFDX::CGUIFontTTFDX(const std::string& fontIdent) : CGUIFontTTF(fontIdent)
42 DX::Windowing()->Register(this);
45 CGUIFontTTFDX::~CGUIFontTTFDX(void)
47 DX::Windowing()->Unregister(this);
49 m_vertexBuffer = nullptr;
50 m_staticIndexBuffer = nullptr;
51 if (!m_buffers.empty())
53 std::for_each(m_buffers.begin(), m_buffers.end(), [](CD3DBuffer* buf) {
54 if (buf)
55 delete buf;
56 });
58 m_buffers.clear();
59 m_staticIndexBufferCreated = false;
60 m_vertexWidth = 0;
63 bool CGUIFontTTFDX::FirstBegin()
65 if (!DX::DeviceResources::Get()->GetD3DContext())
66 return false;
68 CGUIShaderDX* pGUIShader = DX::Windowing()->GetGUIShader();
69 pGUIShader->Begin(SHADER_METHOD_RENDER_FONT);
71 return true;
74 void CGUIFontTTFDX::LastEnd()
76 CWinSystemBase* const winSystem = CServiceBroker::GetWinSystem();
77 ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext();
78 if (!pContext || !winSystem)
79 return;
81 typedef CGUIFontTTF::CTranslatedVertices trans;
82 bool transIsEmpty = std::all_of(m_vertexTrans.begin(), m_vertexTrans.end(),
83 [](trans& _) { return _.m_vertexBuffer->size <= 0; });
84 // no chars to render
85 if (m_vertex.empty() && transIsEmpty)
86 return;
88 CreateStaticIndexBuffer();
90 unsigned int offset = 0;
91 unsigned int stride = sizeof(SVertex);
93 CGUIShaderDX* pGUIShader = DX::Windowing()->GetGUIShader();
94 // Set font texture as shader resource
95 pGUIShader->SetShaderViews(1, m_speedupTexture->GetAddressOfSRV());
96 // Enable alpha blend
97 DX::Windowing()->SetAlphaBlendEnable(true);
98 // Set our static index buffer
99 pContext->IASetIndexBuffer(m_staticIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
100 // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
101 pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
103 if (!m_vertex.empty())
105 // Deal with vertices that had to use software clipping
106 if (!UpdateDynamicVertexBuffer(&m_vertex[0], m_vertex.size()))
107 return;
109 // Set the dynamic vertex buffer to active in the input assembler
110 pContext->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset);
112 // Do the actual drawing operation, split into groups of characters no
113 // larger than the pre-determined size of the element array
114 size_t size = m_vertex.size() / 4;
115 for (size_t character = 0; size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
117 size_t count = size - character;
118 count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
120 // 6 indices and 4 vertices per character
121 pGUIShader->DrawIndexed(count * 6, 0, character * 4);
125 if (!transIsEmpty)
127 // Deal with the vertices that can be hardware clipped and therefore translated
129 // Store current GPU transform
130 XMMATRIX view = pGUIShader->GetView();
131 // Store current scissor
132 CGraphicContext& context = winSystem->GetGfxContext();
133 CRect scissor = context.StereoCorrection(context.GetScissors());
135 for (size_t i = 0; i < m_vertexTrans.size(); i++)
137 // ignore empty buffers
138 if (m_vertexTrans[i].m_vertexBuffer->size == 0)
139 continue;
141 // Apply the clip rectangle
142 CRect clip = DX::Windowing()->ClipRectToScissorRect(m_vertexTrans[i].m_clip);
143 // Intersect with current scissors
144 clip.Intersect(scissor);
146 // skip empty clip, a little improvement to not render invisible text
147 if (clip.IsEmpty())
148 continue;
150 DX::Windowing()->SetScissors(clip);
152 // Apply the translation to the model view matrix
153 XMMATRIX translation =
154 XMMatrixTranslation(m_vertexTrans[i].m_translateX, m_vertexTrans[i].m_translateY,
155 m_vertexTrans[i].m_translateZ);
156 pGUIShader->SetView(XMMatrixMultiply(translation, view));
158 CD3DBuffer* vbuffer =
159 reinterpret_cast<CD3DBuffer*>(m_vertexTrans[i].m_vertexBuffer->bufferHandle);
160 // Set the static vertex buffer to active in the input assembler
161 ID3D11Buffer* buffers[1] = {vbuffer->Get()};
162 pContext->IASetVertexBuffers(0, 1, buffers, &stride, &offset);
164 // Do the actual drawing operation, split into groups of characters no
165 // larger than the pre-determined size of the element array
166 for (size_t character = 0; m_vertexTrans[i].m_vertexBuffer->size > character;
167 character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
169 size_t count = m_vertexTrans[i].m_vertexBuffer->size - character;
170 count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
172 // 6 indices and 4 vertices per character
173 pGUIShader->DrawIndexed(count * 6, 0, character * 4);
177 // restore scissor
178 DX::Windowing()->SetScissors(scissor);
180 // Restore the original transform
181 pGUIShader->SetView(view);
184 pGUIShader->RestoreBuffers();
187 CVertexBuffer CGUIFontTTFDX::CreateVertexBuffer(const std::vector<SVertex>& vertices) const
189 CD3DBuffer* buffer = nullptr;
190 // do not create empty buffers, leave buffer as nullptr, it will be ignored on drawing stage
191 if (!vertices.empty())
193 buffer = new CD3DBuffer();
194 if (!buffer->Create(D3D11_BIND_VERTEX_BUFFER, vertices.size(), sizeof(SVertex),
195 DXGI_FORMAT_UNKNOWN, D3D11_USAGE_IMMUTABLE, &vertices[0]))
196 CLog::LogF(LOGERROR, "Failed to create vertex buffer.");
197 else
198 AddReference((CGUIFontTTFDX*)this, buffer);
201 return CVertexBuffer(reinterpret_cast<void*>(buffer), vertices.size() / 4, this);
204 void CGUIFontTTFDX::AddReference(CGUIFontTTFDX* font, CD3DBuffer* pBuffer)
206 font->m_buffers.emplace_back(pBuffer);
209 void CGUIFontTTFDX::DestroyVertexBuffer(CVertexBuffer& buffer) const
211 if (nullptr != buffer.bufferHandle)
213 CD3DBuffer* vbuffer = reinterpret_cast<CD3DBuffer*>(buffer.bufferHandle);
214 ClearReference((CGUIFontTTFDX*)this, vbuffer);
215 if (vbuffer)
216 delete vbuffer;
217 buffer.bufferHandle = 0;
221 void CGUIFontTTFDX::ClearReference(CGUIFontTTFDX* font, CD3DBuffer* pBuffer)
223 std::list<CD3DBuffer*>::iterator it =
224 std::find(font->m_buffers.begin(), font->m_buffers.end(), pBuffer);
225 if (it != font->m_buffers.end())
226 font->m_buffers.erase(it);
229 std::unique_ptr<CTexture> CGUIFontTTFDX::ReallocTexture(unsigned int& newHeight)
231 assert(newHeight != 0);
232 assert(m_textureWidth != 0);
233 if (m_textureHeight == 0)
235 m_texture.reset();
236 m_speedupTexture.reset();
238 m_staticCache.Flush();
239 m_dynamicCache.Flush();
241 std::unique_ptr<CDXTexture> pNewTexture =
242 std::make_unique<CDXTexture>(m_textureWidth, newHeight, XB_FMT_A8);
243 std::unique_ptr<CD3DTexture> newSpeedupTexture = std::make_unique<CD3DTexture>();
244 if (!newSpeedupTexture->Create(m_textureWidth, newHeight, 1, D3D11_USAGE_DEFAULT,
245 DXGI_FORMAT_R8_UNORM))
247 return nullptr;
250 // There might be data to copy from the previous texture
251 if (newSpeedupTexture && m_speedupTexture)
253 CD3D11_BOX rect(0, 0, 0, m_textureWidth, m_textureHeight, 1);
254 ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetImmediateContext();
255 pContext->CopySubresourceRegion(newSpeedupTexture->Get(), 0, 0, 0, 0, m_speedupTexture->Get(),
256 0, &rect);
259 m_texture.reset();
261 m_textureHeight = newHeight;
262 m_textureScaleY = 1.0f / m_textureHeight;
263 m_speedupTexture = std::move(newSpeedupTexture);
265 return pNewTexture;
268 bool CGUIFontTTFDX::CopyCharToTexture(
269 FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
271 FT_Bitmap bitmap = bitGlyph->bitmap;
273 ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetImmediateContext();
274 if (m_speedupTexture && m_speedupTexture->Get() && pContext && bitmap.buffer)
276 CD3D11_BOX dstBox(x1, y1, 0, x2, y2, 1);
277 pContext->UpdateSubresource(m_speedupTexture->Get(), 0, &dstBox, bitmap.buffer, bitmap.pitch,
279 return true;
282 return false;
285 void CGUIFontTTFDX::DeleteHardwareTexture()
289 bool CGUIFontTTFDX::UpdateDynamicVertexBuffer(const SVertex* pSysMem, unsigned int vertex_count)
291 ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice();
292 ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext();
294 if (!pDevice || !pContext)
295 return false;
297 unsigned width = sizeof(SVertex) * vertex_count;
298 if (width > m_vertexWidth) // create or re-create
300 CD3D11_BUFFER_DESC bufferDesc(width, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC,
301 D3D11_CPU_ACCESS_WRITE);
302 D3D11_SUBRESOURCE_DATA initData = {};
303 initData.pSysMem = pSysMem;
305 if (FAILED(
306 pDevice->CreateBuffer(&bufferDesc, &initData, m_vertexBuffer.ReleaseAndGetAddressOf())))
308 CLog::LogF(LOGERROR, "Failed to create the vertex buffer.");
309 return false;
312 m_vertexWidth = width;
314 else
316 D3D11_MAPPED_SUBRESOURCE resource;
317 if (FAILED(pContext->Map(m_vertexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &resource)))
319 CLog::LogF(LOGERROR, "Failed to update the vertex buffer.");
320 return false;
323 memcpy(resource.pData, pSysMem, width);
324 pContext->Unmap(m_vertexBuffer.Get(), 0);
327 return true;
330 void CGUIFontTTFDX::CreateStaticIndexBuffer(void)
332 if (m_staticIndexBufferCreated)
333 return;
335 ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice();
336 if (!pDevice)
337 return;
339 uint16_t index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
340 for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
342 index[i][0] = 4 * i;
343 index[i][1] = 4 * i + 1;
344 index[i][2] = 4 * i + 2;
345 index[i][3] = 4 * i + 2;
346 index[i][4] = 4 * i + 3;
347 index[i][5] = 4 * i + 0;
350 CD3D11_BUFFER_DESC desc(sizeof(index), D3D11_BIND_INDEX_BUFFER, D3D11_USAGE_IMMUTABLE);
351 D3D11_SUBRESOURCE_DATA initData = {};
352 initData.pSysMem = index;
354 if (SUCCEEDED(
355 pDevice->CreateBuffer(&desc, &initData, m_staticIndexBuffer.ReleaseAndGetAddressOf())))
356 m_staticIndexBufferCreated = true;
359 bool CGUIFontTTFDX::m_staticIndexBufferCreated = false;
360 ComPtr<ID3D11Buffer> CGUIFontTTFDX::m_staticIndexBuffer = nullptr;
362 void CGUIFontTTFDX::OnDestroyDevice(bool fatal)
364 m_staticIndexBufferCreated = false;
365 m_vertexWidth = 0;
366 m_staticIndexBuffer = nullptr;
367 m_vertexBuffer = nullptr;
370 void CGUIFontTTFDX::OnCreateDevice(void)