mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
dd4d85272e
Adding format conversion helper and use compute shader in case that output format does not support RTV. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7088>
2337 lines
73 KiB
C++
2337 lines
73 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 <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,
|
|
};
|
|
|
|
#define DEFAULT_ADAPTER -1
|
|
#define DEFAULT_PATTERN GST_D3D12_TEST_SRC_SMPTE
|
|
#define DEFAULT_ALPHA 1.0f
|
|
|
|
#define ASYNC_DEPTH 2
|
|
|
|
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)
|
|
{
|
|
event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS);
|
|
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, event_handle);
|
|
|
|
CloseHandle (event_handle);
|
|
|
|
{
|
|
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;
|
|
HANDLE event_handle;
|
|
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;
|
|
};
|
|
/* *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, ¶m, 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, ¶m, 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)));
|
|
|
|
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;
|
|
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;
|
|
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_NONE);
|
|
} 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;
|
|
|
|
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 ();
|
|
}
|
|
|
|
if (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, priv->ctx->event_handle);
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|