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
33 static const D3D_SHADER_DATA cbPSShaderCode
[SHADER_METHOD_RENDER_COUNT
] =
35 { guishader_default
, sizeof(guishader_default
) }, // SHADER_METHOD_RENDER_DEFAULT
36 { guishader_texture_noblend
, sizeof(guishader_texture_noblend
) }, // SHADER_METHOD_RENDER_TEXTURE_NOBLEND
37 { guishader_fonts
, sizeof(guishader_fonts
) }, // SHADER_METHOD_RENDER_FONT
38 { guishader_texture
, sizeof(guishader_texture
) }, // SHADER_METHOD_RENDER_TEXTURE_BLEND
39 { guishader_multi_texture_blend
, sizeof(guishader_multi_texture_blend
) }, // SHADER_METHOD_RENDER_MULTI_TEXTURE_BLEND
40 { guishader_interlaced_left
, sizeof(guishader_interlaced_left
) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_LEFT
41 { guishader_interlaced_right
, sizeof(guishader_interlaced_right
) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_RIGHT
42 { guishader_checkerboard_left
, sizeof(guishader_checkerboard_left
) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_LEFT
43 { guishader_checkerboard_right
, sizeof(guishader_checkerboard_right
) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_RIGHT
46 CGUIShaderDX::CGUIShaderDX() :
47 m_pSampLinear(nullptr),
49 m_pWVPBuffer(nullptr),
50 m_pVertexBuffer(nullptr),
63 CGUIShaderDX::~CGUIShaderDX()
68 bool CGUIShaderDX::Initialize()
70 // Create input layout
71 D3D11_INPUT_ELEMENT_DESC layout
[] =
73 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT
, 0, 0, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
74 { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT
, 0, 12, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
75 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT
, 0, 28, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
76 { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT
, 0, 36, D3D11_INPUT_PER_VERTEX_DATA
, 0 },
79 if (!m_vertexShader
.Create(guishader_vert
, sizeof(guishader_vert
), layout
, ARRAYSIZE(layout
)))
84 for (i
= 0; i
< SHADER_METHOD_RENDER_COUNT
; i
++)
86 if (!m_pixelShader
[i
].Create(cbPSShaderCode
[i
].pBytecode
, cbPSShaderCode
[i
].BytecodeLength
))
95 m_vertexShader
.Release();
96 for (size_t j
= 0; j
< i
; j
++)
97 m_pixelShader
[j
].Release();
100 if (!bSuccess
|| !CreateBuffers() || !CreateSamplers())
107 bool CGUIShaderDX::CreateBuffers()
109 ComPtr
<ID3D11Device
> pDevice
= DX::DeviceResources::Get()->GetD3DDevice();
111 // create vertex buffer
112 CD3D11_BUFFER_DESC
bufferDesc(sizeof(Vertex
) * 4, D3D11_BIND_VERTEX_BUFFER
, D3D11_USAGE_DYNAMIC
, D3D11_CPU_ACCESS_WRITE
);
113 if (FAILED(pDevice
->CreateBuffer(&bufferDesc
, NULL
, m_pVertexBuffer
.ReleaseAndGetAddressOf())))
115 CLog::LogF(LOGERROR
, "Failed to create GUI vertex buffer.");
119 // Create the constant buffer for WVP
120 size_t buffSize
= (sizeof(cbWorld
) + 15) & ~15;
121 CD3D11_BUFFER_DESC
cbbd(buffSize
, D3D11_BIND_CONSTANT_BUFFER
, D3D11_USAGE_DYNAMIC
, D3D11_CPU_ACCESS_WRITE
); // it can change very frequently
122 if (FAILED(pDevice
->CreateBuffer(&cbbd
, NULL
, m_pWVPBuffer
.ReleaseAndGetAddressOf())))
124 CLog::LogF(LOGERROR
, "Failed to create the constant buffer.");
127 m_bIsWVPDirty
= true;
130 DX::Windowing()->GetViewPort(viewPort
);
132 // initial data for viewport buffer
133 m_cbViewPort
.TopLeftX
= viewPort
.x1
;
134 m_cbViewPort
.TopLeftY
= viewPort
.y1
;
135 m_cbViewPort
.Width
= viewPort
.Width();
136 m_cbViewPort
.Height
= viewPort
.Height();
138 cbbd
.ByteWidth
= sizeof(cbViewPort
);
139 D3D11_SUBRESOURCE_DATA initData
= { &m_cbViewPort
, 0, 0 };
140 // create viewport buffer
141 if (FAILED(pDevice
->CreateBuffer(&cbbd
, &initData
, m_pVPBuffer
.ReleaseAndGetAddressOf())))
147 bool CGUIShaderDX::CreateSamplers()
149 // Describe the Sampler State
150 D3D11_SAMPLER_DESC sampDesc
= {};
151 sampDesc
.Filter
= D3D11_FILTER_MIN_MAG_MIP_LINEAR
;
152 sampDesc
.AddressU
= D3D11_TEXTURE_ADDRESS_CLAMP
;
153 sampDesc
.AddressV
= D3D11_TEXTURE_ADDRESS_CLAMP
;
154 sampDesc
.AddressW
= D3D11_TEXTURE_ADDRESS_CLAMP
;
155 sampDesc
.ComparisonFunc
= D3D11_COMPARISON_NEVER
;
157 sampDesc
.MaxLOD
= D3D11_FLOAT32_MAX
;
159 if (FAILED(DX::DeviceResources::Get()->GetD3DDevice()->CreateSamplerState(&sampDesc
, m_pSampLinear
.ReleaseAndGetAddressOf())))
162 DX::DeviceResources::Get()->GetD3DContext()->PSSetSamplers(0, 1, m_pSampLinear
.GetAddressOf());
167 void CGUIShaderDX::ApplyStateBlock(void)
172 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
174 m_vertexShader
.BindShader();
175 pContext
->VSSetConstantBuffers(0, 1, m_pWVPBuffer
.GetAddressOf());
177 m_pixelShader
[m_currentShader
].BindShader();
178 pContext
->PSSetConstantBuffers(0, 1, m_pWVPBuffer
.GetAddressOf());
179 pContext
->PSSetConstantBuffers(1, 1, m_pVPBuffer
.GetAddressOf());
181 pContext
->PSSetSamplers(0, 1, m_pSampLinear
.GetAddressOf());
186 void CGUIShaderDX::Begin(unsigned int flags
)
191 if (m_currentShader
!= flags
)
193 m_currentShader
= flags
;
194 m_pixelShader
[m_currentShader
].BindShader();
196 ClipToScissorParams();
199 void CGUIShaderDX::End()
205 void CGUIShaderDX::DrawQuad(Vertex
& v1
, Vertex
& v2
, Vertex
& v3
, Vertex
& v4
)
212 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
214 // update vertex buffer
215 D3D11_MAPPED_SUBRESOURCE resource
;
216 if (SUCCEEDED(pContext
->Map(m_pVertexBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &resource
)))
218 // we are using strip topology
219 Vertex vertices
[4] = { v2
, v3
, v1
, v4
};
220 memcpy(resource
.pData
, &vertices
, sizeof(Vertex
) * 4);
221 pContext
->Unmap(m_pVertexBuffer
.Get(), 0);
223 pContext
->Draw(4, 0);
227 void CGUIShaderDX::DrawIndexed(unsigned int indexCount
, unsigned int startIndex
, unsigned int startVertex
)
233 DX::DeviceResources::Get()->GetD3DContext()->DrawIndexed(indexCount
, startIndex
, startVertex
);
236 void CGUIShaderDX::Draw(unsigned int vertexCount
, unsigned int startVertex
)
242 DX::DeviceResources::Get()->GetD3DContext()->Draw(vertexCount
, startVertex
);
245 void CGUIShaderDX::SetShaderViews(unsigned int numViews
, ID3D11ShaderResourceView
** views
)
250 DX::DeviceResources::Get()->GetD3DContext()->PSSetShaderResources(0, numViews
, views
);
253 void CGUIShaderDX::Release()
255 m_pVertexBuffer
= nullptr;
256 m_pWVPBuffer
= nullptr;
257 m_pVPBuffer
= nullptr;
258 m_pSampLinear
= nullptr;
262 void CGUIShaderDX::SetViewPort(D3D11_VIEWPORT viewPort
)
267 if ( viewPort
.TopLeftX
!= m_cbViewPort
.TopLeftX
268 || viewPort
.TopLeftY
!= m_cbViewPort
.TopLeftY
269 || viewPort
.Width
!= m_cbViewPort
.Width
270 || viewPort
.Height
!= m_cbViewPort
.Height
)
272 m_cbViewPort
.TopLeftX
= viewPort
.TopLeftX
;
273 m_cbViewPort
.TopLeftY
= viewPort
.TopLeftY
;
274 m_cbViewPort
.Width
= viewPort
.Width
;
275 m_cbViewPort
.Height
= viewPort
.Height
;
280 void CGUIShaderDX::Project(float &x
, float &y
, float &z
)
282 #if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
283 XMVECTOR vLocation
= { x
, y
, z
};
284 #elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
285 XMVECTOR vLocation
= { x
, y
};
287 XMVECTOR vScreenCoord
= XMVector3Project(vLocation
, m_cbViewPort
.TopLeftX
, m_cbViewPort
.TopLeftY
,
288 m_cbViewPort
.Width
, m_cbViewPort
.Height
, 0, 1,
289 m_cbWorldViewProj
.projection
, m_cbWorldViewProj
.view
, m_cbWorldViewProj
.world
);
290 x
= XMVectorGetX(vScreenCoord
);
291 y
= XMVectorGetY(vScreenCoord
);
295 void XM_CALLCONV
CGUIShaderDX::SetWVP(const XMMATRIX
&w
, const XMMATRIX
&v
, const XMMATRIX
&p
)
297 m_bIsWVPDirty
= true;
298 m_cbWorldViewProj
.world
= w
;
299 m_cbWorldViewProj
.view
= v
;
300 m_cbWorldViewProj
.projection
= p
;
303 void CGUIShaderDX::SetWorld(const XMMATRIX
&value
)
305 m_bIsWVPDirty
= true;
306 m_cbWorldViewProj
.world
= value
;
309 void CGUIShaderDX::SetView(const XMMATRIX
&value
)
311 m_bIsWVPDirty
= true;
312 m_cbWorldViewProj
.view
= value
;
315 void CGUIShaderDX::SetProjection(const XMMATRIX
&value
)
317 m_bIsWVPDirty
= true;
318 m_cbWorldViewProj
.projection
= value
;
321 void CGUIShaderDX::ApplyChanges(void)
323 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
324 D3D11_MAPPED_SUBRESOURCE res
;
328 if (SUCCEEDED(pContext
->Map(m_pWVPBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &res
)))
330 XMMATRIX worldView
= XMMatrixMultiply(m_cbWorldViewProj
.world
, m_cbWorldViewProj
.view
);
331 XMMATRIX worldViewProj
= XMMatrixMultiplyTranspose(worldView
, m_cbWorldViewProj
.projection
);
333 cbWorld
* buffer
= (cbWorld
*)res
.pData
;
334 buffer
->wvp
= worldViewProj
;
335 buffer
->blackLevel
= (DX::Windowing()->UseLimitedColor() ? 16.f
/ 255.f
: 0.f
);
336 buffer
->colorRange
= (DX::Windowing()->UseLimitedColor() ? (235.f
- 16.f
) / 255.f
: 1.0f
);
337 if (DX::Windowing()->IsTransferPQ())
338 buffer
->sdrPeakLum
= 10000.0f
/ DX::Windowing()->GetGuiSdrPeakLuminance();
339 buffer
->PQ
= (DX::Windowing()->IsTransferPQ() ? 1 : 0);
341 pContext
->Unmap(m_pWVPBuffer
.Get(), 0);
342 m_bIsWVPDirty
= false;
346 // update view port buffer
349 if (SUCCEEDED(pContext
->Map(m_pVPBuffer
.Get(), 0, D3D11_MAP_WRITE_DISCARD
, 0, &res
)))
351 *(cbViewPort
*)res
.pData
= m_cbViewPort
;
352 pContext
->Unmap(m_pVPBuffer
.Get(), 0);
353 m_bIsVPDirty
= false;
358 void CGUIShaderDX::RestoreBuffers(void)
360 const unsigned stride
= sizeof(Vertex
);
361 const unsigned offset
= 0;
363 ComPtr
<ID3D11DeviceContext
> pContext
= DX::DeviceResources::Get()->GetD3DContext();
364 // Set the vertex buffer to active in the input assembler so it can be rendered.
365 pContext
->IASetVertexBuffers(0, 1, m_pVertexBuffer
.GetAddressOf(), &stride
, &offset
);
366 // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
367 pContext
->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
);
370 void CGUIShaderDX::ClipToScissorParams(void)
372 CRect viewPort
; // absolute positions of corners
373 DX::Windowing()->GetViewPort(viewPort
);
375 // get current GUI transform
376 const TransformMatrix
&guiMatrix
= CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIMatrix();
377 // get current GPU transforms
378 XMFLOAT4X4 world
, view
, projection
;
379 XMStoreFloat4x4(&world
, m_cbWorldViewProj
.world
);
380 XMStoreFloat4x4(&view
, m_cbWorldViewProj
.view
);
381 XMStoreFloat4x4(&projection
, m_cbWorldViewProj
.projection
);
383 m_clipPossible
= guiMatrix
.m
[0][1] == 0 &&
384 guiMatrix
.m
[1][0] == 0 &&
385 guiMatrix
.m
[2][0] == 0 &&
386 guiMatrix
.m
[2][1] == 0 &&
393 projection
.m
[0][1] == 0 &&
394 projection
.m
[0][2] == 0 &&
395 projection
.m
[0][3] == 0 &&
396 projection
.m
[1][0] == 0 &&
397 projection
.m
[1][2] == 0 &&
398 projection
.m
[1][3] == 0 &&
399 projection
.m
[3][0] == 0 &&
400 projection
.m
[3][1] == 0 &&
401 projection
.m
[3][3] == 0;
403 m_clipXFactor
= 0.0f
;
404 m_clipXOffset
= 0.0f
;
405 m_clipYFactor
= 0.0f
;
406 m_clipYOffset
= 0.0f
;
410 m_clipXFactor
= guiMatrix
.m
[0][0] * view
.m
[0][0] * projection
.m
[0][0];
411 m_clipXOffset
= (guiMatrix
.m
[0][3] * view
.m
[0][0] + view
.m
[3][0]) * projection
.m
[0][0];
412 m_clipYFactor
= guiMatrix
.m
[1][1] * view
.m
[1][1] * projection
.m
[1][1];
413 m_clipYOffset
= (guiMatrix
.m
[1][3] * view
.m
[1][1] + view
.m
[3][1]) * projection
.m
[1][1];
415 float clipW
= (guiMatrix
.m
[2][3] * view
.m
[2][2] + view
.m
[3][2]) * projection
.m
[2][3];
416 float xMult
= (viewPort
.x2
- viewPort
.x1
) / (2 * clipW
);
417 float yMult
= (viewPort
.y1
- viewPort
.y2
) / (2 * clipW
); // correct for inverted window coordinate scheme
419 m_clipXFactor
= m_clipXFactor
* xMult
;
420 m_clipXOffset
= m_clipXOffset
* xMult
+ (viewPort
.x2
+ viewPort
.x1
) / 2;
421 m_clipYFactor
= m_clipYFactor
* yMult
;
422 m_clipYOffset
= m_clipYOffset
* yMult
+ (viewPort
.y2
+ viewPort
.y1
) / 2;