Merge pull request #22816 from CastagnaIT/fix_tx3g
[xbmc.git] / xbmc / guilib / GUIShaderDX.cpp
blobf5e7adf22123dc5bc378b4f28f47549ea7d08c26
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 "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),
48 m_pVPBuffer(nullptr),
49 m_pWVPBuffer(nullptr),
50 m_pVertexBuffer(nullptr),
51 m_clipXFactor(0.0f),
52 m_clipXOffset(0.0f),
53 m_clipYFactor(0.0f),
54 m_clipYOffset(0.0f),
55 m_bIsWVPDirty(false),
56 m_bIsVPDirty(false),
57 m_bCreated(false),
58 m_currentShader(0),
59 m_clipPossible(false)
63 CGUIShaderDX::~CGUIShaderDX()
65 Release();
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)))
80 return false;
82 size_t i;
83 bool bSuccess = true;
84 for (i = 0; i < SHADER_METHOD_RENDER_COUNT; i++)
86 if (!m_pixelShader[i].Create(cbPSShaderCode[i].pBytecode, cbPSShaderCode[i].BytecodeLength))
88 bSuccess = false;
89 break;
93 if (!bSuccess)
95 m_vertexShader.Release();
96 for (size_t j = 0; j < i; j++)
97 m_pixelShader[j].Release();
100 if (!bSuccess || !CreateBuffers() || !CreateSamplers())
101 return false;
103 m_bCreated = true;
104 return true;
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.");
116 return false;
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.");
125 return false;
127 m_bIsWVPDirty = true;
129 CRect viewPort;
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())))
142 return false;
144 return true;
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;
156 sampDesc.MinLOD = 0;
157 sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
159 if (FAILED(DX::DeviceResources::Get()->GetD3DDevice()->CreateSamplerState(&sampDesc, m_pSampLinear.ReleaseAndGetAddressOf())))
160 return false;
162 DX::DeviceResources::Get()->GetD3DContext()->PSSetSamplers(0, 1, m_pSampLinear.GetAddressOf());
164 return true;
167 void CGUIShaderDX::ApplyStateBlock(void)
169 if (!m_bCreated)
170 return;
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());
183 RestoreBuffers();
186 void CGUIShaderDX::Begin(unsigned int flags)
188 if (!m_bCreated)
189 return;
191 if (m_currentShader != flags)
193 m_currentShader = flags;
194 m_pixelShader[m_currentShader].BindShader();
196 ClipToScissorParams();
199 void CGUIShaderDX::End()
201 if (!m_bCreated)
202 return;
205 void CGUIShaderDX::DrawQuad(Vertex& v1, Vertex& v2, Vertex& v3, Vertex& v4)
207 if (!m_bCreated)
208 return;
210 ApplyChanges();
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);
222 // Draw primitives
223 pContext->Draw(4, 0);
227 void CGUIShaderDX::DrawIndexed(unsigned int indexCount, unsigned int startIndex, unsigned int startVertex)
229 if (!m_bCreated)
230 return;
232 ApplyChanges();
233 DX::DeviceResources::Get()->GetD3DContext()->DrawIndexed(indexCount, startIndex, startVertex);
236 void CGUIShaderDX::Draw(unsigned int vertexCount, unsigned int startVertex)
238 if (!m_bCreated)
239 return;
241 ApplyChanges();
242 DX::DeviceResources::Get()->GetD3DContext()->Draw(vertexCount, startVertex);
245 void CGUIShaderDX::SetShaderViews(unsigned int numViews, ID3D11ShaderResourceView** views)
247 if (!m_bCreated)
248 return;
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;
259 m_bCreated = false;
262 void CGUIShaderDX::SetViewPort(D3D11_VIEWPORT viewPort)
264 if (!m_pVPBuffer)
265 return;
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;
276 m_bIsVPDirty = true;
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 };
286 #endif
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);
292 z = 0;
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;
326 if (m_bIsWVPDirty)
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
347 if (m_bIsVPDirty)
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 &&
387 view.m[0][1] == 0 &&
388 view.m[0][2] == 0 &&
389 view.m[1][0] == 0 &&
390 view.m[1][2] == 0 &&
391 view.m[2][0] == 0 &&
392 view.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;
408 if (m_clipPossible)
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;