gstreamer/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12testsrc.cpp
Seungha Yang d0505fba55 d3d12: Add async-depth property
Adding a property to control the number of in-flight GPU commands
(default is unlimited). Note that actual maximum number is defined
in d3d12device's direct command queue object which is 32 now,
thus total number of scheduled GPU commands cannot exceed 32.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7444>
2024-09-04 12:56:43 +00:00

2348 lines
74 KiB
C++

/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-d3d12testsrc
* @title: d3d12testsrc
*
* The d3d12testsrc element is used to produce test video data
*
* ## Example launch line
* ```
* gst-launch-1.0 d3d12testsrc ! queue ! d3d12videosink
* ```
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d12testsrc.h"
#include "gstd3d12pluginutils.h"
#include <directx/d3dx12.h>
#include <wrl.h>
#include <string.h>
#include <d3d11on12.h>
#include <d3d11.h>
#include <d2d1.h>
#include <math.h>
#include <memory>
#include <vector>
#include <queue>
#include <atomic>
#include <gst/d3dshader/gstd3dshader.h>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
/* *INDENT-ON* */
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_test_src_debug);
#define GST_CAT_DEFAULT gst_d3d12_test_src_debug
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
(GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY, GST_D3D12_ALL_FORMATS) "; "
GST_VIDEO_CAPS_MAKE (GST_D3D12_ALL_FORMATS)));
enum GstD3D12TestSrcPattern
{
GST_D3D12_TEST_SRC_SMPTE,
GST_D3D12_TEST_SRC_SNOW,
GST_D3D12_TEST_SRC_BLACK,
GST_D3D12_TEST_SRC_WHITE,
GST_D3D12_TEST_SRC_RED,
GST_D3D12_TEST_SRC_GREEN,
GST_D3D12_TEST_SRC_BLUE,
GST_D3D12_TEST_SRC_CHECKERS1,
GST_D3D12_TEST_SRC_CHECKERS2,
GST_D3D12_TEST_SRC_CHECKERS4,
GST_D3D12_TEST_SRC_CHECKERS8,
GST_D3D12_TEST_SRC_CIRCULAR,
GST_D3D12_TEST_SRC_BLINK,
/* sync with videotestsrc */
GST_D3D12_TEST_SRC_BALL = 18,
};
#define GST_TYPE_D3D12_TEST_SRC_PATTERN (gst_d3d12_test_src_pattern_get_type ())
static GType
gst_d3d12_test_src_pattern_get_type (void)
{
static GType pattern_type = 0;
GST_D3D12_CALL_ONCE_BEGIN {
static const GEnumValue pattern_types[] = {
{GST_D3D12_TEST_SRC_SMPTE, "SMPTE 100% color bars", "smpte"},
{GST_D3D12_TEST_SRC_SNOW, "Random (television snow)", "snow"},
{GST_D3D12_TEST_SRC_BLACK, "100% Black", "black"},
{GST_D3D12_TEST_SRC_WHITE, "100% White", "white"},
{GST_D3D12_TEST_SRC_RED, "Red", "red"},
{GST_D3D12_TEST_SRC_GREEN, "Green", "green"},
{GST_D3D12_TEST_SRC_BLUE, "Blue", "blue"},
{GST_D3D12_TEST_SRC_CHECKERS1, "Checkers 1px", "checkers-1"},
{GST_D3D12_TEST_SRC_CHECKERS2, "Checkers 2px", "checkers-2"},
{GST_D3D12_TEST_SRC_CHECKERS4, "Checkers 4px", "checkers-4"},
{GST_D3D12_TEST_SRC_CHECKERS8, "Checkers 8px", "checkers-8"},
{GST_D3D12_TEST_SRC_CIRCULAR, "Circular", "circular"},
{GST_D3D12_TEST_SRC_BLINK, "Blink", "blink"},
{GST_D3D12_TEST_SRC_BALL, "Moving ball", "ball"},
{0, nullptr, nullptr},
};
pattern_type = g_enum_register_static ("GstD3D12TestSrcPattern",
pattern_types);
} GST_D3D12_CALL_ONCE_END;
return pattern_type;
}
enum
{
PROP_0,
PROP_ADAPTER,
PROP_IS_LIVE,
PROP_PATTERN,
PROP_ALPHA,
PROP_ALPHA_MODE,
PROP_ASYNC_DEPTH,
};
#define DEFAULT_ADAPTER -1
#define DEFAULT_PATTERN GST_D3D12_TEST_SRC_SMPTE
#define DEFAULT_ALPHA 1.0f
#define DEFAULT_ASYNC_DEPTH 0
struct ColorValue
{
union
{
struct
{
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
};
FLOAT color[4];
};
};
static const ColorValue color_table[] = {
/* white */
{1.0f, 1.0f, 1.0f, 1.0f},
/* yellow */
{1.0f, 1.0f, 0.0f, 1.0f},
/* cyan */
{0.0f, 1.0f, 1.0f, 1.0f},
/* green */
{0.0f, 1.0f, 0.0f, 1.0f},
/* magenta */
{1.0f, 0.0f, 1.0f, 1.0f},
/* red */
{1.0f, 0.0f, 0.0f, 1.0f},
/* blue */
{0.0f, 0.0f, 1.0f, 1.0f},
/* black */
{0.0f, 0.0f, 0.0f, 1.0f},
/* -I */
{0.0, 0.0f, 0.5f, 1.0f},
/* +Q */
{0.0f, 0.5, 1.0f, 1.0f},
/* superblack */
{0.0f, 0.0f, 0.0f, 1.0f},
/* 7.421875% grey */
{19. / 256.0f, 19. / 256.0f, 19. / 256.0, 1.0f},
};
enum
{
COLOR_WHITE = 0,
COLOR_YELLOW,
COLOR_CYAN,
COLOR_GREEN,
COLOR_MAGENTA,
COLOR_RED,
COLOR_BLUE,
COLOR_BLACK,
COLOR_NEG_I,
COLOR_POS_Q,
COLOR_SUPER_BLACK,
COLOR_DARK_GREY,
};
struct SnowConstBuffer
{
FLOAT time = 0.0f;
FLOAT alpha = 1.0f;
};
struct CheckerConstBuffer
{
FLOAT width;
FLOAT height;
FLOAT checker_size;
FLOAT alpha = 1.0f;
};
struct GstD3D12TestSrcQuad
{
ComPtr < ID3D12RootSignature > rs;
ComPtr < ID3D12PipelineState > pso;
ComPtr < ID3D12Resource > vertex_index_buf;
ComPtr < ID3D12Resource > vertex_index_upload;
D3D12_VERTEX_BUFFER_VIEW vbv;
D3D12_INDEX_BUFFER_VIEW ibv;
guint index_count = 0;
gboolean is_checker = FALSE;
gboolean is_snow = FALSE;
CheckerConstBuffer checker_const_buffer;
SnowConstBuffer snow_const_buffer;
};
struct StaticColor
{
ColorValue value;
gboolean is_valid = FALSE;
};
/* *INDENT-OFF* */
struct RenderContext
{
RenderContext (GstD3D12Device * dev)
{
device = (GstD3D12Device *) gst_object_ref (dev);
auto device_handle = gst_d3d12_device_get_device_handle (device);
ca_pool = gst_d3d12_command_allocator_pool_new (device_handle,
D3D12_COMMAND_LIST_TYPE_DIRECT);
}
~RenderContext ()
{
gst_d3d12_device_fence_wait (device, D3D12_COMMAND_LIST_TYPE_DIRECT,
fence_val);
{
GstD3D12Device11on12LockGuard lk (device);
brush = nullptr;
d2d_target = nullptr;
wrapped_texture = nullptr;
device11on12 = nullptr;
d3d11_context = nullptr;
device11 = nullptr;
}
gst_clear_buffer (&render_buffer);
if (convert_pool) {
gst_buffer_pool_set_active (convert_pool, FALSE);
gst_clear_object (&convert_pool);
}
gst_clear_object (&ca_pool);
gst_clear_object (&conv);
gst_clear_object (&device);
}
GstD3D12Device *device = nullptr;
GstD3D12Converter *conv = nullptr;
GstBuffer *render_buffer = nullptr;
GstBufferPool *convert_pool = nullptr;
ComPtr<ID3D11On12Device> device11on12;
ComPtr<ID3D11Device> device11;
ComPtr<ID3D11DeviceContext> d3d11_context;
ComPtr<ID2D1RenderTarget> d2d_target;
ComPtr<ID2D1RadialGradientBrush> brush;
gdouble rad;
FLOAT x;
FLOAT y;
ComPtr<ID3D12Resource> texture;
ComPtr<ID3D12DescriptorHeap> rtv_heap;
ComPtr<ID3D11Resource> wrapped_texture;
ComPtr<ID3D12GraphicsCommandList> cl;
std::queue<guint64> scheduled;
GstD3D12CommandAllocatorPool *ca_pool;
D3D12_VIEWPORT viewport;
D3D12_RECT scissor_rect;
StaticColor static_color[2];
std::vector < std::shared_ptr < GstD3D12TestSrcQuad >> quad;
GstD3D12TestSrcPattern pattern;
guint64 fence_val = 0;
};
struct GstD3D12TestSrcPrivate
{
GstD3D12TestSrcPrivate ()
{
fence_data_pool = gst_d3d12_fence_data_pool_new ();
gst_video_info_init (&info);
}
~GstD3D12TestSrcPrivate ()
{
gst_clear_object (&fence_data_pool);
}
gboolean downstream_supports_d3d12 = FALSE;
GstVideoInfo info;
GstD3D12FenceDataPool *fence_data_pool;
gint adapter_index = DEFAULT_ADAPTER;
GstD3D12TestSrcPattern pattern = DEFAULT_PATTERN;
std::unique_ptr<RenderContext> ctx;
D3D12_VIEWPORT viewport;
ComPtr<ID2D1Factory> d2d_factory;
gfloat alpha = DEFAULT_ALPHA;
gboolean reverse = FALSE;
gint64 n_frames = 0;
gint64 accum_frames = 0;
GstClockTime accum_rtime = 0;
GstClockTime running_time = 0;
std::atomic<guint> async_depth = { DEFAULT_ASYNC_DEPTH };
};
/* *INDENT-ON* */
struct _GstD3D12TestSrc
{
GstBaseSrc src;
GstD3D12Device *device;
GstD3D12TestSrcPrivate *priv;
};
struct UvVertexData
{
struct
{
FLOAT x;
FLOAT y;
FLOAT z;
} position;
struct
{
FLOAT u;
FLOAT v;
} texture;
};
struct ColorVertexData
{
struct
{
FLOAT x;
FLOAT y;
FLOAT z;
} position;
struct
{
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
} color;
};
static const D3D12_ROOT_SIGNATURE_FLAGS g_rs_flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS;
static gboolean
setup_snow_render (GstD3D12TestSrc * self, RenderContext * ctx,
gboolean on_smpte)
{
auto priv = self->priv;
HRESULT hr;
UvVertexData vertex_data[4];
const WORD indices[6] = { 0, 1, 2, 3, 0, 2 };
CD3DX12_ROOT_PARAMETER param;
param.InitAsConstants (2, 0, 0, D3D12_SHADER_VISIBILITY_PIXEL);
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rs_desc = { };
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC::Init_1_0 (rs_desc,
1, &param, 0, nullptr, g_rs_flags);
ComPtr < ID3DBlob > rs_blob;
ComPtr < ID3DBlob > error_blob;
hr = D3DX12SerializeVersionedRootSignature (&rs_desc,
D3D_ROOT_SIGNATURE_VERSION_1_1, &rs_blob, &error_blob);
if (!gst_d3d12_result (hr, self->device)) {
const gchar *error_msg = nullptr;
if (error_blob)
error_msg = (const gchar *) error_blob->GetBufferPointer ();
GST_ERROR_OBJECT (self, "Couldn't serialize root signature, error: %s",
GST_STR_NULL (error_msg));
return FALSE;
}
auto device = gst_d3d12_device_get_device_handle (self->device);
ComPtr < ID3D12RootSignature > rs;
hr = device->CreateRootSignature (0, rs_blob->GetBufferPointer (),
rs_blob->GetBufferSize (), IID_PPV_ARGS (&rs));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create root signature");
return FALSE;
}
GstD3DShaderByteCode vs_code;
GstD3DShaderByteCode ps_code;
if (!gst_d3d_plugin_shader_get_vs_blob (GST_D3D_PLUGIN_VS_COORD,
GST_D3D_SM_5_0, &vs_code)) {
GST_ERROR_OBJECT (self, "Couldn't get vs bytecode");
return FALSE;
}
if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_SNOW,
GST_D3D_SM_5_0, &ps_code)) {
GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
return FALSE;
}
D3D12_INPUT_ELEMENT_DESC input_desc[2];
input_desc[0].SemanticName = "POSITION";
input_desc[0].SemanticIndex = 0;
input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
input_desc[0].InputSlot = 0;
input_desc[0].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[0].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[0].InstanceDataStepRate = 0;
input_desc[1].SemanticName = "TEXCOORD";
input_desc[1].SemanticIndex = 0;
input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
input_desc[1].InputSlot = 0;
input_desc[1].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[1].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[1].InstanceDataStepRate = 0;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { };
pso_desc.pRootSignature = rs.Get ();
pso_desc.VS.BytecodeLength = vs_code.byte_code_len;
pso_desc.VS.pShaderBytecode = vs_code.byte_code;
pso_desc.PS.BytecodeLength = ps_code.byte_code_len;
pso_desc.PS.pShaderBytecode = ps_code.byte_code;
pso_desc.BlendState = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
pso_desc.SampleMask = UINT_MAX;
pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC (D3D12_DEFAULT);
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
pso_desc.InputLayout.pInputElementDescs = input_desc;
pso_desc.InputLayout.NumElements = G_N_ELEMENTS (input_desc);
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pso_desc.NumRenderTargets = 1;
pso_desc.RTVFormats[0] = DXGI_FORMAT_B8G8R8A8_UNORM;
pso_desc.SampleDesc.Count = 1;
ComPtr < ID3D12PipelineState > pso;
hr = device->CreateGraphicsPipelineState (&pso_desc, IID_PPV_ARGS (&pso));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create pso");
return FALSE;
}
if (on_smpte) {
FLOAT left, right, top, bottom;
FLOAT left_u, right_u, top_v, bottom_v;
left = 0.5f;
right = 1.0f;
top = -0.5f;
bottom = -1.0f;
left_u = 3.0f / 4.0f;
right_u = 1.0f;
top_v = 3.0f / 4.0f;
bottom_v = 1.0f;
/* bottom left */
vertex_data[0].position.x = left;
vertex_data[0].position.y = bottom;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = left_u;
vertex_data[0].texture.v = bottom_v;
/* top left */
vertex_data[1].position.x = left;
vertex_data[1].position.y = top;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = left_u;
vertex_data[1].texture.v = top_v;
/* top right */
vertex_data[2].position.x = right;
vertex_data[2].position.y = top;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = right_u;
vertex_data[2].texture.v = top_v;
/* bottom right */
vertex_data[3].position.x = right;
vertex_data[3].position.y = bottom;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = right_u;
vertex_data[3].texture.v = bottom_v;
} else {
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = 0.0f;
vertex_data[0].texture.v = 1.0f;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = 0.0f;
vertex_data[1].texture.v = 0.0f;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = 1.0f;
vertex_data[2].texture.v = 0.0f;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = 1.0f;
vertex_data[3].texture.v = 1.0f;
}
D3D12_HEAP_PROPERTIES heap_prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC buffer_desc =
CD3DX12_RESOURCE_DESC::Buffer (sizeof (UvVertexData) * 4
+ sizeof (indices));
ComPtr < ID3D12Resource > vertex_index_upload;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&vertex_index_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
guint8 *data;
CD3DX12_RANGE range (0, 0);
hr = vertex_index_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map index buffer");
return FALSE;
}
memcpy (data, vertex_data, sizeof (UvVertexData) * 4);
memcpy (data + sizeof (UvVertexData) * 4, indices, sizeof (indices));
vertex_index_upload->Unmap (0, nullptr);
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
ComPtr < ID3D12Resource > vertex_index_buf;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_COMMON, nullptr,
IID_PPV_ARGS (&vertex_index_buf));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
auto quad = std::make_shared < GstD3D12TestSrcQuad > ();
quad->rs = rs;
quad->pso = pso;
quad->vertex_index_buf = vertex_index_buf;
quad->vertex_index_upload = vertex_index_upload;
quad->vbv.BufferLocation = vertex_index_buf->GetGPUVirtualAddress ();
quad->vbv.SizeInBytes = sizeof (UvVertexData) * 4;
quad->vbv.StrideInBytes = sizeof (UvVertexData);
quad->ibv.BufferLocation = quad->vbv.BufferLocation + quad->vbv.SizeInBytes;
quad->ibv.SizeInBytes = sizeof (indices);
quad->ibv.Format = DXGI_FORMAT_R16_UINT;
quad->index_count = 6;
quad->is_snow = TRUE;
quad->snow_const_buffer.time = 0;
quad->snow_const_buffer.alpha = priv->alpha;
ctx->quad.push_back (quad);
return TRUE;
}
static gboolean
setup_smpte_render (GstD3D12TestSrc * self, RenderContext * ctx)
{
auto priv = self->priv;
HRESULT hr;
ColorVertexData vertex_data[4 * 20];
WORD indices[6 * 20];
guint num_vertex = 0;
guint num_index = 0;
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rs_desc = { };
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC::Init_1_0 (rs_desc,
0, nullptr, 0, nullptr, g_rs_flags);
ComPtr < ID3DBlob > rs_blob;
ComPtr < ID3DBlob > error_blob;
hr = D3DX12SerializeVersionedRootSignature (&rs_desc,
D3D_ROOT_SIGNATURE_VERSION_1_1, &rs_blob, &error_blob);
if (!gst_d3d12_result (hr, self->device)) {
const gchar *error_msg = nullptr;
if (error_blob)
error_msg = (const gchar *) error_blob->GetBufferPointer ();
GST_ERROR_OBJECT (self, "Couldn't serialize root signature, error: %s",
GST_STR_NULL (error_msg));
return FALSE;
}
auto device = gst_d3d12_device_get_device_handle (self->device);
ComPtr < ID3D12RootSignature > rs;
hr = device->CreateRootSignature (0, rs_blob->GetBufferPointer (),
rs_blob->GetBufferSize (), IID_PPV_ARGS (&rs));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create root signature");
return FALSE;
}
GstD3DShaderByteCode vs_code;
GstD3DShaderByteCode ps_code;
if (!gst_d3d_plugin_shader_get_vs_blob (GST_D3D_PLUGIN_VS_COLOR,
GST_D3D_SM_5_0, &vs_code)) {
GST_ERROR_OBJECT (self, "Couldn't get vs bytecode");
return FALSE;
}
if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_COLOR,
GST_D3D_SM_5_0, &ps_code)) {
GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
return FALSE;
}
D3D12_INPUT_ELEMENT_DESC input_desc[2];
input_desc[0].SemanticName = "POSITION";
input_desc[0].SemanticIndex = 0;
input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
input_desc[0].InputSlot = 0;
input_desc[0].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[0].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[0].InstanceDataStepRate = 0;
input_desc[1].SemanticName = "COLOR";
input_desc[1].SemanticIndex = 0;
input_desc[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
input_desc[1].InputSlot = 0;
input_desc[1].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[1].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[1].InstanceDataStepRate = 0;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { };
pso_desc.pRootSignature = rs.Get ();
pso_desc.VS.BytecodeLength = vs_code.byte_code_len;
pso_desc.VS.pShaderBytecode = vs_code.byte_code;
pso_desc.PS.BytecodeLength = ps_code.byte_code_len;
pso_desc.PS.pShaderBytecode = ps_code.byte_code;
pso_desc.BlendState = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
pso_desc.SampleMask = UINT_MAX;
pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC (D3D12_DEFAULT);
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
pso_desc.InputLayout.pInputElementDescs = input_desc;
pso_desc.InputLayout.NumElements = G_N_ELEMENTS (input_desc);
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pso_desc.NumRenderTargets = 1;
pso_desc.RTVFormats[0] = DXGI_FORMAT_B8G8R8A8_UNORM;
pso_desc.SampleDesc.Count = 1;
ComPtr < ID3D12PipelineState > pso;
hr = device->CreateGraphicsPipelineState (&pso_desc, IID_PPV_ARGS (&pso));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create pso");
return FALSE;
}
/* top row */
for (guint i = 0; i < 7; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 2.0f / 7.0f;
guint base = i * 4;
guint idx_base = i * 6;
const ColorValue *color = &color_table[i];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = 1.0f;
bottom = -1.0f / 3.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = priv->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = priv->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = priv->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = priv->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 7;
num_index += 6 * 7;
/* middle row */
for (guint i = 0; i < 7; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 2.0f / 7.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if ((i % 2) != 0)
color = &color_table[COLOR_BLACK];
else
color = &color_table[COLOR_BLUE - i];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = -1.0f / 3.0f;
bottom = -0.5f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = priv->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = priv->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = priv->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = priv->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 7;
num_index += 6 * 7;
/* bottom row, left three */
for (guint i = 0; i < 3; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 1.0f / 3.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if (i == 0)
color = &color_table[COLOR_NEG_I];
else if (i == 1)
color = &color_table[COLOR_WHITE];
else
color = &color_table[COLOR_POS_Q];
left = -1.0f + i * scale;
right = -1.0f + (i + 1) * scale;
top = -0.5f;
bottom = -1.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = priv->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = priv->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = priv->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = priv->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
num_vertex += 4 * 3;
num_index += 6 * 3;
/* bottom row, middle three */
for (guint i = 0; i < 3; i++) {
FLOAT left, right, top, bottom;
FLOAT scale = 1.0f / 6.0f;
guint base = i * 4 + num_vertex;
guint idx_base = i * 6 + num_index;
const ColorValue *color;
if (i == 0)
color = &color_table[COLOR_SUPER_BLACK];
else if (i == 1)
color = &color_table[COLOR_BLACK];
else
color = &color_table[COLOR_DARK_GREY];
left = i * scale;
right = (i + 1) * scale;
top = -0.5f;
bottom = -1.0f;
/* bottom left */
vertex_data[base].position.x = left;
vertex_data[base].position.y = bottom;
vertex_data[base].position.z = 0.0f;
vertex_data[base].color.r = color->r;
vertex_data[base].color.g = color->g;
vertex_data[base].color.b = color->b;
vertex_data[base].color.a = priv->alpha;
/* top left */
vertex_data[base + 1].position.x = left;
vertex_data[base + 1].position.y = top;
vertex_data[base + 1].position.z = 0.0f;
vertex_data[base + 1].color = vertex_data[base].color;
vertex_data[base + 1].color.a = priv->alpha;
/* top right */
vertex_data[base + 2].position.x = right;
vertex_data[base + 2].position.y = top;
vertex_data[base + 2].position.z = 0.0f;
vertex_data[base + 2].color = vertex_data[base].color;
vertex_data[base + 2].color.a = priv->alpha;
/* bottom right */
vertex_data[base + 3].position.x = right;
vertex_data[base + 3].position.y = bottom;
vertex_data[base + 3].position.z = 0.0f;
vertex_data[base + 3].color = vertex_data[base].color;
vertex_data[base + 3].color.a = priv->alpha;
/* clockwise indexing */
indices[idx_base] = base; /* bottom left */
indices[idx_base + 1] = base + 1; /* top left */
indices[idx_base + 2] = base + 2; /* top right */
indices[idx_base + 3] = base + 3; /* bottom right */
indices[idx_base + 4] = base; /* bottom left */
indices[idx_base + 5] = base + 2; /* top right */
}
D3D12_HEAP_PROPERTIES heap_prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC buffer_desc =
CD3DX12_RESOURCE_DESC::Buffer (sizeof (ColorVertexData) * 4 * 20
+ sizeof (WORD) * 6 * 20);
ComPtr < ID3D12Resource > vertex_index_upload;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&vertex_index_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
guint8 *data;
CD3DX12_RANGE range (0, 0);
hr = vertex_index_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map index buffer");
return FALSE;
}
memcpy (data, vertex_data, sizeof (ColorVertexData) * 4 * 20);
memcpy (data + sizeof (ColorVertexData) * 4 * 20, indices,
sizeof (WORD) * 6 * 20);
vertex_index_upload->Unmap (0, nullptr);
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
ComPtr < ID3D12Resource > vertex_index_buf;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_COMMON, nullptr,
IID_PPV_ARGS (&vertex_index_buf));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
auto quad = std::make_shared < GstD3D12TestSrcQuad > ();
quad->rs = rs;
quad->pso = pso;
quad->vertex_index_buf = vertex_index_buf;
quad->vertex_index_upload = vertex_index_upload;
quad->vbv.BufferLocation = vertex_index_buf->GetGPUVirtualAddress ();
quad->vbv.SizeInBytes = sizeof (ColorVertexData) * 4 * 20;
quad->vbv.StrideInBytes = sizeof (ColorVertexData);
quad->ibv.BufferLocation = quad->vbv.BufferLocation + quad->vbv.SizeInBytes;
quad->ibv.SizeInBytes = sizeof (WORD) * 6 * 20;
quad->ibv.Format = DXGI_FORMAT_R16_UINT;
quad->index_count = 6 * 20;
ctx->quad.push_back (quad);
return setup_snow_render (self, ctx, TRUE);
}
static gboolean
setup_checker_render (GstD3D12TestSrc * self, RenderContext * ctx,
guint checker_size)
{
auto priv = self->priv;
HRESULT hr;
UvVertexData vertex_data[4];
const WORD indices[6] = { 0, 1, 2, 3, 0, 2 };
CD3DX12_ROOT_PARAMETER param;
param.InitAsConstants (4, 0, 0, D3D12_SHADER_VISIBILITY_PIXEL);
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rs_desc = { };
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC::Init_1_0 (rs_desc,
1, &param, 0, nullptr, g_rs_flags);
ComPtr < ID3DBlob > rs_blob;
ComPtr < ID3DBlob > error_blob;
hr = D3DX12SerializeVersionedRootSignature (&rs_desc,
D3D_ROOT_SIGNATURE_VERSION_1_1, &rs_blob, &error_blob);
if (!gst_d3d12_result (hr, self->device)) {
const gchar *error_msg = nullptr;
if (error_blob)
error_msg = (const gchar *) error_blob->GetBufferPointer ();
GST_ERROR_OBJECT (self, "Couldn't serialize root signature, error: %s",
GST_STR_NULL (error_msg));
return FALSE;
}
auto device = gst_d3d12_device_get_device_handle (self->device);
ComPtr < ID3D12RootSignature > rs;
hr = device->CreateRootSignature (0, rs_blob->GetBufferPointer (),
rs_blob->GetBufferSize (), IID_PPV_ARGS (&rs));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create root signature");
return FALSE;
}
GstD3DShaderByteCode vs_code;
GstD3DShaderByteCode ps_code;
if (!gst_d3d_plugin_shader_get_vs_blob (GST_D3D_PLUGIN_VS_COORD,
GST_D3D_SM_5_0, &vs_code)) {
GST_ERROR_OBJECT (self, "Couldn't get vs bytecode");
return FALSE;
}
if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_CHECKER,
GST_D3D_SM_5_0, &ps_code)) {
GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
return FALSE;
}
D3D12_INPUT_ELEMENT_DESC input_desc[2];
input_desc[0].SemanticName = "POSITION";
input_desc[0].SemanticIndex = 0;
input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
input_desc[0].InputSlot = 0;
input_desc[0].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[0].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[0].InstanceDataStepRate = 0;
input_desc[1].SemanticName = "TEXCOORD";
input_desc[1].SemanticIndex = 0;
input_desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
input_desc[1].InputSlot = 0;
input_desc[1].AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT;
input_desc[1].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
input_desc[1].InstanceDataStepRate = 0;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { };
pso_desc.pRootSignature = rs.Get ();
pso_desc.VS.BytecodeLength = vs_code.byte_code_len;
pso_desc.VS.pShaderBytecode = vs_code.byte_code;
pso_desc.PS.BytecodeLength = ps_code.byte_code_len;
pso_desc.PS.pShaderBytecode = ps_code.byte_code;
pso_desc.BlendState = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
pso_desc.SampleMask = UINT_MAX;
pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC (D3D12_DEFAULT);
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
pso_desc.InputLayout.pInputElementDescs = input_desc;
pso_desc.InputLayout.NumElements = G_N_ELEMENTS (input_desc);
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pso_desc.NumRenderTargets = 1;
pso_desc.RTVFormats[0] = DXGI_FORMAT_B8G8R8A8_UNORM;
pso_desc.SampleDesc.Count = 1;
ComPtr < ID3D12PipelineState > pso;
hr = device->CreateGraphicsPipelineState (&pso_desc, IID_PPV_ARGS (&pso));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create pso");
return FALSE;
}
/* bottom left */
vertex_data[0].position.x = -1.0f;
vertex_data[0].position.y = -1.0f;
vertex_data[0].position.z = 0.0f;
vertex_data[0].texture.u = 0.0f;
vertex_data[0].texture.v = 1.0f;
/* top left */
vertex_data[1].position.x = -1.0f;
vertex_data[1].position.y = 1.0f;
vertex_data[1].position.z = 0.0f;
vertex_data[1].texture.u = 0.0f;
vertex_data[1].texture.v = 0.0f;
/* top right */
vertex_data[2].position.x = 1.0f;
vertex_data[2].position.y = 1.0f;
vertex_data[2].position.z = 0.0f;
vertex_data[2].texture.u = 1.0f;
vertex_data[2].texture.v = 0.0f;
/* bottom right */
vertex_data[3].position.x = 1.0f;
vertex_data[3].position.y = -1.0f;
vertex_data[3].position.z = 0.0f;
vertex_data[3].texture.u = 1.0f;
vertex_data[3].texture.v = 1.0f;
D3D12_HEAP_PROPERTIES heap_prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC buffer_desc =
CD3DX12_RESOURCE_DESC::Buffer (sizeof (UvVertexData) * 4
+ sizeof (indices));
ComPtr < ID3D12Resource > vertex_index_upload;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS (&vertex_index_upload));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
guint8 *data;
CD3DX12_RANGE range (0, 0);
hr = vertex_index_upload->Map (0, &range, (void **) &data);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't map index buffer");
return FALSE;
}
memcpy (data, vertex_data, sizeof (UvVertexData) * 4);
memcpy (data + sizeof (UvVertexData) * 4, indices, sizeof (indices));
vertex_index_upload->Unmap (0, nullptr);
heap_prop = CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
ComPtr < ID3D12Resource > vertex_index_buf;
hr = device->CreateCommittedResource (&heap_prop,
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
&buffer_desc, D3D12_RESOURCE_STATE_COMMON, nullptr,
IID_PPV_ARGS (&vertex_index_buf));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create index buffer");
return FALSE;
}
auto quad = std::make_shared < GstD3D12TestSrcQuad > ();
quad->rs = rs;
quad->pso = pso;
quad->vertex_index_buf = vertex_index_buf;
quad->vertex_index_upload = vertex_index_upload;
quad->vbv.BufferLocation = vertex_index_buf->GetGPUVirtualAddress ();
quad->vbv.SizeInBytes = sizeof (UvVertexData) * 4;
quad->vbv.StrideInBytes = sizeof (UvVertexData);
quad->ibv.BufferLocation = quad->vbv.BufferLocation + quad->vbv.SizeInBytes;
quad->ibv.SizeInBytes = sizeof (indices);
quad->ibv.Format = DXGI_FORMAT_R16_UINT;
quad->index_count = 6;
quad->is_checker = TRUE;
quad->checker_const_buffer.width = priv->info.width;
quad->checker_const_buffer.height = priv->info.height;
quad->checker_const_buffer.checker_size = checker_size;
quad->checker_const_buffer.alpha = priv->alpha;
ctx->quad.push_back (quad);
return TRUE;
}
static gboolean
setup_d2d_render (GstD3D12TestSrc * self, RenderContext * ctx)
{
auto priv = self->priv;
HRESULT hr;
static const D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
};
if (!priv->d2d_factory) {
ComPtr < ID2D1Factory > d2d_factory;
hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
IID_PPV_ARGS (&d2d_factory));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create D2D factory");
return FALSE;
}
priv->d2d_factory = d2d_factory;
}
ComPtr < IUnknown > unknown =
gst_d3d12_device_get_11on12_handle (self->device);
if (!unknown) {
GST_ERROR_OBJECT (self, "Couldn't get d3d11 device");
return FALSE;
}
unknown.As (&ctx->device11on12);
unknown.As (&ctx->device11);
ctx->device11->GetImmediateContext (&ctx->d3d11_context);
D3D11_RESOURCE_FLAGS flags11 = { };
flags11.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
flags11.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
GstD3D12Device11on12LockGuard lk (self->device);
hr = ctx->device11on12->CreateWrappedResource (ctx->texture.Get (), &flags11,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
IID_PPV_ARGS (&ctx->wrapped_texture));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create wrapped resource");
return FALSE;
}
ComPtr < IDXGISurface > surface;
hr = ctx->wrapped_texture.As (&surface);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't get DXGI surface");
return FALSE;
}
D2D1_RENDER_TARGET_PROPERTIES props = { };
props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
props.dpiX = 0;
props.dpiY = 0;
props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
hr = priv->d2d_factory->CreateDxgiSurfaceRenderTarget (surface.Get (),
&props, &ctx->d2d_target);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create d2d render target");
return FALSE;
}
switch (ctx->pattern) {
case GST_D3D12_TEST_SRC_BALL:
{
D2D1_GRADIENT_STOP stops[3];
stops[0].color = D2D1::ColorF (D2D1::ColorF::White, priv->alpha);
stops[0].position = 0.0f;
stops[1].color = D2D1::ColorF (D2D1::ColorF::Snow, priv->alpha);
stops[1].position = 0.3f;
stops[2].color = D2D1::ColorF (D2D1::ColorF::Black, priv->alpha);
stops[2].position = 1.0f;
ComPtr < ID2D1GradientStopCollection > collection;
hr = ctx->d2d_target->CreateGradientStopCollection (stops,
G_N_ELEMENTS (stops), D2D1_GAMMA_1_0, D2D1_EXTEND_MODE_CLAMP,
&collection);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create stop collection");
return FALSE;
}
hr = ctx->
d2d_target->CreateRadialGradientBrush (D2D1::
RadialGradientBrushProperties (D2D1::Point2F (0, 0), D2D1::Point2F (0,
0), 20, 20), collection.Get (), &ctx->brush);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create brush");
return FALSE;
}
break;
}
case GST_D3D12_TEST_SRC_CIRCULAR:
{
D2D1_GRADIENT_STOP stops[129];
FLOAT position = 1.0f;
for (guint i = 0; i < G_N_ELEMENTS (stops); i++) {
FLOAT diff;
if ((i % 2) == 0)
stops[i].color = D2D1::ColorF (D2D1::ColorF::Black, priv->alpha);
else
stops[i].color = D2D1::ColorF (D2D1::ColorF::White, priv->alpha);
stops[i].position = position;
diff = position / G_N_ELEMENTS (stops) * 2;
position -= diff;
}
ComPtr < ID2D1GradientStopCollection > collection;
hr = ctx->d2d_target->CreateGradientStopCollection (stops,
G_N_ELEMENTS (stops), D2D1_GAMMA_1_0, D2D1_EXTEND_MODE_CLAMP,
&collection);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create stop collection");
return FALSE;
}
ctx->x = priv->info.width / 2;
ctx->y = priv->info.height / 2;
ctx->rad = MAX (ctx->x, ctx->y);
hr = ctx->
d2d_target->CreateRadialGradientBrush (D2D1::
RadialGradientBrushProperties (D2D1::Point2F (ctx->x, ctx->y),
D2D1::Point2F (0, 0), ctx->rad, ctx->rad), collection.Get (),
&ctx->brush);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create brush");
return FALSE;
}
break;
}
default:
g_assert_not_reached ();
return FALSE;
}
return TRUE;
}
static void gst_d3d12_test_src_finalize (GObject * object);
static void gst_d3d12_test_src_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_d3d12_test_src_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_d3d12_test_src_set_context (GstElement * element,
GstContext * context);
static gboolean gst_d3d12_test_src_is_seekable (GstBaseSrc * bsrc);
static gboolean gst_d3d12_test_src_do_seek (GstBaseSrc * bsrc,
GstSegment * segment);
static GstCaps *gst_d3d12_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
static gboolean gst_d3d12_test_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
static gboolean gst_d3d12_test_src_decide_allocation (GstBaseSrc *
bsrc, GstQuery * query);
static gboolean gst_d3d12_test_src_start (GstBaseSrc * bsrc);
static gboolean gst_d3d12_test_src_stop (GstBaseSrc * bsrc);
static gboolean gst_d3d12_test_src_src_query (GstBaseSrc * bsrc,
GstQuery * query);
static void gst_d3d12_test_src_get_times (GstBaseSrc * bsrc,
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
static GstFlowReturn gst_d3d12_test_src_create (GstBaseSrc * bsrc,
guint64 offset, guint size, GstBuffer ** buf);
#define gst_d3d12_test_src_parent_class parent_class
G_DEFINE_TYPE (GstD3D12TestSrc, gst_d3d12_test_src, GST_TYPE_BASE_SRC);
static void
gst_d3d12_test_src_class_init (GstD3D12TestSrcClass * klass)
{
auto object_class = G_OBJECT_CLASS (klass);
auto element_class = GST_ELEMENT_CLASS (klass);
auto basesrc_class = GST_BASE_SRC_CLASS (klass);
object_class->finalize = gst_d3d12_test_src_finalize;
object_class->set_property = gst_d3d12_test_src_set_property;
object_class->get_property = gst_d3d12_test_src_get_property;
g_object_class_install_property (object_class, PROP_ADAPTER,
g_param_spec_int ("adapter", "Adapter",
"DXGI Adapter index (-1 for any device)",
-1, G_MAXINT32, DEFAULT_ADAPTER,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_IS_LIVE,
g_param_spec_boolean ("is-live", "Is Live",
"Whether to act as a live source", FALSE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_PATTERN,
g_param_spec_enum ("pattern", "Pattern",
"Type of test pattern to generate", GST_TYPE_D3D12_TEST_SRC_PATTERN,
DEFAULT_PATTERN,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_ALPHA,
g_param_spec_float ("alpha", "Alpha", "Global alpha value to use",
0, 1, DEFAULT_ALPHA,
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_ASYNC_DEPTH,
g_param_spec_uint ("async-depth", "Async Depth",
"Number of in-flight GPU commands which can be scheduled without "
"synchronization (0 = unlimited)", 0, G_MAXINT, DEFAULT_ASYNC_DEPTH,
(GParamFlags) (GST_PARAM_MUTABLE_PLAYING |
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d12_test_src_set_context);
gst_element_class_set_static_metadata (element_class,
"Direct3D12 Test Source", "Source/Video",
"Creates a test video stream", "Seungha Yang <seungha@centricular.com>");
gst_element_class_add_static_pad_template (element_class, &src_template);
basesrc_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_d3d12_test_src_is_seekable);
basesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_do_seek);
basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_fixate);
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_set_caps);
basesrc_class->decide_allocation =
GST_DEBUG_FUNCPTR (gst_d3d12_test_src_decide_allocation);
basesrc_class->start = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_start);
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_stop);
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_src_query);
basesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_get_times);
basesrc_class->create = GST_DEBUG_FUNCPTR (gst_d3d12_test_src_create);
GST_DEBUG_CATEGORY_INIT (gst_d3d12_test_src_debug, "d3d12testsrc", 0,
"d3d12testsrc");
gst_type_mark_as_plugin_api (GST_TYPE_D3D12_TEST_SRC_PATTERN,
(GstPluginAPIFlags) 0);
}
static void
gst_d3d12_test_src_init (GstD3D12TestSrc * self)
{
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
self->priv = new GstD3D12TestSrcPrivate ();
}
static void
gst_d3d12_test_src_finalize (GObject * object)
{
auto self = GST_D3D12_TEST_SRC (object);
delete self->priv;
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_d3d12_test_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_TEST_SRC (object);
auto priv = self->priv;
switch (prop_id) {
case PROP_ADAPTER:
priv->adapter_index = g_value_get_int (value);
break;
case PROP_IS_LIVE:
gst_base_src_set_live (GST_BASE_SRC (self), g_value_get_boolean (value));
break;
case PROP_PATTERN:
priv->pattern = (GstD3D12TestSrcPattern) g_value_get_enum (value);
break;
case PROP_ALPHA:
priv->alpha = g_value_get_float (value);
break;
case PROP_ASYNC_DEPTH:
priv->async_depth = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d12_test_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_TEST_SRC (object);
auto priv = self->priv;
switch (prop_id) {
case PROP_ADAPTER:
g_value_set_int (value, priv->adapter_index);
break;
case PROP_IS_LIVE:
g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (self)));
break;
case PROP_PATTERN:
g_value_set_enum (value, priv->pattern);
break;
case PROP_ALPHA:
g_value_set_float (value, priv->alpha);
break;
case PROP_ASYNC_DEPTH:
g_value_set_uint (value, priv->async_depth);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d12_test_src_set_context (GstElement * element, GstContext * context)
{
auto self = GST_D3D12_TEST_SRC (element);
auto priv = self->priv;
gst_d3d12_handle_set_context (element,
context, priv->adapter_index, &self->device);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
static gboolean
gst_d3d12_test_src_is_seekable (GstBaseSrc * bsrc)
{
return TRUE;
}
static gboolean
gst_d3d12_test_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
GstClockTime position;
segment->time = segment->start;
position = segment->position;
priv->reverse = segment->rate < 0;
/* now move to the position indicated */
if (priv->info.fps_n) {
priv->n_frames = gst_util_uint64_scale (position,
priv->info.fps_n, priv->info.fps_d * GST_SECOND);
} else {
priv->n_frames = 0;
}
priv->accum_frames = 0;
priv->accum_rtime = 0;
if (priv->info.fps_n) {
priv->running_time = gst_util_uint64_scale (priv->n_frames,
priv->info.fps_d * GST_SECOND, priv->info.fps_n);
} else {
/* FIXME : Not sure what to set here */
priv->running_time = 0;
}
return TRUE;
}
static GstCaps *
gst_d3d12_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
{
GstStructure *s;
caps = gst_caps_make_writable (caps);
s = gst_caps_get_structure (caps, 0);
gst_structure_fixate_field_nearest_int (s, "width", 320);
gst_structure_fixate_field_nearest_int (s, "height", 240);
gst_structure_fixate_field_nearest_fraction (s, "framerate", 30, 1);
return GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
}
static gboolean
gst_d3d12_test_src_setup_context (GstD3D12TestSrc * self, GstCaps * caps)
{
auto priv = self->priv;
GstVideoInfo draw_info;
HRESULT hr;
GstStructure *config = nullptr;
auto ctx = std::make_unique < RenderContext > (self->device);
/* D2D uses premultiplied alpha */
if (priv->pattern == GST_D3D12_TEST_SRC_CIRCULAR ||
priv->pattern == GST_D3D12_TEST_SRC_BALL) {
config = gst_structure_new ("converter-config",
GST_D3D12_CONVERTER_OPT_SRC_ALPHA_MODE,
GST_TYPE_D3D12_CONVERTER_ALPHA_MODE,
GST_D3D12_CONVERTER_ALPHA_MODE_PREMULTIPLIED, nullptr);
}
gst_video_info_set_format (&draw_info, GST_VIDEO_FORMAT_BGRA,
priv->info.width, priv->info.height);
ctx->conv = gst_d3d12_converter_new (self->device, nullptr,
&draw_info, &priv->info, nullptr, nullptr, config);
if (!ctx->conv) {
GST_ERROR_OBJECT (self, "Failed to create converter");
return FALSE;
}
GstD3D12Format device_format;
if (!gst_d3d12_device_get_format (self->device,
GST_VIDEO_INFO_FORMAT (&priv->info), &device_format)) {
GST_ERROR_OBJECT (self, "Couldn't get device foramt");
return FALSE;
}
auto device = gst_d3d12_device_get_device_handle (self->device);
D3D12_HEAP_PROPERTIES heap_prop =
CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC resource_desc =
CD3DX12_RESOURCE_DESC::Tex2D (DXGI_FORMAT_B8G8R8A8_UNORM,
priv->info.width, priv->info.height, 1, 1, 1, 0,
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
D3D12_CLEAR_VALUE clear_value = { };
D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
clear_value.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
clear_value.Color[0] = 0.0f;
clear_value.Color[1] = 0.0f;
clear_value.Color[2] = 0.0f;
clear_value.Color[3] = 1.0f;
switch (priv->pattern) {
case GST_D3D12_TEST_SRC_WHITE:
memcpy (clear_value.Color, color_table[COLOR_WHITE].color,
sizeof (FLOAT) * 4);
break;
case GST_D3D12_TEST_SRC_RED:
memcpy (clear_value.Color, color_table[COLOR_RED].color,
sizeof (FLOAT) * 4);
break;
case GST_D3D12_TEST_SRC_GREEN:
memcpy (clear_value.Color, color_table[COLOR_GREEN].color,
sizeof (FLOAT) * 4);
break;
case GST_D3D12_TEST_SRC_BLUE:
memcpy (clear_value.Color, color_table[COLOR_BLUE].color,
sizeof (FLOAT) * 4);
break;
case GST_D3D12_TEST_SRC_CIRCULAR:
case GST_D3D12_TEST_SRC_BALL:
heap_flags |= D3D12_HEAP_FLAG_SHARED;
break;
default:
break;
}
hr = device->CreateCommittedResource (&heap_prop, heap_flags,
&resource_desc, D3D12_RESOURCE_STATE_COMMON, &clear_value,
IID_PPV_ARGS (&ctx->texture));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't create texture");
return FALSE;
}
auto mem = gst_d3d12_allocator_alloc_wrapped (nullptr, self->device,
ctx->texture.Get (), 0, nullptr, nullptr);
if (!mem) {
GST_ERROR_OBJECT (self, "Couldn't wrap texture");
return FALSE;
}
ctx->rtv_heap =
gst_d3d12_memory_get_render_target_view_heap ((GstD3D12Memory *) mem);
if (!ctx->rtv_heap) {
GST_ERROR_OBJECT (self, "Couldn't get rtv heap");
gst_memory_unref (mem);
return FALSE;
}
ctx->render_buffer = gst_buffer_new ();
gst_buffer_append_memory (ctx->render_buffer, mem);
if (!priv->downstream_supports_d3d12) {
ctx->convert_pool = gst_d3d12_buffer_pool_new (self->device);
config = gst_buffer_pool_get_config (ctx->convert_pool);
gst_buffer_pool_config_set_params (config, caps, priv->info.size, 0, 0);
D3D12_RESOURCE_FLAGS resource_flags =
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
if ((device_format.format_flags & GST_D3D12_FORMAT_FLAG_OUTPUT_UAV)
== GST_D3D12_FORMAT_FLAG_OUTPUT_UAV) {
resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
} else {
resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
}
auto params = gst_d3d12_allocation_params_new (self->device, &priv->info,
GST_D3D12_ALLOCATION_FLAG_DEFAULT, resource_flags,
D3D12_HEAP_FLAG_NONE);
gst_buffer_pool_config_set_d3d12_allocation_params (config, params);
gst_d3d12_allocation_params_free (params);
if (!gst_buffer_pool_set_config (ctx->convert_pool, config)) {
GST_ERROR_OBJECT (self, "Couldn't set pool config");
return FALSE;
}
if (!gst_buffer_pool_set_active (ctx->convert_pool, TRUE)) {
GST_ERROR_OBJECT (self, "Couldn't configure buffer pool");
return FALSE;
}
}
ctx->viewport.TopLeftX = 0;
ctx->viewport.TopLeftY = 0;
ctx->viewport.Width = priv->info.width;
ctx->viewport.Height = priv->info.height;
ctx->viewport.MinDepth = 0.0f;
ctx->viewport.MaxDepth = 1.0f;
ctx->scissor_rect.left = 0;
ctx->scissor_rect.top = 0;
ctx->scissor_rect.right = priv->info.width;
ctx->scissor_rect.bottom = priv->info.height;
ctx->pattern = priv->pattern;
switch (priv->pattern) {
case GST_D3D12_TEST_SRC_SMPTE:
if (!setup_smpte_render (self, ctx.get ()))
return FALSE;
break;
case GST_D3D12_TEST_SRC_SNOW:
if (!setup_snow_render (self, ctx.get (), FALSE))
return FALSE;
break;
case GST_D3D12_TEST_SRC_BLACK:
ctx->static_color[0].value = color_table[COLOR_BLACK];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_WHITE:
ctx->static_color[0].value = color_table[COLOR_WHITE];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_RED:
ctx->static_color[0].value = color_table[COLOR_RED];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_GREEN:
ctx->static_color[0].value = color_table[COLOR_GREEN];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_BLUE:
ctx->static_color[0].value = color_table[COLOR_BLUE];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_CHECKERS1:
if (!setup_checker_render (self, ctx.get (), 1))
return FALSE;
break;
case GST_D3D12_TEST_SRC_CHECKERS2:
if (!setup_checker_render (self, ctx.get (), 2))
return FALSE;
break;
case GST_D3D12_TEST_SRC_CHECKERS4:
if (!setup_checker_render (self, ctx.get (), 4))
return FALSE;
break;
case GST_D3D12_TEST_SRC_CHECKERS8:
if (!setup_checker_render (self, ctx.get (), 8))
return FALSE;
break;
case GST_D3D12_TEST_SRC_BLINK:
ctx->static_color[0].value = color_table[COLOR_BLACK];
ctx->static_color[0].value.a = priv->alpha;
ctx->static_color[0].is_valid = TRUE;
ctx->static_color[1].value = color_table[COLOR_WHITE];
ctx->static_color[1].value.a = priv->alpha;
ctx->static_color[1].is_valid = TRUE;
break;
case GST_D3D12_TEST_SRC_CIRCULAR:
case GST_D3D12_TEST_SRC_BALL:
if (!setup_d2d_render (self, ctx.get ()))
return FALSE;
break;
}
priv->ctx = std::move (ctx);
return TRUE;
}
static gboolean
gst_d3d12_test_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
GST_DEBUG_OBJECT (self, "Set caps %" GST_PTR_FORMAT, caps);
priv->ctx = nullptr;
auto features = gst_caps_get_features (caps, 0);
if (features && gst_caps_features_contains (features,
GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY)) {
priv->downstream_supports_d3d12 = TRUE;
} else {
priv->downstream_supports_d3d12 = FALSE;
}
GST_OBJECT_LOCK (self);
gst_video_info_from_caps (&priv->info, caps);
GST_OBJECT_UNLOCK (self);
if (priv->info.fps_d <= 0 || priv->info.fps_n <= 0) {
GST_ERROR_OBJECT (self, "Invalid framerate %d/%d", priv->info.fps_n,
priv->info.fps_d);
return FALSE;
}
gst_base_src_set_blocksize (bsrc, GST_VIDEO_INFO_SIZE (&priv->info));
return gst_d3d12_test_src_setup_context (self, caps);
}
static gboolean
gst_d3d12_test_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
GstBufferPool *pool = nullptr;
GstCaps *caps;
guint min, max, size;
gboolean update_pool;
GstVideoInfo vinfo;
gst_query_parse_allocation (query, &caps, nullptr);
if (!caps) {
GST_ERROR_OBJECT (self, "No output caps");
return FALSE;
}
gst_video_info_from_caps (&vinfo, caps);
GstD3D12Format device_format;
if (!gst_d3d12_device_get_format (self->device,
GST_VIDEO_INFO_FORMAT (&vinfo), &device_format)) {
GST_ERROR_OBJECT (self, "Couldn't get device foramt");
return FALSE;
}
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
update_pool = TRUE;
} else {
size = GST_VIDEO_INFO_SIZE (&vinfo);
min = max = 0;
update_pool = FALSE;
}
if (pool && priv->downstream_supports_d3d12) {
if (!GST_IS_D3D12_BUFFER_POOL (pool)) {
gst_clear_object (&pool);
} else {
GstD3D12BufferPool *dpool = GST_D3D12_BUFFER_POOL (pool);
if (!gst_d3d12_device_is_equal (dpool->device, self->device))
gst_clear_object (&pool);
}
}
if (!pool) {
if (priv->downstream_supports_d3d12)
pool = gst_d3d12_buffer_pool_new (self->device);
else
pool = gst_video_buffer_pool_new ();
}
auto config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, min, max);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
if (priv->downstream_supports_d3d12) {
D3D12_RESOURCE_FLAGS resource_flags =
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
if ((device_format.format_flags & GST_D3D12_FORMAT_FLAG_OUTPUT_UAV)
== GST_D3D12_FORMAT_FLAG_OUTPUT_UAV) {
resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
} else {
resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
}
auto params = gst_buffer_pool_config_get_d3d12_allocation_params (config);
if (!params) {
params = gst_d3d12_allocation_params_new (self->device, &vinfo,
GST_D3D12_ALLOCATION_FLAG_DEFAULT, resource_flags,
D3D12_HEAP_FLAG_SHARED);
} else {
gst_d3d12_allocation_params_set_resource_flags (params, resource_flags);
gst_d3d12_allocation_params_unset_resource_flags (params,
D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE);
}
gst_buffer_pool_config_set_d3d12_allocation_params (config, params);
gst_d3d12_allocation_params_free (params);
}
if (!gst_buffer_pool_set_config (pool, config)) {
GST_ERROR_OBJECT (self, "Failed to set config");
gst_clear_object (&pool);
return FALSE;
}
if (update_pool)
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
else
gst_query_add_allocation_pool (query, pool, size, min, max);
gst_object_unref (pool);
return TRUE;
}
static gboolean
gst_d3d12_test_src_start (GstBaseSrc * bsrc)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
if (!gst_d3d12_ensure_element_data (GST_ELEMENT (bsrc), priv->adapter_index,
&self->device)) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
("Failed to prepare device"), (nullptr));
return FALSE;
}
priv->running_time = 0;
priv->reverse = FALSE;
priv->n_frames = 0;
priv->accum_frames = 0;
priv->accum_rtime = 0;
gst_video_info_init (&priv->info);
return TRUE;
}
static gboolean
gst_d3d12_test_src_stop (GstBaseSrc * bsrc)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
priv->ctx = nullptr;
priv->d2d_factory = nullptr;
gst_clear_object (&self->device);
return TRUE;
}
static gboolean
gst_d3d12_test_src_src_query (GstBaseSrc * bsrc, GstQuery * query)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
if (gst_d3d12_handle_context_query (GST_ELEMENT_CAST (self), query,
self->device)) {
return TRUE;
}
break;
case GST_QUERY_LATENCY:
GST_OBJECT_LOCK (self);
if (priv->info.fps_n > 0 && priv->info.fps_d > 0) {
GstClockTime latency;
latency =
gst_util_uint64_scale (GST_SECOND, priv->info.fps_d,
priv->info.fps_n);
GST_OBJECT_UNLOCK (self);
gst_query_set_latency (query,
gst_base_src_is_live (bsrc), latency, GST_CLOCK_TIME_NONE);
GST_DEBUG_OBJECT (self, "Reporting latency of %" GST_TIME_FORMAT,
GST_TIME_ARGS (latency));
return TRUE;
}
GST_OBJECT_UNLOCK (self);
break;
case GST_QUERY_DURATION:
if (bsrc->num_buffers > 0) {
GstFormat format;
gst_query_parse_duration (query, &format, nullptr);
if (format != GST_FORMAT_TIME)
return FALSE;
GST_OBJECT_LOCK (self);
if (format == GST_FORMAT_TIME && priv->info.fps_n > 0 &&
priv->info.fps_d > 0) {
gint64 dur;
dur = gst_util_uint64_scale_int_round (bsrc->num_buffers
* GST_SECOND, priv->info.fps_d, priv->info.fps_n);
gst_query_set_duration (query, GST_FORMAT_TIME, dur);
GST_OBJECT_UNLOCK (self);
return TRUE;
}
GST_OBJECT_UNLOCK (self);
}
break;
default:
break;
}
return GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
}
static void
gst_d3d12_test_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
/* for live sources, sync on the timestamp of the buffer */
if (gst_base_src_is_live (bsrc)) {
GstClockTime timestamp = GST_BUFFER_PTS (buffer);
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
/* get duration to calculate end time */
GstClockTime duration = GST_BUFFER_DURATION (buffer);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
*end = timestamp + duration;
}
*start = timestamp;
}
} else {
*start = -1;
*end = -1;
}
}
static gboolean
gst_d3d12_test_src_draw_ball (GstD3D12TestSrc * self)
{
auto priv = self->priv;
gdouble rad;
FLOAT x, y;
rad = (gdouble) priv->n_frames / 200;
rad = 2 * G_PI * rad;
x = 20 + (0.5 + 0.5 * sin (rad)) * (priv->info.width - 40);
y = 20 + (0.5 + 0.5 * sin (rad * sqrt (2))) * (priv->info.height - 40);
ID3D11Resource *resources[] = { priv->ctx->wrapped_texture.Get () };
GstD3D12Device11on12LockGuard lk (self->device);
priv->ctx->device11on12->AcquireWrappedResources (resources, 1);
priv->ctx->brush->SetCenter (D2D1::Point2F (x, y));
priv->ctx->d2d_target->BeginDraw ();
priv->ctx->d2d_target->Clear (D2D1::ColorF (D2D1::ColorF::Black));
priv->ctx->d2d_target->FillEllipse (D2D1::Ellipse (D2D1::Point2F (x, y),
20, 20), priv->ctx->brush.Get ());
priv->ctx->d2d_target->EndDraw ();
priv->ctx->device11on12->ReleaseWrappedResources (resources, 1);
priv->ctx->d3d11_context->Flush ();
return TRUE;
}
static gboolean
gst_d3d12_test_src_draw_circular (GstD3D12TestSrc * self)
{
auto priv = self->priv;
ID3D11Resource *resources[] = { priv->ctx->wrapped_texture.Get () };
GstD3D12Device11on12LockGuard lk (self->device);
priv->ctx->device11on12->AcquireWrappedResources (resources, 1);
priv->ctx->d2d_target->BeginDraw ();
priv->ctx->d2d_target->Clear (D2D1::ColorF (D2D1::ColorF::Black));
priv->ctx->d2d_target->FillEllipse (D2D1::Ellipse (D2D1::Point2F (priv->
ctx->x, priv->ctx->y), priv->ctx->rad, priv->ctx->rad),
priv->ctx->brush.Get ());
priv->ctx->d2d_target->EndDraw ();
priv->ctx->device11on12->ReleaseWrappedResources (resources, 1);
priv->ctx->d3d11_context->Flush ();
return TRUE;
}
static gboolean
gst_d3d12_test_src_draw_pattern (GstD3D12TestSrc * self, GstClockTime pts,
ID3D12GraphicsCommandList * cl)
{
auto priv = self->priv;
auto ctx = priv->ctx.get ();
D3D12_RESOURCE_BARRIER barrier;
if (ctx->pattern == GST_D3D12_TEST_SRC_BALL)
return gst_d3d12_test_src_draw_ball (self);
else if (ctx->pattern == GST_D3D12_TEST_SRC_CIRCULAR)
return gst_d3d12_test_src_draw_circular (self);
if (ctx->static_color[0].is_valid) {
if (ctx->static_color[1].is_valid && (priv->n_frames % 2) == 1) {
cl->ClearRenderTargetView (GetCPUDescriptorHandleForHeapStart
(ctx->rtv_heap), ctx->static_color[1].value.color, 0, nullptr);
} else {
cl->ClearRenderTargetView (GetCPUDescriptorHandleForHeapStart
(ctx->rtv_heap), ctx->static_color[0].value.color, 0, nullptr);
}
} else {
cl->IASetPrimitiveTopology (D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cl->RSSetViewports (1, &ctx->viewport);
cl->RSSetScissorRects (1, &ctx->scissor_rect);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_heaps[] = {
GetCPUDescriptorHandleForHeapStart (priv->ctx->rtv_heap)
};
cl->OMSetRenderTargets (1, rtv_heaps, FALSE, nullptr);
for (size_t i = 0; i < ctx->quad.size (); i++) {
auto quad = ctx->quad[i];
if (priv->ctx->fence_val == 0) {
cl->CopyResource (quad->vertex_index_buf.Get (),
quad->vertex_index_upload.Get ());
barrier =
CD3DX12_RESOURCE_BARRIER::Transition (quad->vertex_index_buf.Get (),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER |
D3D12_RESOURCE_STATE_INDEX_BUFFER);
cl->ResourceBarrier (1, &barrier);
}
cl->SetGraphicsRootSignature (quad->rs.Get ());
if (quad->is_snow) {
quad->snow_const_buffer.time = (FLOAT) pts / GST_SECOND;
quad->snow_const_buffer.alpha = priv->alpha;
cl->SetGraphicsRoot32BitConstants (0, 2, &quad->snow_const_buffer, 0);
} else if (quad->is_checker) {
quad->checker_const_buffer.alpha = priv->alpha;
cl->SetGraphicsRoot32BitConstants (0, 4, &quad->checker_const_buffer,
0);
}
cl->SetPipelineState (quad->pso.Get ());
cl->IASetIndexBuffer (&quad->ibv);
cl->IASetVertexBuffers (0, 1, &quad->vbv);
cl->DrawIndexedInstanced (quad->index_count, 1, 0, 0, 0);
}
}
barrier = CD3DX12_RESOURCE_BARRIER::Transition (ctx->texture.Get (),
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
cl->ResourceBarrier (1, &barrier);
return TRUE;
}
static GstFlowReturn
gst_d3d12_test_src_create (GstBaseSrc * bsrc, guint64 offset,
guint size, GstBuffer ** buf)
{
auto self = GST_D3D12_TEST_SRC (bsrc);
auto priv = self->priv;
GstBuffer *buffer = nullptr;
GstBuffer *convert_buffer = nullptr;
GstFlowReturn ret = GST_FLOW_ERROR;
GstClockTime pts;
GstClockTime next_time;
if (priv->downstream_supports_d3d12) {
ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc,
offset, size, &convert_buffer);
} else {
ret = gst_buffer_pool_acquire_buffer (priv->ctx->convert_pool,
&convert_buffer, nullptr);
}
if (ret != GST_FLOW_OK)
return ret;
GstD3D12CommandAllocator *gst_ca;
if (!gst_d3d12_command_allocator_pool_acquire (priv->ctx->ca_pool, &gst_ca)) {
GST_ERROR_OBJECT (self, "Couldn't acquire command allocator");
gst_clear_buffer (&convert_buffer);
return GST_FLOW_ERROR;
}
auto ca = gst_d3d12_command_allocator_get_handle (gst_ca);
auto hr = ca->Reset ();
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't reset command allocator");
gst_d3d12_command_allocator_unref (gst_ca);
gst_clear_buffer (&convert_buffer);
return GST_FLOW_ERROR;
}
if (!priv->ctx->cl) {
auto device = gst_d3d12_device_get_device_handle (self->device);
hr = device->CreateCommandList (0, D3D12_COMMAND_LIST_TYPE_DIRECT,
ca, nullptr, IID_PPV_ARGS (&priv->ctx->cl));
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't reset command list");
gst_d3d12_command_allocator_unref (gst_ca);
gst_clear_buffer (&convert_buffer);
return GST_FLOW_ERROR;
}
} else {
hr = priv->ctx->cl->Reset (ca, nullptr);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't reset command list");
gst_d3d12_command_allocator_unref (gst_ca);
gst_clear_buffer (&convert_buffer);
return GST_FLOW_ERROR;
}
}
auto cl = priv->ctx->cl;
GstD3D12FenceData *fence_data;
gst_d3d12_fence_data_pool_acquire (priv->fence_data_pool, &fence_data);
gst_d3d12_fence_data_push (fence_data, FENCE_NOTIFY_MINI_OBJECT (gst_ca));
pts = priv->accum_rtime + priv->running_time;
gst_d3d12_test_src_draw_pattern (self, pts, cl.Get ());
if (!gst_d3d12_converter_convert_buffer (priv->ctx->conv,
priv->ctx->render_buffer, convert_buffer, fence_data, cl.Get (),
FALSE)) {
GST_ERROR_OBJECT (self, "Couldn't build convert command");
gst_clear_buffer (&convert_buffer);
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
hr = cl->Close ();
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't close command list");
gst_clear_buffer (&convert_buffer);
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
ID3D12CommandList *cmd_list[] = { priv->ctx->cl.Get () };
hr = gst_d3d12_device_execute_command_lists (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT, 1, cmd_list, &priv->ctx->fence_val);
if (!gst_d3d12_result (hr, self->device)) {
GST_ERROR_OBJECT (self, "Couldn't execute command list");
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
gst_d3d12_buffer_set_fence (convert_buffer,
gst_d3d12_device_get_fence_handle (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT), priv->ctx->fence_val, FALSE);
gst_d3d12_device_set_fence_notify (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT, priv->ctx->fence_val,
FENCE_NOTIFY_MINI_OBJECT (fence_data));
priv->ctx->scheduled.push (priv->ctx->fence_val);
auto completed = gst_d3d12_device_get_completed_value (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT);
while (!priv->ctx->scheduled.empty ()) {
if (priv->ctx->scheduled.front () > completed)
break;
priv->ctx->scheduled.pop ();
}
auto async_depth = priv->async_depth.load ();
if (async_depth > 0 && priv->ctx->scheduled.size () > async_depth) {
auto fence_to_wait = priv->ctx->scheduled.front ();
priv->ctx->scheduled.pop ();
gst_d3d12_device_fence_wait (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT, fence_to_wait);
}
if (priv->downstream_supports_d3d12) {
buffer = convert_buffer;
convert_buffer = nullptr;
} else {
ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc,
offset, size, &buffer);
if (ret != GST_FLOW_OK) {
gst_buffer_unref (convert_buffer);
return ret;
}
GstVideoFrame src_frame, dst_frame;
if (!gst_video_frame_map (&src_frame, &priv->info, convert_buffer,
GST_MAP_READ)) {
GST_ERROR_OBJECT (self, "Couldn't map convert buffer");
gst_buffer_unref (convert_buffer);
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
if (!gst_video_frame_map (&dst_frame, &priv->info, buffer, GST_MAP_WRITE)) {
GST_ERROR_OBJECT (self, "Couldn't map output buffer");
gst_video_frame_unmap (&src_frame);
gst_buffer_unref (convert_buffer);
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
auto copy_ret = gst_video_frame_copy (&dst_frame, &src_frame);
gst_video_frame_unmap (&src_frame);
gst_video_frame_unmap (&dst_frame);
gst_buffer_unref (convert_buffer);
if (!copy_ret) {
GST_ERROR_OBJECT (self, "Couldn't copy frame");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
}
GST_BUFFER_PTS (buffer) = pts;
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (buffer) = priv->accum_frames + priv->n_frames;
if (priv->reverse) {
priv->n_frames--;
} else {
priv->n_frames++;
}
GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET (buffer) + 1;
next_time = gst_util_uint64_scale (priv->n_frames,
priv->info.fps_d * GST_SECOND, priv->info.fps_n);
if (priv->reverse) {
/* We already decremented to next frame */
GstClockTime prev_pts = gst_util_uint64_scale (priv->n_frames + 2,
priv->info.fps_d * GST_SECOND, priv->info.fps_n);
GST_BUFFER_DURATION (buffer) = prev_pts - GST_BUFFER_PTS (buffer);
} else {
GST_BUFFER_DURATION (buffer) = next_time - priv->running_time;
}
priv->running_time = next_time;
*buf = buffer;
return GST_FLOW_OK;
}