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.
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"
21 using namespace Microsoft::WRL
;
23 #ifdef TARGET_WINDOWS_STORE
24 #define generic GenericFromFreeTypeLibrary
27 #include FT_FREETYPE_H
32 constexpr size_t ELEMENT_ARRAY_MAX_CHAR_INDEX
= 2000;
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
) {
59 m_staticIndexBufferCreated
= false;
63 bool CGUIFontTTFDX::FirstBegin()
65 if (!DX::DeviceResources::Get()->GetD3DContext())
68 CGUIShaderDX
* pGUIShader
= DX::Windowing()->GetGUIShader();
69 pGUIShader
->Begin(SHADER_METHOD_RENDER_FONT
);
74 void CGUIFontTTFDX::LastEnd()
76 CWinSystemBase
* const winSystem
= CServiceBroker::GetWinSystem();
77 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
78 if (!pContext
|| !winSystem
)
81 typedef CGUIFontTTF::CTranslatedVertices trans
;
82 bool transIsEmpty
= std::all_of(m_vertexTrans
.begin(), m_vertexTrans
.end(),
83 [](trans
& _
) { return _
.m_vertexBuffer
->size
<= 0; });
85 if (m_vertex
.empty() && transIsEmpty
)
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());
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()))
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);
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)
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
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);
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.");
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
);
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)
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
))
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(),
261 m_textureHeight
= newHeight
;
262 m_textureScaleY
= 1.0f
/ m_textureHeight
;
263 m_speedupTexture
= std::move(newSpeedupTexture
);
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
,
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
)
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
;
306 pDevice
->CreateBuffer(&bufferDesc
, &initData
, m_vertexBuffer
.ReleaseAndGetAddressOf())))
308 CLog::LogF(LOGERROR
, "Failed to create the vertex buffer.");
312 m_vertexWidth
= width
;
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.");
323 memcpy(resource
.pData
, pSysMem
, width
);
324 pContext
->Unmap(m_vertexBuffer
.Get(), 0);
330 void CGUIFontTTFDX::CreateStaticIndexBuffer(void)
332 if (m_staticIndexBufferCreated
)
335 ComPtr
<ID3D11Device
> pDevice
= DX::DeviceResources::Get()->GetD3DDevice();
339 uint16_t index
[ELEMENT_ARRAY_MAX_CHAR_INDEX
][6];
340 for (size_t i
= 0; i
< ELEMENT_ARRAY_MAX_CHAR_INDEX
; 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
;
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;
366 m_staticIndexBuffer
= nullptr;
367 m_vertexBuffer
= nullptr;
370 void CGUIFontTTFDX::OnCreateDevice(void)