[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / GUIShaderDX.cpp
blob2bf158799aeae6ce4ab5c8b6ee18e99f424e38be
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 // clang-format off
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
46 // clang-format on
48 CGUIShaderDX::CGUIShaderDX() :
49 m_pSampLinear(nullptr),
50 m_pVPBuffer(nullptr),
51 m_pWVPBuffer(nullptr),
52 m_pVertexBuffer(nullptr),
53 m_clipXFactor(0.0f),
54 m_clipXOffset(0.0f),
55 m_clipYFactor(0.0f),
56 m_clipYOffset(0.0f),
57 m_bIsWVPDirty(false),
58 m_bIsVPDirty(false),
59 m_bCreated(false),
60 m_currentShader(0),
61 m_clipPossible(false)
65 CGUIShaderDX::~CGUIShaderDX()
67 Release();
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)))
82 return false;
84 size_t i;
85 bool bSuccess = true;
86 for (i = 0; i < SHADER_METHOD_RENDER_COUNT; i++)
88 if (!m_pixelShader[i].Create(cbPSShaderCode[i].pBytecode, cbPSShaderCode[i].BytecodeLength))
90 bSuccess = false;
91 break;
95 if (!bSuccess)
97 m_vertexShader.Release();
98 for (size_t j = 0; j < i; j++)
99 m_pixelShader[j].Release();
102 if (!bSuccess || !CreateBuffers() || !CreateSamplers())
103 return false;
105 m_bCreated = true;
106 return true;
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.");
118 return false;
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.");
127 return false;
129 m_bIsWVPDirty = true;
131 CRect viewPort;
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())))
144 return false;
146 return true;
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;
158 sampDesc.MinLOD = 0;
159 sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
161 if (FAILED(DX::DeviceResources::Get()->GetD3DDevice()->CreateSamplerState(&sampDesc, m_pSampLinear.ReleaseAndGetAddressOf())))
162 return false;
164 DX::DeviceResources::Get()->GetD3DContext()->PSSetSamplers(0, 1, m_pSampLinear.GetAddressOf());
166 return true;
169 void CGUIShaderDX::ApplyStateBlock(void)
171 if (!m_bCreated)
172 return;
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());
185 RestoreBuffers();
188 void CGUIShaderDX::Begin(unsigned int flags)
190 if (!m_bCreated)
191 return;
193 if (m_currentShader != flags)
195 m_currentShader = flags;
196 m_pixelShader[m_currentShader].BindShader();
198 ClipToScissorParams();
201 void CGUIShaderDX::End()
203 if (!m_bCreated)
204 return;
207 void CGUIShaderDX::DrawQuad(Vertex& v1, Vertex& v2, Vertex& v3, Vertex& v4)
209 if (!m_bCreated)
210 return;
212 ApplyChanges();
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);
224 // Draw primitives
225 pContext->Draw(4, 0);
229 void CGUIShaderDX::DrawIndexed(unsigned int indexCount, unsigned int startIndex, unsigned int startVertex)
231 if (!m_bCreated)
232 return;
234 ApplyChanges();
235 DX::DeviceResources::Get()->GetD3DContext()->DrawIndexed(indexCount, startIndex, startVertex);
238 void CGUIShaderDX::Draw(unsigned int vertexCount, unsigned int startVertex)
240 if (!m_bCreated)
241 return;
243 ApplyChanges();
244 DX::DeviceResources::Get()->GetD3DContext()->Draw(vertexCount, startVertex);
247 void CGUIShaderDX::SetShaderViews(unsigned int numViews, ID3D11ShaderResourceView** views)
249 if (!m_bCreated)
250 return;
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;
261 m_bCreated = false;
264 void CGUIShaderDX::SetViewPort(D3D11_VIEWPORT viewPort)
266 if (!m_pVPBuffer)
267 return;
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;
278 m_bIsVPDirty = true;
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 };
288 #endif
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);
294 z = 0;
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;
328 if (m_bIsWVPDirty)
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
349 if (m_bIsVPDirty)
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 &&
389 view.m[0][1] == 0 &&
390 view.m[0][2] == 0 &&
391 view.m[1][0] == 0 &&
392 view.m[1][2] == 0 &&
393 view.m[2][0] == 0 &&
394 view.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;
410 if (m_clipPossible)
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;