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 "GUIShaderDX.h"
10 #include "windowing/GraphicContext.h"
11 #include "rendering/dx/DeviceResources.h"
12 #include "rendering/dx/RenderContext.h"
13 #include "utils/log.h"
15 // shaders bytecode includes
16 #include "guishader_vert.h"
17 #include "guishader_checkerboard_right.h"
18 #include "guishader_checkerboard_left.h"
19 #include "guishader_default.h"
20 #include "guishader_fonts.h"
21 #include "guishader_interlaced_right.h"
22 #include "guishader_interlaced_left.h"
23 #include "guishader_multi_texture_blend.h"
24 #include "guishader_texture.h"
25 #include "guishader_texture_noblend.h"
27 #include <d3dcompiler.h>
29 using namespace DirectX
;
30 using namespace Microsoft::WRL
;
32 // shaders bytecode holder
34 static const D3D_SHADER_DATA cbPSShaderCode
[SHADER_METHOD_RENDER_COUNT
] =
36 { guishader_default
, sizeof(guishader_default
) }, // SHADER_METHOD_RENDER_DEFAULT
37 { guishader_texture_noblend
, sizeof(guishader_texture_noblend
) }, // SHADER_METHOD_RENDER_TEXTURE_NOBLEND
38 { guishader_fonts
, sizeof(guishader_fonts
) }, // SHADER_METHOD_RENDER_FONT
39 { guishader_texture
, sizeof(guishader_texture
) }, // SHADER_METHOD_RENDER_TEXTURE_BLEND
40 { guishader_multi_texture_blend
, sizeof(guishader_multi_texture_blend
) }, // SHADER_METHOD_RENDER_MULTI_TEXTURE_BLEND
41 { guishader_interlaced_left
, sizeof(guishader_interlaced_left
) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_LEFT
42 { guishader_interlaced_right
, sizeof(guishader_interlaced_right
) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_RIGHT
43 { guishader_checkerboard_left
, sizeof(guishader_checkerboard_left
) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_LEFT
44 { guishader_checkerboard_right
, sizeof(guishader_checkerboard_right
) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_RIGHT
48 CGUIShaderDX::CGUIShaderDX() :
49 m_pSampLinear(nullptr),
51 m_pWVPBuffer(nullptr),
52 m_pVertexBuffer(nullptr),
65 CGUIShaderDX::~CGUIShaderDX()
70 bool CGUIShaderDX::Initialize()
72 // Create input layout
73 D3D11_INPUT_ELEMENT_DESC layout
[] =
75 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT
, 0, 0, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
76 { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT
, 0, 12, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
77 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT
, 0, 28, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
78 { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT
, 0, 36, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
81 if (!m_vertexShader
.Create(guishader_vert
, sizeof(guishader_vert
), layout
, ARRAYSIZE(layout
)))
86 for (i
= 0; i
< SHADER_METHOD_RENDER_COUNT
; i
++)
88 if (!m_pixelShader
[i
].Create(cbPSShaderCode
[i
].pBytecode
, cbPSShaderCode
[i
].BytecodeLength
))
97 m_vertexShader
.Release();
98 for (size_t j
= 0; j
< i
; j
++)
99 m_pixelShader
[j
].Release();
102 if (!bSuccess
|| !CreateBuffers() || !CreateSamplers())
109 bool CGUIShaderDX::CreateBuffers()
111 ComPtr
<ID3D11Device
> pDevice
= DX::DeviceResources::Get()->GetD3DDevice();
113 // create vertex buffer
114 CD3D11_BUFFER_DESC
bufferDesc(sizeof(Vertex
) * 4, D3D11_BIND_VERTEX_BUFFER
, D3D11_USAGE_DYNAMIC
, D3D11_CPU_ACCESS_WRITE
);
115 if (FAILED(pDevice
->CreateBuffer(&bufferDesc
, NULL
, m_pVertexBuffer
.ReleaseAndGetAddressOf())))
117 CLog::LogF(LOGERROR
, "Failed to create GUI vertex buffer.");
121 // Create the constant buffer for WVP
122 size_t buffSize
= (sizeof(cbWorld
) + 15) & ~15;
123 CD3D11_BUFFER_DESC
cbbd(buffSize
, D3D11_BIND_CONSTANT_BUFFER
, D3D11_USAGE_DYNAMIC
, D3D11_CPU_ACCESS_WRITE
); // it can change very frequently
124 if (FAILED(pDevice
->CreateBuffer(&cbbd
, NULL
, m_pWVPBuffer
.ReleaseAndGetAddressOf())))
126 CLog::LogF(LOGERROR
, "Failed to create the constant buffer.");
129 m_bIsWVPDirty
= true;
132 DX::Windowing()->GetViewPort(viewPort
);
134 // initial data for viewport buffer
135 m_cbViewPort
.TopLeftX
= viewPort
.x1
;
136 m_cbViewPort
.TopLeftY
= viewPort
.y1
;
137 m_cbViewPort
.Width
= viewPort
.Width();
138 m_cbViewPort
.Height
= viewPort
.Height();
140 cbbd
.ByteWidth
= sizeof(cbViewPort
);
141 D3D11_SUBRESOURCE_DATA initData
= { &m_cbViewPort
, 0, 0 };
142 // create viewport buffer
143 if (FAILED(pDevice
->CreateBuffer(&cbbd
, &initData
, m_pVPBuffer
.ReleaseAndGetAddressOf())))
149 bool CGUIShaderDX::CreateSamplers()
151 // Describe the Sampler State
152 D3D11_SAMPLER_DESC sampDesc
= {};
153 sampDesc
.Filter
= D3D11_FILTER_MIN_MAG_MIP_LINEAR
;
154 sampDesc
.AddressU
= D3D11_TEXTURE_ADDRESS_CLAMP
;
155 sampDesc
.AddressV
= D3D11_TEXTURE_ADDRESS_CLAMP
;
156 sampDesc
.AddressW
= D3D11_TEXTURE_ADDRESS_CLAMP
;
157 sampDesc
.ComparisonFunc
= D3D11_COMPARISON_NEVER
;
159 sampDesc
.MaxLOD
= D3D11_FLOAT32_MAX
;
161 if (FAILED(DX::DeviceResources::Get()->GetD3DDevice()->CreateSamplerState(&sampDesc
, m_pSampLinear
.ReleaseAndGetAddressOf())))
164 DX::DeviceResources::Get()->GetD3DContext()->PSSetSamplers(0, 1, m_pSampLinear
.GetAddressOf());
169 void CGUIShaderDX::ApplyStateBlock(void)
174 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
176 m_vertexShader
.BindShader();
177 pContext
->VSSetConstantBuffers(0, 1, m_pWVPBuffer
.GetAddressOf());
179 m_pixelShader
[m_currentShader
].BindShader();
180 pContext
->PSSetConstantBuffers(0, 1, m_pWVPBuffer
.GetAddressOf());
181 pContext
->PSSetConstantBuffers(1, 1, m_pVPBuffer
.GetAddressOf());
183 pContext
->PSSetSamplers(0, 1, m_pSampLinear
.GetAddressOf());
188 void CGUIShaderDX::Begin(unsigned int flags
)
193 if (m_currentShader
!= flags
)
195 m_currentShader
= flags
;
196 m_pixelShader
[m_currentShader
].BindShader();
198 ClipToScissorParams();
201 void CGUIShaderDX::End()
207 void CGUIShaderDX::DrawQuad(Vertex
& v1
, Vertex
& v2
, Vertex
& v3
, Vertex
& v4
)
214 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
216 // update vertex buffer
217 D3D11_MAPPED_SUBRESOURCE resource
;
218 if (SUCCEEDED(pContext
->Map(m_pVertexBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &resource
)))
220 // we are using strip topology
221 Vertex vertices
[4] = { v2
, v3
, v1
, v4
};
222 memcpy(resource
.pData
, &vertices
, sizeof(Vertex
) * 4);
223 pContext
->Unmap(m_pVertexBuffer
.Get(), 0);
225 pContext
->Draw(4, 0);
229 void CGUIShaderDX::DrawIndexed(unsigned int indexCount
, unsigned int startIndex
, unsigned int startVertex
)
235 DX::DeviceResources::Get()->GetD3DContext()->DrawIndexed(indexCount
, startIndex
, startVertex
);
238 void CGUIShaderDX::Draw(unsigned int vertexCount
, unsigned int startVertex
)
244 DX::DeviceResources::Get()->GetD3DContext()->Draw(vertexCount
, startVertex
);
247 void CGUIShaderDX::SetShaderViews(unsigned int numViews
, ID3D11ShaderResourceView
** views
)
252 DX::DeviceResources::Get()->GetD3DContext()->PSSetShaderResources(0, numViews
, views
);
255 void CGUIShaderDX::Release()
257 m_pVertexBuffer
= nullptr;
258 m_pWVPBuffer
= nullptr;
259 m_pVPBuffer
= nullptr;
260 m_pSampLinear
= nullptr;
264 void CGUIShaderDX::SetViewPort(D3D11_VIEWPORT viewPort
)
269 if ( viewPort
.TopLeftX
!= m_cbViewPort
.TopLeftX
270 || viewPort
.TopLeftY
!= m_cbViewPort
.TopLeftY
271 || viewPort
.Width
!= m_cbViewPort
.Width
272 || viewPort
.Height
!= m_cbViewPort
.Height
)
274 m_cbViewPort
.TopLeftX
= viewPort
.TopLeftX
;
275 m_cbViewPort
.TopLeftY
= viewPort
.TopLeftY
;
276 m_cbViewPort
.Width
= viewPort
.Width
;
277 m_cbViewPort
.Height
= viewPort
.Height
;
282 void CGUIShaderDX::Project(float &x
, float &y
, float &z
)
284 #if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
285 XMVECTOR vLocation
= { x
, y
, z
};
286 #elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
287 XMVECTOR vLocation
= { x
, y
};
289 XMVECTOR vScreenCoord
= XMVector3Project(vLocation
, m_cbViewPort
.TopLeftX
, m_cbViewPort
.TopLeftY
,
290 m_cbViewPort
.Width
, m_cbViewPort
.Height
, 0, 1,
291 m_cbWorldViewProj
.projection
, m_cbWorldViewProj
.view
, m_cbWorldViewProj
.world
);
292 x
= XMVectorGetX(vScreenCoord
);
293 y
= XMVectorGetY(vScreenCoord
);
297 void XM_CALLCONV
CGUIShaderDX::SetWVP(const XMMATRIX
&w
, const XMMATRIX
&v
, const XMMATRIX
&p
)
299 m_bIsWVPDirty
= true;
300 m_cbWorldViewProj
.world
= w
;
301 m_cbWorldViewProj
.view
= v
;
302 m_cbWorldViewProj
.projection
= p
;
305 void CGUIShaderDX::SetWorld(const XMMATRIX
&value
)
307 m_bIsWVPDirty
= true;
308 m_cbWorldViewProj
.world
= value
;
311 void CGUIShaderDX::SetView(const XMMATRIX
&value
)
313 m_bIsWVPDirty
= true;
314 m_cbWorldViewProj
.view
= value
;
317 void CGUIShaderDX::SetProjection(const XMMATRIX
&value
)
319 m_bIsWVPDirty
= true;
320 m_cbWorldViewProj
.projection
= value
;
323 void CGUIShaderDX::ApplyChanges(void)
325 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
326 D3D11_MAPPED_SUBRESOURCE res
;
330 if (SUCCEEDED(pContext
->Map(m_pWVPBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &res
)))
332 XMMATRIX worldView
= XMMatrixMultiply(m_cbWorldViewProj
.world
, m_cbWorldViewProj
.view
);
333 XMMATRIX worldViewProj
= XMMatrixMultiplyTranspose(worldView
, m_cbWorldViewProj
.projection
);
335 cbWorld
* buffer
= (cbWorld
*)res
.pData
;
336 buffer
->wvp
= worldViewProj
;
337 buffer
->blackLevel
= (DX::Windowing()->UseLimitedColor() ? 16.f
/ 255.f
: 0.f
);
338 buffer
->colorRange
= (DX::Windowing()->UseLimitedColor() ? (235.f
- 16.f
) / 255.f
: 1.0f
);
339 if (DX::Windowing()->IsTransferPQ())
340 buffer
->sdrPeakLum
= 10000.0f
/ DX::Windowing()->GetGuiSdrPeakLuminance();
341 buffer
->PQ
= (DX::Windowing()->IsTransferPQ() ? 1 : 0);
343 pContext
->Unmap(m_pWVPBuffer
.Get(), 0);
344 m_bIsWVPDirty
= false;
348 // update view port buffer
351 if (SUCCEEDED(pContext
->Map(m_pVPBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &res
)))
353 *(cbViewPort
*)res
.pData
= m_cbViewPort
;
354 pContext
->Unmap(m_pVPBuffer
.Get(), 0);
355 m_bIsVPDirty
= false;
360 void CGUIShaderDX::RestoreBuffers(void)
362 const unsigned stride
= sizeof(Vertex
);
363 const unsigned offset
= 0;
365 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
366 // Set the vertex buffer to active in the input assembler so it can be rendered.
367 pContext
->IASetVertexBuffers(0, 1, m_pVertexBuffer
.GetAddressOf(), &stride
, &offset
);
368 // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
369 pContext
->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
);
372 void CGUIShaderDX::ClipToScissorParams(void)
374 CRect viewPort
; // absolute positions of corners
375 DX::Windowing()->GetViewPort(viewPort
);
377 // get current GUI transform
378 const TransformMatrix
&guiMatrix
= CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIMatrix();
379 // get current GPU transforms
380 XMFLOAT4X4 world
, view
, projection
;
381 XMStoreFloat4x4(&world
, m_cbWorldViewProj
.world
);
382 XMStoreFloat4x4(&view
, m_cbWorldViewProj
.view
);
383 XMStoreFloat4x4(&projection
, m_cbWorldViewProj
.projection
);
385 m_clipPossible
= guiMatrix
.m
[0][1] == 0 &&
386 guiMatrix
.m
[1][0] == 0 &&
387 guiMatrix
.m
[2][0] == 0 &&
388 guiMatrix
.m
[2][1] == 0 &&
395 projection
.m
[0][1] == 0 &&
396 projection
.m
[0][2] == 0 &&
397 projection
.m
[0][3] == 0 &&
398 projection
.m
[1][0] == 0 &&
399 projection
.m
[1][2] == 0 &&
400 projection
.m
[1][3] == 0 &&
401 projection
.m
[3][0] == 0 &&
402 projection
.m
[3][1] == 0 &&
403 projection
.m
[3][3] == 0;
405 m_clipXFactor
= 0.0f
;
406 m_clipXOffset
= 0.0f
;
407 m_clipYFactor
= 0.0f
;
408 m_clipYOffset
= 0.0f
;
412 m_clipXFactor
= guiMatrix
.m
[0][0] * view
.m
[0][0] * projection
.m
[0][0];
413 m_clipXOffset
= (guiMatrix
.m
[0][3] * view
.m
[0][0] + view
.m
[3][0]) * projection
.m
[0][0];
414 m_clipYFactor
= guiMatrix
.m
[1][1] * view
.m
[1][1] * projection
.m
[1][1];
415 m_clipYOffset
= (guiMatrix
.m
[1][3] * view
.m
[1][1] + view
.m
[3][1]) * projection
.m
[1][1];
417 float clipW
= (guiMatrix
.m
[2][3] * view
.m
[2][2] + view
.m
[3][2]) * projection
.m
[2][3];
418 float xMult
= (viewPort
.x2
- viewPort
.x1
) / (2 * clipW
);
419 float yMult
= (viewPort
.y1
- viewPort
.y2
) / (2 * clipW
); // correct for inverted window coordinate scheme
421 m_clipXFactor
= m_clipXFactor
* xMult
;
422 m_clipXOffset
= m_clipXOffset
* xMult
+ (viewPort
.x2
+ viewPort
.x1
) / 2;
423 m_clipYFactor
= m_clipYFactor
* yMult
;
424 m_clipYOffset
= m_clipYOffset
* yMult
+ (viewPort
.y2
+ viewPort
.y1
) / 2;