You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

431 lines
15 KiB

/*****************************************************************************
* d3d11_quad.c: Direct3D11 Quad handling
*****************************************************************************
* Copyright (C) 2017-2018 VLC authors and VideoLAN
*
* Authors: Steve Lhomme <robux4@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cassert>
#include <vlc_common.h>
#include "d3d11_quad.h"
#include "common.h"
using Microsoft::WRL::ComPtr;
void D3D11_RenderQuad(d3d11_device_t *d3d_dev, d3d11_quad_t *quad, d3d11_vertex_shader_t *vsshader,
ID3D11ShaderResourceView *resourceView[DXGI_MAX_SHADER_VIEW],
d3d11_select_plane_t selectPlane, void *selectOpaque)
{
UINT offset = 0;
/* Render the quad */
d3d_dev->d3dcontext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
/* vertex shader */
d3d_dev->d3dcontext->IASetInputLayout(vsshader->layout.Get());
d3d_dev->d3dcontext->IASetVertexBuffers(0, 1, quad->vertexBuffer.GetAddressOf(), &quad->generic.vertexStride, &offset);
d3d_dev->d3dcontext->IASetIndexBuffer(quad->indexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
if ( quad->viewpointShaderConstant.Get() )
d3d_dev->d3dcontext->VSSetConstantBuffers(0, 1, quad->viewpointShaderConstant.GetAddressOf());
d3d_dev->d3dcontext->VSSetShader(vsshader->shader.Get(), NULL, 0);
if (quad->SamplerStates[0].Get())
{
ID3D11SamplerState *states[] = {quad->SamplerStates[0].Get(), quad->SamplerStates[1].Get()};
d3d_dev->d3dcontext->PSSetSamplers(0, 2, states);
}
/* pixel shader */
d3d_dev->d3dcontext->PSSetConstantBuffers(0, 1, quad->pPixelShaderConstants.GetAddressOf());
assert(quad->resourceCount <= DXGI_MAX_SHADER_VIEW);
d3d_dev->d3dcontext->PSSetShaderResources(0, quad->resourceCount, resourceView);
for (size_t i=0; i<ARRAY_SIZE(quad->d3dpixelShader); i++)
{
if (!quad->d3dpixelShader[i].Get())
break;
ID3D11RenderTargetView *renderView = NULL;
if (unlikely(!selectPlane(selectOpaque, i, &renderView)))
continue;
if (renderView != NULL)
d3d_dev->d3dcontext->OMSetRenderTargets(1, &renderView, NULL);
d3d_dev->d3dcontext->PSSetShader(quad->d3dpixelShader[i].Get(), NULL, 0);
d3d_dev->d3dcontext->RSSetViewports(1, &quad->cropViewport[i]);
d3d_dev->d3dcontext->DrawIndexed(quad->generic.indexCount, 0, 0);
// /* force unbinding the input texture, otherwise we get:
// * OMSetRenderTargets: Resource being set to OM RenderTarget slot 0 is still bound on input! */
// ID3D11ShaderResourceView *reset[DXGI_MAX_SHADER_VIEW] = { 0 };
// d3d_dev->d3dcontext->PSSetShaderResources(0, quad->resourceCount, reset);
}
}
static bool AllocQuadVertices(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad, video_projection_mode_t projection)
{
HRESULT hr;
if (!D3D_QuadSetupBuffers(o, &quad->generic, projection))
return false;
D3D11_BUFFER_DESC bd = { };
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = quad->generic.vertexStride * quad->generic.vertexCount;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = d3d_dev->d3ddevice->CreateBuffer(&bd, NULL, &quad->vertexBuffer);
if(FAILED(hr)) {
msg_Err(o, "Failed to create vertex buffer. (hr=%lX)", hr);
goto fail;
}
/* create the index of the vertices */
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.ByteWidth = sizeof(WORD) * quad->generic.indexCount;
hr = d3d_dev->d3ddevice->CreateBuffer(&bd, NULL, &quad->indexBuffer);
if(FAILED(hr)) {
msg_Err(o, "Could not create the quad indices. (hr=0x%lX)", hr);
goto fail;
}
return true;
fail:
quad->vertexBuffer.Reset();
quad->indexBuffer.Reset();
return false;
}
void d3d11_quad_t::Reset()
{
pPixelShaderConstants.Reset();
vertexBuffer.Reset();
indexBuffer.Reset();
viewpointShaderConstant.Reset();
for (size_t i=0; i<ARRAY_SIZE(d3dpixelShader); i++)
{
d3dpixelShader[i].Reset();
SamplerStates[i].Reset();
}
ReleaseD3D11PictureSys(&picSys);
}
#undef D3D11_UpdateQuadPosition
bool D3D11_UpdateQuadPosition( vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad,
video_transform_t orientation )
{
bool result = true;
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE mappedResource;
d3d_vertex_t *dst_data;
if (unlikely(quad->vertexBuffer.Get() == NULL))
return false;
/* create the vertices */
hr = d3d_dev->d3dcontext->Map(quad->vertexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(hr)) {
msg_Err(o, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
return false;
}
dst_data = static_cast<d3d_vertex_t*>(mappedResource.pData);
/* create the vertex indices */
hr = d3d_dev->d3dcontext->Map(quad->indexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(hr)) {
msg_Err(o, "Failed to lock the index buffer (hr=0x%lX)", hr);
d3d_dev->d3dcontext->Unmap(quad->vertexBuffer.Get(), 0);
return false;
}
RECT output;
output.left = quad->quad_fmt.i_x_offset;
output.right = quad->quad_fmt.i_x_offset + quad->quad_fmt.i_visible_width;
output.top = quad->quad_fmt.i_y_offset;
output.bottom = quad->quad_fmt.i_y_offset + quad->quad_fmt.i_visible_height;
result = D3D_SetupQuadData(o, &quad->generic, &output, dst_data, mappedResource.pData, orientation);
d3d_dev->d3dcontext->Unmap(quad->indexBuffer.Get(), 0);
d3d_dev->d3dcontext->Unmap(quad->vertexBuffer.Get(), 0);
return result;
}
static bool ShaderUpdateConstants(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad, int type, void *new_buf)
{
ID3D11Resource *res;
switch (type)
{
case PS_CONST_LUMI_BOUNDS:
res = quad->pPixelShaderConstants.Get();
break;
case VS_CONST_VIEWPOINT:
res = quad->viewpointShaderConstant.Get();
break;
}
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = d3d_dev->d3dcontext->Map(res, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (unlikely(FAILED(hr)))
{
msg_Err(o, "Failed to lock the picture shader constants (hr=0x%lX)", hr);
return false;
}
switch (type)
{
case PS_CONST_LUMI_BOUNDS:
memcpy(mappedResource.pData, new_buf, sizeof(PS_CONSTANT_BUFFER));
break;
case VS_CONST_VIEWPOINT:
memcpy(mappedResource.pData, new_buf, sizeof(VS_PROJECTION_CONST));
break;
}
d3d_dev->d3dcontext->Unmap(res, 0);
return true;
}
void (D3D11_UpdateQuadOpacity)(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad, float opacity)
{
float old = quad->generic.shaderConstants->Opacity;
if (!D3D_UpdateQuadOpacity(&quad->generic, opacity))
return;
if (!ShaderUpdateConstants(o, d3d_dev, quad, PS_CONST_LUMI_BOUNDS, quad->generic.shaderConstants))
D3D_UpdateQuadOpacity(&quad->generic, old);
}
void (D3D11_UpdateQuadLuminanceScale)(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad, float luminanceScale)
{
float old = quad->generic.shaderConstants->LuminanceScale;
if (!D3D_UpdateQuadLuminanceScale(&quad->generic, luminanceScale))
return;
if (!ShaderUpdateConstants(o, d3d_dev, quad, PS_CONST_LUMI_BOUNDS, quad->generic.shaderConstants))
D3D_UpdateQuadLuminanceScale(&quad->generic, old);
}
void (D3D11_UpdateViewpoint)(vlc_object_t *o, d3d11_device_t *d3d_dev, d3d11_quad_t *quad,
const vlc_viewpoint_t *viewpoint, float f_sar)
{
if (!quad->viewpointShaderConstant.Get())
return;
D3D_UpdateViewpoint(&quad->generic, viewpoint, f_sar);
ShaderUpdateConstants(o, d3d_dev, quad, VS_CONST_VIEWPOINT, quad->generic.vertexConstants);
}
#undef D3D11_AllocateQuad
int D3D11_AllocateQuad(vlc_object_t *o, d3d11_device_t *d3d_dev,
video_projection_mode_t projection, d3d11_quad_t *quad)
{
quad->generic.vertexConstants = &quad->vConstants;
quad->generic.shaderConstants = &quad->pConstants;
HRESULT hr;
static_assert((sizeof(PS_CONSTANT_BUFFER)%16)==0,"Constant buffers require 16-byte alignment");
D3D11_BUFFER_DESC constantDesc = { };
constantDesc.Usage = D3D11_USAGE_DYNAMIC;
constantDesc.ByteWidth = sizeof(PS_CONSTANT_BUFFER);
constantDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
hr = d3d_dev->d3ddevice->CreateBuffer(&constantDesc, NULL, &quad->pPixelShaderConstants);
if(FAILED(hr)) {
msg_Err(o, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
goto error;
}
if (projection == PROJECTION_MODE_EQUIRECTANGULAR || projection == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
{
static_assert((sizeof(VS_PROJECTION_CONST)%16)==0,"Constant buffers require 16-byte alignment");
constantDesc.ByteWidth = sizeof(VS_PROJECTION_CONST);
hr = d3d_dev->d3ddevice->CreateBuffer(&constantDesc, NULL, &quad->viewpointShaderConstant);
if(FAILED(hr)) {
msg_Err(o, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
goto error;
}
}
if (!AllocQuadVertices(o, d3d_dev, quad, projection))
goto error;
return VLC_SUCCESS;
error:
quad->Reset();
return VLC_EGENERIC;
}
#undef D3D11_SetupQuad
int D3D11_SetupQuad(vlc_object_t *o, d3d11_device_t *d3d_dev, const video_format_t *fmt, d3d11_quad_t *quad,
const display_info_t *displayFormat)
{
D3D_SetupQuad(o, fmt, &quad->generic, displayFormat);
ShaderUpdateConstants(o, d3d_dev, quad, PS_CONST_LUMI_BOUNDS, quad->generic.shaderConstants);
for (size_t i=0; i<ARRAY_SIZE(quad->cropViewport); i++)
{
quad->cropViewport[i].MinDepth = 0.0f;
quad->cropViewport[i].MaxDepth = 1.0f;
}
quad->resourceCount = DxgiResourceCount(quad->generic.textureFormat);
return VLC_SUCCESS;
}
void d3d11_quad_t::UpdateViewport(const vout_display_place_t *place, const d3d_format_t *display)
{
cropViewport[0].TopLeftX = place->x;
cropViewport[0].TopLeftY = place->y;
cropViewport[0].Width = place->width;
cropViewport[0].Height = place->height;
switch ( generic.textureFormat->formatTexture )
{
case DXGI_FORMAT_NV12:
case DXGI_FORMAT_P010:
cropViewport[1].TopLeftX = place->x / 2;
cropViewport[1].TopLeftY = place->y / 2;
cropViewport[1].Width = place->width / 2;
cropViewport[1].Height = place->height / 2;
break;
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_B5G6R5_UNORM:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_R16G16B16A16_UNORM:
case DXGI_FORMAT_YUY2:
case DXGI_FORMAT_AYUV:
case DXGI_FORMAT_Y210:
case DXGI_FORMAT_Y410:
if ( display->formatTexture == DXGI_FORMAT_NV12 ||
display->formatTexture == DXGI_FORMAT_P010 )
{
cropViewport[1].TopLeftX = place->x / 2;
cropViewport[1].TopLeftY = place->y / 2;
cropViewport[1].Width = place->width / 2;
cropViewport[1].Height = place->height / 2;
}
break;
case DXGI_FORMAT_UNKNOWN:
switch ( generic.textureFormat->fourcc )
{
case VLC_CODEC_I444_16L:
case VLC_CODEC_I444_12L:
case VLC_CODEC_I444_10L:
case VLC_CODEC_I444:
if ( display->formatTexture != DXGI_FORMAT_NV12 &&
display->formatTexture != DXGI_FORMAT_P010 )
{
cropViewport[1] = cropViewport[0];
break;
}
break;
case VLC_CODEC_YUVA:
if ( display->formatTexture != DXGI_FORMAT_NV12 &&
display->formatTexture != DXGI_FORMAT_P010 )
{
cropViewport[1] = cropViewport[0];
break;
}
/* fallthrough */
case VLC_CODEC_I420_10L:
case VLC_CODEC_I420:
case VLC_CODEC_YUV420A:
cropViewport[1].TopLeftX = place->x / 2;
cropViewport[1].TopLeftY = place->y / 2;
cropViewport[1].Width = place->width / 2;
cropViewport[1].Height = place->height / 2;
break;
}
break;
default:
vlc_assert_unreachable();
}
}
HRESULT D3D11_InitFence(d3d11_device_t & d3d_dev, d3d11_gpu_fence & fence)
{
HRESULT hr;
ComPtr<ID3D11Device5> d3ddev5;
hr = d3d_dev.d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&d3ddev5));
if (FAILED(hr))
goto error;
hr = d3ddev5->CreateFence(fence.renderFence, D3D11_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&fence.d3dRenderFence));
if (FAILED(hr))
goto error;
hr = d3d_dev.d3dcontext->QueryInterface(IID_GRAPHICS_PPV_ARGS(&fence.d3dcontext4));
if (FAILED(hr))
goto error;
fence.renderFinished = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (unlikely(fence.renderFinished == nullptr))
goto error;
return S_OK;
error:
fence.d3dRenderFence.Reset();
fence.d3dcontext4.Reset();
CloseHandle(fence.renderFinished);
return hr;
}
void D3D11_ReleaseFence(d3d11_gpu_fence & fence)
{
if (fence.d3dcontext4.Get())
{
fence.d3dRenderFence.Reset();
fence.d3dcontext4.Reset();
CloseHandle(fence.renderFinished);
fence.renderFinished = nullptr;
}
}
int D3D11_WaitFence(d3d11_gpu_fence & fence)
{
if (fence.d3dcontext4.Get())
{
if (fence.renderFence == UINT64_MAX)
fence.renderFence = 0;
else
fence.renderFence++;
ResetEvent(fence.renderFinished);
fence.d3dRenderFence->SetEventOnCompletion(fence.renderFence, fence.renderFinished);
fence.d3dcontext4->Signal(fence.d3dRenderFence.Get(), fence.renderFence);
WaitForSingleObject(fence.renderFinished, INFINITE);
return VLC_SUCCESS;
}
return VLC_ENOTSUP;
}