diff options
Diffstat (limited to 'xbmc/guilib/GUIShaderDX.cpp')
-rw-r--r-- | xbmc/guilib/GUIShaderDX.cpp | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/xbmc/guilib/GUIShaderDX.cpp b/xbmc/guilib/GUIShaderDX.cpp new file mode 100644 index 0000000..b9e801e --- /dev/null +++ b/xbmc/guilib/GUIShaderDX.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIShaderDX.h" +#include "windowing/GraphicContext.h" +#include "rendering/dx/DeviceResources.h" +#include "rendering/dx/RenderContext.h" +#include "utils/log.h" + +// shaders bytecode includes +#include "guishader_vert.h" +#include "guishader_checkerboard_right.h" +#include "guishader_checkerboard_left.h" +#include "guishader_default.h" +#include "guishader_fonts.h" +#include "guishader_interlaced_right.h" +#include "guishader_interlaced_left.h" +#include "guishader_multi_texture_blend.h" +#include "guishader_texture.h" +#include "guishader_texture_noblend.h" + +#include <d3dcompiler.h> + +using namespace DirectX; +using namespace Microsoft::WRL; + +// shaders bytecode holder +static const D3D_SHADER_DATA cbPSShaderCode[SHADER_METHOD_RENDER_COUNT] = +{ + { guishader_default, sizeof(guishader_default) }, // SHADER_METHOD_RENDER_DEFAULT + { guishader_texture_noblend, sizeof(guishader_texture_noblend) }, // SHADER_METHOD_RENDER_TEXTURE_NOBLEND + { guishader_fonts, sizeof(guishader_fonts) }, // SHADER_METHOD_RENDER_FONT + { guishader_texture, sizeof(guishader_texture) }, // SHADER_METHOD_RENDER_TEXTURE_BLEND + { guishader_multi_texture_blend, sizeof(guishader_multi_texture_blend) }, // SHADER_METHOD_RENDER_MULTI_TEXTURE_BLEND + { guishader_interlaced_left, sizeof(guishader_interlaced_left) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_LEFT + { guishader_interlaced_right, sizeof(guishader_interlaced_right) }, // SHADER_METHOD_RENDER_STEREO_INTERLACED_RIGHT + { guishader_checkerboard_left, sizeof(guishader_checkerboard_left) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_LEFT + { guishader_checkerboard_right, sizeof(guishader_checkerboard_right) }, // SHADER_METHOD_RENDER_STEREO_CHECKERBOARD_RIGHT +}; + +CGUIShaderDX::CGUIShaderDX() : + m_pSampLinear(nullptr), + m_pVPBuffer(nullptr), + m_pWVPBuffer(nullptr), + m_pVertexBuffer(nullptr), + m_clipXFactor(0.0f), + m_clipXOffset(0.0f), + m_clipYFactor(0.0f), + m_clipYOffset(0.0f), + m_bIsWVPDirty(false), + m_bIsVPDirty(false), + m_bCreated(false), + m_currentShader(0), + m_clipPossible(false) +{ +} + +CGUIShaderDX::~CGUIShaderDX() +{ + Release(); +} + +bool CGUIShaderDX::Initialize() +{ + // Create input layout + D3D11_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + if (!m_vertexShader.Create(guishader_vert, sizeof(guishader_vert), layout, ARRAYSIZE(layout))) + return false; + + size_t i; + bool bSuccess = true; + for (i = 0; i < SHADER_METHOD_RENDER_COUNT; i++) + { + if (!m_pixelShader[i].Create(cbPSShaderCode[i].pBytecode, cbPSShaderCode[i].BytecodeLength)) + { + bSuccess = false; + break; + } + } + + if (!bSuccess) + { + m_vertexShader.Release(); + for (size_t j = 0; j < i; j++) + m_pixelShader[j].Release(); + } + + if (!bSuccess || !CreateBuffers() || !CreateSamplers()) + return false; + + m_bCreated = true; + return true; +} + +bool CGUIShaderDX::CreateBuffers() +{ + ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice(); + + // create vertex buffer + CD3D11_BUFFER_DESC bufferDesc(sizeof(Vertex) * 4, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + if (FAILED(pDevice->CreateBuffer(&bufferDesc, NULL, m_pVertexBuffer.ReleaseAndGetAddressOf()))) + { + CLog::LogF(LOGERROR, "Failed to create GUI vertex buffer."); + return false; + } + + // Create the constant buffer for WVP + size_t buffSize = (sizeof(cbWorld) + 15) & ~15; + CD3D11_BUFFER_DESC cbbd(buffSize, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); // it can change very frequently + if (FAILED(pDevice->CreateBuffer(&cbbd, NULL, m_pWVPBuffer.ReleaseAndGetAddressOf()))) + { + CLog::LogF(LOGERROR, "Failed to create the constant buffer."); + return false; + } + m_bIsWVPDirty = true; + + CRect viewPort; + DX::Windowing()->GetViewPort(viewPort); + + // initial data for viewport buffer + m_cbViewPort.TopLeftX = viewPort.x1; + m_cbViewPort.TopLeftY = viewPort.y1; + m_cbViewPort.Width = viewPort.Width(); + m_cbViewPort.Height = viewPort.Height(); + + cbbd.ByteWidth = sizeof(cbViewPort); + D3D11_SUBRESOURCE_DATA initData = { &m_cbViewPort, 0, 0 }; + // create viewport buffer + if (FAILED(pDevice->CreateBuffer(&cbbd, &initData, m_pVPBuffer.ReleaseAndGetAddressOf()))) + return false; + + return true; +} + +bool CGUIShaderDX::CreateSamplers() +{ + // Describe the Sampler State + D3D11_SAMPLER_DESC sampDesc = {}; + sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampDesc.MinLOD = 0; + sampDesc.MaxLOD = D3D11_FLOAT32_MAX; + + if (FAILED(DX::DeviceResources::Get()->GetD3DDevice()->CreateSamplerState(&sampDesc, m_pSampLinear.ReleaseAndGetAddressOf()))) + return false; + + DX::DeviceResources::Get()->GetD3DContext()->PSSetSamplers(0, 1, m_pSampLinear.GetAddressOf()); + + return true; +} + +void CGUIShaderDX::ApplyStateBlock(void) +{ + if (!m_bCreated) + return; + + ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext(); + + m_vertexShader.BindShader(); + pContext->VSSetConstantBuffers(0, 1, m_pWVPBuffer.GetAddressOf()); + + m_pixelShader[m_currentShader].BindShader(); + pContext->PSSetConstantBuffers(0, 1, m_pWVPBuffer.GetAddressOf()); + pContext->PSSetConstantBuffers(1, 1, m_pVPBuffer.GetAddressOf()); + + pContext->PSSetSamplers(0, 1, m_pSampLinear.GetAddressOf()); + + RestoreBuffers(); +} + +void CGUIShaderDX::Begin(unsigned int flags) +{ + if (!m_bCreated) + return; + + if (m_currentShader != flags) + { + m_currentShader = flags; + m_pixelShader[m_currentShader].BindShader(); + } + ClipToScissorParams(); +} + +void CGUIShaderDX::End() +{ + if (!m_bCreated) + return; +} + +void CGUIShaderDX::DrawQuad(Vertex& v1, Vertex& v2, Vertex& v3, Vertex& v4) +{ + if (!m_bCreated) + return; + + ApplyChanges(); + + ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext(); + + // update vertex buffer + D3D11_MAPPED_SUBRESOURCE resource; + if (SUCCEEDED(pContext->Map(m_pVertexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &resource))) + { + // we are using strip topology + Vertex vertices[4] = { v2, v3, v1, v4 }; + memcpy(resource.pData, &vertices, sizeof(Vertex) * 4); + pContext->Unmap(m_pVertexBuffer.Get(), 0); + // Draw primitives + pContext->Draw(4, 0); + } +} + +void CGUIShaderDX::DrawIndexed(unsigned int indexCount, unsigned int startIndex, unsigned int startVertex) +{ + if (!m_bCreated) + return; + + ApplyChanges(); + DX::DeviceResources::Get()->GetD3DContext()->DrawIndexed(indexCount, startIndex, startVertex); +} + +void CGUIShaderDX::Draw(unsigned int vertexCount, unsigned int startVertex) +{ + if (!m_bCreated) + return; + + ApplyChanges(); + DX::DeviceResources::Get()->GetD3DContext()->Draw(vertexCount, startVertex); +} + +void CGUIShaderDX::SetShaderViews(unsigned int numViews, ID3D11ShaderResourceView** views) +{ + if (!m_bCreated) + return; + + DX::DeviceResources::Get()->GetD3DContext()->PSSetShaderResources(0, numViews, views); +} + +void CGUIShaderDX::Release() +{ + m_pVertexBuffer = nullptr; + m_pWVPBuffer = nullptr; + m_pVPBuffer = nullptr; + m_pSampLinear = nullptr; + m_bCreated = false; +} + +void CGUIShaderDX::SetViewPort(D3D11_VIEWPORT viewPort) +{ + if (!m_pVPBuffer) + return; + + if ( viewPort.TopLeftX != m_cbViewPort.TopLeftX + || viewPort.TopLeftY != m_cbViewPort.TopLeftY + || viewPort.Width != m_cbViewPort.Width + || viewPort.Height != m_cbViewPort.Height) + { + m_cbViewPort.TopLeftX = viewPort.TopLeftX; + m_cbViewPort.TopLeftY = viewPort.TopLeftY; + m_cbViewPort.Width = viewPort.Width; + m_cbViewPort.Height = viewPort.Height; + m_bIsVPDirty = true; + } +} + +void CGUIShaderDX::Project(float &x, float &y, float &z) +{ +#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + XMVECTOR vLocation = { x, y, z }; +#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_) + XMVECTOR vLocation = { x, y }; +#endif + XMVECTOR vScreenCoord = XMVector3Project(vLocation, m_cbViewPort.TopLeftX, m_cbViewPort.TopLeftY, + m_cbViewPort.Width, m_cbViewPort.Height, 0, 1, + m_cbWorldViewProj.projection, m_cbWorldViewProj.view, m_cbWorldViewProj.world); + x = XMVectorGetX(vScreenCoord); + y = XMVectorGetY(vScreenCoord); + z = 0; +} + +void XM_CALLCONV CGUIShaderDX::SetWVP(const XMMATRIX &w, const XMMATRIX &v, const XMMATRIX &p) +{ + m_bIsWVPDirty = true; + m_cbWorldViewProj.world = w; + m_cbWorldViewProj.view = v; + m_cbWorldViewProj.projection = p; +} + +void CGUIShaderDX::SetWorld(const XMMATRIX &value) +{ + m_bIsWVPDirty = true; + m_cbWorldViewProj.world = value; +} + +void CGUIShaderDX::SetView(const XMMATRIX &value) +{ + m_bIsWVPDirty = true; + m_cbWorldViewProj.view = value; +} + +void CGUIShaderDX::SetProjection(const XMMATRIX &value) +{ + m_bIsWVPDirty = true; + m_cbWorldViewProj.projection = value; +} + +void CGUIShaderDX::ApplyChanges(void) +{ + ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext(); + D3D11_MAPPED_SUBRESOURCE res; + + if (m_bIsWVPDirty) + { + if (SUCCEEDED(pContext->Map(m_pWVPBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res))) + { + XMMATRIX worldView = XMMatrixMultiply(m_cbWorldViewProj.world, m_cbWorldViewProj.view); + XMMATRIX worldViewProj = XMMatrixMultiplyTranspose(worldView, m_cbWorldViewProj.projection); + + cbWorld* buffer = (cbWorld*)res.pData; + buffer->wvp = worldViewProj; + buffer->blackLevel = (DX::Windowing()->UseLimitedColor() ? 16.f / 255.f : 0.f); + buffer->colorRange = (DX::Windowing()->UseLimitedColor() ? (235.f - 16.f) / 255.f : 1.0f); + buffer->sdrPeakLum = (100 - DX::Windowing()->GetGuiSdrPeakLuminance()) + 10; + buffer->PQ = (DX::Windowing()->IsTransferPQ() ? 1 : 0); + + pContext->Unmap(m_pWVPBuffer.Get(), 0); + m_bIsWVPDirty = false; + } + } + + // update view port buffer + if (m_bIsVPDirty) + { + if (SUCCEEDED(pContext->Map(m_pVPBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res))) + { + *(cbViewPort*)res.pData = m_cbViewPort; + pContext->Unmap(m_pVPBuffer.Get(), 0); + m_bIsVPDirty = false; + } + } +} + +void CGUIShaderDX::RestoreBuffers(void) +{ + const unsigned stride = sizeof(Vertex); + const unsigned offset = 0; + + ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext(); + // Set the vertex buffer to active in the input assembler so it can be rendered. + pContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset); + // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles. + pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); +} + +void CGUIShaderDX::ClipToScissorParams(void) +{ + CRect viewPort; // absolute positions of corners + DX::Windowing()->GetViewPort(viewPort); + + // get current GUI transform + const TransformMatrix &guiMatrix = CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIMatrix(); + // get current GPU transforms + XMFLOAT4X4 world, view, projection; + XMStoreFloat4x4(&world, m_cbWorldViewProj.world); + XMStoreFloat4x4(&view, m_cbWorldViewProj.view); + XMStoreFloat4x4(&projection, m_cbWorldViewProj.projection); + + m_clipPossible = guiMatrix.m[0][1] == 0 && + guiMatrix.m[1][0] == 0 && + guiMatrix.m[2][0] == 0 && + guiMatrix.m[2][1] == 0 && + view.m[0][1] == 0 && + view.m[0][2] == 0 && + view.m[1][0] == 0 && + view.m[1][2] == 0 && + view.m[2][0] == 0 && + view.m[2][1] == 0 && + projection.m[0][1] == 0 && + projection.m[0][2] == 0 && + projection.m[0][3] == 0 && + projection.m[1][0] == 0 && + projection.m[1][2] == 0 && + projection.m[1][3] == 0 && + projection.m[3][0] == 0 && + projection.m[3][1] == 0 && + projection.m[3][3] == 0; + + m_clipXFactor = 0.0f; + m_clipXOffset = 0.0f; + m_clipYFactor = 0.0f; + m_clipYOffset = 0.0f; + + if (m_clipPossible) + { + m_clipXFactor = guiMatrix.m[0][0] * view.m[0][0] * projection.m[0][0]; + m_clipXOffset = (guiMatrix.m[0][3] * view.m[0][0] + view.m[3][0]) * projection.m[0][0]; + m_clipYFactor = guiMatrix.m[1][1] * view.m[1][1] * projection.m[1][1]; + m_clipYOffset = (guiMatrix.m[1][3] * view.m[1][1] + view.m[3][1]) * projection.m[1][1]; + + float clipW = (guiMatrix.m[2][3] * view.m[2][2] + view.m[3][2]) * projection.m[2][3]; + float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW); + float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme + + m_clipXFactor = m_clipXFactor * xMult; + m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2; + m_clipYFactor = m_clipYFactor * yMult; + m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2; + } +} |